From c75a6011a42feb5403266239e5822c6283f060a7 Mon Sep 17 00:00:00 2001 From: Weilbyte Date: Sat, 4 Jul 2020 12:21:12 +0200 Subject: [PATCH] Remove old files --- discordDark.css | 481 - serverside/ddInstall.sh | 50 - serverside/ddRemove.sh | 26 - serverside/jsmod/5.4-3.sh | 32 - serverside/jsmod/5.4-3/charts.js | 22013 --------- serverside/jsmod/5.4-3/charts.js.original | 22010 --------- serverside/jsmod/5.4-3/proxmoxlib.js | 6757 --- serverside/jsmod/5.4-3/proxmoxlib.js.original | 6757 --- serverside/jsmod/5.4-3/pvemanagerlib.js | 38347 --------------- .../jsmod/5.4-3/pvemanagerlib.js.original | 38347 --------------- serverside/jsmod/6.0-4.sh | 32 - serverside/jsmod/6.0-4/charts.js | 22013 --------- serverside/jsmod/6.0-4/charts.js.original | 1 - serverside/jsmod/6.0-4/proxmoxlib.js | 7357 --- serverside/jsmod/6.0-4/proxmoxlib.js.original | 7357 --- serverside/jsmod/6.0-4/pvemanagerlib.js | 39779 --------------- .../jsmod/6.0-4/pvemanagerlib.js.original | 39779 --------------- serverside/jsmod/6.1-3.sh | 36 - serverside/jsmod/6.1-3/charts.js | 22013 --------- serverside/jsmod/6.1-3/charts.js.original | 1 - serverside/jsmod/6.1-3/proxmoxlib.js.original | 7639 --- .../jsmod/6.1-3/pvemanagerlib.js.original | 40953 ---------------- serverside/jsmod/README.md | 22 - serverside/jsmod/changes.md | 31 - serverside/style.css | 834 - 25 files changed, 322667 deletions(-) delete mode 100644 discordDark.css delete mode 100644 serverside/ddInstall.sh delete mode 100644 serverside/ddRemove.sh delete mode 100644 serverside/jsmod/5.4-3.sh delete mode 100644 serverside/jsmod/5.4-3/charts.js delete mode 100644 serverside/jsmod/5.4-3/charts.js.original delete mode 100644 serverside/jsmod/5.4-3/proxmoxlib.js delete mode 100644 serverside/jsmod/5.4-3/proxmoxlib.js.original delete mode 100644 serverside/jsmod/5.4-3/pvemanagerlib.js delete mode 100644 serverside/jsmod/5.4-3/pvemanagerlib.js.original delete mode 100644 serverside/jsmod/6.0-4.sh delete mode 100644 serverside/jsmod/6.0-4/charts.js delete mode 100644 serverside/jsmod/6.0-4/charts.js.original delete mode 100644 serverside/jsmod/6.0-4/proxmoxlib.js delete mode 100644 serverside/jsmod/6.0-4/proxmoxlib.js.original delete mode 100644 serverside/jsmod/6.0-4/pvemanagerlib.js delete mode 100644 serverside/jsmod/6.0-4/pvemanagerlib.js.original delete mode 100644 serverside/jsmod/6.1-3.sh delete mode 100644 serverside/jsmod/6.1-3/charts.js delete mode 100644 serverside/jsmod/6.1-3/charts.js.original delete mode 100644 serverside/jsmod/6.1-3/proxmoxlib.js.original delete mode 100644 serverside/jsmod/6.1-3/pvemanagerlib.js.original delete mode 100644 serverside/jsmod/README.md delete mode 100644 serverside/jsmod/changes.md delete mode 100644 serverside/style.css diff --git a/discordDark.css b/discordDark.css deleted file mode 100644 index 90b97e2..0000000 --- a/discordDark.css +++ /dev/null @@ -1,481 +0,0 @@ -@-moz-document regexp("(https:\/\/.*):(8006)\/?(.*)") -{ -.x-box-inner { -overflow:hidden; -position:relative; -left:0; -top:0; -background:#23272a -} - -.x-body { -color:#fff; -font-size:13px; -line-height:17px; -font-weight:300; -font-family:helvetica, arial, verdana, sans-serif; -background:#fff -} - -.x-viewport,.x-viewport > .x-body { -background:#23272a -} - -.x-form-text-default { -color:#818082; -background-color:#4a4d53; -font:300 13px/17px helvetica, arial, verdana, sans-serif; -min-height:22px; -padding:0 6px 2px -} - -.x-form-trigger-wrap-default { -border-color:#cfcfcf; -border-style:solid; -border-width:0 -} - -.x-form-trigger-default { -width:22px; -background:0 center #72767d url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_trigger.png) no-repeat -} - -.x-panel-body-default { -background:#2c2f33; -color:#fff; -font-size:13px; -font-weight:300; -font-family:helvetica, arial, verdana, sans-serif; -border-color:#fff; -border-style:solid; -border-width:0 -} - -.x-grid-cell-inner,.x-grid-cell-rowbody { -background:#2c2f33; -color:#fff -} - -.x-toolbar-default { -background-color:#2c2f33; -border-width:0 -} - -.x-toolbar { -background:#2c2f33 -} - -#toolbar-1069-innerCt { -background:#2c2f33 -} - -.x-autocontainer-innerCt { -background:#2c2f33 -} - -[id^="toolbar"] { -background:#2c2f33 -} - -[id^="legend"] { -background:#23272a; -border-color:red -} - -.x-legend-item { -background:#2c2f33; -color:#fff; -border-width:0 -} - -.x-legend-container { -background:#2c2f33; -border-radius:0; -box-shadow:rgba(0,0,0,0) 0 0 0; -border-width:0 -} - -.x-legend.x-docked-top .x-legend-item,.x-legend.x-docked-bottom .x-legend-item,.x-legend-panel.x-docked-top .x-legend-item,.x-legend-panel.x-docked-bottom .x-legend-item { -border-left:0; -border-bottom:0 -} - -.x-treelist-item-leaf,.x-treelist-container,.x-treelist-row,.x-treelist-row-with-icon,.x-treelist-item-expandable,.x-treelist-item-wrap,.x-treelist-item-text,.x-treelist-item-icon { -background:#2c2f33; -color:#fff -} - -.x-treelist-item-selected > .x-treelist-row { -background-color:#23272a -} - -.x-treelist-row-over { -color:#FF0 -} - -.x-treelist-row-over > * > .x-treelist-item-text { -color:#7289da; -transition:color .5s -} - -.x-treelist-row-over > * > .x-treelist-item-icon { -color:#7289da; -transition:color .5s -} - -.fa-ceph:before { -background-image:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_cephwhite.png) -} - -.x-treelist-row-over > * > .fa-ceph:before { -background-image:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_cephblurp.png); -background-size:14px 14px; -transition:background-image .5s -} - -.x-progress { -background:#2c2f33 -} - -.x-progress-bar { -background-color:#7289da!important -} - -.x-progress-text.x-progress-text-back,.x-progress-text { -color:#fff!important -} - -.x-component.x-box-item.x-component-default { -background:#23272a -} - -.x-panel-header { -background:#23272a; -border:0 -} - -.x-title-text { -color:#7289da -} - -.x-btn { -background:#7289da; -border-width:0 -} - -.x-btn-inner,.x-btn-inner.x-btn-inner-default-small { -color:#fff -} - -#button-1030 { -background:#23272a; -border-color:#d23d3f; -border-width:1px -} - -.x-btn.x-unselectable.x-box-item.x-toolbar-item.x-btn-default-toolbar-small.x-btn-over,.x-btn.x-unselectable.x-box-item.x-btn-default-small.x-btn-over,.x-menu-item-focus { -background-color:#677bc4 -} - -.x-btn.x-unselectable.x-box-item.x-toolbar-item.x-btn-default-toolbar-small.x-btn-menu-active { -background-color:#677bc4 -} - -.x-btn-disabled { -background-image:none; -background-color:#737fab!important -} - -.x-btn-icon-el.x-btn-icon-el-default-small.fa.fa-book.x-btn-icon-el-default-toolbar-small,.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-undo,.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-terminal,.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-fw.fa-ellipsis-v,.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.x-btn-icon-el-default-toolbar-small.fa.fa-question-circle,.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-send-o,.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-play,.x-btn-inner.x-btn-inner-default-toolbar-small,.x-btn-icon-el.x-btn-icon-el-default-small.fa.black.fa-gear,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-send-o,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-clone,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-file-o,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-heartbeat,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-trash-o,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-desktop,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-cube,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-terminal { -color:#fff -} - -.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-play,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-play { -color:#43b581 -} - -.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-power-off,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-stop,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-stop,.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-power-off { -color:#d23d3f -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-pause { -color:#faa61a -} - -.x-menu-item,.x-menu-default { -background:#7289da; -border-style:none; -border-width:0 -} - -.x-menu-item-text { -color:#fff -} - -.x-menu-item-default.x-menu-item-separator { -height:0; -border-top-width:0; -margin:0; -padding:0 -} - -.x-menu-header { -border-radius:1px; -background:#23272a; -border-width:0 -} - -[id^="tooltip"] { -background:#7289da; -color:#fff; -border-width:0 -} - -[id^="ext-quicktips-tip-body"],[id^="ext-quicktips-tip-innerCt"],[id^="ext-quicktips-tip-ext-quicktips-tip-outerCt"],[id^="ext-form-error"],.x-tip-default { -background-color:#7289da; -color:#fff; -border-radius:0; -border-width:0 -} - -.x-tool-img { -background-image:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_tool-sprites.png) -} - -.x-window-header.x-header.x-header-draggable.x-docked.x-unselectable.x-window-header-default.x-horizontal.x-window-header-horizontal.x-window-header-default-horizontal.x-top.x-window-header-top.x-window-header-default-top.x-box-layout-ct,.x-window-body { -background:#23272a; -border-bottom-width:0; -border-right-width:0 -} - -.x-window-default { -border-radius:0; -background-color:#23272a; -box-shadow:none; -border-color:#23272a; -border-style:none; -border-width:0; -padding:0 -} - -.x-window-default-mc { -background-color:#23272a -} - -.x-window-body-default { -color: white; -border-width:0!important -} - -.x-window-header-default-top { -border-top-left-radius:0!important; -border-top-right-radius:0!important; -border-bottom-right-radius:0!important; -border-bottom-left-radius:0!important; -background-color:#23272a; -border-width:0!important; -padding:9px -} - -.x-form-text,.x-form-item-label-inner-default,.x-window-text,.x-form-display-field,.x-component { -color:#fff -} - -.x-mask { -background-color:rgba(26,26,29,0.27) -} - -.x-splitter-collapsed .x-layout-split-bottom { -background-image:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_mini-top.png) -} - -.x-layout-split-bottom { -background-image:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_mini-bottom.png) -} - -.x-tab { -background:#737fab; -color:#fff; -border-width:0 -} - -.x-tab-inner { -color:#fff -} - -.x-tab-active,.x-tab-over,.x-tab-default-focus,.x-tab-focus { -background:#7289da!important; -border-width:0!important -} - -.x-tab-bar-body { -background:#23272a -} - -.x-tab-disabled { -background:#737fab!important -} - -.x-column-header-inner,.x-column-header-default,.x-grid-item,.x-grid-header-ct { -background:#2c2f33; -border-width:0!important -} - -.x-column-header-text-inner { -color:#fff -} - -.x-column-header-trigger { -border-color:#23272a -} - -.x-toolbar-text.x-box-item.x-toolbar-item.x-toolbar-text-default,.x-grid-group-title { -color:#fff -} - -[id^="pveNodeStatus"] { -background:#23272a -} - -div[id^="pveNotesView-"][id$="-innerCt"] { -background:#23272a -} - -img[src^="/pve2/images/proxmox_logo"] { -background:#23272a; -content:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_logo.png) -} - -[id^="versioninfo"] { -background:#23272a -} - -[id^="proxmoxGauge"] { -background:#23272a -} - -div[id^="panel-"][id$="-body"] { -background:#23272a -} - -.x-surface-canvas { -border-radius:3px -} - -.x-component.x-fieldset-header-text.x-component-default { -color:#fff -} - -.x-fieldset-default { -border:1px solid #7289da -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-server,.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-server,.fa-building.online,.x-tree-icon.x-tree-icon-custom.x-tree-icon-leaf.fa.fa-cube.running.ha-unmanaged,.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-building,.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-building,.x-tree-icon.x-tree-icon-custom.x-tree-icon-leaf.fa.fa-cube,.x-tree-icon.x-tree-icon-custom.x-tree-icon-leaf.fa.fa-desktop,.x-tree-icon.x-tree-icon-custom.x-tree-icon-leaf.fa.fa-database,.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-cube,.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-desktop,.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-database,.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-database,.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-desktop,.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-cube { -color:#7289da!important -} - -.x-grid-group-hd.x-grid-group-hd-collapsible { -background:#23272a; -color:#fff; -border-width:0 -} - -.x-splitter { -background:#23272a -} - -.x-box-scroller,.x-box-scroller-body-vertical,.x-vertical-scroller,.x-toolbar-vertical-scroller,.x-toolbar-default-vertical-scroller { -background:#2c2f33!important -} - -.pve-itype-icon-processor { -background-image:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-cpu.png) -} - -.pve-itype-icon-memory { -background-image:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-ram.png) -} - -.pve-itype-icon-storage { -background-image:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-hdd.png) -} - -.pve-itype-icon-swap { -background-image:url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-swap.png) -} - -.pve-itype-icon-display, .x-grid-row-console { -background-image: url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-display.png); -} - -.pve-itype-icon-cdrom { -background-image: url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-cd.png); -} - -.pve-itype-icon-network { -background-image: url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-network.png); -} - -.pve-itype-icon-pci { -background-image: url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-pci.png); -} - -.pve-itype-icon-usb { -background-image: url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-usb.png); -} - -.pve-itype-icon-serial { -background-image: url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-serial.png); -} - -.pve-itype-icon-cloud { -background-image: url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-cloud.png); -} - -.fa-fw.x-grid-icon-custom.fa.fa-database,.fa-fw.x-grid-icon-custom.fa.fa-desktop,.fa-fw.x-grid-icon-custom.fa.fa-cube { -color:#7289da -} - -html { -overflow:scroll; -overflow-x:hidden -} - -::-webkit-scrollbar { -width:0; -background:transparent -} - -.lxc:after,.qemu:after { -background:transparent!important; -color:#7289da; -text-shadow:0 0 0 #2c2f33!important -} - -.x-tree-icon-custom:after,.x-grid-icon-custom:after { -text-shadow:0 0 0 #2c2f33 -} - -* { - font-weight: 350; -} - -.x-surface-canvas { - filter: invert(100%); -} - -.x-legend-item-marker { - filter: invert(100%); -} - -.x-grid-row-loading { - background-image: url(https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_loading.svg); - background-size: 32px; -} - -.x-grid-item-alt { -background:#23272a; -border-width:0!important; -} - -} \ No newline at end of file diff --git a/serverside/ddInstall.sh b/serverside/ddInstall.sh deleted file mode 100644 index a82d1a0..0000000 --- a/serverside/ddInstall.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -Say () { - printf "\e[1;34m $1 \e[0m \n"; -} - -DotSay () { - printf "[\e[1;34m*\e[0m] \e[1;34m $1 \e[0m \n"; -} - - -Say '[PVE Discord Dark UI Theme Installer]' -Say 'Internet connection required to download files' -Say '>Press any key to begin installation' -read -p "" -Say ' ' -DotSay 'Backing up index template file' -cp /usr/share/pve-manager/index.html.tpl /usr/share/pve-manager/index.html.tpl.bak -DotSay 'Applying stylesheet..' -echo "" >> /usr/share/pve-manager/index.html.tpl -cd /usr/share/pve-manager/css -wget -O dd_style.css https://raw.githubusercontent.com/Weilbyte/PVEDiscordDark/master/serverside/style.css &> /dev/null -DotSay 'Applied stylesheet!' -DotSay 'Downloading images..' -cd /usr/share/pve-manager/images -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_cephblurp.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_cephwhite.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-cpu.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-hdd.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-ram.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-swap.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-cd.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-display.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-network.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-cloud.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-serial.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-usb.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_icon-pci.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_logo.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_mini-bottom.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_mini-top.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_readme &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_tool-sprites.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_trigger.png &> /dev/null -wget https://github.com/Weilbyte/PVEDiscordDark/raw/master/images/dd_loading.svg &> /dev/null - -DotSay 'Downloaded images!' -Say '' -Say 'Installation finished!' -Say 'o7' diff --git a/serverside/ddRemove.sh b/serverside/ddRemove.sh deleted file mode 100644 index 5149c2f..0000000 --- a/serverside/ddRemove.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -Say () { - printf "\e[1;34m $1 \e[0m \n"; -} - -DotSay () { - printf "[\e[1;34m*\e[0m] \e[1;34m $1 \e[0m \n"; -} - - -Say '[PVE Discord Dark UI Theme Remover]' -Say '>Press any key to remove theme' -read -p "" -Say ' ' -DotSay 'Reverting template change' -rm /usr/share/pve-manager/index.html.tpl -cp /usr/share/pve-manager/index.html.tpl.bak /usr/share/pve-manager/index.html.tpl -DotSay 'Removing stylesheet' -rm /usr/share/pve-manager/css/dd_style.css -DotSay 'Removing images' -cd /usr/share/pve-manager/images -rm dd_* -Say '' -Say 'Theme removed!' -Say 'o7' diff --git a/serverside/jsmod/5.4-3.sh b/serverside/jsmod/5.4-3.sh deleted file mode 100644 index 98b9a8f..0000000 --- a/serverside/jsmod/5.4-3.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -Say () { - printf "\e[1;34m $1 \e[0m \n"; -} - -DotSay () { - printf "[\e[1;34m*\e[0m] \e[1;34m $1 \e[0m \n"; -} - - -Say '[PVE Discord Dark UI Theme JSMOD Installer]' -Say 'Internet connection REQUIRED.' -Say '!!ONLY FOR PVE 5.4-3!!' -Say '>Press any key to begin installation' -read -p "" -Say ' ' -DotSay 'Backing up files' -cp /usr/share/pve-manager/js/pvemanagerlib.js /usr/share/pve-manager/js/pvemanagerlib.js.bak -cp /usr/share/javascript/extjs/charts.js /usr/share/javascript/extjs/charts.js.bak -cp /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.bak -DotSay 'Replacing files with modded versions' -rm /usr/share/pve-manager/js/pvemanagerlib.js -wget https://raw.githubusercontent.com/Weilbyte/PVEDiscordDark/master/serverside/jsmod/5.4-3/pvemanagerlib.js -P /usr/share/pve-manager/js/ &> /dev/null -rm /usr/share/javascript/extjs/charts.js -wget https://raw.githubusercontent.com/Weilbyte/PVEDiscordDark/master/serverside/jsmod/5.4-3/charts.js -P /usr/share/javascript/extjs/ &> /dev/null -rm /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js -wget https://raw.githubusercontent.com/Weilbyte/PVEDiscordDark/master/serverside/jsmod/5.4-3/proxmoxlib.js -P /usr/share/javascript/proxmox-widget-toolkit/ &> /dev/null -DotSay 'Applied successfully.' -Say '' -Say 'Installation finished!' -Say 'o7' diff --git a/serverside/jsmod/5.4-3/charts.js b/serverside/jsmod/5.4-3/charts.js deleted file mode 100644 index bc45966..0000000 --- a/serverside/jsmod/5.4-3/charts.js +++ /dev/null @@ -1,22013 +0,0 @@ -Ext.define("Ext.draw.ContainerBase", { - extend: "Ext.panel.Panel", - requires: ["Ext.window.Window"], - previewTitleText: "Chart Preview", - previewAltText: "Chart preview", - layout: "container", - addElementListener: function() { - var b = this, - a = arguments; - if (b.rendered) { - b.el.on.apply(b.el, a) - } else { - b.on("render", function() { - b.el.on.apply(b.el, a) - }) - } - }, - removeElementListener: function() { - var b = this, - a = arguments; - if (b.rendered) { - b.el.un.apply(b.el, a) - } - }, - afterRender: function() { - this.callParent(arguments); - this.initAnimator() - }, - getItems: function() { - var b = this, - a = b.items; - if (!a || !a.isMixedCollection) { - b.initItems() - } - return b.items - }, - onRender: function() { - this.callParent(arguments); - this.element = this.el; - this.innerElement = this.body - }, - setItems: function(a) { - this.items = a; - return a - }, - setSurfaceSize: function(b, a) { - this.resizeHandler({ - width: b, - height: a - }); - this.renderFrame() - }, - onResize: function(c, a, b, e) { - var d = this; - d.callParent([c, a, b, e]); - d.setBodySize({ - width: c, - height: a - }) - }, - preview: function() { - var a = this.getImage(); - new Ext.window.Window({ - title: this.previewTitleText, - closeable: true, - renderTo: Ext.getBody(), - autoShow: true, - maximizeable: true, - maximized: true, - border: true, - layout: { - type: "hbox", - pack: "center", - align: "middle" - }, - items: { - xtype: "container", - items: { - xtype: "image", - mode: "img", - cls: Ext.baseCSSPrefix + "chart-image", - alt: this.previewAltText, - src: a.data, - listeners: { - afterrender: function() { - var e = this, - b = e.imgEl.dom, - d = a.type === "svg" ? 1 : (window.devicePixelRatio || 1), - c; - if (!b.naturalWidth || !b.naturalHeight) { - b.onload = function() { - var g = b.naturalWidth, - f = b.naturalHeight; - e.setWidth(Math.floor(g / d)); - e.setHeight(Math.floor(f / d)) - } - } else { - c = e.getSize(); - e.setWidth(Math.floor(c.width / d)); - e.setHeight(Math.floor(c.height / d)) - } - } - } - } - } - }) - }, - privates: { - getTargetEl: function() { - return this.innerElement - }, - reattachToBody: function() { - var a = this; - if (a.pendingDetachSize) { - a.onBodyResize() - } - a.pendingDetachSize = false; - a.callParent() - } - } -}); -Ext.define("Ext.draw.SurfaceBase", { - extend: "Ext.Widget", - getOwnerBody: function() { - return this.ownerCt.body - }, - destroy: function() { - var a = this; - if (a.hasListeners.destroy) { - a.fireEvent("destroy", a) - } - a.callParent() - } -}); -Ext.define("Ext.draw.Color", { - statics: { - colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/, - rgbToHexRe: /\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/, - rgbaToHexRe: /\s*rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\.\d]+)\)/, - hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/, - NONE: "none", - RGBA_NONE: "rgba(0, 0, 0, 0)" - }, - isColor: true, - lightnessFactor: 0.2, - constructor: function(d, b, a, c) { - this.setRGB(d, b, a, c) - }, - setRGB: function(e, c, a, d) { - var b = this; - b.r = Math.min(255, Math.max(0, e)); - b.g = Math.min(255, Math.max(0, c)); - b.b = Math.min(255, Math.max(0, a)); - if (d === undefined) { - b.a = 1 - } else { - b.a = Math.min(1, Math.max(0, d)) - } - }, - getGrayscale: function() { - return this.r * 0.3 + this.g * 0.59 + this.b * 0.11 - }, - getHSL: function() { - var i = this, - a = i.r / 255, - f = i.g / 255, - j = i.b / 255, - k = Math.max(a, f, j), - d = Math.min(a, f, j), - m = k - d, - e, n = 0, - c = 0.5 * (k + d); - if (d !== k) { - n = (c <= 0.5) ? m / (k + d) : m / (2 - k - d); - if (a === k) { - e = 60 * (f - j) / m - } else { - if (f === k) { - e = 120 + 60 * (j - a) / m - } else { - e = 240 + 60 * (a - f) / m - } - } - if (e < 0) { - e += 360 - } - if (e >= 360) { - e -= 360 - } - } - return [e, n, c] - }, - getHSV: function() { - var i = this, - a = i.r / 255, - f = i.g / 255, - j = i.b / 255, - k = Math.max(a, f, j), - d = Math.min(a, f, j), - c = k - d, - e, m = 0, - l = k; - if (d != k) { - m = l ? c / l : 0; - if (a === k) { - e = 60 * (f - j) / c - } else { - if (f === k) { - e = 60 * (j - a) / c + 120 - } else { - e = 60 * (a - f) / c + 240 - } - } - if (e < 0) { - e += 360 - } - if (e >= 360) { - e -= 360 - } - } - return [e, m, l] - }, - setHSL: function(g, f, e) { - var i = this, - d = Math.abs, - j, b, a; - g = (g % 360 + 360) % 360; - f = f > 1 ? 1 : f < 0 ? 0 : f; - e = e > 1 ? 1 : e < 0 ? 0 : e; - if (f === 0 || g === null) { - e *= 255; - i.setRGB(e, e, e) - } else { - g /= 60; - j = f * (1 - d(2 * e - 1)); - b = j * (1 - d(g % 2 - 1)); - a = e - j / 2; - a *= 255; - j *= 255; - b *= 255; - switch (Math.floor(g)) { - case 0: - i.setRGB(j + a, b + a, a); - break; - case 1: - i.setRGB(b + a, j + a, a); - break; - case 2: - i.setRGB(a, j + a, b + a); - break; - case 3: - i.setRGB(a, b + a, j + a); - break; - case 4: - i.setRGB(b + a, a, j + a); - break; - case 5: - i.setRGB(j + a, a, b + a); - break - } - } - return i - }, - setHSV: function(f, e, d) { - var g = this, - i, b, a; - f = (f % 360 + 360) % 360; - e = e > 1 ? 1 : e < 0 ? 0 : e; - d = d > 1 ? 1 : d < 0 ? 0 : d; - if (e === 0 || f === null) { - d *= 255; - g.setRGB(d, d, d) - } else { - f /= 60; - i = d * e; - b = i * (1 - Math.abs(f % 2 - 1)); - a = d - i; - a *= 255; - i *= 255; - b *= 255; - switch (Math.floor(f)) { - case 0: - g.setRGB(i + a, b + a, a); - break; - case 1: - g.setRGB(b + a, i + a, a); - break; - case 2: - g.setRGB(a, i + a, b + a); - break; - case 3: - g.setRGB(a, b + a, i + a); - break; - case 4: - g.setRGB(b + a, a, i + a); - break; - case 5: - g.setRGB(i + a, a, b + a); - break - } - } - return g - }, - createLighter: function(b) { - if (!b && b !== 0) { - b = this.lightnessFactor - } - var a = this.getHSL(); - a[2] = Ext.Number.constrain(a[2] + b, 0, 1); - return Ext.draw.Color.fromHSL(a[0], a[1], a[2]) - }, - createDarker: function(a) { - if (!a && a !== 0) { - a = this.lightnessFactor - } - return this.createLighter(-a) - }, - toString: function() { - var f = this, - c = Math.round; - if (f.a === 1) { - var e = c(f.r).toString(16), - d = c(f.g).toString(16), - a = c(f.b).toString(16); - e = (e.length === 1) ? "0" + e : e; - d = (d.length === 1) ? "0" + d : d; - a = (a.length === 1) ? "0" + a : a; - return ["#", e, d, a].join("") - } else { - return "rgba(" + [c(f.r), c(f.g), c(f.b), f.a === 0 ? 0 : f.a.toFixed(15)].join(", ") + ")" - } - }, - toHex: function(b) { - if (Ext.isArray(b)) { - b = b[0] - } - if (!Ext.isString(b)) { - return "" - } - if (b.substr(0, 1) === "#") { - return b - } - var e = Ext.draw.Color.colorToHexRe.exec(b); - if (Ext.isArray(e)) { - var f = parseInt(e[2], 10), - d = parseInt(e[3], 10), - a = parseInt(e[4], 10), - c = a | (d << 8) | (f << 16); - return e[1] + "#" + ("000000" + c.toString(16)).slice(-6) - } else { - return "" - } - }, - setFromString: function(j) { - var e, h, f, c, d = 1, - i = parseInt; - if (j === Ext.draw.Color.NONE) { - this.r = this.g = this.b = this.a = 0; - return this - } - if ((j.length === 4 || j.length === 7) && j.substr(0, 1) === "#") { - e = j.match(Ext.draw.Color.hexRe); - if (e) { - h = i(e[1], 16) >> 0; - f = i(e[2], 16) >> 0; - c = i(e[3], 16) >> 0; - if (j.length === 4) { - h += (h * 16); - f += (f * 16); - c += (c * 16) - } - } - } else { - if ((e = j.match(Ext.draw.Color.rgbToHexRe))) { - h = +e[1]; - f = +e[2]; - c = +e[3] - } else { - if ((e = j.match(Ext.draw.Color.rgbaToHexRe))) { - h = +e[1]; - f = +e[2]; - c = +e[3]; - d = +e[4] - } else { - if (Ext.draw.Color.ColorList.hasOwnProperty(j.toLowerCase())) { - return this.setFromString(Ext.draw.Color.ColorList[j.toLowerCase()]) - } - } - } - } - if (typeof h === "undefined") { - return this - } - this.r = h; - this.g = f; - this.b = c; - this.a = d; - return this - } -}, function() { - var a = new this(); - this.addStatics({ - fly: function(f, e, c, d) { - switch (arguments.length) { - case 1: - a.setFromString(f); - break; - case 3: - case 4: - a.setRGB(f, e, c, d); - break; - default: - return null - } - return a - }, - ColorList: { - aliceblue: "#f0f8ff", - antiquewhite: "#faebd7", - aqua: "#00ffff", - aquamarine: "#7fffd4", - azure: "#f0ffff", - beige: "#f5f5dc", - bisque: "#ffe4c4", - black: "#000000", - blanchedalmond: "#ffebcd", - blue: "#0000ff", - blueviolet: "#8a2be2", - brown: "#a52a2a", - burlywood: "#deb887", - cadetblue: "#5f9ea0", - chartreuse: "#7fff00", - chocolate: "#d2691e", - coral: "#ff7f50", - cornflowerblue: "#6495ed", - cornsilk: "#fff8dc", - crimson: "#dc143c", - cyan: "#00ffff", - darkblue: "#00008b", - darkcyan: "#008b8b", - darkgoldenrod: "#b8860b", - darkgray: "#a9a9a9", - darkgreen: "#006400", - darkkhaki: "#bdb76b", - darkmagenta: "#8b008b", - darkolivegreen: "#556b2f", - darkorange: "#ff8c00", - darkorchid: "#9932cc", - darkred: "#8b0000", - darksalmon: "#e9967a", - darkseagreen: "#8fbc8f", - darkslateblue: "#483d8b", - darkslategray: "#2f4f4f", - darkturquoise: "#00ced1", - darkviolet: "#9400d3", - deeppink: "#ff1493", - deepskyblue: "#00bfff", - dimgray: "#696969", - dodgerblue: "#1e90ff", - firebrick: "#b22222", - floralwhite: "#fffaf0", - forestgreen: "#228b22", - fuchsia: "#ff00ff", - gainsboro: "#dcdcdc", - ghostwhite: "#f8f8ff", - gold: "#ffd700", - goldenrod: "#daa520", - gray: "#808080", - green: "#008000", - greenyellow: "#adff2f", - honeydew: "#f0fff0", - hotpink: "#ff69b4", - indianred: "#cd5c5c", - indigo: "#4b0082", - ivory: "#fffff0", - khaki: "#f0e68c", - lavender: "#e6e6fa", - lavenderblush: "#fff0f5", - lawngreen: "#7cfc00", - lemonchiffon: "#fffacd", - lightblue: "#add8e6", - lightcoral: "#f08080", - lightcyan: "#e0ffff", - lightgoldenrodyellow: "#fafad2", - lightgray: "#d3d3d3", - lightgrey: "#d3d3d3", - lightgreen: "#90ee90", - lightpink: "#ffb6c1", - lightsalmon: "#ffa07a", - lightseagreen: "#20b2aa", - lightskyblue: "#87cefa", - lightslategray: "#778899", - lightsteelblue: "#b0c4de", - lightyellow: "#ffffe0", - lime: "#00ff00", - limegreen: "#32cd32", - linen: "#faf0e6", - magenta: "#ff00ff", - maroon: "#800000", - mediumaquamarine: "#66cdaa", - mediumblue: "#0000cd", - mediumorchid: "#ba55d3", - mediumpurple: "#9370d8", - mediumseagreen: "#3cb371", - mediumslateblue: "#7b68ee", - mediumspringgreen: "#00fa9a", - mediumturquoise: "#48d1cc", - mediumvioletred: "#c71585", - midnightblue: "#191970", - mintcream: "#f5fffa", - mistyrose: "#ffe4e1", - moccasin: "#ffe4b5", - navajowhite: "#ffdead", - navy: "#000080", - oldlace: "#fdf5e6", - olive: "#808000", - olivedrab: "#6b8e23", - orange: "#ffa500", - orangered: "#ff4500", - orchid: "#da70d6", - palegoldenrod: "#eee8aa", - palegreen: "#98fb98", - paleturquoise: "#afeeee", - palevioletred: "#d87093", - papayawhip: "#ffefd5", - peachpuff: "#ffdab9", - peru: "#cd853f", - pink: "#ffc0cb", - plum: "#dda0dd", - powderblue: "#b0e0e6", - purple: "#800080", - red: "#ff0000", - rosybrown: "#bc8f8f", - royalblue: "#4169e1", - saddlebrown: "#8b4513", - salmon: "#fa8072", - sandybrown: "#f4a460", - seagreen: "#2e8b57", - seashell: "#fff5ee", - sienna: "#a0522d", - silver: "#c0c0c0", - skyblue: "#87ceeb", - slateblue: "#6a5acd", - slategray: "#708090", - snow: "#fffafa", - springgreen: "#00ff7f", - steelblue: "#4682b4", - tan: "#d2b48c", - teal: "#008080", - thistle: "#d8bfd8", - tomato: "#ff6347", - turquoise: "#40e0d0", - violet: "#ee82ee", - wheat: "#f5deb3", - white: "#ffffff", - whitesmoke: "#f5f5f5", - yellow: "#ffff00", - yellowgreen: "#9acd32" - }, - fromHSL: function(d, c, b) { - return (new this(0, 0, 0, 0)).setHSL(d, c, b) - }, - fromHSV: function(d, c, b) { - return (new this(0, 0, 0, 0)).setHSL(d, c, b) - }, - fromString: function(b) { - return (new this(0, 0, 0, 0)).setFromString(b) - }, - create: function(b) { - if (b instanceof this) { - return b - } else { - if (Ext.isArray(b)) { - return new Ext.draw.Color(b[0], b[1], b[2], b[3]) - } else { - if (Ext.isString(b)) { - return Ext.draw.Color.fromString(b) - } else { - if (arguments.length > 2) { - return new Ext.draw.Color(arguments[0], arguments[1], arguments[2], arguments[3]) - } else { - return new Ext.draw.Color(0, 0, 0, 0) - } - } - } - } - } - }) -}); -Ext.define("Ext.draw.sprite.AnimationParser", function() { - function a(d, c, b) { - return d + (c - d) * b - } - return { - singleton: true, - attributeRe: /^url\(#([a-zA-Z\-]+)\)$/, - requires: ["Ext.draw.Color"], - color: { - parseInitial: function(c, b) { - if (Ext.isString(c)) { - c = Ext.draw.Color.create(c) - } - if (Ext.isString(b)) { - b = Ext.draw.Color.create(b) - } - if ((c instanceof Ext.draw.Color) && (b instanceof Ext.draw.Color)) { - return [ - [c.r, c.g, c.b, c.a], - [b.r, b.g, b.b, b.a] - ] - } else { - return [c || b, b || c] - } - }, - compute: function(d, c, b) { - if (!Ext.isArray(d) || !Ext.isArray(c)) { - return c || d - } else { - return [a(d[0], c[0], b), a(d[1], c[1], b), a(d[2], c[2], b), a(d[3], c[3], b)] - } - }, - serve: function(c) { - var b = Ext.draw.Color.fly(c[0], c[1], c[2], c[3]); - return b.toString() - } - }, - number: { - parse: function(b) { - return b === null ? null : +b - }, - compute: function(d, c, b) { - if (!Ext.isNumber(d) || !Ext.isNumber(c)) { - return c || d - } else { - return a(d, c, b) - } - } - }, - angle: { - parseInitial: function(c, b) { - if (b - c > Math.PI) { - b -= Math.PI * 2 - } else { - if (b - c < -Math.PI) { - b += Math.PI * 2 - } - } - return [c, b] - }, - compute: function(d, c, b) { - if (!Ext.isNumber(d) || !Ext.isNumber(c)) { - return c || d - } else { - return a(d, c, b) - } - } - }, - path: { - parseInitial: function(m, n) { - var c = m.toStripes(), - o = n.toStripes(), - e, d, k = c.length, - p = o.length, - h, f, b, g = o[p - 1], - l = [g[g.length - 2], g[g.length - 1]]; - for (e = k; e < p; e++) { - c.push(c[k - 1].slice(0)) - } - for (e = p; e < k; e++) { - o.push(l.slice(0)) - } - b = c.length; - o.path = n; - o.temp = new Ext.draw.Path(); - for (e = 0; e < b; e++) { - h = c[e]; - f = o[e]; - k = h.length; - p = f.length; - o.temp.commands.push("M"); - for (d = p; d < k; d += 6) { - f.push(l[0], l[1], l[0], l[1], l[0], l[1]) - } - g = o[o.length - 1]; - l = [g[g.length - 2], g[g.length - 1]]; - for (d = k; d < p; d += 6) { - h.push(l[0], l[1], l[0], l[1], l[0], l[1]) - } - for (e = 0; e < f.length; e++) { - f[e] -= h[e] - } - for (e = 2; e < f.length; e += 6) { - o.temp.commands.push("C") - } - } - return [c, o] - }, - compute: function(c, l, m) { - if (m >= 1) { - return l.path - } - var e = 0, - f = c.length, - d = 0, - b, k, h, n = l.temp.params, - g = 0; - for (; e < f; e++) { - k = c[e]; - h = l[e]; - b = k.length; - for (d = 0; d < b; d++) { - n[g++] = h[d] * m + k[d] - } - } - return l.temp - } - }, - data: { - compute: function(h, j, k, g) { - var m = h.length - 1, - b = j.length - 1, - e = Math.max(m, b), - d, l, c; - if (!g || g === h) { - g = [] - } - g.length = e + 1; - for (c = 0; c <= e; c++) { - d = h[Math.min(c, m)]; - l = j[Math.min(c, b)]; - if (Ext.isNumber(d)) { - if (!Ext.isNumber(l)) { - l = 0 - } - g[c] = (l - d) * k + d - } else { - g[c] = l - } - } - return g - } - }, - text: { - compute: function(d, c, b) { - return d.substr(0, Math.round(d.length * (1 - b))) + c.substr(Math.round(c.length * (1 - b))) - } - }, - limited: "number", - limited01: "number" - } -}); -(function() { - if (!Ext.global.Float32Array) { - var a = function(d) { - if (typeof d === "number") { - this.length = d - } else { - if ("length" in d) { - this.length = d.length; - for (var c = 0, b = d.length; c < b; c++) { - this[c] = +d[c] - } - } - } - }; - a.prototype = []; - Ext.global.Float32Array = a - } -})(); -Ext.define("Ext.draw.Draw", { - singleton: true, - radian: Math.PI / 180, - pi2: Math.PI * 2, - reflectFn: function(b) { - return b - }, - rad: function(a) { - return (a % 360) * this.radian - }, - degrees: function(a) { - return (a / this.radian) % 360 - }, - isBBoxIntersect: function(b, a, c) { - c = c || 0; - return (Math.max(b.x, a.x) - c > Math.min(b.x + b.width, a.x + a.width)) || (Math.max(b.y, a.y) - c > Math.min(b.y + b.height, a.y + a.height)) - }, - isPointInBBox: function(a, c, b) { - return !!b && a >= b.x && a <= (b.x + b.width) && c >= b.y && c <= (b.y + b.height) - }, - spline: function(m) { - var e, c, k = m.length, - b, h, l, f, a = 0, - g = new Float32Array(m.length), - n = new Float32Array(m.length * 3 - 2); - g[0] = 0; - g[k - 1] = 0; - for (e = 1; e < k - 1; e++) { - g[e] = (m[e + 1] + m[e - 1] - 2 * m[e]) - g[e - 1]; - a = 1 / (4 - a); - g[e] *= a - } - for (e = k - 2; e > 0; e--) { - a = 3.732050807568877 + 48.248711305964385 / (-13.928203230275537 + Math.pow(0.07179676972449123, e)); - g[e] -= g[e + 1] * a - } - f = m[0]; - b = f - g[0]; - for (e = 0, c = 0; e < k - 1; c += 3) { - l = f; - h = b; - e++; - f = m[e]; - b = f - g[e]; - n[c] = l; - n[c + 1] = (b + 2 * h) / 3; - n[c + 2] = (b * 2 + h) / 3 - } - n[c] = f; - return n - }, - getAnchors: function(e, d, i, h, t, s, o) { - o = o || 4; - var n = Math.PI, - p = n / 2, - k = Math.abs, - a = Math.sin, - b = Math.cos, - f = Math.atan, - r, q, g, j, m, l, v, u, c; - r = (i - e) / o; - q = (t - i) / o; - if ((h >= d && h >= s) || (h <= d && h <= s)) { - g = j = p - } else { - g = f((i - e) / k(h - d)); - if (d < h) { - g = n - g - } - j = f((t - i) / k(h - s)); - if (s < h) { - j = n - j - } - } - c = p - ((g + j) % (n * 2)) / 2; - if (c > p) { - c -= n - } - g += c; - j += c; - m = i - r * a(g); - l = h + r * b(g); - v = i + q * a(j); - u = h + q * b(j); - if ((h > d && l < d) || (h < d && l > d)) { - m += k(d - l) * (m - i) / (l - h); - l = d - } - if ((h > s && u < s) || (h < s && u > s)) { - v -= k(s - u) * (v - i) / (u - h); - u = s - } - return { - x1: m, - y1: l, - x2: v, - y2: u - } - }, - smooth: function(l, j, o) { - var k = l.length, - h, g, c, b, q, p, n, m, f = [], - e = [], - d, a; - for (d = 0; d < k - 1; d++) { - h = l[d]; - g = j[d]; - if (d === 0) { - n = h; - m = g; - f.push(n); - e.push(m); - if (k === 1) { - break - } - } - c = l[d + 1]; - b = j[d + 1]; - q = l[d + 2]; - p = j[d + 2]; - if (!Ext.isNumber(q + p)) { - f.push(n, c, c); - e.push(m, b, b); - break - } - a = this.getAnchors(h, g, c, b, q, p, o); - f.push(n, a.x1, c); - e.push(m, a.y1, b); - n = a.x2; - m = a.y2 - } - return { - smoothX: f, - smoothY: e - } - }, - beginUpdateIOS: Ext.os.is.iOS ? function() { - this.iosUpdateEl = Ext.getBody().createChild({ - style: "position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; background: rgba(0,0,0,0.001); z-index: 100000" - }) - } : Ext.emptyFn, - endUpdateIOS: function() { - this.iosUpdateEl = Ext.destroy(this.iosUpdateEl) - } -}); -Ext.define("Ext.draw.gradient.Gradient", { - requires: ["Ext.draw.Color"], - isGradient: true, - config: { - stops: [] - }, - applyStops: function(f) { - var e = [], - d = f.length, - c, b, a; - for (c = 0; c < d; c++) { - b = f[c]; - a = b.color; - if (!(a && a.isColor)) { - a = Ext.draw.Color.fly(a || Ext.draw.Color.NONE) - } - e.push({ - offset: Math.min(1, Math.max(0, "offset" in b ? b.offset : b.position || 0)), - color: a.toString() - }) - } - e.sort(function(h, g) { - return h.offset - g.offset - }); - return e - }, - onClassExtended: function(a, b) { - if (!b.alias && b.type) { - b.alias = "gradient." + b.type - } - }, - constructor: function(a) { - this.initConfig(a) - }, - generateGradient: Ext.emptyFn -}); -Ext.define("Ext.draw.gradient.GradientDefinition", { - singleton: true, - urlStringRe: /^url\(#([\w\-]+)\)$/, - gradients: {}, - add: function(a) { - var b = this.gradients, - c, e, d; - for (c = 0, e = a.length; c < e; c++) { - d = a[c]; - if (Ext.isString(d.id)) { - b[d.id] = d - } - } - }, - get: function(d) { - var a = this.gradients, - b = d.match(this.urlStringRe), - c; - if (b && b[1] && (c = a[b[1]])) { - return c || d - } - return d - } -}); -Ext.define("Ext.draw.sprite.AttributeParser", { - singleton: true, - attributeRe: /^url\(#([a-zA-Z\-]+)\)$/, - requires: ["Ext.draw.Color", "Ext.draw.gradient.GradientDefinition"], - "default": Ext.identityFn, - string: function(a) { - return String(a) - }, - number: function(a) { - if (Ext.isNumber(+a)) { - return a - } - }, - angle: function(a) { - if (Ext.isNumber(a)) { - a %= Math.PI * 2; - if (a < -Math.PI) { - a += Math.PI * 2 - } else { - if (a >= Math.PI) { - a -= Math.PI * 2 - } - } - return a - } - }, - data: function(a) { - if (Ext.isArray(a)) { - return a.slice() - } else { - if (a instanceof Float32Array) { - return new Float32Array(a) - } - } - }, - bool: function(a) { - return !!a - }, - color: function(a) { - if (a instanceof Ext.draw.Color) { - return a.toString() - } else { - if (a instanceof Ext.draw.gradient.Gradient) { - return a - } else { - if (!a) { - return Ext.draw.Color.NONE - } else { - if (Ext.isString(a)) { - if (a.substr(0, 3) === "url") { - a = Ext.draw.gradient.GradientDefinition.get(a); - if (Ext.isString(a)) { - return a - } - } else { - return Ext.draw.Color.fly(a).toString() - } - } - } - } - } - if (a.type === "linear") { - return Ext.create("Ext.draw.gradient.Linear", a) - } else { - if (a.type === "radial") { - return Ext.create("Ext.draw.gradient.Radial", a) - } else { - if (a.type === "pattern") { - return Ext.create("Ext.draw.gradient.Pattern", a) - } else { - return Ext.draw.Color.NONE - } - } - } - }, - limited: function(a, b) { - return function(c) { - c = +c; - return Ext.isNumber(c) ? Math.min(Math.max(c, a), b) : undefined - } - }, - limited01: function(a) { - a = +a; - return Ext.isNumber(a) ? Math.min(Math.max(a, 0), 1) : undefined - }, - enums: function() { - var d = {}, - a = Array.prototype.slice.call(arguments, 0), - b, c; - for (b = 0, c = a.length; b < c; b++) { - d[a[b]] = true - } - return function(e) { - return e in d ? e : undefined - } - } -}); -Ext.define("Ext.draw.sprite.AttributeDefinition", { - requires: ["Ext.draw.sprite.AttributeParser", "Ext.draw.sprite.AnimationParser"], - config: { - defaults: { - $value: {}, - lazy: true - }, - aliases: {}, - animationProcessors: {}, - processors: { - $value: {}, - lazy: true - }, - dirtyTriggers: {}, - triggers: {}, - updaters: {} - }, - inheritableStatics: { - processorFactoryRe: /^(\w+)\(([\w\-,]*)\)$/ - }, - spriteClass: null, - constructor: function(a) { - var b = this; - b.initConfig(a) - }, - applyDefaults: function(b, a) { - a = Ext.apply(a || {}, this.normalize(b)); - return a - }, - applyAliases: function(b, a) { - return Ext.apply(a || {}, b) - }, - applyProcessors: function(e, i) { - this.getAnimationProcessors(); - var j = i || {}, - h = Ext.draw.sprite.AttributeParser, - a = this.self.processorFactoryRe, - g = {}, - d, b, c, f; - for (b in e) { - f = e[b]; - if (typeof f === "string") { - c = f.match(a); - if (c) { - f = h[c[1]].apply(h, c[2].split(",")) - } else { - if (h[f]) { - g[b] = f; - d = true; - f = h[f] - } - } - } - j[b] = f - } - if (d) { - this.setAnimationProcessors(g) - } - return j - }, - applyAnimationProcessors: function(c, a) { - var e = Ext.draw.sprite.AnimationParser, - b, d; - if (!a) { - a = {} - } - for (b in c) { - d = c[b]; - if (d === "none") { - a[b] = null - } else { - if (Ext.isString(d) && !(b in a)) { - if (d in e) { - while (Ext.isString(e[d])) { - d = e[d] - } - a[b] = e[d] - } - } else { - if (Ext.isObject(d)) { - a[b] = d - } - } - } - } - return a - }, - updateDirtyTriggers: function(a) { - this.setTriggers(a) - }, - applyTriggers: function(b, c) { - if (!c) { - c = {} - } - for (var a in b) { - c[a] = b[a].split(",") - } - return c - }, - applyUpdaters: function(b, a) { - return Ext.apply(a || {}, b) - }, - batchedNormalize: function(f, n) { - if (!f) { - return {} - } - var j = this.getProcessors(), - d = this.getAliases(), - a = f.translation || f.translate, - o = {}, - g, h, b, e, p, c, m, l, k; - if ("rotation" in f) { - p = f.rotation - } else { - p = ("rotate" in f) ? f.rotate : undefined - } - if ("scaling" in f) { - c = f.scaling - } else { - c = ("scale" in f) ? f.scale : undefined - } - if (typeof c !== "undefined") { - if (Ext.isNumber(c)) { - o.scalingX = c; - o.scalingY = c - } else { - if ("x" in c) { - o.scalingX = c.x - } - if ("y" in c) { - o.scalingY = c.y - } - if ("centerX" in c) { - o.scalingCenterX = c.centerX - } - if ("centerY" in c) { - o.scalingCenterY = c.centerY - } - } - } - if (typeof p !== "undefined") { - if (Ext.isNumber(p)) { - p = Ext.draw.Draw.rad(p); - o.rotationRads = p - } else { - if ("rads" in p) { - o.rotationRads = p.rads - } else { - if ("degrees" in p) { - if (Ext.isArray(p.degrees)) { - o.rotationRads = Ext.Array.map(p.degrees, function(i) { - return Ext.draw.Draw.rad(i) - }) - } else { - o.rotationRads = Ext.draw.Draw.rad(p.degrees) - } - } - } - if ("centerX" in p) { - o.rotationCenterX = p.centerX - } - if ("centerY" in p) { - o.rotationCenterY = p.centerY - } - } - } - if (typeof a !== "undefined") { - if ("x" in a) { - o.translationX = a.x - } - if ("y" in a) { - o.translationY = a.y - } - } - if ("matrix" in f) { - m = Ext.draw.Matrix.create(f.matrix); - k = m.split(); - o.matrix = m; - o.rotationRads = k.rotation; - o.rotationCenterX = 0; - o.rotationCenterY = 0; - o.scalingX = k.scaleX; - o.scalingY = k.scaleY; - o.scalingCenterX = 0; - o.scalingCenterY = 0; - o.translationX = k.translateX; - o.translationY = k.translateY - } - for (b in f) { - e = f[b]; - if (typeof e === "undefined") { - continue - } else { - if (Ext.isArray(e)) { - if (b in d) { - b = d[b] - } - if (b in j) { - o[b] = []; - for (g = 0, h = e.length; g < h; g++) { - l = j[b].call(this, e[g]); - if (typeof l !== "undefined") { - o[b][g] = l - } - } - } else { - if (n) { - o[b] = e - } - } - } else { - if (b in d) { - b = d[b] - } - if (b in j) { - e = j[b].call(this, e); - if (typeof e !== "undefined") { - o[b] = e - } - } else { - if (n) { - o[b] = e - } - } - } - } - } - return o - }, - normalize: function(i, j) { - if (!i) { - return {} - } - var f = this.getProcessors(), - d = this.getAliases(), - a = i.translation || i.translate, - k = {}, - b, e, l, c, h, g; - if ("rotation" in i) { - l = i.rotation - } else { - l = ("rotate" in i) ? i.rotate : undefined - } - if ("scaling" in i) { - c = i.scaling - } else { - c = ("scale" in i) ? i.scale : undefined - } - if (a) { - if ("x" in a) { - k.translationX = a.x - } - if ("y" in a) { - k.translationY = a.y - } - } - if (typeof c !== "undefined") { - if (Ext.isNumber(c)) { - k.scalingX = c; - k.scalingY = c - } else { - if ("x" in c) { - k.scalingX = c.x - } - if ("y" in c) { - k.scalingY = c.y - } - if ("centerX" in c) { - k.scalingCenterX = c.centerX - } - if ("centerY" in c) { - k.scalingCenterY = c.centerY - } - } - } - if (typeof l !== "undefined") { - if (Ext.isNumber(l)) { - l = Ext.draw.Draw.rad(l); - k.rotationRads = l - } else { - if ("rads" in l) { - k.rotationRads = l.rads - } else { - if ("degrees" in l) { - k.rotationRads = Ext.draw.Draw.rad(l.degrees) - } - } - if ("centerX" in l) { - k.rotationCenterX = l.centerX - } - if ("centerY" in l) { - k.rotationCenterY = l.centerY - } - } - } - if ("matrix" in i) { - h = Ext.draw.Matrix.create(i.matrix); - g = h.split(); - k.matrix = h; - k.rotationRads = g.rotation; - k.rotationCenterX = 0; - k.rotationCenterY = 0; - k.scalingX = g.scaleX; - k.scalingY = g.scaleY; - k.scalingCenterX = 0; - k.scalingCenterY = 0; - k.translationX = g.translateX; - k.translationY = g.translateY - } - for (b in i) { - e = i[b]; - if (typeof e === "undefined") { - continue - } - if (b in d) { - b = d[b] - } - if (b in f) { - e = f[b].call(this, e); - if (typeof e !== "undefined") { - k[b] = e - } - } else { - if (j) { - k[b] = e - } - } - } - return k - }, - setBypassingNormalization: function(a, c, b) { - return c.pushDown(a, b) - }, - set: function(a, c, b) { - b = this.normalize(b); - return this.setBypassingNormalization(a, c, b) - } -}); -Ext.define("Ext.draw.Matrix", { - isMatrix: true, - statics: { - createAffineMatrixFromTwoPair: function(h, t, g, s, k, o, i, j) { - var v = g - h, - u = s - t, - e = i - k, - q = j - o, - d = 1 / (v * v + u * u), - p = v * e + u * q, - n = e * u - v * q, - m = -p * h - n * t, - l = n * h - p * t; - return new this(p * d, -n * d, n * d, p * d, m * d + k, l * d + o) - }, - createPanZoomFromTwoPair: function(q, e, p, c, h, s, n, g) { - if (arguments.length === 2) { - return this.createPanZoomFromTwoPair.apply(this, q.concat(e)) - } - var k = p - q, - j = c - e, - d = (q + p) * 0.5, - b = (e + c) * 0.5, - o = n - h, - a = g - s, - f = (h + n) * 0.5, - l = (s + g) * 0.5, - m = k * k + j * j, - i = o * o + a * a, - t = Math.sqrt(i / m); - return new this(t, 0, 0, t, f - t * d, l - t * b) - }, - fly: (function() { - var a = null, - b = function(c) { - a.elements = c; - return a - }; - return function(c) { - if (!a) { - a = new Ext.draw.Matrix() - } - a.elements = c; - Ext.draw.Matrix.fly = b; - return a - } - })(), - create: function(a) { - if (a instanceof this) { - return a - } - return new this(a) - } - }, - constructor: function(e, d, a, f, c, b) { - if (e && e.length === 6) { - this.elements = e.slice() - } else { - if (e !== undefined) { - this.elements = [e, d, a, f, c, b] - } else { - this.elements = [1, 0, 0, 1, 0, 0] - } - } - }, - prepend: function(a, l, h, g, m, k) { - var b = this.elements, - d = b[0], - j = b[1], - e = b[2], - c = b[3], - i = b[4], - f = b[5]; - b[0] = a * d + h * j; - b[1] = l * d + g * j; - b[2] = a * e + h * c; - b[3] = l * e + g * c; - b[4] = a * i + h * f + m; - b[5] = l * i + g * f + k; - return this - }, - prependMatrix: function(a) { - return this.prepend.apply(this, a.elements) - }, - append: function(a, l, h, g, m, k) { - var b = this.elements, - d = b[0], - j = b[1], - e = b[2], - c = b[3], - i = b[4], - f = b[5]; - b[0] = a * d + l * e; - b[1] = a * j + l * c; - b[2] = h * d + g * e; - b[3] = h * j + g * c; - b[4] = m * d + k * e + i; - b[5] = m * j + k * c + f; - return this - }, - appendMatrix: function(a) { - return this.append.apply(this, a.elements) - }, - set: function(f, e, a, g, c, b) { - var d = this.elements; - d[0] = f; - d[1] = e; - d[2] = a; - d[3] = g; - d[4] = c; - d[5] = b; - return this - }, - inverse: function(i) { - var g = this.elements, - o = g[0], - m = g[1], - l = g[2], - k = g[3], - j = g[4], - h = g[5], - n = 1 / (o * k - m * l); - o *= n; - m *= n; - l *= n; - k *= n; - if (i) { - i.set(k, -m, -l, o, l * h - k * j, m * j - o * h); - return i - } else { - return new Ext.draw.Matrix(k, -m, -l, o, l * h - k * j, m * j - o * h) - } - }, - translate: function(a, c, b) { - if (b) { - return this.prepend(1, 0, 0, 1, a, c) - } else { - return this.append(1, 0, 0, 1, a, c) - } - }, - scale: function(f, e, c, a, b) { - var d = this; - if (e == null) { - e = f - } - if (c === undefined) { - c = 0 - } - if (a === undefined) { - a = 0 - } - if (b) { - return d.prepend(f, 0, 0, e, c - c * f, a - a * e) - } else { - return d.append(f, 0, 0, e, c - c * f, a - a * e) - } - }, - rotate: function(g, e, c, b) { - var d = this, - f = Math.cos(g), - a = Math.sin(g); - e = e || 0; - c = c || 0; - if (b) { - return d.prepend(f, a, -a, f, e - f * e + c * a, c - f * c - e * a) - } else { - return d.append(f, a, -a, f, e - f * e + c * a, c - f * c - e * a) - } - }, - rotateFromVector: function(a, h, c) { - var e = this, - g = Math.sqrt(a * a + h * h), - f = a / g, - b = h / g; - if (c) { - return e.prepend(f, b, -b, f, 0, 0) - } else { - return e.append(f, b, -b, f, 0, 0) - } - }, - clone: function() { - return new Ext.draw.Matrix(this.elements) - }, - flipX: function() { - return this.append(-1, 0, 0, 1, 0, 0) - }, - flipY: function() { - return this.append(1, 0, 0, -1, 0, 0) - }, - skewX: function(a) { - return this.append(1, 0, Math.tan(a), 1, 0, 0) - }, - skewY: function(a) { - return this.append(1, Math.tan(a), 0, 1, 0, 0) - }, - shearX: function(a) { - return this.append(1, 0, a, 1, 0, 0) - }, - shearY: function(a) { - return this.append(1, a, 0, 1, 0, 0) - }, - reset: function() { - return this.set(1, 0, 0, 1, 0, 0) - }, - precisionCompensate: function(j, g) { - var c = this.elements, - f = c[0], - e = c[1], - i = c[2], - h = c[3], - d = c[4], - b = c[5], - a = e * i - f * h; - g.b = j * e / f; - g.c = j * i / h; - g.d = j; - g.xx = f / j; - g.yy = h / j; - g.dx = (b * f * i - d * f * h) / a / j; - g.dy = (d * e * h - b * f * h) / a / j - }, - precisionCompensateRect: function(j, g) { - var b = this.elements, - f = b[0], - e = b[1], - i = b[2], - h = b[3], - c = b[4], - a = b[5], - d = i / f; - g.b = j * e / f; - g.c = j * d; - g.d = j * h / f; - g.xx = f / j; - g.yy = f / j; - g.dx = (a * i - c * h) / (e * d - h) / j; - g.dy = -(a * f - c * e) / (e * d - h) / j - }, - x: function(a, c) { - var b = this.elements; - return a * b[0] + c * b[2] + b[4] - }, - y: function(a, c) { - var b = this.elements; - return a * b[1] + c * b[3] + b[5] - }, - get: function(b, a) { - return +this.elements[b + a * 2].toFixed(4) - }, - transformPoint: function(b) { - var c = this.elements, - a, d; - if (b.isPoint) { - a = b.x; - d = b.y - } else { - a = b[0]; - d = b[1] - } - return [a * c[0] + d * c[2] + c[4], a * c[1] + d * c[3] + c[5]] - }, - transformBBox: function(q, i, j) { - var b = this.elements, - d = q.x, - r = q.y, - g = q.width * 0.5, - o = q.height * 0.5, - a = b[0], - s = b[1], - n = b[2], - k = b[3], - e = d + g, - c = r + o, - p, f, m; - if (i) { - g -= i; - o -= i; - m = [Math.sqrt(b[0] * b[0] + b[2] * b[2]), Math.sqrt(b[1] * b[1] + b[3] * b[3])]; - p = Math.abs(g * a) + Math.abs(o * n) + Math.abs(m[0] * i); - f = Math.abs(g * s) + Math.abs(o * k) + Math.abs(m[1] * i) - } else { - p = Math.abs(g * a) + Math.abs(o * n); - f = Math.abs(g * s) + Math.abs(o * k) - } - if (!j) { - j = {} - } - j.x = e * a + c * n + b[4] - p; - j.y = e * s + c * k + b[5] - f; - j.width = p + p; - j.height = f + f; - return j - }, - transformList: function(e) { - var b = this.elements, - a = b[0], - h = b[2], - l = b[4], - k = b[1], - g = b[3], - j = b[5], - f = e.length, - c, d; - for (d = 0; d < f; d++) { - c = e[d]; - e[d] = [c[0] * a + c[1] * h + l, c[0] * k + c[1] * g + j] - } - return e - }, - isIdentity: function() { - var a = this.elements; - return a[0] === 1 && a[1] === 0 && a[2] === 0 && a[3] === 1 && a[4] === 0 && a[5] === 0 - }, - isEqual: function(a) { - var c = a && a.isMatrix ? a.elements : a, - b = this.elements; - return b[0] === c[0] && b[1] === c[1] && b[2] === c[2] && b[3] === c[3] && b[4] === c[4] && b[5] === c[5] - }, - equals: function(a) { - return this.isEqual(a) - }, - toArray: function() { - var a = this.elements; - return [a[0], a[2], a[4], a[1], a[3], a[5]] - }, - toVerticalArray: function() { - return this.elements.slice() - }, - toString: function() { - var a = this; - return [a.get(0, 0), a.get(0, 1), a.get(1, 0), a.get(1, 1), a.get(2, 0), a.get(2, 1)].join(",") - }, - toContext: function(a) { - a.transform.apply(a, this.elements); - return this - }, - toSvg: function() { - var a = this.elements; - return "matrix(" + a[0].toFixed(9) + "," + a[1].toFixed(9) + "," + a[2].toFixed(9) + "," + a[3].toFixed(9) + "," + a[4].toFixed(9) + "," + a[5].toFixed(9) + ")" - }, - getScaleX: function() { - var a = this.elements; - return Math.sqrt(a[0] * a[0] + a[2] * a[2]) - }, - getScaleY: function() { - var a = this.elements; - return Math.sqrt(a[1] * a[1] + a[3] * a[3]) - }, - getXX: function() { - return this.elements[0] - }, - getXY: function() { - return this.elements[1] - }, - getYX: function() { - return this.elements[2] - }, - getYY: function() { - return this.elements[3] - }, - getDX: function() { - return this.elements[4] - }, - getDY: function() { - return this.elements[5] - }, - split: function() { - var b = this.elements, - d = b[0], - c = b[1], - e = b[3], - a = { - translateX: b[4], - translateY: b[5] - }; - a.rotate = a.rotation = Math.atan2(c, d); - a.scaleX = d / Math.cos(a.rotate); - a.scaleY = e / d * a.scaleX; - return a - } -}, function() { - function b(e, c, d) { - e[c] = { - get: function() { - return this.elements[d] - }, - set: function(f) { - this.elements[d] = f - } - } - } - if (Object.defineProperties) { - var a = {}; - b(a, "a", 0); - b(a, "b", 1); - b(a, "c", 2); - b(a, "d", 3); - b(a, "e", 4); - b(a, "f", 5); - Object.defineProperties(this.prototype, a) - } - this.prototype.multiply = this.prototype.appendMatrix -}); -Ext.define("Ext.draw.modifier.Modifier", { - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - previous: null, - next: null, - sprite: null - }, - constructor: function(a) { - this.mixins.observable.constructor.call(this, a) - }, - updateNext: function(a) { - if (a) { - a.setPrevious(this) - } - }, - updatePrevious: function(a) { - if (a) { - a.setNext(this) - } - }, - prepareAttributes: function(a) { - if (this._previous) { - this._previous.prepareAttributes(a) - } - }, - popUp: function(a, b) { - if (this._next) { - this._next.popUp(a, b) - } else { - Ext.apply(a, b) - } - }, - pushDown: function(a, c) { - if (this._previous) { - return this._previous.pushDown(a, c) - } else { - for (var b in c) { - if (c[b] === a[b]) { - delete c[b] - } - } - return c - } - } -}); -Ext.define("Ext.draw.modifier.Target", { - requires: ["Ext.draw.Matrix"], - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.target", - statics: { - uniqueId: 0 - }, - prepareAttributes: function(a) { - var b = this.getPrevious(); - if (b) { - b.prepareAttributes(a) - } - a.attributeId = "attribute-" + Ext.draw.modifier.Target.uniqueId++; - if (!a.hasOwnProperty("canvasAttributes")) { - a.bbox = { - plain: { - dirty: true - }, - transform: { - dirty: true - } - }; - a.dirty = true; - a.pendingUpdaters = {}; - a.canvasAttributes = {}; - a.matrix = new Ext.draw.Matrix(); - a.inverseMatrix = new Ext.draw.Matrix() - } - }, - applyChanges: function(f, k) { - Ext.apply(f, k); - var l = this.getSprite(), - o = f.pendingUpdaters, - h = l.self.def.getTriggers(), - p, a, m, b, e, n, d, c, g; - for (b in k) { - e = true; - if ((p = h[b])) { - l.scheduleUpdaters(f, p, [b]) - } - if (f.template && k.removeFromInstance && k.removeFromInstance[b]) { - delete f[b] - } - } - if (!e) { - return - } - if (o.canvas) { - n = o.canvas; - delete o.canvas; - for (d = 0, g = n.length; d < g; d++) { - b = n[d]; - f.canvasAttributes[b] = f[b] - } - } - if (f.hasOwnProperty("children")) { - a = f.children; - for (d = 0, g = a.length; d < g; d++) { - m = a[d]; - Ext.apply(m.pendingUpdaters, o); - if (n) { - for (c = 0; c < n.length; c++) { - b = n[c]; - m.canvasAttributes[b] = m[b] - } - } - l.callUpdaters(m) - } - } - l.setDirty(true); - l.callUpdaters(f) - }, - popUp: function(a, b) { - this.applyChanges(a, b) - }, - pushDown: function(a, b) { - var c = this.getPrevious(); - if (c) { - b = c.pushDown(a, b) - } - this.applyChanges(a, b); - return b - } -}); -Ext.define("Ext.draw.TimingFunctions", function() { - var g = Math.pow, - j = Math.sin, - m = Math.cos, - l = Math.sqrt, - e = Math.PI, - b = ["quad", "cube", "quart", "quint"], - c = { - pow: function(o, i) { - return g(o, i || 6) - }, - expo: function(i) { - return g(2, 8 * (i - 1)) - }, - circ: function(i) { - return 1 - l(1 - i * i) - }, - sine: function(i) { - return 1 - j((1 - i) * e / 2) - }, - back: function(i, o) { - o = o || 1.616; - return i * i * ((o + 1) * i - o) - }, - bounce: function(q) { - for (var o = 0, i = 1; 1; o += i, i /= 2) { - if (q >= (7 - 4 * o) / 11) { - return i * i - g((11 - 6 * o - 11 * q) / 4, 2) - } - } - }, - elastic: function(o, i) { - return g(2, 10 * --o) * m(20 * o * e * (i || 1) / 3) - } - }, - k = {}, - a, f, d; - - function h(i) { - return function(o) { - return g(o, i) - } - } - - function n(i, o) { - k[i + "In"] = function(p) { - return o(p) - }; - k[i + "Out"] = function(p) { - return 1 - o(1 - p) - }; - k[i + "InOut"] = function(p) { - return (p <= 0.5) ? o(2 * p) / 2 : (2 - o(2 * (1 - p))) / 2 - } - } - for (d = 0, f = b.length; d < f; ++d) { - c[b[d]] = h(d + 2) - } - for (a in c) { - n(a, c[a]) - } - k.linear = Ext.identityFn; - k.easeIn = k.quadIn; - k.easeOut = k.quadOut; - k.easeInOut = k.quadInOut; - return { - singleton: true, - easingMap: k - } -}, function(a) { - Ext.apply(a, a.easingMap) -}); -Ext.define("Ext.draw.Animator", { - uses: ["Ext.draw.Draw"], - singleton: true, - frameCallbacks: {}, - frameCallbackId: 0, - scheduled: 0, - frameStartTimeOffset: Ext.now(), - animations: [], - running: false, - animationTime: function() { - return Ext.AnimationQueue.frameStartTime - this.frameStartTimeOffset - }, - add: function(b) { - var a = this; - if (!a.contains(b)) { - a.animations.push(b); - a.ignite(); - if ("fireEvent" in b) { - b.fireEvent("animationstart", b) - } - } - }, - remove: function(d) { - var c = this, - e = c.animations, - b = 0, - a = e.length; - for (; b < a; ++b) { - if (e[b] === d) { - e.splice(b, 1); - if ("fireEvent" in d) { - d.fireEvent("animationend", d) - } - return - } - } - }, - contains: function(a) { - return Ext.Array.indexOf(this.animations, a) > -1 - }, - empty: function() { - return this.animations.length === 0 - }, - step: function(d) { - var c = this, - f = c.animations, - e, a = 0, - b = f.length; - for (; a < b; a++) { - e = f[a]; - e.step(d); - if (!e.animating) { - f.splice(a, 1); - a--; - b--; - if (e.fireEvent) { - e.fireEvent("animationend", e) - } - } - } - }, - schedule: function(c, a) { - a = a || this; - var b = "frameCallback" + (this.frameCallbackId++); - if (Ext.isString(c)) { - c = a[c] - } - Ext.draw.Animator.frameCallbacks[b] = { - fn: c, - scope: a, - once: true - }; - this.scheduled++; - Ext.draw.Animator.ignite(); - return b - }, - scheduleIf: function(e, b) { - b = b || this; - var c = Ext.draw.Animator.frameCallbacks, - a, d; - if (Ext.isString(e)) { - e = b[e] - } - for (d in c) { - a = c[d]; - if (a.once && a.fn === e && a.scope === b) { - return null - } - } - return this.schedule(e, b) - }, - cancel: function(a) { - if (Ext.draw.Animator.frameCallbacks[a] && Ext.draw.Animator.frameCallbacks[a].once) { - this.scheduled--; - delete Ext.draw.Animator.frameCallbacks[a] - } - }, - addFrameCallback: function(c, a) { - a = a || this; - if (Ext.isString(c)) { - c = a[c] - } - var b = "frameCallback" + (this.frameCallbackId++); - Ext.draw.Animator.frameCallbacks[b] = { - fn: c, - scope: a - }; - return b - }, - removeFrameCallback: function(a) { - delete Ext.draw.Animator.frameCallbacks[a] - }, - fireFrameCallbacks: function() { - var c = this.frameCallbacks, - d, b, a; - for (d in c) { - a = c[d]; - b = a.fn; - if (Ext.isString(b)) { - b = a.scope[b] - } - b.call(a.scope); - if (c[d] && a.once) { - this.scheduled--; - delete c[d] - } - } - }, - handleFrame: function() { - this.step(this.animationTime()); - this.fireFrameCallbacks(); - if (!this.scheduled && this.empty()) { - Ext.AnimationQueue.stop(this.handleFrame, this); - this.running = false; - Ext.draw.Draw.endUpdateIOS() - } - }, - ignite: function() { - if (!this.running) { - this.running = true; - Ext.AnimationQueue.start(this.handleFrame, this); - Ext.draw.Draw.beginUpdateIOS() - } - } -}); -Ext.define("Ext.draw.modifier.Animation", { - requires: ["Ext.draw.TimingFunctions", "Ext.draw.Animator"], - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.animation", - config: { - easing: Ext.identityFn, - duration: 0, - customEasings: {}, - customDurations: {}, - customDuration: null - }, - constructor: function(a) { - var b = this; - b.anyAnimation = b.anySpecialAnimations = false; - b.animating = 0; - b.animatingPool = []; - b.callParent([a]) - }, - prepareAttributes: function(a) { - if (!a.hasOwnProperty("timers")) { - a.animating = false; - a.timers = {}; - a.animationOriginal = Ext.Object.chain(a); - a.animationOriginal.prototype = a - } - if (this._previous) { - this._previous.prepareAttributes(a.animationOriginal) - } - }, - updateSprite: function(a) { - this.setConfig(a.config.fx) - }, - updateDuration: function(a) { - this.anyAnimation = a > 0 - }, - applyEasing: function(a) { - if (typeof a === "string") { - a = Ext.draw.TimingFunctions.easingMap[a] - } - return a - }, - applyCustomEasings: function(a, e) { - e = e || {}; - var g, d, b, h, c, f; - for (d in a) { - g = true; - h = a[d]; - b = d.split(","); - if (typeof h === "string") { - h = Ext.draw.TimingFunctions.easingMap[h] - } - for (c = 0, f = b.length; c < f; c++) { - e[b[c]] = h - } - } - if (g) { - this.anySpecialAnimations = g - } - return e - }, - setEasingOn: function(a, e) { - a = Ext.Array.from(a).slice(); - var c = {}, - d = a.length, - b = 0; - for (; b < d; b++) { - c[a[b]] = e - } - this.setCustomEasings(c) - }, - clearEasingOn: function(a) { - a = Ext.Array.from(a, true); - var b = 0, - c = a.length; - for (; b < c; b++) { - delete this._customEasings[a[b]] - } - }, - applyCustomDurations: function(g, h) { - h = h || {}; - var e, c, f, a, b, d; - for (c in g) { - e = true; - f = g[c]; - a = c.split(","); - for (b = 0, d = a.length; b < d; b++) { - h[a[b]] = f - } - } - if (e) { - this.anySpecialAnimations = e - } - return h - }, - applyCustomDuration: function(a, b) { - if (a) { - this.getCustomDurations(); - this.setCustomDurations(a) - } - }, - setDurationOn: function(b, e) { - b = Ext.Array.from(b).slice(); - var a = {}, - c = 0, - d = b.length; - for (; c < d; c++) { - a[b[c]] = e - } - this.setCustomDurations(a) - }, - clearDurationOn: function(a) { - a = Ext.Array.from(a, true); - var b = 0, - c = a.length; - for (; b < c; b++) { - delete this._customDurations[a[b]] - } - }, - setAnimating: function(a, b) { - var e = this, - d = e.animatingPool; - if (a.animating !== b) { - a.animating = b; - if (b) { - d.push(a); - if (e.animating === 0) { - Ext.draw.Animator.add(e) - } - e.animating++ - } else { - for (var c = d.length; c--;) { - if (d[c] === a) { - d.splice(c, 1) - } - } - e.animating = d.length - } - } - }, - setAttrs: function(r, t) { - var s = this, - m = r.timers, - h = s._sprite.self.def._animationProcessors, - f = s._easing, - e = s._duration, - j = s._customDurations, - i = s._customEasings, - g = s.anySpecialAnimations, - n = s.anyAnimation || g, - o = r.animationOriginal, - d = false, - k, u, l, p, c, q, a; - if (!n) { - for (u in t) { - if (r[u] === t[u]) { - delete t[u] - } else { - r[u] = t[u] - } - delete o[u]; - delete m[u] - } - return t - } else { - for (u in t) { - l = t[u]; - p = r[u]; - if (l !== p && p !== undefined && p !== null && (c = h[u])) { - q = f; - a = e; - if (g) { - if (u in i) { - q = i[u] - } - if (u in j) { - a = j[u] - } - } - if (p && p.isGradient || l && l.isGradient) { - a = 0 - } - if (a) { - if (!m[u]) { - m[u] = {} - } - k = m[u]; - k.start = 0; - k.easing = q; - k.duration = a; - k.compute = c.compute; - k.serve = c.serve || Ext.identityFn; - k.remove = t.removeFromInstance && t.removeFromInstance[u]; - if (c.parseInitial) { - var b = c.parseInitial(p, l); - k.source = b[0]; - k.target = b[1] - } else { - if (c.parse) { - k.source = c.parse(p); - k.target = c.parse(l) - } else { - k.source = p; - k.target = l - } - } - o[u] = l; - delete t[u]; - d = true; - continue - } else { - delete o[u] - } - } else { - delete o[u] - } - delete m[u] - } - } - if (d && !r.animating) { - s.setAnimating(r, true) - } - return t - }, - updateAttributes: function(g) { - if (!g.animating) { - return {} - } - var h = {}, - e = false, - d = g.timers, - f = g.animationOriginal, - c = Ext.draw.Animator.animationTime(), - a, b, i; - if (g.lastUpdate === c) { - return null - } - for (a in d) { - b = d[a]; - if (!b.start) { - b.start = c; - i = 0 - } else { - i = (c - b.start) / b.duration - } - if (i >= 1) { - h[a] = f[a]; - delete f[a]; - if (d[a].remove) { - h.removeFromInstance = h.removeFromInstance || {}; - h.removeFromInstance[a] = true - } - delete d[a] - } else { - h[a] = b.serve(b.compute(b.source, b.target, b.easing(i), g[a])); - e = true - } - } - g.lastUpdate = c; - this.setAnimating(g, e); - return h - }, - pushDown: function(a, b) { - b = this.callParent([a.animationOriginal, b]); - return this.setAttrs(a, b) - }, - popUp: function(a, b) { - a = a.prototype; - b = this.setAttrs(a, b); - if (this._next) { - return this._next.popUp(a, b) - } else { - return Ext.apply(a, b) - } - }, - step: function(g) { - var f = this, - c = f.animatingPool.slice(), - e = c.length, - b = 0, - a, d; - for (; b < e; b++) { - a = c[b]; - d = f.updateAttributes(a); - if (d && f._next) { - f._next.popUp(a, d) - } - } - }, - stop: function() { - this.step(); - var d = this, - b = d.animatingPool, - a, c; - for (a = 0, c = b.length; a < c; a++) { - b[a].animating = false - } - d.animatingPool.length = 0; - d.animating = 0; - Ext.draw.Animator.remove(d) - }, - destroy: function() { - this.animatingPool.length = 0; - this.animating = 0; - this.callParent() - } -}); -Ext.define("Ext.draw.modifier.Highlight", { - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.highlight", - config: { - enabled: false, - highlightStyle: null - }, - preFx: true, - applyHighlightStyle: function(b, a) { - a = a || {}; - if (this.getSprite()) { - Ext.apply(a, this.getSprite().self.def.normalize(b)) - } else { - Ext.apply(a, b) - } - return a - }, - prepareAttributes: function(a) { - if (!a.hasOwnProperty("highlightOriginal")) { - a.highlighted = false; - a.highlightOriginal = Ext.Object.chain(a); - a.highlightOriginal.prototype = a; - a.highlightOriginal.removeFromInstance = {} - } - if (this._previous) { - this._previous.prepareAttributes(a.highlightOriginal) - } - }, - updateSprite: function(b, a) { - if (b) { - if (this.getHighlightStyle()) { - this._highlightStyle = b.self.def.normalize(this.getHighlightStyle()) - } - this.setHighlightStyle(b.config.highlight) - } - b.self.def.setConfig({ - defaults: { - highlighted: false - }, - processors: { - highlighted: "bool" - } - }); - this.setSprite(b) - }, - filterChanges: function(a, d) { - var e = this, - f = a.highlightOriginal, - c = e.getHighlightStyle(), - b; - if (a.highlighted) { - for (b in d) { - if (c.hasOwnProperty(b)) { - f[b] = d[b]; - delete d[b] - } - } - } - for (b in d) { - if (b !== "highlighted" && f[b] === d[b]) { - delete d[b] - } - } - return d - }, - pushDown: function(e, g) { - var f = this.getHighlightStyle(), - c = e.highlightOriginal, - i = c.removeFromInstance, - d, a, h, b; - if (g.hasOwnProperty("highlighted")) { - d = g.highlighted; - delete g.highlighted; - if (this._previous) { - g = this._previous.pushDown(c, g) - } - g = this.filterChanges(e, g); - if (d !== e.highlighted) { - if (d) { - for (a in f) { - if (a in g) { - c[a] = g[a] - } else { - h = e.template && e.template.ownAttr; - if (h && !e.prototype.hasOwnProperty(a)) { - i[a] = true; - c[a] = h.animationOriginal[a] - } else { - b = c.timers[a]; - if (b && b.remove) { - i[a] = true - } - c[a] = e[a] - } - } - if (c[a] !== f[a]) { - g[a] = f[a] - } - } - } else { - for (a in f) { - if (!(a in g)) { - g[a] = c[a] - } - delete c[a] - } - g.removeFromInstance = g.removeFromInstance || {}; - Ext.apply(g.removeFromInstance, i); - c.removeFromInstance = {} - } - g.highlighted = d - } - } else { - if (this._previous) { - g = this._previous.pushDown(c, g) - } - g = this.filterChanges(e, g) - } - return g - }, - popUp: function(a, b) { - b = this.filterChanges(a, b); - Ext.draw.modifier.Modifier.prototype.popUp.call(this, a, b) - } -}); -Ext.define("Ext.draw.sprite.Sprite", { - alias: "sprite.sprite", - mixins: { - observable: "Ext.mixin.Observable" - }, - requires: ["Ext.draw.Draw", "Ext.draw.gradient.Gradient", "Ext.draw.sprite.AttributeDefinition", "Ext.draw.modifier.Target", "Ext.draw.modifier.Animation", "Ext.draw.modifier.Highlight"], - isSprite: true, - statics: { - defaultHitTestOptions: { - fill: true, - stroke: true - } - }, - inheritableStatics: { - def: { - processors: { - strokeStyle: "color", - fillStyle: "color", - strokeOpacity: "limited01", - fillOpacity: "limited01", - lineWidth: "number", - lineCap: "enums(butt,round,square)", - lineJoin: "enums(round,bevel,miter)", - lineDash: "data", - lineDashOffset: "number", - miterLimit: "number", - shadowColor: "color", - shadowOffsetX: "number", - shadowOffsetY: "number", - shadowBlur: "number", - globalAlpha: "limited01", - globalCompositeOperation: "enums(source-over,destination-over,source-in,destination-in,source-out,destination-out,source-atop,destination-atop,lighter,xor,copy)", - hidden: "bool", - transformFillStroke: "bool", - zIndex: "number", - translationX: "number", - translationY: "number", - rotationRads: "number", - rotationCenterX: "number", - rotationCenterY: "number", - scalingX: "number", - scalingY: "number", - scalingCenterX: "number", - scalingCenterY: "number", - constrainGradients: "bool" - }, - aliases: { - stroke: "strokeStyle", - fill: "fillStyle", - color: "fillStyle", - "stroke-width": "lineWidth", - "stroke-linecap": "lineCap", - "stroke-linejoin": "lineJoin", - "stroke-miterlimit": "miterLimit", - "text-anchor": "textAlign", - opacity: "globalAlpha", - translateX: "translationX", - translateY: "translationY", - rotateRads: "rotationRads", - rotateCenterX: "rotationCenterX", - rotateCenterY: "rotationCenterY", - scaleX: "scalingX", - scaleY: "scalingY", - scaleCenterX: "scalingCenterX", - scaleCenterY: "scalingCenterY" - }, - defaults: { - hidden: false, - zIndex: 0, - strokeStyle: "none", - fillStyle: "none", - lineWidth: 1, - lineDash: [], - lineDashOffset: 0, - lineCap: "butt", - lineJoin: "miter", - miterLimit: 10, - shadowColor: "none", - shadowOffsetX: 0, - shadowOffsetY: 0, - shadowBlur: 0, - globalAlpha: 1, - strokeOpacity: 1, - fillOpacity: 1, - transformFillStroke: false, - translationX: 0, - translationY: 0, - rotationRads: 0, - rotationCenterX: null, - rotationCenterY: null, - scalingX: 1, - scalingY: 1, - scalingCenterX: null, - scalingCenterY: null, - constrainGradients: false - }, - triggers: { - zIndex: "zIndex", - globalAlpha: "canvas", - globalCompositeOperation: "canvas", - transformFillStroke: "canvas", - strokeStyle: "canvas", - fillStyle: "canvas", - strokeOpacity: "canvas", - fillOpacity: "canvas", - lineWidth: "canvas", - lineCap: "canvas", - lineJoin: "canvas", - lineDash: "canvas", - lineDashOffset: "canvas", - miterLimit: "canvas", - shadowColor: "canvas", - shadowOffsetX: "canvas", - shadowOffsetY: "canvas", - shadowBlur: "canvas", - translationX: "transform", - translationY: "transform", - rotationRads: "transform", - rotationCenterX: "transform", - rotationCenterY: "transform", - scalingX: "transform", - scalingY: "transform", - scalingCenterX: "transform", - scalingCenterY: "transform", - constrainGradients: "canvas" - }, - updaters: { - bbox: "bboxUpdater", - zIndex: function(a) { - a.dirtyZIndex = true - }, - transform: function(a) { - a.dirtyTransform = true; - a.bbox.transform.dirty = true - } - } - } - }, - config: { - parent: null, - surface: null - }, - onClassExtended: function(d, c) { - var b = d.superclass.self.def.initialConfig, - e = c.inheritableStatics && c.inheritableStatics.def, - a; - if (e) { - a = Ext.Object.merge({}, b, e); - d.def = new Ext.draw.sprite.AttributeDefinition(a); - delete c.inheritableStatics.def - } else { - d.def = new Ext.draw.sprite.AttributeDefinition(b) - } - d.def.spriteClass = d - }, - constructor: function(b) { - var d = this, - c = d.self.def, - e = c.getDefaults(), - a; - b = Ext.isObject(b) ? b : {}; - d.id = b.id || Ext.id(null, "ext-sprite-"); - d.attr = {}; - d.mixins.observable.constructor.apply(d, arguments); - a = Ext.Array.from(b.modifiers, true); - d.prepareModifiers(a); - d.initializeAttributes(); - d.setAttributes(e, true); - d.setAttributes(b) - }, - getDirty: function() { - return this.attr.dirty - }, - setDirty: function(b) { - this.attr.dirty = b; - if (b) { - var a = this.getParent(); - if (a) { - a.setDirty(true) - } - } - }, - addModifier: function(a, b) { - var c = this; - if (!(a instanceof Ext.draw.modifier.Modifier)) { - a = Ext.factory(a, null, null, "modifier") - } - a.setSprite(c); - if (a.preFx || a.config && a.config.preFx) { - if (c.fx.getPrevious()) { - c.fx.getPrevious().setNext(a) - } - a.setNext(c.fx) - } else { - c.topModifier.getPrevious().setNext(a); - a.setNext(c.topModifier) - } - if (b) { - c.initializeAttributes() - } - return a - }, - prepareModifiers: function(d) { - var c = this, - a, b; - c.topModifier = new Ext.draw.modifier.Target({ - sprite: c - }); - c.fx = new Ext.draw.modifier.Animation({ - sprite: c - }); - c.fx.setNext(c.topModifier); - for (a = 0, b = d.length; a < b; a++) { - c.addModifier(d[a], false) - } - }, - getAnimation: function() { - return this.fx - }, - setAnimation: function(a) { - this.fx.setConfig(a) - }, - initializeAttributes: function() { - this.topModifier.prepareAttributes(this.attr) - }, - callUpdaters: function(d) { - var e = this, - h = d.pendingUpdaters, - i = e.self.def.getUpdaters(), - c = false, - a = false, - b, g, f; - e.callUpdaters = Ext.emptyFn; - do { - c = false; - for (g in h) { - c = true; - b = h[g]; - delete h[g]; - f = i[g]; - if (typeof f === "string") { - f = e[f] - } - if (f) { - f.call(e, d, b) - } - } - a = a || c - } while (c); - delete e.callUpdaters; - if (a) { - e.setDirty(true) - } - }, - scheduleUpdaters: function(a, e, c) { - var f; - if (c) { - for (var b = 0, d = e.length; b < d; b++) { - f = e[b]; - this.scheduleUpdater(a, f, c) - } - } else { - for (f in e) { - c = e[f]; - this.scheduleUpdater(a, f, c) - } - } - }, - scheduleUpdater: function(a, c, b) { - b = b || []; - var d = a.pendingUpdaters; - if (c in d) { - if (b.length) { - d[c] = Ext.Array.merge(d[c], b) - } - } else { - d[c] = b - } - }, - setAttributes: function(d, g, c) { - var a = this.attr, - b, e, f; - if (g) { - if (c) { - this.topModifier.pushDown(a, d) - } else { - f = {}; - for (b in d) { - e = d[b]; - if (e !== a[b]) { - f[b] = e - } - } - this.topModifier.pushDown(a, f) - } - } else { - this.topModifier.pushDown(a, this.self.def.normalize(d)) - } - }, - setAttributesBypassingNormalization: function(b, a) { - return this.setAttributes(b, true, a) - }, - bboxUpdater: function(b) { - var c = b.rotationRads !== 0, - a = b.scalingX !== 1 || b.scalingY !== 1, - d = b.rotationCenterX === null || b.rotationCenterY === null, - e = b.scalingCenterX === null || b.scalingCenterY === null; - b.bbox.plain.dirty = true; - b.bbox.transform.dirty = true; - if (c && d || a && e) { - this.scheduleUpdater(b, "transform") - } - }, - getBBox: function(d) { - var e = this, - a = e.attr, - f = a.bbox, - c = f.plain, - b = f.transform; - if (c.dirty) { - e.updatePlainBBox(c); - c.dirty = false - } - if (!d) { - e.applyTransformations(); - if (b.dirty) { - e.updateTransformedBBox(b, c); - b.dirty = false - } - return b - } - return c - }, - updatePlainBBox: Ext.emptyFn, - updateTransformedBBox: function(a, b) { - this.attr.matrix.transformBBox(b, 0, a) - }, - getBBoxCenter: function(a) { - var b = this.getBBox(a); - if (b) { - return [b.x + b.width * 0.5, b.y + b.height * 0.5] - } else { - return [0, 0] - } - }, - hide: function() { - this.attr.hidden = true; - this.setDirty(true); - return this - }, - show: function() { - this.attr.hidden = false; - this.setDirty(true); - return this - }, - useAttributes: function(i, f) { - this.applyTransformations(); - var d = this.attr, - h = d.canvasAttributes, - e = h.strokeStyle, - g = h.fillStyle, - b = h.lineDash, - c = h.lineDashOffset, - a; - if (e) { - if (e.isGradient) { - i.strokeStyle = "black"; - i.strokeGradient = e - } else { - i.strokeGradient = false - } - } - if (g) { - if (g.isGradient) { - i.fillStyle = "black"; - i.fillGradient = g - } else { - i.fillGradient = false - } - } - if (b) { - i.setLineDash(b) - } - if (Ext.isNumber(c + i.lineDashOffset)) { - i.lineDashOffset = c - } - for (a in h) { - if (h[a] !== undefined && h[a] !== i[a]) { - i[a] = h[a] - } - } - this.setGradientBBox(i, f) - }, - setGradientBBox: function(b, c) { - var a = this.attr; - if (a.constrainGradients) { - b.setGradientBBox({ - x: c[0], - y: c[1], - width: c[2], - height: c[3] - }) - } else { - b.setGradientBBox(this.getBBox(a.transformFillStroke)) - } - }, - applyTransformations: function(b) { - if (!b && !this.attr.dirtyTransform) { - return - } - var r = this, - k = r.attr, - p = r.getBBoxCenter(true), - g = p[0], - f = p[1], - q = k.translationX, - o = k.translationY, - j = k.scalingX, - i = k.scalingY === null ? k.scalingX : k.scalingY, - m = k.scalingCenterX === null ? g : k.scalingCenterX, - l = k.scalingCenterY === null ? f : k.scalingCenterY, - s = k.rotationRads, - e = k.rotationCenterX === null ? g : k.rotationCenterX, - d = k.rotationCenterY === null ? f : k.rotationCenterY, - c = Math.cos(s), - a = Math.sin(s), - n, h; - if (j === 1 && i === 1) { - m = 0; - l = 0 - } - if (s === 0) { - e = 0; - d = 0 - } - n = m * (1 - j) - e; - h = l * (1 - i) - d; - k.matrix.elements = [c * j, a * j, -a * i, c * i, c * n - a * h + e + q, a * n + c * h + d + o]; - k.matrix.inverse(k.inverseMatrix); - k.dirtyTransform = false; - k.bbox.transform.dirty = true - }, - transform: function(b, c) { - var a = this.attr, - e = a.matrix, - d; - if (b && b.isMatrix) { - d = b.elements - } else { - d = b - } - e.prepend.apply(e, d.slice()); - e.inverse(a.inverseMatrix); - if (c) { - this.updateTransformAttributes() - } - a.dirtyTransform = false; - a.bbox.transform.dirty = true; - this.setDirty(true); - return this - }, - updateTransformAttributes: function() { - var a = this.attr, - b = a.matrix.split(); - a.rotationRads = b.rotate; - a.rotationCenterX = 0; - a.rotationCenterY = 0; - a.scalingX = b.scaleX; - a.scalingY = b.scaleY; - a.scalingCenterX = 0; - a.scalingCenterY = 0; - a.translationX = b.translateX; - a.translationY = b.translateY - }, - resetTransform: function(b) { - var a = this.attr; - a.matrix.reset(); - a.inverseMatrix.reset(); - if (!b) { - this.updateTransformAttributes() - } - a.dirtyTransform = false; - a.bbox.transform.dirty = true; - this.setDirty(true); - return this - }, - setTransform: function(a, b) { - this.resetTransform(true); - this.transform.call(this, a, b); - return this - }, - preRender: Ext.emptyFn, - render: Ext.emptyFn, - hitTest: function(b, c) { - if (this.isVisible()) { - var a = b[0], - f = b[1], - e = this.getBBox(), - d = e && a >= e.x && a <= (e.x + e.width) && f >= e.y && f <= (e.y + e.height); - if (d) { - return { - sprite: this - } - } - } - return null - }, - isVisible: function() { - var e = this.attr, - f = this.getParent(), - g = f && (f.isSurface || f.isVisible()), - d = g && !e.hidden && e.globalAlpha, - b = Ext.draw.Color.NONE, - a = Ext.draw.Color.RGBA_NONE, - c = e.fillOpacity && e.fillStyle !== b && e.fillStyle !== a, - i = e.strokeOpacity && e.strokeStyle !== b && e.strokeStyle !== a, - h = d && (c || i); - return !!h - }, - repaint: function() { - var a = this.getSurface(); - if (a) { - a.renderFrame() - } - }, - remove: function() { - var a = this.getSurface(); - if (a && a.isSurface) { - return a.remove(this) - } - return null - }, - destroy: function() { - var b = this, - a = b.topModifier, - c; - while (a) { - c = a; - a = a.getPrevious(); - c.destroy() - } - delete b.attr; - b.remove(); - if (b.fireEvent("beforedestroy", b) !== false) { - b.fireEvent("destroy", b) - } - b.callParent() - } -}, function() { - this.def = new Ext.draw.sprite.AttributeDefinition(this.def); - this.def.spriteClass = this -}); -Ext.define("Ext.draw.Path", { - requires: ["Ext.draw.Draw"], - statics: { - pathRe: /,?([achlmqrstvxz]),?/gi, - pathRe2: /-/gi, - pathSplitRe: /\s|,/g - }, - svgString: "", - constructor: function(a) { - var b = this; - b.commands = []; - b.params = []; - b.cursor = null; - b.startX = 0; - b.startY = 0; - if (a) { - b.fromSvgString(a) - } - }, - clear: function() { - var a = this; - a.params.length = 0; - a.commands.length = 0; - a.cursor = null; - a.startX = 0; - a.startY = 0; - a.dirt() - }, - dirt: function() { - this.svgString = "" - }, - moveTo: function(a, c) { - var b = this; - if (!b.cursor) { - b.cursor = [a, c] - } - b.params.push(a, c); - b.commands.push("M"); - b.startX = a; - b.startY = c; - b.cursor[0] = a; - b.cursor[1] = c; - b.dirt() - }, - lineTo: function(a, c) { - var b = this; - if (!b.cursor) { - b.cursor = [a, c]; - b.params.push(a, c); - b.commands.push("M") - } else { - b.params.push(a, c); - b.commands.push("L") - } - b.cursor[0] = a; - b.cursor[1] = c; - b.dirt() - }, - bezierCurveTo: function(c, e, b, d, a, g) { - var f = this; - if (!f.cursor) { - f.moveTo(c, e) - } - f.params.push(c, e, b, d, a, g); - f.commands.push("C"); - f.cursor[0] = a; - f.cursor[1] = g; - f.dirt() - }, - quadraticCurveTo: function(b, e, a, d) { - var c = this; - if (!c.cursor) { - c.moveTo(b, e) - } - c.bezierCurveTo((2 * b + c.cursor[0]) / 3, (2 * e + c.cursor[1]) / 3, (2 * b + a) / 3, (2 * e + d) / 3, a, d) - }, - closePath: function() { - var a = this; - if (a.cursor) { - a.cursor = null; - a.commands.push("Z"); - a.dirt() - } - }, - arcTo: function(A, f, z, d, j, i, v) { - var E = this; - if (i === undefined) { - i = j - } - if (v === undefined) { - v = 0 - } - if (!E.cursor) { - E.moveTo(A, f); - return - } - if (j === 0 || i === 0) { - E.lineTo(A, f); - return - } - z -= A; - d -= f; - var B = E.cursor[0] - A, - g = E.cursor[1] - f, - C = z * g - d * B, - b, a, l, r, k, q, x = Math.sqrt(B * B + g * g), - u = Math.sqrt(z * z + d * d), - t, e, c; - if (C === 0) { - E.lineTo(A, f); - return - } - if (i !== j) { - b = Math.cos(v); - a = Math.sin(v); - l = b / j; - r = a / i; - k = -a / j; - q = b / i; - var D = l * B + r * g; - g = k * B + q * g; - B = D; - D = l * z + r * d; - d = k * z + q * d; - z = D - } else { - B /= j; - g /= i; - z /= j; - d /= i - } - e = B * u + z * x; - c = g * u + d * x; - t = 1 / (Math.sin(Math.asin(Math.abs(C) / (x * u)) * 0.5) * Math.sqrt(e * e + c * c)); - e *= t; - c *= t; - var o = (e * B + c * g) / (B * B + g * g), - m = (e * z + c * d) / (z * z + d * d); - var n = B * o - e, - p = g * o - c, - h = z * m - e, - y = d * m - c, - w = Math.atan2(p, n), - s = Math.atan2(y, h); - if (C > 0) { - if (s < w) { - s += Math.PI * 2 - } - } else { - if (w < s) { - w += Math.PI * 2 - } - } - if (i !== j) { - e = b * e * j - a * c * i + A; - c = a * c * i + b * c * i + f; - E.lineTo(b * j * n - a * i * p + e, a * j * n + b * i * p + c); - E.ellipse(e, c, j, i, v, w, s, C < 0) - } else { - e = e * j + A; - c = c * i + f; - E.lineTo(j * n + e, i * p + c); - E.ellipse(e, c, j, i, v, w, s, C < 0) - } - }, - ellipse: function(h, f, c, a, q, n, d, e) { - var o = this, - g = o.params, - b = g.length, - m, l, k; - if (d - n >= Math.PI * 2) { - o.ellipse(h, f, c, a, q, n, n + Math.PI, e); - o.ellipse(h, f, c, a, q, n + Math.PI, d, e); - return - } - if (!e) { - if (d < n) { - d += Math.PI * 2 - } - m = o.approximateArc(g, h, f, c, a, q, n, d) - } else { - if (n < d) { - n += Math.PI * 2 - } - m = o.approximateArc(g, h, f, c, a, q, d, n); - for (l = b, k = g.length - 2; l < k; l += 2, k -= 2) { - var p = g[l]; - g[l] = g[k]; - g[k] = p; - p = g[l + 1]; - g[l + 1] = g[k + 1]; - g[k + 1] = p - } - } - if (!o.cursor) { - o.cursor = [g[g.length - 2], g[g.length - 1]]; - o.commands.push("M") - } else { - o.cursor[0] = g[g.length - 2]; - o.cursor[1] = g[g.length - 1]; - o.commands.push("L") - } - for (l = 2; l < m; l += 6) { - o.commands.push("C") - } - o.dirt() - }, - arc: function(b, f, a, d, c, e) { - this.ellipse(b, f, a, a, 0, d, c, e) - }, - rect: function(b, e, c, a) { - if (c == 0 || a == 0) { - return - } - var d = this; - d.moveTo(b, e); - d.lineTo(b + c, e); - d.lineTo(b + c, e + a); - d.lineTo(b, e + a); - d.closePath() - }, - approximateArc: function(s, i, f, o, n, d, x, v) { - var e = Math.cos(d), - z = Math.sin(d), - k = Math.cos(x), - l = Math.sin(x), - q = e * k * o - z * l * n, - y = -e * l * o - z * k * n, - p = z * k * o + e * l * n, - w = -z * l * o + e * k * n, - m = Math.PI / 2, - r = 2, - j = q, - u = y, - h = p, - t = w, - b = 0.547443256150549, - C, g, A, a, B, c; - v -= x; - if (v < 0) { - v += Math.PI * 2 - } - s.push(q + i, p + f); - while (v >= m) { - s.push(j + u * b + i, h + t * b + f, j * b + u + i, h * b + t + f, u + i, t + f); - r += 6; - v -= m; - C = j; - j = u; - u = -C; - C = h; - h = t; - t = -C - } - if (v) { - g = (0.3294738052815987 + 0.012120855841304373 * v) * v; - A = Math.cos(v); - a = Math.sin(v); - B = A + g * a; - c = a - g * A; - s.push(j + u * g + i, h + t * g + f, j * B + u * c + i, h * B + t * c + f, j * A + u * a + i, h * A + t * a + f); - r += 6 - } - return r - }, - arcSvg: function(j, h, r, m, w, t, c) { - if (j < 0) { - j = -j - } - if (h < 0) { - h = -h - } - var x = this, - u = x.cursor[0], - f = x.cursor[1], - a = (u - t) / 2, - y = (f - c) / 2, - d = Math.cos(r), - s = Math.sin(r), - o = a * d + y * s, - v = -a * s + y * d, - i = o / j, - g = v / h, - p = i * i + g * g, - e = (u + t) * 0.5, - b = (f + c) * 0.5, - l = 0, - k = 0; - if (p >= 1) { - p = Math.sqrt(p); - j *= p; - h *= p - } else { - p = Math.sqrt(1 / p - 1); - if (m === w) { - p = -p - } - l = p * j * g; - k = -p * h * i; - e += d * l - s * k; - b += s * l + d * k - } - var q = Math.atan2((v - k) / h, (o - l) / j), - n = Math.atan2((-v - k) / h, (-o - l) / j) - q; - if (w) { - if (n <= 0) { - n += Math.PI * 2 - } - } else { - if (n >= 0) { - n -= Math.PI * 2 - } - } - x.ellipse(e, b, j, h, r, q, q + n, 1 - w) - }, - fromSvgString: function(e) { - if (!e) { - return - } - var m = this, - h, l = { - a: 7, - c: 6, - h: 1, - l: 2, - m: 2, - q: 4, - s: 4, - t: 2, - v: 1, - z: 0, - A: 7, - C: 6, - H: 1, - L: 2, - M: 2, - Q: 4, - S: 4, - T: 2, - V: 1, - Z: 0 - }, - k = "", - g, f, c = 0, - b = 0, - d = false, - j, n, a; - if (Ext.isString(e)) { - h = e.replace(Ext.draw.Path.pathRe, " $1 ").replace(Ext.draw.Path.pathRe2, " -").split(Ext.draw.Path.pathSplitRe) - } else { - if (Ext.isArray(e)) { - h = e.join(",").split(Ext.draw.Path.pathSplitRe) - } - } - for (j = 0, n = 0; j < h.length; j++) { - if (h[j] !== "") { - h[n++] = h[j] - } - } - h.length = n; - m.clear(); - for (j = 0; j < h.length;) { - k = d; - d = h[j]; - a = (d.toUpperCase() !== d); - j++; - switch (d) { - case "M": - m.moveTo(c = +h[j], b = +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2 - } - break; - case "L": - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2 - } - break; - case "A": - while (j < n && !l.hasOwnProperty(h[j])) { - m.arcSvg(+h[j], +h[j + 1], +h[j + 2] * Math.PI / 180, +h[j + 3], +h[j + 4], c = +h[j + 5], b = +h[j + 6]); - j += 7 - } - break; - case "C": - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(+h[j], +h[j + 1], g = +h[j + 2], f = +h[j + 3], c = +h[j + 4], b = +h[j + 5]); - j += 6 - } - break; - case "Z": - m.closePath(); - break; - case "m": - m.moveTo(c += +h[j], b += +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2 - } - break; - case "l": - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2 - } - break; - case "a": - while (j < n && !l.hasOwnProperty(h[j])) { - m.arcSvg(+h[j], +h[j + 1], +h[j + 2] * Math.PI / 180, +h[j + 3], +h[j + 4], c += +h[j + 5], b += +h[j + 6]); - j += 7 - } - break; - case "c": - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + (+h[j]), b + (+h[j + 1]), g = c + (+h[j + 2]), f = b + (+h[j + 3]), c += +h[j + 4], b += +h[j + 5]); - j += 6 - } - break; - case "z": - m.closePath(); - break; - case "s": - if (!(k === "c" || k === "C" || k === "s" || k === "S")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + c - g, b + b - f, g = c + (+h[j]), f = b + (+h[j + 1]), c += +h[j + 2], b += +h[j + 3]); - j += 4 - } - break; - case "S": - if (!(k === "c" || k === "C" || k === "s" || k === "S")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + c - g, b + b - f, g = +h[j], f = +h[j + 1], c = (+h[j + 2]), b = (+h[j + 3])); - j += 4 - } - break; - case "q": - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + (+h[j]), f = b + (+h[j + 1]), c += +h[j + 2], b += +h[j + 3]); - j += 4 - } - break; - case "Q": - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = +h[j], f = +h[j + 1], c = +h[j + 2], b = +h[j + 3]); - j += 4 - } - break; - case "t": - if (!(k === "q" || k === "Q" || k === "t" || k === "T")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + c - g, f = b + b - f, c += +h[j + 1], b += +h[j + 2]); - j += 2 - } - break; - case "T": - if (!(k === "q" || k === "Q" || k === "t" || k === "T")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + c - g, f = b + b - f, c = (+h[j + 1]), b = (+h[j + 2])); - j += 2 - } - break; - case "h": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b); - j++ - } - break; - case "H": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b); - j++ - } - break; - case "v": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c, b += +h[j]); - j++ - } - break; - case "V": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c, b = +h[j]); - j++ - } - break - } - } - }, - clone: function() { - var a = this, - b = new Ext.draw.Path(); - b.params = a.params.slice(0); - b.commands = a.commands.slice(0); - b.cursor = a.cursor ? a.cursor.slice(0) : null; - b.startX = a.startX; - b.startY = a.startY; - b.svgString = a.svgString; - return b - }, - transform: function(j) { - if (j.isIdentity()) { - return - } - var a = j.getXX(), - f = j.getYX(), - m = j.getDX(), - l = j.getXY(), - e = j.getYY(), - k = j.getDY(), - b = this.params, - c = 0, - d = b.length, - h, g; - for (; c < d; c += 2) { - h = b[c]; - g = b[c + 1]; - b[c] = h * a + g * f + m; - b[c + 1] = h * l + g * e + k - } - this.dirt() - }, - getDimension: function(f) { - if (!f) { - f = {} - } - if (!this.commands || !this.commands.length) { - f.x = 0; - f.y = 0; - f.width = 0; - f.height = 0; - return f - } - f.left = Infinity; - f.top = Infinity; - f.right = -Infinity; - f.bottom = -Infinity; - var d = 0, - c = 0, - b = this.commands, - g = this.params, - e = b.length, - a, h; - for (; d < e; d++) { - switch (b[d]) { - case "M": - case "L": - a = g[c]; - h = g[c + 1]; - f.left = Math.min(a, f.left); - f.top = Math.min(h, f.top); - f.right = Math.max(a, f.right); - f.bottom = Math.max(h, f.bottom); - c += 2; - break; - case "C": - this.expandDimension(f, a, h, g[c], g[c + 1], g[c + 2], g[c + 3], a = g[c + 4], h = g[c + 5]); - c += 6; - break - } - } - f.x = f.left; - f.y = f.top; - f.width = f.right - f.left; - f.height = f.bottom - f.top; - return f - }, - getDimensionWithTransform: function(n, f) { - if (!this.commands || !this.commands.length) { - if (!f) { - f = {} - } - f.x = 0; - f.y = 0; - f.width = 0; - f.height = 0; - return f - } - f.left = Infinity; - f.top = Infinity; - f.right = -Infinity; - f.bottom = -Infinity; - var a = n.getXX(), - k = n.getYX(), - q = n.getDX(), - p = n.getXY(), - h = n.getYY(), - o = n.getDY(), - e = 0, - d = 0, - b = this.commands, - c = this.params, - g = b.length, - m, l; - for (; e < g; e++) { - switch (b[e]) { - case "M": - case "L": - m = c[d] * a + c[d + 1] * k + q; - l = c[d] * p + c[d + 1] * h + o; - f.left = Math.min(m, f.left); - f.top = Math.min(l, f.top); - f.right = Math.max(m, f.right); - f.bottom = Math.max(l, f.bottom); - d += 2; - break; - case "C": - this.expandDimension(f, m, l, c[d] * a + c[d + 1] * k + q, c[d] * p + c[d + 1] * h + o, c[d + 2] * a + c[d + 3] * k + q, c[d + 2] * p + c[d + 3] * h + o, m = c[d + 4] * a + c[d + 5] * k + q, l = c[d + 4] * p + c[d + 5] * h + o); - d += 6; - break - } - } - if (!f) { - f = {} - } - f.x = f.left; - f.y = f.top; - f.width = f.right - f.left; - f.height = f.bottom - f.top; - return f - }, - expandDimension: function(i, d, p, k, g, j, e, c, o) { - var m = this, - f = i.left, - a = i.right, - q = i.top, - n = i.bottom, - h = m.dim || (m.dim = []); - m.curveDimension(d, k, j, c, h); - f = Math.min(f, h[0]); - a = Math.max(a, h[1]); - m.curveDimension(p, g, e, o, h); - q = Math.min(q, h[0]); - n = Math.max(n, h[1]); - i.left = f; - i.right = a; - i.top = q; - i.bottom = n - }, - curveDimension: function(p, n, k, j, h) { - var i = 3 * (-p + 3 * (n - k) + j), - g = 6 * (p - 2 * n + k), - f = -3 * (p - n), - o, m, e = Math.min(p, j), - l = Math.max(p, j), - q; - if (i === 0) { - if (g === 0) { - h[0] = e; - h[1] = l; - return - } else { - o = -f / g; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - } - } else { - q = g * g - 4 * i * f; - if (q >= 0) { - q = Math.sqrt(q); - o = (q - g) / 2 / i; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - if (q > 0) { - o -= q / i; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - } - } - } - h[0] = e; - h[1] = l - }, - interpolate: function(f, e, j, i, g) { - if (g === 0) { - return f - } - if (g === 1) { - return i - } - var h = (1 - g) / g; - return g * g * g * (i + h * (3 * j + h * (3 * e + h * f))) - }, - fromStripes: function(g) { - var e = this, - c = 0, - d = g.length, - b, a, f; - e.clear(); - for (; c < d; c++) { - f = g[c]; - e.params.push.apply(e.params, f); - e.commands.push("M"); - for (b = 2, a = f.length; b < a; b += 6) { - e.commands.push("C") - } - } - if (!e.cursor) { - e.cursor = [] - } - e.cursor[0] = e.params[e.params.length - 2]; - e.cursor[1] = e.params[e.params.length - 1]; - e.dirt() - }, - toStripes: function(k) { - var o = k || [], - p, n, m, b, a, h, g, f, e, c = this.commands, - d = this.params, - l = c.length; - for (f = 0, e = 0; f < l; f++) { - switch (c[f]) { - case "M": - p = [h = b = d[e++], g = a = d[e++]]; - o.push(p); - break; - case "L": - n = d[e++]; - m = d[e++]; - p.push((b + b + n) / 3, (a + a + m) / 3, (b + n + n) / 3, (a + m + m) / 3, b = n, a = m); - break; - case "C": - p.push(d[e++], d[e++], d[e++], d[e++], b = d[e++], a = d[e++]); - break; - case "Z": - n = h; - m = g; - p.push((b + b + n) / 3, (a + a + m) / 3, (b + n + n) / 3, (a + m + m) / 3, b = n, a = m); - break - } - } - return o - }, - updateSvgString: function() { - var b = [], - a = this.commands, - f = this.params, - e = a.length, - d = 0, - c = 0; - for (; d < e; d++) { - switch (a[d]) { - case "M": - b.push("M" + f[c] + "," + f[c + 1]); - c += 2; - break; - case "L": - b.push("L" + f[c] + "," + f[c + 1]); - c += 2; - break; - case "C": - b.push("C" + f[c] + "," + f[c + 1] + " " + f[c + 2] + "," + f[c + 3] + " " + f[c + 4] + "," + f[c + 5]); - c += 6; - break; - case "Z": - b.push("Z"); - break - } - } - this.svgString = b.join("") - }, - toString: function() { - if (!this.svgString) { - this.updateSvgString() - } - return this.svgString - } -}); -Ext.define("Ext.draw.overrides.Path", { - override: "Ext.draw.Path", - rayOrigin: { - x: -10000, - y: -10000 - }, - isPointInPath: function(o, n) { - var m = this, - c = m.commands, - q = Ext.draw.PathUtil, - p = m.rayOrigin, - f = m.params, - l = c.length, - e = null, - d = null, - b = 0, - a = 0, - k = 0, - h, g; - for (h = 0, g = 0; h < l; h++) { - switch (c[h]) { - case "M": - if (e !== null) { - if (q.linesIntersection(e, d, b, a, p.x, p.y, o, n)) { - k += 1 - } - } - e = b = f[g]; - d = a = f[g + 1]; - g += 2; - break; - case "L": - if (q.linesIntersection(b, a, f[g], f[g + 1], p.x, p.y, o, n)) { - k += 1 - } - b = f[g]; - a = f[g + 1]; - g += 2; - break; - case "C": - k += q.cubicLineIntersections(b, f[g], f[g + 2], f[g + 4], a, f[g + 1], f[g + 3], f[g + 5], p.x, p.y, o, n).length; - b = f[g + 4]; - a = f[g + 5]; - g += 6; - break; - case "Z": - if (e !== null) { - if (q.linesIntersection(e, d, b, a, p.x, p.y, o, n)) { - k += 1 - } - } - break - } - } - return k % 2 === 1 - }, - isPointOnPath: function(n, m) { - var l = this, - c = l.commands, - o = Ext.draw.PathUtil, - f = l.params, - k = c.length, - e = null, - d = null, - b = 0, - a = 0, - h, g; - for (h = 0, g = 0; h < k; h++) { - switch (c[h]) { - case "M": - if (e !== null) { - if (o.pointOnLine(e, d, b, a, n, m)) { - return true - } - } - e = b = f[g]; - d = a = f[g + 1]; - g += 2; - break; - case "L": - if (o.pointOnLine(b, a, f[g], f[g + 1], n, m)) { - return true - } - b = f[g]; - a = f[g + 1]; - g += 2; - break; - case "C": - if (o.pointOnCubic(b, f[g], f[g + 2], f[g + 4], a, f[g + 1], f[g + 3], f[g + 5], n, m)) { - return true - } - b = f[g + 4]; - a = f[g + 5]; - g += 6; - break; - case "Z": - if (e !== null) { - if (o.pointOnLine(e, d, b, a, n, m)) { - return true - } - } - break - } - } - return false - }, - getSegmentIntersections: function(t, d, s, c, r, b, o, a) { - var w = this, - g = arguments.length, - v = Ext.draw.PathUtil, - f = w.commands, - u = w.params, - k = f.length, - m = null, - l = null, - h = 0, - e = 0, - x = [], - q, n, p; - for (q = 0, n = 0; q < k; q++) { - switch (f[q]) { - case "M": - if (m !== null) { - switch (g) { - case 4: - p = v.linesIntersection(m, l, h, e, t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, m, l, h, e); - x.push.apply(x, p); - break - } - } - m = h = u[n]; - l = e = u[n + 1]; - n += 2; - break; - case "L": - switch (g) { - case 4: - p = v.linesIntersection(h, e, u[n], u[n + 1], t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, h, e, u[n], u[n + 1]); - x.push.apply(x, p); - break - } - h = u[n]; - e = u[n + 1]; - n += 2; - break; - case "C": - switch (g) { - case 4: - p = v.cubicLineIntersections(h, u[n], u[n + 2], u[n + 4], e, u[n + 1], u[n + 3], u[n + 5], t, d, s, c); - x.push.apply(x, p); - break; - case 8: - p = v.cubicsIntersections(h, u[n], u[n + 2], u[n + 4], e, u[n + 1], u[n + 3], u[n + 5], t, s, r, o, d, c, b, a); - x.push.apply(x, p); - break - } - h = u[n + 4]; - e = u[n + 5]; - n += 6; - break; - case "Z": - if (m !== null) { - switch (g) { - case 4: - p = v.linesIntersection(m, l, h, e, t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, m, l, h, e); - x.push.apply(x, p); - break - } - } - break - } - } - return x - }, - getIntersections: function(o) { - var m = this, - c = m.commands, - g = m.params, - l = c.length, - f = null, - e = null, - b = 0, - a = 0, - d = [], - k, h, n; - for (k = 0, h = 0; k < l; k++) { - switch (c[k]) { - case "M": - if (f !== null) { - n = o.getSegmentIntersections.call(o, f, e, b, a); - d.push.apply(d, n) - } - f = b = g[h]; - e = a = g[h + 1]; - h += 2; - break; - case "L": - n = o.getSegmentIntersections.call(o, b, a, g[h], g[h + 1]); - d.push.apply(d, n); - b = g[h]; - a = g[h + 1]; - h += 2; - break; - case "C": - n = o.getSegmentIntersections.call(o, b, a, g[h], g[h + 1], g[h + 2], g[h + 3], g[h + 4], g[h + 5]); - d.push.apply(d, n); - b = g[h + 4]; - a = g[h + 5]; - h += 6; - break; - case "Z": - if (f !== null) { - n = o.getSegmentIntersections.call(o, f, e, b, a); - d.push.apply(d, n) - } - break - } - } - return d - } -}); -Ext.define("Ext.draw.sprite.Path", { - extend: "Ext.draw.sprite.Sprite", - requires: ["Ext.draw.Draw", "Ext.draw.Path"], - alias: ["sprite.path", "Ext.draw.Sprite"], - type: "path", - isPath: true, - inheritableStatics: { - def: { - processors: { - path: function(b, a) { - if (!(b instanceof Ext.draw.Path)) { - b = new Ext.draw.Path(b) - } - return b - } - }, - aliases: { - d: "path" - }, - triggers: { - path: "bbox" - }, - updaters: { - path: function(a) { - var b = a.path; - if (!b || b.bindAttr !== a) { - b = new Ext.draw.Path(); - b.bindAttr = a; - a.path = b - } - b.clear(); - this.updatePath(b, a); - this.scheduleUpdater(a, "bbox", ["path"]) - } - } - } - }, - updatePlainBBox: function(a) { - if (this.attr.path) { - this.attr.path.getDimension(a) - } - }, - updateTransformedBBox: function(a) { - if (this.attr.path) { - this.attr.path.getDimensionWithTransform(this.attr.matrix, a) - } - }, - render: function(b, c) { - var d = this.attr.matrix, - a = this.attr; - if (!a.path || a.path.params.length === 0) { - return - } - d.toContext(c); - c.appendPath(a.path); - c.fillStroke(a) - }, - updatePath: function(b, a) {} -}); -Ext.define("Ext.draw.overrides.sprite.Path", { - override: "Ext.draw.sprite.Path", - requires: ["Ext.draw.Color"], - isPointInPath: function(c, g) { - var b = this.attr; - if (b.fillStyle === Ext.draw.Color.RGBA_NONE) { - return this.isPointOnPath(c, g) - } - var e = b.path, - d = b.matrix, - f, a; - if (!d.isIdentity()) { - f = e.params.slice(0); - e.transform(b.matrix) - } - a = e.isPointInPath(c, g); - if (f) { - e.params = f - } - return a - }, - isPointOnPath: function(c, g) { - var b = this.attr, - e = b.path, - d = b.matrix, - f, a; - if (!d.isIdentity()) { - f = e.params.slice(0); - e.transform(b.matrix) - } - a = e.isPointOnPath(c, g); - if (f) { - e.params = f - } - return a - }, - hitTest: function(i, l) { - var e = this, - c = e.attr, - k = c.path, - g = c.matrix, - h = i[0], - f = i[1], - d = e.callParent([i, l]), - j = null, - a, b; - if (!d) { - return j - } - l = l || Ext.draw.sprite.Sprite.defaultHitTestOptions; - if (!g.isIdentity()) { - a = k.params.slice(0); - k.transform(c.matrix) - } - if (l.fill && l.stroke) { - b = c.fillStyle !== Ext.draw.Color.NONE && c.fillStyle !== Ext.draw.Color.RGBA_NONE; - if (b) { - if (k.isPointInPath(h, f)) { - j = { - sprite: e - } - } - } else { - if (k.isPointInPath(h, f) || k.isPointOnPath(h, f)) { - j = { - sprite: e - } - } - } - } else { - if (l.stroke && !l.fill) { - if (k.isPointOnPath(h, f)) { - j = { - sprite: e - } - } - } else { - if (l.fill && !l.stroke) { - if (k.isPointInPath(h, f)) { - j = { - sprite: e - } - } - } - } - } - if (a) { - k.params = a - } - return j - }, - getIntersections: function(j) { - if (!(j.isSprite && j.isPath)) { - return [] - } - var e = this.attr, - d = j.attr, - i = e.path, - h = d.path, - g = e.matrix, - a = d.matrix, - c, f, b; - if (!g.isIdentity()) { - c = i.params.slice(0); - i.transform(e.matrix) - } - if (!a.isIdentity()) { - f = h.params.slice(0); - h.transform(d.matrix) - } - b = i.getIntersections(h); - if (c) { - i.params = c - } - if (f) { - h.params = f - } - return b - } -}); -Ext.define("Ext.draw.sprite.Circle", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.circle", - type: "circle", - inheritableStatics: { - def: { - processors: { - cx: "number", - cy: "number", - r: "number" - }, - aliases: { - radius: "r", - x: "cx", - y: "cy", - centerX: "cx", - centerY: "cy" - }, - defaults: { - cx: 0, - cy: 0, - r: 4 - }, - triggers: { - cx: "path", - cy: "path", - r: "path" - } - } - }, - updatePlainBBox: function(c) { - var b = this.attr, - a = b.cx, - e = b.cy, - d = b.r; - c.x = a - d; - c.y = e - d; - c.width = d + d; - c.height = d + d - }, - updateTransformedBBox: function(d) { - var g = this.attr, - f = g.cx, - e = g.cy, - a = g.r, - h = g.matrix, - j = h.getScaleX(), - i = h.getScaleY(), - c, b; - c = j * a; - b = i * a; - d.x = h.x(f, e) - c; - d.y = h.y(f, e) - b; - d.width = c + c; - d.height = b + b - }, - updatePath: function(b, a) { - b.arc(a.cx, a.cy, a.r, 0, Math.PI * 2, false) - } -}); -Ext.define("Ext.draw.sprite.Arc", { - extend: "Ext.draw.sprite.Circle", - alias: "sprite.arc", - type: "arc", - inheritableStatics: { - def: { - processors: { - startAngle: "number", - endAngle: "number", - anticlockwise: "bool" - }, - aliases: { - from: "startAngle", - to: "endAngle", - start: "startAngle", - end: "endAngle" - }, - defaults: { - startAngle: 0, - endAngle: Math.PI * 2, - anticlockwise: false - }, - triggers: { - startAngle: "path", - endAngle: "path", - anticlockwise: "path" - } - } - }, - updatePath: function(b, a) { - b.arc(a.cx, a.cy, a.r, a.startAngle, a.endAngle, a.anticlockwise) - } -}); -Ext.define("Ext.draw.sprite.Arrow", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.arrow", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 1.5, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c * 0.7, ",", e - c * 0.4, "l", [c * 0.6, 0, 0, -c * 0.4, c, c * 0.8, -c, c * 0.8, 0, -c * 0.4, -c * 0.6, 0], "z")) - } -}); -Ext.define("Ext.draw.sprite.Composite", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.composite", - type: "composite", - isComposite: true, - config: { - sprites: [] - }, - constructor: function() { - this.sprites = []; - this.sprites.map = {}; - this.callParent(arguments) - }, - add: function(c) { - if (!c) { - return null - } - if (!c.isSprite) { - c = Ext.create("sprite." + c.type, c); - c.setParent(this); - c.setSurface(this.getSurface()) - } - var d = this, - a = d.attr, - b = c.applyTransformations; - c.applyTransformations = function() { - if (c.attr.dirtyTransform) { - a.dirtyTransform = true; - a.bbox.plain.dirty = true; - a.bbox.transform.dirty = true - } - b.call(c) - }; - d.sprites.push(c); - d.sprites.map[c.id] = c.getId(); - a.bbox.plain.dirty = true; - a.bbox.transform.dirty = true; - return c - }, - updateSurface: function(a) { - for (var b = 0, c = this.sprites.length; b < c; b++) { - this.sprites[b].setSurface(a) - } - }, - addAll: function(b) { - if (b.isSprite || b.type) { - this.add(b) - } else { - if (Ext.isArray(b)) { - var a = 0; - while (a < b.length) { - this.add(b[a++]) - } - } - } - }, - updatePlainBBox: function(g) { - var e = this, - b = Infinity, - h = -Infinity, - f = Infinity, - a = -Infinity, - j, k, c, d; - for (c = 0, d = e.sprites.length; c < d; c++) { - j = e.sprites[c]; - j.applyTransformations(); - k = j.getBBox(); - if (b > k.x) { - b = k.x - } - if (h < k.x + k.width) { - h = k.x + k.width - } - if (f > k.y) { - f = k.y - } - if (a < k.y + k.height) { - a = k.y + k.height - } - } - g.x = b; - g.y = f; - g.width = h - b; - g.height = a - f - }, - render: function(a, b, f) { - var d = this.attr.matrix, - c, e; - d.toContext(b); - for (c = 0, e = this.sprites.length; c < e; c++) { - a.renderSprite(this.sprites[c], f) - } - }, - destroy: function() { - var c = this, - d = c.sprites, - b = d.length, - a; - c.callParent(); - for (a = 0; a < b; a++) { - d[a].destroy() - } - d.length = 0 - } -}); -Ext.define("Ext.draw.sprite.Cross", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.cross", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size / 1.7, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c, ",", e, "l", [-c, -c, c, -c, c, c, c, -c, c, c, -c, c, c, c, -c, c, -c, -c, -c, c, -c, -c, "z"])) - } -}); -Ext.define("Ext.draw.sprite.Diamond", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.diamond", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 1.25, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString(["M", a, e - c, "l", c, c, -c, c, -c, -c, c, -c, "z"]) - } -}); -Ext.define("Ext.draw.sprite.Ellipse", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.ellipse", - type: "ellipse", - inheritableStatics: { - def: { - processors: { - cx: "number", - cy: "number", - rx: "number", - ry: "number", - axisRotation: "number" - }, - aliases: { - radius: "r", - x: "cx", - y: "cy", - centerX: "cx", - centerY: "cy", - radiusX: "rx", - radiusY: "ry" - }, - defaults: { - cx: 0, - cy: 0, - rx: 1, - ry: 1, - axisRotation: 0 - }, - triggers: { - cx: "path", - cy: "path", - rx: "path", - ry: "path", - axisRotation: "path" - } - } - }, - updatePlainBBox: function(c) { - var b = this.attr, - a = b.cx, - f = b.cy, - e = b.rx, - d = b.ry; - c.x = a - e; - c.y = f - d; - c.width = e + e; - c.height = d + d - }, - updateTransformedBBox: function(d) { - var i = this.attr, - f = i.cx, - e = i.cy, - c = i.rx, - b = i.ry, - l = b / c, - m = i.matrix.clone(), - a, q, k, j, p, o, n, g; - m.append(1, 0, 0, l, 0, e * (1 - l)); - a = m.getXX(); - k = m.getYX(); - p = m.getDX(); - q = m.getXY(); - j = m.getYY(); - o = m.getDY(); - n = Math.sqrt(a * a + k * k) * c; - g = Math.sqrt(q * q + j * j) * c; - d.x = f * a + e * k + p - n; - d.y = f * q + e * j + o - g; - d.width = n + n; - d.height = g + g - }, - updatePath: function(b, a) { - b.ellipse(a.cx, a.cy, a.rx, a.ry, a.axisRotation, 0, Math.PI * 2, false) - } -}); -Ext.define("Ext.draw.sprite.EllipticalArc", { - extend: "Ext.draw.sprite.Ellipse", - alias: "sprite.ellipticalArc", - type: "ellipticalArc", - inheritableStatics: { - def: { - processors: { - startAngle: "number", - endAngle: "number", - anticlockwise: "bool" - }, - aliases: { - from: "startAngle", - to: "endAngle", - start: "startAngle", - end: "endAngle" - }, - defaults: { - startAngle: 0, - endAngle: Math.PI * 2, - anticlockwise: false - }, - triggers: { - startAngle: "path", - endAngle: "path", - anticlockwise: "path" - } - } - }, - updatePath: function(b, a) { - b.ellipse(a.cx, a.cy, a.rx, a.ry, a.axisRotation, a.startAngle, a.endAngle, a.anticlockwise) - } -}); -Ext.define("Ext.draw.sprite.Rect", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.rect", - type: "rect", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number", - radius: "number" - }, - aliases: {}, - triggers: { - x: "path", - y: "path", - width: "path", - height: "path", - radius: "path" - }, - defaults: { - x: 0, - y: 0, - width: 8, - height: 8, - radius: 0 - } - } - }, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.x; - b.y = a.y; - b.width = a.width; - b.height = a.height - }, - updateTransformedBBox: function(a, b) { - this.attr.matrix.transformBBox(b, this.attr.radius, a) - }, - updatePath: function(f, d) { - var c = d.x, - g = d.y, - e = d.width, - b = d.height, - a = Math.min(d.radius, Math.abs(d.height) * 0.5, Math.abs(d.width) * 0.5); - if (a === 0) { - f.rect(c, g, e, b) - } else { - f.moveTo(c + a, g); - f.arcTo(c + e, g, c + e, g + b, a); - f.arcTo(c + e, g + b, c, g + b, a); - f.arcTo(c, g + b, c, g, a); - f.arcTo(c, g, c + a, g, a) - } - } -}); -Ext.define("Ext.draw.sprite.Image", { - extend: "Ext.draw.sprite.Rect", - alias: "sprite.image", - type: "image", - statics: { - imageLoaders: {} - }, - inheritableStatics: { - def: { - processors: { - src: "string" - }, - defaults: { - src: "", - width: null, - height: null - } - } - }, - render: function(c, o) { - var j = this, - h = j.attr, - n = h.matrix, - a = h.src, - l = h.x, - k = h.y, - b = h.width, - m = h.height, - g = Ext.draw.sprite.Image.imageLoaders[a], - f, d, e; - if (g && g.done) { - n.toContext(o); - d = g.image; - o.drawImage(d, l, k, b || (d.naturalWidth || d.width) / c.devicePixelRatio, m || (d.naturalHeight || d.height) / c.devicePixelRatio) - } else { - if (!g) { - f = new Image(); - g = Ext.draw.sprite.Image.imageLoaders[a] = { - image: f, - done: false, - pendingSprites: [j], - pendingSurfaces: [c] - }; - f.width = b; - f.height = m; - f.onload = function() { - if (!g.done) { - g.done = true; - for (e = 0; e < g.pendingSprites.length; e++) { - g.pendingSprites[e].setDirty(true) - } - for (e in g.pendingSurfaces) { - g.pendingSurfaces[e].renderFrame() - } - } - }; - f.src = a - } else { - Ext.Array.include(g.pendingSprites, j); - Ext.Array.include(g.pendingSurfaces, c) - } - } - } -}); -Ext.define("Ext.draw.sprite.Instancing", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.instancing", - type: "instancing", - isInstancing: true, - config: { - template: null - }, - instances: null, - applyTemplate: function(a) { - if (!a.isSprite) { - if (!a.xclass && !a.type) { - a.type = "circle" - } - a = Ext.create(a.xclass || "sprite." + a.type, a) - } - a.setParent(this); - return a - }, - updateTemplate: function(a, b) { - if (b) { - delete b.ownAttr - } - a.setSurface(this.getSurface()); - a.ownAttr = a.attr; - this.clearAll() - }, - updateSurface: function(a) { - var b = this.getTemplate(); - if (b) { - b.setSurface(a) - } - }, - get: function(a) { - return this.instances[a] - }, - getCount: function() { - return this.instances.length - }, - clearAll: function() { - var a = this.getTemplate(); - a.attr.children = this.instances = []; - this.position = 0 - }, - createInstance: function(d, f, c) { - var e = this.getTemplate(), - b = e.attr, - a = Ext.Object.chain(b); - e.topModifier.prepareAttributes(a); - e.attr = a; - e.setAttributes(d, f, c); - a.template = e; - this.instances.push(a); - e.attr = b; - this.position++; - return a - }, - getBBox: function() { - return null - }, - getBBoxFor: function(b, d) { - var c = this.getTemplate(), - a = c.attr, - e; - c.attr = this.instances[b]; - e = c.getBBox(d); - c.attr = a; - return e - }, - isVisible: function() { - var b = this.attr, - c = this.getParent(), - a; - a = c && c.isSurface && !b.hidden && b.globalAlpha; - return !!a - }, - isInstanceVisible: function(c) { - var e = this, - d = e.getTemplate(), - b = d.attr, - f = e.instances, - a = false; - if (!Ext.isNumber(c) || c < 0 || c >= f.length || !e.isVisible()) { - return a - } - d.attr = f[c]; - a = d.isVisible(point, options); - d.attr = b; - return a - }, - render: function(b, l, d, h) { - var g = this, - j = g.getTemplate(), - k = g.attr.matrix, - c = j.attr, - a = g.instances, - e, f = g.position; - k.toContext(l); - j.preRender(b, l, d, h); - j.useAttributes(l, h); - for (e = 0; e < f; e++) { - if (a[e].dirtyZIndex) { - break - } - } - for (e = 0; e < f; e++) { - if (a[e].hidden) { - continue - } - l.save(); - j.attr = a[e]; - j.useAttributes(l, h); - j.render(b, l, d, h); - l.restore() - } - j.attr = c - }, - setAttributesFor: function(c, e, f) { - var d = this.getTemplate(), - b = d.attr, - a = this.instances[c]; - if (!a) { - return - } - d.attr = a; - if (f) { - e = Ext.apply({}, e) - } else { - e = d.self.def.normalize(e) - } - d.topModifier.pushDown(a, e); - d.attr = b - }, - destroy: function() { - var b = this, - a = b.getTemplate(); - b.instances = null; - if (a) { - a.destroy() - } - b.callParent() - } -}); -Ext.define("Ext.draw.overrides.sprite.Instancing", { - override: "Ext.draw.sprite.Instancing", - hitTest: function(f, j) { - var e = this, - g = e.getTemplate(), - b = g.attr, - a = e.instances, - d = a.length, - c = 0, - h = null; - if (!e.isVisible()) { - return h - } - for (; c < d; c++) { - g.attr = a[c]; - h = g.hitTest(f, j); - if (h) { - h.isInstance = true; - h.template = h.sprite; - h.sprite = this; - h.instance = a[c]; - h.index = c; - return h - } - } - g.attr = b; - return h - } -}); -Ext.define("Ext.draw.sprite.Line", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.line", - type: "line", - inheritableStatics: { - def: { - processors: { - fromX: "number", - fromY: "number", - toX: "number", - toY: "number" - }, - defaults: { - fromX: 0, - fromY: 0, - toX: 1, - toY: 1, - strokeStyle: "black" - }, - aliases: { - x1: "fromX", - y1: "fromY", - x2: "toX", - y2: "toY" - } - } - }, - updateLineBBox: function(b, i, s, g, r, f) { - var o = this.attr, - q = o.matrix, - h = o.lineWidth / 2, - m, l, d, c, k, j, n; - if (i) { - n = q.transformPoint([s, g]); - s = n[0]; - g = n[1]; - n = q.transformPoint([r, f]); - r = n[0]; - f = n[1] - } - m = Math.min(s, r); - d = Math.max(s, r); - l = Math.min(g, f); - c = Math.max(g, f); - var t = Math.atan2(d - m, c - l), - a = Math.sin(t), - e = Math.cos(t), - k = h * e, - j = h * a; - m -= k; - l -= j; - d += k; - c += j; - b.x = m; - b.y = l; - b.width = d - m; - b.height = c - l - }, - updatePlainBBox: function(b) { - var a = this.attr; - this.updateLineBBox(b, false, a.fromX, a.fromY, a.toX, a.toY) - }, - updateTransformedBBox: function(b, c) { - var a = this.attr; - this.updateLineBBox(b, true, a.fromX, a.fromY, a.toX, a.toY) - }, - render: function(b, c) { - var a = this.attr, - d = this.attr.matrix; - d.toContext(c); - c.beginPath(); - c.moveTo(a.fromX, a.fromY); - c.lineTo(a.toX, a.toY); - c.stroke() - } -}); -Ext.define("Ext.draw.sprite.Plus", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.plus", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size / 1.3, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c / 2, ",", e - c / 2, "l", [0, -c, c, 0, 0, c, c, 0, 0, c, -c, 0, 0, c, -c, 0, 0, -c, -c, 0, 0, -c, "z"])) - } -}); -Ext.define("Ext.draw.sprite.Sector", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.sector", - type: "sector", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - margin: "number" - }, - aliases: { - rho: "endRho" - }, - triggers: { - centerX: "path,bbox", - centerY: "path,bbox", - startAngle: "path,bbox", - endAngle: "path,bbox", - startRho: "path,bbox", - endRho: "path,bbox", - margin: "path,bbox" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: 0, - endAngle: 0, - startRho: 0, - endRho: 150, - margin: 0, - path: "M 0,0" - } - } - }, - getMidAngle: function() { - return this.midAngle || 0 - }, - updatePath: function(j, h) { - var g = Math.min(h.startAngle, h.endAngle), - c = Math.max(h.startAngle, h.endAngle), - b = this.midAngle = (g + c) * 0.5, - d = h.margin, - f = h.centerX, - e = h.centerY, - i = Math.min(h.startRho, h.endRho), - a = Math.max(h.startRho, h.endRho); - if (d) { - f += d * Math.cos(b); - e += d * Math.sin(b) - } - j.moveTo(f + i * Math.cos(g), e + i * Math.sin(g)); - j.lineTo(f + a * Math.cos(g), e + a * Math.sin(g)); - j.arc(f, e, a, g, c, false); - j.lineTo(f + i * Math.cos(c), e + i * Math.sin(c)); - j.arc(f, e, i, c, g, true) - } -}); -Ext.define("Ext.draw.sprite.Square", { - extend: "Ext.draw.sprite.Rect", - alias: "sprite.square", - inheritableStatics: { - def: { - processors: { - size: "number" - }, - defaults: { - size: 4 - }, - triggers: { - size: "size" - }, - updaters: { - size: function(a) { - var c = a.size, - b = a.lineWidth / 2; - this.setAttributes({ - x: a.x - c - b, - y: a.y - c, - height: 2 * c, - width: 2 * c - }) - } - } - } - } -}); -Ext.define("Ext.draw.TextMeasurer", { - singleton: true, - requires: ["Ext.util.TextMetrics"], - measureDiv: null, - measureCache: {}, - precise: Ext.isIE8, - measureDivTpl: { - tag: "div", - style: { - overflow: "hidden", - position: "relative", - "float": "left", - width: 0, - height: 0 - }, - children: { - tag: "div", - style: { - display: "block", - position: "absolute", - x: -100000, - y: -100000, - padding: 0, - margin: 0, - "z-index": -100000, - "white-space": "nowrap" - } - } - }, - actualMeasureText: function(g, b) { - var e = Ext.draw.TextMeasurer, - f = e.measureDiv, - a = 100000, - c; - if (!f) { - var d = Ext.Element.create({ - style: { - overflow: "hidden", - position: "relative", - "float": "left", - width: 0, - height: 0 - } - }); - e.measureDiv = f = Ext.Element.create({ - style: { - position: "absolute", - x: a, - y: a, - "z-index": -a, - "white-space": "nowrap", - display: "block", - padding: 0, - margin: 0 - } - }); - Ext.getBody().appendChild(d); - d.appendChild(f) - } - if (b) { - f.setStyle({ - font: b, - lineHeight: "normal" - }) - } - f.setText("(" + g + ")"); - c = f.getSize(); - f.setText("()"); - c.width -= f.getSize().width; - return c - }, - measureTextSingleLine: function(h, d) { - if (this.precise) { - return this.preciseMeasureTextSingleLine(h, d) - } - h = h.toString(); - var a = this.measureCache, - g = h.split(""), - c = 0, - j = 0, - l, b, e, f, k; - if (!a[d]) { - a[d] = {} - } - a = a[d]; - if (a[h]) { - return a[h] - } - for (e = 0, f = g.length; e < f; e++) { - b = g[e]; - if (!(l = a[b])) { - k = this.actualMeasureText(b, d); - l = a[b] = k - } - c += l.width; - j = Math.max(j, l.height) - } - return a[h] = { - width: c, - height: j - } - }, - preciseMeasureTextSingleLine: function(c, a) { - c = c.toString(); - var b = this.measureDiv || (this.measureDiv = Ext.getBody().createChild(this.measureDivTpl).down("div")); - b.setStyle({ - font: a || "" - }); - return Ext.util.TextMetrics.measure(b, c) - }, - measureText: function(e, b) { - var h = e.split("\n"), - d = h.length, - f = 0, - a = 0, - j, c, g; - if (d === 1) { - return this.measureTextSingleLine(e, b) - } - g = []; - for (c = 0; c < d; c++) { - j = this.measureTextSingleLine(h[c], b); - g.push(j); - f += j.height; - a = Math.max(a, j.width) - } - return { - width: a, - height: f, - sizes: g - } - } -}); -Ext.define("Ext.draw.sprite.Text", function() { - var d = { - "xx-small": true, - "x-small": true, - small: true, - medium: true, - large: true, - "x-large": true, - "xx-large": true - }; - var b = { - normal: true, - bold: true, - bolder: true, - lighter: true, - 100: true, - 200: true, - 300: true, - 400: true, - 500: true, - 600: true, - 700: true, - 800: true, - 900: true - }; - var a = { - start: "start", - left: "start", - center: "center", - middle: "center", - end: "end", - right: "end" - }; - var c = { - top: "top", - hanging: "hanging", - middle: "middle", - center: "middle", - alphabetic: "alphabetic", - ideographic: "ideographic", - bottom: "bottom" - }; - return { - extend: "Ext.draw.sprite.Sprite", - requires: ["Ext.draw.TextMeasurer", "Ext.draw.Color"], - alias: "sprite.text", - type: "text", - lineBreakRe: /\r?\n/g, - inheritableStatics: { - def: { - animationProcessors: { - text: "text" - }, - processors: { - x: "number", - y: "number", - text: "string", - fontSize: function(e) { - if (Ext.isNumber(+e)) { - return e + "px" - } else { - if (e.match(Ext.dom.Element.unitRe)) { - return e - } else { - if (e in d) { - return e - } - } - } - }, - fontStyle: "enums(,italic,oblique)", - fontVariant: "enums(,small-caps)", - fontWeight: function(e) { - if (e in b) { - return String(e) - } else { - return "" - } - }, - fontFamily: "string", - textAlign: function(e) { - return a[e] || "center" - }, - textBaseline: function(e) { - return c[e] || "alphabetic" - }, - font: "string" - }, - aliases: { - "font-size": "fontSize", - "font-family": "fontFamily", - "font-weight": "fontWeight", - "font-variant": "fontVariant", - "text-anchor": "textAlign" - }, - defaults: { - fontStyle: "", - fontVariant: "", - fontWeight: "", - fontSize: "10px", - fontFamily: "sans-serif", - font: "10px sans-serif", - textBaseline: "alphabetic", - textAlign: "start", - strokeStyle: "rgba(0, 0, 0, 0)", - fillStyle: "#000", - x: 0, - y: 0, - text: "" - }, - triggers: { - fontStyle: "fontX,bbox", - fontVariant: "fontX,bbox", - fontWeight: "fontX,bbox", - fontSize: "fontX,bbox", - fontFamily: "fontX,bbox", - font: "font,bbox,canvas", - textBaseline: "bbox", - textAlign: "bbox", - x: "bbox", - y: "bbox", - text: "bbox" - }, - updaters: { - fontX: "makeFontShorthand", - font: "parseFontShorthand" - } - } - }, - constructor: function(e) { - if (e && e.font) { - e = Ext.clone(e); - for (var f in e) { - if (f !== "font" && f.indexOf("font") === 0) { - delete e[f] - } - } - } - Ext.draw.sprite.Sprite.prototype.constructor.call(this, e) - }, - fontValuesMap: { - italic: "fontStyle", - oblique: "fontStyle", - "small-caps": "fontVariant", - bold: "fontWeight", - bolder: "fontWeight", - lighter: "fontWeight", - "100": "fontWeight", - "200": "fontWeight", - "300": "fontWeight", - "400": "fontWeight", - "500": "fontWeight", - "600": "fontWeight", - "700": "fontWeight", - "800": "fontWeight", - "900": "fontWeight", - "xx-small": "fontSize", - "x-small": "fontSize", - small: "fontSize", - medium: "fontSize", - large: "fontSize", - "x-large": "fontSize", - "xx-large": "fontSize" - }, - makeFontShorthand: function(e) { - var f = []; - if (e.fontStyle) { - f.push(e.fontStyle) - } - if (e.fontVariant) { - f.push(e.fontVariant) - } - if (e.fontWeight) { - f.push(e.fontWeight) - } - if (e.fontSize) { - f.push(e.fontSize) - } - if (e.fontFamily) { - f.push(e.fontFamily) - } - this.setAttributes({ - font: f.join(" ") - }, true) - }, - parseFontShorthand: function(j) { - var m = j.font, - k = m.length, - l = {}, - n = this.fontValuesMap, - e = 0, - i, g, f, h; - while (e < k && i !== -1) { - i = m.indexOf(" ", e); - if (i < 0) { - f = m.substr(e) - } else { - if (i > e) { - f = m.substr(e, i - e) - } else { - continue - } - } - g = f.indexOf("/"); - if (g > 0) { - f = f.substr(0, g) - } else { - if (g === 0) { - continue - } - } - if (f !== "normal" && f !== "inherit") { - h = n[f]; - if (h) { - l[h] = f - } else { - if (f.match(Ext.dom.Element.unitRe)) { - l.fontSize = f - } else { - l.fontFamily = m.substr(e); - break - } - } - } - e = i + 1 - } - if (!l.fontStyle) { - l.fontStyle = "" - } - if (!l.fontVariant) { - l.fontVariant = "" - } - if (!l.fontWeight) { - l.fontWeight = "" - } - this.setAttributes(l, true) - }, - fontProperties: { - fontStyle: true, - fontVariant: true, - fontWeight: true, - fontSize: true, - fontFamily: true - }, - setAttributes: function(g, i, e) { - var f, h; - if (g && g.font) { - h = {}; - for (f in g) { - if (!(f in this.fontProperties)) { - h[f] = g[f] - } - } - g = h - } - this.callParent([g, i, e]) - }, - getBBox: function(g) { - var h = this, - f = h.attr.bbox.plain, - e = h.getSurface(); - if (f.dirty) { - h.updatePlainBBox(f); - f.dirty = false - } - if (e.getInherited().rtl && e.getFlipRtlText()) { - h.updatePlainBBox(f, true) - } - return h.callParent([g]) - }, - rtlAlignments: { - start: "end", - center: "center", - end: "start" - }, - updatePlainBBox: function(k, B) { - var C = this, - w = C.attr, - o = w.x, - n = w.y, - q = [], - t = w.font, - r = w.text, - s = w.textBaseline, - l = w.textAlign, - u = (B && C.oldSize) ? C.oldSize : (C.oldSize = Ext.draw.TextMeasurer.measureText(r, t)), - z = C.getSurface(), - p = z.getInherited().rtl, - v = p && z.getFlipRtlText(), - h = z.getRect(), - f = u.sizes, - g = u.height, - j = u.width, - m = f ? f.length : 0, - e, A = 0; - switch (s) { - case "hanging": - case "top": - break; - case "ideographic": - case "bottom": - n -= g; - break; - case "alphabetic": - n -= g * 0.8; - break; - case "middle": - n -= g * 0.5; - break - } - if (v) { - o = h[2] - h[0] - o; - l = C.rtlAlignments[l] - } - switch (l) { - case "start": - if (p) { - for (; A < m; A++) { - e = f[A].width; - q.push(-(j - e)) - } - } - break; - case "end": - o -= j; - if (p) { - break - } - for (; A < m; A++) { - e = f[A].width; - q.push(j - e) - } - break; - case "center": - o -= j * 0.5; - for (; A < m; A++) { - e = f[A].width; - q.push((p ? -1 : 1) * (j - e) * 0.5) - } - break - } - w.textAlignOffsets = q; - k.x = o; - k.y = n; - k.width = j; - k.height = g - }, - setText: function(e) { - this.setAttributes({ - text: e - }, true) - }, - render: function(e, q, k) { - var h = this, - g = h.attr, - p = Ext.draw.Matrix.fly(g.matrix.elements.slice(0)), - o = h.getBBox(true), - s = g.textAlignOffsets, - m = Ext.draw.Color.RGBA_NONE, - l, j, f, r, n; - if (g.text.length === 0) { - return - } - r = g.text.split(h.lineBreakRe); - n = o.height / r.length; - l = g.bbox.plain.x; - j = g.bbox.plain.y + n * 0.78; - p.toContext(q); - if (e.getInherited().rtl) { - l += g.bbox.plain.width - } - for (f = 0; f < r.length; f++) { - if (q.fillStyle !== m) { - q.fillText(r[f], l + (s[f] || 0), j + n * f) - } - if (q.strokeStyle !== m) { - q.strokeText(r[f], l + (s[f] || 0), j + n * f) - } - } - } - } -}); -Ext.define("Ext.draw.sprite.Tick", { - extend: "Ext.draw.sprite.Line", - alias: "sprite.tick", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "tick", - y: "tick", - size: "tick" - }, - updaters: { - tick: function(b) { - var d = b.size * 1.5, - c = b.lineWidth / 2, - a = b.x, - e = b.y; - this.setAttributes({ - fromX: a - c, - fromY: e - d, - toX: a - c, - toY: e + d - }) - } - } - } - } -}); -Ext.define("Ext.draw.sprite.Triangle", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.triangle", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 2.2, - a = b.x, - e = b.y; - d.fromSvgString("M".concat(a, ",", e, "m0-", c * 0.58, "l", c * 0.5, ",", c * 0.87, "-", c, ",0z")) - } -}); -Ext.define("Ext.draw.gradient.Linear", { - extend: "Ext.draw.gradient.Gradient", - requires: ["Ext.draw.Color"], - type: "linear", - config: { - degrees: 0, - radians: 0 - }, - applyRadians: function(b, a) { - if (Ext.isNumber(b)) { - return b - } - return a - }, - applyDegrees: function(b, a) { - if (Ext.isNumber(b)) { - return b - } - return a - }, - updateRadians: function(a) { - this.setDegrees(Ext.draw.Draw.degrees(a)) - }, - updateDegrees: function(a) { - this.setRadians(Ext.draw.Draw.rad(a)) - }, - generateGradient: function(q, o) { - var c = this.getRadians(), - p = Math.cos(c), - j = Math.sin(c), - m = o.width, - f = o.height, - d = o.x + m * 0.5, - b = o.y + f * 0.5, - n = this.getStops(), - g = n.length, - k, a, e; - if (Ext.isNumber(d + b) && f > 0 && m > 0) { - a = (Math.sqrt(f * f + m * m) * Math.abs(Math.cos(c - Math.atan(f / m)))) / 2; - k = q.createLinearGradient(d + p * a, b + j * a, d - p * a, b - j * a); - for (e = 0; e < g; e++) { - k.addColorStop(n[e].offset, n[e].color) - } - return k - } - return Ext.draw.Color.NONE - } -}); -Ext.define("Ext.draw.gradient.Radial", { - extend: "Ext.draw.gradient.Gradient", - type: "radial", - config: { - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - } - }, - applyStart: function(a, b) { - if (!b) { - return a - } - var c = { - x: b.x, - y: b.y, - r: b.r - }; - if ("x" in a) { - c.x = a.x - } else { - if ("centerX" in a) { - c.x = a.centerX - } - } - if ("y" in a) { - c.y = a.y - } else { - if ("centerY" in a) { - c.y = a.centerY - } - } - if ("r" in a) { - c.r = a.r - } else { - if ("radius" in a) { - c.r = a.radius - } - } - return c - }, - applyEnd: function(b, a) { - if (!a) { - return b - } - var c = { - x: a.x, - y: a.y, - r: a.r - }; - if ("x" in b) { - c.x = b.x - } else { - if ("centerX" in b) { - c.x = b.centerX - } - } - if ("y" in b) { - c.y = b.y - } else { - if ("centerY" in b) { - c.y = b.centerY - } - } - if ("r" in b) { - c.r = b.r - } else { - if ("radius" in b) { - c.r = b.radius - } - } - return c - }, - generateGradient: function(n, m) { - var a = this.getStart(), - b = this.getEnd(), - k = m.width * 0.5, - d = m.height * 0.5, - j = m.x + k, - f = m.y + d, - g = n.createRadialGradient(j + a.x * k, f + a.y * d, a.r * Math.max(k, d), j + b.x * k, f + b.y * d, b.r * Math.max(k, d)), - l = this.getStops(), - e = l.length, - c; - for (c = 0; c < e; c++) { - g.addColorStop(l[c].offset, l[c].color) - } - return g - } -}); -Ext.define("Ext.draw.Surface", { - extend: "Ext.draw.SurfaceBase", - xtype: "surface", - requires: ["Ext.draw.sprite.*", "Ext.draw.gradient.*", "Ext.draw.sprite.AttributeDefinition", "Ext.draw.Matrix", "Ext.draw.Draw"], - uses: ["Ext.draw.engine.Canvas"], - devicePixelRatio: window.devicePixelRatio || window.screen.deviceXDPI / window.screen.logicalXDPI, - deprecated: { - "5.1.0": { - statics: { - methods: { - stableSort: function(a) { - return Ext.Array.sort(a, function(d, c) { - return d.attr.zIndex - c.attr.zIndex - }) - } - } - } - } - }, - config: { - cls: Ext.baseCSSPrefix + "surface", - rect: null, - background: null, - items: [], - dirty: false, - flipRtlText: false - }, - isSurface: true, - isPendingRenderFrame: false, - dirtyPredecessorCount: 0, - constructor: function(a) { - var b = this; - b.predecessors = []; - b.successors = []; - b.map = {}; - b.callParent([a]); - b.matrix = new Ext.draw.Matrix(); - b.inverseMatrix = b.matrix.inverse() - }, - roundPixel: function(a) { - return Math.round(this.devicePixelRatio * a) / this.devicePixelRatio - }, - waitFor: function(a) { - var b = this, - c = b.predecessors; - if (!Ext.Array.contains(c, a)) { - c.push(a); - a.successors.push(b); - if (a.getDirty()) { - b.dirtyPredecessorCount++ - } - } - }, - updateDirty: function(d) { - var c = this.successors, - e = c.length, - b = 0, - a; - for (; b < e; b++) { - a = c[b]; - if (d) { - a.dirtyPredecessorCount++; - a.setDirty(true) - } else { - a.dirtyPredecessorCount--; - if (a.dirtyPredecessorCount === 0 && a.isPendingRenderFrame) { - a.renderFrame() - } - } - } - }, - applyBackground: function(a, b) { - this.setDirty(true); - if (Ext.isString(a)) { - a = { - fillStyle: a - } - } - return Ext.factory(a, Ext.draw.sprite.Rect, b) - }, - applyRect: function(a, b) { - if (b && a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]) { - return - } - if (Ext.isArray(a)) { - return [a[0], a[1], a[2], a[3]] - } else { - if (Ext.isObject(a)) { - return [a.x || a.left, a.y || a.top, a.width || (a.right - a.left), a.height || (a.bottom - a.top)] - } - } - }, - updateRect: function(i) { - var h = this, - c = i[0], - f = i[1], - g = c + i[2], - a = f + i[3], - e = h.getBackground(), - d = h.element; - d.setLocalXY(Math.floor(c), Math.floor(f)); - d.setSize(Math.ceil(g - Math.floor(c)), Math.ceil(a - Math.floor(f))); - if (e) { - e.setAttributes({ - x: 0, - y: 0, - width: Math.ceil(g - Math.floor(c)), - height: Math.ceil(a - Math.floor(f)) - }) - } - h.setDirty(true) - }, - resetTransform: function() { - this.matrix.set(1, 0, 0, 1, 0, 0); - this.inverseMatrix.set(1, 0, 0, 1, 0, 0); - this.setDirty(true) - }, - get: function(a) { - return this.map[a] || this.getItems()[a] - }, - add: function() { - var g = this, - e = Array.prototype.slice.call(arguments), - j = Ext.isArray(e[0]), - a = g.map, - c = [], - f, k, h, b, d; - f = Ext.Array.clean(j ? e[0] : e); - if (!f.length) { - return c - } - for (b = 0, d = f.length; b < d; b++) { - k = f[b]; - h = null; - if (k.isSprite && !a[k.getId()]) { - h = k - } else { - if (!a[k.id]) { - h = this.createItem(k) - } - } - if (h) { - a[h.getId()] = h; - c.push(h); - h.setParent(g); - h.setSurface(g); - g.onAdd(h) - } - } - f = g.getItems(); - if (f) { - f.push.apply(f, c) - } - g.dirtyZIndex = true; - g.setDirty(true); - if (!j && c.length === 1) { - return c[0] - } else { - return c - } - }, - onAdd: Ext.emptyFn, - remove: function(a, c) { - var b = this, - e, d; - if (a) { - if (a.charAt) { - a = b.map[a] - } - if (!a || !a.isSprite) { - return null - } - if (a.isDestroyed || a.isDestroying) { - return a - } - e = a.getId(); - d = b.map[e]; - delete b.map[e]; - if (c) { - a.destroy() - } - if (!d) { - return a - } - a.setParent(null); - a.setSurface(null); - Ext.Array.remove(b.getItems(), a); - b.dirtyZIndex = true; - b.setDirty(true) - } - return a || null - }, - removeAll: function(d) { - var a = this.getItems(), - b = a.length - 1, - c; - if (d) { - for (; b >= 0; b--) { - a[b].destroy() - } - } else { - for (; b >= 0; b--) { - c = a[b]; - c.setParent(null); - c.setSurface(null) - } - } - a.length = 0; - this.map = {}; - this.dirtyZIndex = true - }, - applyItems: function(a) { - if (this.getItems()) { - this.removeAll(true) - } - return Ext.Array.from(this.add(a)) - }, - createItem: function(a) { - return Ext.create(a.xclass || "sprite." + a.type, a) - }, - getBBox: function(f, b) { - var f = Ext.Array.from(f), - c = Infinity, - h = -Infinity, - g = Infinity, - a = -Infinity, - j, k, d, e; - for (d = 0, e = f.length; d < e; d++) { - j = f[d]; - k = j.getBBox(b); - if (c > k.x) { - c = k.x - } - if (h < k.x + k.width) { - h = k.x + k.width - } - if (g > k.y) { - g = k.y - } - if (a < k.y + k.height) { - a = k.y + k.height - } - } - return { - x: c, - y: g, - width: h - c, - height: a - g - } - }, - emptyRect: [0, 0, 0, 0], - getEventXY: function(d) { - var g = this, - f = g.getInherited().rtl, - c = d.getXY(), - a = g.getOwnerBody(), - i = a.getXY(), - h = g.getRect() || g.emptyRect, - j = [], - b; - if (f) { - b = a.getWidth(); - j[0] = i[0] - c[0] - h[0] + b - } else { - j[0] = c[0] - i[0] - h[0] - } - j[1] = c[1] - i[1] - h[1]; - return j - }, - clear: Ext.emptyFn, - orderByZIndex: function() { - var d = this, - a = d.getItems(), - e = false, - b, c; - if (d.getDirty()) { - for (b = 0, c = a.length; b < c; b++) { - if (a[b].attr.dirtyZIndex) { - e = true; - break - } - } - if (e) { - Ext.Array.sort(a, function(g, f) { - return g.attr.zIndex - f.attr.zIndex - }); - this.setDirty(true) - } - for (b = 0, c = a.length; b < c; b++) { - a[b].attr.dirtyZIndex = false - } - } - }, - repaint: function() { - var a = this; - a.repaint = Ext.emptyFn; - Ext.defer(function() { - delete a.repaint; - a.element.repaint() - }, 1) - }, - renderFrame: function() { - var g = this; - if (!g.element) { - return - } - if (g.dirtyPredecessorCount > 0) { - g.isPendingRenderFrame = true; - return - } - var f = g.getRect(), - c = g.getBackground(), - a = g.getItems(), - e, b, d; - if (!f) { - return - } - g.orderByZIndex(); - if (g.getDirty()) { - g.clear(); - g.clearTransform(); - if (c) { - g.renderSprite(c) - } - for (b = 0, d = a.length; b < d; b++) { - e = a[b]; - if (g.renderSprite(e) === false) { - return - } - e.attr.textPositionCount = g.textPosition - } - g.setDirty(false) - } - }, - renderSprite: Ext.emptyFn, - clearTransform: Ext.emptyFn, - destroy: function() { - var a = this; - a.removeAll(true); - a.predecessors = null; - a.successors = null; - a.callParent() - } -}); -Ext.define("Ext.draw.overrides.Surface", { - override: "Ext.draw.Surface", - hitTest: function(b, c) { - var f = this, - g = f.getItems(), - e, d, a; - c = c || Ext.draw.sprite.Sprite.defaultHitTestOptions; - for (e = g.length - 1; e >= 0; e--) { - d = g[e]; - if (d.hitTest) { - a = d.hitTest(b, c); - if (a) { - return a - } - } - } - return null - }, - hitTestEvent: function(b, a) { - var c = this.getEventXY(b); - return this.hitTest(c, a) - } -}); -Ext.define("Ext.draw.engine.SvgContext", { - requires: ["Ext.draw.Color"], - toSave: ["strokeOpacity", "strokeStyle", "fillOpacity", "fillStyle", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "lineDash", "lineDashOffset", "miterLimit", "shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor", "globalCompositeOperation", "position", "fillGradient", "strokeGradient"], - strokeOpacity: 1, - strokeStyle: "none", - fillOpacity: 1, - fillStyle: "none", - lineDash: [], - lineDashOffset: 0, - globalAlpha: 1, - lineWidth: 1, - lineCap: "butt", - lineJoin: "miter", - miterLimit: 10, - shadowOffsetX: 0, - shadowOffsetY: 0, - shadowBlur: 0, - shadowColor: "none", - globalCompositeOperation: "src", - urlStringRe: /^url\(#([\w\-]+)\)$/, - constructor: function(a) { - this.surface = a; - this.state = []; - this.matrix = new Ext.draw.Matrix(); - this.path = null; - this.clear() - }, - clear: function() { - this.group = this.surface.mainGroup; - this.position = 0; - this.path = null - }, - getElement: function(a) { - return this.surface.getSvgElement(this.group, a, this.position++) - }, - removeElement: function(d) { - var d = Ext.fly(d), - h, g, b, f, a, e, c; - if (!d) { - return - } - if (d.dom.tagName === "g") { - a = d.dom.gradients; - for (c in a) { - a[c].destroy() - } - } else { - h = d.getAttribute("fill"); - g = d.getAttribute("stroke"); - b = h && h.match(this.urlStringRe); - f = g && g.match(this.urlStringRe); - if (b && b[1]) { - e = Ext.fly(b[1]); - if (e) { - e.destroy() - } - } - if (f && f[1]) { - e = Ext.fly(f[1]); - if (e) { - e.destroy() - } - } - } - d.destroy() - }, - save: function() { - var c = this.toSave, - e = {}, - d = this.getElement("g"), - b, a; - for (a = 0; a < c.length; a++) { - b = c[a]; - if (b in this) { - e[b] = this[b] - } - } - this.position = 0; - e.matrix = this.matrix.clone(); - this.state.push(e); - this.group = d; - return d - }, - restore: function() { - var d = this.toSave, - e = this.state.pop(), - c = this.group.dom.childNodes, - b, a; - while (c.length > this.position) { - this.removeElement(c[c.length - 1]) - } - for (a = 0; a < d.length; a++) { - b = d[a]; - if (b in e) { - this[b] = e[b] - } else { - delete this[b] - } - } - this.setTransform.apply(this, e.matrix.elements); - this.group = this.group.getParent() - }, - transform: function(f, b, e, g, d, c) { - if (this.path) { - var a = Ext.draw.Matrix.fly([f, b, e, g, d, c]).inverse(); - this.path.transform(a) - } - this.matrix.append(f, b, e, g, d, c) - }, - setTransform: function(e, a, d, f, c, b) { - if (this.path) { - this.path.transform(this.matrix) - } - this.matrix.reset(); - this.transform(e, a, d, f, c, b) - }, - scale: function(a, b) { - this.transform(a, 0, 0, b, 0, 0) - }, - rotate: function(d) { - var c = Math.cos(d), - a = Math.sin(d), - b = -Math.sin(d), - e = Math.cos(d); - this.transform(c, a, b, e, 0, 0) - }, - translate: function(a, b) { - this.transform(1, 0, 0, 1, a, b) - }, - setGradientBBox: function(a) { - this.bbox = a - }, - beginPath: function() { - this.path = new Ext.draw.Path() - }, - moveTo: function(a, b) { - if (!this.path) { - this.beginPath() - } - this.path.moveTo(a, b); - this.path.element = null - }, - lineTo: function(a, b) { - if (!this.path) { - this.beginPath() - } - this.path.lineTo(a, b); - this.path.element = null - }, - rect: function(b, d, c, a) { - this.moveTo(b, d); - this.lineTo(b + c, d); - this.lineTo(b + c, d + a); - this.lineTo(b, d + a); - this.closePath() - }, - strokeRect: function(b, d, c, a) { - this.beginPath(); - this.rect(b, d, c, a); - this.stroke() - }, - fillRect: function(b, d, c, a) { - this.beginPath(); - this.rect(b, d, c, a); - this.fill() - }, - closePath: function() { - if (!this.path) { - this.beginPath() - } - this.path.closePath(); - this.path.element = null - }, - arcSvg: function(d, a, f, g, c, b, e) { - if (!this.path) { - this.beginPath() - } - this.path.arcSvg(d, a, f, g, c, b, e); - this.path.element = null - }, - arc: function(b, f, a, d, c, e) { - if (!this.path) { - this.beginPath() - } - this.path.arc(b, f, a, d, c, e); - this.path.element = null - }, - ellipse: function(a, h, g, f, d, c, b, e) { - if (!this.path) { - this.beginPath() - } - this.path.ellipse(a, h, g, f, d, c, b, e); - this.path.element = null - }, - arcTo: function(b, e, a, d, g, f, c) { - if (!this.path) { - this.beginPath() - } - this.path.arcTo(b, e, a, d, g, f, c); - this.path.element = null - }, - bezierCurveTo: function(d, f, b, e, a, c) { - if (!this.path) { - this.beginPath() - } - this.path.bezierCurveTo(d, f, b, e, a, c); - this.path.element = null - }, - strokeText: function(d, a, e) { - d = String(d); - if (this.strokeStyle) { - var b = this.getElement("text"), - c = this.surface.getSvgElement(b, "tspan", 0); - this.surface.setElementAttributes(b, { - x: a, - y: e, - transform: this.matrix.toSvg(), - stroke: this.strokeStyle, - fill: "none", - opacity: this.globalAlpha, - "stroke-opacity": this.strokeOpacity, - style: "font: " + this.font, - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }); - if (this.lineDash.length) { - this.surface.setElementAttributes(b, { - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }) - } - if (c.dom.firstChild) { - c.dom.removeChild(c.dom.firstChild) - } - this.surface.setElementAttributes(c, { - "alignment-baseline": "alphabetic" - }); - c.dom.appendChild(document.createTextNode(Ext.String.htmlDecode(d))) - } - }, - fillText: function(d, a, e) { - d = String(d); - if (this.fillStyle) { - var b = this.getElement("text"), - c = this.surface.getSvgElement(b, "tspan", 0); - this.surface.setElementAttributes(b, { - x: a, - y: e, - transform: this.matrix.toSvg(), - fill: this.fillStyle, - opacity: this.globalAlpha, - "fill-opacity": this.fillOpacity, - style: "font: " + this.font - }); - if (c.dom.firstChild) { - c.dom.removeChild(c.dom.firstChild) - } - this.surface.setElementAttributes(c, { - "alignment-baseline": "alphabetic" - }); - c.dom.appendChild(document.createTextNode(Ext.String.htmlDecode(d))) - } - }, - drawImage: function(c, k, i, l, e, p, n, a, g) { - var f = this, - d = f.getElement("image"), - j = k, - h = i, - b = typeof l === "undefined" ? c.width : l, - m = typeof e === "undefined" ? c.height : e, - o = null; - if (typeof g !== "undefined") { - o = k + " " + i + " " + l + " " + e; - j = p; - h = n; - b = a; - m = g - } - d.dom.setAttributeNS("http://www.w3.org/1999/xlink", "href", c.src); - f.surface.setElementAttributes(d, { - viewBox: o, - x: j, - y: h, - width: b, - height: m, - opacity: f.globalAlpha, - transform: f.matrix.toSvg() - }) - }, - fill: function() { - if (!this.path) { - return - } - if (this.fillStyle) { - var c, a = this.fillGradient, - d = this.bbox, - b = this.path.element; - if (!b) { - c = this.path.toString(); - b = this.path.element = this.getElement("path"); - this.surface.setElementAttributes(b, { - d: c, - transform: this.matrix.toSvg() - }) - } - this.surface.setElementAttributes(b, { - fill: a && d ? a.generateGradient(this, d) : this.fillStyle, - "fill-opacity": this.fillOpacity * this.globalAlpha - }) - } - }, - stroke: function() { - if (!this.path) { - return - } - if (this.strokeStyle) { - var c, b = this.strokeGradient, - d = this.bbox, - a = this.path.element; - if (!a || !this.path.svgString) { - c = this.path.toString(); - if (!c) { - return - } - a = this.path.element = this.getElement("path"); - this.surface.setElementAttributes(a, { - fill: "none", - d: c, - transform: this.matrix.toSvg() - }) - } - this.surface.setElementAttributes(a, { - stroke: b && d ? b.generateGradient(this, d) : this.strokeStyle, - "stroke-linecap": this.lineCap, - "stroke-linejoin": this.lineJoin, - "stroke-width": this.lineWidth, - "stroke-opacity": this.strokeOpacity * this.globalAlpha, - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }); - if (this.lineDash.length) { - this.surface.setElementAttributes(a, { - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }) - } - } - }, - fillStroke: function(a, e) { - var b = this, - d = b.fillStyle, - g = b.strokeStyle, - c = b.fillOpacity, - f = b.strokeOpacity; - if (e === undefined) { - e = a.transformFillStroke - } - if (!e) { - a.inverseMatrix.toContext(b) - } - if (d && c !== 0) { - b.fill() - } - if (g && f !== 0) { - b.stroke() - } - }, - appendPath: function(a) { - this.path = a.clone() - }, - setLineDash: function(a) { - this.lineDash = a - }, - getLineDash: function() { - return this.lineDash - }, - createLinearGradient: function(d, g, b, e) { - var f = this, - c = f.surface.getNextDef("linearGradient"), - a = f.group.dom.gradients || (f.group.dom.gradients = {}), - h; - f.surface.setElementAttributes(c, { - x1: d, - y1: g, - x2: b, - y2: e, - gradientUnits: "userSpaceOnUse" - }); - h = new Ext.draw.engine.SvgContext.Gradient(f, f.surface, c); - a[c.dom.id] = h; - return h - }, - createRadialGradient: function(b, j, d, a, i, c) { - var g = this, - e = g.surface.getNextDef("radialGradient"), - f = g.group.dom.gradients || (g.group.dom.gradients = {}), - h; - g.surface.setElementAttributes(e, { - fx: b, - fy: j, - cx: a, - cy: i, - r: c, - gradientUnits: "userSpaceOnUse" - }); - h = new Ext.draw.engine.SvgContext.Gradient(g, g.surface, e, d / c); - f[e.dom.id] = h; - return h - } -}); -Ext.define("Ext.draw.engine.SvgContext.Gradient", { - statics: { - map: {} - }, - constructor: function(c, a, d, b) { - var f = this.statics().map, - e; - e = f[d.dom.id]; - if (e) { - e.element = null - } - f[d.dom.id] = this; - this.ctx = c; - this.surface = a; - this.element = d; - this.position = 0; - this.compression = b || 0 - }, - addColorStop: function(d, b) { - var c = this.surface.getSvgElement(this.element, "stop", this.position++), - a = this.compression; - this.surface.setElementAttributes(c, { - offset: (((1 - a) * d + a) * 100).toFixed(2) + "%", - "stop-color": b, - "stop-opacity": Ext.draw.Color.fly(b).a.toFixed(15) - }) - }, - toString: function() { - var a = this.element.dom.childNodes; - while (a.length > this.position) { - Ext.fly(a[a.length - 1]).destroy() - } - return "url(#" + this.element.getId() + ")" - }, - destroy: function() { - var b = this.statics().map, - a = this.element; - if (a && a.dom) { - delete b[a.dom.id]; - a.destroy() - } - this.callParent() - } -}); -Ext.define("Ext.draw.engine.Svg", { - extend: "Ext.draw.Surface", - requires: ["Ext.draw.engine.SvgContext"], - statics: { - BBoxTextCache: {} - }, - config: { - highPrecision: false - }, - getElementConfig: function() { - return { - reference: "element", - style: { - position: "absolute" - }, - children: [{ - reference: "innerElement", - style: { - width: "100%", - height: "100%", - position: "relative" - }, - children: [{ - tag: "svg", - reference: "svgElement", - namespace: "http://www.w3.org/2000/svg", - width: "100%", - height: "100%", - version: 1.1 - }] - }] - } - }, - constructor: function(a) { - var b = this; - b.callParent([a]); - b.mainGroup = b.createSvgNode("g"); - b.defElement = b.createSvgNode("defs"); - b.svgElement.appendChild(b.mainGroup); - b.svgElement.appendChild(b.defElement); - b.ctx = new Ext.draw.engine.SvgContext(b) - }, - createSvgNode: function(a) { - var b = document.createElementNS("http://www.w3.org/2000/svg", a); - return Ext.get(b) - }, - getSvgElement: function(d, b, a) { - var c; - if (d.dom.childNodes.length > a) { - c = d.dom.childNodes[a]; - if (c.tagName === b) { - return Ext.get(c) - } else { - Ext.destroy(c) - } - } - c = Ext.get(this.createSvgNode(b)); - if (a === 0) { - d.insertFirst(c) - } else { - c.insertAfter(Ext.fly(d.dom.childNodes[a - 1])) - } - c.cache = {}; - return c - }, - setElementAttributes: function(d, b) { - var f = d.dom, - a = d.cache, - c, e; - for (c in b) { - e = b[c]; - if (a[c] !== e) { - a[c] = e; - f.setAttribute(c, e) - } - } - }, - getNextDef: function(a) { - return this.getSvgElement(this.defElement, a, this.defPosition++) - }, - clearTransform: function() { - var a = this; - a.mainGroup.set({ - transform: a.matrix.toSvg() - }) - }, - clear: function() { - this.ctx.clear(); - this.defPosition = 0 - }, - renderSprite: function(b) { - var d = this, - c = d.getRect(), - a = d.ctx; - if (b.attr.hidden || b.attr.globalAlpha === 0) { - a.save(); - a.restore(); - return - } - b.element = a.save(); - b.preRender(this); - b.useAttributes(a, c); - if (false === b.render(this, a, [0, 0, c[2], c[3]])) { - return false - } - b.setDirty(false); - a.restore() - }, - flatten: function(e, b) { - var c = '', - f = Ext.getClassName(this), - a, g, d; - c += ''; - for (d = 0; d < b.length; d++) { - a = b[d]; - if (Ext.getClassName(a) !== f) { - continue - } - g = a.getRect(); - c += ''; - c += this.serializeNode(a.svgElement.dom); - c += "" - } - c += ""; - return { - data: "data:image/svg+xml;utf8," + encodeURIComponent(c), - type: "svg" - } - }, - serializeNode: function(d) { - var b = "", - c, f, a, e; - if (d.nodeType === document.TEXT_NODE) { - return d.nodeValue - } - b += "<" + d.nodeName; - if (d.attributes.length) { - for (c = 0, f = d.attributes.length; c < f; c++) { - a = d.attributes[c]; - b += " " + a.name + '="' + a.value + '"' - } - } - b += ">"; - if (d.childNodes && d.childNodes.length) { - for (c = 0, f = d.childNodes.length; c < f; c++) { - e = d.childNodes[c]; - b += this.serializeNode(e) - } - } - b += ""; - return b - }, - destroy: function() { - var a = this; - a.ctx.destroy(); - a.mainGroup.destroy(); - delete a.mainGroup; - delete a.ctx; - a.callParent() - }, - remove: function(a, b) { - if (a && a.element) { - if (this.ctx) { - this.ctx.removeElement(a.element) - } else { - a.element.destroy() - } - a.element = null - } - this.callParent(arguments) - } -}); -Ext.draw || (Ext.draw = {}); -Ext.draw.engine || (Ext.draw.engine = {}); -Ext.draw.engine.excanvas = true; -if (!document.createElement("canvas").getContext) { - (function() { - var ab = Math; - var n = ab.round; - var l = ab.sin; - var A = ab.cos; - var H = ab.abs; - var N = ab.sqrt; - var d = 10; - var f = d / 2; - var z = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1]; - - function y() { - return this.context_ || (this.context_ = new D(this)) - } - var t = Array.prototype.slice; - - function g(j, m, p) { - var i = t.call(arguments, 2); - return function() { - return j.apply(m, i.concat(t.call(arguments))) - } - } - - function af(i) { - return String(i).replace(/&/g, "&").replace(/"/g, """) - } - - function Y(m, j, i) { - Ext.onReady(function() { - if (!m.namespaces[j]) { - m.namespaces.add(j, i, "#default#VML") - } - }) - } - - function R(j) { - Y(j, "g_vml_", "urn:schemas-microsoft-com:vml"); - Y(j, "g_o_", "urn:schemas-microsoft-com:office:office"); - if (!j.styleSheets.ex_canvas_) { - var i = j.createStyleSheet(); - i.owningElement.id = "ex_canvas_"; - i.cssText = "canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}" - } - } - R(document); - var e = { - init: function(i) { - var j = i || document; - j.createElement("canvas"); - j.attachEvent("onreadystatechange", g(this.init_, this, j)) - }, - init_: function(p) { - var m = p.getElementsByTagName("canvas"); - for (var j = 0; j < m.length; j++) { - this.initElement(m[j]) - } - }, - initElement: function(j) { - if (!j.getContext) { - j.getContext = y; - R(j.ownerDocument); - j.innerHTML = ""; - j.attachEvent("onpropertychange", x); - j.attachEvent("onresize", W); - var i = j.attributes; - if (i.width && i.width.specified) { - j.style.width = i.width.nodeValue + "px" - } else { - j.width = j.clientWidth - } - if (i.height && i.height.specified) { - j.style.height = i.height.nodeValue + "px" - } else { - j.height = j.clientHeight - } - } - return j - } - }; - - function x(j) { - var i = j.srcElement; - switch (j.propertyName) { - case "width": - i.getContext().clearRect(); - i.style.width = i.attributes.width.nodeValue + "px"; - i.firstChild.style.width = i.clientWidth + "px"; - break; - case "height": - i.getContext().clearRect(); - i.style.height = i.attributes.height.nodeValue + "px"; - i.firstChild.style.height = i.clientHeight + "px"; - break - } - } - - function W(j) { - var i = j.srcElement; - if (i.firstChild) { - i.firstChild.style.width = i.clientWidth + "px"; - i.firstChild.style.height = i.clientHeight + "px" - } - } - e.init(); - var k = []; - for (var ae = 0; ae < 16; ae++) { - for (var ad = 0; ad < 16; ad++) { - k[ae * 16 + ad] = ae.toString(16) + ad.toString(16) - } - } - - function B() { - return [ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1] - ] - } - - function J(p, m) { - var j = B(); - for (var i = 0; i < 3; i++) { - for (var ah = 0; ah < 3; ah++) { - var Z = 0; - for (var ag = 0; ag < 3; ag++) { - Z += p[i][ag] * m[ag][ah] - } - j[i][ah] = Z - } - } - return j - } - - function v(j, i) { - i.fillStyle = j.fillStyle; - i.lineCap = j.lineCap; - i.lineJoin = j.lineJoin; - i.lineDash = j.lineDash; - i.lineWidth = j.lineWidth; - i.miterLimit = j.miterLimit; - i.shadowBlur = j.shadowBlur; - i.shadowColor = j.shadowColor; - i.shadowOffsetX = j.shadowOffsetX; - i.shadowOffsetY = j.shadowOffsetY; - i.strokeStyle = j.strokeStyle; - i.globalAlpha = j.globalAlpha; - i.font = j.font; - i.textAlign = j.textAlign; - i.textBaseline = j.textBaseline; - i.arcScaleX_ = j.arcScaleX_; - i.arcScaleY_ = j.arcScaleY_; - i.lineScale_ = j.lineScale_ - } - var b = { - aliceblue: "#F0F8FF", - antiquewhite: "#FAEBD7", - aquamarine: "#7FFFD4", - azure: "#F0FFFF", - beige: "#F5F5DC", - bisque: "#FFE4C4", - black: "#000000", - blanchedalmond: "#FFEBCD", - blueviolet: "#8A2BE2", - brown: "#A52A2A", - burlywood: "#DEB887", - cadetblue: "#5F9EA0", - chartreuse: "#7FFF00", - chocolate: "#D2691E", - coral: "#FF7F50", - cornflowerblue: "#6495ED", - cornsilk: "#FFF8DC", - crimson: "#DC143C", - cyan: "#00FFFF", - darkblue: "#00008B", - darkcyan: "#008B8B", - darkgoldenrod: "#B8860B", - darkgray: "#A9A9A9", - darkgreen: "#006400", - darkgrey: "#A9A9A9", - darkkhaki: "#BDB76B", - darkmagenta: "#8B008B", - darkolivegreen: "#556B2F", - darkorange: "#FF8C00", - darkorchid: "#9932CC", - darkred: "#8B0000", - darksalmon: "#E9967A", - darkseagreen: "#8FBC8F", - darkslateblue: "#483D8B", - darkslategray: "#2F4F4F", - darkslategrey: "#2F4F4F", - darkturquoise: "#00CED1", - darkviolet: "#9400D3", - deeppink: "#FF1493", - deepskyblue: "#00BFFF", - dimgray: "#696969", - dimgrey: "#696969", - dodgerblue: "#1E90FF", - firebrick: "#B22222", - floralwhite: "#FFFAF0", - forestgreen: "#228B22", - gainsboro: "#DCDCDC", - ghostwhite: "#F8F8FF", - gold: "#FFD700", - goldenrod: "#DAA520", - grey: "#808080", - greenyellow: "#ADFF2F", - honeydew: "#F0FFF0", - hotpink: "#FF69B4", - indianred: "#CD5C5C", - indigo: "#4B0082", - ivory: "#FFFFF0", - khaki: "#F0E68C", - lavender: "#E6E6FA", - lavenderblush: "#FFF0F5", - lawngreen: "#7CFC00", - lemonchiffon: "#FFFACD", - lightblue: "#ADD8E6", - lightcoral: "#F08080", - lightcyan: "#E0FFFF", - lightgoldenrodyellow: "#FAFAD2", - lightgreen: "#90EE90", - lightgrey: "#D3D3D3", - lightpink: "#FFB6C1", - lightsalmon: "#FFA07A", - lightseagreen: "#20B2AA", - lightskyblue: "#87CEFA", - lightslategray: "#778899", - lightslategrey: "#778899", - lightsteelblue: "#B0C4DE", - lightyellow: "#FFFFE0", - limegreen: "#32CD32", - linen: "#FAF0E6", - magenta: "#FF00FF", - mediumaquamarine: "#66CDAA", - mediumblue: "#0000CD", - mediumorchid: "#BA55D3", - mediumpurple: "#9370DB", - mediumseagreen: "#3CB371", - mediumslateblue: "#7B68EE", - mediumspringgreen: "#00FA9A", - mediumturquoise: "#48D1CC", - mediumvioletred: "#C71585", - midnightblue: "#191970", - mintcream: "#F5FFFA", - mistyrose: "#FFE4E1", - moccasin: "#FFE4B5", - navajowhite: "#FFDEAD", - oldlace: "#FDF5E6", - olivedrab: "#6B8E23", - orange: "#FFA500", - orangered: "#FF4500", - orchid: "#DA70D6", - palegoldenrod: "#EEE8AA", - palegreen: "#98FB98", - paleturquoise: "#AFEEEE", - palevioletred: "#DB7093", - papayawhip: "#FFEFD5", - peachpuff: "#FFDAB9", - peru: "#CD853F", - pink: "#FFC0CB", - plum: "#DDA0DD", - powderblue: "#B0E0E6", - rosybrown: "#BC8F8F", - royalblue: "#4169E1", - saddlebrown: "#8B4513", - salmon: "#FA8072", - sandybrown: "#F4A460", - seagreen: "#2E8B57", - seashell: "#FFF5EE", - sienna: "#A0522D", - skyblue: "#87CEEB", - slateblue: "#6A5ACD", - slategray: "#708090", - slategrey: "#708090", - snow: "#FFFAFA", - springgreen: "#00FF7F", - steelblue: "#4682B4", - tan: "#D2B48C", - thistle: "#D8BFD8", - tomato: "#FF6347", - turquoise: "#40E0D0", - violet: "#EE82EE", - wheat: "#F5DEB3", - whitesmoke: "#F5F5F5", - yellowgreen: "#9ACD32" - }; - - function M(j) { - var p = j.indexOf("(", 3); - var i = j.indexOf(")", p + 1); - var m = j.substring(p + 1, i).split(","); - if (m.length != 4 || j.charAt(3) != "a") { - m[3] = 1 - } - return m - } - - function c(i) { - return parseFloat(i) / 100 - } - - function r(j, m, i) { - return Math.min(i, Math.max(m, j)) - } - - function I(ag) { - var i, ai, aj, ah, ak, Z; - ah = parseFloat(ag[0]) / 360 % 360; - if (ah < 0) { - ah++ - } - ak = r(c(ag[1]), 0, 1); - Z = r(c(ag[2]), 0, 1); - if (ak == 0) { - i = ai = aj = Z - } else { - var j = Z < 0.5 ? Z * (1 + ak) : Z + ak - Z * ak; - var m = 2 * Z - j; - i = a(m, j, ah + 1 / 3); - ai = a(m, j, ah); - aj = a(m, j, ah - 1 / 3) - } - return "#" + k[Math.floor(i * 255)] + k[Math.floor(ai * 255)] + k[Math.floor(aj * 255)] - } - - function a(j, i, m) { - if (m < 0) { - m++ - } - if (m > 1) { - m-- - } - if (6 * m < 1) { - return j + (i - j) * 6 * m - } else { - if (2 * m < 1) { - return i - } else { - if (3 * m < 2) { - return j + (i - j) * (2 / 3 - m) * 6 - } else { - return j - } - } - } - } - var C = {}; - - function F(j) { - if (j in C) { - return C[j] - } - var ag, Z = 1; - j = String(j); - if (j.charAt(0) == "#") { - ag = j - } else { - if (/^rgb/.test(j)) { - var p = M(j); - var ag = "#", - ah; - for (var m = 0; m < 3; m++) { - if (p[m].indexOf("%") != -1) { - ah = Math.floor(c(p[m]) * 255) - } else { - ah = +p[m] - } - ag += k[r(ah, 0, 255)] - } - Z = +p[3] - } else { - if (/^hsl/.test(j)) { - var p = M(j); - ag = I(p); - Z = p[3] - } else { - ag = b[j] || j - } - } - } - return C[j] = { - color: ag, - alpha: Z - } - } - var o = { - style: "normal", - variant: "normal", - weight: "normal", - size: 10, - family: "sans-serif" - }; - var L = {}; - - function E(i) { - if (L[i]) { - return L[i] - } - var p = document.createElement("div"); - var m = p.style; - try { - m.font = i - } catch (j) {} - return L[i] = { - style: m.fontStyle || o.style, - variant: m.fontVariant || o.variant, - weight: m.fontWeight || o.weight, - size: m.fontSize || o.size, - family: m.fontFamily || o.family - } - } - - function u(m, j) { - var i = {}; - for (var ah in m) { - i[ah] = m[ah] - } - var ag = parseFloat(j.currentStyle.fontSize), - Z = parseFloat(m.size); - if (typeof m.size == "number") { - i.size = m.size - } else { - if (m.size.indexOf("px") != -1) { - i.size = Z - } else { - if (m.size.indexOf("em") != -1) { - i.size = ag * Z - } else { - if (m.size.indexOf("%") != -1) { - i.size = (ag / 100) * Z - } else { - if (m.size.indexOf("pt") != -1) { - i.size = Z / 0.75 - } else { - i.size = ag - } - } - } - } - } - i.size *= 0.981; - return i - } - - function ac(i) { - return i.style + " " + i.variant + " " + i.weight + " " + i.size + "px " + i.family - } - var s = { - butt: "flat", - round: "round" - }; - - function S(i) { - return s[i] || "square" - } - - function D(i) { - this.m_ = B(); - this.mStack_ = []; - this.aStack_ = []; - this.currentPath_ = []; - this.strokeStyle = "#000"; - this.fillStyle = "#000"; - this.lineWidth = 1; - this.lineJoin = "miter"; - this.lineDash = []; - this.lineCap = "butt"; - this.miterLimit = d * 1; - this.globalAlpha = 1; - this.font = "10px sans-serif"; - this.textAlign = "left"; - this.textBaseline = "alphabetic"; - this.canvas = i; - var m = "width:" + i.clientWidth + "px;height:" + i.clientHeight + "px;overflow:hidden;position:absolute"; - var j = i.ownerDocument.createElement("div"); - j.style.cssText = m; - i.appendChild(j); - var p = j.cloneNode(false); - p.style.backgroundColor = "red"; - p.style.filter = "alpha(opacity=0)"; - i.appendChild(p); - this.element_ = j; - this.arcScaleX_ = 1; - this.arcScaleY_ = 1; - this.lineScale_ = 1 - } - var q = D.prototype; - q.clearRect = function() { - if (this.textMeasureEl_) { - this.textMeasureEl_.removeNode(true); - this.textMeasureEl_ = null - } - this.element_.innerHTML = "" - }; - q.beginPath = function() { - this.currentPath_ = [] - }; - q.moveTo = function(j, i) { - var m = V(this, j, i); - this.currentPath_.push({ - type: "moveTo", - x: m.x, - y: m.y - }); - this.currentX_ = m.x; - this.currentY_ = m.y - }; - q.lineTo = function(j, i) { - var m = V(this, j, i); - this.currentPath_.push({ - type: "lineTo", - x: m.x, - y: m.y - }); - this.currentX_ = m.x; - this.currentY_ = m.y - }; - q.bezierCurveTo = function(m, j, ak, aj, ai, ag) { - var i = V(this, ai, ag); - var ah = V(this, m, j); - var Z = V(this, ak, aj); - K(this, ah, Z, i) - }; - - function K(i, Z, m, j) { - i.currentPath_.push({ - type: "bezierCurveTo", - cp1x: Z.x, - cp1y: Z.y, - cp2x: m.x, - cp2y: m.y, - x: j.x, - y: j.y - }); - i.currentX_ = j.x; - i.currentY_ = j.y - } - q.quadraticCurveTo = function(ai, m, j, i) { - var ah = V(this, ai, m); - var ag = V(this, j, i); - var aj = { - x: this.currentX_ + 2 / 3 * (ah.x - this.currentX_), - y: this.currentY_ + 2 / 3 * (ah.y - this.currentY_) - }; - var Z = { - x: aj.x + (ag.x - this.currentX_) / 3, - y: aj.y + (ag.y - this.currentY_) / 3 - }; - K(this, aj, Z, ag) - }; - q.arc = function(al, aj, ak, ag, j, m) { - ak *= d; - var ap = m ? "at" : "wa"; - var am = al + A(ag) * ak - f; - var ao = aj + l(ag) * ak - f; - var i = al + A(j) * ak - f; - var an = aj + l(j) * ak - f; - if (am == i && !m) { - am += 0.125 - } - var Z = V(this, al, aj); - var ai = V(this, am, ao); - var ah = V(this, i, an); - this.currentPath_.push({ - type: ap, - x: Z.x, - y: Z.y, - radius: ak, - xStart: ai.x, - yStart: ai.y, - xEnd: ah.x, - yEnd: ah.y - }) - }; - q.rect = function(m, j, i, p) { - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath() - }; - q.strokeRect = function(m, j, i, p) { - var Z = this.currentPath_; - this.beginPath(); - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath(); - this.stroke(); - this.currentPath_ = Z - }; - q.fillRect = function(m, j, i, p) { - var Z = this.currentPath_; - this.beginPath(); - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath(); - this.fill(); - this.currentPath_ = Z - }; - q.createLinearGradient = function(j, p, i, m) { - var Z = new U("gradient"); - Z.x0_ = j; - Z.y0_ = p; - Z.x1_ = i; - Z.y1_ = m; - return Z - }; - q.createRadialGradient = function(p, ag, m, j, Z, i) { - var ah = new U("gradientradial"); - ah.x0_ = p; - ah.y0_ = ag; - ah.r0_ = m; - ah.x1_ = j; - ah.y1_ = Z; - ah.r1_ = i; - return ah - }; - q.drawImage = function(an, j) { - var ah, Z, aj, ar, al, ak, ao, av; - var ai = an.runtimeStyle.width; - var am = an.runtimeStyle.height; - an.runtimeStyle.width = "auto"; - an.runtimeStyle.height = "auto"; - var ag = an.width; - var aq = an.height; - an.runtimeStyle.width = ai; - an.runtimeStyle.height = am; - if (arguments.length == 3) { - ah = arguments[1]; - Z = arguments[2]; - al = ak = 0; - ao = aj = ag; - av = ar = aq - } else { - if (arguments.length == 5) { - ah = arguments[1]; - Z = arguments[2]; - aj = arguments[3]; - ar = arguments[4]; - al = ak = 0; - ao = ag; - av = aq - } else { - if (arguments.length == 9) { - al = arguments[1]; - ak = arguments[2]; - ao = arguments[3]; - av = arguments[4]; - ah = arguments[5]; - Z = arguments[6]; - aj = arguments[7]; - ar = arguments[8] - } else { - throw Error("Invalid number of arguments") - } - } - } - var au = V(this, ah, Z); - var at = []; - var i = 10; - var p = 10; - var ap = this.m_; - at.push(" ', '", ""); - this.element_.insertAdjacentHTML("BeforeEnd", at.join("")) - }; - q.setLineDash = function(i) { - if (i.length === 1) { - i = i.slice(); - i[1] = i[0] - } - this.lineDash = i - }; - q.getLineDash = function() { - return this.lineDash - }; - q.stroke = function(ak) { - var ai = []; - var m = 10; - var al = 10; - ai.push(" aj.x) { - aj.x = j.x - } - if (Z.y == null || j.y < Z.y) { - Z.y = j.y - } - if (aj.y == null || j.y > aj.y) { - aj.y = j.y - } - } - } - ai.push(' ">'); - if (!ak) { - w(this, ai) - } else { - G(this, ai, Z, aj) - } - ai.push(""); - this.element_.insertAdjacentHTML("beforeEnd", ai.join("")) - }; - - function w(m, ag) { - var j = F(m.strokeStyle); - var p = j.color; - var Z = j.alpha * m.globalAlpha; - var i = m.lineScale_ * m.lineWidth; - if (i < 1) { - Z *= i - } - ag.push("') - } - - function G(aq, ai, aK, ar) { - var aj = aq.fillStyle; - var aB = aq.arcScaleX_; - var aA = aq.arcScaleY_; - var j = ar.x - aK.x; - var p = ar.y - aK.y; - if (aj instanceof U) { - var an = 0; - var aF = { - x: 0, - y: 0 - }; - var ax = 0; - var am = 1; - if (aj.type_ == "gradient") { - var al = aj.x0_ / aB; - var m = aj.y0_ / aA; - var ak = aj.x1_ / aB; - var aM = aj.y1_ / aA; - var aJ = V(aq, al, m); - var aI = V(aq, ak, aM); - var ag = aI.x - aJ.x; - var Z = aI.y - aJ.y; - an = Math.atan2(ag, Z) * 180 / Math.PI; - if (an < 0) { - an += 360 - } - if (an < 0.000001) { - an = 0 - } - } else { - var aJ = V(aq, aj.x0_, aj.y0_); - aF = { - x: (aJ.x - aK.x) / j, - y: (aJ.y - aK.y) / p - }; - j /= aB * d; - p /= aA * d; - var aD = ab.max(j, p); - ax = 2 * aj.r0_ / aD; - am = 2 * aj.r1_ / aD - ax - } - var av = aj.colors_; - av.sort(function(aN, i) { - return aN.offset - i.offset - }); - var ap = av.length; - var au = av[0].color; - var at = av[ap - 1].color; - var az = av[0].alpha * aq.globalAlpha; - var ay = av[ap - 1].alpha * aq.globalAlpha; - var aE = []; - for (var aH = 0; aH < ap; aH++) { - var ao = av[aH]; - aE.push(ao.offset * am + ax + " " + ao.color) - } - ai.push('') - } else { - if (aj instanceof T) { - if (j && p) { - var ah = -aK.x; - var aC = -aK.y; - ai.push("') - } - } else { - var aL = F(aq.fillStyle); - var aw = aL.color; - var aG = aL.alpha * aq.globalAlpha; - ai.push('') - } - } - } - q.fill = function() { - this.$stroke(true) - }; - q.closePath = function() { - this.currentPath_.push({ - type: "close" - }) - }; - - function V(j, Z, p) { - var i = j.m_; - return { - x: d * (Z * i[0][0] + p * i[1][0] + i[2][0]) - f, - y: d * (Z * i[0][1] + p * i[1][1] + i[2][1]) - f - } - } - q.save = function() { - var i = {}; - v(this, i); - this.aStack_.push(i); - this.mStack_.push(this.m_); - this.m_ = J(B(), this.m_) - }; - q.restore = function() { - if (this.aStack_.length) { - v(this.aStack_.pop(), this); - this.m_ = this.mStack_.pop() - } - }; - - function h(i) { - return isFinite(i[0][0]) && isFinite(i[0][1]) && isFinite(i[1][0]) && isFinite(i[1][1]) && isFinite(i[2][0]) && isFinite(i[2][1]) - } - - function aa(j, i, p) { - if (!h(i)) { - return - } - j.m_ = i; - if (p) { - var Z = i[0][0] * i[1][1] - i[0][1] * i[1][0]; - j.lineScale_ = N(H(Z)) - } - } - q.translate = function(m, j) { - var i = [ - [1, 0, 0], - [0, 1, 0], - [m, j, 1] - ]; - aa(this, J(i, this.m_), false) - }; - q.rotate = function(j) { - var p = A(j); - var m = l(j); - var i = [ - [p, m, 0], - [-m, p, 0], - [0, 0, 1] - ]; - aa(this, J(i, this.m_), false) - }; - q.scale = function(m, j) { - this.arcScaleX_ *= m; - this.arcScaleY_ *= j; - var i = [ - [m, 0, 0], - [0, j, 0], - [0, 0, 1] - ]; - aa(this, J(i, this.m_), true) - }; - q.transform = function(Z, p, ah, ag, j, i) { - var m = [ - [Z, p, 0], - [ah, ag, 0], - [j, i, 1] - ]; - aa(this, J(m, this.m_), true) - }; - q.setTransform = function(ag, Z, ai, ah, p, j) { - var i = [ - [ag, Z, 0], - [ai, ah, 0], - [p, j, 1] - ]; - aa(this, i, true) - }; - q.drawText_ = function(am, ak, aj, ap, ai) { - var ao = this.m_, - at = 1000, - j = 0, - ar = at, - ah = { - x: 0, - y: 0 - }, - ag = []; - var i = u(E(this.font), this.element_); - var p = ac(i); - var au = this.element_.currentStyle; - var Z = this.textAlign.toLowerCase(); - switch (Z) { - case "left": - case "center": - case "right": - break; - case "end": - Z = au.direction == "ltr" ? "right" : "left"; - break; - case "start": - Z = au.direction == "rtl" ? "right" : "left"; - break; - default: - Z = "left" - } - switch (this.textBaseline) { - case "hanging": - case "top": - ah.y = i.size / 1.75; - break; - case "middle": - break; - default: - case null: - case "alphabetic": - case "ideographic": - case "bottom": - ah.y = -i.size / 3; - break - } - switch (Z) { - case "right": - j = at; - ar = 0.05; - break; - case "center": - j = ar = at / 2; - break - } - var aq = V(this, ak + ah.x, aj + ah.y); - ag.push(''); - if (ai) { - w(this, ag) - } else { - G(this, ag, { - x: -j, - y: 0 - }, { - x: ar, - y: i.size - }) - } - var an = ao[0][0].toFixed(3) + "," + ao[1][0].toFixed(3) + "," + ao[0][1].toFixed(3) + "," + ao[1][1].toFixed(3) + ",0,0"; - var al = n(aq.x / d) + "," + n(aq.y / d); - ag.push('', '', ''); - this.element_.insertAdjacentHTML("beforeEnd", ag.join("")) - }; - q.fillText = function(m, i, p, j) { - this.drawText_(m, i, p, j, false) - }; - q.strokeText = function(m, i, p, j) { - this.drawText_(m, i, p, j, true) - }; - q.measureText = function(m) { - if (!this.textMeasureEl_) { - var i = ''; - this.element_.insertAdjacentHTML("beforeEnd", i); - this.textMeasureEl_ = this.element_.lastChild - } - var j = this.element_.ownerDocument; - this.textMeasureEl_.innerHTML = ""; - this.textMeasureEl_.style.font = this.font; - this.textMeasureEl_.appendChild(j.createTextNode(m)); - return { - width: this.textMeasureEl_.offsetWidth - } - }; - q.clip = function() {}; - q.arcTo = function() {}; - q.createPattern = function(j, i) { - return new T(j, i) - }; - - function U(i) { - this.type_ = i; - this.x0_ = 0; - this.y0_ = 0; - this.r0_ = 0; - this.x1_ = 0; - this.y1_ = 0; - this.r1_ = 0; - this.colors_ = [] - } - U.prototype.addColorStop = function(j, i) { - i = F(i); - this.colors_.push({ - offset: j, - color: i.color, - alpha: i.alpha - }) - }; - - function T(j, i) { - Q(j); - switch (i) { - case "repeat": - case null: - case "": - this.repetition_ = "repeat"; - break; - case "repeat-x": - case "repeat-y": - case "no-repeat": - this.repetition_ = i; - break; - default: - O("SYNTAX_ERR") - } - this.src_ = j.src; - this.width_ = j.width; - this.height_ = j.height - } - - function O(i) { - throw new P(i) - } - - function Q(i) { - if (!i || i.nodeType != 1 || i.tagName != "IMG") { - O("TYPE_MISMATCH_ERR") - } - if (i.readyState != "complete") { - O("INVALID_STATE_ERR") - } - } - - function P(i) { - this.code = this[i]; - this.message = i + ": DOM Exception " + this.code - } - var X = P.prototype = new Error(); - X.INDEX_SIZE_ERR = 1; - X.DOMSTRING_SIZE_ERR = 2; - X.HIERARCHY_REQUEST_ERR = 3; - X.WRONG_DOCUMENT_ERR = 4; - X.INVALID_CHARACTER_ERR = 5; - X.NO_DATA_ALLOWED_ERR = 6; - X.NO_MODIFICATION_ALLOWED_ERR = 7; - X.NOT_FOUND_ERR = 8; - X.NOT_SUPPORTED_ERR = 9; - X.INUSE_ATTRIBUTE_ERR = 10; - X.INVALID_STATE_ERR = 11; - X.SYNTAX_ERR = 12; - X.INVALID_MODIFICATION_ERR = 13; - X.NAMESPACE_ERR = 14; - X.INVALID_ACCESS_ERR = 15; - X.VALIDATION_ERR = 16; - X.TYPE_MISMATCH_ERR = 17; - G_vmlCanvasManager = e; - CanvasRenderingContext2D = D; - CanvasGradient = U; - CanvasPattern = T; - DOMException = P - })() -} -Ext.define("Ext.draw.engine.Canvas", { - extend: "Ext.draw.Surface", - requires: ["Ext.draw.engine.excanvas", "Ext.draw.Animator", "Ext.draw.Color"], - config: { - highPrecision: false - }, - statics: { - contextOverrides: { - setGradientBBox: function(a) { - this.bbox = a - }, - fill: function() { - var c = this.fillStyle, - a = this.fillGradient, - b = this.fillOpacity, - d = this.globalAlpha, - e = this.bbox; - if (c !== Ext.draw.Color.RGBA_NONE && b !== 0) { - if (a && e) { - this.fillStyle = a.generateGradient(this, e) - } - if (b !== 1) { - this.globalAlpha = d * b - } - this.$fill(); - if (b !== 1) { - this.globalAlpha = d - } - if (a && e) { - this.fillStyle = c - } - } - }, - stroke: function() { - var e = this.strokeStyle, - c = this.strokeGradient, - a = this.strokeOpacity, - b = this.globalAlpha, - d = this.bbox; - if (e !== Ext.draw.Color.RGBA_NONE && a !== 0) { - if (c && d) { - this.strokeStyle = c.generateGradient(this, d) - } - if (a !== 1) { - this.globalAlpha = b * a - } - this.$stroke(); - if (a !== 1) { - this.globalAlpha = b - } - if (c && d) { - this.strokeStyle = e - } - } - }, - fillStroke: function(d, e) { - var j = this, - i = this.fillStyle, - h = this.fillOpacity, - f = this.strokeStyle, - c = this.strokeOpacity, - b = j.shadowColor, - a = j.shadowBlur, - g = Ext.draw.Color.RGBA_NONE; - if (e === undefined) { - e = d.transformFillStroke - } - if (!e) { - d.inverseMatrix.toContext(j) - } - if (i !== g && h !== 0) { - j.fill(); - j.shadowColor = g; - j.shadowBlur = 0 - } - if (f !== g && c !== 0) { - j.stroke() - } - j.shadowColor = b; - j.shadowBlur = a - }, - setLineDash: function(a) { - if (this.$setLineDash) { - this.$setLineDash(a) - } - }, - getLineDash: function() { - if (this.$getLineDash) { - return this.$getLineDash() - } - }, - ellipse: function(g, e, c, a, j, b, f, d) { - var i = Math.cos(j), - h = Math.sin(j); - this.transform(i * c, h * c, -h * a, i * a, g, e); - this.arc(0, 0, 1, b, f, d); - this.transform(i / c, -h / a, h / c, i / a, -(i * g + h * e) / c, (h * g - i * e) / a) - }, - appendPath: function(f) { - var e = this, - c = 0, - b = 0, - a = f.commands, - g = f.params, - d = a.length; - e.beginPath(); - for (; c < d; c++) { - switch (a[c]) { - case "M": - e.moveTo(g[b], g[b + 1]); - b += 2; - break; - case "L": - e.lineTo(g[b], g[b + 1]); - b += 2; - break; - case "C": - e.bezierCurveTo(g[b], g[b + 1], g[b + 2], g[b + 3], g[b + 4], g[b + 5]); - b += 6; - break; - case "Z": - e.closePath(); - break - } - } - }, - save: function() { - var c = this.toSave, - d = c.length, - e = d && {}, - b = 0, - a; - for (; b < d; b++) { - a = c[b]; - if (a in this) { - e[a] = this[a] - } - } - this.state.push(e); - this.$save() - }, - restore: function() { - var b = this.state.pop(), - a; - if (b) { - for (a in b) { - this[a] = b[a] - } - } - this.$restore() - } - } - }, - splitThreshold: 3000, - toSave: ["fillGradient", "strokeGradient"], - element: { - reference: "element", - style: { - position: "absolute" - }, - children: [{ - reference: "innerElement", - style: { - width: "100%", - height: "100%", - position: "relative" - } - }] - }, - createCanvas: function() { - var c = Ext.Element.create({ - tag: "canvas", - cls: Ext.baseCSSPrefix + "surface-canvas" - }); - window.G_vmlCanvasManager && G_vmlCanvasManager.initElement(c.dom); - var d = Ext.draw.engine.Canvas.contextOverrides, - a = c.dom.getContext("2d"), - b; - if (a.ellipse) { - delete d.ellipse - } - a.state = []; - a.toSave = this.toSave; - for (b in d) { - a["$" + b] = a[b] - } - Ext.apply(a, d); - if (this.getHighPrecision()) { - this.enablePrecisionCompensation(a) - } else { - this.disablePrecisionCompensation(a) - } - this.innerElement.appendChild(c); - this.canvases.push(c); - this.contexts.push(a) - }, - updateHighPrecision: function(d) { - var e = this.contexts, - c = e.length, - b, a; - for (b = 0; b < c; b++) { - a = e[b]; - if (d) { - this.enablePrecisionCompensation(a) - } else { - this.disablePrecisionCompensation(a) - } - } - }, - precisionNames: ["rect", "fillRect", "strokeRect", "clearRect", "moveTo", "lineTo", "arc", "arcTo", "save", "restore", "updatePrecisionCompensate", "setTransform", "transform", "scale", "translate", "rotate", "quadraticCurveTo", "bezierCurveTo", "createLinearGradient", "createRadialGradient", "fillText", "strokeText", "drawImage"], - disablePrecisionCompensation: function(b) { - var a = Ext.draw.engine.Canvas.contextOverrides, - f = this.precisionNames, - e = f.length, - d, c; - for (d = 0; d < e; d++) { - c = f[d]; - if (!(c in a)) { - delete b[c] - } - } - this.setDirty(true) - }, - enablePrecisionCompensation: function(j) { - var c = this, - a = 1, - g = 1, - l = 0, - k = 0, - i = new Ext.draw.Matrix(), - b = [], - e = {}, - d = Ext.draw.engine.Canvas.contextOverrides, - h = j.constructor.prototype; - var f = { - toSave: c.toSave, - rect: function(m, p, n, o) { - return h.rect.call(this, m * a + l, p * g + k, n * a, o * g) - }, - fillRect: function(m, p, n, o) { - this.updatePrecisionCompensateRect(); - h.fillRect.call(this, m * a + l, p * g + k, n * a, o * g); - this.updatePrecisionCompensate() - }, - strokeRect: function(m, p, n, o) { - this.updatePrecisionCompensateRect(); - h.strokeRect.call(this, m * a + l, p * g + k, n * a, o * g); - this.updatePrecisionCompensate() - }, - clearRect: function(m, p, n, o) { - return h.clearRect.call(this, m * a + l, p * g + k, n * a, o * g) - }, - moveTo: function(m, n) { - return h.moveTo.call(this, m * a + l, n * g + k) - }, - lineTo: function(m, n) { - return h.lineTo.call(this, m * a + l, n * g + k) - }, - arc: function(n, r, m, p, o, q) { - this.updatePrecisionCompensateRect(); - h.arc.call(this, n * a + l, r * a + k, m * a, p, o, q); - this.updatePrecisionCompensate() - }, - arcTo: function(o, q, n, p, m) { - this.updatePrecisionCompensateRect(); - h.arcTo.call(this, o * a + l, q * g + k, n * a + l, p * g + k, m * a); - this.updatePrecisionCompensate() - }, - save: function() { - b.push(i); - i = i.clone(); - d.save.call(this); - h.save.call(this) - }, - restore: function() { - i = b.pop(); - d.restore.call(this); - h.restore.call(this); - this.updatePrecisionCompensate() - }, - updatePrecisionCompensate: function() { - i.precisionCompensate(c.devicePixelRatio, e); - a = e.xx; - g = e.yy; - l = e.dx; - k = e.dy; - h.setTransform.call(this, c.devicePixelRatio, e.b, e.c, e.d, 0, 0) - }, - updatePrecisionCompensateRect: function() { - i.precisionCompensateRect(c.devicePixelRatio, e); - a = e.xx; - g = e.yy; - l = e.dx; - k = e.dy; - h.setTransform.call(this, c.devicePixelRatio, e.b, e.c, e.d, 0, 0) - }, - setTransform: function(q, o, n, m, r, p) { - i.set(q, o, n, m, r, p); - this.updatePrecisionCompensate() - }, - transform: function(q, o, n, m, r, p) { - i.append(q, o, n, m, r, p); - this.updatePrecisionCompensate() - }, - scale: function(n, m) { - this.transform(n, 0, 0, m, 0, 0) - }, - translate: function(n, m) { - this.transform(1, 0, 0, 1, n, m) - }, - rotate: function(o) { - var n = Math.cos(o), - m = Math.sin(o); - this.transform(n, m, -m, n, 0, 0) - }, - quadraticCurveTo: function(n, p, m, o) { - h.quadraticCurveTo.call(this, n * a + l, p * g + k, m * a + l, o * g + k) - }, - bezierCurveTo: function(r, p, o, n, m, q) { - h.bezierCurveTo.call(this, r * a + l, p * g + k, o * a + l, n * g + k, m * a + l, q * g + k) - }, - createLinearGradient: function(n, p, m, o) { - this.updatePrecisionCompensateRect(); - var q = h.createLinearGradient.call(this, n * a + l, p * g + k, m * a + l, o * g + k); - this.updatePrecisionCompensate(); - return q - }, - createRadialGradient: function(p, r, o, n, q, m) { - this.updatePrecisionCompensateRect(); - var s = h.createLinearGradient.call(this, p * a + l, r * a + k, o * a, n * a + l, q * a + k, m * a); - this.updatePrecisionCompensate(); - return s - }, - fillText: function(o, m, p, n) { - h.setTransform.apply(this, i.elements); - if (typeof n === "undefined") { - h.fillText.call(this, o, m, p) - } else { - h.fillText.call(this, o, m, p, n) - } - this.updatePrecisionCompensate() - }, - strokeText: function(o, m, p, n) { - h.setTransform.apply(this, i.elements); - if (typeof n === "undefined") { - h.strokeText.call(this, o, m, p) - } else { - h.strokeText.call(this, o, m, p, n) - } - this.updatePrecisionCompensate() - }, - fill: function() { - var m = this.fillGradient, - n = this.bbox; - this.updatePrecisionCompensateRect(); - if (m && n) { - this.fillStyle = m.generateGradient(this, n) - } - h.fill.call(this); - this.updatePrecisionCompensate() - }, - stroke: function() { - var m = this.strokeGradient, - n = this.bbox; - this.updatePrecisionCompensateRect(); - if (m && n) { - this.strokeStyle = m.generateGradient(this, n) - } - h.stroke.call(this); - this.updatePrecisionCompensate() - }, - drawImage: function(u, s, r, q, p, o, n, m, t) { - switch (arguments.length) { - case 3: - return h.drawImage.call(this, u, s * a + l, r * g + k); - case 5: - return h.drawImage.call(this, u, s * a + l, r * g + k, q * a, p * g); - case 9: - return h.drawImage.call(this, u, s, r, q, p, o * a + l, n * g * k, m * a, t * g) - } - } - }; - Ext.apply(j, f); - this.setDirty(true) - }, - updateRect: function(a) { - this.callParent([a]); - var C = this, - p = Math.floor(a[0]), - e = Math.floor(a[1]), - g = Math.ceil(a[0] + a[2]), - B = Math.ceil(a[1] + a[3]), - u = C.devicePixelRatio, - D = C.canvases, - d = g - p, - y = B - e, - n = Math.round(C.splitThreshold / u), - c = C.xSplits = Math.ceil(d / n), - f = C.ySplits = Math.ceil(y / n), - v, s, q, A, z, x, o, m; - for (s = 0, z = 0; s < f; s++, z += n) { - for (v = 0, A = 0; v < c; v++, A += n) { - q = s * c + v; - if (q >= D.length) { - C.createCanvas() - } - x = D[q].dom; - x.style.left = A + "px"; - x.style.top = z + "px"; - m = Math.min(n, y - z); - if (m * u !== x.height) { - x.height = m * u; - x.style.height = m + "px" - } - o = Math.min(n, d - A); - if (o * u !== x.width) { - x.width = o * u; - x.style.width = o + "px" - } - C.applyDefaults(C.contexts[q]) - } - } - for (q += 1; q < D.length; q++) { - D[q].destroy() - } - C.activeCanvases = c * f; - D.length = C.activeCanvases; - C.clear() - }, - clearTransform: function() { - var f = this, - a = f.xSplits, - g = f.ySplits, - d = f.contexts, - h = f.splitThreshold, - l = f.devicePixelRatio, - e, c, b, m; - for (e = 0; e < a; e++) { - for (c = 0; c < g; c++) { - b = c * a + e; - m = d[b]; - m.translate(-h * e, -h * c); - m.scale(l, l); - f.matrix.toContext(m) - } - } - }, - renderSprite: function(q) { - var C = this, - b = C.getRect(), - e = C.matrix, - g = q.getParent(), - v = Ext.draw.Matrix.fly([1, 0, 0, 1, 0, 0]), - p = C.splitThreshold / C.devicePixelRatio, - c = C.xSplits, - m = C.ySplits, - A, z, s, a, r, o, d = 0, - B, n = 0, - f, l = b[2], - y = b[3], - x, u, t; - while (g && (g !== C)) { - v.prependMatrix(g.matrix || g.attr && g.attr.matrix); - g = g.getParent() - } - v.prependMatrix(e); - a = q.getBBox(); - if (a) { - a = v.transformBBox(a) - } - q.preRender(C); - if (q.attr.hidden || q.attr.globalAlpha === 0) { - q.setDirty(false); - return - } - for (u = 0, z = 0; u < m; u++, z += p) { - for (x = 0, A = 0; x < c; x++, A += p) { - t = u * c + x; - s = C.contexts[t]; - r = Math.min(p, l - A); - o = Math.min(p, y - z); - d = A; - B = d + r; - n = z; - f = n + o; - if (a) { - if (a.x > B || a.x + a.width < d || a.y > f || a.y + a.height < n) { - continue - } - } - s.save(); - q.useAttributes(s, b); - if (false === q.render(C, s, [d, n, r, o], b)) { - return false - } - s.restore() - } - } - q.setDirty(false) - }, - flatten: function(n, a) { - var k = document.createElement("canvas"), - f = Ext.getClassName(this), - g = this.devicePixelRatio, - l = k.getContext("2d"), - b, c, h, e, d, m; - k.width = Math.ceil(n.width * g); - k.height = Math.ceil(n.height * g); - for (e = 0; e < a.length; e++) { - b = a[e]; - if (Ext.getClassName(b) !== f) { - continue - } - h = b.getRect(); - for (d = 0; d < b.canvases.length; d++) { - c = b.canvases[d]; - m = c.getOffsetsTo(c.getParent()); - l.drawImage(c.dom, (h[0] + m[0]) * g, (h[1] + m[1]) * g) - } - } - return { - data: k.toDataURL(), - type: "png" - } - }, - applyDefaults: function(a) { - var b = Ext.draw.Color.RGBA_NONE; - a.strokeStyle = b; - a.fillStyle = b; - a.textAlign = "start"; - a.textBaseline = "alphabetic"; - a.miterLimit = 1 - }, - clear: function() { - var d = this, - e = d.activeCanvases, - c, b, a; - for (c = 0; c < e; c++) { - b = d.canvases[c].dom; - a = d.contexts[c]; - a.setTransform(1, 0, 0, 1, 0, 0); - a.clearRect(0, 0, b.width, b.height) - } - d.setDirty(true) - }, - destroy: function() { - var c = this, - a, b = c.canvases.length; - for (a = 0; a < b; a++) { - c.contexts[a] = null; - c.canvases[a].destroy(); - c.canvases[a] = null - } - delete c.contexts; - delete c.canvases; - c.callParent() - }, - privates: { - initElement: function() { - var a = this; - a.callParent(); - a.canvases = []; - a.contexts = []; - a.activeCanvases = (a.xSplits = 0) * (a.ySplits = 0) - } - } -}, function() { - var c = this, - b = c.prototype, - a = 10000000000; - if (Ext.os.is.Android4 && Ext.browser.is.Chrome) { - a = 3000 - } else { - if (Ext.is.iOS) { - a = 2200 - } - } - b.splitThreshold = a -}); -Ext.define("Ext.draw.Container", { - extend: "Ext.draw.ContainerBase", - alternateClassName: "Ext.draw.Component", - xtype: "draw", - defaultType: "surface", - isDrawContainer: true, - requires: ["Ext.draw.Surface", "Ext.draw.engine.Svg", "Ext.draw.engine.Canvas", "Ext.draw.gradient.GradientDefinition"], - engine: "Ext.draw.engine.Canvas", - config: { - cls: Ext.baseCSSPrefix + "draw-container", - resizeHandler: null, - sprites: null, - gradients: [] - }, - defaultDownloadServerUrl: "http://svg.sencha.io", - supportedFormats: ["png", "pdf", "jpeg", "gif"], - supportedOptions: { - version: Ext.isNumber, - data: Ext.isString, - format: function(a) { - return Ext.Array.indexOf(this.supportedFormats, a) >= 0 - }, - filename: Ext.isString, - width: Ext.isNumber, - height: Ext.isNumber, - scale: Ext.isNumber, - pdf: Ext.isObject, - jpeg: Ext.isObject - }, - initAnimator: function() { - this.frameCallbackId = Ext.draw.Animator.addFrameCallback("renderFrame", this) - }, - applyGradients: function(b) { - var a = [], - c, f, d, e; - if (!Ext.isArray(b)) { - return a - } - for (c = 0, f = b.length; c < f; c++) { - d = b[c]; - if (!Ext.isObject(d)) { - continue - } - if (typeof d.type !== "string") { - d.type = "linear" - } - if (d.angle) { - d.degrees = d.angle; - delete d.angle - } - if (Ext.isObject(d.stops)) { - d.stops = (function(i) { - var g = [], - h; - for (e in i) { - h = i[e]; - h.offset = e / 100; - g.push(h) - } - return g - })(d.stops) - } - a.push(d) - } - Ext.draw.gradient.GradientDefinition.add(a); - return a - }, - applySprites: function(f) { - if (!f) { - return - } - f = Ext.Array.from(f); - var e = f.length, - b = [], - d, a, c; - for (d = 0; d < e; d++) { - c = f[d]; - a = c.surface; - if (!(a && a.isSurface)) { - if (Ext.isString(a)) { - a = this.getSurface(a) - } else { - a = this.getSurface("main") - } - } - c = a.add(c); - b.push(c) - } - return b - }, - onBodyResize: function() { - var b = this.element, - a; - if (!b) { - return - } - a = b.getSize(); - if (a.width && a.height) { - this.setBodySize(a) - } - }, - setBodySize: function(c) { - var d = this, - b = d.getResizeHandler() || d.defaultResizeHandler, - a; - d.fireEvent("bodyresize", d, c); - a = b.call(d, c); - if (a !== false) { - d.renderFrame() - } - }, - defaultResizeHandler: function(a) { - this.getItems().each(function(b) { - b.setRect([0, 0, a.width, a.height]) - }) - }, - getSurface: function(d) { - d = this.getId() + "-" + (d || "main"); - var c = this, - b = c.getItems(), - a = b.get(d); - if (!a) { - a = c.add({ - xclass: c.engine, - id: d - }); - c.onBodyResize() - } - return a - }, - renderFrame: function() { - var e = this, - a = e.getItems(), - b, d, c; - for (b = 0, d = a.length; b < d; b++) { - c = a.items[b]; - if (c.isSurface) { - c.renderFrame() - } - } - }, - getImage: function(k) { - var l = this.innerElement.getSize(), - a = Array.prototype.slice.call(this.items.items), - d, g, c = this.surfaceZIndexes, - f, e, b, h; - for (e = 1; e < a.length; e++) { - b = a[e]; - h = c[b.type]; - f = e - 1; - while (f >= 0 && c[a[f].type] > h) { - a[f + 1] = a[f]; - f-- - } - a[f + 1] = b - } - d = a[0].flatten(l, a); - if (k === "image") { - g = new Image(); - g.src = d.data; - d.data = g; - return d - } - if (k === "stream") { - d.data = d.data.replace(/^data:image\/[^;]+/, "data:application/octet-stream"); - return d - } - return d - }, - download: function(d) { - var e = this, - a = [], - b, c, f; - d = Ext.apply({ - version: 2, - data: e.getImage().data - }, d); - for (c in d) { - if (d.hasOwnProperty(c)) { - f = d[c]; - if (c in e.supportedOptions) { - if (e.supportedOptions[c].call(e, f)) { - a.push({ - tag: "input", - type: "hidden", - name: c, - value: Ext.String.htmlEncode(Ext.isObject(f) ? Ext.JSON.encode(f) : f) - }) - } - } - } - } - b = Ext.dom.Helper.markup({ - tag: "html", - children: [{ - tag: "head" - }, { - tag: "body", - children: [{ - tag: "form", - method: "POST", - action: d.url || e.defaultDownloadServerUrl, - children: a - }, { - tag: "script", - type: "text/javascript", - children: 'document.getElementsByTagName("form")[0].submit();' - }] - }] - }); - window.open("", "ImageDownload_" + Date.now()).document.write(b) - }, - destroy: function() { - var a = this.frameCallbackId; - if (a) { - Ext.draw.Animator.removeFrameCallback(a) - } - this.callParent() - } -}, function() { - if (location.search.match("svg")) { - Ext.draw.Container.prototype.engine = "Ext.draw.engine.Svg" - } else { - if ((Ext.os.is.BlackBerry && Ext.os.version.getMajor() === 10) || (Ext.browser.is.AndroidStock4 && (Ext.os.version.getMinor() === 1 || Ext.os.version.getMinor() === 2 || Ext.os.version.getMinor() === 3))) { - Ext.draw.Container.prototype.engine = "Ext.draw.engine.Svg" - } - } -}); -Ext.define("Ext.chart.theme.Base", { - mixins: { - factoryable: "Ext.mixin.Factoryable" - }, - requires: ["Ext.draw.Color"], - factoryConfig: { - type: "chart.theme" - }, - isTheme: true, - config: { - baseColor: null, - colors: undefined, - gradients: null, - chart: { - defaults: { - background: "#23272a" - } - }, - axis: { - defaults: { - label: { - x: 0, - y: 0, - textBaseline: "middle", - textAlign: "center", - fontSize: "default", - fontFamily: "default", - fontWeight: "default", - fillStyle: "white", - color: "white" - }, - title: { - fillStyle: "black", - fontSize: "default*1.23", - fontFamily: "default", - fontWeight: "default", - color: "white" - }, - style: { - strokeStyle: "black" - }, - grid: { - strokeStyle: "rgba(44, 47, 51, 1)" - } - }, - top: { - style: { - textPadding: 5 - } - }, - bottom: { - style: { - textPadding: 5 - } - } - }, - series: { - defaults: { - label: { - fillStyle: "black", - strokeStyle: "none", - fontFamily: "default", - fontWeight: "default", - fontSize: "default*1.077", - textBaseline: "middle", - textAlign: "center" - }, - labelOverflowPadding: 5 - } - }, - sprites: { - text: { - fontSize: "default", - fontWeight: "default", - fontFamily: "default", - fillStyle: "black", - color: "white" - } - }, - seriesThemes: undefined, - markerThemes: { - type: ["circle", "cross", "plus", "square", "triangle", "diamond"] - }, - useGradients: false, - background: null - }, - colorDefaults: ["#94ae0a", "#115fa6", "#a61120", "#ff8809", "#ffd13e", "#a61187", "#24ad9a", "#7c7474", "#a66111"], - constructor: function(a) { - this.initConfig(a); - this.resolveDefaults() - }, - defaultRegEx: /^default([+\-/\*]\d+(?:\.\d+)?)?$/, - defaultOperators: { - "*": function(b, a) { - return b * a - }, - "+": function(b, a) { - return b + a - }, - "-": function(b, a) { - return b - a - } - }, - resolveDefaults: function() { - var a = this; - Ext.onReady(function() { - var f = Ext.clone(a.getSprites()), - e = Ext.clone(a.getAxis()), - d = Ext.clone(a.getSeries()), - g, c, b; - if (!a.superclass.defaults) { - g = Ext.getBody().createChild({ - tag: "div", - cls: "x-component" - }); - a.superclass.defaults = { - fontFamily: g.getStyle("fontFamily"), - fontWeight: g.getStyle("fontWeight"), - fontSize: parseFloat(g.getStyle("fontSize")), - fontVariant: g.getStyle("fontVariant"), - fontStyle: g.getStyle("fontStyle") - }; - g.destroy() - } - a.replaceDefaults(f.text); - a.setSprites(f); - for (c in e) { - b = e[c]; - a.replaceDefaults(b.label); - a.replaceDefaults(b.title) - } - a.setAxis(e); - for (c in d) { - b = d[c]; - a.replaceDefaults(b.label) - } - a.setSeries(d) - }) - }, - replaceDefaults: function(h) { - var e = this, - g = e.superclass.defaults, - a = e.defaultRegEx, - d, f, c, b; - if (Ext.isObject(h)) { - for (d in g) { - c = a.exec(h[d]); - if (c) { - f = g[d]; - c = c[1]; - if (c) { - b = e.defaultOperators[c.charAt(0)]; - f = Math.round(b(f, parseFloat(c.substr(1)))) - } - h[d] = f - } - } - } - }, - applyBaseColor: function(c) { - var a, b; - if (c) { - a = c.isColor ? c : Ext.draw.Color.fromString(c); - b = a.getHSL()[2]; - if (b < 0.15) { - a = a.createLighter(0.3) - } else { - if (b < 0.3) { - a = a.createLighter(0.15) - } else { - if (b > 0.85) { - a = a.createDarker(0.3) - } else { - if (b > 0.7) { - a = a.createDarker(0.15) - } - } - } - } - this.setColors([a.createDarker(0.3).toString(), a.createDarker(0.15).toString(), a.toString(), a.createLighter(0.12).toString(), a.createLighter(0.24).toString(), a.createLighter(0.31).toString()]) - } - return c - }, - applyColors: function(a) { - return a || this.colorDefaults - }, - updateUseGradients: function(a) { - if (a) { - this.updateGradients({ - type: "linear", - degrees: 90 - }) - } - }, - updateBackground: function(a) { - if (a) { - var b = this.getChart(); - b.defaults.background = a; - this.setChart(b) - } - }, - updateGradients: function(a) { - var c = this.getColors(), - e = [], - h, b, d, f, g; - if (Ext.isObject(a)) { - for (f = 0, g = c && c.length || 0; f < g; f++) { - b = Ext.draw.Color.fromString(c[f]); - if (b) { - d = b.createLighter(0.15).toString(); - h = Ext.apply(Ext.Object.chain(a), { - stops: [{ - offset: 1, - color: b.toString() - }, { - offset: 0, - color: d.toString() - }] - }); - e.push(h) - } - } - this.setColors(e) - } - }, - applySeriesThemes: function(a) { - this.getBaseColor(); - this.getUseGradients(); - this.getGradients(); - var b = this.getColors(); - if (!a) { - a = { - fillStyle: Ext.Array.clone(b), - strokeStyle: Ext.Array.map(b, function(d) { - var c = Ext.draw.Color.fromString(d.stops ? d.stops[0].color : d); - return c.createDarker(0.15).toString() - }) - } - } - return a - } -}); -Ext.define("Ext.chart.theme.Default", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.default", "chart.theme.Base"] -}); -Ext.define("Ext.chart.Markers", { - extend: "Ext.draw.sprite.Instancing", - isMarkers: true, - defaultCategory: "default", - constructor: function() { - this.callParent(arguments); - this.categories = {}; - this.revisions = {} - }, - destroy: function() { - this.categories = null; - this.revisions = null; - this.callParent() - }, - getMarkerFor: function(b, a) { - if (b in this.categories) { - var c = this.categories[b]; - if (a in c) { - return this.get(c[a]) - } - } - }, - clear: function(a) { - a = a || this.defaultCategory; - if (!(a in this.revisions)) { - this.revisions[a] = 1 - } else { - this.revisions[a]++ - } - }, - putMarkerFor: function(e, b, c, h, f) { - e = e || this.defaultCategory; - var d = this, - g = d.categories[e] || (d.categories[e] = {}), - a; - if (c in g) { - d.setAttributesFor(g[c], b, h) - } else { - g[c] = d.getCount(); - d.createInstance(b, h) - } - a = d.get(g[c]); - if (a) { - a.category = e; - if (!f) { - a.revision = d.revisions[e] || (d.revisions[e] = 1) - } - } - }, - getMarkerBBoxFor: function(c, a, b) { - if (c in this.categories) { - var d = this.categories[c]; - if (a in d) { - return this.getBBoxFor(d[a], b) - } - } - }, - getBBox: function() { - return null - }, - render: function(a, l, b) { - var f = this, - k = f.revisions, - j = f.attr.matrix, - h = f.getTemplate(), - d = h.attr, - g, c, e; - j.toContext(l); - h.preRender(a, l, b); - h.useAttributes(l, b); - for (c = 0, e = f.instances.length; c < e; c++) { - g = f.get(c); - if (g.hidden || g.revision !== k[g.category]) { - continue - } - l.save(); - h.attr = g; - h.useAttributes(l, b); - h.render(a, l, b); - l.restore() - } - h.attr = d - } -}); -Ext.define("Ext.chart.label.Callout", { - extend: "Ext.draw.modifier.Modifier", - prepareAttributes: function(a) { - if (!a.hasOwnProperty("calloutOriginal")) { - a.calloutOriginal = Ext.Object.chain(a); - a.calloutOriginal.prototype = a - } - if (this._previous) { - this._previous.prepareAttributes(a.calloutOriginal) - } - }, - setAttrs: function(e, h) { - var d = e.callout, - i = e.calloutOriginal, - l = e.bbox.plain, - c = (l.width || 0) + e.labelOverflowPadding, - m = (l.height || 0) + e.labelOverflowPadding, - p, o; - if ("callout" in h) { - d = h.callout - } - if ("callout" in h || "calloutPlaceX" in h || "calloutPlaceY" in h || "x" in h || "y" in h) { - var n = "rotationRads" in h ? i.rotationRads = h.rotationRads : i.rotationRads, - g = "x" in h ? (i.x = h.x) : i.x, - f = "y" in h ? (i.y = h.y) : i.y, - b = "calloutPlaceX" in h ? h.calloutPlaceX : e.calloutPlaceX, - a = "calloutPlaceY" in h ? h.calloutPlaceY : e.calloutPlaceY, - k = "calloutVertical" in h ? h.calloutVertical : e.calloutVertical, - j; - n %= Math.PI * 2; - if (Math.cos(n) < 0) { - n = (n + Math.PI) % (Math.PI * 2) - } - if (n > Math.PI) { - n -= Math.PI * 2 - } - if (k) { - n = n * (1 - d) - Math.PI / 2 * d; - j = c; - c = m; - m = j - } else { - n = n * (1 - d) - } - h.rotationRads = n; - h.x = g * (1 - d) + b * d; - h.y = f * (1 - d) + a * d; - p = b - g; - o = a - f; - if (Math.abs(o * c) > Math.abs(p * m)) { - if (o > 0) { - h.calloutEndX = h.x - (m / 2) * (p / o) * d; - h.calloutEndY = h.y - (m / 2) * d - } else { - h.calloutEndX = h.x + (m / 2) * (p / o) * d; - h.calloutEndY = h.y + (m / 2) * d - } - } else { - if (p > 0) { - h.calloutEndX = h.x - c / 2; - h.calloutEndY = h.y - (c / 2) * (o / p) * d - } else { - h.calloutEndX = h.x + c / 2; - h.calloutEndY = h.y + (c / 2) * (o / p) * d - } - } - if (h.calloutStartX && h.calloutStartY) { - h.calloutHasLine = (p > 0 && h.calloutStartX < h.calloutEndX) || (p <= 0 && h.calloutStartX > h.calloutEndX) || (o > 0 && h.calloutStartY < h.calloutEndY) || (o <= 0 && h.calloutStartY > h.calloutEndY) - } else { - h.calloutHasLine = true - } - } - return h - }, - pushDown: function(a, b) { - b = this.callParent([a.calloutOriginal, b]); - return this.setAttrs(a, b) - }, - popUp: function(a, b) { - a = a.prototype; - b = this.setAttrs(a, b); - if (this._next) { - return this._next.popUp(a, b) - } else { - return Ext.apply(a, b) - } - } -}); -Ext.define("Ext.chart.label.Label", { - extend: "Ext.draw.sprite.Text", - requires: ["Ext.chart.label.Callout"], - inheritableStatics: { - def: { - processors: { - callout: "limited01", - calloutHasLine: "bool", - calloutPlaceX: "number", - calloutPlaceY: "number", - calloutStartX: "number", - calloutStartY: "number", - calloutEndX: "number", - calloutEndY: "number", - calloutColor: "color", - calloutWidth: "number", - calloutVertical: "bool", - labelOverflowPadding: "number", - display: "enums(none,under,over,rotate,insideStart,insideEnd,inside,outside)", - orientation: "enums(horizontal,vertical)", - renderer: "default" - }, - defaults: { - callout: 0, - calloutHasLine: true, - calloutPlaceX: 0, - calloutPlaceY: 0, - calloutStartX: 0, - calloutStartY: 0, - calloutEndX: 0, - calloutEndY: 0, - calloutWidth: 1, - calloutVertical: false, - calloutColor: "black", - labelOverflowPadding: 5, - display: "none", - orientation: "", - renderer: null - }, - triggers: { - callout: "transform", - calloutPlaceX: "transform", - calloutPlaceY: "transform", - labelOverflowPadding: "transform", - calloutRotation: "transform", - display: "hidden" - }, - updaters: { - hidden: function(a) { - a.hidden = a.display === "none" - } - } - } - }, - config: { - fx: { - customDurations: { - callout: 200 - } - }, - field: null, - calloutLine: true - }, - applyCalloutLine: function(a) { - if (a) { - return Ext.apply({}, a) - } - }, - prepareModifiers: function() { - this.callParent(arguments); - this.calloutModifier = new Ext.chart.label.Callout({ - sprite: this - }); - this.fx.setNext(this.calloutModifier); - this.calloutModifier.setNext(this.topModifier) - }, - render: function(b, c) { - var e = this, - a = e.attr, - d = a.calloutColor; - c.save(); - c.globalAlpha *= a.callout; - if (c.globalAlpha > 0 && a.calloutHasLine) { - if (d && d.isGradient) { - d = d.getStops()[0].color - } - c.strokeStyle = d; - c.fillStyle = d; - c.lineWidth = a.calloutWidth; - c.beginPath(); - c.moveTo(e.attr.calloutStartX, e.attr.calloutStartY); - c.lineTo(e.attr.calloutEndX, e.attr.calloutEndY); - c.stroke(); - c.beginPath(); - c.arc(e.attr.calloutStartX, e.attr.calloutStartY, 1 * a.calloutWidth, 0, 2 * Math.PI, true); - c.fill(); - c.beginPath(); - c.arc(e.attr.calloutEndX, e.attr.calloutEndY, 1 * a.calloutWidth, 0, 2 * Math.PI, true); - c.fill() - } - c.restore(); - Ext.draw.sprite.Text.prototype.render.apply(e, arguments) - } -}); -Ext.define("Ext.chart.series.Series", { - requires: ["Ext.chart.Markers", "Ext.chart.label.Label", "Ext.tip.ToolTip"], - mixins: ["Ext.mixin.Observable", "Ext.mixin.Bindable"], - isSeries: true, - defaultBindProperty: "store", - type: null, - seriesType: "sprite", - identifiablePrefix: "ext-line-", - observableType: "series", - darkerStrokeRatio: 0.15, - config: { - chart: null, - title: null, - renderer: null, - showInLegend: true, - triggerAfterDraw: false, - style: {}, - subStyle: {}, - themeStyle: {}, - colors: null, - useDarkerStrokeColor: true, - store: null, - label: {}, - labelOverflowPadding: null, - showMarkers: true, - marker: null, - markerSubStyle: null, - itemInstancing: null, - background: null, - highlightItem: null, - surface: null, - overlaySurface: null, - hidden: false, - highlight: false, - highlightCfg: { - merge: function(a) { - return a - }, - $value: { - fillStyle: "yellow", - strokeStyle: "red" - } - }, - animation: null, - tooltip: null - }, - directions: [], - sprites: null, - themeColorCount: function() { - return 1 - }, - isStoreDependantColorCount: false, - themeMarkerCount: function() { - return 0 - }, - getFields: function(f) { - var e = this, - a = [], - c, b, d; - for (b = 0, d = f.length; b < d; b++) { - c = e["get" + f[b] + "Field"](); - if (Ext.isArray(c)) { - a.push.apply(a, c) - } else { - a.push(c) - } - } - return a - }, - applyAnimation: function(a, b) { - if (!a) { - a = { - duration: 0 - } - } else { - if (a === true) { - a = { - easing: "easeInOut", - duration: 500 - } - } - } - return b ? Ext.apply({}, a, b) : a - }, - getAnimation: function() { - var a = this.getChart(); - if (a && a.animationSuspendCount) { - return { - duration: 0 - } - } else { - return this.callParent() - } - }, - updateTitle: function(a) { - var j = this, - g = j.getChart(); - if (!g || g.isInitializing) { - return - } - a = Ext.Array.from(a); - var c = g.getSeries(), - b = Ext.Array.indexOf(c, j), - e = g.getLegendStore(), - h = j.getYField(), - d, l, k, f; - if (e.getCount() && b !== -1) { - f = h ? Math.min(a.length, h.length) : a.length; - for (d = 0; d < f; d++) { - k = a[d]; - l = e.getAt(b + d); - if (k && l) { - l.set("name", k) - } - } - } - }, - applyHighlight: function(a, b) { - if (Ext.isObject(a)) { - a = Ext.merge({}, this.config.highlightCfg, a) - } else { - if (a === true) { - a = this.config.highlightCfg - } - } - return Ext.apply(b || {}, a) - }, - updateHighlight: function(a) { - this.getStyle(); - if (!Ext.Object.isEmpty(a)) { - this.addItemHighlight() - } - }, - updateHighlightCfg: function(a) { - if (!Ext.Object.equals(a, this.defaultConfig.highlightCfg)) { - this.addItemHighlight() - } - }, - applyItemInstancing: function(a, b) { - return Ext.merge(b || {}, a) - }, - setAttributesForItem: function(c, d) { - var b = c && c.sprite, - a; - if (b) { - if (b.itemsMarker && c.category === "items") { - b.putMarker(c.category, d, c.index, false, true) - } - if (b.isMarkerHolder && c.category === "markers") { - b.putMarker(c.category, d, c.index, false, true) - } else { - if (b.isInstancing) { - b.setAttributesFor(c.index, d) - } else { - if (Ext.isArray(b)) { - for (a = 0; a < b.length; a++) { - b[a].setAttributes(d) - } - } else { - b.setAttributes(d) - } - } - } - } - }, - getBBoxForItem: function(a) { - if (a && a.sprite) { - if (a.sprite.itemsMarker && a.category === "items") { - return a.sprite.getMarkerBBox(a.category, a.index) - } else { - if (a.sprite instanceof Ext.draw.sprite.Instancing) { - return a.sprite.getBBoxFor(a.index) - } else { - return a.sprite.getBBox() - } - } - } - return null - }, - applyHighlightItem: function(d, a) { - if (d === a) { - return - } - if (Ext.isObject(d) && Ext.isObject(a)) { - var c = d.sprite === a.sprite, - b = d.index === a.index; - if (c && b) { - return - } - } - return d - }, - updateHighlightItem: function(b, a) { - this.setAttributesForItem(a, { - highlighted: false - }); - this.setAttributesForItem(b, { - highlighted: true - }) - }, - constructor: function(a) { - var b = this, - c; - a = a || {}; - if (a.tips) { - a = Ext.apply({ - tooltip: a.tips - }, a) - } - if (a.highlightCfg) { - a = Ext.apply({ - highlight: a.highlightCfg - }, a) - } - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.sprites = []; - b.dataRange = []; - b.mixins.observable.constructor.call(b, a); - b.initBindable() - }, - lookupViewModel: function(a) { - var b = this.getChart(); - return b ? b.lookupViewModel(a) : null - }, - applyTooltip: function(c, b) { - var a = Ext.apply({ - xtype: "tooltip", - renderer: Ext.emptyFn, - constrainPosition: true, - shrinkWrapDock: true, - autoHide: true, - offsetX: 10, - offsetY: 10 - }, c); - return Ext.create(a) - }, - updateTooltip: function() { - this.addItemHighlight() - }, - addItemHighlight: function() { - var d = this.getChart(); - if (!d) { - return - } - var e = d.getInteractions(), - c, a, b; - for (c = 0; c < e.length; c++) { - a = e[c]; - if (a.isItemHighlight || a.isItemEdit) { - b = true; - break - } - } - if (!b) { - e.push("itemhighlight"); - d.setInteractions(e) - } - }, - showTooltip: function(l, m) { - var d = this, - n = d.getTooltip(), - j, a, i, f, h, k, g, e, b, c; - if (!n) { - return - } - clearTimeout(d.tooltipTimeout); - b = n.config; - if (n.trackMouse) { - m[0] += b.offsetX; - m[1] += b.offsetY - } else { - j = l.sprite; - a = j.getSurface(); - i = Ext.get(a.getId()); - if (i) { - k = l.series.getBBoxForItem(l); - g = k.x + k.width / 2; - e = k.y + k.height / 2; - h = a.matrix.transformPoint([g, e]); - f = i.getXY(); - c = a.getInherited().rtl; - g = c ? f[0] + i.getWidth() - h[0] : f[0] + h[0]; - e = f[1] + h[1]; - m = [g, e] - } - } - Ext.callback(n.renderer, n.scope, [n, l.record, l], 0, d); - n.show(m) - }, - hideTooltip: function(b) { - var a = this, - c = a.getTooltip(); - if (!c) { - return - } - clearTimeout(a.tooltipTimeout); - a.tooltipTimeout = Ext.defer(function() { - c.hide() - }, 1) - }, - applyStore: function(a) { - return a && Ext.StoreManager.lookup(a) - }, - getStore: function() { - return this._store || this.getChart() && this.getChart().getStore() - }, - updateStore: function(b, a) { - var h = this, - g = h.getChart(), - c = g && g.getStore(), - f, j, e, d; - a = a || c; - if (a && a !== b) { - a.un({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: h - }) - } - if (b) { - b.on({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: h - }); - f = h.getSprites(); - for (d = 0, e = f.length; d < e; d++) { - j = f[d]; - if (j.setStore) { - j.setStore(b) - } - } - h.onDataChanged() - } - h.fireEvent("storechange", h, b, a) - }, - onStoreChange: function(b, a, c) { - if (!this._store) { - this.updateStore(a, c) - } - }, - coordinate: function(o, m, e) { - var l = this, - p = l.getStore(), - h = l.getHidden(), - k = p.getData().items, - b = l["get" + o + "Axis"](), - f = { - min: Infinity, - max: -Infinity - }, - q = l["fieldCategory" + o] || [o], - g = l.getFields(q), - d, n, c, a = {}, - j = l.getSprites(); - if (j.length > 0) { - if (!Ext.isBoolean(h) || !h) { - for (d = 0; d < q.length; d++) { - n = g[d]; - c = l.coordinateData(k, n, b); - l.getRangeOfData(c, f); - a["data" + q[d]] = c - } - } - l.dataRange[m] = f.min; - l.dataRange[m + e] = f.max; - a["dataMin" + o] = f.min; - a["dataMax" + o] = f.max; - if (b) { - b.range = null; - a["range" + o] = b.getRange() - } - for (d = 0; d < j.length; d++) { - j[d].setAttributes(a) - } - } - }, - coordinateData: function(b, h, d) { - var g = [], - f = b.length, - e = d && d.getLayout(), - c, a; - for (c = 0; c < f; c++) { - a = b[c].data[h]; - if (!Ext.isEmpty(a, true)) { - if (e) { - g[c] = e.getCoordFor(a, h, c, b) - } else { - g[c] = +a - } - } else { - g[c] = a - } - } - return g - }, - getRangeOfData: function(g, b) { - var e = g.length, - d = b.min, - a = b.max, - c, f; - for (c = 0; c < e; c++) { - f = g[c]; - if (f < d) { - d = f - } - if (f > a) { - a = f - } - } - b.min = d; - b.max = a - }, - updateLabelData: function() { - var h = this, - l = h.getStore(), - g = l.getData().items, - f = h.getSprites(), - a = h.getLabel().getTemplate(), - n = Ext.Array.from(a.getField()), - c, b, e, d, m, k; - if (!f.length || !n.length) { - return - } - for (c = 0; c < f.length; c++) { - d = []; - m = f[c]; - k = m.getField(); - if (Ext.Array.indexOf(n, k) < 0) { - k = n[c] - } - for (b = 0, e = g.length; b < e; b++) { - d.push(g[b].get(k)) - } - m.setAttributes({ - labels: d - }) - } - }, - processData: function() { - if (!this.getStore()) { - return - } - var d = this, - f = this.directions, - a, c = f.length, - e, b; - for (a = 0; a < c; a++) { - e = f[a]; - b = d["get" + e + "Axis"](); - if (b) { - b.processData(d); - continue - } - if (d["coordinate" + e]) { - d["coordinate" + e]() - } - } - d.updateLabelData() - }, - applyBackground: function(a) { - if (this.getChart()) { - this.getSurface().setBackground(a); - return this.getSurface().getBackground() - } else { - return a - } - }, - updateChart: function(d, a) { - var c = this, - b = c._store; - if (a) { - a.un("axeschange", "onAxesChange", c); - c.clearSprites(); - c.setSurface(null); - c.setOverlaySurface(null); - a.unregister(c); - c.onChartDetached(a); - if (!b) { - c.updateStore(null) - } - } - if (d) { - c.setSurface(d.getSurface("series")); - c.setOverlaySurface(d.getSurface("overlay")); - d.on("axeschange", "onAxesChange", c); - if (d.getAxes()) { - c.onAxesChange(d) - } - c.onChartAttached(d); - d.register(c); - if (!b) { - c.updateStore(d.getStore()) - } - } - }, - onAxesChange: function(h) { - var k = this, - g = h.getAxes(), - c, a = {}, - b = {}, - e = false, - j = this.directions, - l, d, f; - for (d = 0, f = j.length; d < f; d++) { - l = j[d]; - b[l] = k.getFields(k["fieldCategory" + l]) - } - for (d = 0, f = g.length; d < f; d++) { - c = g[d]; - if (!a[c.getDirection()]) { - a[c.getDirection()] = [c] - } else { - a[c.getDirection()].push(c) - } - } - for (d = 0, f = j.length; d < f; d++) { - l = j[d]; - if (k["get" + l + "Axis"]()) { - continue - } - if (a[l]) { - c = k.findMatchingAxis(a[l], b[l]); - if (c) { - k["set" + l + "Axis"](c); - if (c.getNeedHighPrecision()) { - e = true - } - } - } - } - this.getSurface().setHighPrecision(e) - }, - findMatchingAxis: function(f, e) { - var d, c, b, a; - for (b = 0; b < f.length; b++) { - d = f[b]; - c = d.getFields(); - if (!c.length) { - return d - } else { - if (e) { - for (a = 0; a < e.length; a++) { - if (Ext.Array.indexOf(c, e[a]) >= 0) { - return d - } - } - } - } - } - }, - onChartDetached: function(a) { - var b = this; - b.fireEvent("chartdetached", a, b); - a.un("storechange", "onStoreChange", b) - }, - onChartAttached: function(a) { - var b = this; - b.setBackground(b.getBackground()); - b.fireEvent("chartattached", a, b); - a.on("storechange", "onStoreChange", b); - b.processData() - }, - updateOverlaySurface: function(a) { - var b = this; - if (a) { - if (b.getLabel()) { - b.getOverlaySurface().add(b.getLabel()) - } - } - }, - applyLabel: function(a, b) { - if (!b) { - b = new Ext.chart.Markers({ - zIndex: 10 - }); - b.setTemplate(new Ext.chart.label.Label(a)) - } else { - b.getTemplate().setAttributes(a) - } - return b - }, - createItemInstancingSprite: function(c, b) { - var e = this, - f = new Ext.chart.Markers(), - a, d; - f.setAttributes({ - zIndex: Number.MAX_VALUE - }); - a = Ext.apply({}, b); - if (e.getHighlight()) { - a.highlight = e.getHighlight(); - a.modifiers = ["highlight"] - } - f.setTemplate(a); - d = f.getTemplate(); - d.setAttributes(e.getStyle()); - d.fx.on("animationstart", "onSpriteAnimationStart", this); - d.fx.on("animationend", "onSpriteAnimationEnd", this); - c.bindMarker("items", f); - e.getSurface().add(f); - return f - }, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer() - } - }, - updateRenderer: function(c) { - var b = this, - a = b.getChart(), - d; - if (a && a.isInitializing) { - return - } - d = b.getSprites(); - if (d.length) { - d[0].setAttributes({ - renderer: c || null - }); - if (a && !a.isInitializing) { - a.redraw() - } - } - }, - updateShowMarkers: function(a) { - var d = this.getSprites(), - b = d && d[0], - c = b && b.getMarker("markers"); - if (c) { - c.getTemplate().setAttributes({ - hidden: !a - }) - } - }, - createSprite: function() { - var f = this, - a = f.getSurface(), - e = f.getItemInstancing(), - d = a.add(f.getDefaultSpriteConfig()), - b = f.getMarker(), - g, c; - d.setAttributes(f.getStyle()); - d.setSeries(f); - if (e) { - d.itemsMarker = f.createItemInstancingSprite(d, e) - } - if (d.bindMarker) { - if (b) { - g = new Ext.chart.Markers(); - c = Ext.Object.merge({}, b); - if (f.getHighlight()) { - c.highlight = f.getHighlight(); - c.modifiers = ["highlight"] - } - g.setTemplate(c); - g.getTemplate().fx.setCustomDurations({ - translationX: 0, - translationY: 0 - }); - d.dataMarker = g; - d.bindMarker("markers", g); - f.getOverlaySurface().add(g) - } - if (f.getLabel().getTemplate().getField()) { - d.bindMarker("labels", f.getLabel()) - } - } - if (d.setStore) { - d.setStore(f.getStore()) - } - d.fx.on("animationstart", "onSpriteAnimationStart", f); - d.fx.on("animationend", "onSpriteAnimationEnd", f); - f.sprites.push(d); - return d - }, - getSprites: Ext.emptyFn, - onDataChanged: function() { - var d = this, - c = d.getChart(), - b = c && c.getStore(), - a = d.getStore(); - if (a !== b) { - d.processData() - } - }, - isXType: function(a) { - return a === "series" - }, - getItemId: function() { - return this.getId() - }, - applyThemeStyle: function(e, a) { - var b = this, - d, c; - d = e && e.subStyle && e.subStyle.fillStyle; - c = d && e.subStyle.strokeStyle; - if (d && !c) { - e.subStyle.strokeStyle = b.getStrokeColorsFromFillColors(d) - } - d = e && e.markerSubStyle && e.markerSubStyle.fillStyle; - c = d && e.markerSubStyle.strokeStyle; - if (d && !c) { - e.markerSubStyle.strokeStyle = b.getStrokeColorsFromFillColors(d) - } - return Ext.apply(a || {}, e) - }, - applyStyle: function(c, b) { - var a = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + this.seriesType)); - if (a && a.def) { - c = a.def.normalize(c) - } - return Ext.apply({}, c, b) - }, - applySubStyle: function(b, c) { - var a = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + this.seriesType)); - if (a && a.def) { - b = a.def.batchedNormalize(b, true) - } - return Ext.merge({}, c, b) - }, - applyMarker: function(c, a) { - var d = (c && c.type) || (a && a.type) || "circle", - b = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + d)); - if (b && b.def) { - c = b.def.normalize(Ext.isObject(c) ? c : {}, true); - c.type = d - } - return Ext.merge(a || {}, c) - }, - applyMarkerSubStyle: function(c, a) { - var d = (c && c.type) || (a && a.type) || "circle", - b = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + d)); - if (b && b.def) { - c = b.def.batchedNormalize(c, true) - } - return Ext.merge(a || {}, c) - }, - updateHidden: function(b) { - var a = this; - a.getColors(); - a.getSubStyle(); - a.setSubStyle({ - hidden: b - }); - a.processData(); - a.doUpdateStyles(); - if (!Ext.isArray(b)) { - a.updateLegendStore(b) - } - }, - updateLegendStore: function(f, b) { - var e = this, - d = e.getChart(), - c = d.getLegendStore(), - g = e.getId(), - a; - if (c) { - if (arguments.length > 1) { - a = c.findBy(function(h) { - return h.get("series") === g && h.get("index") === b - }); - if (a !== -1) { - a = c.getAt(a) - } - } else { - a = c.findRecord("series", g) - } - if (a && a.get("disabled") !== f) { - a.set("disabled", f) - } - } - }, - setHiddenByIndex: function(a, c) { - var b = this; - if (Ext.isArray(b.getHidden())) { - b.getHidden()[a] = c; - b.updateHidden(b.getHidden()); - b.updateLegendStore(c, a) - } else { - b.setHidden(c) - } - }, - getStrokeColorsFromFillColors: function(a) { - var c = this, - e = c.getUseDarkerStrokeColor(), - b = (Ext.isNumber(e) ? e : c.darkerStrokeRatio), - d; - if (e) { - d = Ext.Array.map(a, function(f) { - f = Ext.isString(f) ? f : f.stops[0].color; - f = Ext.draw.Color.fromString(f); - return f.createDarker(b).toString() - }) - } else { - d = Ext.Array.clone(a) - } - return d - }, - updateThemeColors: function(b) { - var c = this, - d = c.getThemeStyle(), - a = Ext.Array.clone(b), - f = c.getStrokeColorsFromFillColors(b), - e = { - fillStyle: a, - strokeStyle: f - }; - d.subStyle = Ext.apply(d.subStyle || {}, e); - d.markerSubStyle = Ext.apply(d.markerSubStyle || {}, e); - c.doUpdateStyles() - }, - themeOnlyIfConfigured: {}, - updateTheme: function(d) { - var h = this, - a = d.getSeries(), - n = h.getInitialConfig(), - c = h.defaultConfig, - f = h.getConfigurator().configs, - j = a.defaults, - k = a[h.type], - g = h.themeOnlyIfConfigured, - l, i, o, b, m, e; - a = Ext.merge({}, j, k); - for (l in a) { - i = a[l]; - e = f[l]; - if (i !== null && i !== undefined && e) { - m = n[l]; - o = Ext.isObject(i); - b = m === c[l]; - if (o) { - if (b && g[l]) { - continue - } - i = Ext.merge({}, i, m) - } - if (b || o) { - h[e.names.set](i) - } - } - } - }, - updateChartColors: function(a) { - var b = this; - if (!b.getColors()) { - b.updateThemeColors(a) - } - }, - updateColors: function(a) { - this.updateThemeColors(a) - }, - updateStyle: function() { - this.doUpdateStyles() - }, - updateSubStyle: function() { - this.doUpdateStyles() - }, - updateThemeStyle: function() { - this.doUpdateStyles() - }, - doUpdateStyles: function() { - var g = this, - h = g.sprites, - d = g.getItemInstancing(), - c = 0, - f = h && h.length, - a = g.getConfig("showMarkers", true), - b = g.getMarker(), - e; - for (; c < f; c++) { - e = g.getStyleByIndex(c); - if (d) { - h[c].itemsMarker.getTemplate().setAttributes(e) - } - h[c].setAttributes(e); - if (b && h[c].dataMarker) { - h[c].dataMarker.getTemplate().setAttributes(g.getMarkerStyleByIndex(c)) - } - } - }, - getStyleWithTheme: function() { - var b = this, - c = b.getThemeStyle(), - d = (c && c.style) || {}, - a = Ext.applyIf(Ext.apply({}, b.getStyle()), d); - return a - }, - getSubStyleWithTheme: function() { - var c = this, - d = c.getThemeStyle(), - a = (d && d.subStyle) || {}, - b = Ext.applyIf(Ext.apply({}, c.getSubStyle()), a); - return b - }, - getStyleByIndex: function(b) { - var e = this, - h = e.getThemeStyle(), - d, g, c, f, a = {}; - d = e.getStyle(); - g = (h && h.style) || {}; - c = e.styleDataForIndex(e.getSubStyle(), b); - f = e.styleDataForIndex((h && h.subStyle), b); - Ext.apply(a, g); - Ext.apply(a, f); - Ext.apply(a, d); - Ext.apply(a, c); - return a - }, - getMarkerStyleByIndex: function(d) { - var g = this, - c = g.getThemeStyle(), - a, e, k, j, b, l, h, f, m = {}; - a = g.getStyle(); - e = (c && c.style) || {}; - k = g.styleDataForIndex(g.getSubStyle(), d); - if (k.hasOwnProperty("hidden")) { - k.hidden = k.hidden || !this.getConfig("showMarkers", true) - } - j = g.styleDataForIndex((c && c.subStyle), d); - b = g.getMarker(); - l = (c && c.marker) || {}; - h = g.getMarkerSubStyle(); - f = g.styleDataForIndex((c && c.markerSubStyle), d); - Ext.apply(m, e); - Ext.apply(m, j); - Ext.apply(m, l); - Ext.apply(m, f); - Ext.apply(m, a); - Ext.apply(m, k); - Ext.apply(m, b); - Ext.apply(m, h); - return m - }, - styleDataForIndex: function(d, c) { - var e, b, a = {}; - if (d) { - for (b in d) { - e = d[b]; - if (Ext.isArray(e)) { - a[b] = e[c % e.length] - } else { - a[b] = e - } - } - } - return a - }, - getItemForPoint: Ext.emptyFn, - getItemByIndex: function(a, e) { - var d = this, - f = d.getSprites(), - b = f && f[0], - c; - if (!b) { - return - } - if (e === undefined && b.isMarkerHolder) { - e = d.getItemInstancing() ? "items" : "markers" - } else { - if (!e || e === "" || e === "sprites") { - b = f[a] - } - } - if (b) { - c = { - series: d, - category: e, - index: a, - record: d.getStore().getData().items[a], - field: d.getYField(), - sprite: b - }; - return c - } - }, - onSpriteAnimationStart: function(a) { - this.fireEvent("animationstart", this, a) - }, - onSpriteAnimationEnd: function(a) { - this.fireEvent("animationend", this, a) - }, - resolveListenerScope: function(e) { - var d = this, - a = Ext._namedScopes[e], - c = d.getChart(), - b; - if (!a) { - b = c ? c.resolveListenerScope(e, false) : (e || d) - } else { - if (a.isThis) { - b = d - } else { - if (a.isController) { - b = c ? c.resolveListenerScope(e, false) : d - } else { - if (a.isSelf) { - b = c ? c.resolveListenerScope(e, false) : d; - if (b === c && !c.getInheritedConfig("defaultListenerScope")) { - b = d - } - } - } - } - } - return b - }, - provideLegendInfo: function(a) { - a.push({ - name: this.getTitle() || this.getId(), - mark: "black", - disabled: this.getHidden(), - series: this.getId(), - index: 0 - }) - }, - clearSprites: function() { - var d = this.sprites, - b, a, c; - for (a = 0, c = d.length; a < c; a++) { - b = d[a]; - if (b && b.isSprite) { - b.destroy() - } - } - this.sprites = [] - }, - destroy: function() { - var b = this, - a = b._store, - c = b.getConfig("tooltip", true); - if (a && a.getAutoDestroy()) { - Ext.destroy(a) - } - b.setChart(null); - b.clearListeners(); - if (c) { - Ext.destroy(c); - clearTimeout(b.tooltipTimeout) - } - b.callParent() - } -}); -Ext.define("Ext.chart.interactions.Abstract", { - xtype: "interaction", - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - gestures: { - tap: "onGesture" - }, - chart: null, - enabled: true - }, - throttleGap: 0, - stopAnimationBeforeSync: false, - constructor: function(a) { - var b = this, - c; - a = a || {}; - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.mixins.observable.constructor.call(b, a) - }, - initialize: Ext.emptyFn, - updateChart: function(c, a) { - var b = this; - if (a === c) { - return - } - if (a) { - a.unregister(b); - b.removeChartListener(a) - } - if (c) { - c.register(b); - b.addChartListener() - } - }, - updateEnabled: function(a) { - var c = this, - b = c.getChart(); - if (b) { - if (a) { - c.addChartListener() - } else { - c.removeChartListener(b) - } - } - }, - onGesture: Ext.emptyFn, - getItemForEvent: function(d) { - var b = this, - a = b.getChart(), - c = a.getEventXY(d); - return a.getItemForPoint(c[0], c[1]) - }, - getItemsForEvent: function(d) { - var b = this, - a = b.getChart(), - c = a.getEventXY(d); - return a.getItemsForPoint(c[0], c[1]) - }, - addChartListener: function() { - var c = this, - b = c.getChart(), - e = c.getGestures(), - a; - if (!c.getEnabled()) { - return - } - - function d(f, g) { - b.addElementListener(f, c.listeners[f] = function(j) { - var i = c.getLocks(), - h; - if (c.getEnabled() && (!(f in i) || i[f] === c)) { - h = (Ext.isFunction(g) ? g : c[g]).apply(this, arguments); - if (h === false && j && j.stopPropagation) { - j.stopPropagation() - } - return h - } - }, c) - } - c.listeners = c.listeners || {}; - for (a in e) { - d(a, e[a]) - } - }, - removeChartListener: function(c) { - var d = this, - e = d.getGestures(), - b; - - function a(f) { - var g = d.listeners[f]; - if (g) { - c.removeElementListener(f, g); - delete d.listeners[f] - } - } - if (d.listeners) { - for (b in e) { - a(b) - } - } - }, - lockEvents: function() { - var d = this, - c = d.getLocks(), - a = Array.prototype.slice.call(arguments), - b = a.length; - while (b--) { - c[a[b]] = d - } - }, - unlockEvents: function() { - var c = this.getLocks(), - a = Array.prototype.slice.call(arguments), - b = a.length; - while (b--) { - delete c[a[b]] - } - }, - getLocks: function() { - var a = this.getChart(); - return a.lockedEvents || (a.lockedEvents = {}) - }, - isMultiTouch: function() { - if (Ext.browser.is.IE10) { - return true - } - return !Ext.os.is.Desktop - }, - initializeDefaults: Ext.emptyFn, - doSync: function() { - var b = this, - a = b.getChart(); - if (b.syncTimer) { - clearTimeout(b.syncTimer); - b.syncTimer = null - } - if (b.stopAnimationBeforeSync) { - a.animationSuspendCount++ - } - a.redraw(); - if (b.stopAnimationBeforeSync) { - a.animationSuspendCount-- - } - b.syncThrottle = Date.now() + b.throttleGap - }, - sync: function() { - var a = this; - if (a.throttleGap && Ext.frameStartTime < a.syncThrottle) { - if (a.syncTimer) { - return - } - a.syncTimer = Ext.defer(function() { - a.doSync() - }, a.throttleGap) - } else { - a.doSync() - } - }, - getItemId: function() { - return this.getId() - }, - isXType: function(a) { - return a === "interaction" - }, - destroy: function() { - var a = this; - a.setChart(null); - delete a.listeners; - a.callParent() - } -}, function() { - if (Ext.os.is.Android4) { - this.prototype.throttleGap = 40 - } -}); -Ext.define("Ext.chart.MarkerHolder", { - extend: "Ext.Mixin", - mixinConfig: { - id: "markerHolder", - after: { - constructor: "constructor", - preRender: "preRender" - }, - before: { - destroy: "destroy" - } - }, - isMarkerHolder: true, - surfaceMatrix: null, - inverseSurfaceMatrix: null, - deprecated: { - 6: { - methods: { - getBoundMarker: { - message: "Please use the 'getMarker' method instead.", - fn: function(b) { - var a = this.boundMarkers[b]; - return a ? [a] : a - } - } - } - } - }, - constructor: function() { - this.boundMarkers = {}; - this.cleanRedraw = false - }, - bindMarker: function(b, a) { - var c = this, - d = c.boundMarkers; - if (a && a.isMarkers) { - c.releaseMarker(b); - d[b] = a; - a.on("destroy", c.onMarkerDestroy, c) - } - }, - onMarkerDestroy: function(a) { - this.releaseMarker(a) - }, - releaseMarker: function(a) { - var c = this.boundMarkers, - b; - if (a && a.isMarkers) { - for (b in c) { - if (c[b] === a) { - delete c[b]; - break - } - } - } else { - b = a; - a = c[b]; - delete c[b] - } - return a || null - }, - getMarker: function(a) { - return this.boundMarkers[a] || null - }, - preRender: function() { - var f = this, - g = f.getId(), - d = f.boundMarkers, - e = f.getParent(), - c, a, b; - if (f.surfaceMatrix) { - b = f.surfaceMatrix.set(1, 0, 0, 1, 0, 0) - } else { - b = f.surfaceMatrix = new Ext.draw.Matrix() - } - f.cleanRedraw = !f.attr.dirty; - if (!f.cleanRedraw) { - for (c in d) { - a = d[c]; - if (a) { - a.clear(g) - } - } - } - while (e && e.attr && e.attr.matrix) { - b.prependMatrix(e.attr.matrix); - e = e.getParent() - } - b.prependMatrix(e.matrix); - f.surfaceMatrix = b; - f.inverseSurfaceMatrix = b.inverse(f.inverseSurfaceMatrix) - }, - putMarker: function(d, a, c, g, e) { - var b = this.boundMarkers[d], - f = this.getId(); - if (b) { - b.putMarkerFor(f, a, c, g, e) - } - }, - getMarkerBBox: function(c, b, d) { - var a = this.boundMarkers[c], - e = this.getId(); - if (a) { - return a.getMarkerBBoxFor(e, b, d) - } - }, - destroy: function() { - var c = this.boundMarkers, - b, a; - for (b in c) { - a = c[b]; - a.destroy() - } - } -}); -Ext.define("Ext.chart.axis.sprite.Axis", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.axis", - type: "axis", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - requires: ["Ext.draw.sprite.Text"], - inheritableStatics: { - def: { - processors: { - grid: "bool", - axisLine: "bool", - minorTicks: "bool", - minorTickSize: "number", - majorTicks: "bool", - majorTickSize: "number", - length: "number", - startGap: "number", - endGap: "number", - dataMin: "number", - dataMax: "number", - visibleMin: "number", - visibleMax: "number", - position: "enums(left,right,top,bottom,angular,radial,gauge)", - minStepSize: "number", - estStepSize: "number", - titleOffset: "number", - textPadding: "number", - min: "number", - max: "number", - centerX: "number", - centerY: "number", - radius: "number", - totalAngle: "number", - baseRotation: "number", - data: "default", - enlargeEstStepSizeByText: "bool" - }, - defaults: { - grid: false, - axisLine: true, - minorTicks: false, - minorTickSize: 3, - majorTicks: true, - majorTickSize: 5, - length: 0, - startGap: 0, - endGap: 0, - visibleMin: 0, - visibleMax: 1, - dataMin: 0, - dataMax: 1, - position: "", - minStepSize: 0, - estStepSize: 20, - min: 0, - max: 1, - centerX: 0, - centerY: 0, - radius: 1, - baseRotation: 0, - data: null, - titleOffset: 0, - textPadding: 0, - scalingCenterY: 0, - scalingCenterX: 0, - strokeStyle: "black", - enlargeEstStepSizeByText: false - }, - triggers: { - minorTickSize: "bbox", - majorTickSize: "bbox", - position: "bbox,layout", - axisLine: "bbox,layout", - min: "layout", - max: "layout", - length: "layout", - minStepSize: "layout", - estStepSize: "layout", - data: "layout", - dataMin: "layout", - dataMax: "layout", - visibleMin: "layout", - visibleMax: "layout", - enlargeEstStepSizeByText: "layout" - }, - updaters: { - layout: "layoutUpdater" - } - } - }, - config: { - label: null, - layout: null, - segmenter: null, - renderer: null, - layoutContext: null, - axis: null - }, - thickness: 0, - stepSize: 0, - getBBox: function() { - return null - }, - defaultRenderer: function(a) { - return this.segmenter.renderer(a, this) - }, - layoutUpdater: function() { - var h = this, - f = h.getAxis().getChart(); - if (f.isInitializing) { - return - } - var e = h.attr, - d = h.getLayout(), - g = f.getInherited().rtl, - b = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMin, - i = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMax, - c = e.position, - a = { - attr: e, - segmenter: h.getSegmenter(), - renderer: h.defaultRenderer - }; - if (c === "left" || c === "right") { - e.translationX = 0; - e.translationY = i * e.length / (i - b); - e.scalingX = 1; - e.scalingY = -e.length / (i - b); - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } else { - if (c === "top" || c === "bottom") { - if (g) { - e.translationX = e.length + b * e.length / (i - b) + 1 - } else { - e.translationX = -b * e.length / (i - b) - } - e.translationY = 0; - e.scalingX = (g ? -1 : 1) * e.length / (i - b); - e.scalingY = 1; - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } - } - if (d) { - d.calculateLayout(a); - h.setLayoutContext(a) - } - }, - iterate: function(e, j) { - var c, g, a, b, h, d, k = Ext.Array.some, - m = Math.abs, - f; - if (e.getLabel) { - if (e.min < e.from) { - j.call(this, e.min, e.getLabel(e.min), -1, e) - } - for (c = 0; c <= e.steps; c++) { - j.call(this, e.get(c), e.getLabel(c), c, e) - } - if (e.max > e.to) { - j.call(this, e.max, e.getLabel(e.max), e.steps + 1, e) - } - } else { - b = this.getAxis(); - h = b.floatingAxes; - d = []; - f = (e.to - e.from) / (e.steps + 1); - if (b.getFloating()) { - for (a in h) { - d.push(h[a]) - } - } - - function l(i) { - return !d.length || k(d, function(n) { - return m(n - i) > f - }) - } - if (e.min < e.from && l(e.min)) { - j.call(this, e.min, e.min, -1, e) - } - for (c = 0; c <= e.steps; c++) { - g = e.get(c); - if (l(g)) { - j.call(this, g, g, c, e) - } - } - if (e.max > e.to && l(e.max)) { - j.call(this, e.max, e.max, e.steps + 1, e) - } - } - }, - renderTicks: function(l, m, s, p) { - var v = this, - k = v.attr, - u = k.position, - n = k.matrix, - e = 0.5 * k.lineWidth, - f = n.getXX(), - i = n.getDX(), - j = n.getYY(), - h = n.getDY(), - o = s.majorTicks, - d = k.majorTickSize, - a = s.minorTicks, - r = k.minorTickSize; - if (o) { - switch (u) { - case "right": - function q(w) { - return function(x, z, y) { - x = l.roundPixel(x * j + h) + e; - m.moveTo(0, x); - m.lineTo(w, x) - } - } - v.iterate(o, q(d)); - a && v.iterate(a, q(r)); - break; - case "left": - function t(w) { - return function(x, z, y) { - x = l.roundPixel(x * j + h) + e; - m.moveTo(p[2] - w, x); - m.lineTo(p[2], x) - } - } - v.iterate(o, t(d)); - a && v.iterate(a, t(r)); - break; - case "bottom": - function c(w) { - return function(x, z, y) { - x = l.roundPixel(x * f + i) - e; - m.moveTo(x, 0); - m.lineTo(x, w) - } - } - v.iterate(o, c(d)); - a && v.iterate(a, c(r)); - break; - case "top": - function b(w) { - return function(x, z, y) { - x = l.roundPixel(x * f + i) - e; - m.moveTo(x, p[3]); - m.lineTo(x, p[3] - w) - } - } - v.iterate(o, b(d)); - a && v.iterate(a, b(r)); - break; - case "angular": - v.iterate(o, function(w, y, x) { - w = w / (k.max + 1) * Math.PI * 2 + k.baseRotation; - m.moveTo(k.centerX + (k.length) * Math.cos(w), k.centerY + (k.length) * Math.sin(w)); - m.lineTo(k.centerX + (k.length + d) * Math.cos(w), k.centerY + (k.length + d) * Math.sin(w)) - }); - break; - case "gauge": - var g = v.getGaugeAngles(); - v.iterate(o, function(w, y, x) { - w = (w - k.min) / (k.max - k.min + 1) * k.totalAngle - k.totalAngle + g.start; - m.moveTo(k.centerX + (k.length) * Math.cos(w), k.centerY + (k.length) * Math.sin(w)); - m.lineTo(k.centerX + (k.length + d) * Math.cos(w), k.centerY + (k.length + d) * Math.sin(w)) - }); - break - } - } - }, - renderLabels: function(E, q, D, K) { - var o = this, - k = o.attr, - i = 0.5 * k.lineWidth, - u = k.position, - y = k.matrix, - A = k.textPadding, - x = y.getXX(), - d = y.getDX(), - g = y.getYY(), - c = y.getDY(), - n = 0, - I = D.majorTicks, - G = Math.max(k.majorTickSize, k.minorTickSize) + k.lineWidth, - f = Ext.draw.Draw.isBBoxIntersect, - F = o.getLabel(), - J, s, r = null, - w = 0, - b = 0, - m = D.segmenter, - B = o.getRenderer(), - t = o.getAxis(), - z = t.getTitle(), - a = z && z.attr.text !== "" && z.getBBox(), - l, h = null, - p, C, v, e, H; - if (I && F && !F.attr.hidden) { - J = F.attr.font; - if (q.font !== J) { - q.font = J - } - F.setAttributes({ - translationX: 0, - translationY: 0 - }, true); - F.applyTransformations(); - l = F.attr.inverseMatrix.elements.slice(0); - switch (u) { - case "left": - e = a ? a.x + a.width : 0; - switch (F.attr.textAlign) { - case "start": - H = E.roundPixel(e + d) - i; - break; - case "end": - H = E.roundPixel(K[2] - G + d) - i; - break; - default: - H = E.roundPixel(e + (K[2] - e - G) / 2 + d) - i - } - F.setAttributes({ - translationX: H - }, true); - break; - case "right": - e = a ? K[2] - a.x : 0; - switch (F.attr.textAlign) { - case "start": - H = E.roundPixel(G + d) + i; - break; - case "end": - H = E.roundPixel(K[2] - e + d) + i; - break; - default: - H = E.roundPixel(G + (K[2] - G - e) / 2 + d) + i - } - F.setAttributes({ - translationX: H - }, true); - break; - case "top": - e = a ? a.y + a.height : 0; - F.setAttributes({ - translationY: E.roundPixel(e + (K[3] - e - G) / 2) - i - }, true); - break; - case "bottom": - e = a ? K[3] - a.y : 0; - F.setAttributes({ - translationY: E.roundPixel(G + (K[3] - G - e) / 2) + i - }, true); - break; - case "radial": - F.setAttributes({ - translationX: k.centerX - }, true); - break; - case "angular": - F.setAttributes({ - translationY: k.centerY - }, true); - break; - case "gauge": - F.setAttributes({ - translationY: k.centerY - }, true); - break - } - if (u === "left" || u === "right") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - F.setAttributes({ - text: String(v), - translationY: E.roundPixel(L * g + c) - }, true); - F.applyTransformations(); - n = Math.max(n, F.getBBox().width + G); - if (n <= o.thickness) { - C = Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0)); - p = C.prepend.apply(C, l).transformBBox(F.getBBox(true)); - if (h && !f(p, h, A)) { - return - } - E.renderSprite(F); - h = p; - w += p.height; - b++ - } - }) - } else { - if (u === "top" || u === "bottom") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - F.setAttributes({ - text: String(v), - translationX: E.roundPixel(L * x + d) - }, true); - F.applyTransformations(); - n = Math.max(n, F.getBBox().height + G); - if (n <= o.thickness) { - C = Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0)); - p = C.prepend.apply(C, l).transformBBox(F.getBBox(true)); - if (h && !f(p, h, A)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "radial") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - if (typeof v !== "undefined") { - F.setAttributes({ - text: String(v), - translationX: k.centerX - E.roundPixel(L) / k.max * k.length * Math.cos(k.baseRotation + Math.PI / 2), - translationY: k.centerY - E.roundPixel(L) / k.max * k.length * Math.sin(k.baseRotation + Math.PI / 2) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "angular") { - s = k.majorTickSize + k.lineWidth * 0.5 + (parseInt(F.attr.fontSize, 10) || 10) / 2; - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - n = Math.max(n, Math.max(k.majorTickSize, k.minorTickSize) + (k.lineCap !== "butt" ? k.lineWidth * 0.5 : 0)); - if (typeof v !== "undefined") { - var O = L / (k.max + 1) * Math.PI * 2 + k.baseRotation; - F.setAttributes({ - text: String(v), - translationX: k.centerX + (k.length + s) * Math.cos(O), - translationY: k.centerY + (k.length + s) * Math.sin(O) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "gauge") { - var j = o.getGaugeAngles(); - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - if (typeof v !== "undefined") { - var O = (L - k.min) / (k.max - k.min + 1) * k.totalAngle - k.totalAngle + j.start; - F.setAttributes({ - text: String(v), - translationX: k.centerX + (k.length + 10) * Math.cos(O), - translationY: k.centerY + (k.length + 10) * Math.sin(O) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } - } - } - } - } - if (k.enlargeEstStepSizeByText && b) { - w /= b; - w += G; - w *= 2; - if (k.estStepSize < w) { - k.estStepSize = w - } - } - if (Math.abs(o.thickness - (n)) > 1) { - o.thickness = n; - k.bbox.plain.dirty = true; - k.bbox.transform.dirty = true; - o.doThicknessChanged(); - return false - } - } - }, - renderAxisLine: function(a, i, e, c) { - var h = this, - g = h.attr, - b = g.lineWidth * 0.5, - j = g.position, - d, f; - if (g.axisLine && g.length) { - switch (j) { - case "left": - d = a.roundPixel(c[2]) - b; - i.moveTo(d, -g.endGap); - i.lineTo(d, g.length + g.startGap + 1); - break; - case "right": - i.moveTo(b, -g.endGap); - i.lineTo(b, g.length + g.startGap + 1); - break; - case "bottom": - i.moveTo(-g.startGap, b); - i.lineTo(g.length + g.endGap, b); - break; - case "top": - d = a.roundPixel(c[3]) - b; - i.moveTo(-g.startGap, d); - i.lineTo(g.length + g.endGap, d); - break; - case "angular": - i.moveTo(g.centerX + g.length, g.centerY); - i.arc(g.centerX, g.centerY, g.length, 0, Math.PI * 2, true); - break; - case "gauge": - f = h.getGaugeAngles(); - i.moveTo(g.centerX + Math.cos(f.start) * g.length, g.centerY + Math.sin(f.start) * g.length); - i.arc(g.centerX, g.centerY, g.length, f.start, f.end, true); - break - } - } - }, - getGaugeAngles: function() { - var a = this, - c = a.attr.totalAngle, - b; - if (c <= Math.PI) { - b = (Math.PI - c) * 0.5 - } else { - b = -(Math.PI * 2 - c) * 0.5 - } - b = Math.PI * 2 - b; - return { - start: b, - end: b - c - } - }, - renderGridLines: function(m, n, s, r) { - var t = this, - b = t.getAxis(), - l = t.attr, - p = l.matrix, - d = l.startGap, - a = l.endGap, - c = p.getXX(), - k = p.getYY(), - h = p.getDX(), - g = p.getDY(), - u = l.position, - f = b.getGridAlignment(), - q = s.majorTicks, - e, o, i; - if (l.grid) { - if (q) { - if (u === "left" || u === "right") { - i = l.min * k + g + a + d; - t.iterate(q, function(j, w, v) { - e = j * k + g + a; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - y: e, - height: i - e - }, o = v, true); - i = e - }); - o++; - e = 0; - t.putMarker(f + "-" + (o % 2 ? "odd" : "even"), { - y: e, - height: i - e - }, o, true) - } else { - if (u === "top" || u === "bottom") { - i = l.min * c + h + d; - if (d) { - t.putMarker(f + "-even", { - x: 0, - width: i - }, -1, true) - } - t.iterate(q, function(j, w, v) { - e = j * c + h + d; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - x: e, - width: i - e - }, o = v, true); - i = e - }); - o++; - e = l.length + l.startGap + l.endGap; - t.putMarker(f + "-" + (o % 2 ? "odd" : "even"), { - x: e, - width: i - e - }, o, true) - } else { - if (u === "radial") { - t.iterate(q, function(j, w, v) { - if (!j) { - return - } - e = j / l.max * l.length; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - scalingX: e, - scalingY: e - }, v, true); - i = e - }) - } else { - if (u === "angular") { - t.iterate(q, function(j, w, v) { - if (!l.length) { - return - } - e = j / (l.max + 1) * Math.PI * 2 + l.baseRotation; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - rotationRads: e, - rotationCenterX: 0, - rotationCenterY: 0, - scalingX: l.length, - scalingY: l.length - }, v, true); - i = e - }) - } - } - } - } - } - } - }, - renderLimits: function(o) { - var t = this, - a = t.getAxis(), - h = a.getChart(), - p = h.getInnerPadding(), - d = Ext.Array.from(a.getLimits()); - if (!d.length) { - return - } - var r = a.limits.surface.getRect(), - m = t.attr, - n = m.matrix, - u = m.position, - k = Ext.Object.chain, - v = a.limits.titles, - c, j, b, s, l, q, f, g, e; - v.instances = []; - v.position = 0; - if (u === "left" || u === "right") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l * n.getYY() + n.getDY(); - s.line.y = l + p.top; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("horizontal-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - j = s.line.title.position || (u === "left" ? "start" : "end"); - switch (j) { - case "start": - g = 10; - break; - case "end": - g = r[2] - 10; - break; - case "middle": - g = r[2] / 2; - break - } - v.setAttributesFor(v.position - 1, { - x: g, - y: s.line.y - c.height / 2, - textAlign: j, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "top" || u === "bottom") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l * n.getXX() + n.getDX(); - s.line.x = l + p.left; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("vertical-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - j = s.line.title.position || (u === "top" ? "end" : "start"); - switch (j) { - case "start": - e = r[3] - c.width / 2 - 10; - break; - case "end": - e = c.width / 2 + 10; - break; - case "middle": - e = r[3] / 2; - break - } - v.setAttributesFor(v.position - 1, { - x: s.line.x + c.height / 2, - y: e, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle, - rotationRads: Math.PI / 2 - }) - } - } - } else { - if (u === "radial") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - if (l > m.max) { - continue - } - l = l / m.max * m.length; - s.line.cx = m.centerX; - s.line.cy = m.centerY; - s.line.scalingX = l; - s.line.scalingY = l; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("circular-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - v.setAttributesFor(v.position - 1, { - x: m.centerX, - y: m.centerY - l - c.height / 2, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "angular") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l / (m.max + 1) * Math.PI * 2 + m.baseRotation; - s.line.translationX = m.centerX; - s.line.translationY = m.centerY; - s.line.rotationRads = l; - s.line.rotationCenterX = 0; - s.line.rotationCenterY = 0; - s.line.scalingX = m.length; - s.line.scalingY = m.length; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("radial-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - b = ((l > -0.5 * Math.PI && l < 0.5 * Math.PI) || (l > 1.5 * Math.PI && l < 2 * Math.PI)) ? 1 : -1; - v.setAttributesFor(v.position - 1, { - x: m.centerX + 0.5 * m.length * Math.cos(l) + b * c.height / 2 * Math.sin(l), - y: m.centerY + 0.5 * m.length * Math.sin(l) - b * c.height / 2 * Math.cos(l), - rotationRads: b === 1 ? l : l - Math.PI, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "gauge") {} - } - } - } - } - }, - doThicknessChanged: function() { - var a = this.getAxis(); - if (a) { - a.onThicknessChanged() - } - }, - render: function(a, c, d) { - var e = this, - b = e.getLayoutContext(); - if (b) { - if (false === e.renderLabels(a, c, b, d)) { - return false - } - c.beginPath(); - e.renderTicks(a, c, b, d); - e.renderAxisLine(a, c, b, d); - e.renderGridLines(a, c, b, d); - e.renderLimits(d); - c.stroke() - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Segmenter", { - config: { - axis: null - }, - constructor: function(a) { - this.initConfig(a) - }, - renderer: function(b, a) { - return String(b) - }, - from: function(a) { - return a - }, - diff: Ext.emptyFn, - align: Ext.emptyFn, - add: Ext.emptyFn, - preferredStep: Ext.emptyFn -}); -Ext.define("Ext.chart.axis.segmenter.Names", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.names", - renderer: function(b, a) { - return b - }, - diff: function(b, a, c) { - return Math.floor(a - b) - }, - align: function(c, b, a) { - return Math.floor(c) - }, - add: function(c, b, a) { - return c + b - }, - preferredStep: function(c, a, b, d) { - return { - unit: 1, - step: 1 - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Numeric", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.numeric", - isNumeric: true, - renderer: function(b, a) { - return b.toFixed(Math.max(0, a.majorTicks.unit.fixes)) - }, - diff: function(b, a, c) { - return Math.floor((a - b) / c.scale) - }, - align: function(c, b, a) { - return Math.floor(c / (a.scale * b)) * a.scale * b - }, - add: function(c, b, a) { - return c + b * a.scale - }, - preferredStep: function(c, b) { - var a = Math.floor(Math.log(b) * Math.LOG10E), - d = Math.pow(10, a); - b /= d; - if (b < 2) { - b = 2 - } else { - if (b < 5) { - b = 5 - } else { - if (b < 10) { - b = 10; - a++ - } - } - } - return { - unit: { - fixes: -a, - scale: d - }, - step: b - } - }, - exactStep: function(c, b) { - var a = Math.floor(Math.log(b) * Math.LOG10E), - d = Math.pow(10, a); - return { - unit: { - fixes: -a + (b % d === 0 ? 0 : 1), - scale: 1 - }, - step: b - } - }, - adjustByMajorUnit: function(e, g, c) { - var d = c[0], - b = c[1], - a = e * g, - f = d % a; - if (f !== 0) { - c[0] = d - f + (d < 0 ? -a : 0) - } - f = b % a; - if (f !== 0) { - c[1] = b - f + (b > 0 ? a : 0) - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Time", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.time", - config: { - step: null - }, - renderer: function(c, b) { - var a = Ext.Date; - switch (b.majorTicks.unit) { - case "y": - return a.format(c, "Y"); - case "mo": - return a.format(c, "Y-m"); - case "d": - return a.format(c, "Y-m-d") - } - return a.format(c, "Y-m-d\nH:i:s") - }, - from: function(a) { - return new Date(a) - }, - diff: function(b, a, c) { - if (isFinite(b)) { - b = new Date(b) - } - if (isFinite(a)) { - a = new Date(a) - } - return Ext.Date.diff(b, a, c) - }, - align: function(a, c, b) { - if (b === "d" && c >= 7) { - a = Ext.Date.align(a, "d", c); - a.setDate(a.getDate() - a.getDay() + 1); - return a - } else { - return Ext.Date.align(a, b, c) - } - }, - add: function(c, b, a) { - return Ext.Date.add(new Date(c), a, b) - }, - stepUnits: [ - [Ext.Date.YEAR, 1, 2, 5, 10, 20, 50, 100, 200, 500], - [Ext.Date.MONTH, 1, 3, 6], - [Ext.Date.DAY, 1, 7, 14], - [Ext.Date.HOUR, 1, 6, 12], - [Ext.Date.MINUTE, 1, 5, 15, 30], - [Ext.Date.SECOND, 1, 5, 15, 30], - [Ext.Date.MILLI, 1, 2, 5, 10, 20, 50, 100, 200, 500] - ], - preferredStep: function(b, e) { - if (this.getStep()) { - return this.getStep() - } - var f = new Date(+b), - g = new Date(+b + Math.ceil(e)), - d = this.stepUnits, - l, k, h, c, a; - for (c = 0; c < d.length; c++) { - k = d[c][0]; - h = this.diff(f, g, k); - if (h > 0) { - for (a = 1; a < d[c].length; a++) { - if (h <= d[c][a]) { - l = { - unit: k, - step: d[c][a] - }; - break - } - } - if (!l) { - c--; - l = { - unit: d[c][0], - step: 1 - } - } - break - } - } - if (!l) { - l = { - unit: Ext.Date.DAY, - step: 1 - } - } - return l - } -}); -Ext.define("Ext.chart.axis.layout.Layout", { - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - axis: null - }, - constructor: function(a) { - this.mixins.observable.constructor.call(this, a) - }, - processData: function(b) { - var e = this, - c = e.getAxis(), - f = c.getDirection(), - g = c.boundSeries, - a, d; - if (b) { - b["coordinate" + f]() - } else { - for (a = 0, d = g.length; a < d; a++) { - g[a]["coordinate" + f]() - } - } - }, - calculateMajorTicks: function(a) { - var f = this, - e = a.attr, - d = e.max - e.min, - i = d / Math.max(1, e.length) * (e.visibleMax - e.visibleMin), - h = e.min + d * e.visibleMin, - b = e.min + d * e.visibleMax, - g = e.estStepSize * i, - c = f.snapEnds(a, e.min, e.max, g); - if (c) { - f.trimByRange(a, c, h, b); - a.majorTicks = c - } - }, - calculateMinorTicks: function(a) { - if (this.snapMinorEnds) { - a.minorTicks = this.snapMinorEnds(a) - } - }, - calculateLayout: function(b) { - var c = this, - a = b.attr; - if (a.length === 0) { - return null - } - if (a.majorTicks) { - c.calculateMajorTicks(b); - if (a.minorTicks) { - c.calculateMinorTicks(b) - } - } - }, - snapEnds: Ext.emptyFn, - trimByRange: function(b, f, i, a) { - var g = b.segmenter, - j = f.unit, - h = g.diff(f.from, i, j), - d = g.diff(f.from, a, j), - c = Math.max(0, Math.ceil(h / f.step)), - e = Math.min(f.steps, Math.floor(d / f.step)); - if (e < f.steps) { - f.to = g.add(f.from, e * f.step, j) - } - if (f.max > a) { - f.max = f.to - } - if (f.from < i) { - f.from = g.add(f.from, c * f.step, j); - while (f.from < i) { - c++; - f.from = g.add(f.from, f.step, j) - } - } - if (f.min < i) { - f.min = f.from - } - f.steps = e - c - } -}); -Ext.define("Ext.chart.axis.layout.Discrete", { - extend: "Ext.chart.axis.layout.Layout", - alias: "axisLayout.discrete", - isDiscrete: true, - processData: function() { - var f = this, - d = f.getAxis(), - c = d.boundSeries, - g = d.getDirection(), - b, e, a; - f.labels = []; - f.labelMap = {}; - for (b = 0, e = c.length; b < e; b++) { - a = c[b]; - if (a["get" + g + "Axis"]() === d) { - a["coordinate" + g]() - } - } - d.getSprites()[0].setAttributes({ - data: f.labels - }); - f.fireEvent("datachange", f.labels) - }, - calculateLayout: function(a) { - a.data = this.labels; - this.callParent([a]) - }, - calculateMajorTicks: function(a) { - var g = this, - f = a.attr, - d = a.data, - e = f.max - f.min, - j = e / Math.max(1, f.length) * (f.visibleMax - f.visibleMin), - i = f.min + e * f.visibleMin, - b = f.min + e * f.visibleMax, - h = f.estStepSize * j; - var c = g.snapEnds(a, Math.max(0, f.min), Math.min(f.max, d.length - 1), h); - if (c) { - g.trimByRange(a, c, i, b); - a.majorTicks = c - } - }, - snapEnds: function(e, d, a, b) { - b = Math.ceil(b); - var c = Math.floor((a - d) / b), - f = e.data; - return { - min: d, - max: a, - from: d, - to: c * b + d, - step: b, - steps: c, - unit: 1, - getLabel: function(g) { - return f[this.from + this.step * g] - }, - get: function(g) { - return this.from + this.step * g - } - } - }, - trimByRange: function(b, f, h, a) { - var i = f.unit, - g = Math.ceil((h - f.from) / i) * i, - d = Math.floor((a - f.from) / i) * i, - c = Math.max(0, Math.ceil(g / f.step)), - e = Math.min(f.steps, Math.floor(d / f.step)); - if (e < f.steps) { - f.to = e - } - if (f.max > a) { - f.max = f.to - } - if (f.from < h && f.step > 0) { - f.from = f.from + c * f.step * i; - while (f.from < h) { - c++; - f.from += f.step * i - } - } - if (f.min < h) { - f.min = f.from - } - f.steps = e - c - }, - getCoordFor: function(c, d, a, b) { - this.labels.push(c); - return this.labels.length - 1 - } -}); -Ext.define("Ext.chart.axis.layout.CombineDuplicate", { - extend: "Ext.chart.axis.layout.Discrete", - alias: "axisLayout.combineDuplicate", - getCoordFor: function(d, e, b, c) { - if (!(d in this.labelMap)) { - var a = this.labelMap[d] = this.labels.length; - this.labels.push(d); - return a - } - return this.labelMap[d] - } -}); -Ext.define("Ext.chart.axis.layout.Continuous", { - extend: "Ext.chart.axis.layout.Layout", - alias: "axisLayout.continuous", - isContinuous: true, - config: { - adjustMinimumByMajorUnit: false, - adjustMaximumByMajorUnit: false - }, - getCoordFor: function(c, d, a, b) { - return +c - }, - snapEnds: function(a, d, i, h) { - var f = a.segmenter, - c = this.getAxis(), - l = c.getMajorTickSteps(), - e = l && f.exactStep ? f.exactStep(d, (i - d) / l) : f.preferredStep(d, h), - k = e.unit, - b = e.step, - j = f.align(d, b, k), - g = (l || f.diff(d, i, k)) + 1; - return { - min: f.from(d), - max: f.from(i), - from: j, - to: f.add(j, g * b, k), - step: b, - steps: g, - unit: k, - get: function(m) { - return f.add(this.from, this.step * m, k) - } - } - }, - snapMinorEnds: function(a) { - var e = a.majorTicks, - m = this.getAxis().getMinorTickSteps(), - f = a.segmenter, - d = e.min, - i = e.max, - k = e.from, - l = e.unit, - b = e.step / m, - n = b * l.scale, - j = k - d, - c = Math.floor(j / n), - h = c + Math.floor((i - e.to) / n) + 1, - g = e.steps * m + h; - return { - min: d, - max: i, - from: d + j % n, - to: f.add(k, g * b, l), - step: b, - steps: g, - unit: l, - get: function(o) { - return (o % m + c + 1 !== 0) ? f.add(this.from, this.step * o, l) : null - } - } - } -}); -Ext.define("Ext.chart.axis.Axis", { - xtype: "axis", - mixins: { - observable: "Ext.mixin.Observable" - }, - requires: ["Ext.chart.axis.sprite.Axis", "Ext.chart.axis.segmenter.*", "Ext.chart.axis.layout.*"], - isAxis: true, - config: { - position: "bottom", - fields: [], - label: undefined, - grid: false, - limits: null, - renderer: null, - chart: null, - style: null, - margin: 0, - titleMargin: 4, - background: null, - minimum: NaN, - maximum: NaN, - reconcileRange: false, - minZoom: 1, - maxZoom: 10000, - layout: "continuous", - segmenter: "numeric", - hidden: false, - majorTickSteps: 0, - minorTickSteps: 0, - adjustByMajorUnit: true, - title: null, - increment: 0.5, - length: 0, - center: null, - radius: null, - totalAngle: Math.PI, - rotation: null, - labelInSpan: null, - visibleRange: [0, 1], - needHighPrecision: false, - linkedTo: null, - floating: null - }, - titleOffset: 0, - spriteAnimationCount: 0, - prevMin: 0, - prevMax: 1, - boundSeries: [], - sprites: null, - surface: null, - range: null, - xValues: [], - yValues: [], - masterAxis: null, - applyRotation: function(b) { - var a = Math.PI * 2; - return (b % a + Math.PI) % a - Math.PI - }, - updateRotation: function(b) { - var c = this.getSprites(), - a = this.getPosition(); - if (!this.getHidden() && a === "angular" && c[0]) { - c[0].setAttributes({ - baseRotation: b - }) - } - }, - applyTitle: function(c, b) { - var a; - if (Ext.isString(c)) { - c = { - text: c - } - } - if (!b) { - b = Ext.create("sprite.text", c); - if ((a = this.getSurface())) { - a.add(b) - } - } else { - b.setAttributes(c) - } - return b - }, - applyFloating: function(b, a) { - if (b === null) { - b = { - value: null, - alongAxis: null - } - } else { - if (Ext.isNumber(b)) { - b = { - value: b, - alongAxis: null - } - } - } - if (Ext.isObject(b)) { - if (a && a.alongAxis) { - delete this.getChart().getAxis(a.alongAxis).floatingAxes[this.getId()] - } - return b - } - return a - }, - constructor: function(a) { - var b = this, - c; - b.sprites = []; - b.labels = []; - b.floatingAxes = {}; - a = a || {}; - if (a.position === "angular") { - a.style = a.style || {}; - a.style.estStepSize = 1 - } - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.mixins.observable.constructor.apply(b, arguments) - }, - getAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "vertical"; - case "top": - case "bottom": - return "horizontal"; - case "radial": - return "radial"; - case "angular": - return "angular" - } - }, - getGridAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "horizontal"; - case "top": - case "bottom": - return "vertical"; - case "radial": - return "circular"; - case "angular": - return "radial" - } - }, - getSurface: function() { - var e = this, - d = e.getChart(); - if (d && !e.surface) { - var b = e.surface = d.getSurface(e.getId(), "axis"), - c = e.gridSurface = d.getSurface("main"), - a = e.getSprites()[0], - f = e.getGridAlignment(); - c.waitFor(b); - e.getGrid(); - if (e.getLimits() && f) { - f = f.replace("3d", ""); - e.limits = { - surface: d.getSurface("overlay"), - lines: new Ext.chart.Markers(), - titles: new Ext.draw.sprite.Instancing() - }; - e.limits.lines.setTemplate({ - xclass: "grid." + f - }); - e.limits.lines.getTemplate().setAttributes({ - strokeStyle: "black" - }, true); - e.limits.surface.add(e.limits.lines); - a.bindMarker(f + "-limit-lines", e.limits.lines); - e.limitTitleTpl = new Ext.draw.sprite.Text(); - e.limits.titles.setTemplate(e.limitTitleTpl); - e.limits.surface.add(e.limits.titles); - d.on("redraw", e.renderLimits, e) - } - } - return e.surface - }, - applyGrid: function(a) { - if (a === true) { - return {} - } - return a - }, - updateGrid: function(b) { - var e = this, - d = e.getChart(); - if (!d) { - e.on({ - chartattached: Ext.bind(e.updateGrid, e, [b]), - single: true - }); - return - } - var c = e.gridSurface, - a = e.getSprites()[0], - f = e.getGridAlignment(), - g; - if (b) { - g = e.gridSpriteEven; - if (!g) { - g = e.gridSpriteEven = new Ext.chart.Markers(); - g.setTemplate({ - xclass: "grid." + f - }); - c.add(g); - a.bindMarker(f + "-even", g) - } - if (Ext.isObject(b)) { - g.getTemplate().setAttributes(b); - if (Ext.isObject(b.even)) { - g.getTemplate().setAttributes(b.even) - } - } - g = e.gridSpriteOdd; - if (!g) { - g = e.gridSpriteOdd = new Ext.chart.Markers(); - g.setTemplate({ - xclass: "grid." + f - }); - c.add(g); - a.bindMarker(f + "-odd", g) - } - if (Ext.isObject(b)) { - g.getTemplate().setAttributes(b); - if (Ext.isObject(b.odd)) { - g.getTemplate().setAttributes(b.odd) - } - } - } - }, - renderLimits: function() { - this.getSprites()[0].renderLimits() - }, - getCoordFor: function(c, d, a, b) { - return this.getLayout().getCoordFor(c, d, a, b) - }, - applyPosition: function(a) { - return a.toLowerCase() - }, - applyLength: function(b, a) { - return b > 0 ? b : a - }, - applyLabel: function(b, a) { - if (!a) { - a = new Ext.draw.sprite.Text({}) - } - if (this.limitTitleTpl) { - this.limitTitleTpl.setAttributes(b) - } - a.setAttributes(b); - return a - }, - applyLayout: function(b, a) { - b = Ext.factory(b, null, a, "axisLayout"); - b.setAxis(this); - return b - }, - applySegmenter: function(a, b) { - a = Ext.factory(a, null, b, "segmenter"); - a.setAxis(this); - return a - }, - updateMinimum: function() { - this.range = null - }, - updateMaximum: function() { - this.range = null - }, - hideLabels: function() { - this.getSprites()[0].setDirty(true); - this.setLabel({ - hidden: true - }) - }, - showLabels: function() { - this.getSprites()[0].setDirty(true); - this.setLabel({ - hidden: false - }) - }, - renderFrame: function() { - this.getSurface().renderFrame() - }, - updateChart: function(d, b) { - var c = this, - a; - if (b) { - b.unregister(c); - b.un("serieschange", c.onSeriesChange, c); - b.un("redraw", c.renderLimits, c); - c.linkAxis(); - c.fireEvent("chartdetached", b, c) - } - if (d) { - d.on("serieschange", c.onSeriesChange, c); - c.surface = null; - a = c.getSurface(); - c.getLabel().setSurface(a); - a.add(c.getSprites()); - a.add(c.getTitle()); - d.register(c); - c.fireEvent("chartattached", d, c) - } - }, - applyBackground: function(a) { - var b = Ext.ClassManager.getByAlias("sprite.rect"); - return b.def.normalize(a) - }, - processData: function() { - this.getLayout().processData(); - this.range = null - }, - getDirection: function() { - return this.getChart().getDirectionForAxis(this.getPosition()) - }, - isSide: function() { - var a = this.getPosition(); - return a === "left" || a === "right" - }, - applyFields: function(a) { - return Ext.Array.from(a) - }, - applyVisibleRange: function(a, c) { - this.getChart(); - if (a[0] > a[1]) { - var b = a[0]; - a[0] = a[1]; - a[0] = b - } - if (a[1] === a[0]) { - a[1] += 1 / this.getMaxZoom() - } - if (a[1] > a[0] + 1) { - a[0] = 0; - a[1] = 1 - } else { - if (a[0] < 0) { - a[1] -= a[0]; - a[0] = 0 - } else { - if (a[1] > 1) { - a[0] -= a[1] - 1; - a[1] = 1 - } - } - } - if (c && a[0] === c[0] && a[1] === c[1]) { - return undefined - } - return a - }, - updateVisibleRange: function(a) { - this.fireEvent("visiblerangechange", this, a) - }, - onSeriesChange: function(e) { - var f = this, - b = e.getSeries(), - j = "get" + f.getDirection() + "Axis", - g = [], - c, d = b.length, - a, h; - for (c = 0; c < d; c++) { - if (this === b[c][j]()) { - g.push(b[c]) - } - } - f.boundSeries = g; - a = f.getLinkedTo(); - h = !Ext.isEmpty(a) && e.getAxis(a); - if (h) { - f.linkAxis(h) - } else { - f.getLayout().processData() - } - }, - linkAxis: function(a) { - var c = this; - - function b(f, d, e) { - e.getLayout()[f]("datachange", "onDataChange", d); - e[f]("rangechange", "onMasterAxisRangeChange", d) - } - if (c.masterAxis) { - b("un", c, c.masterAxis); - c.masterAxis = null - } - if (a) { - if (a.type !== this.type) { - Ext.Error.raise("Linked axes must be of the same type.") - } - b("on", c, a); - c.onDataChange(a.getLayout().labels); - c.onMasterAxisRangeChange(a, a.range); - c.setStyle(Ext.apply({}, c.config.style, a.config.style)); - c.setTitle(Ext.apply({}, c.config.title, a.config.title)); - c.setLabel(Ext.apply({}, c.config.label, a.config.label)); - c.masterAxis = a - } - }, - onDataChange: function(a) { - this.getLayout().labels = a - }, - onMasterAxisRangeChange: function(b, a) { - this.range = a - }, - applyRange: function(a) { - if (!a) { - return this.dataRange.slice(0) - } else { - return [a[0] === null ? this.dataRange[0] : a[0], a[1] === null ? this.dataRange[1] : a[1]] - } - }, - getRange: function() { - var m = this; - if (m.range) { - return m.range - } else { - if (m.masterAxis) { - return m.masterAxis.range - } - } - if (Ext.isNumber(m.getMinimum() + m.getMaximum())) { - return m.range = [m.getMinimum(), m.getMaximum()] - } - var d = Infinity, - n = -Infinity, - o = m.boundSeries, - h = m.getLayout(), - l = m.getSegmenter(), - p = m.getVisibleRange(), - b = "get" + m.getDirection() + "Range", - a, j, g, f, e, k; - for (e = 0, k = o.length; e < k; e++) { - f = o[e]; - var c = f[b](); - if (c) { - if (c[0] < d) { - d = c[0] - } - if (c[1] > n) { - n = c[1] - } - } - } - if (!isFinite(n)) { - n = m.prevMax - } - if (!isFinite(d)) { - d = m.prevMin - } - if (m.getLabelInSpan() || d === n) { - n += m.getIncrement(); - d -= m.getIncrement() - } - if (Ext.isNumber(m.getMinimum())) { - d = m.getMinimum() - } else { - m.prevMin = d - } - if (Ext.isNumber(m.getMaximum())) { - n = m.getMaximum() - } else { - m.prevMax = n - } - m.range = [Ext.Number.correctFloat(d), Ext.Number.correctFloat(n)]; - if (m.getReconcileRange()) { - m.reconcileRange() - } - if (m.getAdjustByMajorUnit() && l.adjustByMajorUnit && !m.getMajorTickSteps()) { - j = Ext.Object.chain(m.getSprites()[0].attr); - j.min = m.range[0]; - j.max = m.range[1]; - j.visibleMin = p[0]; - j.visibleMax = p[1]; - a = { - attr: j, - segmenter: l - }; - h.calculateLayout(a); - g = a.majorTicks; - if (g) { - l.adjustByMajorUnit(g.step, g.unit.scale, m.range); - j.min = m.range[0]; - j.max = m.range[1]; - delete a.majorTicks; - h.calculateLayout(a); - g = a.majorTicks; - l.adjustByMajorUnit(g.step, g.unit.scale, m.range) - } else { - if (!m.hasClearRangePending) { - m.hasClearRangePending = true; - m.getChart().on("layout", "clearRange", m) - } - } - } - if (!Ext.Array.equals(m.range, m.oldRange || [])) { - m.fireEvent("rangechange", m, m.range); - m.oldRange = m.range - } - return m.range - }, - clearRange: function() { - delete this.hasClearRangePending; - this.range = null - }, - reconcileRange: function() { - var e = this, - g = e.getChart().getAxes(), - f = e.getDirection(), - b, d, c, a; - if (!g) { - return - } - for (b = 0, d = g.length; b < d; b++) { - c = g[b]; - a = c.getRange(); - if (c === e || c.getDirection() !== f || !a || !c.getReconcileRange()) { - continue - } - if (a[0] < e.range[0]) { - e.range[0] = a[0] - } - if (a[1] > e.range[1]) { - e.range[1] = a[1] - } - } - }, - applyStyle: function(c, b) { - var a = Ext.ClassManager.getByAlias("sprite." + this.seriesType); - if (a && a.def) { - c = a.def.normalize(c) - } - b = Ext.apply(b || {}, c); - return b - }, - themeOnlyIfConfigured: { - grid: true - }, - updateTheme: function(d) { - var i = this, - k = d.getAxis(), - e = i.getPosition(), - o = i.getInitialConfig(), - c = i.defaultConfig, - g = i.getConfigurator().configs, - a = k.defaults, - n = k[e], - h = i.themeOnlyIfConfigured, - l, j, p, b, m, f; - k = Ext.merge({}, a, n); - for (l in k) { - j = k[l]; - f = g[l]; - if (j !== null && j !== undefined && f) { - m = o[l]; - p = Ext.isObject(j); - b = m === c[l]; - if (p) { - if (b && h[l]) { - continue - } - j = Ext.merge({}, j, m) - } - if (b || p) { - i[f.names.set](j) - } - } - } - }, - updateCenter: function(b) { - var e = this.getSprites(), - a = e[0], - d = b[0], - c = b[1]; - if (a) { - a.setAttributes({ - centerX: d, - centerY: c - }) - } - if (this.gridSpriteEven) { - this.gridSpriteEven.getTemplate().setAttributes({ - translationX: d, - translationY: c, - rotationCenterX: d, - rotationCenterY: c - }) - } - if (this.gridSpriteOdd) { - this.gridSpriteOdd.getTemplate().setAttributes({ - translationX: d, - translationY: c, - rotationCenterX: d, - rotationCenterY: c - }) - } - }, - getSprites: function() { - if (!this.getChart()) { - return - } - var i = this, - e = i.getRange(), - f = i.getPosition(), - g = i.getChart(), - c = g.getAnimation(), - d, a, b = i.getLength(), - h = i.superclass; - if (c === false) { - c = { - duration: 0 - } - } - if (e) { - a = Ext.applyIf({ - position: f, - axis: i, - min: e[0], - max: e[1], - length: b, - grid: i.getGrid(), - hidden: i.getHidden(), - titleOffset: i.titleOffset, - layout: i.getLayout(), - segmenter: i.getSegmenter(), - totalAngle: i.getTotalAngle(), - label: i.getLabel() - }, i.getStyle()); - if (!i.sprites.length) { - while (!h.xtype) { - h = h.superclass - } - d = Ext.create("sprite." + h.xtype, a); - d.fx.setCustomDurations({ - baseRotation: 0 - }); - d.fx.on("animationstart", "onAnimationStart", i); - d.fx.on("animationend", "onAnimationEnd", i); - d.setLayout(i.getLayout()); - d.setSegmenter(i.getSegmenter()); - d.setLabel(i.getLabel()); - i.sprites.push(d); - i.updateTitleSprite() - } else { - d = i.sprites[0]; - d.setAnimation(c); - d.setAttributes(a) - } - if (i.getRenderer()) { - d.setRenderer(i.getRenderer()) - } - } - return i.sprites - }, - updateTitleSprite: function() { - var f = this, - b = f.getLength(); - if (!f.sprites[0] || !Ext.isNumber(b)) { - return - } - var h = this.sprites[0].thickness, - a = f.getSurface(), - g = f.getTitle(), - e = f.getPosition(), - c = f.getMargin(), - i = f.getTitleMargin(), - d = a.roundPixel(b / 2); - if (g) { - switch (e) { - case "top": - g.setAttributes({ - x: d, - y: c + i / 2, - textBaseline: "top", - textAlign: "center" - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().height + i; - break; - case "bottom": - g.setAttributes({ - x: d, - y: h + i / 2, - textBaseline: "top", - textAlign: "center" - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().height + i; - break; - case "left": - g.setAttributes({ - x: c + i / 2, - y: d, - textBaseline: "top", - textAlign: "center", - rotationCenterX: c + i / 2, - rotationCenterY: d, - rotationRads: -Math.PI / 2 - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().width + i; - break; - case "right": - g.setAttributes({ - x: h - c + i / 2, - y: d, - textBaseline: "bottom", - textAlign: "center", - rotationCenterX: h + i / 2, - rotationCenterY: d, - rotationRads: Math.PI / 2 - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().width + i; - break - } - } - }, - onThicknessChanged: function() { - this.getChart().onThicknessChanged() - }, - getThickness: function() { - if (this.getHidden()) { - return 0 - } - return (this.sprites[0] && this.sprites[0].thickness || 1) + this.titleOffset + this.getMargin() - }, - onAnimationStart: function() { - this.spriteAnimationCount++; - if (this.spriteAnimationCount === 1) { - this.fireEvent("animationstart", this) - } - }, - onAnimationEnd: function() { - this.spriteAnimationCount--; - if (this.spriteAnimationCount === 0) { - this.fireEvent("animationend", this) - } - }, - getItemId: function() { - return this.getId() - }, - getAncestorIds: function() { - return [this.getChart().getId()] - }, - isXType: function(a) { - return a === "axis" - }, - resolveListenerScope: function(e) { - var d = this, - a = Ext._namedScopes[e], - c = d.getChart(), - b; - if (!a) { - b = c ? c.resolveListenerScope(e, false) : (e || d) - } else { - if (a.isThis) { - b = d - } else { - if (a.isController) { - b = c ? c.resolveListenerScope(e, false) : d - } else { - if (a.isSelf) { - b = c ? c.resolveListenerScope(e, false) : d; - if (b === c && !c.getInheritedConfig("defaultListenerScope")) { - b = d - } - } - } - } - } - return b - }, - destroy: function() { - var a = this; - a.setChart(null); - a.surface.destroy(); - a.surface = null; - a.callParent() - } -}); -Ext.define("Ext.chart.LegendBase", { - extend: "Ext.view.View", - config: { - tpl: ['
', '', '
', "', "{name}", "
", "
", "
"], - nodeContainerSelector: "div." + Ext.baseCSSPrefix + "legend-container", - itemSelector: "div." + Ext.baseCSSPrefix + "legend-item", - docked: "bottom" - }, - setDocked: function(d) { - var c = this, - a = c.ownerCt, - b; - c.docked = d; - switch (d) { - case "top": - case "bottom": - c.addCls(Ext.baseCSSPrefix + "horizontal"); - b = "hbox"; - break; - case "left": - case "right": - c.removeCls(Ext.baseCSSPrefix + "horizontal"); - b = "vbox"; - break - } - if (a) { - a.setDocked(d) - } - }, - setStore: function(a) { - this.bindStore(a) - }, - clearViewEl: function() { - this.callParent(arguments); - Ext.removeNode(this.getNodeContainer()) - }, - onItemClick: function(a, c, b, d) { - this.callParent(arguments); - this.toggleItem(b) - } -}); -Ext.define("Ext.chart.Legend", { - xtype: "legend", - extend: "Ext.chart.LegendBase", - config: { - baseCls: Ext.baseCSSPrefix + "legend", - padding: 5, - rect: null, - disableSelection: true, - toggleable: true - }, - toggleItem: function(c) { - if (!this.getToggleable()) { - return - } - var b = this.getStore(), - h = 0, - e, g = true, - d, f, a; - if (b) { - f = b.getCount(); - for (d = 0; d < f; d++) { - a = b.getAt(d); - if (a.get("disabled")) { - h++ - } - } - g = f - h > 1; - a = b.getAt(c); - if (a) { - e = a.get("disabled"); - if (e || g) { - a.set("disabled", !e) - } - } - } - } -}); -Ext.define("Ext.chart.AbstractChart", { - extend: "Ext.draw.Container", - requires: ["Ext.chart.theme.Default", "Ext.chart.series.Series", "Ext.chart.interactions.Abstract", "Ext.chart.axis.Axis", "Ext.data.StoreManager", "Ext.chart.Legend", "Ext.data.Store"], - isChart: true, - defaultBindProperty: "store", - config: { - store: "ext-empty-store", - theme: "default", - style: null, - animation: !Ext.isIE8, - series: [], - axes: [], - legend: null, - colors: null, - insetPadding: { - top: 10, - left: 10, - right: 10, - bottom: 10 - }, - background: null, - interactions: [], - mainRect: null, - resizeHandler: null, - highlightItem: null - }, - animationSuspendCount: 0, - chartLayoutSuspendCount: 0, - axisThicknessSuspendCount: 0, - isThicknessChanged: false, - surfaceZIndexes: { - background: 0, - main: 1, - grid: 2, - series: 3, - axis: 4, - chart: 5, - overlay: 6, - events: 7 - }, - constructor: function(a) { - var b = this; - b.itemListeners = {}; - b.surfaceMap = {}; - b.chartComponents = {}; - b.isInitializing = true; - b.suspendChartLayout(); - b.animationSuspendCount++; - b.callParent(arguments); - delete b.isInitializing; - b.getSurface("main"); - b.getSurface("chart").setFlipRtlText(b.getInherited().rtl); - b.getSurface("overlay").waitFor(b.getSurface("series")); - b.animationSuspendCount--; - b.resumeChartLayout() - }, - applyAnimation: function(a, b) { - if (!a) { - a = { - duration: 0 - } - } else { - if (a === true) { - a = { - easing: "easeInOut", - duration: 500 - } - } - } - return b ? Ext.apply({}, a, b) : a - }, - getAnimation: function() { - if (this.animationSuspendCount) { - return { - duration: 0 - } - } else { - return this.callParent() - } - }, - applyInsetPadding: function(b, a) { - if (!Ext.isObject(b)) { - return Ext.util.Format.parseBox(b) - } else { - if (!a) { - return b - } else { - return Ext.apply(a, b) - } - } - }, - suspendAnimation: function() { - var d = this, - c = d.getSeries(), - e = c.length, - b = -1, - a; - d.animationSuspendCount++; - if (d.animationSuspendCount === 1) { - while (++b < e) { - a = c[b]; - a.setAnimation(a.getAnimation()) - } - } - }, - resumeAnimation: function() { - var d = this, - c = d.getSeries(), - f = c.length, - b = -1, - a, e; - d.animationSuspendCount--; - if (d.animationSuspendCount === 0) { - while (++b < f) { - a = c[b]; - e = a.getAnimation(); - a.setAnimation(e.duration && e || d.getAnimation()) - } - } - }, - suspendChartLayout: function() { - this.chartLayoutSuspendCount++; - if (this.chartLayoutSuspendCount === 1) { - if (this.scheduledLayoutId) { - this.layoutInSuspension = true; - this.cancelChartLayout() - } else { - this.layoutInSuspension = false - } - } - }, - resumeChartLayout: function() { - this.chartLayoutSuspendCount--; - if (this.chartLayoutSuspendCount === 0) { - if (this.layoutInSuspension) { - this.scheduleLayout() - } - } - }, - cancelChartLayout: function() { - if (this.scheduledLayoutId) { - Ext.draw.Animator.cancel(this.scheduledLayoutId); - this.scheduledLayoutId = null - } - }, - scheduleLayout: function() { - var a = this; - if (a.allowSchedule() && !a.scheduledLayoutId) { - a.scheduledLayoutId = Ext.draw.Animator.schedule("doScheduleLayout", a) - } - }, - allowSchedule: function() { - return true - }, - doScheduleLayout: function() { - if (this.chartLayoutSuspendCount) { - this.layoutInSuspension = true - } else { - this.performLayout() - } - }, - suspendThicknessChanged: function() { - this.axisThicknessSuspendCount++ - }, - resumeThicknessChanged: function() { - if (this.axisThicknessSuspendCount > 0) { - this.axisThicknessSuspendCount--; - if (this.axisThicknessSuspendCount === 0 && this.isThicknessChanged) { - this.onThicknessChanged() - } - } - }, - onThicknessChanged: function() { - if (this.axisThicknessSuspendCount === 0) { - this.isThicknessChanged = false; - this.performLayout() - } else { - this.isThicknessChanged = true - } - }, - applySprites: function(b) { - var a = this.getSurface("chart"); - b = Ext.Array.from(b); - a.removeAll(true); - a.add(b); - return b - }, - initItems: function() { - var a = this.items, - b, d, c; - if (a && !a.isMixedCollection) { - this.items = []; - a = Ext.Array.from(a); - for (b = 0, d = a.length; b < d; b++) { - c = a[b]; - if (c.type) { - Ext.raise("To add custom sprites to the chart use the 'sprites' config.") - } else { - this.items.push(c) - } - } - } - this.callParent() - }, - applyBackground: function(c, e) { - var b = this.getSurface("background"), - d, a, f; - if (c) { - if (e) { - d = e.attr.width; - a = e.attr.height; - f = e.type === (c.type || "rect") - } - if (c.isSprite) { - e = c - } else { - if (c.type === "image" && Ext.isString(c.src)) { - if (f) { - e.setAttributes({ - src: c.src - }) - } else { - b.remove(e, true); - e = b.add(c) - } - } else { - if (f) { - e.setAttributes({ - fillStyle: c - }) - } else { - b.remove(e, true); - e = b.add({ - type: "rect", - fillStyle: c, - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0 - } - } - }) - } - } - } - } - if (d && a) { - e.setAttributes({ - width: d, - height: a - }) - } - e.setAnimation(this.getAnimation()); - return e - }, - getLegendStore: function() { - return this.legendStore - }, - refreshLegendStore: function() { - if (this.getLegendStore()) { - var d, e, c = this.getSeries(), - b, a = []; - if (c) { - for (d = 0, e = c.length; d < e; d++) { - b = c[d]; - if (b.getShowInLegend()) { - b.provideLegendInfo(a) - } - } - } - this.getLegendStore().setData(a) - } - }, - resetLegendStore: function() { - var c = this.getLegendStore(), - e, d, a, b; - if (c) { - e = this.getLegendStore().getData().items; - for (d = 0, a = e.length; d < a; d++) { - b = e[d]; - b.beginEdit(); - b.set("disabled", false); - b.commit() - } - } - }, - onUpdateLegendStore: function(b, a) { - var d = this.getSeries(), - c; - if (a && d) { - c = d.map[a.get("series")]; - if (c) { - c.setHiddenByIndex(a.get("index"), a.get("disabled")); - this.redraw() - } - } - }, - defaultResizeHandler: function(a) { - this.scheduleLayout(); - return false - }, - applyMainRect: function(a, b) { - if (!b) { - return a - } - this.getSeries(); - this.getAxes(); - if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]) { - return b - } else { - return a - } - }, - register: function(a) { - var b = this.chartComponents, - c = a.getId(); - b[c] = a - }, - unregister: function(a) { - var b = this.chartComponents, - c = a.getId(); - delete b[c] - }, - get: function(a) { - return this.chartComponents[a] - }, - getAxis: function(a) { - if (a instanceof Ext.chart.axis.Axis) { - return a - } else { - if (Ext.isNumber(a)) { - return this.getAxes()[a] - } else { - if (Ext.isString(a)) { - return this.get(a) - } - } - } - }, - getSurface: function(b, c) { - b = b || "main"; - c = c || b; - var d = this, - a = this.callParent([b]), - f = d.surfaceZIndexes, - e = d.surfaceMap; - if (c in f) { - a.element.setStyle("zIndex", f[c]) - } - if (!e[c]) { - e[c] = [] - } - if (Ext.Array.indexOf(e[c], a) < 0) { - a.type = c; - e[c].push(a); - a.on("destroy", d.forgetSurface, d) - } - return a - }, - forgetSurface: function(a) { - var d = this.surfaceMap; - if (!d || this.isDestroying) { - return - } - var c = d[a.type], - b = c ? Ext.Array.indexOf(c, a) : -1; - if (b >= 0) { - c.splice(b, 1) - } - }, - applyAxes: function(b, k) { - var l = this, - g = { - left: "right", - right: "left" - }, - m = [], - c, d, e, a, f, h, j; - l.animationSuspendCount++; - l.getStore(); - if (!k) { - k = []; - k.map = {} - } - j = k.map; - m.map = {}; - b = Ext.Array.from(b, true); - for (f = 0, h = b.length; f < h; f++) { - c = b[f]; - if (!c) { - continue - } - if (c instanceof Ext.chart.axis.Axis) { - d = j[c.getId()]; - c.setChart(l) - } else { - c = Ext.Object.chain(c); - e = c.linkedTo; - a = c.id; - if (Ext.isNumber(e)) { - c = Ext.merge({}, b[e], c) - } else { - if (Ext.isString(e)) { - Ext.Array.each(b, function(i) { - if (i.id === c.linkedTo) { - c = Ext.merge({}, i, c); - return false - } - }) - } - } - c.id = a; - c.chart = l; - if (l.getInherited().rtl) { - c.position = g[c.position] || c.position - } - a = c.getId && c.getId() || c.id; - c = Ext.factory(c, null, d = j[a], "axis") - } - if (c) { - m.push(c); - m.map[c.getId()] = c; - if (!d) { - c.on("animationstart", "onAnimationStart", l); - c.on("animationend", "onAnimationEnd", l) - } - } - } - for (f in j) { - if (!m.map[f]) { - j[f].destroy() - } - } - l.animationSuspendCount--; - return m - }, - updateAxes: function() { - if (!this.isDestroying) { - this.scheduleLayout() - } - }, - circularCopyArray: function(e, f, d) { - var c = [], - b, a = e && e.length; - if (a) { - for (b = 0; b < d; b++) { - c.push(e[(f + b) % a]) - } - } - return c - }, - circularCopyObject: function(f, g, d) { - var c = this, - b, e, a = {}; - if (d) { - for (b in f) { - if (f.hasOwnProperty(b)) { - e = f[b]; - if (Ext.isArray(e)) { - a[b] = c.circularCopyArray(e, g, d) - } else { - a[b] = e - } - } - } - } - return a - }, - getColors: function() { - var b = this, - a = b.config.colors, - c = b.getTheme(); - if (Ext.isArray(a) && a.length > 0) { - a = b.applyColors(a) - } - return a || (c && c.getColors()) - }, - applyColors: function(a) { - a = Ext.Array.map(a, function(b) { - if (Ext.isString(b)) { - return b - } else { - return b.toString() - } - }); - return a - }, - updateColors: function(c) { - var k = this, - e = k.getTheme(), - a = c || (e && e.getColors()), - l = 0, - f = k.getSeries(), - d = f && f.length, - g, j, b, h; - if (a.length) { - for (g = 0; g < d; g++) { - j = f[g]; - h = j.themeColorCount(); - b = k.circularCopyArray(a, l, h); - l += h; - j.updateChartColors(b) - } - } - k.refreshLegendStore() - }, - applyTheme: function(a) { - if (a && a.isTheme) { - return a - } - return Ext.Factory.chartTheme(a) - }, - updateTheme: function(g) { - var e = this, - f = e.getAxes(), - d = e.getSeries(), - a = e.getColors(), - c, b; - e.updateChartTheme(g); - for (b = 0; b < f.length; b++) { - f[b].updateTheme(g) - } - for (b = 0; b < d.length; b++) { - c = d[b]; - c.updateTheme(g) - } - e.updateSpriteTheme(g); - e.updateColors(a); - e.redraw() - }, - themeOnlyIfConfigured: {}, - updateChartTheme: function(c) { - var i = this, - k = c.getChart(), - n = i.getInitialConfig(), - b = i.defaultConfig, - e = i.getConfigurator().configs, - f = k.defaults, - g = k[i.xtype], - h = i.themeOnlyIfConfigured, - l, j, o, a, m, d; - k = Ext.merge({}, f, g); - for (l in k) { - j = k[l]; - d = e[l]; - if (j !== null && j !== undefined && d) { - m = n[l]; - o = Ext.isObject(j); - a = m === b[l]; - if (o) { - if (a && h[l]) { - continue - } - j = Ext.merge({}, j, m) - } - if (a || o) { - i[d.names.set](j) - } - } - } - }, - updateSpriteTheme: function(c) { - this.getSprites(); - var j = this, - e = j.getSurface("chart"), - h = e.getItems(), - m = c.getSprites(), - k, a, l, f, d, b, g; - for (b = 0, g = h.length; b < g; b++) { - k = h[b]; - a = m[k.type]; - if (a) { - f = {}; - d = k.type === "text"; - for (l in a) { - if (!(l in k.config)) { - if (!(d && l.indexOf("font") === 0 && k.config.font)) { - f[l] = a[l] - } - } - } - k.setAttributes(f) - } - } - }, - addSeries: function(b) { - var a = this.getSeries(); - Ext.Array.push(a, b); - this.setSeries(a) - }, - removeSeries: function(d) { - d = Ext.Array.from(d); - var b = this.getSeries(), - f = [], - a = d.length, - g = {}, - c, e; - for (c = 0; c < a; c++) { - e = d[c]; - if (typeof e !== "string") { - e = e.getId() - } - g[e] = true - } - for (c = 0, a = b.length; c < a; c++) { - if (!g[b[c].getId()]) { - f.push(b[c]) - } - } - this.setSeries(f) - }, - applySeries: function(e, d) { - var g = this, - j = [], - h, a, c, f, b; - g.animationSuspendCount++; - g.getAxes(); - if (d) { - h = d.map - } else { - d = []; - h = d.map = {} - } - j.map = {}; - e = Ext.Array.from(e, true); - for (c = 0, f = e.length; c < f; c++) { - b = e[c]; - if (!b) { - continue - } - a = h[b.getId && b.getId() || b.id]; - if (b instanceof Ext.chart.series.Series) { - if (a && a !== b) { - a.destroy() - } - b.setChart(g) - } else { - if (Ext.isObject(b)) { - if (a) { - a.setConfig(b); - b = a - } else { - if (Ext.isString(b)) { - b = { - type: b - } - } - b.chart = g; - b = Ext.create(b.xclass || ("series." + b.type), b); - b.on("animationstart", "onAnimationStart", g); - b.on("animationend", "onAnimationEnd", g) - } - } - } - j.push(b); - j.map[b.getId()] = b - } - for (c in h) { - if (!j.map[h[c].getId()]) { - h[c].destroy() - } - } - g.animationSuspendCount--; - return j - }, - applyLegend: function(b, a) { - return Ext.factory(b, Ext.chart.Legend, a) - }, - updateLegend: function(b, a) { - if (a) { - a.destroy() - } - if (b) { - this.getItems(); - this.legendStore = new Ext.data.Store({ - autoDestroy: true, - fields: ["id", "name", "mark", "disabled", "series", "index"] - }); - b.setStore(this.legendStore); - this.refreshLegendStore(); - this.legendStore.on("update", "onUpdateLegendStore", this) - } - }, - updateSeries: function(b, a) { - var c = this; - if (c.isDestroying) { - return - } - c.animationSuspendCount++; - c.fireEvent("serieschange", c, b, a); - c.refreshLegendStore(); - if (!Ext.isEmpty(b)) { - c.updateTheme(c.getTheme()) - } - c.scheduleLayout(); - c.animationSuspendCount-- - }, - applyInteractions: function(h, d) { - if (!d) { - d = []; - d.map = {} - } - var g = this, - a = [], - c = d.map, - e, f, b; - a.map = {}; - h = Ext.Array.from(h, true); - for (e = 0, f = h.length; e < f; e++) { - b = h[e]; - if (!b) { - continue - } - b = Ext.factory(b, null, c[b.getId && b.getId() || b.id], "interaction"); - if (b) { - b.setChart(g); - a.push(b); - a.map[b.getId()] = b - } - } - for (e in c) { - if (!a.map[e]) { - c[e].destroy() - } - } - return a - }, - getInteraction: function(e) { - var f = this.getInteractions(), - a = f && f.length, - c = null, - b, d; - if (a) { - for (d = 0; d < a; ++d) { - b = f[d]; - if (b.type === e) { - c = b; - break - } - } - } - return c - }, - applyStore: function(a) { - return a && Ext.StoreManager.lookup(a) - }, - updateStore: function(a, c) { - var b = this; - if (c) { - c.un({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: b, - order: "after" - }); - if (c.autoDestroy) { - c.destroy() - } - } - if (a) { - a.on({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: b, - order: "after" - }) - } - b.fireEvent("storechange", b, a, c); - b.onDataChanged() - }, - redraw: function() { - this.fireEvent("redraw", this) - }, - performLayout: function() { - var d = this, - b = d.getChartSize(true), - c = [0, 0, b.width, b.height], - a = d.getBackground(); - d.hasFirstLayout = true; - d.fireEvent("layout", d); - d.cancelChartLayout(); - d.getSurface("background").setRect(c); - d.getSurface("chart").setRect(c); - a.setAttributes({ - width: b.width, - height: b.height - }) - }, - getChartSize: function(b) { - var a = this; - if (b) { - a.chartSize = null - } - return a.chartSize || (a.chartSize = a.innerElement.getSize()) - }, - getEventXY: function(a) { - return this.getSurface().getEventXY(a) - }, - getItemForPoint: function(h, g) { - var f = this, - a = f.getSeries(), - e = f.getMainRect(), - d = a.length, - b = f.hasFirstLayout ? d - 1 : -1, - c, j; - if (!(e && h >= 0 && h <= e[2] && g >= 0 && g <= e[3])) { - return null - } - for (; b >= 0; b--) { - c = a[b]; - j = c.getItemForPoint(h, g); - if (j) { - return j - } - } - return null - }, - getItemsForPoint: function(h, g) { - var f = this, - a = f.getSeries(), - d = a.length, - b = f.hasFirstLayout ? d - 1 : -1, - e = [], - c, j; - for (; b >= 0; b--) { - c = a[b]; - j = c.getItemForPoint(h, g); - if (j) { - e.push(j) - } - } - return e - }, - onAnimationStart: function() { - this.fireEvent("animationstart", this) - }, - onAnimationEnd: function() { - this.fireEvent("animationend", this) - }, - onDataChanged: function() { - var d = this; - if (d.isInitializing) { - return - } - var c = d.getMainRect(), - a = d.getStore(), - b = d.getSeries(), - e = d.getAxes(); - if (!a || !e || !b) { - return - } - if (!c) { - d.on({ - redraw: d.onDataChanged, - scope: d, - single: true - }); - return - } - d.processData(); - d.redraw() - }, - recordCount: 0, - processData: function() { - var g = this, - e = g.getStore().getCount(), - c = g.getSeries(), - f = c.length, - d = false, - b = 0, - a; - for (; b < f; b++) { - a = c[b]; - a.processData(); - if (!d && a.isStoreDependantColorCount) { - d = true - } - } - if (d && e > g.recordCount) { - g.updateColors(g.getColors()); - g.recordCount = e - } - }, - bindStore: function(a) { - this.setStore(a) - }, - applyHighlightItem: function(f, a) { - if (f === a) { - return - } - if (Ext.isObject(f) && Ext.isObject(a)) { - var e = f, - d = a, - c = e.sprite && (e.sprite[0] || e.sprite), - b = d.sprite && (d.sprite[0] || d.sprite); - if (c === b && e.index === d.index) { - return - } - } - return f - }, - updateHighlightItem: function(b, a) { - if (a) { - a.series.setAttributesForItem(a, { - highlighted: false - }) - } - if (b) { - b.series.setAttributesForItem(b, { - highlighted: true - }); - this.fireEvent("itemhighlight", this, b, a) - } - this.fireEvent("itemhighlightchange", this, b, a) - }, - destroyChart: function() { - var f = this, - d = f.getLegend(), - g = f.getAxes(), - c = f.getSeries(), - h = f.getInteractions(), - b = [], - a, e; - f.surfaceMap = null; - for (a = 0, e = h.length; a < e; a++) { - h[a].destroy() - } - for (a = 0, e = g.length; a < e; a++) { - g[a].destroy() - } - for (a = 0, e = c.length; a < e; a++) { - c[a].destroy() - } - f.setInteractions(b); - f.setAxes(b); - f.setSeries(b); - if (d) { - d.destroy(); - f.setLegend(null) - } - f.legendStore = null; - f.setStore(null); - f.cancelChartLayout() - }, - getRefItems: function(b) { - var g = this, - e = g.getSeries(), - h = g.getAxes(), - a = g.getInteractions(), - c = [], - d, f; - for (d = 0, f = e.length; d < f; d++) { - c.push(e[d]); - if (e[d].getRefItems) { - c.push.apply(c, e[d].getRefItems(b)) - } - } - for (d = 0, f = h.length; d < f; d++) { - c.push(h[d]); - if (h[d].getRefItems) { - c.push.apply(c, h[d].getRefItems(b)) - } - } - for (d = 0, f = a.length; d < f; d++) { - c.push(a[d]); - if (a[d].getRefItems) { - c.push.apply(c, a[d].getRefItems(b)) - } - } - return c - } -}); -Ext.define("Ext.chart.overrides.AbstractChart", { - override: "Ext.chart.AbstractChart", - updateLegend: function(b, a) { - var c; - this.callParent([b, a]); - if (b) { - c = b.docked; - this.addDocked({ - dock: c, - xtype: "panel", - shrinkWrap: true, - scrollable: true, - layout: { - type: c === "top" || c === "bottom" ? "hbox" : "vbox", - pack: "center" - }, - items: b, - cls: Ext.baseCSSPrefix + "legend-panel" - }) - } - }, - performLayout: function() { - if (this.isVisible(true)) { - return this.callParent() - } - this.cancelChartLayout(); - return false - }, - afterComponentLayout: function(c, a, b, d) { - this.callParent([c, a, b, d]); - this.scheduleLayout() - }, - allowSchedule: function() { - return this.rendered - }, - onDestroy: function() { - this.destroyChart(); - this.callParent(arguments) - } -}); -Ext.define("Ext.chart.grid.HorizontalGrid", { - extend: "Ext.draw.sprite.Sprite", - alias: "grid.horizontal", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number" - }, - defaults: { - x: 0, - y: 0, - width: 1, - height: 1, - strokeStyle: "#DDD" - } - } - }, - render: function(b, c, e) { - var a = this.attr, - f = b.roundPixel(a.y), - d = c.lineWidth * 0.5; - c.beginPath(); - c.rect(e[0] - b.matrix.getDX(), f + d, +e[2], a.height); - c.fill(); - c.beginPath(); - c.moveTo(e[0] - b.matrix.getDX(), f + d); - c.lineTo(e[0] + e[2] - b.matrix.getDX(), f + d); - c.stroke() - } -}); -Ext.define("Ext.chart.grid.VerticalGrid", { - extend: "Ext.draw.sprite.Sprite", - alias: "grid.vertical", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number" - }, - defaults: { - x: 0, - y: 0, - width: 1, - height: 1, - strokeStyle: "#DDD" - } - } - }, - render: function(c, d, f) { - var b = this.attr, - a = c.roundPixel(b.x), - e = d.lineWidth * 0.5; - d.beginPath(); - d.rect(a - e, f[1] - c.matrix.getDY(), b.width, f[3]); - d.fill(); - d.beginPath(); - d.moveTo(a - e, f[1] - c.matrix.getDY()); - d.lineTo(a - e, f[1] + f[3] - c.matrix.getDY()); - d.stroke() - } -}); -Ext.define("Ext.chart.CartesianChart", { - extend: "Ext.chart.AbstractChart", - alternateClassName: "Ext.chart.Chart", - requires: ["Ext.chart.grid.HorizontalGrid", "Ext.chart.grid.VerticalGrid"], - xtype: ["cartesian", "chart"], - isCartesian: true, - config: { - flipXY: false, - innerRect: [0, 0, 1, 1], - innerPadding: { - top: 0, - left: 0, - right: 0, - bottom: 0 - } - }, - applyInnerPadding: function(b, a) { - if (!Ext.isObject(b)) { - return Ext.util.Format.parseBox(b) - } else { - if (!a) { - return b - } else { - return Ext.apply(a, b) - } - } - }, - getDirectionForAxis: function(a) { - var b = this.getFlipXY(); - if (a === "left" || a === "right") { - if (b) { - return "X" - } else { - return "Y" - } - } else { - if (b) { - return "Y" - } else { - return "X" - } - } - }, - performLayout: function() { - var A = this; - A.animationSuspendCount++; - if (A.callParent() === false) { - --A.animationSuspendCount; - return - } - A.suspendThicknessChanged(); - var d = A.getSurface("chart").getRect(), - o = d[2], - n = d[3], - z = A.getAxes(), - b, q = A.getSeries(), - h, l, a, f = A.getInsetPadding(), - v = A.getInnerPadding(), - r, c, e = Ext.apply({}, f), - u, p, s, k, m, y, t, x, g, j = A.getInherited().rtl, - w = A.getFlipXY(); - if (o <= 0 || n <= 0) { - return - } - for (x = 0; x < z.length; x++) { - b = z[x]; - l = b.getSurface(); - m = b.getFloating(); - y = m ? m.value : null; - a = b.getThickness(); - switch (b.getPosition()) { - case "top": - l.setRect([0, e.top + 1, o, a]); - break; - case "bottom": - l.setRect([0, n - (e.bottom + a), o, a]); - break; - case "left": - l.setRect([e.left, 0, a, n]); - break; - case "right": - l.setRect([o - (e.right + a), 0, a, n]); - break - } - if (y === null) { - e[b.getPosition()] += a - } - } - o -= e.left + e.right; - n -= e.top + e.bottom; - u = [e.left, e.top, o, n]; - e.left += v.left; - e.top += v.top; - e.right += v.right; - e.bottom += v.bottom; - p = o - v.left - v.right; - s = n - v.top - v.bottom; - A.setInnerRect([e.left, e.top, p, s]); - if (p <= 0 || s <= 0) { - return - } - A.setMainRect(u); - A.getSurface().setRect(u); - for (x = 0, g = A.surfaceMap.grid && A.surfaceMap.grid.length; x < g; x++) { - c = A.surfaceMap.grid[x]; - c.setRect(u); - c.matrix.set(1, 0, 0, 1, v.left, v.top); - c.matrix.inverse(c.inverseMatrix) - } - for (x = 0; x < z.length; x++) { - b = z[x]; - l = b.getSurface(); - t = l.matrix; - k = t.elements; - switch (b.getPosition()) { - case "top": - case "bottom": - k[4] = e.left; - b.setLength(p); - break; - case "left": - case "right": - k[5] = e.top; - b.setLength(s); - break - } - b.updateTitleSprite(); - t.inverse(l.inverseMatrix) - } - for (x = 0, g = q.length; x < g; x++) { - h = q[x]; - r = h.getSurface(); - r.setRect(u); - if (w) { - if (j) { - r.matrix.set(0, -1, -1, 0, v.left + p, v.top + s) - } else { - r.matrix.set(0, -1, 1, 0, v.left, v.top + s) - } - } else { - r.matrix.set(1, 0, 0, -1, v.left, v.top + s) - } - r.matrix.inverse(r.inverseMatrix); - h.getOverlaySurface().setRect(u) - } - A.redraw(); - A.animationSuspendCount--; - A.resumeThicknessChanged() - }, - refloatAxes: function() { - var h = this, - g = h.getAxes(), - o = (g && g.length) || 0, - c, d, n, f, l, b, k, r = h.getChartSize(), - q = h.getInsetPadding(), - p = h.getInnerPadding(), - a = r.width - q.left - q.right, - m = r.height - q.top - q.bottom, - j, e; - for (e = 0; e < o; e++) { - c = g[e]; - f = c.getFloating(); - l = f ? f.value : null; - if (l === null) { - delete c.floatingAtCoord; - continue - } - d = c.getSurface(); - n = d.getRect(); - if (!n) { - continue - } - n = n.slice(); - b = h.getAxis(f.alongAxis); - if (b) { - j = b.getAlignment() === "horizontal"; - if (Ext.isString(l)) { - l = b.getCoordFor(l) - } - b.floatingAxes[c.getId()] = l; - k = b.getSprites()[0].attr.matrix; - if (j) { - l = l * k.getXX() + k.getDX(); - c.floatingAtCoord = l + p.left + p.right - } else { - l = l * k.getYY() + k.getDY(); - c.floatingAtCoord = l + p.top + p.bottom - } - } else { - j = c.getAlignment() === "horizontal"; - if (j) { - c.floatingAtCoord = l + p.top + p.bottom - } else { - c.floatingAtCoord = l + p.left + p.right - } - l = d.roundPixel(0.01 * l * (j ? m : a)) - } - switch (c.getPosition()) { - case "top": - n[1] = q.top + p.top + l - n[3] + 1; - break; - case "bottom": - n[1] = q.top + p.top + (b ? l : m - l); - break; - case "left": - n[0] = q.left + p.left + l - n[2]; - break; - case "right": - n[0] = q.left + p.left + (b ? l : a - l) - 1; - break - } - d.setRect(n) - } - }, - redraw: function() { - var C = this, - r = C.getSeries(), - z = C.getAxes(), - b = C.getMainRect(), - p, t, w = C.getInnerPadding(), - f, l, s, e, q, A, v, g, d, c, a, k, n, y = C.getFlipXY(), - x = 1000, - m, u, h, o, B; - if (!b) { - return - } - p = b[2] - w.left - w.right; - t = b[3] - w.top - w.bottom; - for (A = 0; A < r.length; A++) { - h = r[A]; - if ((c = h.getXAxis())) { - n = c.getVisibleRange(); - l = c.getRange(); - l = [l[0] + (l[1] - l[0]) * n[0], l[0] + (l[1] - l[0]) * n[1]] - } else { - l = h.getXRange() - } - if ((a = h.getYAxis())) { - n = a.getVisibleRange(); - s = a.getRange(); - s = [s[0] + (s[1] - s[0]) * n[0], s[0] + (s[1] - s[0]) * n[1]] - } else { - s = h.getYRange() - } - q = { - visibleMinX: l[0], - visibleMaxX: l[1], - visibleMinY: s[0], - visibleMaxY: s[1], - innerWidth: p, - innerHeight: t, - flipXY: y - }; - f = h.getSprites(); - for (v = 0, g = f.length; v < g; v++) { - o = f[v]; - m = o.attr.zIndex; - if (m < x) { - m += (A + 1) * 100 + x; - o.attr.zIndex = m; - B = o.getMarker("items"); - if (B) { - u = B.attr.zIndex; - if (u === Number.MAX_VALUE) { - B.attr.zIndex = m - } else { - if (u < x) { - B.attr.zIndex = m + u - } - } - } - } - o.setAttributes(q, true) - } - } - for (A = 0; A < z.length; A++) { - d = z[A]; - e = d.isSide(); - f = d.getSprites(); - k = d.getRange(); - n = d.getVisibleRange(); - q = { - dataMin: k[0], - dataMax: k[1], - visibleMin: n[0], - visibleMax: n[1] - }; - if (e) { - q.length = t; - q.startGap = w.bottom; - q.endGap = w.top - } else { - q.length = p; - q.startGap = w.left; - q.endGap = w.right - } - for (v = 0, g = f.length; v < g; v++) { - f[v].setAttributes(q, true) - } - } - C.renderFrame(); - C.callParent(arguments) - }, - renderFrame: function() { - this.refloatAxes(); - this.callParent() - } -}); -Ext.define("Ext.chart.grid.CircularGrid", { - extend: "Ext.draw.sprite.Circle", - alias: "grid.circular", - inheritableStatics: { - def: { - defaults: { - r: 1, - strokeStyle: "#DDD" - } - } - } -}); -Ext.define("Ext.chart.grid.RadialGrid", { - extend: "Ext.draw.sprite.Path", - alias: "grid.radial", - inheritableStatics: { - def: { - processors: { - startRadius: "number", - endRadius: "number" - }, - defaults: { - startRadius: 0, - endRadius: 1, - scalingCenterX: 0, - scalingCenterY: 0, - strokeStyle: "#DDD" - }, - triggers: { - startRadius: "path,bbox", - endRadius: "path,bbox" - } - } - }, - render: function() { - this.callParent(arguments) - }, - updatePath: function(d, a) { - var b = a.startRadius, - c = a.endRadius; - d.moveTo(b, 0); - d.lineTo(c, 0) - } -}); -Ext.define("Ext.chart.PolarChart", { - extend: "Ext.chart.AbstractChart", - requires: ["Ext.chart.grid.CircularGrid", "Ext.chart.grid.RadialGrid"], - xtype: "polar", - isPolar: true, - config: { - center: [0, 0], - radius: 0, - innerPadding: 0 - }, - getDirectionForAxis: function(a) { - return a === "radial" ? "Y" : "X" - }, - applyCenter: function(a, b) { - if (b && a[0] === b[0] && a[1] === b[1]) { - return - } - return [+a[0], +a[1]] - }, - updateCenter: function(a) { - var g = this, - h = g.getAxes(), - d = g.getSeries(), - c, f, e, b; - for (c = 0, f = h.length; c < f; c++) { - e = h[c]; - e.setCenter(a) - } - for (c = 0, f = d.length; c < f; c++) { - b = d[c]; - b.setCenter(a) - } - }, - applyInnerPadding: function(b, a) { - return Ext.isNumber(b) ? b : a - }, - doSetSurfaceRect: function(b, c) { - var a = this.getMainRect(); - b.setRect(c); - b.matrix.set(1, 0, 0, 1, a[0] - c[0], a[1] - c[1]); - b.inverseMatrix.set(1, 0, 0, 1, c[0] - a[0], c[1] - a[1]) - }, - applyAxes: function(f, h) { - var e = this, - g = Ext.Array.from(e.config.series)[0], - b, d, c, a; - if (g.type === "radar" && f && f.length) { - for (b = 0, d = f.length; b < d; b++) { - c = f[b]; - if (c.position === "angular") { - a = true; - break - } - } - if (!a) { - f.push({ - type: "category", - position: "angular", - fields: g.xField || g.angleField, - style: { - estStepSize: 1 - }, - grid: true - }) - } - } - return this.callParent(arguments) - }, - performLayout: function() { - var F = this, - g = true; - try { - F.animationSuspendCount++; - if (this.callParent() === false) { - g = false; - return - } - F.suspendThicknessChanged(); - var h = F.getSurface("chart").getRect(), - v = F.getInsetPadding(), - G = F.getInnerPadding(), - l = Ext.apply({}, v), - d, s = h[2] - v.left - v.right, - r = h[3] - v.top - v.bottom, - x = [v.left, v.top, s, r], - u = F.getSeries(), - p, t = s - G * 2, - w = r - G * 2, - D = [t * 0.5 + G, w * 0.5 + G], - j = Math.min(t, w) * 0.5, - A = F.getAxes(), - f, a, k, m = [], - o = [], - E = j - G, - z, n, b, q, y, c, C; - F.setMainRect(x); - F.doSetSurfaceRect(F.getSurface(), x); - for (z = 0, n = F.surfaceMap.grid && F.surfaceMap.grid.length; z < n; z++) { - F.doSetSurfaceRect(F.surfaceMap.grid[z], h) - } - for (z = 0, n = A.length; z < n; z++) { - f = A[z]; - switch (f.getPosition()) { - case "angular": - m.push(f); - break; - case "radial": - o.push(f); - break - } - } - for (z = 0, n = m.length; z < n; z++) { - f = m[z]; - q = f.getFloating(); - y = q ? q.value : null; - F.doSetSurfaceRect(f.getSurface(), h); - a = f.getThickness(); - for (d in l) { - l[d] += a - } - s = h[2] - l.left - l.right; - r = h[3] - l.top - l.bottom; - b = Math.min(s, r) * 0.5; - if (z === 0) { - E = b - G - } - f.setMinimum(0); - f.setLength(b); - f.getSprites(); - k = f.sprites[0].attr.lineWidth * 0.5; - for (d in l) { - l[d] += k - } - } - for (z = 0, n = o.length; z < n; z++) { - f = o[z]; - F.doSetSurfaceRect(f.getSurface(), h); - f.setMinimum(0); - f.setLength(E); - f.getSprites() - } - for (z = 0, n = u.length; z < n; z++) { - p = u[z]; - if (p.type === "gauge" && !c) { - c = p - } else { - p.setRadius(E) - } - F.doSetSurfaceRect(p.getSurface(), x) - } - F.doSetSurfaceRect(F.getSurface("overlay"), h); - if (c) { - c.setRect(x); - C = c.getRadius() - G; - F.setRadius(C); - F.setCenter(c.getCenter()); - c.setRadius(C); - if (A.length && A[0].getPosition() === "gauge") { - f = A[0]; - F.doSetSurfaceRect(f.getSurface(), h); - f.setTotalAngle(c.getTotalAngle()); - f.setLength(C) - } - } else { - F.setRadius(j); - F.setCenter(D) - } - F.redraw() - } catch (B) { - throw B - } finally { - F.animationSuspendCount--; - if (g) { - F.resumeThicknessChanged() - } - } - }, - refloatAxes: function() { - var j = this, - g = j.getAxes(), - h = j.getMainRect(), - f, k, b, d, a, c, e; - if (!h) { - return - } - e = 0.5 * Math.min(h[2], h[3]); - for (d = 0, a = g.length; d < a; d++) { - c = g[d]; - f = c.getFloating(); - k = f ? f.value : null; - if (k !== null) { - b = j.getAxis(f.alongAxis); - if (c.getPosition() === "angular") { - if (b) { - k = b.getLength() * k / b.getRange()[1] - } else { - k = 0.01 * k * e - } - c.sprites[0].setAttributes({ - length: k - }, true) - } else { - if (b) { - if (Ext.isString(k)) { - k = b.getCoordFor(k) - } - k = k / (b.getRange()[1] + 1) * Math.PI * 2 - Math.PI * 1.5 + c.getRotation() - } else { - k = Ext.draw.Draw.rad(k) - } - c.sprites[0].setAttributes({ - baseRotation: k - }, true) - } - } - } - }, - redraw: function() { - var f = this, - g = f.getAxes(), - d, c = f.getSeries(), - b, a, e; - for (a = 0, e = g.length; a < e; a++) { - d = g[a]; - d.getSprites() - } - for (a = 0, e = c.length; a < e; a++) { - b = c[a]; - b.getSprites() - } - f.renderFrame(); - f.callParent(arguments) - }, - renderFrame: function() { - this.refloatAxes(); - this.callParent() - } -}); -Ext.define("Ext.chart.SpaceFillingChart", { - extend: "Ext.chart.AbstractChart", - xtype: "spacefilling", - config: {}, - performLayout: function() { - var j = this; - try { - j.animationSuspendCount++; - if (j.callParent() === false) { - return - } - var k = j.getSurface("chart").getRect(), - l = j.getInsetPadding(), - a = k[2] - l.left - l.right, - m = k[3] - l.top - l.bottom, - h = [l.left, l.top, a, m], - b = j.getSeries(), - d, c, g; - j.getSurface().setRect(h); - j.setMainRect(h); - for (c = 0, g = b.length; c < g; c++) { - d = b[c]; - d.getSurface().setRect(h); - if (d.setRect) { - d.setRect(h) - } - d.getOverlaySurface().setRect(k) - } - j.redraw() - } catch (f) { - throw f - } finally { - j.animationSuspendCount-- - } - }, - redraw: function() { - var e = this, - c = e.getSeries(), - b, a, d; - for (a = 0, d = c.length; a < d; a++) { - b = c[a]; - b.getSprites() - } - e.renderFrame(); - e.callParent(arguments) - } -}); -Ext.define("Ext.chart.axis.sprite.Axis3D", { - extend: "Ext.chart.axis.sprite.Axis", - alias: "sprite.axis3d", - type: "axis3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - }, - triggers: { - depth: "layout" - } - } - }, - config: { - fx: { - customDurations: { - depth: 0 - } - } - }, - layoutUpdater: function() { - var h = this, - f = h.getAxis().getChart(); - if (f.isInitializing) { - return - } - var e = h.attr, - d = h.getLayout(), - c = d.isDiscrete ? 0 : e.depth, - g = f.getInherited().rtl, - b = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMin, - i = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMax, - a = { - attr: e, - segmenter: h.getSegmenter(), - renderer: h.defaultRenderer - }; - if (e.position === "left" || e.position === "right") { - e.translationX = 0; - e.translationY = i * (e.length - c) / (i - b) + c; - e.scalingX = 1; - e.scalingY = (-e.length + c) / (i - b); - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } else { - if (e.position === "top" || e.position === "bottom") { - if (g) { - e.translationX = e.length + b * e.length / (i - b) + 1 - } else { - e.translationX = -b * e.length / (i - b) - } - e.translationY = 0; - e.scalingX = (g ? -1 : 1) * (e.length - c) / (i - b); - e.scalingY = 1; - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } - } - if (d) { - d.calculateLayout(a); - h.setLayoutContext(a) - } - }, - renderAxisLine: function(a, j, f, c) { - var i = this, - h = i.attr, - b = h.lineWidth * 0.5, - f = i.getLayout(), - d = f.isDiscrete ? 0 : h.depth, - k = h.position, - e, g; - if (h.axisLine && h.length) { - switch (k) { - case "left": - e = a.roundPixel(c[2]) - b; - j.moveTo(e, -h.endGap + d); - j.lineTo(e, h.length + h.startGap); - break; - case "right": - j.moveTo(b, -h.endGap); - j.lineTo(b, h.length + h.startGap); - break; - case "bottom": - j.moveTo(-h.startGap, b); - j.lineTo(h.length - d + h.endGap, b); - break; - case "top": - e = a.roundPixel(c[3]) - b; - j.moveTo(-h.startGap, e); - j.lineTo(h.length + h.endGap, e); - break; - case "angular": - j.moveTo(h.centerX + h.length, h.centerY); - j.arc(h.centerX, h.centerY, h.length, 0, Math.PI * 2, true); - break; - case "gauge": - g = i.getGaugeAngles(); - j.moveTo(h.centerX + Math.cos(g.start) * h.length, h.centerY + Math.sin(g.start) * h.length); - j.arc(h.centerX, h.centerY, h.length, g.start, g.end, true); - break - } - } - } -}); -Ext.define("Ext.chart.axis.Axis3D", { - extend: "Ext.chart.axis.Axis", - xtype: "axis3d", - requires: ["Ext.chart.axis.sprite.Axis3D"], - config: { - depth: 0 - }, - onSeriesChange: function(e) { - var g = this, - b = "depthchange", - f = "onSeriesDepthChange", - d, c; - - function a(h) { - var i = g.boundSeries; - for (d = 0; d < i.length; d++) { - c = i[d]; - c[h](b, f, g) - } - } - a("un"); - g.callParent(arguments); - a("on") - }, - onSeriesDepthChange: function(b, f) { - var d = this, - g = f, - e = d.boundSeries, - a, c; - if (f > d.getDepth()) { - g = f - } else { - for (a = 0; a < e.length; a++) { - c = e[a]; - if (c !== b && c.getDepth) { - f = c.getDepth(); - if (f > g) { - g = f - } - } - } - } - d.setDepth(g) - }, - updateDepth: function(d) { - var b = this, - c = b.getSprites(), - a = { - depth: d - }; - if (c && c.length) { - c[0].setAttributes(a) - } - if (b.gridSpriteEven && b.gridSpriteOdd) { - b.gridSpriteEven.getTemplate().setAttributes(a); - b.gridSpriteOdd.getTemplate().setAttributes(a) - } - }, - getGridAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "horizontal3d"; - case "top": - case "bottom": - return "vertical3d" - } - } -}); -Ext.define("Ext.chart.axis.Category", { - requires: ["Ext.chart.axis.layout.CombineDuplicate", "Ext.chart.axis.segmenter.Names"], - extend: "Ext.chart.axis.Axis", - alias: "axis.category", - type: "category", - config: { - layout: "combineDuplicate", - segmenter: "names" - } -}); -Ext.define("Ext.chart.axis.Category3D", { - requires: ["Ext.chart.axis.layout.CombineDuplicate", "Ext.chart.axis.segmenter.Names"], - extend: "Ext.chart.axis.Axis3D", - alias: "axis.category3d", - type: "category3d", - config: { - layout: "combineDuplicate", - segmenter: "names" - } -}); -Ext.define("Ext.chart.axis.Numeric", { - extend: "Ext.chart.axis.Axis", - type: "numeric", - alias: ["axis.numeric", "axis.radial"], - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Numeric"], - config: { - layout: "continuous", - segmenter: "numeric", - aggregator: "double" - } -}); -Ext.define("Ext.chart.axis.Numeric3D", { - extend: "Ext.chart.axis.Axis3D", - alias: ["axis.numeric3d"], - type: "numeric3d", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Numeric"], - config: { - layout: "continuous", - segmenter: "numeric", - aggregator: "double" - } -}); -Ext.define("Ext.chart.axis.Time", { - extend: "Ext.chart.axis.Numeric", - alias: "axis.time", - type: "time", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Time"], - config: { - calculateByLabelSize: true, - dateFormat: null, - fromDate: null, - toDate: null, - step: [Ext.Date.DAY, 1], - layout: "continuous", - segmenter: "time", - aggregator: "time" - }, - updateDateFormat: function(a) { - this.setRenderer(function(c, b) { - return Ext.Date.format(new Date(b), a) - }) - }, - updateFromDate: function(a) { - this.setMinimum(+a) - }, - updateToDate: function(a) { - this.setMaximum(+a) - }, - getCoordFor: function(a) { - if (Ext.isString(a)) { - a = new Date(a) - } - return +a - } -}); -Ext.define("Ext.chart.axis.Time3D", { - extend: "Ext.chart.axis.Numeric3D", - alias: "axis.time3d", - type: "time3d", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Time"], - config: { - calculateByLabelSize: true, - dateFormat: null, - fromDate: null, - toDate: null, - step: [Ext.Date.DAY, 1], - layout: "continuous", - segmenter: "time", - aggregator: "time" - }, - updateDateFormat: function(a) { - this.setRenderer(function(c, b) { - return Ext.Date.format(new Date(b), a) - }) - }, - updateFromDate: function(a) { - this.setMinimum(+a) - }, - updateToDate: function(a) { - this.setMaximum(+a) - }, - getCoordFor: function(a) { - if (Ext.isString(a)) { - a = new Date(a) - } - return +a - } -}); -Ext.define("Ext.chart.grid.HorizontalGrid3D", { - extend: "Ext.chart.grid.HorizontalGrid", - alias: "grid.horizontal3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - } - } - }, - render: function(a, k, d) { - var f = this.attr, - i = a.roundPixel(f.x), - h = a.roundPixel(f.y), - l = a.matrix.getDX(), - c = k.lineWidth * 0.5, - j = f.height, - e = f.depth, - b, g; - if (h <= d[1]) { - return - } - b = d[0] + e - l; - g = h + c - e; - k.beginPath(); - k.rect(b, g, d[2], j); - k.fill(); - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + d[2], g); - k.stroke(); - b = d[0] + i - l; - g = h + c; - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + e, g - e); - k.lineTo(b + e, g - e + j); - k.lineTo(b, g + j); - k.closePath(); - k.fill(); - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + e, g - e); - k.stroke() - } -}); -Ext.define("Ext.chart.grid.VerticalGrid3D", { - extend: "Ext.chart.grid.VerticalGrid", - alias: "grid.vertical3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - } - } - }, - render_: function(c, d, f) { - var b = this.attr, - a = c.roundPixel(b.x), - e = d.lineWidth * 0.5; - d.beginPath(); - d.rect(a - e, f[1] - c.matrix.getDY(), b.width, f[3]); - d.fill(); - d.beginPath(); - d.moveTo(a - e, f[1] - c.matrix.getDY()); - d.lineTo(a - e, f[1] + f[3] - c.matrix.getDY()); - d.stroke() - }, - render: function(b, j, e) { - var g = this.attr, - i = b.roundPixel(g.x), - k = b.matrix.getDY(), - d = j.lineWidth * 0.5, - a = g.width, - f = g.depth, - c, h; - if (i >= e[2]) { - return - } - c = i - d + f; - h = e[1] - f - k; - j.beginPath(); - j.rect(c, h, a, e[3]); - j.fill(); - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c, h + e[3]); - j.stroke(); - c = i - d; - h = e[3]; - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c + f, h - f); - j.lineTo(c + f + a, h - f); - j.lineTo(c + a, h); - j.closePath(); - j.fill(); - c = i - d; - h = e[3]; - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c + f, h - f); - j.stroke() - } -}); -Ext.define("Ext.chart.interactions.CrossZoom", { - extend: "Ext.chart.interactions.Abstract", - type: "crosszoom", - alias: "interaction.crosszoom", - isCrossZoom: true, - config: { - axes: true, - gestures: { - dragstart: "onGestureStart", - drag: "onGesture", - dragend: "onGestureEnd", - dblclick: "onDoubleTap" - }, - undoButton: {} - }, - stopAnimationBeforeSync: false, - zoomAnimationInProgress: false, - constructor: function() { - this.callParent(arguments); - this.zoomHistory = [] - }, - applyAxes: function(b) { - var a = {}; - if (b === true) { - return { - top: {}, - right: {}, - bottom: {}, - left: {} - } - } else { - if (Ext.isArray(b)) { - a = {}; - Ext.each(b, function(c) { - a[c] = {} - }) - } else { - if (Ext.isObject(b)) { - Ext.iterate(b, function(c, d) { - if (d === true) { - a[c] = {} - } else { - if (d !== false) { - a[c] = d - } - } - }) - } - } - } - return a - }, - applyUndoButton: function(b, a) { - var c = this; - if (a) { - a.destroy() - } - if (b) { - return Ext.create("Ext.Button", Ext.apply({ - cls: [], - text: "Undo Zoom", - disabled: true, - handler: function() { - c.undoZoom() - } - }, b)) - } - }, - getSurface: function() { - return this.getChart() && this.getChart().getSurface("main") - }, - setSeriesOpacity: function(b) { - var a = this.getChart() && this.getChart().getSurface("series"); - if (a) { - a.element.setStyle("opacity", b) - } - }, - onGestureStart: function(h) { - var j = this, - i = j.getChart(), - d = j.getSurface(), - l = i.getInnerRect(), - c = i.getInnerPadding(), - g = c.left, - b = g + l[2], - f = c.top, - a = f + l[3], - n = i.getEventXY(h), - m = n[0], - k = n[1]; - if (j.zoomAnimationInProgress) { - return - } - if (m > g && m < b && k > f && k < a) { - j.gestureEvent = "drag"; - j.lockEvents(j.gestureEvent); - j.startX = m; - j.startY = k; - j.selectionRect = d.add({ - type: "rect", - globalAlpha: 0.5, - fillStyle: "rgba(80,80,140,0.5)", - strokeStyle: "rgba(80,80,140,1)", - lineWidth: 2, - x: m, - y: k, - width: 0, - height: 0, - zIndex: 10000 - }); - j.setSeriesOpacity(0.8); - return false - } - }, - onGesture: function(h) { - var j = this; - if (j.zoomAnimationInProgress) { - return - } - if (j.getLocks()[j.gestureEvent] === j) { - var i = j.getChart(), - d = j.getSurface(), - l = i.getInnerRect(), - c = i.getInnerPadding(), - g = c.left, - b = g + l[2], - f = c.top, - a = f + l[3], - n = i.getEventXY(h), - m = n[0], - k = n[1]; - if (m < g) { - m = g - } else { - if (m > b) { - m = b - } - } - if (k < f) { - k = f - } else { - if (k > a) { - k = a - } - } - j.selectionRect.setAttributes({ - width: m - j.startX, - height: k - j.startY - }); - if (Math.abs(j.startX - m) < 11 || Math.abs(j.startY - k) < 11) { - j.selectionRect.setAttributes({ - globalAlpha: 0.5 - }) - } else { - j.selectionRect.setAttributes({ - globalAlpha: 1 - }) - } - d.renderFrame(); - return false - } - }, - onGestureEnd: function(i) { - var l = this; - if (l.zoomAnimationInProgress) { - return - } - if (l.getLocks()[l.gestureEvent] === l) { - var k = l.getChart(), - d = l.getSurface(), - n = k.getInnerRect(), - c = k.getInnerPadding(), - g = c.left, - b = g + n[2], - f = c.top, - a = f + n[3], - h = n[2], - j = n[3], - p = k.getEventXY(i), - o = p[0], - m = p[1]; - if (o < g) { - o = g - } else { - if (o > b) { - o = b - } - } - if (m < f) { - m = f - } else { - if (m > a) { - m = a - } - } - if (Math.abs(l.startX - o) < 11 || Math.abs(l.startY - m) < 11) { - d.remove(l.selectionRect) - } else { - l.zoomBy([Math.min(l.startX, o) / h, 1 - Math.max(l.startY, m) / j, Math.max(l.startX, o) / h, 1 - Math.min(l.startY, m) / j]); - l.selectionRect.setAttributes({ - x: Math.min(l.startX, o), - y: Math.min(l.startY, m), - width: Math.abs(l.startX - o), - height: Math.abs(l.startY - m) - }); - l.selectionRect.setAnimation(k.getAnimation() || { - duration: 0 - }); - l.selectionRect.setAttributes({ - globalAlpha: 0, - x: 0, - y: 0, - width: h, - height: j - }); - l.zoomAnimationInProgress = true; - k.suspendThicknessChanged(); - l.selectionRect.fx.on("animationend", function() { - k.resumeThicknessChanged(); - d.remove(l.selectionRect); - l.selectionRect = null; - l.zoomAnimationInProgress = false - }) - } - d.renderFrame(); - l.sync(); - l.unlockEvents(l.gestureEvent); - l.setSeriesOpacity(1); - if (!l.zoomAnimationInProgress) { - d.remove(l.selectionRect); - l.selectionRect = null - } - } - }, - zoomBy: function(o) { - var n = this, - a = n.getAxes(), - k = n.getChart(), - j = k.getAxes(), - l = k.getInherited().rtl, - f, d = {}, - c, b; - if (l) { - o = o.slice(); - c = 1 - o[0]; - b = 1 - o[2]; - o[0] = Math.min(c, b); - o[2] = Math.max(c, b) - } - for (var h = 0; h < j.length; h++) { - var g = j[h]; - f = a[g.getPosition()]; - if (f && f.allowZoom !== false) { - var e = g.isSide(), - m = g.getVisibleRange(); - d[g.getId()] = m.slice(0); - if (!e) { - g.setVisibleRange([(m[1] - m[0]) * o[0] + m[0], (m[1] - m[0]) * o[2] + m[0]]) - } else { - g.setVisibleRange([(m[1] - m[0]) * o[1] + m[0], (m[1] - m[0]) * o[3] + m[0]]) - } - } - } - n.zoomHistory.push(d); - n.getUndoButton().setDisabled(false) - }, - undoZoom: function() { - var c = this.zoomHistory.pop(), - d = this.getChart().getAxes(); - if (c) { - for (var a = 0; a < d.length; a++) { - var b = d[a]; - if (c[b.getId()]) { - b.setVisibleRange(c[b.getId()]) - } - } - } - this.getUndoButton().setDisabled(this.zoomHistory.length === 0); - this.sync() - }, - onDoubleTap: function(a) { - this.undoZoom() - }, - destroy: function() { - this.setUndoButton(null); - this.callParent(arguments) - } -}); -Ext.define("Ext.chart.interactions.Crosshair", { - extend: "Ext.chart.interactions.Abstract", - requires: ["Ext.chart.grid.HorizontalGrid", "Ext.chart.grid.VerticalGrid", "Ext.chart.CartesianChart", "Ext.chart.axis.layout.Discrete"], - type: "crosshair", - alias: "interaction.crosshair", - config: { - axes: { - top: { - label: {}, - rect: {} - }, - right: { - label: {}, - rect: {} - }, - bottom: { - label: {}, - rect: {} - }, - left: { - label: {}, - rect: {} - } - }, - lines: { - horizontal: { - strokeStyle: "black", - lineDash: [5, 5] - }, - vertical: { - strokeStyle: "black", - lineDash: [5, 5] - } - }, - gesture: "drag" - }, - applyAxes: function(b, a) { - return Ext.merge(a || {}, b) - }, - applyLines: function(a, b) { - return Ext.merge(b || {}, a) - }, - updateChart: function(a) { - if (a && !a.isCartesian) { - Ext.raise("Crosshair interaction can only be used on cartesian charts.") - } - this.callParent(arguments) - }, - getGestures: function() { - var a = this, - b = {}; - b[a.getGesture()] = "onGesture"; - b[a.getGesture() + "start"] = "onGestureStart"; - b[a.getGesture() + "end"] = "onGestureEnd"; - return b - }, - onGestureStart: function(N) { - var m = this, - O = m.getChart(), - B = O.getTheme().getAxis(), - A, F = O.getSurface("overlay"), - s = O.getInnerRect(), - n = s[2], - M = s[3], - r = O.getEventXY(N), - D = r[0], - C = r[1], - E = O.getAxes(), - u = m.getAxes(), - h = m.getLines(), - q, v, b, d, k, z, G, L, J, o, I, w, l, f, p, j, t, a, g, H, c, K; - if (D > 0 && D < n && C > 0 && C < M) { - m.lockEvents(m.getGesture()); - H = Ext.apply({ - xclass: "Ext.chart.grid.HorizontalGrid", - x: 0, - y: C, - width: n - }, h.horizontal); - c = Ext.apply({ - xclass: "Ext.chart.grid.VerticalGrid", - x: D, - y: 0, - height: M - }, h.vertical); - m.axesLabels = m.axesLabels || {}; - for (K = 0; K < E.length; K++) { - q = E[K]; - v = q.getSurface(); - b = v.getRect(); - w = q.getSprites()[0]; - d = b[2]; - k = b[3]; - z = q.getPosition(); - G = q.getAlignment(); - t = q.getTitle(); - a = t && t.attr.text !== "" && t.getBBox(); - l = w.attr; - f = w.thickness; - p = l.axisLine ? l.lineWidth : 0; - j = p / 2; - I = Math.max(l.majorTickSize, l.minorTickSize) + p; - L = m.axesLabels[z] = v.add({ - type: "composite" - }); - L.labelRect = L.add(Ext.apply({ - type: "rect", - fillStyle: "white", - x: z === "right" ? p : 0, - y: z === "bottom" ? p : 0, - width: d - p - (G === "vertical" && a ? a.width : 0), - height: k - p - (G === "horizontal" && a ? a.height : 0), - translationX: z === "left" && a ? a.width : 0, - translationY: z === "top" && a ? a.height : 0 - }, u.rect || u[z].rect)); - if (G === "vertical" && !c.strokeStyle) { - c.strokeStyle = l.strokeStyle - } - if (G === "horizontal" && !H.strokeStyle) { - H.strokeStyle = l.strokeStyle - } - A = Ext.merge({}, B.defaults, B[z]); - J = Ext.apply({}, q.config.label, A.label); - o = u.label || u[z].label; - L.labelText = L.add(Ext.apply(J, o, { - type: "text", - x: (function() { - switch (z) { - case "left": - g = a ? a.x + a.width : 0; - return g + (d - g - I) / 2 - j; - case "right": - g = a ? d - a.x : 0; - return I + (d - I - g) / 2 + j; - default: - return 0 - } - })(), - y: (function() { - switch (z) { - case "top": - g = a ? a.y + a.height : 0; - return g + (k - g - I) / 2 - j; - case "bottom": - g = a ? k - a.y : 0; - return I + (k - I - g) / 2 + j; - default: - return 0 - } - })() - })) - } - m.horizontalLine = F.add(H); - m.verticalLine = F.add(c); - return false - } - }, - onGesture: function(G) { - var K = this; - if (K.getLocks()[K.getGesture()] !== K) { - return - } - var u = K.getChart(), - z = u.getSurface("overlay"), - a = Ext.Array.slice(u.getInnerRect()), - r = u.getInnerPadding(), - t = r.left, - q = r.top, - E = a[2], - f = a[3], - d = u.getEventXY(G), - k = d[0], - j = d[1], - D = u.getAxes(), - c, h, m, p, J, w, I, H, s, b, C, g, v, n, l, A, F, o, B; - if (k < 0) { - k = 0 - } else { - if (k > E) { - k = E - } - } - if (j < 0) { - j = 0 - } else { - if (j > f) { - j = f - } - } - k += t; - j += q; - for (B = 0; B < D.length; B++) { - c = D[B]; - h = c.getPosition(); - m = c.getAlignment(); - p = c.getSurface(); - J = c.getSprites()[0]; - w = J.attr.matrix; - C = J.attr.textPadding * 2; - s = K.axesLabels[h]; - I = J.getLayoutContext(); - H = c.getSegmenter(); - if (s) { - if (m === "vertical") { - v = w.getYY(); - l = w.getDY(); - F = (j - l - q) / v; - if (c.getLayout() instanceof Ext.chart.axis.layout.Discrete) { - j = Math.round(F) * v + l + q; - F = H.from(Math.round(F)); - F = J.attr.data[F] - } else { - F = H.from(F) - } - o = H.renderer(F, I); - s.setAttributes({ - translationY: j - q - }); - s.labelText.setAttributes({ - text: o - }); - b = s.labelText.getBBox(); - s.labelRect.setAttributes({ - height: b.height + C, - y: -(b.height + C) / 2 - }); - p.renderFrame() - } else { - g = w.getXX(); - n = w.getDX(); - A = (k - n - t) / g; - if (c.getLayout() instanceof Ext.chart.axis.layout.Discrete) { - k = Math.round(A) * g + n + t; - A = H.from(Math.round(A)); - A = J.attr.data[A] - } else { - A = H.from(A) - } - o = H.renderer(A, I); - s.setAttributes({ - translationX: k - t - }); - s.labelText.setAttributes({ - text: o - }); - b = s.labelText.getBBox(); - s.labelRect.setAttributes({ - width: b.width + C, - x: -(b.width + C) / 2 - }); - p.renderFrame() - } - } - } - K.horizontalLine.setAttributes({ - y: j, - strokeStyle: J.attr.strokeStyle - }); - K.verticalLine.setAttributes({ - x: k, - strokeStyle: J.attr.strokeStyle - }); - z.renderFrame(); - return false - }, - onGestureEnd: function(h) { - var l = this, - k = l.getChart(), - a = k.getSurface("overlay"), - j = k.getAxes(), - c, g, d, b, f; - a.remove(l.verticalLine); - a.remove(l.horizontalLine); - for (f = 0; f < j.length; f++) { - c = j[f]; - g = c.getPosition(); - d = c.getSurface(); - b = l.axesLabels[g]; - if (b) { - delete l.axesLabels[g]; - d.remove(b) - } - d.renderFrame() - } - a.renderFrame(); - l.unlockEvents(l.getGesture()) - } -}); -Ext.define("Ext.chart.interactions.ItemHighlight", { - extend: "Ext.chart.interactions.Abstract", - type: "itemhighlight", - alias: "interaction.itemhighlight", - isItemHighlight: true, - config: { - gestures: { - tap: "onTapGesture", - mousemove: "onMouseMoveGesture", - mousedown: "onMouseDownGesture", - mouseup: "onMouseUpGesture", - mouseleave: "onMouseUpGesture" - }, - sticky: false - }, - stickyHighlightItem: null, - onMouseMoveGesture: function(g) { - var d = this, - h = d.tipItem, - a = g.pointerType === "mouse", - c, f, b; - if (d.getSticky()) { - return true - } - if (d.isDragging) { - if (h && a) { - h.series.hideTooltip(h); - d.tipItem = null - } - } else { - if (!d.stickyHighlightItem) { - c = d.getItemForEvent(g); - b = d.getChart(); - if (c !== b.getHighlightItem()) { - d.highlight(c); - d.sync() - } - if (a) { - if (h && (!c || h.field !== c.field || h.record !== c.record)) { - h.series.hideTooltip(h); - d.tipItem = h = null - } - if (c && (f = c.series.getTooltip())) { - if (f.trackMouse || !h) { - c.series.showTooltip(c, g.getXY()) - } - d.tipItem = c - } - } - return false - } - } - }, - highlight: function(a) { - this.getChart().setHighlightItem(a) - }, - showTooltip: function(b, a) { - a.series.showTooltip(a, b.getXY()); - this.tipItem = a - }, - onMouseDownGesture: function() { - this.isDragging = true - }, - onMouseUpGesture: function() { - this.isDragging = false - }, - onTapGesture: function(c) { - var b = this; - if (c.pointerType === "mouse" && !b.getSticky()) { - return - } - var a = b.getItemForEvent(c); - if (b.stickyHighlightItem && a && (b.stickyHighlightItem.index === a.index)) { - a = null - } - b.stickyHighlightItem = a; - b.highlight(a) - } -}); -Ext.define("Ext.chart.interactions.ItemEdit", { - extend: "Ext.chart.interactions.ItemHighlight", - requires: ["Ext.tip.ToolTip"], - type: "itemedit", - alias: "interaction.itemedit", - isItemEdit: true, - config: { - style: null, - renderer: null, - tooltip: true, - gestures: { - dragstart: "onDragStart", - drag: "onDrag", - dragend: "onDragEnd" - }, - cursors: { - ewResize: "ew-resize", - nsResize: "ns-resize", - move: "move" - } - }, - item: null, - applyTooltip: function(b) { - if (b) { - var a = Ext.apply({}, b, { - renderer: this.defaultTooltipRenderer, - constrainPosition: true, - shrinkWrapDock: true, - autoHide: true, - offsetX: 10, - offsetY: 10 - }); - b = new Ext.tip.ToolTip(a) - } - return b - }, - defaultTooltipRenderer: function(b, a, f, d) { - var c = []; - if (f.xField) { - c.push(f.xField + ": " + f.xValue) - } - if (f.yField) { - c.push(f.yField + ": " + f.yValue) - } - b.setHtml(c.join("
")) - }, - onDragStart: function(d) { - var c = this, - a = c.getChart(), - b = a.getHighlightItem(); - if (b) { - a.fireEvent("beginitemedit", a, c, c.item = b); - return false - } - }, - onDrag: function(f) { - var d = this, - b = d.getChart(), - c = b.getHighlightItem(), - a = c && c.sprite.type; - if (c) { - switch (a) { - case "barSeries": - return d.onDragBar(f); - break; - case "scatterSeries": - return d.onDragScatter(f); - break - } - } - }, - highlight: function(f) { - var e = this, - d = e.getChart(), - a = d.getFlipXY(), - g = e.getCursors(), - c = f && f.sprite.type, - b = d.el.dom.style; - e.callParent([f]); - if (f) { - switch (c) { - case "barSeries": - if (a) { - b.cursor = g.ewResize - } else { - b.cursor = g.nsResize - } - break; - case "scatterSeries": - b.cursor = g.move; - break - } - } else { - d.el.dom.style.cursor = "default" - } - }, - onDragBar: function(i) { - var m = this, - k = m.getChart(), - l = k.getInherited().rtl, - f = k.isCartesian && k.getFlipXY(), - q = k.getHighlightItem(), - g = q.sprite.getMarker("items"), - p = g.getMarkerFor(q.sprite.getId(), q.index), - b = q.sprite.getSurface(), - c = b.getRect(), - r = b.getEventXY(i), - o = q.sprite.attr.matrix, - j = m.getRenderer(), - a, n, d, h; - if (f) { - h = l ? c[2] - r[0] : r[0] - } else { - h = c[3] - r[1] - } - a = { - x: p.x, - y: h, - width: p.width, - height: p.height + (p.y - h), - radius: p.radius, - fillStyle: "none", - lineDash: [4, 4], - zIndex: 100 - }; - Ext.apply(a, m.getStyle()); - if (Ext.isArray(q.series.getYField())) { - h = h - p.y - p.height - } - m.target = { - index: q.index, - yField: q.field, - yValue: (h - o.getDY()) / o.getYY() - }; - d = [k, { - target: m.target, - style: a, - item: q - }]; - n = Ext.callback(j, null, d, 0, k); - if (n) { - Ext.apply(a, n) - } - q.sprite.putMarker("items", a, "itemedit"); - m.showTooltip(i, m.target, q); - b.renderFrame() - }, - onDragScatter: function(n) { - var t = this, - g = t.getChart(), - d = g.getInherited().rtl, - l = g.isCartesian && g.getFlipXY(), - o = g.getHighlightItem(), - b = o.sprite.getMarker("items"), - p = b.getMarkerFor(o.sprite.getId(), o.index), - j = o.sprite.getSurface(), - h = j.getRect(), - a = j.getEventXY(n), - k = o.sprite.attr.matrix, - c = o.series.getXAxis(), - f = c && c.getLayout().isContinuous, - i = t.getRenderer(), - m, u, q, s, r; - if (l) { - r = d ? h[2] - a[0] : a[0] - } else { - r = h[3] - a[1] - } - if (f) { - if (l) { - s = h[3] - a[1] - } else { - s = a[0] - } - } else { - s = p.translationX - } - m = { - translationX: s, - translationY: r, - scalingX: p.scalingX, - scalingY: p.scalingY, - r: p.r, - fillStyle: "none", - lineDash: [4, 4], - zIndex: 100 - }; - Ext.apply(m, t.getStyle()); - t.target = { - index: o.index, - yField: o.field, - yValue: (r - k.getDY()) / k.getYY() - }; - if (f) { - Ext.apply(t.target, { - xField: o.series.getXField(), - xValue: (s - k.getDX()) / k.getXX() - }) - } - q = [g, { - target: t.target, - style: m, - item: o - }]; - u = Ext.callback(i, null, q, 0, g); - if (u) { - Ext.apply(m, u) - } - o.sprite.putMarker("items", m, "itemedit"); - t.showTooltip(n, t.target, o); - j.renderFrame() - }, - showTooltip: function(g, f, c) { - var d = this.getTooltip(), - a, b; - if (d && Ext.toolkit !== "modern") { - a = d.config; - b = this.getChart(); - Ext.callback(a.renderer, null, [d, c, f, g], 0, b); - d.show([g.x + a.offsetX, g.y + a.offsetY]) - } - }, - hideTooltip: function() { - var a = this.getTooltip(); - if (a && Ext.toolkit !== "modern") { - a.hide() - } - }, - onDragEnd: function(g) { - var d = this, - f = d.target, - c = d.getChart(), - b = c.getStore(), - a; - if (f) { - a = b.getAt(f.index); - if (f.yField) { - a.set(f.yField, f.yValue, { - convert: false - }) - } - if (f.xField) { - a.set(f.xField, f.xValue, { - convert: false - }) - } - if (f.yField || f.xField) { - d.getChart().onDataChanged() - } - d.target = null - } - d.hideTooltip(); - if (d.item) { - c.fireEvent("enditemedit", c, d, d.item, f) - } - d.highlight(d.item = null) - }, - destroy: function() { - var a = this.getConfig("tooltip", true); - Ext.destroy(a); - this.callParent() - } -}); -Ext.define("Ext.chart.interactions.PanZoom", { - extend: "Ext.chart.interactions.Abstract", - type: "panzoom", - alias: "interaction.panzoom", - requires: ["Ext.draw.Animator"], - config: { - axes: { - top: {}, - right: {}, - bottom: {}, - left: {} - }, - minZoom: null, - maxZoom: null, - showOverflowArrows: true, - panGesture: "drag", - zoomGesture: "pinch", - zoomOnPanGesture: false, - modeToggleButton: { - xtype: "segmentedbutton", - width: 200, - defaults: { - ui: "default-toolbar" - }, - cls: Ext.baseCSSPrefix + "panzoom-toggle", - items: [{ - text: "Pan" - }, { - text: "Zoom" - }] - }, - hideLabelInGesture: false - }, - stopAnimationBeforeSync: true, - applyAxes: function(b, a) { - return Ext.merge(a || {}, b) - }, - applyZoomOnPanGesture: function(a) { - this.getChart(); - if (this.isMultiTouch()) { - return false - } - return a - }, - updateZoomOnPanGesture: function(b) { - var a = this.getModeToggleButton(); - if (!this.isMultiTouch()) { - a.show(); - a.setValue(b ? 1 : 0) - } else { - a.hide() - } - }, - toggleMode: function() { - var a = this; - if (!a.isMultiTouch()) { - a.setZoomOnPanGesture(!a.getZoomOnPanGesture()) - } - }, - applyModeToggleButton: function(c, b) { - var d = this, - a = Ext.factory(c, "Ext.button.Segmented", b); - if (!a && b) { - b.destroy() - } - if (a && !b) { - a.addListener("toggle", function(e) { - d.setZoomOnPanGesture(e.getValue() === 1) - }) - } - return a - }, - getGestures: function() { - var c = this, - e = {}, - d = c.getPanGesture(), - b = c.getZoomGesture(), - a = Ext.supports.Touch; - e[b] = "onZoomGestureMove"; - e[b + "start"] = "onZoomGestureStart"; - e[b + "end"] = "onZoomGestureEnd"; - e[d] = "onPanGestureMove"; - e[d + "start"] = "onPanGestureStart"; - e[d + "end"] = "onPanGestureEnd"; - e.doubletap = "onDoubleTap"; - return e - }, - onDoubleTap: function(h) { - var f = this, - c = f.getChart(), - g = c.getAxes(), - b, a, d; - for (a = 0, d = g.length; a < d; a++) { - b = g[a]; - b.setVisibleRange([0, 1]) - } - c.redraw() - }, - onPanGestureStart: function(d) { - if (!d || !d.touches || d.touches.length < 2) { - var b = this, - a = b.getChart().getInnerRect(), - c = b.getChart().element.getXY(); - b.startX = d.getX() - c[0] - a[0]; - b.startY = d.getY() - c[1] - a[1]; - b.oldVisibleRanges = null; - b.hideLabels(); - b.getChart().suspendThicknessChanged(); - b.lockEvents(b.getPanGesture()); - return false - } - }, - onPanGestureMove: function(d) { - var b = this; - if (b.getLocks()[b.getPanGesture()] === b) { - var a = b.getChart().getInnerRect(), - c = b.getChart().element.getXY(); - if (b.getZoomOnPanGesture()) { - b.transformAxesBy(b.getZoomableAxes(d), 0, 0, (d.getX() - c[0] - a[0]) / b.startX, b.startY / (d.getY() - c[1] - a[1])) - } else { - b.transformAxesBy(b.getPannableAxes(d), d.getX() - c[0] - a[0] - b.startX, d.getY() - c[1] - a[1] - b.startY, 1, 1) - } - b.sync(); - return false - } - }, - onPanGestureEnd: function(b) { - var a = this, - c = a.getPanGesture(); - if (a.getLocks()[c] === a) { - a.getChart().resumeThicknessChanged(); - a.showLabels(); - a.sync(); - a.unlockEvents(c); - return false - } - }, - onZoomGestureStart: function(b) { - if (b.touches && b.touches.length === 2) { - var c = this, - i = c.getChart().element.getXY(), - f = c.getChart().getInnerRect(), - h = i[0] + f[0], - d = i[1] + f[1], - j = [b.touches[0].point.x - h, b.touches[0].point.y - d, b.touches[1].point.x - h, b.touches[1].point.y - d], - g = Math.max(44, Math.abs(j[2] - j[0])), - a = Math.max(44, Math.abs(j[3] - j[1])); - c.getChart().suspendThicknessChanged(); - c.lastZoomDistances = [g, a]; - c.lastPoints = j; - c.oldVisibleRanges = null; - c.hideLabels(); - c.lockEvents(c.getZoomGesture()); - return false - } - }, - onZoomGestureMove: function(d) { - var f = this; - if (f.getLocks()[f.getZoomGesture()] === f) { - var i = f.getChart().getInnerRect(), - n = f.getChart().element.getXY(), - k = n[0] + i[0], - h = n[1] + i[1], - o = Math.abs, - c = f.lastPoints, - m = [d.touches[0].point.x - k, d.touches[0].point.y - h, d.touches[1].point.x - k, d.touches[1].point.y - h], - g = Math.max(44, o(m[2] - m[0])), - b = Math.max(44, o(m[3] - m[1])), - a = this.lastZoomDistances || [g, b], - l = g / a[0], - j = b / a[1]; - f.transformAxesBy(f.getZoomableAxes(d), i[2] * (l - 1) / 2 + m[2] - c[2] * l, i[3] * (j - 1) / 2 + m[3] - c[3] * j, l, j); - f.sync(); - return false - } - }, - onZoomGestureEnd: function(c) { - var b = this, - a = b.getZoomGesture(); - if (b.getLocks()[a] === b) { - b.getChart().resumeThicknessChanged(); - b.showLabels(); - b.sync(); - b.unlockEvents(a); - return false - } - }, - hideLabels: function() { - if (this.getHideLabelInGesture()) { - this.eachInteractiveAxes(function(a) { - a.hideLabels() - }) - } - }, - showLabels: function() { - if (this.getHideLabelInGesture()) { - this.eachInteractiveAxes(function(a) { - a.showLabels() - }) - } - }, - isEventOnAxis: function(c, a) { - var b = a.getSurface().getRect(); - return b[0] <= c.getX() && c.getX() <= b[0] + b[2] && b[1] <= c.getY() && c.getY() <= b[1] + b[3] - }, - getPannableAxes: function(d) { - var h = this, - a = h.getAxes(), - f = h.getChart().getAxes(), - c, g = f.length, - k = [], - j = false, - b; - if (d) { - for (c = 0; c < g; c++) { - if (this.isEventOnAxis(d, f[c])) { - j = true; - break - } - } - } - for (c = 0; c < g; c++) { - b = a[f[c].getPosition()]; - if (b && b.allowPan !== false && (!j || this.isEventOnAxis(d, f[c]))) { - k.push(f[c]) - } - } - return k - }, - getZoomableAxes: function(f) { - var j = this, - a = j.getAxes(), - g = j.getChart().getAxes(), - l = [], - d, h = g.length, - c, k = false, - b; - if (f) { - for (d = 0; d < h; d++) { - if (this.isEventOnAxis(f, g[d])) { - k = true; - break - } - } - } - for (d = 0; d < h; d++) { - c = g[d]; - b = a[c.getPosition()]; - if (b && b.allowZoom !== false && (!k || this.isEventOnAxis(f, c))) { - l.push(c) - } - } - return l - }, - eachInteractiveAxes: function(c) { - var d = this, - b = d.getAxes(), - e = d.getChart().getAxes(); - for (var a = 0; a < e.length; a++) { - if (b[e[a].getPosition()]) { - if (false === c.call(this, e[a])) { - return - } - } - } - }, - transformAxesBy: function(d, j, g, h, e) { - var f = this.getChart().getInnerRect(), - a = this.getAxes(), - k, b = this.oldVisibleRanges, - l = false; - if (!b) { - this.oldVisibleRanges = b = {}; - this.eachInteractiveAxes(function(i) { - b[i.getId()] = i.getVisibleRange() - }) - } - if (!f) { - return - } - for (var c = 0; c < d.length; c++) { - k = a[d[c].getPosition()]; - l = this.transformAxisBy(d[c], b[d[c].getId()], j, g, h, e, this.minZoom || k.minZoom, this.maxZoom || k.maxZoom) || l - } - return l - }, - transformAxisBy: function(c, o, r, q, k, i, h, m) { - var s = this, - b = o[1] - o[0], - l = c.getVisibleRange(), - g = h || s.getMinZoom() || c.config.minZoom, - j = m || s.getMaxZoom() || c.config.maxZoom, - a = s.getChart().getInnerRect(), - f, p; - if (!a) { - return - } - var d = c.isSide(), - e = d ? a[3] : a[2], - n = d ? -q : r; - b /= d ? i : k; - if (b < 0) { - b = -b - } - if (b * g > 1) { - b = 1 - } - if (b * j < 1) { - b = 1 / j - } - f = o[0]; - p = o[1]; - l = l[1] - l[0]; - if (b === l && l === 1) { - return - } - c.setVisibleRange([(o[0] + o[1] - b) * 0.5 - n / e * b, (o[0] + o[1] + b) * 0.5 - n / e * b]); - return (Math.abs(f - c.getVisibleRange()[0]) > 1e-10 || Math.abs(p - c.getVisibleRange()[1]) > 1e-10) - }, - destroy: function() { - this.setModeToggleButton(null); - this.callParent() - } -}); -Ext.define("Ext.chart.interactions.Rotate", { - extend: "Ext.chart.interactions.Abstract", - type: "rotate", - alias: "interaction.rotate", - config: { - gesture: "rotate", - gestures: { - rotate: "onRotate", - rotateend: "onRotate", - dragstart: "onGestureStart", - drag: "onGesture", - dragend: "onGestureEnd" - }, - rotation: 0 - }, - oldRotations: null, - getAngle: function(f) { - var c = this, - b = c.getChart(), - d = b.getEventXY(f), - a = b.getCenter(); - return Math.atan2(d[1] - a[1], d[0] - a[0]) - }, - getRadius: function(a) { - return this.getChart().getRadius() - }, - getEventRadius: function(h) { - var f = this, - d = f.getChart(), - g = d.getEventXY(h), - a = d.getCenter(), - c = g[0] - a[0], - b = g[1] - a[1]; - return Math.sqrt(c * c + b * b) - }, - onGestureStart: function(d) { - var c = this, - b = c.getRadius(d), - a = c.getEventRadius(d); - if (b >= a) { - c.lockEvents("drag"); - c.angle = c.getAngle(d); - c.oldRotations = {}; - return false - } - }, - onGesture: function(b) { - var a = this, - c = a.getAngle(b) - a.angle; - if (a.getLocks().drag === a) { - a.doRotateTo(c, true); - return false - } - }, - doRotateTo: function(d, a, b) { - var n = this, - l = n.getChart(), - k = l.getAxes(), - f = l.getSeries(), - m = n.oldRotations, - c, j, g, e, h; - if (!b) { - l.suspendAnimation() - } - for (e = 0, h = k.length; e < h; e++) { - c = k[e]; - g = m[c.getId()] || (m[c.getId()] = c.getRotation()); - c.setRotation(d + (a ? g : 0)) - } - for (e = 0, h = f.length; e < h; e++) { - j = f[e]; - g = m[j.getId()] || (m[j.getId()] = j.getRotation()); - j.setRotation(d + (a ? g : 0)) - } - n.setRotation(d + (a ? g : 0)); - n.fireEvent("rotate", n, n.getRotation()); - n.sync(); - if (!b) { - l.resumeAnimation() - } - }, - rotateTo: function(c, b, a) { - this.doRotateTo(c, b, a); - this.oldRotations = {} - }, - onGestureEnd: function(b) { - var a = this; - if (a.getLocks().drag === a) { - a.onGesture(b); - a.unlockEvents("drag"); - a.fireEvent("rotationEnd", a, a.getRotation()); - return false - } - }, - onRotate: function(a) {} -}); -Ext.define("Ext.chart.interactions.RotatePie3D", { - extend: "Ext.chart.interactions.Rotate", - type: "rotatePie3d", - alias: "interaction.rotatePie3d", - getAngle: function(g) { - var a = this.getChart(), - f = a.getInherited().rtl, - d = f ? -1 : 1, - h = g.getXY(), - c = a.element.getXY(), - b = a.getMainRect(); - return d * Math.atan2(h[1] - c[1] - b[3] * 0.5, h[0] - c[0] - b[2] * 0.5) - }, - getRadius: function(j) { - var f = this.getChart(), - a = f.getRadius(), - d = f.getSeries(), - h = d.length, - c = 0, - b, g; - for (; c < h; c++) { - b = d[c]; - if (b.isPie3D) { - g = b.getRadius(); - if (g > a) { - a = g - } - } - } - return a - } -}); -Ext.define("Ext.chart.plugin.ItemEvents", { - extend: "Ext.plugin.Abstract", - alias: "plugin.chartitemevents", - moveEvents: false, - mouseMoveEvents: { - mousemove: true, - mouseover: true, - mouseout: true - }, - itemMouseMoveEvents: { - itemmousemove: true, - itemmouseover: true, - itemmouseout: true - }, - init: function(b) { - var a = "handleEvent"; - this.chart = b; - b.addElementListener({ - click: a, - dblclick: a, - mousedown: a, - mousemove: a, - mouseup: a, - mouseover: a, - mouseout: a, - priority: 1001, - scope: this - }) - }, - hasItemMouseMoveListeners: function() { - var b = this.chart.hasListeners, - a; - for (a in this.itemMouseMoveEvents) { - if (a in b) { - return true - } - } - return false - }, - handleEvent: function(g) { - var d = this, - a = d.chart, - h = g.type in d.mouseMoveEvents, - c = d.lastItem, - f, b; - if (h && !d.hasItemMouseMoveListeners() && !d.moveEvents) { - return - } - f = a.getEventXY(g); - b = a.getItemForPoint(f[0], f[1]); - if (h && !Ext.Object.equals(b, c)) { - if (c) { - a.fireEvent("itemmouseout", a, c, g); - c.series.fireEvent("itemmouseout", c.series, c, g) - } - if (b) { - a.fireEvent("itemmouseover", a, b, g); - b.series.fireEvent("itemmouseover", b.series, b, g) - } - } - if (b) { - a.fireEvent("item" + g.type, a, b, g); - b.series.fireEvent("item" + g.type, b.series, b, g) - } - d.lastItem = b - } -}); -Ext.define("Ext.chart.series.Cartesian", { - extend: "Ext.chart.series.Series", - config: { - xField: null, - yField: null, - xAxis: null, - yAxis: null - }, - directions: ["X", "Y"], - fieldCategoryX: ["X"], - fieldCategoryY: ["Y"], - applyXAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - applyYAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - updateXAxis: function(a) { - a.processData(this) - }, - updateYAxis: function(a) { - a.processData(this) - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - getItemForPoint: function(a, g) { - if (this.getSprites()) { - var f = this, - d = f.getSprites()[0], - b = f.getStore(), - e, c; - if (f.getHidden()) { - return null - } - if (d) { - c = d.getIndexNearPoint(a, g); - if (c !== -1) { - e = { - series: f, - category: f.getItemInstancing() ? "items" : "markers", - index: c, - record: b.getData().items[c], - field: f.getYField(), - sprite: d - }; - return e - } - } - } - }, - createSprite: function() { - var c = this, - a = c.callParent(), - b = c.getChart(), - d = c.getXAxis(); - a.setAttributes({ - flipXY: b.getFlipXY(), - xAxis: d - }); - if (a.setAggregator && d && d.getAggregator) { - if (d.getAggregator) { - a.setAggregator({ - strategy: d.getAggregator() - }) - } else { - a.setAggregator({}) - } - } - return a - }, - getSprites: function() { - var d = this, - c = this.getChart(), - e = d.getAnimation() || c && c.getAnimation(), - b = d.getItemInstancing(), - f = d.sprites, - a; - if (!c) { - return [] - } - if (!f.length) { - a = d.createSprite() - } else { - a = f[0] - } - if (e) { - if (b) { - a.itemsMarker.getTemplate().setAnimation(e) - } - a.setAnimation(e) - } - return f - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getSubStyleWithTheme(), - c = a.fillStyle; - if (Ext.isArray(c)) { - c = c[0] - } - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - }, - getXRange: function() { - return [this.dataRange[0], this.dataRange[2]] - }, - getYRange: function() { - return [this.dataRange[1], this.dataRange[3]] - } -}); -Ext.define("Ext.chart.series.StackedCartesian", { - extend: "Ext.chart.series.Cartesian", - config: { - stacked: true, - splitStacks: true, - fullStack: false, - fullStackTotal: 100, - hidden: [] - }, - spriteAnimationCount: 0, - themeColorCount: function() { - var b = this, - a = b.getYField(); - return Ext.isArray(a) ? a.length : 1 - }, - updateStacked: function() { - this.processData() - }, - updateSplitStacks: function() { - this.processData() - }, - coordinateY: function() { - return this.coordinateStacked("Y", 1, 2) - }, - coordinateStacked: function(D, e, m) { - var F = this, - f = F.getStore(), - r = f.getData().items, - B = r.length, - c = F["get" + D + "Axis"](), - x = F.getHidden(), - a = F.getSplitStacks(), - z = F.getFullStack(), - l = F.getFullStackTotal(), - p = { - min: 0, - max: 0 - }, - n = F["fieldCategory" + D], - C = [], - o = [], - E = [], - h, A = F.getStacked(), - g = F.getSprites(), - q = [], - w, v, u, s, H, y, b, d, G, t; - if (!g.length) { - return - } - for (w = 0; w < n.length; w++) { - d = n[w]; - s = F.getFields([d]); - H = s.length; - for (v = 0; v < B; v++) { - C[v] = 0; - o[v] = 0; - E[v] = 0 - } - for (v = 0; v < H; v++) { - if (!x[v]) { - q[v] = F.coordinateData(r, s[v], c) - } - } - if (A && z) { - y = []; - if (a) { - b = [] - } - for (v = 0; v < B; v++) { - y[v] = 0; - if (a) { - b[v] = 0 - } - for (u = 0; u < H; u++) { - G = q[u]; - if (!G) { - continue - } - G = G[v]; - if (G >= 0 || !a) { - y[v] += G - } else { - if (G < 0) { - b[v] += G - } - } - } - } - } - for (v = 0; v < H; v++) { - t = {}; - if (x[v]) { - t["dataStart" + d] = C; - t["data" + d] = C; - g[v].setAttributes(t); - continue - } - G = q[v]; - if (A) { - h = []; - for (u = 0; u < B; u++) { - if (!G[u]) { - G[u] = 0 - } - if (G[u] >= 0 || !a) { - if (z && y[u]) { - G[u] *= l / y[u] - } - C[u] = o[u]; - o[u] += G[u]; - h[u] = o[u] - } else { - if (z && b[u]) { - G[u] *= l / b[u] - } - C[u] = E[u]; - E[u] += G[u]; - h[u] = E[u] - } - } - t["dataStart" + d] = C; - t["data" + d] = h; - F.getRangeOfData(C, p); - F.getRangeOfData(h, p) - } else { - t["dataStart" + d] = C; - t["data" + d] = G; - F.getRangeOfData(G, p) - } - g[v].setAttributes(t) - } - } - F.dataRange[e] = p.min; - F.dataRange[e + m] = p.max; - t = {}; - t["dataMin" + D] = p.min; - t["dataMax" + D] = p.max; - for (w = 0; w < g.length; w++) { - g[w].setAttributes(t) - } - }, - getFields: function(f) { - var e = this, - a = [], - c, b, d; - for (b = 0, d = f.length; b < d; b++) { - c = e["get" + f[b] + "Field"](); - if (Ext.isArray(c)) { - a.push.apply(a, c) - } else { - a.push(c) - } - } - return a - }, - updateLabelOverflowPadding: function(a) { - this.getLabel().setAttributes({ - labelOverflowPadding: a - }) - }, - getSprites: function() { - var k = this, - j = k.getChart(), - c = k.getAnimation() || j && j.getAnimation(), - f = k.getFields(k.fieldCategoryY), - b = k.getItemInstancing(), - h = k.sprites, - l, e = k.getHidden(), - g = false, - d, a = f.length; - if (!j) { - return [] - } - for (d = 0; d < a; d++) { - l = h[d]; - if (!l) { - l = k.createSprite(); - l.setAttributes({ - zIndex: -d - }); - l.setField(f[d]); - g = true; - e.push(false); - if (b) { - l.itemsMarker.getTemplate().setAttributes(k.getStyleByIndex(d)) - } else { - l.setAttributes(k.getStyleByIndex(d)) - } - } - if (c) { - if (b) { - l.itemsMarker.getTemplate().setAnimation(c) - } - l.setAnimation(c) - } - } - if (g) { - k.updateHidden(e) - } - return h - }, - getItemForPoint: function(k, j) { - if (this.getSprites()) { - var h = this, - b, g, m, a = h.getItemInstancing(), - f = h.getSprites(), - l = h.getStore(), - c = h.getHidden(), - n, d, e; - for (b = 0, g = f.length; b < g; b++) { - if (!c[b]) { - m = f[b]; - d = m.getIndexNearPoint(k, j); - if (d !== -1) { - e = h.getYField(); - n = { - series: h, - index: d, - category: a ? "items" : "markers", - record: l.getData().items[d], - field: typeof e === "string" ? e : e[b], - sprite: m - }; - return n - } - } - } - return null - } - }, - provideLegendInfo: function(e) { - var g = this, - f = g.getSprites(), - h = g.getTitle(), - j = g.getYField(), - d = g.getHidden(), - k = f.length === 1, - b, l, c, a; - for (c = 0; c < f.length; c++) { - b = g.getStyleByIndex(c); - l = b.fillStyle; - if (h) { - if (Ext.isArray(h)) { - a = h[c] - } else { - if (k) { - a = h - } - } - } else { - if (Ext.isArray(j)) { - a = j[c] - } else { - a = g.getId() - } - } - e.push({ - name: a, - mark: (Ext.isObject(l) ? l.stops && l.stops[0].color : l) || b.strokeStyle || "black", - disabled: d[c], - series: g.getId(), - index: c - }) - } - }, - onSpriteAnimationStart: function(a) { - this.spriteAnimationCount++; - if (this.spriteAnimationCount === 1) { - this.fireEvent("animationstart") - } - }, - onSpriteAnimationEnd: function(a) { - this.spriteAnimationCount--; - if (this.spriteAnimationCount === 0) { - this.fireEvent("animationend") - } - } -}); -Ext.define("Ext.chart.series.sprite.Series", { - extend: "Ext.draw.sprite.Sprite", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - inheritableStatics: { - def: { - processors: { - dataMinX: "number", - dataMaxX: "number", - dataMinY: "number", - dataMaxY: "number", - rangeX: "data", - rangeY: "data", - dataX: "data", - dataY: "data" - }, - defaults: { - dataMinX: 0, - dataMaxX: 1, - dataMinY: 0, - dataMaxY: 1, - rangeX: null, - rangeY: null, - dataX: null, - dataY: null - }, - triggers: { - dataX: "bbox", - dataY: "bbox", - dataMinX: "bbox", - dataMaxX: "bbox", - dataMinY: "bbox", - dataMaxY: "bbox" - } - } - }, - config: { - store: null, - series: null, - field: null - } -}); -Ext.define("Ext.chart.series.sprite.Cartesian", { - extend: "Ext.chart.series.sprite.Series", - inheritableStatics: { - def: { - processors: { - labels: "default", - labelOverflowPadding: "number", - selectionTolerance: "number", - flipXY: "bool", - renderer: "default", - visibleMinX: "number", - visibleMinY: "number", - visibleMaxX: "number", - visibleMaxY: "number", - innerWidth: "number", - innerHeight: "number" - }, - defaults: { - labels: null, - labelOverflowPadding: 10, - selectionTolerance: 20, - flipXY: false, - renderer: null, - transformFillStroke: false, - visibleMinX: 0, - visibleMinY: 0, - visibleMaxX: 1, - visibleMaxY: 1, - innerWidth: 1, - innerHeight: 1 - }, - triggers: { - dataX: "dataX,bbox", - dataY: "dataY,bbox", - visibleMinX: "panzoom", - visibleMinY: "panzoom", - visibleMaxX: "panzoom", - visibleMaxY: "panzoom", - innerWidth: "panzoom", - innerHeight: "panzoom" - }, - updaters: { - dataX: function(a) { - this.processDataX(); - this.scheduleUpdater(a, "dataY", ["dataY"]) - }, - dataY: function() { - this.processDataY() - }, - panzoom: function(c) { - var e = c.visibleMaxX - c.visibleMinX, - d = c.visibleMaxY - c.visibleMinY, - b = c.flipXY ? c.innerHeight : c.innerWidth, - g = !c.flipXY ? c.innerHeight : c.innerWidth, - a = this.getSurface(), - f = a ? a.getInherited().rtl : false; - if (f && !c.flipXY) { - c.translationX = b + c.visibleMinX * b / e - } else { - c.translationX = -c.visibleMinX * b / e - } - c.translationY = -c.visibleMinY * g / d; - c.scalingX = (f && !c.flipXY ? -1 : 1) * b / e; - c.scalingY = g / d; - c.scalingCenterX = 0; - c.scalingCenterY = 0; - this.applyTransformations(true) - } - } - } - }, - processDataY: Ext.emptyFn, - processDataX: Ext.emptyFn, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.dataMinX; - b.y = a.dataMinY; - b.width = a.dataMaxX - a.dataMinX; - b.height = a.dataMaxY - a.dataMinY - }, - binarySearch: function(d) { - var b = this.attr.dataX, - f = 0, - a = b.length; - if (d <= b[0]) { - return f - } - if (d >= b[a - 1]) { - return a - 1 - } - while (f + 1 < a) { - var c = (f + a) >> 1, - e = b[c]; - if (e === d) { - return c - } else { - if (e < d) { - f = c - } else { - a = c - } - } - } - return f - }, - render: function(b, c, g) { - var f = this, - a = f.attr, - e = a.inverseMatrix.clone(); - e.appendMatrix(b.inverseMatrix); - if (a.dataX === null || a.dataX === undefined) { - return - } - if (a.dataY === null || a.dataY === undefined) { - return - } - if (e.getXX() * e.getYX() || e.getXY() * e.getYY()) { - console.log("Cartesian Series sprite does not support rotation/sheering"); - return - } - var d = e.transformList([ - [g[0] - 1, g[3] + 1], - [g[0] + g[2] + 1, -1] - ]); - d = d[0].concat(d[1]); - f.renderClipped(b, c, d, g) - }, - renderClipped: Ext.emptyFn, - getIndexNearPoint: function(f, e) { - var w = this, - q = w.attr.matrix, - h = w.attr.dataX, - g = w.attr.dataY, - k = w.attr.selectionTolerance, - t, r, c = -1, - j = q.clone().prependMatrix(w.surfaceMatrix).inverse(), - u = j.transformPoint([f, e]), - b = j.transformPoint([f - k, e - k]), - n = j.transformPoint([f + k, e + k]), - a = Math.min(b[0], n[0]), - s = Math.max(b[0], n[0]), - l = Math.min(b[1], n[1]), - d = Math.max(b[1], n[1]), - m, v, o, p; - for (o = 0, p = h.length; o < p; o++) { - m = h[o]; - v = g[o]; - if (m > a && m < s && v > l && v < d) { - if (c === -1 || (Math.abs(m - u[0]) < t) && (Math.abs(v - u[1]) < r)) { - t = Math.abs(m - u[0]); - r = Math.abs(v - u[1]); - c = o - } - } - } - return c - } -}); -Ext.define("Ext.chart.series.sprite.StackedCartesian", { - extend: "Ext.chart.series.sprite.Cartesian", - inheritableStatics: { - def: { - processors: { - groupCount: "number", - groupOffset: "number", - dataStartY: "data" - }, - defaults: { - selectionTolerance: 20, - groupCount: 1, - groupOffset: 0, - dataStartY: null - }, - triggers: { - dataStartY: "dataY,bbox" - } - } - }, - getIndexNearPoint: function(e, d) { - var o = this, - q = o.attr.matrix, - h = o.attr.dataX, - f = o.attr.dataY, - u = o.attr.dataStartY, - l = o.attr.selectionTolerance, - s = 0.5, - r = Infinity, - b = -1, - k = q.clone().prependMatrix(this.surfaceMatrix).inverse(), - t = k.transformPoint([e, d]), - a = k.transformPoint([e - l, d - l]), - n = k.transformPoint([e + l, d + l]), - m = Math.min(a[1], n[1]), - c = Math.max(a[1], n[1]), - j, g; - for (var p = 0; p < h.length; p++) { - if (Math.min(u[p], f[p]) <= c && m <= Math.max(u[p], f[p])) { - j = Math.abs(h[p] - t[0]); - g = Math.max(-Math.min(f[p] - t[1], t[1] - u[p]), 0); - if (j < s && g <= r) { - s = j; - r = g; - b = p - } - } - } - return b - } -}); -Ext.define("Ext.chart.series.sprite.Area", { - alias: "sprite.areaSeries", - extend: "Ext.chart.series.sprite.StackedCartesian", - inheritableStatics: { - def: { - processors: { - step: "bool" - }, - defaults: { - step: false - } - } - }, - renderClipped: function(q, s, A) { - var B = this, - p = B.attr, - l = p.dataX, - j = p.dataY, - C = p.dataStartY, - t = p.matrix, - h, g, v, f, d, z, w, e = t.elements[0], - m = t.elements[4], - o = t.elements[3], - k = t.elements[5], - c = B.surfaceMatrix, - n = {}, - r = Math.min(A[0], A[2]), - u = Math.max(A[0], A[2]), - b = Math.max(0, this.binarySearch(r)), - a = Math.min(l.length - 1, this.binarySearch(u) + 1); - s.beginPath(); - z = l[b] * e + m; - w = j[b] * o + k; - s.moveTo(z, w); - if (p.step) { - d = w; - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, d); - s.lineTo(h, d = g) - } - } else { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, g) - } - } - if (C) { - if (p.step) { - f = l[a] * e + m; - for (v = a; v >= b; v--) { - h = l[v] * e + m; - g = C[v] * o + k; - s.lineTo(f, g); - s.lineTo(f = h, g) - } - } else { - for (v = a; v >= b; v--) { - h = l[v] * e + m; - g = C[v] * o + k; - s.lineTo(h, g) - } - } - } else { - s.lineTo(l[a] * e + m, g); - s.lineTo(l[a] * e + m, k); - s.lineTo(z, k); - s.lineTo(z, j[v] * o + k) - } - if (p.transformFillStroke) { - p.matrix.toContext(s) - } - s.fill(); - if (p.transformFillStroke) { - p.inverseMatrix.toContext(s) - } - s.beginPath(); - s.moveTo(z, w); - if (p.step) { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, d); - s.lineTo(h, d = g); - n.translationX = c.x(h, g); - n.translationY = c.y(h, g); - B.putMarker("markers", n, v, !p.renderer) - } - } else { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, g); - n.translationX = c.x(h, g); - n.translationY = c.y(h, g); - B.putMarker("markers", n, v, !p.renderer) - } - } - if (p.transformFillStroke) { - p.matrix.toContext(s) - } - s.stroke() - } -}); -Ext.define("Ext.chart.series.Area", { - extend: "Ext.chart.series.StackedCartesian", - alias: "series.area", - type: "area", - seriesType: "areaSeries", - requires: ["Ext.chart.series.sprite.Area"], - config: { - splitStacks: false - } -}); -Ext.define("Ext.chart.series.sprite.Bar", { - alias: "sprite.barSeries", - extend: "Ext.chart.series.sprite.StackedCartesian", - inheritableStatics: { - def: { - processors: { - minBarWidth: "number", - maxBarWidth: "number", - minGapWidth: "number", - radius: "number", - inGroupGapWidth: "number" - }, - defaults: { - minBarWidth: 2, - maxBarWidth: 100, - minGapWidth: 5, - inGroupGapWidth: 3, - radius: 0 - } - } - }, - drawLabel: function(k, i, s, h, o) { - var q = this, - n = q.attr, - f = q.getMarker("labels"), - d = f.getTemplate(), - l = q.labelCfg || (q.labelCfg = {}), - c = q.surfaceMatrix, - j = n.labelOverflowPadding, - b = d.attr.display, - m = d.attr.orientation, - g, e, a, r, t, p; - l.x = c.x(i, h); - l.y = c.y(i, h); - if (!n.flipXY) { - l.rotationRads = -Math.PI * 0.5 - } else { - l.rotationRads = 0 - } - l.calloutVertical = !n.flipXY; - switch (m) { - case "horizontal": - l.rotationRads = 0; - l.calloutVertical = false; - break; - case "vertical": - l.rotationRads = -Math.PI * 0.5; - l.calloutVertical = true; - break - } - l.text = k; - if (d.attr.renderer) { - p = [k, f, l, { - store: q.getStore() - }, o]; - r = Ext.callback(d.attr.renderer, null, p, 0, q.getSeries()); - if (typeof r === "string") { - l.text = r - } else { - if (typeof r === "object") { - if ("text" in r) { - l.text = r.text - } - t = true - } - } - } - a = q.getMarkerBBox("labels", o, true); - if (!a) { - q.putMarker("labels", l, o); - a = q.getMarkerBBox("labels", o, true) - } - e = (a.width / 2 + j); - if (s > h) { - e = -e - } - if ((m === "horizontal" && n.flipXY) || (m === "vertical" && !n.flipXY) || !m) { - g = (b === "insideStart") ? s + e : h - e - } else { - g = (b === "insideStart") ? s + j * 2 : h - j * 2 - } - l.x = c.x(i, g); - l.y = c.y(i, g); - g = (b === "insideStart") ? s - e : h + e; - l.calloutPlaceX = c.x(i, g); - l.calloutPlaceY = c.y(i, g); - g = (b === "insideStart") ? s : h; - l.calloutStartX = c.x(i, g); - l.calloutStartY = c.y(i, g); - if (s > h) { - e = -e - } - if (Math.abs(h - s) <= e * 2 || b === "outside") { - l.callout = 1 - } else { - l.callout = 0 - } - if (t) { - Ext.apply(l, r) - } - q.putMarker("labels", l, o) - }, - drawBar: function(l, b, d, c, h, k, a, e) { - var g = this, - j = {}, - f = g.attr.renderer, - i; - j.x = c; - j.y = h; - j.width = k - c; - j.height = a - h; - j.radius = g.attr.radius; - if (f) { - i = Ext.callback(f, null, [g, j, { - store: g.getStore() - }, e], 0, g.getSeries()); - Ext.apply(j, i) - } - g.putMarker("items", j, e, !f) - }, - renderClipped: function(G, u, F, C) { - if (this.cleanRedraw) { - return - } - var q = this, - o = q.attr, - w = o.dataX, - v = o.dataY, - H = o.labels, - n = o.dataStartY, - m = o.groupCount, - E = o.groupOffset - (m - 1) * 0.5, - z = o.inGroupGapWidth, - t = u.lineWidth, - D = o.matrix, - B = D.elements[0], - j = D.elements[3], - e = D.elements[4], - d = G.roundPixel(D.elements[5]) - 1, - J = (B < 0 ? -1 : 1) * B - o.minGapWidth, - k = (Math.min(J, o.maxBarWidth) - z * (m - 1)) / m, - A = G.roundPixel(Math.max(o.minBarWidth, k)), - c = q.surfaceMatrix, - g, I, b, h, K, a, l = 0.5 * o.lineWidth, - L = Math.min(F[0], F[2]), - x = Math.max(F[0], F[2]), - y = Math.max(0, Math.floor(L)), - p = Math.min(w.length - 1, Math.ceil(x)), - f = H && q.getMarker("labels"), - s, r; - for (K = y; K <= p; K++) { - s = n ? n[K] : 0; - r = v[K]; - a = w[K] * B + e + E * (A + z); - g = G.roundPixel(a - A / 2) + l; - h = G.roundPixel(r * j + d + t); - I = G.roundPixel(a + A / 2) - l; - b = G.roundPixel(s * j + d + t); - q.drawBar(u, G, F, g, h - l, I, b - l, K); - if (f && H[K] != null) { - q.drawLabel(H[K], a, b, h, K) - } - q.putMarker("markers", { - translationX: c.x(a, h), - translationY: c.y(a, h) - }, K, true) - } - }, - getIndexNearPoint: function(l, k) { - var m = this, - g = m.attr, - h = g.dataX, - a = m.getSurface(), - b = a.getRect() || [0, 0, 0, 0], - j = b[3], - e, d, c, n, f = -1; - if (g.flipXY) { - e = j - k; - if (a.getInherited().rtl) { - d = b[2] - l - } else { - d = l - } - } else { - e = l; - d = j - k - } - for (c = 0; c < h.length; c++) { - n = m.getMarkerBBox("items", c); - if (Ext.draw.Draw.isPointInBBox(e, d, n)) { - f = c; - break - } - } - return f - } -}); -Ext.define("Ext.chart.series.Bar", { - extend: "Ext.chart.series.StackedCartesian", - alias: "series.bar", - type: "bar", - seriesType: "barSeries", - requires: ["Ext.chart.series.sprite.Bar", "Ext.draw.sprite.Rect"], - config: { - itemInstancing: { - type: "rect", - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0, - radius: 0 - } - } - } - }, - getItemForPoint: function(a, f) { - if (this.getSprites()) { - var d = this, - c = d.getChart(), - e = c.getInnerPadding(), - b = c.getInherited().rtl; - arguments[0] = a + (b ? e.right : -e.left); - arguments[1] = f + e.bottom; - return d.callParent(arguments) - } - }, - updateXAxis: function(a) { - a.setLabelInSpan(true); - this.callParent(arguments) - }, - updateHidden: function(a) { - this.callParent(arguments); - this.updateStacked() - }, - updateStacked: function(c) { - var e = this, - g = e.getSprites(), - d = g.length, - f = [], - a = {}, - b; - for (b = 0; b < d; b++) { - if (!g[b].attr.hidden) { - f.push(g[b]) - } - } - d = f.length; - if (e.getStacked()) { - a.groupCount = 1; - a.groupOffset = 0; - for (b = 0; b < d; b++) { - f[b].setAttributes(a) - } - } else { - a.groupCount = f.length; - for (b = 0; b < d; b++) { - a.groupOffset = b; - f[b].setAttributes(a) - } - } - e.callParent(arguments) - } -}); -Ext.define("Ext.chart.series.sprite.Bar3D", { - extend: "Ext.chart.series.sprite.Bar", - alias: "sprite.bar3dSeries", - requires: ["Ext.draw.gradient.Linear"], - inheritableStatics: { - def: { - processors: { - depthWidthRatio: "number", - saturationFactor: "number", - brightnessFactor: "number", - colorSpread: "number" - }, - defaults: { - depthWidthRatio: 1 / 3, - saturationFactor: 1, - brightnessFactor: 1, - colorSpread: 1, - transformFillStroke: true - }, - triggers: { - groupCount: "panzoom" - }, - updaters: { - panzoom: function(c) { - var g = this, - e = c.visibleMaxX - c.visibleMinX, - d = c.visibleMaxY - c.visibleMinY, - b = c.flipXY ? c.innerHeight : c.innerWidth, - h = !c.flipXY ? c.innerHeight : c.innerWidth, - a = g.getSurface(), - f = a ? a.getInherited().rtl : false; - if (f && !c.flipXY) { - c.translationX = b + c.visibleMinX * b / e - } else { - c.translationX = -c.visibleMinX * b / e - } - c.translationY = -c.visibleMinY * (h - g.depth) / d; - c.scalingX = (f && !c.flipXY ? -1 : 1) * b / e; - c.scalingY = (h - g.depth) / d; - c.scalingCenterX = 0; - c.scalingCenterY = 0; - g.applyTransformations(true) - } - } - } - }, - config: { - showStroke: false - }, - depth: 0, - drawBar: function(p, b, d, c, l, o, a, h) { - var k = this, - i = k.attr, - n = {}, - j = i.renderer, - m, g, f, e; - n.x = (c + o) * 0.5; - n.y = l; - n.width = (o - c) * 0.75; - n.height = a - l; - n.depth = g = n.width * i.depthWidthRatio; - n.orientation = i.flipXY ? "horizontal" : "vertical"; - n.saturationFactor = i.saturationFactor; - n.brightnessFactor = i.brightnessFactor; - n.colorSpread = i.colorSpread; - if (g !== k.depth) { - k.depth = g; - f = k.getSeries(); - f.fireEvent("depthchange", f, g) - } - if (j) { - e = [k, n, { - store: k.getStore() - }, h]; - m = Ext.callback(j, null, e, 0, k.getSeries()); - Ext.apply(n, m) - } - k.putMarker("items", n, h, !j) - } -}); -Ext.define("Ext.chart.series.sprite.Box", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.box", - type: "box", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number", - depth: "number", - orientation: "enums(vertical,horizontal)", - showStroke: "bool", - saturationFactor: "number", - brightnessFactor: "number", - colorSpread: "number" - }, - triggers: { - x: "bbox", - y: "bbox", - width: "bbox", - height: "bbox", - depth: "bbox", - orientation: "bbox" - }, - defaults: { - x: 0, - y: 0, - width: 8, - height: 8, - depth: 8, - orientation: "vertical", - showStroke: false, - saturationFactor: 1, - brightnessFactor: 1, - colorSpread: 1, - lineJoin: "bevel" - } - } - }, - constructor: function(a) { - this.callParent([a]); - this.topGradient = new Ext.draw.gradient.Linear({}); - this.rightGradient = new Ext.draw.gradient.Linear({}); - this.frontGradient = new Ext.draw.gradient.Linear({}) - }, - updatePlainBBox: function(d) { - var c = this.attr, - b = c.x, - g = c.y, - e = c.width, - a = c.height, - f = c.depth; - d.x = b - e * 0.5; - d.width = e + f; - if (a > 0) { - d.y = g; - d.height = a + f - } else { - d.y = g + f; - d.height = a - f - } - }, - render: function(l, m) { - var u = this, - k = u.attr, - r = k.x, - j = k.y, - f = j + k.height, - i = j < f, - e = k.width * 0.5, - v = k.depth, - d = k.orientation === "horizontal", - g = k.globalAlpha < 1, - c = k.fillStyle, - n = Ext.draw.Color.create(c.isGradient ? c.getStops()[0].color : c), - h = k.saturationFactor, - o = k.brightnessFactor, - t = k.colorSpread, - b = n.getHSV(), - a = {}, - s, q, p; - if (!k.showStroke) { - m.strokeStyle = Ext.draw.Color.RGBA_NONE - } - if (i) { - p = j; - j = f; - f = p - } - u.topGradient.setDegrees(d ? 0 : 80); - u.topGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 + t * 0.1) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.11) * o, 0, 1)) - }]); - u.rightGradient.setDegrees(d ? 45 : 90); - u.rightGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.14) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 + t * 0.4) * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.32) * o, 0, 1)) - }]); - if (d) { - u.frontGradient.setDegrees(0) - } else { - u.frontGradient.setRadians(Math.atan2(j - f, e * 2)) - } - u.frontGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 - t * 0.1) * h, 0, 1), Ext.Number.constrain((0.5 + t * 0.1) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 + t * 0.1) * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.23) * o, 0, 1)) - }]); - if (g || i) { - m.beginPath(); - m.moveTo(r - e, f); - m.lineTo(r - e + v, f + v); - m.lineTo(r + e + v, f + v); - m.lineTo(r + e, f); - m.closePath(); - a.x = r - e; - a.y = j; - a.width = e + v; - a.height = v; - m.fillStyle = (d ? u.rightGradient : u.topGradient).generateGradient(m, a); - m.fillStroke(k) - } - if (g) { - m.beginPath(); - m.moveTo(r - e, j); - m.lineTo(r - e + v, j + v); - m.lineTo(r - e + v, f + v); - m.lineTo(r - e, f); - m.closePath(); - a.x = r + e; - a.y = f; - a.width = v; - a.height = j + v - f; - m.fillStyle = (d ? u.topGradient : u.rightGradient).generateGradient(m, a); - m.fillStroke(k) - } - q = l.roundPixel(j); - m.beginPath(); - m.moveTo(r - e, q); - m.lineTo(r - e + v, j + v); - m.lineTo(r + e + v, j + v); - m.lineTo(r + e, q); - m.closePath(); - a.x = r - e; - a.y = j; - a.width = e + v; - a.height = v; - m.fillStyle = (d ? u.rightGradient : u.topGradient).generateGradient(m, a); - m.fillStroke(k); - s = l.roundPixel(r + e); - m.beginPath(); - m.moveTo(s, l.roundPixel(j)); - m.lineTo(r + e + v, j + v); - m.lineTo(r + e + v, f + v); - m.lineTo(s, f); - m.closePath(); - a.x = r + e; - a.y = f; - a.width = v; - a.height = j + v - f; - m.fillStyle = (d ? u.topGradient : u.rightGradient).generateGradient(m, a); - m.fillStroke(k); - s = l.roundPixel(r + e); - q = l.roundPixel(j); - m.beginPath(); - m.moveTo(r - e, f); - m.lineTo(r - e, q); - m.lineTo(s, q); - m.lineTo(s, f); - m.closePath(); - a.x = r - e; - a.y = f; - a.width = e * 2; - a.height = j - f; - m.fillStyle = u.frontGradient.generateGradient(m, a); - m.fillStroke(k) - } -}); -Ext.define("Ext.chart.series.Bar3D", { - extend: "Ext.chart.series.Bar", - requires: ["Ext.chart.series.sprite.Bar3D", "Ext.chart.series.sprite.Box"], - alias: "series.bar3d", - type: "bar3d", - seriesType: "bar3dSeries", - config: { - itemInstancing: { - type: "box", - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0, - depth: 0 - } - } - }, - highlightCfg: { - opacity: 0.8 - } - }, - getSprites: function() { - var c = this.callParent(arguments), - b, d, a; - for (a = 0; a < c.length; a++) { - b = c[a]; - d = b.attr.zIndex; - if (d < 0) { - b.setAttributes({ - zIndex: -d - }) - } - if (b.setSeries) { - b.setSeries(this) - } - } - return c - }, - getDepth: function() { - var a = this.getSprites()[0]; - return a ? (a.depth || 0) : 0 - }, - getItemForPoint: function(m, k) { - if (this.getSprites()) { - var j = this, - b, o, a = j.getItemInstancing(), - h = j.getSprites(), - n = j.getStore(), - c = j.getHidden(), - g = j.getChart(), - l = g.getInnerPadding(), - f = g.getInherited().rtl, - p, d, e; - m = m + (f ? l.right : -l.left); - k = k + l.bottom; - for (b = h.length - 1; b >= 0; b--) { - if (!c[b]) { - o = h[b]; - d = o.getIndexNearPoint(m, k); - if (d !== -1) { - e = j.getYField(); - p = { - series: j, - index: d, - category: a ? "items" : "markers", - record: n.getData().items[d], - field: typeof e === "string" ? e : e[b], - sprite: o - }; - return p - } - } - } - return null - } - } -}); -Ext.define("Ext.draw.LimitedCache", { - config: { - limit: 40, - feeder: function() { - return 0 - }, - scope: null - }, - cache: null, - constructor: function(a) { - this.cache = {}; - this.cache.list = []; - this.cache.tail = 0; - this.initConfig(a) - }, - get: function(e) { - var c = this.cache, - b = this.getLimit(), - a = this.getFeeder(), - d = this.getScope() || this; - if (c[e]) { - return c[e].value - } - if (c.list[c.tail]) { - delete c[c.list[c.tail].cacheId] - } - c[e] = c.list[c.tail] = { - value: a.apply(d, Array.prototype.slice.call(arguments, 1)), - cacheId: e - }; - c.tail++; - if (c.tail === b) { - c.tail = 0 - } - return c[e].value - }, - clear: function() { - this.cache = {}; - this.cache.list = []; - this.cache.tail = 0 - } -}); -Ext.define("Ext.draw.SegmentTree", { - config: { - strategy: "double" - }, - time: function(m, l, n, c, E, d, e) { - var f = 0, - o, A, s = new Date(n[m.startIdx[0]]), - x = new Date(n[m.endIdx[l - 1]]), - D = Ext.Date, - u = [ - [D.MILLI, 1, "ms1", null], - [D.MILLI, 2, "ms2", "ms1"], - [D.MILLI, 5, "ms5", "ms1"], - [D.MILLI, 10, "ms10", "ms5"], - [D.MILLI, 50, "ms50", "ms10"], - [D.MILLI, 100, "ms100", "ms50"], - [D.MILLI, 500, "ms500", "ms100"], - [D.SECOND, 1, "s1", "ms500"], - [D.SECOND, 10, "s10", "s1"], - [D.SECOND, 30, "s30", "s10"], - [D.MINUTE, 1, "mi1", "s10"], - [D.MINUTE, 5, "mi5", "mi1"], - [D.MINUTE, 10, "mi10", "mi5"], - [D.MINUTE, 30, "mi30", "mi10"], - [D.HOUR, 1, "h1", "mi30"], - [D.HOUR, 6, "h6", "h1"], - [D.HOUR, 12, "h12", "h6"], - [D.DAY, 1, "d1", "h12"], - [D.DAY, 7, "d7", "d1"], - [D.MONTH, 1, "mo1", "d1"], - [D.MONTH, 3, "mo3", "mo1"], - [D.MONTH, 6, "mo6", "mo3"], - [D.YEAR, 1, "y1", "mo3"], - [D.YEAR, 5, "y5", "y1"], - [D.YEAR, 10, "y10", "y5"], - [D.YEAR, 100, "y100", "y10"] - ], - z, b, k = f, - F = l, - j = false, - r = m.startIdx, - h = m.endIdx, - w = m.minIdx, - C = m.maxIdx, - a = m.open, - y = m.close, - g = m.minX, - q = m.minY, - p = m.maxX, - B = m.maxY, - v, t; - for (z = 0; l > f + 1 && z < u.length; z++) { - s = new Date(n[r[0]]); - b = u[z]; - s = D.align(s, b[0], b[1]); - if (D.diff(s, x, b[0]) > n.length * 2 * b[1]) { - continue - } - if (b[3] && m.map["time_" + b[3]]) { - o = m.map["time_" + b[3]][0]; - A = m.map["time_" + b[3]][1] - } else { - o = k; - A = F - } - f = l; - t = s; - j = true; - r[l] = r[o]; - h[l] = h[o]; - w[l] = w[o]; - C[l] = C[o]; - a[l] = a[o]; - y[l] = y[o]; - g[l] = g[o]; - q[l] = q[o]; - p[l] = p[o]; - B[l] = B[o]; - t = Ext.Date.add(t, b[0], b[1]); - for (v = o + 1; v < A; v++) { - if (n[h[v]] < +t) { - h[l] = h[v]; - y[l] = y[v]; - if (B[v] > B[l]) { - B[l] = B[v]; - p[l] = p[v]; - C[l] = C[v] - } - if (q[v] < q[l]) { - q[l] = q[v]; - g[l] = g[v]; - w[l] = w[v] - } - } else { - l++; - r[l] = r[v]; - h[l] = h[v]; - w[l] = w[v]; - C[l] = C[v]; - a[l] = a[v]; - y[l] = y[v]; - g[l] = g[v]; - q[l] = q[v]; - p[l] = p[v]; - B[l] = B[v]; - t = Ext.Date.add(t, b[0], b[1]) - } - } - if (l > f) { - m.map["time_" + b[2]] = [f, l] - } - } - }, - "double": function(h, u, j, a, t, b, c) { - var e = 0, - k, f = 1, - n, d, v, g, s, l, m, r, q, p, o; - while (u > e + 1) { - k = e; - e = u; - f += f; - for (n = k; n < e; n += 2) { - if (n === e - 1) { - d = h.startIdx[n]; - v = h.endIdx[n]; - g = h.minIdx[n]; - s = h.maxIdx[n]; - l = h.open[n]; - m = h.close[n]; - r = h.minX[n]; - q = h.minY[n]; - p = h.maxX[n]; - o = h.maxY[n] - } else { - d = h.startIdx[n]; - v = h.endIdx[n + 1]; - l = h.open[n]; - m = h.close[n]; - if (h.minY[n] <= h.minY[n + 1]) { - g = h.minIdx[n]; - r = h.minX[n]; - q = h.minY[n] - } else { - g = h.minIdx[n + 1]; - r = h.minX[n + 1]; - q = h.minY[n + 1] - } - if (h.maxY[n] >= h.maxY[n + 1]) { - s = h.maxIdx[n]; - p = h.maxX[n]; - o = h.maxY[n] - } else { - s = h.maxIdx[n + 1]; - p = h.maxX[n + 1]; - o = h.maxY[n + 1] - } - } - h.startIdx[u] = d; - h.endIdx[u] = v; - h.minIdx[u] = g; - h.maxIdx[u] = s; - h.open[u] = l; - h.close[u] = m; - h.minX[u] = r; - h.minY[u] = q; - h.maxX[u] = p; - h.maxY[u] = o; - u++ - } - h.map["double_" + f] = [e, u] - } - }, - none: Ext.emptyFn, - aggregateData: function(h, a, r, c, d) { - var b = h.length, - e = [], - s = [], - f = [], - q = [], - j = [], - p = [], - n = [], - o = [], - m = [], - k = [], - g = { - startIdx: e, - endIdx: s, - minIdx: f, - maxIdx: q, - open: j, - minX: p, - minY: n, - maxX: o, - maxY: m, - close: k - }, - l; - for (l = 0; l < b; l++) { - e[l] = l; - s[l] = l; - f[l] = l; - q[l] = l; - j[l] = a[l]; - p[l] = h[l]; - n[l] = c[l]; - o[l] = h[l]; - m[l] = r[l]; - k[l] = d[l] - } - g.map = { - original: [0, b] - }; - if (b) { - this[this.getStrategy()](g, b, h, a, r, c, d) - } - return g - }, - binarySearchMin: function(c, g, a, e) { - var b = this.dataX; - if (e <= b[c.startIdx[0]]) { - return g - } - if (e >= b[c.startIdx[a - 1]]) { - return a - 1 - } - while (g + 1 < a) { - var d = (g + a) >> 1, - f = b[c.startIdx[d]]; - if (f === e) { - return d - } else { - if (f < e) { - g = d - } else { - a = d - } - } - } - return g - }, - binarySearchMax: function(c, g, a, e) { - var b = this.dataX; - if (e <= b[c.endIdx[0]]) { - return g - } - if (e >= b[c.endIdx[a - 1]]) { - return a - 1 - } - while (g + 1 < a) { - var d = (g + a) >> 1, - f = b[c.endIdx[d]]; - if (f === e) { - return d - } else { - if (f < e) { - g = d - } else { - a = d - } - } - } - return a - }, - constructor: function(a) { - this.initConfig(a) - }, - setData: function(d, a, b, c, e) { - if (!b) { - e = c = b = a - } - this.dataX = d; - this.dataOpen = a; - this.dataHigh = b; - this.dataLow = c; - this.dataClose = e; - if (d.length === b.length && d.length === c.length) { - this.cache = this.aggregateData(d, a, b, c, e) - } - }, - getAggregation: function(d, k, i) { - if (!this.cache) { - return null - } - var c = Infinity, - g = this.dataX[this.dataX.length - 1] - this.dataX[0], - l = this.cache.map, - m = l.original, - a, e, j, b, f, h; - for (a in l) { - e = l[a]; - j = e[1] - e[0] - 1; - b = g / j; - if (i <= b && b < c) { - m = e; - c = b - } - } - f = Math.max(this.binarySearchMin(this.cache, m[0], m[1], d), m[0]); - h = Math.min(this.binarySearchMax(this.cache, m[0], m[1], k) + 1, m[1]); - return { - data: this.cache, - start: f, - end: h - } - } -}); -Ext.define("Ext.chart.series.sprite.Aggregative", { - extend: "Ext.chart.series.sprite.Cartesian", - requires: ["Ext.draw.LimitedCache", "Ext.draw.SegmentTree"], - inheritableStatics: { - def: { - processors: { - dataHigh: "data", - dataLow: "data", - dataClose: "data" - }, - aliases: { - dataOpen: "dataY" - }, - defaults: { - dataHigh: null, - dataLow: null, - dataClose: null - } - } - }, - config: { - aggregator: {} - }, - applyAggregator: function(b, a) { - return Ext.factory(b, Ext.draw.SegmentTree, a) - }, - constructor: function() { - this.callParent(arguments) - }, - processDataY: function() { - var d = this, - b = d.attr, - e = b.dataHigh, - a = b.dataLow, - f = b.dataClose, - c = b.dataY; - d.callParent(arguments); - if (b.dataX && c && c.length > 0) { - if (e) { - d.getAggregator().setData(b.dataX, b.dataY, e, a, f) - } else { - d.getAggregator().setData(b.dataX, b.dataY) - } - } - }, - getGapWidth: function() { - return 1 - }, - renderClipped: function(b, c, g, f) { - var e = this, - d = Math.min(g[0], g[2]), - a = Math.max(g[0], g[2]), - h = e.getAggregator() && e.getAggregator().getAggregation(d, a, (a - d) / f[2] * e.getGapWidth()); - if (h) { - e.dataStart = h.data.startIdx[h.start]; - e.dataEnd = h.data.endIdx[h.end - 1]; - e.renderAggregates(h.data, h.start, h.end, b, c, g, f) - } - } -}); -Ext.define("Ext.chart.series.sprite.CandleStick", { - alias: "sprite.candlestickSeries", - extend: "Ext.chart.series.sprite.Aggregative", - inheritableStatics: { - def: { - processors: { - raiseStyle: function(b, a) { - return Ext.merge({}, a || {}, b) - }, - dropStyle: function(b, a) { - return Ext.merge({}, a || {}, b) - }, - barWidth: "number", - padding: "number", - ohlcType: "enums(candlestick,ohlc)" - }, - defaults: { - raiseStyle: { - strokeStyle: "green", - fillStyle: "green" - }, - dropStyle: { - strokeStyle: "red", - fillStyle: "red" - }, - planar: false, - barWidth: 15, - padding: 3, - lineJoin: "miter", - miterLimit: 5, - ohlcType: "candlestick" - }, - triggers: { - raiseStyle: "raiseStyle", - dropStyle: "dropStyle" - }, - updaters: { - raiseStyle: function() { - this.raiseTemplate && this.raiseTemplate.setAttributes(this.attr.raiseStyle) - }, - dropStyle: function() { - this.dropTemplate && this.dropTemplate.setAttributes(this.attr.dropStyle) - } - } - } - }, - candlestick: function(i, c, a, e, h, f, b) { - var d = Math.min(c, h), - g = Math.max(c, h); - i.moveTo(f, e); - i.lineTo(f, g); - i.moveTo(f + b, g); - i.lineTo(f + b, d); - i.lineTo(f - b, d); - i.lineTo(f - b, g); - i.closePath(); - i.moveTo(f, a); - i.lineTo(f, d) - }, - ohlc: function(b, d, e, a, f, c, g) { - b.moveTo(c, e); - b.lineTo(c, a); - b.moveTo(c, d); - b.lineTo(c - g, d); - b.moveTo(c, f); - b.lineTo(c + g, f) - }, - constructor: function() { - this.callParent(arguments); - this.raiseTemplate = new Ext.draw.sprite.Rect({ - parent: this - }); - this.dropTemplate = new Ext.draw.sprite.Rect({ - parent: this - }) - }, - getGapWidth: function() { - var a = this.attr, - b = a.barWidth, - c = a.padding; - return b + c - }, - renderAggregates: function(d, c, b, t, u, z) { - var D = this, - s = this.attr, - j = s.dataX, - v = s.matrix, - e = v.getXX(), - r = v.getYY(), - l = v.getDX(), - h = v.getDY(), - o = s.barWidth / e, - C, k = s.ohlcType, - f = Math.round(o * 0.5 * e), - a = d.open, - y = d.close, - B = d.maxY, - p = d.minY, - q = d.startIdx, - m, g, E, n, A, x, w = s.lineWidth * t.devicePixelRatio / 2; - w -= Math.floor(w); - u.save(); - C = this.raiseTemplate; - C.useAttributes(u, z); - u.beginPath(); - for (x = c; x < b; x++) { - if (a[x] <= y[x]) { - m = Math.round(a[x] * r + h) + w; - g = Math.round(B[x] * r + h) + w; - E = Math.round(p[x] * r + h) + w; - n = Math.round(y[x] * r + h) + w; - A = Math.round(j[q[x]] * e + l) + w; - D[k](u, m, g, E, n, A, f) - } - } - u.fillStroke(C.attr); - u.restore(); - u.save(); - C = this.dropTemplate; - C.useAttributes(u, z); - u.beginPath(); - for (x = c; x < b; x++) { - if (a[x] > y[x]) { - m = Math.round(a[x] * r + h) + w; - g = Math.round(B[x] * r + h) + w; - E = Math.round(p[x] * r + h) + w; - n = Math.round(y[x] * r + h) + w; - A = Math.round(j[q[x]] * e + l) + w; - D[k](u, m, g, E, n, A, f) - } - } - u.fillStroke(C.attr); - u.restore() - } -}); -Ext.define("Ext.chart.series.CandleStick", { - extend: "Ext.chart.series.Cartesian", - requires: ["Ext.chart.series.sprite.CandleStick"], - alias: "series.candlestick", - type: "candlestick", - seriesType: "candlestickSeries", - config: { - openField: null, - highField: null, - lowField: null, - closeField: null - }, - fieldCategoryY: ["Open", "High", "Low", "Close"], - themeColorCount: function() { - return 2 - } -}); -Ext.define("Ext.chart.series.Polar", { - extend: "Ext.chart.series.Series", - config: { - rotation: 0, - radius: null, - center: [0, 0], - offsetX: 0, - offsetY: 0, - showInLegend: true, - xField: null, - yField: null, - angleField: null, - radiusField: null, - xAxis: null, - yAxis: null - }, - directions: ["X", "Y"], - fieldCategoryX: ["X"], - fieldCategoryY: ["Y"], - deprecatedConfigs: { - field: "angleField", - lengthField: "radiusField" - }, - constructor: function(b) { - var c = this, - a = c.getConfigurator(), - e = a.configs, - d; - if (b) { - for (d in c.deprecatedConfigs) { - if (d in b && !(b in e)) { - Ext.raise("'" + d + "' config has been deprecated. Please use the '" + c.deprecatedConfigs[d] + "' config instead.") - } - } - } - c.callParent([b]) - }, - getXField: function() { - return this.getAngleField() - }, - updateXField: function(a) { - this.setAngleField(a) - }, - getYField: function() { - return this.getRadiusField() - }, - updateYField: function(a) { - this.setRadiusField(a) - }, - applyXAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - applyYAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - getXRange: function() { - return [this.dataRange[0], this.dataRange[2]] - }, - getYRange: function() { - return [this.dataRange[1], this.dataRange[3]] - }, - themeColorCount: function() { - var c = this, - a = c.getStore(), - b = a && a.getCount() || 0; - return b - }, - isStoreDependantColorCount: true, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer(), - centerX: 0, - centerY: 0, - rotationCenterX: 0, - rotationCenterY: 0 - } - }, - applyRotation: function(a) { - return Ext.draw.sprite.AttributeParser.angle(a) - }, - updateRotation: function(a) { - var b = this.getSprites(); - if (b && b[0]) { - b[0].setAttributes({ - baseRotation: a - }) - } - } -}); -Ext.define("Ext.chart.series.Gauge", { - alias: "series.gauge", - extend: "Ext.chart.series.Polar", - type: "gauge", - seriesType: "pieslice", - requires: ["Ext.draw.sprite.Sector"], - config: { - needle: false, - needleLength: 90, - needleWidth: 4, - donut: 30, - showInLegend: false, - value: null, - colors: null, - sectors: null, - minimum: 0, - maximum: 100, - rotation: 0, - totalAngle: Math.PI / 2, - rect: [0, 0, 1, 1], - center: [0.5, 0.75], - radius: 0.5, - wholeDisk: false - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - updateNeedle: function(b) { - var a = this, - d = a.getSprites(), - c = a.valueToAngle(a.getValue()); - if (d && d.length) { - d[0].setAttributes({ - startAngle: (b ? c : 0), - endAngle: c, - strokeOpacity: (b ? 1 : 0), - lineWidth: (b ? a.getNeedleWidth() : 0) - }); - a.doUpdateStyles() - } - }, - themeColorCount: function() { - var c = this, - a = c.getStore(), - b = a && a.getCount() || 0; - return b + (c.getNeedle() ? 0 : 1) - }, - updateColors: function(a, b) { - var f = this, - h = f.getSectors(), - j = h && h.length, - e = f.getSprites(), - c = Ext.Array.clone(a), - g = a && a.length, - d; - if (!g || !a[0]) { - return - } - for (d = 0; d < j; d++) { - c[d + 1] = h[d].color || c[d + 1] || a[d % g] - } - if (e.length) { - e[0].setAttributes({ - strokeStyle: c[0] - }) - } - this.setSubStyle({ - fillStyle: c, - strokeStyle: c - }); - this.doUpdateStyles() - }, - updateRect: function(f) { - var d = this.getWholeDisk(), - c = d ? Math.PI : this.getTotalAngle() / 2, - g = this.getDonut() / 100, - e, b, a; - if (c <= Math.PI / 2) { - e = 2 * Math.sin(c); - b = 1 - g * Math.cos(c) - } else { - e = 2; - b = 1 - Math.cos(c) - } - a = Math.min(f[2] / e, f[3] / b); - this.setRadius(a); - this.setCenter([f[2] / 2, a + (f[3] - b * a) / 2]) - }, - updateCenter: function(a) { - this.setStyle({ - centerX: a[0], - centerY: a[1], - rotationCenterX: a[0], - rotationCenterY: a[1] - }); - this.doUpdateStyles() - }, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a - (this.getTotalAngle() + Math.PI) / 2 - }); - this.doUpdateStyles() - }, - doUpdateShape: function(b, f) { - var a, d = this.getSectors(), - c = (d && d.length) || 0, - e = this.getNeedleLength() / 100; - a = [b * e, b]; - while (c--) { - a.push(b) - } - this.setSubStyle({ - endRho: a, - startRho: b / 100 * f - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - var b = this.getDonut(); - this.doUpdateShape(a, b) - }, - updateDonut: function(b) { - var a = this.getRadius(); - this.doUpdateShape(a, b) - }, - valueToAngle: function(a) { - a = this.applyValue(a); - return this.getTotalAngle() * (a - this.getMinimum()) / (this.getMaximum() - this.getMinimum()) - }, - applyValue: function(a) { - return Math.min(this.getMaximum(), Math.max(a, this.getMinimum())) - }, - updateValue: function(b) { - var a = this, - c = a.getNeedle(), - e = a.valueToAngle(b), - d = a.getSprites(); - d[0].rendererData.value = b; - d[0].setAttributes({ - startAngle: (c ? e : 0), - endAngle: e - }); - a.doUpdateStyles() - }, - processData: function() { - var f = this, - j = f.getStore(), - a, d, h, b, g, e = j && j.first(), - c, i; - if (e) { - c = f.getXField(); - if (c) { - i = e.get(c) - } - } - if (a = f.getXAxis()) { - d = a.getMinimum(); - h = a.getMaximum(); - b = a.getSprites()[0].fx; - g = b.getDuration(); - b.setDuration(0); - if (Ext.isNumber(d)) { - f.setMinimum(d) - } else { - a.setMinimum(f.getMinimum()) - } - if (Ext.isNumber(h)) { - f.setMaximum(h) - } else { - a.setMaximum(f.getMaximum()) - } - b.setDuration(g) - } - if (!Ext.isNumber(i)) { - i = f.getMinimum() - } - f.setValue(i) - }, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer(), - fx: { - customDurations: { - translationX: 0, - translationY: 0, - rotationCenterX: 0, - rotationCenterY: 0, - centerX: 0, - centerY: 0, - startRho: 0, - endRho: 0, - baseRotation: 0 - } - } - } - }, - normalizeSectors: function(f) { - var d = this, - c = (f && f.length) || 0, - b, e, g, a; - if (c) { - for (b = 0; b < c; b++) { - e = f[b]; - if (typeof e === "number") { - f[b] = { - start: (b > 0 ? f[b - 1].end : d.getMinimum()), - end: Math.min(e, d.getMaximum()) - }; - if (b == (c - 1) && f[b].end < d.getMaximum()) { - f[b + 1] = { - start: f[b].end, - end: d.getMaximum() - } - } - } else { - if (typeof e.start === "number") { - g = Math.max(e.start, d.getMinimum()) - } else { - g = (b > 0 ? f[b - 1].end : d.getMinimum()) - } - if (typeof e.end === "number") { - a = Math.min(e.end, d.getMaximum()) - } else { - a = d.getMaximum() - } - f[b].start = g; - f[b].end = a - } - } - } else { - f = [{ - start: d.getMinimum(), - end: d.getMaximum() - }] - } - return f - }, - getSprites: function() { - var j = this, - m = j.getStore(), - l = j.getValue(), - c, g; - if (!m && !Ext.isNumber(l)) { - return [] - } - var h = j.getChart(), - b = j.getAnimation() || h && h.getAnimation(), - f = j.sprites, - k = 0, - o, n, e, d, a = []; - if (f && f.length) { - f[0].setAnimation(b); - return f - } - d = { - store: m, - field: j.getXField(), - angleField: j.getXField(), - value: l, - series: j - }; - o = j.createSprite(); - o.setAttributes({ - zIndex: 10 - }, true); - o.rendererData = d; - o.rendererIndex = k++; - a.push(j.getNeedleWidth()); - j.getLabel().getTemplate().setField(true); - n = j.normalizeSectors(j.getSectors()); - for (c = 0, g = n.length; c < g; c++) { - e = { - startAngle: j.valueToAngle(n[c].start), - endAngle: j.valueToAngle(n[c].end), - label: n[c].label, - fillStyle: n[c].color, - strokeOpacity: 0, - doCallout: false, - labelOverflowPadding: -1 - }; - Ext.apply(e, n[c].style); - o = j.createSprite(); - o.rendererData = d; - o.rendererIndex = k++; - o.setAttributes(e, true); - a.push(e.lineWidth) - } - j.setSubStyle({ - lineWidth: a - }); - j.doUpdateStyles(); - return f - } -}); -Ext.define("Ext.chart.series.sprite.Line", { - alias: "sprite.lineSeries", - extend: "Ext.chart.series.sprite.Aggregative", - inheritableStatics: { - def: { - processors: { - smooth: "bool", - fillArea: "bool", - step: "bool", - preciseStroke: "bool", - xAxis: "default", - yCap: "default" - }, - defaults: { - smooth: false, - fillArea: false, - step: false, - preciseStroke: true, - xAxis: null, - yCap: Math.pow(2, 20), - yJump: 50 - }, - triggers: { - dataX: "dataX,bbox,smooth", - dataY: "dataY,bbox,smooth", - smooth: "smooth" - }, - updaters: { - smooth: function(a) { - var c = a.dataX, - b = a.dataY; - if (a.smooth && c && b && c.length > 2 && b.length > 2) { - this.smoothX = Ext.draw.Draw.spline(c); - this.smoothY = Ext.draw.Draw.spline(b) - } else { - delete this.smoothX; - delete this.smoothY - } - } - } - } - }, - list: null, - updatePlainBBox: function(d) { - var b = this.attr, - c = Math.min(0, b.dataMinY), - a = Math.max(0, b.dataMaxY); - d.x = b.dataMinX; - d.y = c; - d.width = b.dataMaxX - b.dataMinX; - d.height = a - c - }, - drawStrip: function(a, c) { - a.moveTo(c[0], c[1]); - for (var b = 2, d = c.length; b < d; b += 2) { - a.lineTo(c[b], c[b + 1]) - } - }, - drawStraightStroke: function(p, q, e, d, u, h) { - var w = this, - o = w.attr, - n = o.renderer, - g = o.step, - a = true, - l = { - type: "line", - smooth: false, - step: g - }, - m = [], - l, z, v, f, k, j, t, c, s, b, r; - for (r = 3; r < u.length; r += 3) { - t = u[r - 3]; - c = u[r - 2]; - k = u[r]; - j = u[r + 1]; - s = u[r + 3]; - b = u[r + 4]; - if (n) { - l.x = k; - l.y = j; - l.x0 = t; - l.y0 = c; - v = [w, l, w.rendererData, e + r / 3]; - z = Ext.callback(n, null, v, 0, w.getSeries()) - } - if (Ext.isNumber(k + j + t + c)) { - if (a) { - q.beginPath(); - q.moveTo(t, c); - m.push(t, c); - f = t; - a = false - } - } else { - continue - } - if (g) { - q.lineTo(k, c); - m.push(k, c) - } - q.lineTo(k, j); - m.push(k, j); - if (z || !(Ext.isNumber(s + b))) { - q.save(); - Ext.apply(q, z); - if (o.fillArea) { - q.lineTo(k, h); - q.lineTo(f, h); - q.closePath(); - q.fill() - } - q.beginPath(); - w.drawStrip(q, m); - m = []; - q.stroke(); - q.restore(); - q.beginPath(); - a = true - } - } - }, - calculateScale: function(c, a) { - var b = 0, - d = c; - while (d < a && c > 0) { - b++; - d += c >> b - } - return Math.pow(2, b > 0 ? b - 1 : b) - }, - drawSmoothStroke: function(u, v, c, b, C, f) { - var G = this, - t = G.attr, - d = t.step, - z = t.matrix, - s = t.renderer, - e = z.getXX(), - p = z.getYY(), - m = z.getDX(), - k = z.getDY(), - r = G.smoothX, - q = G.smoothY, - I = G.calculateScale(t.dataX.length, b), - o, F, n, E, h, g, B, a, A, w, H, D, l = { - type: "line", - smooth: true, - step: d - }; - v.beginPath(); - v.moveTo(r[c * 3] * e + m, q[c * 3] * p + k); - for (A = 0, w = c * 3 + 1; A < C.length - 3; A += 3, w += 3 * I) { - o = r[w] * e + m; - F = q[w] * p + k; - n = r[w + 1] * e + m; - E = q[w + 1] * p + k; - h = u.roundPixel(C[A + 3]); - g = C[A + 4]; - B = u.roundPixel(C[A]); - a = C[A + 1]; - if (s) { - l.x0 = B; - l.y0 = a; - l.cx1 = o; - l.cy1 = F; - l.cx2 = n; - l.cy2 = E; - l.x = h; - l.y = g; - D = [G, l, G.rendererData, c + A / 3 + 1]; - H = Ext.callback(s, null, D, 0, G.getSeries()); - v.save(); - Ext.apply(v, H) - } - if (t.fillArea) { - v.moveTo(B, a); - v.bezierCurveTo(o, F, n, E, h, g); - v.lineTo(h, f); - v.lineTo(B, f); - v.lineTo(B, a); - v.closePath(); - v.fill(); - v.beginPath() - } - v.moveTo(B, a); - v.bezierCurveTo(o, F, n, E, h, g); - v.stroke(); - v.moveTo(B, a); - v.closePath(); - if (s) { - v.restore() - } - v.beginPath(); - v.moveTo(h, g) - } - v.beginPath() - }, - drawLabel: function(k, i, h, o, a) { - var q = this, - n = q.attr, - e = q.getMarker("labels"), - d = e.getTemplate(), - m = q.labelCfg || (q.labelCfg = {}), - c = q.surfaceMatrix, - g, f, j = n.labelOverflowPadding, - l, b, r, p, s; - m.x = c.x(i, h); - m.y = c.y(i, h); - if (n.flipXY) { - m.rotationRads = Math.PI * 0.5 - } else { - m.rotationRads = 0 - } - m.text = k; - if (d.attr.renderer) { - p = [k, e, m, q.rendererData, o]; - r = Ext.callback(d.attr.renderer, null, p, 0, q.getSeries()); - if (typeof r === "string") { - m.text = r - } else { - if (typeof r === "object") { - if ("text" in r) { - m.text = r.text - } - s = true - } - } - } - b = q.getMarkerBBox("labels", o, true); - if (!b) { - q.putMarker("labels", m, o); - b = q.getMarkerBBox("labels", o, true) - } - l = b.height / 2; - g = i; - switch (d.attr.display) { - case "under": - f = h - l - j; - break; - case "rotate": - g += j; - f = h - j; - m.rotationRads = -Math.PI / 4; - break; - default: - f = h + l + j - } - m.x = c.x(g, f); - m.y = c.y(g, f); - if (s) { - Ext.apply(m, r) - } - q.putMarker("labels", m, o) - }, - drawMarker: function(j, h, d) { - var g = this, - e = g.attr, - f = e.renderer, - c = g.surfaceMatrix, - b = {}, - i, a; - if (f && g.getMarker("markers")) { - b.type = "marker"; - b.x = j; - b.y = h; - a = [g, b, g.rendererData, d]; - i = Ext.callback(f, null, a, 0, g.getSeries()); - if (i) { - Ext.apply(b, i) - } - } - b.translationX = c.x(j, h); - b.translationY = c.y(j, h); - delete b.x; - delete b.y; - g.putMarker("markers", b, d, !f) - }, - drawStroke: function(a, c, h, b, f, e) { - var d = this, - g = d.attr.smooth && d.smoothX && d.smoothY; - if (g) { - d.drawSmoothStroke(a, c, h, b, f, e) - } else { - d.drawStraightStroke(a, c, h, b, f, e) - } - }, - renderAggregates: function(B, w, l, N, o, I, D) { - var m = this, - k = m.attr, - s = k.dataX, - r = k.dataY, - h = k.labels, - v = k.xAxis, - a = k.yCap, - g = k.smooth && m.smoothX && m.smoothY, - d = h && m.getMarker("labels"), - t = m.getMarker("markers"), - E = k.matrix, - u = N.devicePixelRatio, - C = E.getXX(), - f = E.getYY(), - c = E.getDX(), - b = E.getDY(), - q = m.list || (m.list = []), - F = B.minX, - e = B.maxX, - j = B.minY, - P = B.maxY, - U = B.startIdx, - S = true, - Q, T, L, K, R, G; - m.rendererData = { - store: m.getStore() - }; - q.length = 0; - for (R = w; R < l; R++) { - var O = F[R], - p = e[R], - M = j[R], - n = P[R]; - if (O < p) { - q.push(O * C + c, M * f + b, U[R]); - q.push(p * C + c, n * f + b, U[R]) - } else { - if (O > p) { - q.push(p * C + c, n * f + b, U[R]); - q.push(O * C + c, M * f + b, U[R]) - } else { - q.push(p * C + c, n * f + b, U[R]) - } - } - } - if (q.length) { - for (R = 0; R < q.length; R += 3) { - L = q[R]; - K = q[R + 1]; - if (Ext.isNumber(L + K)) { - if (K > a) { - K = a - } else { - if (K < -a) { - K = -a - } - } - q[R + 1] = K - } else { - S = false; - continue - } - G = q[R + 2]; - if (t) { - m.drawMarker(L, K, G) - } - if (d && h[G]) { - m.drawLabel(h[G], L, K, G, D) - } - } - m.isContinuousLine = S; - if (g && !S) { - Ext.raise("Line smoothing in only supported for gapless data, where all data points are finite numbers.") - } - if (v) { - T = v.getAlignment() === "vertical"; - if (Ext.isNumber(v.floatingAtCoord)) { - Q = (T ? D[2] : D[3]) - v.floatingAtCoord - } else { - Q = T ? D[0] : D[1] - } - } else { - Q = k.flipXY ? D[0] : D[1] - } - if (k.preciseStroke) { - if (k.fillArea) { - o.fill() - } - if (k.transformFillStroke) { - k.inverseMatrix.toContext(o) - } - m.drawStroke(N, o, w, l, q, Q); - if (k.transformFillStroke) { - k.matrix.toContext(o) - } - o.stroke() - } else { - m.drawStroke(N, o, w, l, q, Q); - if (S && g && k.fillArea && !k.renderer) { - var A = s[s.length - 1] * C + c + u, - z = r[r.length - 1] * f + b, - J = s[0] * C + c - u, - H = r[0] * f + b; - o.lineTo(A, z); - o.lineTo(A, Q - k.lineWidth); - o.lineTo(J, Q - k.lineWidth); - o.lineTo(J, H) - } - if (k.transformFillStroke) { - k.matrix.toContext(o) - } - if (k.fillArea) { - o.fillStroke(k, true) - } else { - o.stroke(true) - } - } - } - } -}); -Ext.define("Ext.chart.series.Line", { - extend: "Ext.chart.series.Cartesian", - alias: "series.line", - type: "line", - seriesType: "lineSeries", - requires: ["Ext.chart.series.sprite.Line"], - config: { - selectionTolerance: 20, - smooth: false, - step: false, - fill: undefined, - aggregator: { - strategy: "double" - } - }, - defaultSmoothness: 3, - overflowBuffer: 1, - themeMarkerCount: function() { - return 1 - }, - getDefaultSpriteConfig: function() { - var d = this, - e = d.callParent(arguments), - c = Ext.apply({}, d.getStyle()), - b, a = false; - if (typeof d.config.fill != "undefined") { - if (d.config.fill) { - a = true; - if (typeof c.fillStyle == "undefined") { - if (typeof c.strokeStyle == "undefined") { - b = d.getStyleWithTheme(); - c.fillStyle = b.fillStyle; - c.strokeStyle = b.strokeStyle - } else { - c.fillStyle = c.strokeStyle - } - } - } - } else { - if (c.fillStyle) { - a = true - } - } - if (!a) { - delete c.fillStyle - } - c = Ext.apply(e || {}, c); - return Ext.apply(c, { - fillArea: a, - step: d.config.step, - smooth: d.config.smooth, - selectionTolerance: d.config.selectionTolerance - }) - }, - updateStep: function(b) { - var a = this.getSprites()[0]; - if (a && a.attr.step !== b) { - a.setAttributes({ - step: b - }) - } - }, - updateFill: function(b) { - var a = this.getSprites()[0]; - if (a && a.attr.fillArea !== b) { - a.setAttributes({ - fillArea: b - }) - } - }, - updateSmooth: function(a) { - var b = this.getSprites()[0]; - if (b && b.attr.smooth !== a) { - b.setAttributes({ - smooth: a - }) - } - } -}); -Ext.define("Ext.chart.series.sprite.PieSlice", { - extend: "Ext.draw.sprite.Sector", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - alias: "sprite.pieslice", - inheritableStatics: { - def: { - processors: { - doCallout: "bool", - label: "string", - rotateLabels: "bool", - labelOverflowPadding: "number", - renderer: "default" - }, - defaults: { - doCallout: true, - rotateLabels: true, - label: "", - labelOverflowPadding: 10, - renderer: null - } - } - }, - config: { - rendererData: null, - rendererIndex: 0, - series: null - }, - setGradientBBox: function(q, k) { - var j = this, - i = j.attr, - g = (i.fillStyle && i.fillStyle.isGradient) || (i.strokeStyle && i.strokeStyle.isGradient); - if (g && !i.constrainGradients) { - var b = j.getMidAngle(), - d = i.margin, - e = i.centerX, - c = i.centerY, - a = i.endRho, - l = i.matrix, - o = l.getScaleX(), - n = l.getScaleY(), - m = o * a, - f = n * a, - p = { - width: m + m, - height: f + f - }; - if (d) { - e += d * Math.cos(b); - c += d * Math.sin(b) - } - p.x = l.x(e, c) - m; - p.y = l.y(e, c) - f; - q.setGradientBBox(p) - } else { - j.callParent([q, k]) - } - }, - render: function(b, c, g, f) { - var e = this, - a = e.attr, - h = {}, - d; - if (a.renderer) { - h = { - type: "sector", - text: a.text, - centerX: a.centerX, - centerY: a.centerY, - margin: a.margin, - startAngle: Math.min(a.startAngle, a.endAngle), - endAngle: Math.max(a.startAngle, a.endAngle), - startRho: Math.min(a.startRho, a.endRho), - endRho: Math.max(a.startRho, a.endRho) - }; - d = Ext.callback(a.renderer, null, [e, h, e.rendererData, e.rendererIndex], 0, e.getSeries()); - e.setAttributes(d); - e.useAttributes(c, g) - } - e.callParent([b, c, g, f]); - if (a.label && e.getMarker("labels")) { - e.placeLabel() - } - }, - placeLabel: function() { - var z = this, - s = z.attr, - r = s.attributeId, - t = Math.min(s.startAngle, s.endAngle), - p = Math.max(s.startAngle, s.endAngle), - k = (t + p) * 0.5, - n = s.margin, - h = s.centerX, - g = s.centerY, - f = Math.sin(k), - c = Math.cos(k), - v = Math.min(s.startRho, s.endRho) + n, - m = Math.max(s.startRho, s.endRho) + n, - l = (v + m) * 0.5, - b = z.surfaceMatrix, - o = z.labelCfg || (z.labelCfg = {}), - e = z.getMarker("labels"), - d = e.getTemplate(), - a = d.getCalloutLine(), - q = a && a.length || 40, - u, j, i, A, w; - b.appendMatrix(s.matrix); - o.text = s.label; - j = h + c * l; - i = g + f * l; - o.x = b.x(j, i); - o.y = b.y(j, i); - j = h + c * m; - i = g + f * m; - o.calloutStartX = b.x(j, i); - o.calloutStartY = b.y(j, i); - j = h + c * (m + q); - i = g + f * (m + q); - o.calloutPlaceX = b.x(j, i); - o.calloutPlaceY = b.y(j, i); - if (!s.rotateLabels) { - o.rotationRads = 0 - } else { - switch (d.attr.orientation) { - case "horizontal": - o.rotationRads = k + Math.atan2(b.y(1, 0) - b.y(0, 0), b.x(1, 0) - b.x(0, 0)) + Math.PI / 2; - break; - case "vertical": - o.rotationRads = k + Math.atan2(b.y(1, 0) - b.y(0, 0), b.x(1, 0) - b.x(0, 0)); - break - } - } - o.calloutColor = (a && a.color) || z.attr.fillStyle; - if (a) { - if (a.width) { - o.calloutWidth = a.width - } - } else { - o.calloutHasLine = false - } - o.globalAlpha = s.globalAlpha * s.fillOpacity; - o.hidden = (s.startAngle == s.endAngle); - if (d.attr.renderer) { - w = [z.attr.label, e, o, z.rendererData, z.rendererIndex]; - A = Ext.callback(d.attr.renderer, null, w, 0, z.getSeries()); - if (typeof A === "string") { - o.text = A - } else { - Ext.apply(o, A) - } - } - z.putMarker("labels", o, r); - u = z.getMarkerBBox("labels", r, true); - if (u) { - if (s.doCallout) { - if (d.attr.display === "outside") { - z.putMarker("labels", { - callout: 1 - }, r) - } else { - if (d.attr.display === "inside") { - z.putMarker("labels", { - callout: 0 - }, r) - } else { - z.putMarker("labels", { - callout: 1 - z.sliceContainsLabel(s, u) - }, r) - } - } - } else { - z.putMarker("labels", { - globalAlpha: z.sliceContainsLabel(s, u) - }, r) - } - } - }, - sliceContainsLabel: function(d, f) { - var e = d.labelOverflowPadding, - h = (d.endRho + d.startRho) / 2, - g = h + (f.width + e) / 2, - i = h - (f.width + e) / 2, - j, c, b, a; - if (e < 0) { - return 1 - } - if (f.width + e * 2 > (d.endRho - d.startRho)) { - return 0 - } - c = Math.sqrt(d.endRho * d.endRho - g * g); - b = Math.sqrt(d.endRho * d.endRho - i * i); - j = Math.abs(d.endAngle - d.startAngle); - a = (j > Math.PI / 2 ? i : Math.abs(Math.tan(j / 2)) * i); - if (f.height + e * 2 > Math.min(c, b, a) * 2) { - return 0 - } - return 1 - } -}); -Ext.define("Ext.chart.series.Pie", { - extend: "Ext.chart.series.Polar", - requires: ["Ext.chart.series.sprite.PieSlice"], - type: "pie", - alias: "series.pie", - seriesType: "pieslice", - config: { - donut: 0, - rotation: 0, - clockwise: true, - totalAngle: 2 * Math.PI, - hidden: [], - radiusFactor: 100, - highlightCfg: { - margin: 20 - }, - style: {} - }, - directions: ["X"], - applyLabel: function(a, b) { - if (Ext.isObject(a) && !Ext.isString(a.orientation)) { - Ext.apply(a = Ext.Object.chain(a), { - orientation: "vertical" - }) - } - return this.callParent([a, b]) - }, - updateLabelData: function() { - var h = this, - j = h.getStore(), - g = j.getData().items, - e = h.getSprites(), - a = h.getLabel().getTemplate().getField(), - d = h.getHidden(), - b, f, c, k; - if (e.length && a) { - c = []; - for (b = 0, f = g.length; b < f; b++) { - c.push(g[b].get(a)) - } - for (b = 0, f = e.length; b < f; b++) { - k = e[b]; - k.setAttributes({ - label: c[b] - }); - k.putMarker("labels", { - hidden: d[b] - }, k.attr.attributeId) - } - } - }, - coordinateX: function() { - var t = this, - f = t.getStore(), - q = f.getData().items, - c = q.length, - b = t.getXField(), - e = t.getYField(), - l, a = 0, - m, k, s = 0, - o = t.getHidden(), - d = [], - p, g = 0, - h = t.getTotalAngle(), - r = t.getClockwise() ? 1 : -1, - j = t.getSprites(), - n; - if (!j) { - return - } - for (p = 0; p < c; p++) { - l = Math.abs(Number(q[p].get(b))) || 0; - k = e && Math.abs(Number(q[p].get(e))) || 0; - if (!o[p]) { - a += l; - if (k > s) { - s = k - } - } - d[p] = a; - if (p >= o.length) { - o[p] = false - } - } - o.length = c; - t.maxY = s; - if (a !== 0) { - m = h / a - } - for (p = 0; p < c; p++) { - j[p].setAttributes({ - startAngle: g, - endAngle: g = (m ? r * d[p] * m : 0), - globalAlpha: 1 - }) - } - if (c < t.sprites.length) { - for (p = c; p < t.sprites.length; p++) { - n = t.sprites[p]; - n.getMarker("labels").clear(n.getId()); - n.releaseMarker("labels"); - n.destroy() - } - t.sprites.length = c - } - for (p = c; p < t.sprites.length; p++) { - j[p].setAttributes({ - startAngle: h, - endAngle: h, - globalAlpha: 0 - }) - } - t.getChart().refreshLegendStore() - }, - updateCenter: function(a) { - this.setStyle({ - translationX: a[0] + this.getOffsetX(), - translationY: a[1] + this.getOffsetY() - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - this.setStyle({ - startRho: a * this.getDonut() * 0.01, - endRho: a * this.getRadiusFactor() * 0.01 - }); - this.doUpdateStyles() - }, - getStyleByIndex: function(c) { - var g = this, - j = g.getStore(), - k = j.getAt(c), - f = g.getYField(), - d = g.getRadius(), - a = {}, - e, b, h; - if (k) { - h = f && Math.abs(Number(k.get(f))) || 0; - e = d * g.getDonut() * 0.01; - b = d * g.getRadiusFactor() * 0.01; - a = g.callParent([c]); - a.startRho = e; - a.endRho = g.maxY ? (e + (b - e) * h / g.maxY) : b - } - return a - }, - updateDonut: function(b) { - var a = this.getRadius(); - this.setStyle({ - startRho: a * b * 0.01, - endRho: a * this.getRadiusFactor() * 0.01 - }); - this.doUpdateStyles() - }, - rotationOffset: -Math.PI / 2, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a + this.rotationOffset - }); - this.doUpdateStyles() - }, - updateTotalAngle: function(a) { - this.processData() - }, - getSprites: function() { - var k = this, - h = k.getChart(), - n = k.getStore(); - if (!h || !n) { - return [] - } - k.getColors(); - k.getSubStyle(); - var j = n.getData().items, - b = j.length, - d = k.getAnimation() || h && h.getAnimation(), - g = k.sprites, - o, l = 0, - f, e, c = false, - m = k.getLabel(), - a = m.getTemplate(); - f = { - store: n, - field: k.getXField(), - angleField: k.getXField(), - radiusField: k.getYField(), - series: k - }; - for (e = 0; e < b; e++) { - o = g[e]; - if (!o) { - o = k.createSprite(); - if (k.getHighlight()) { - o.config.highlight = k.getHighlight(); - o.addModifier("highlight", true) - } - if (a.getField()) { - a.setAttributes({ - labelOverflowPadding: k.getLabelOverflowPadding() - }); - a.fx.setCustomDurations({ - callout: 200 - }) - } - o.setAttributes(k.getStyleByIndex(e)); - o.rendererData = f; - o.rendererIndex = l++; - c = true - } - o.setAnimation(d) - } - if (c) { - k.doUpdateStyles() - } - return k.sprites - }, - betweenAngle: function(d, f, c) { - var e = Math.PI * 2, - g = this.rotationOffset; - if (!this.getClockwise()) { - d *= -1; - f *= -1; - c *= -1; - f -= g; - c -= g - } else { - f += g; - c += g - } - d -= f; - c -= f; - d %= e; - c %= e; - d += e; - c += e; - d %= e; - c %= e; - return d < c || c === 0 - }, - getItemForAngle: function(a) { - var h = this, - f = h.getSprites(), - d; - a %= Math.PI * 2; - while (a < 0) { - a += Math.PI * 2 - } - if (f) { - var j = h.getStore(), - g = j.getData().items, - c = h.getHidden(), - b = 0, - e = j.getCount(); - for (; b < e; b++) { - if (!c[b]) { - d = f[b].attr; - if (d.startAngle <= a && d.endAngle >= a) { - return { - series: h, - sprite: f[b], - index: b, - record: g[b], - field: h.getXField() - } - } - } - } - } - return null - }, - getItemForPoint: function(f, e) { - var t = this, - c = t.getSprites(); - if (c) { - var s = t.getCenter(), - q = t.getOffsetX(), - p = t.getOffsetY(), - j = f - s[0] + q, - h = e - s[1] + p, - b = t.getStore(), - g = t.getDonut(), - o = b.getData().items, - r = Math.atan2(h, j) - t.getRotation(), - a = Math.sqrt(j * j + h * h), - l = t.getRadius() * g * 0.01, - m = t.getHidden(), - n, d, k; - for (n = 0, d = o.length; n < d; n++) { - if (!m[n]) { - k = c[n].attr; - if (a >= l + k.margin && a <= k.endRho + k.margin) { - if (t.betweenAngle(r, k.startAngle, k.endAngle)) { - return { - series: t, - sprite: c[n], - index: n, - record: o[n], - field: t.getXField() - } - } - } - } - } - return null - } - }, - provideLegendInfo: function(f) { - var h = this, - j = h.getStore(); - if (j) { - var g = j.getData().items, - b = h.getLabel().getTemplate().getField(), - c = h.getXField(), - e = h.getHidden(), - d, a, k; - for (d = 0; d < g.length; d++) { - a = h.getStyleByIndex(d); - k = a.fillStyle; - if (Ext.isObject(k)) { - k = k.stops && k.stops[0].color - } - f.push({ - name: b ? String(g[d].get(b)) : c + " " + d, - mark: k || a.strokeStyle || "black", - disabled: e[d], - series: h.getId(), - index: d - }) - } - } - } -}); -Ext.define("Ext.chart.series.sprite.Pie3DPart", { - extend: "Ext.draw.sprite.Path", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - alias: "sprite.pie3dPart", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - margin: "number", - thickness: "number", - bevelWidth: "number", - distortion: "number", - baseColor: "color", - colorSpread: "number", - baseRotation: "number", - part: "enums(top,bottom,start,end,innerFront,innerBack,outerFront,outerBack)", - label: "string" - }, - aliases: { - rho: "endRho" - }, - triggers: { - centerX: "path,bbox", - centerY: "path,bbox", - startAngle: "path,partZIndex", - endAngle: "path,partZIndex", - startRho: "path", - endRho: "path,bbox", - margin: "path,bbox", - thickness: "path", - distortion: "path", - baseRotation: "path,partZIndex", - baseColor: "partZIndex,partColor", - colorSpread: "partColor", - part: "path,partZIndex", - globalAlpha: "canvas,alpha" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: Math.PI * 2, - endAngle: Math.PI * 2, - startRho: 0, - endRho: 150, - margin: 0, - thickness: 35, - distortion: 0.5, - baseRotation: 0, - baseColor: "white", - colorSpread: 1, - miterLimit: 1, - bevelWidth: 5, - strokeOpacity: 0, - part: "top", - label: "" - }, - updaters: { - alpha: "alphaUpdater", - partColor: "partColorUpdater", - partZIndex: "partZIndexUpdater" - } - } - }, - bevelParams: [], - constructor: function(a) { - this.callParent([a]); - this.bevelGradient = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: "rgba(255,255,255,0)" - }, { - offset: 0.7, - color: "rgba(255,255,255,0.6)" - }, { - offset: 1, - color: "rgba(255,255,255,0)" - }] - }) - }, - alphaUpdater: function(a) { - var d = this, - c = a.globalAlpha, - b = d.oldOpacity; - if (c !== b && (c === 1 || b === 1)) { - d.scheduleUpdater(a, "path", ["globalAlpha"]); - d.oldOpacity = c - } - }, - partColorUpdater: function(a) { - var d = Ext.draw.Color.fly(a.baseColor), - b = d.toString(), - e = a.colorSpread, - c; - switch (a.part) { - case "top": - c = new Ext.draw.gradient.Radial({ - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - }, - stops: [{ - offset: 0, - color: d.createLighter(0.1 * e) - }, { - offset: 1, - color: d.createDarker(0.1 * e) - }] - }); - break; - case "bottom": - c = new Ext.draw.gradient.Radial({ - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - }, - stops: [{ - offset: 0, - color: d.createDarker(0.2 * e) - }, { - offset: 1, - color: d.toString() - }] - }); - break; - case "outerFront": - case "outerBack": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.15 * e).toString() - }, { - offset: 0.3, - color: b - }, { - offset: 0.8, - color: d.createLighter(0.2 * e).toString() - }, { - offset: 1, - color: d.createDarker(0.25 * e).toString() - }] - }); - break; - case "start": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 1, - color: d.createLighter(0.2 * e).toString() - }] - }); - break; - case "end": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 1, - color: d.createLighter(0.2 * e).toString() - }] - }); - break; - case "innerFront": - case "innerBack": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 0.2, - color: d.createLighter(0.2 * e).toString() - }, { - offset: 0.7, - color: b - }, { - offset: 1, - color: d.createDarker(0.1 * e).toString() - }] - }); - break - } - a.fillStyle = c; - a.canvasAttributes.fillStyle = c - }, - partZIndexUpdater: function(a) { - var c = Ext.draw.sprite.AttributeParser.angle, - e = a.baseRotation, - d = a.startAngle, - b = a.endAngle, - f; - switch (a.part) { - case "top": - a.zIndex = 5; - break; - case "outerFront": - d = c(d + e); - b = c(b + e); - if (d >= 0 && b < 0) { - f = Math.sin(d) - } else { - if (d <= 0 && b > 0) { - f = Math.sin(b) - } else { - if (d >= 0 && b > 0) { - if (d > b) { - f = 0 - } else { - f = Math.max(Math.sin(d), Math.sin(b)) - } - } else { - f = 1 - } - } - } - a.zIndex = 4 + f; - break; - case "outerBack": - a.zIndex = 1; - break; - case "start": - a.zIndex = 4 + Math.sin(c(d + e)); - break; - case "end": - a.zIndex = 4 + Math.sin(c(b + e)); - break; - case "innerFront": - a.zIndex = 2; - break; - case "innerBack": - a.zIndex = 4 + Math.sin(c((d + b) / 2 + e)); - break; - case "bottom": - a.zIndex = 0; - break - } - a.dirtyZIndex = true - }, - updatePlainBBox: function(k) { - var f = this.attr, - a = f.part, - b = f.baseRotation, - e = f.centerX, - d = f.centerY, - j, c, i, h, g, l; - if (a === "start") { - c = f.startAngle + b - } else { - if (a === "end") { - c = f.endAngle + b - } - } - if (Ext.isNumber(c)) { - g = Math.sin(c); - l = Math.cos(c); - i = Math.min(e + l * f.startRho, e + l * f.endRho); - h = d + g * f.startRho * f.distortion; - k.x = i; - k.y = h; - k.width = l * (f.endRho - f.startRho); - k.height = f.thickness + g * (f.endRho - f.startRho) * 2; - return - } - if (a === "innerFront" || a === "innerBack") { - j = f.startRho - } else { - j = f.endRho - } - k.width = j * 2; - k.height = j * f.distortion * 2 + f.thickness; - k.x = f.centerX - j; - k.y = f.centerY - j * f.distortion - }, - updateTransformedBBox: function(a) { - if (this.attr.part === "start" || this.attr.part === "end") { - return this.callParent(arguments) - } - return this.updatePlainBBox(a) - }, - updatePath: function(a) { - if (!this.attr.globalAlpha) { - return - } - if (this.attr.endAngle < this.attr.startAngle) { - return - } - this[this.attr.part + "Renderer"](a) - }, - render: function(b, c) { - var d = this, - a = d.attr; - if (!a.globalAlpha) { - return - } - d.callParent([b, c]); - d.bevelRenderer(b, c); - if (a.label && d.getMarker("labels")) { - d.placeLabel() - } - }, - placeLabel: function() { - var z = this, - u = z.attr, - t = u.attributeId, - p = u.margin, - c = u.distortion, - i = u.centerX, - h = u.centerY, - j = u.baseRotation, - v = u.startAngle + j, - r = u.endAngle + j, - m = (v + r) / 2, - w = u.startRho + p, - o = u.endRho + p, - n = (w + o) / 2, - a = Math.sin(m), - b = Math.cos(m), - e = z.surfaceMatrix, - g = z.getMarker("labels"), - f = g.getTemplate(), - d = f.getCalloutLine(), - s = d && d.length || 40, - q = {}, - l, k; - e.appendMatrix(u.matrix); - q.text = u.label; - l = i + b * n; - k = h + a * n * c; - q.x = e.x(l, k); - q.y = e.y(l, k); - l = i + b * o; - k = h + a * o * c; - q.calloutStartX = e.x(l, k); - q.calloutStartY = e.y(l, k); - l = i + b * (o + s); - k = h + a * (o + s) * c; - q.calloutPlaceX = e.x(l, k); - q.calloutPlaceY = e.y(l, k); - q.calloutWidth = 2; - z.putMarker("labels", q, t); - z.putMarker("labels", { - callout: 1 - }, t) - }, - bevelRenderer: function(b, c) { - var f = this, - a = f.attr, - e = a.bevelWidth, - g = f.bevelParams, - d; - for (d = 0; d < g.length; d++) { - c.beginPath(); - c.ellipse.apply(c, g[d]); - c.save(); - c.lineWidth = e; - c.strokeOpacity = e ? 1 : 0; - c.strokeGradient = f.bevelGradient; - c.stroke(a); - c.restore() - } - }, - lidRenderer: function(o, m) { - var k = this.attr, - g = k.margin, - c = k.distortion, - i = k.centerX, - h = k.centerY, - f = k.baseRotation, - j = k.startAngle + f, - e = k.endAngle + f, - d = (j + e) / 2, - l = k.startRho, - b = k.endRho, - n = Math.sin(e), - a = Math.cos(e); - i += Math.cos(d) * g; - h += Math.sin(d) * g * c; - o.ellipse(i, h + m, l, l * c, 0, j, e, false); - o.lineTo(i + a * b, h + m + n * b * c); - o.ellipse(i, h + m, b, b * c, 0, e, j, true); - o.closePath() - }, - topRenderer: function(a) { - this.lidRenderer(a, 0) - }, - bottomRenderer: function(b) { - var a = this.attr; - if (a.globalAlpha < 1 || a.shadowColor !== Ext.draw.Color.RGBA_NONE) { - this.lidRenderer(b, a.thickness) - } - }, - sideRenderer: function(l, s) { - var o = this.attr, - k = o.margin, - g = o.centerX, - f = o.centerY, - e = o.distortion, - h = o.baseRotation, - p = o.startAngle + h, - m = o.endAngle + h, - a = o.thickness, - q = o.startRho, - j = o.endRho, - r = (s === "start" && p) || (s === "end" && m), - b = Math.sin(r), - d = Math.cos(r), - c = o.globalAlpha < 1, - n = s === "start" && d < 0 || s === "end" && d > 0 || c, - i; - if (n) { - i = (p + m) / 2; - g += Math.cos(i) * k; - f += Math.sin(i) * k * e; - l.moveTo(g + d * q, f + b * q * e); - l.lineTo(g + d * j, f + b * j * e); - l.lineTo(g + d * j, f + b * j * e + a); - l.lineTo(g + d * q, f + b * q * e + a); - l.closePath() - } - }, - startRenderer: function(a) { - this.sideRenderer(a, "start") - }, - endRenderer: function(a) { - this.sideRenderer(a, "end") - }, - rimRenderer: function(q, e, o, j) { - var w = this, - s = w.attr, - p = s.margin, - h = s.centerX, - g = s.centerY, - d = s.distortion, - i = s.baseRotation, - t = Ext.draw.sprite.AttributeParser.angle, - u = s.startAngle + i, - r = s.endAngle + i, - k = t((u + r) / 2), - a = s.thickness, - b = s.globalAlpha < 1, - c, n, v; - w.bevelParams = []; - u = t(u); - r = t(r); - h += Math.cos(k) * p; - g += Math.sin(k) * p * d; - c = u >= 0 && r >= 0; - n = u <= 0 && r <= 0; - - function l() { - q.ellipse(h, g + a, e, e * d, 0, Math.PI, u, true); - q.lineTo(h + Math.cos(u) * e, g + Math.sin(u) * e * d); - v = [h, g, e, e * d, 0, u, Math.PI, false]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function f() { - q.ellipse(h, g + a, e, e * d, 0, 0, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, 0, true]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function x() { - q.ellipse(h, g + a, e, e * d, 0, Math.PI, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, Math.PI, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function m() { - q.ellipse(h, g + a, e, e * d, 0, u, 0, false); - q.lineTo(h + e, g); - v = [h, g, e, e * d, 0, 0, u, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - if (j) { - if (!o || b) { - if (u >= 0 && r < 0) { - l() - } else { - if (u <= 0 && r > 0) { - f() - } else { - if (u <= 0 && r < 0) { - if (u > r) { - q.ellipse(h, g + a, e, e * d, 0, 0, Math.PI, false); - q.lineTo(h - e, g); - v = [h, g, e, e * d, 0, Math.PI, 0, true]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } else { - if (u > r) { - l(); - f() - } else { - v = [h, g, e, e * d, 0, u, r, false]; - if (c && !o || n && o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d + a); - q.ellipse(h, g + a, e, e * d, 0, r, u, true); - q.closePath() - } - } - } - } - } - } else { - if (o || b) { - if (u >= 0 && r < 0) { - x() - } else { - if (u <= 0 && r > 0) { - m() - } else { - if (u <= 0 && r < 0) { - if (u > r) { - x(); - m() - } else { - q.ellipse(h, g + a, e, e * d, 0, u, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, u, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } else { - if (u > r) { - q.ellipse(h, g + a, e, e * d, 0, -Math.PI, 0, false); - q.lineTo(h + e, g); - v = [h, g, e, e * d, 0, 0, -Math.PI, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } - } - } - } - } - }, - innerFrontRenderer: function(a) { - this.rimRenderer(a, this.attr.startRho, true, true) - }, - innerBackRenderer: function(a) { - this.rimRenderer(a, this.attr.startRho, true, false) - }, - outerFrontRenderer: function(a) { - this.rimRenderer(a, this.attr.endRho, false, true) - }, - outerBackRenderer: function(a) { - this.rimRenderer(a, this.attr.endRho, false, false) - } -}); -Ext.define("Ext.draw.PathUtil", function() { - var a = Math.abs, - c = Math.pow, - e = Math.cos, - b = Math.acos, - d = Math.sqrt, - f = Math.PI; - return { - singleton: true, - requires: ["Ext.draw.overrides.Path", "Ext.draw.overrides.sprite.Path", "Ext.draw.overrides.sprite.Instancing", "Ext.draw.overrides.Surface"], - cubicRoots: function(m) { - var z = m[0], - x = m[1], - w = m[2], - v = m[3]; - if (z === 0) { - return this.quadraticRoots(x, w, v) - } - var s = x / z, - r = w / z, - q = v / z, - k = (3 * r - c(s, 2)) / 9, - j = (9 * s * r - 27 * q - 2 * c(s, 3)) / 54, - p = c(k, 3) + c(j, 2), - n = [], - h, g, o, l, u, y = Ext.Number.sign; - if (p >= 0) { - h = y(j + d(p)) * c(a(j + d(p)), 1 / 3); - g = y(j - d(p)) * c(a(j - d(p)), 1 / 3); - n[0] = -s / 3 + (h + g); - n[1] = -s / 3 - (h + g) / 2; - n[2] = n[1]; - o = a(d(3) * (h - g) / 2); - if (o !== 0) { - n[1] = -1; - n[2] = -1 - } - } else { - l = b(j / d(-c(k, 3))); - n[0] = 2 * d(-k) * e(l / 3) - s / 3; - n[1] = 2 * d(-k) * e((l + 2 * f) / 3) - s / 3; - n[2] = 2 * d(-k) * e((l + 4 * f) / 3) - s / 3 - } - for (u = 0; u < 3; u++) { - if (n[u] < 0 || n[u] > 1) { - n[u] = -1 - } - } - return n - }, - quadraticRoots: function(h, g, n) { - var m, l, k, j; - if (h === 0) { - return this.linearRoot(g, n) - } - m = g * g - 4 * h * n; - if (m === 0) { - k = [-g / (2 * h)] - } else { - if (m > 0) { - l = d(m); - k = [(-g - l) / (2 * h), (-g + l) / (2 * h)] - } else { - return [] - } - } - for (j = 0; j < k.length; j++) { - if (k[j] < 0 || k[j] > 1) { - k[j] = -1 - } - } - return k - }, - linearRoot: function(h, g) { - var i = -g / h; - if (h === 0 || i < 0 || i > 1) { - return [] - } - return [i] - }, - bezierCoeffs: function(h, g, k, j) { - var i = []; - i[0] = -h + 3 * g - 3 * k + j; - i[1] = 3 * h - 6 * g + 3 * k; - i[2] = -3 * h + 3 * g; - i[3] = h; - return i - }, - cubicLineIntersections: function(I, G, F, E, l, k, j, h, M, p, K, n) { - var u = [], - N = [], - D = p - n, - z = K - M, - y = M * (n - p) - p * (K - M), - L = this.bezierCoeffs(I, G, F, E), - J = this.bezierCoeffs(l, k, j, h), - H, x, w, v, g, q, o, m; - u[0] = D * L[0] + z * J[0]; - u[1] = D * L[1] + z * J[1]; - u[2] = D * L[2] + z * J[2]; - u[3] = D * L[3] + z * J[3] + y; - x = this.cubicRoots(u); - for (H = 0; H < x.length; H++) { - v = x[H]; - if (v < 0 || v > 1) { - continue - } - g = v * v; - q = g * v; - o = L[0] * q + L[1] * g + L[2] * v + L[3]; - m = J[0] * q + J[1] * g + J[2] * v + J[3]; - if ((K - M) !== 0) { - w = (o - M) / (K - M) - } else { - w = (m - p) / (n - p) - } - if (!(w < 0 || w > 1)) { - N.push([o, m]) - } - } - return N - }, - splitCubic: function(g, q, p, o, m) { - var j = m * m, - n = m * j, - i = m - 1, - h = i * i, - k = i * h, - l = n * o - 3 * j * i * p + 3 * m * h * q - k * g; - return [ - [g, m * q - i * g, j * p - 2 * m * i * q + h * g, l], - [l, j * o - 2 * m * i * p + h * q, m * o - i * p, o] - ] - }, - cubicDimension: function(p, o, l, k) { - var j = 3 * (-p + 3 * (o - l) + k), - i = 6 * (p - 2 * o + l), - h = -3 * (p - o), - q, n, g = Math.min(p, k), - m = Math.max(p, k), - r; - if (j === 0) { - if (i === 0) { - return [g, m] - } else { - q = -h / i; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - } - } else { - r = i * i - 4 * j * h; - if (r >= 0) { - r = d(r); - q = (r - i) / 2 / j; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - if (r > 0) { - q -= r / j; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - } - } - } - return [g, m] - }, - interpolateCubic: function(h, g, l, k, i) { - if (i === 0) { - return h - } - if (i === 1) { - return k - } - var j = (1 - i) / i; - return i * i * i * (k + j * (3 * l + j * (3 * g + j * h))) - }, - cubicsIntersections: function(r, q, p, o, A, z, y, v, g, F, E, D, m, l, k, i) { - var C = this, - x = C.cubicDimension(r, q, p, o), - B = C.cubicDimension(A, z, y, v), - n = C.cubicDimension(g, F, E, D), - s = C.cubicDimension(m, l, k, i), - j, h, u, t, w = []; - if (x[0] > n[1] || x[1] < n[0] || B[0] > s[1] || B[1] < s[0]) { - return [] - } - if (a(A - z) < 1 && a(y - v) < 1 && a(r - o) < 1 && a(q - p) < 1 && a(m - l) < 1 && a(k - i) < 1 && a(g - D) < 1 && a(F - E) < 1) { - return [ - [(r + o) * 0.5, (A + z) * 0.5] - ] - } - j = C.splitCubic(r, q, p, o, 0.5); - h = C.splitCubic(A, z, y, v, 0.5); - u = C.splitCubic(g, F, E, D, 0.5); - t = C.splitCubic(m, l, k, i, 0.5); - w.push.apply(w, C.cubicsIntersections.apply(C, j[0].concat(h[0], u[0], t[0]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[0].concat(h[0], u[1], t[1]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[1].concat(h[1], u[0], t[0]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[1].concat(h[1], u[1], t[1]))); - return w - }, - linesIntersection: function(k, p, j, o, h, n, q, m) { - var l = (j - k) * (m - n) - (o - p) * (q - h), - i, g; - if (l === 0) { - return null - } - i = ((q - h) * (p - n) - (k - h) * (m - n)) / l; - g = ((j - k) * (p - n) - (o - p) * (k - h)) / l; - if (i >= 0 && i <= 1 && g >= 0 && g <= 1) { - return [k + i * (j - k), p + i * (o - p)] - } - return null - }, - pointOnLine: function(j, m, h, l, g, n) { - var k, i; - if (a(h - j) < a(l - m)) { - i = j; - j = m; - m = i; - i = h; - h = l; - l = i; - i = g; - g = n; - n = i - } - k = (g - j) / (h - j); - if (k < 0 || k > 1) { - return false - } - return a(m + k * (l - m) - n) < 4 - }, - pointOnCubic: function(w, u, s, r, l, k, h, g, p, o) { - var C = this, - B = C.bezierCoeffs(w, u, s, r), - A = C.bezierCoeffs(l, k, h, g), - z, v, n, m, q; - B[3] -= p; - A[3] -= o; - n = C.cubicRoots(B); - m = C.cubicRoots(A); - for (z = 0; z < n.length; z++) { - q = n[z]; - for (v = 0; v < m.length; v++) { - if (q >= 0 && q <= 1 && a(q - m[v]) < 0.05) { - return true - } - } - } - return false - } - } -}); -Ext.define("Ext.chart.series.Pie3D", { - extend: "Ext.chart.series.Polar", - requires: ["Ext.chart.series.sprite.Pie3DPart", "Ext.draw.PathUtil"], - type: "pie3d", - seriesType: "pie3d", - alias: "series.pie3d", - isPie3D: true, - config: { - rect: [0, 0, 0, 0], - thickness: 35, - distortion: 0.5, - donut: false, - hidden: [], - highlightCfg: { - margin: 20 - }, - shadow: false - }, - rotationOffset: -Math.PI / 2, - setField: function(a) { - return this.setXField(a) - }, - getField: function() { - return this.getXField() - }, - updateRotation: function(a) { - this.setStyle({ - baseRotation: a + this.rotationOffset - }); - this.doUpdateStyles() - }, - updateDistortion: function() { - this.setRadius() - }, - updateThickness: function() { - this.setRadius() - }, - updateColors: function(a) { - this.setSubStyle({ - baseColor: a - }) - }, - applyShadow: function(a) { - if (a === true) { - a = { - shadowColor: "rgba(0,0,0,0.8)", - shadowBlur: 30 - } - } else { - if (!Ext.isObject(a)) { - a = { - shadowColor: Ext.draw.Color.RGBA_NONE - } - } - } - return a - }, - updateShadow: function(g) { - var e = this, - f = e.getSprites(), - d = e.spritesPerSlice, - c = f && f.length, - b, a; - for (b = 1; b < c; b += d) { - a = f[b]; - if (a.attr.part = "bottom") { - a.setAttributes(g) - } - } - }, - getStyleByIndex: function(b) { - var d = this.callParent([b]), - c = this.getStyle(), - a = d.fillStyle || d.fill || d.color, - e = c.strokeStyle || c.stroke; - if (a) { - d.baseColor = a; - delete d.fillStyle; - delete d.fill; - delete d.color - } - if (e) { - d.strokeStyle = e - } - return d - }, - doUpdateStyles: function() { - var g = this, - h = g.getSprites(), - f = g.spritesPerSlice, - e = h && h.length, - c = 0, - b = 0, - a, d; - for (; c < e; c += f, b++) { - d = g.getStyleByIndex(b); - for (a = 0; a < f; a++) { - h[c + a].setAttributes(d) - } - } - }, - coordinateX: function() { - var w = this, - m = w.getChart(), - u = m && m.getAnimation(), - f = w.getStore(), - t = f.getData().items, - d = t.length, - b = w.getXField(), - p = w.getRotation(), - s = w.getHidden(), - n, c = 0, - h, e = [], - k = w.getSprites(), - a = k.length, - l = w.spritesPerSlice, - g = 0, - o = Math.PI * 2, - v = 1e-10, - r, q; - for (r = 0; r < d; r++) { - n = Math.abs(Number(t[r].get(b))) || 0; - if (!s[r]) { - c += n - } - e[r] = c; - if (r >= s.length) { - s[r] = false - } - } - s.length = d; - if (c === 0) { - return - } - h = 2 * Math.PI / c; - for (r = 0; r < d; r++) { - e[r] *= h - } - for (r = 0; r < a; r++) { - k[r].setAnimation(u) - } - for (r = 0; r < d; r++) { - for (q = 0; q < l; q++) { - k[r * l + q].setAttributes({ - startAngle: g, - endAngle: e[r] - v, - globalAlpha: 1, - baseRotation: p - }) - } - g = e[r] - } - for (r *= l; r < a; r++) { - k[r].setAnimation(u); - k[r].setAttributes({ - startAngle: o, - endAngle: o, - globalAlpha: 0, - baseRotation: p - }) - } - }, - updateLabelData: function() { - var l = this, - m = l.getStore(), - k = m.getData().items, - h = l.getSprites(), - b = l.getLabel().getTemplate().getField(), - f = l.getHidden(), - a = l.spritesPerSlice, - d, c, g, e, n; - if (h.length && b) { - e = []; - for (d = 0, g = k.length; d < g; d++) { - e.push(k[d].get(b)) - } - for (d = 0, c = 0, g = h.length; d < g; d += a, c++) { - n = h[d]; - n.setAttributes({ - label: e[c] - }); - n.putMarker("labels", { - hidden: f[c] - }, n.attr.attributeId) - } - } - }, - applyRadius: function() { - var f = this, - d = f.getChart(), - h = d.getInnerPadding(), - e = d.getMainRect() || [0, 0, 1, 1], - c = e[2] - h * 2, - a = e[3] - h * 2 - f.getThickness(), - g = c / 2, - b = g * f.getDistortion(); - if (b > a / 2) { - return a / (f.getDistortion() * 2) - } else { - return g - } - }, - getSprites: function() { - var y = this, - e = y.getStore(); - if (!e) { - return [] - } - var n = y.getChart(), - p = y.getSurface(), - t = e.getData().items, - l = y.spritesPerSlice, - a = t.length, - v = y.getAnimation() || n && n.getAnimation(), - x = y.getCenter(), - w = y.getOffsetX(), - u = y.getOffsetY(), - b = y.getRadius(), - q = y.getRotation(), - d = y.getHighlight(), - c = { - centerX: x[0] + w, - centerY: x[1] + u - y.getThickness() / 2, - endRho: b, - startRho: b * y.getDonut() / 100, - thickness: y.getThickness(), - distortion: y.getDistortion() - }, - k = y.sprites, - h = y.getLabel(), - f = h.getTemplate(), - m, g, o, s, r; - for (s = 0; s < a; s++) { - g = Ext.apply({}, this.getStyleByIndex(s), c); - if (!k[s * l]) { - for (r = 0; r < y.partNames.length; r++) { - o = p.add({ - type: "pie3dPart", - part: y.partNames[r] - }); - if (r === 0 && f.getField()) { - o.bindMarker("labels", h) - } - o.fx.setDurationOn("baseRotation", q); - if (d) { - o.config.highlight = d; - o.addModifier("highlight", true) - } - o.setAttributes(g); - k.push(o) - } - } else { - m = k.slice(s * l, (s + 1) * l); - for (r = 0; r < m.length; r++) { - o = m[r]; - if (v) { - o.setAnimation(v) - } - o.setAttributes(g) - } - } - } - return k - }, - betweenAngle: function(d, f, c) { - var e = Math.PI * 2, - g = this.rotationOffset; - f += g; - c += g; - d -= f; - c -= f; - d %= e; - c %= e; - d += e; - c += e; - d %= e; - c %= e; - return d < c || c === 0 - }, - getItemForPoint: function(k, j) { - var h = this, - g = h.getSprites(); - if (g) { - var l = h.getStore(), - b = l.getData().items, - a = h.spritesPerSlice, - e = h.getHidden(), - c, f, m, d; - for (c = 0, f = b.length; c < f; c++) { - if (!e[c]) { - d = c * a; - m = g[d]; - if (m.hitTest([k, j])) { - return { - series: h, - sprite: g.slice(d, d + a), - index: c, - record: b[c], - category: "sprites", - field: h.getXField() - } - } - } - } - return null - } - }, - provideLegendInfo: function(f) { - var h = this, - k = h.getStore(); - if (k) { - var g = k.getData().items, - b = h.getLabel().getTemplate().getField(), - j = h.getField(), - e = h.getHidden(), - d, a, c; - for (d = 0; d < g.length; d++) { - a = h.getStyleByIndex(d); - c = a.baseColor; - f.push({ - name: b ? String(g[d].get(b)) : j + " " + d, - mark: c || "black", - disabled: e[d], - series: h.getId(), - index: d - }) - } - } - } -}, function() { - var b = this.prototype, - a = Ext.chart.series.sprite.Pie3DPart.def.getInitialConfig().processors.part; - b.partNames = a.replace(/^enums\(|\)/g, "").split(","); - b.spritesPerSlice = b.partNames.length -}); -Ext.define("Ext.chart.series.sprite.Polar", { - extend: "Ext.chart.series.sprite.Series", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - baseRotation: "number", - labels: "default", - labelOverflowPadding: "number" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: 0, - endAngle: Math.PI, - startRho: 0, - endRho: 150, - baseRotation: 0, - labels: null, - labelOverflowPadding: 10 - }, - triggers: { - centerX: "bbox", - centerY: "bbox", - startAngle: "bbox", - endAngle: "bbox", - startRho: "bbox", - endRho: "bbox", - baseRotation: "bbox" - } - } - }, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.centerX - a.endRho; - b.y = a.centerY + a.endRho; - b.width = a.endRho * 2; - b.height = a.endRho * 2 - } -}); -Ext.define("Ext.chart.series.sprite.Radar", { - alias: "sprite.radar", - extend: "Ext.chart.series.sprite.Polar", - getDataPointXY: function(d) { - var u = this, - n = u.attr, - f = n.centerX, - e = n.centerY, - o = n.matrix, - t = n.dataMinX, - s = n.dataMaxX, - k = n.dataX, - j = n.dataY, - l = n.endRho, - p = n.startRho, - g = n.baseRotation, - i, h, m, c, b, a, q; - if (n.rangeY) { - q = n.rangeY[1] - } else { - q = n.dataMaxY - } - c = (k[d] - t) / (s - t + 1) * 2 * Math.PI + g; - m = j[d] / q * (l - p) + p; - b = f + Math.cos(c) * m; - a = e + Math.sin(c) * m; - i = o.x(b, a); - h = o.y(b, a); - return [i, h] - }, - render: function(a, l) { - var h = this, - f = h.attr, - g = f.dataX, - b = g.length, - e = h.surfaceMatrix, - d = {}, - c, k, j, m; - l.beginPath(); - for (c = 0; c < b; c++) { - m = h.getDataPointXY(c); - k = m[0]; - j = m[1]; - if (c === 0) { - l.moveTo(k, j) - } - l.lineTo(k, j); - d.translationX = e.x(k, j); - d.translationY = e.y(k, j); - h.putMarker("markers", d, c, true) - } - l.closePath(); - l.fillStroke(f) - } -}); -Ext.define("Ext.chart.series.Radar", { - extend: "Ext.chart.series.Polar", - type: "radar", - seriesType: "radar", - alias: "series.radar", - requires: ["Ext.chart.series.sprite.Radar"], - themeColorCount: function() { - return 1 - }, - isStoreDependantColorCount: false, - themeMarkerCount: function() { - return 1 - }, - updateAngularAxis: function(a) { - a.processData(this) - }, - updateRadialAxis: function(a) { - a.processData(this) - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - updateCenter: function(a) { - this.setStyle({ - translationX: a[0] + this.getOffsetX(), - translationY: a[1] + this.getOffsetY() - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - this.setStyle({ - endRho: a - }); - this.doUpdateStyles() - }, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a - }); - this.doUpdateStyles() - }, - updateTotalAngle: function(a) { - this.processData() - }, - getItemForPoint: function(k, j) { - var h = this, - m = h.sprites && h.sprites[0], - f = m.attr, - g = f.dataX, - a = g.length, - l = h.getStore(), - e = h.getMarker(), - b, o, p, d, n, c; - if (h.getHidden()) { - return null - } - if (m && e) { - c = m.getMarker("markers"); - for (d = 0; d < a; d++) { - n = c.getBBoxFor(d); - b = (n.width + n.height) * 0.25; - p = m.getDataPointXY(d); - if (Math.abs(p[0] - k) < b && Math.abs(p[1] - j) < b) { - o = { - series: h, - sprite: m, - index: d, - category: "markers", - record: l.getData().items[d], - field: h.getYField() - }; - return o - } - } - } - return h.callParent(arguments) - }, - getDefaultSpriteConfig: function() { - var a = this.callParent(), - b = { - customDurations: { - translationX: 0, - translationY: 0, - rotationRads: 0, - dataMinX: 0, - dataMaxX: 0 - } - }; - if (a.fx) { - Ext.apply(a.fx, b) - } else { - a.fx = b - } - return a - }, - getSprites: function() { - var d = this, - c = d.getChart(), - e = d.getAnimation() || c && c.getAnimation(), - b = d.sprites[0], - a; - if (!c) { - return [] - } - if (!b) { - b = d.createSprite() - } - if (e) { - a = b.getMarker("markers"); - if (a) { - a.getTemplate().setAnimation(e) - } - b.setAnimation(e) - } - return d.sprites - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getSubStyleWithTheme(), - c = a.fillStyle; - if (Ext.isArray(c)) { - c = c[0] - } - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - } -}); -Ext.define("Ext.chart.series.sprite.Scatter", { - alias: "sprite.scatterSeries", - extend: "Ext.chart.series.sprite.Cartesian", - renderClipped: function(r, s, w, u) { - if (this.cleanRedraw) { - return - } - var C = this, - q = C.attr, - l = q.dataX, - h = q.dataY, - z = q.labels, - j = C.getSeries(), - b = z && C.getMarker("labels"), - t = C.attr.matrix, - c = t.getXX(), - p = t.getYY(), - m = t.getDX(), - k = t.getDY(), - n = {}, - D, B, d = r.getInherited().rtl && !q.flipXY ? -1 : 1, - a, A, o, e, g, f, v; - if (q.flipXY) { - a = u[1] - c * d; - A = u[1] + u[3] + c * d; - o = u[0] - p; - e = u[0] + u[2] + p - } else { - a = u[0] - c * d; - A = u[0] + u[2] + c * d; - o = u[1] - p; - e = u[1] + u[3] + p - } - for (v = 0; v < l.length; v++) { - g = l[v]; - f = h[v]; - g = g * c + m; - f = f * p + k; - if (a <= g && g <= A && o <= f && f <= e) { - if (q.renderer) { - n = { - type: "items", - translationX: g, - translationY: f - }; - B = [C, n, { - store: C.getStore() - }, v]; - D = Ext.callback(q.renderer, null, B, 0, j); - n = Ext.apply(n, D) - } else { - n.translationX = g; - n.translationY = f - } - C.putMarker("items", n, v, !q.renderer); - if (b && z[v]) { - C.drawLabel(z[v], g, f, v, u) - } - } - } - }, - drawLabel: function(j, h, g, p, a) { - var r = this, - m = r.attr, - d = r.getMarker("labels"), - c = d.getTemplate(), - l = r.labelCfg || (r.labelCfg = {}), - b = r.surfaceMatrix, - f, e, i = m.labelOverflowPadding, - o = m.flipXY, - k, n, s, q; - l.text = j; - n = r.getMarkerBBox("labels", p, true); - if (!n) { - r.putMarker("labels", l, p); - n = r.getMarkerBBox("labels", p, true) - } - if (o) { - l.rotationRads = Math.PI * 0.5 - } else { - l.rotationRads = 0 - } - k = n.height / 2; - f = h; - switch (c.attr.display) { - case "under": - e = g - k - i; - break; - case "rotate": - f += i; - e = g - i; - l.rotationRads = -Math.PI / 4; - break; - default: - e = g + k + i - } - l.x = b.x(f, e); - l.y = b.y(f, e); - if (c.attr.renderer) { - q = [j, d, l, { - store: r.getStore() - }, p]; - s = Ext.callback(c.attr.renderer, null, q, 0, r.getSeries()); - if (typeof s === "string") { - l.text = s - } else { - Ext.apply(l, s) - } - } - r.putMarker("labels", l, p) - } -}); -Ext.define("Ext.chart.series.Scatter", { - extend: "Ext.chart.series.Cartesian", - alias: "series.scatter", - type: "scatter", - seriesType: "scatterSeries", - requires: ["Ext.chart.series.sprite.Scatter"], - config: { - itemInstancing: { - fx: { - customDurations: { - translationX: 0, - translationY: 0 - } - } - } - }, - themeMarkerCount: function() { - return 1 - }, - applyMarker: function(b, a) { - this.getItemInstancing(); - this.setItemInstancing(b); - return this.callParent(arguments) - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getMarkerStyleByIndex(0), - c = a.fillStyle; - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - } -}); -Ext.define("Ext.chart.theme.Blue", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.blue", "chart.theme.Blue"], - config: { - baseColor: "#4d7fe6" - } -}); -Ext.define("Ext.chart.theme.BlueGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.blue-gradients", "chart.theme.Blue:gradients"], - config: { - baseColor: "#4d7fe6", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category1", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category1", "chart.theme.Category1"], - config: { - colors: ["#f0a50a", "#c20024", "#2044ba", "#810065", "#7eae29"] - } -}); -Ext.define("Ext.chart.theme.Category1Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category1-gradients", "chart.theme.Category1:gradients"], - config: { - colors: ["#f0a50a", "#c20024", "#2044ba", "#810065", "#7eae29"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category2", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category2", "chart.theme.Category2"], - config: { - colors: ["#6d9824", "#87146e", "#2a9196", "#d39006", "#1e40ac"] - } -}); -Ext.define("Ext.chart.theme.Category2Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category2-gradients", "chart.theme.Category2:gradients"], - config: { - colors: ["#6d9824", "#87146e", "#2a9196", "#d39006", "#1e40ac"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category3", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category3", "chart.theme.Category3"], - config: { - colors: ["#fbbc29", "#ce2e4e", "#7e0062", "#158b90", "#57880e"] - } -}); -Ext.define("Ext.chart.theme.Category3Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category3-gradients", "chart.theme.Category3:gradients"], - config: { - colors: ["#fbbc29", "#ce2e4e", "#7e0062", "#158b90", "#57880e"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category4", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category4", "chart.theme.Category4"], - config: { - colors: ["#ef5773", "#fcbd2a", "#4f770d", "#1d3eaa", "#9b001f"] - } -}); -Ext.define("Ext.chart.theme.Category4Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category4-gradients", "chart.theme.Category4:gradients"], - config: { - colors: ["#ef5773", "#fcbd2a", "#4f770d", "#1d3eaa", "#9b001f"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category5", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category5", "chart.theme.Category5"], - config: { - colors: ["#7eae29", "#fdbe2a", "#910019", "#27b4bc", "#d74dbc"] - } -}); -Ext.define("Ext.chart.theme.Category5Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category5-gradients", "chart.theme.Category5:gradients"], - config: { - colors: ["#7eae29", "#fdbe2a", "#910019", "#27b4bc", "#d74dbc"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category6", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category6", "chart.theme.Category6"], - config: { - colors: ["#44dce1", "#0b2592", "#996e05", "#7fb325", "#b821a1"] - } -}); -Ext.define("Ext.chart.theme.Category6Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category6-gradients", "chart.theme.Category6:gradients"], - config: { - colors: ["#44dce1", "#0b2592", "#996e05", "#7fb325", "#b821a1"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.DefaultGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.default-gradients", "chart.theme.Base:gradients"], - config: { - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Green", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.green", "chart.theme.Green"], - config: { - baseColor: "#b1da5a" - } -}); -Ext.define("Ext.chart.theme.GreenGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.green-gradients", "chart.theme.Green:gradients"], - config: { - baseColor: "#b1da5a", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Midnight", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.midnight", "chart.theme.Midnight"], - config: { - colors: ["#A837FF", "#4AC0F2", "#FF4D35", "#FF8809", "#61C102", "#FF37EA"], - chart: { - defaults: { - background: "rgb(52, 52, 53)" - } - }, - axis: { - defaults: { - style: { - strokeStyle: "rgb(224, 224, 227)" - }, - label: { - fillStyle: "rgb(224, 224, 227)" - }, - title: { - fillStyle: "rgb(224, 224, 227)" - }, - grid: { - strokeStyle: "rgb(112, 112, 115)" - } - } - }, - series: { - defaults: { - label: { - fillStyle: "rgb(224, 224, 227)" - } - } - }, - sprites: { - text: { - fillStyle: "rgb(224, 224, 227)" - } - } - } -}); -Ext.define("Ext.chart.theme.Muted", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.muted", "chart.theme.Muted"], - config: { - colors: ["#8ca640", "#974144", "#4091ba", "#8e658e", "#3b8d8b", "#b86465", "#d2af69", "#6e8852", "#3dcc7e", "#a6bed1", "#cbaa4b", "#998baa"] - } -}); -Ext.define("Ext.chart.theme.Purple", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.purple", "chart.theme.Purple"], - config: { - baseColor: "#da5abd" - } -}); -Ext.define("Ext.chart.theme.PurpleGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.purple-gradients", "chart.theme.Purple:gradients"], - config: { - baseColor: "#da5abd", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Red", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.red", "chart.theme.Red"], - config: { - baseColor: "#e84b67" - } -}); -Ext.define("Ext.chart.theme.RedGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.red-gradients", "chart.theme.Red:gradients"], - config: { - baseColor: "#e84b67", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Sky", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.sky", "chart.theme.Sky"], - config: { - baseColor: "#4ce0e7" - } -}); -Ext.define("Ext.chart.theme.SkyGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.sky-gradients", "chart.theme.Sky:gradients"], - config: { - baseColor: "#4ce0e7", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Yellow", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.yellow", "chart.theme.Yellow"], - config: { - baseColor: "#fec935" - } -}); -Ext.define("Ext.chart.theme.YellowGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.yellow-gradients", "chart.theme.Yellow:gradients"], - config: { - baseColor: "#fec935", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.draw.Point", { - requires: ["Ext.draw.Draw", "Ext.draw.Matrix"], - isPoint: true, - x: 0, - y: 0, - length: 0, - angle: 0, - angleUnits: "degrees", - statics: { - fly: (function() { - var a = null; - return function(b, c) { - if (!a) { - a = new Ext.draw.Point() - } - a.constructor(b, c); - return a - } - })() - }, - constructor: function(a, c) { - var b = this; - if (typeof a === "number") { - b.x = a; - if (typeof c === "number") { - b.y = c - } else { - b.y = a - } - } else { - if (Ext.isArray(a)) { - b.x = a[0]; - b.y = a[1] - } else { - if (a) { - b.x = a.x; - b.y = a.y - } - } - } - b.calculatePolar() - }, - calculateCartesian: function() { - var b = this, - a = b.length, - c = b.angle; - if (b.angleUnits === "degrees") { - c = Ext.draw.Draw.rad(c) - } - b.x = Math.cos(c) * a; - b.y = Math.sin(c) * a - }, - calculatePolar: function() { - var b = this, - a = b.x, - c = b.y; - b.length = Math.sqrt(a * a + c * c); - b.angle = Math.atan2(c, a); - if (b.angleUnits === "degrees") { - b.angle = Ext.draw.Draw.degrees(b.angle) - } - }, - setX: function(a) { - this.x = a; - this.calculatePolar() - }, - setY: function(a) { - this.y = a; - this.calculatePolar() - }, - set: function(a, b) { - this.constructor(a, b) - }, - setAngle: function(a) { - this.angle = a; - this.calculateCartesian() - }, - setLength: function(a) { - this.length = a; - this.calculateCartesian() - }, - setPolar: function(b, a) { - this.angle = b; - this.length = a; - this.calculateCartesian() - }, - clone: function() { - return new Ext.draw.Point(this.x, this.y) - }, - add: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return new Ext.draw.Point(this.x + b.x, this.y + b.y) - }, - sub: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return new Ext.draw.Point(this.x - b.x, this.y - b.y) - }, - mul: function(a) { - return new Ext.draw.Point(this.x * a, this.y * a) - }, - div: function(a) { - return new Ext.draw.Point(this.x / a, this.y / a) - }, - dot: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return this.x * b.x + this.y * b.y - }, - equals: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return this.x === b.x && this.y === b.y - }, - rotate: function(f, c) { - var d, e, b, g, a; - if (this.angleUnits === "degrees") { - f = Ext.draw.Draw.rad(f); - d = Math.sin(f); - e = Math.cos(f) - } - if (c) { - b = c.x; - g = c.y - } else { - b = 0; - g = 0 - } - a = Ext.draw.Matrix.fly([e, d, -d, e, b - e * b + g * d, g - e * g + b * -d]).transformPoint(this); - return new Ext.draw.Point(a) - }, - transform: function(a) { - if (a && a.isMatrix) { - return new Ext.draw.Point(a.transformPoint(this)) - } else { - if (arguments.length === 6) { - return new Ext.draw.Point(Ext.draw.Matrix.fly(arguments).transformPoint(this)) - } else { - Ext.raise("Invalid parameters.") - } - } - }, - round: function() { - return new Ext.draw.Point(Math.round(this.x), Math.round(this.y)) - }, - ceil: function() { - return new Ext.draw.Point(Math.ceil(this.x), Math.ceil(this.y)) - }, - floor: function() { - return new Ext.draw.Point(Math.floor(this.x), Math.floor(this.y)) - }, - abs: function(a, b) { - return new Ext.draw.Point(Math.abs(this.x), Math.abs(this.y)) - }, - normalize: function(c) { - var b = this.x, - f = this.y, - a, e, d; - c = c || 1; - if (b === 0) { - a = 0; - e = c * Ext.Number.sign(f) - } else { - d = f / b; - a = c / Math.sqrt(1 + d * d); - e = a * d - } - return new Ext.draw.Point(a, e) - }, - getDistanceToLine: function(c, b) { - if (arguments.length === 4) { - c = new Ext.draw.Point(arguments[0], arguments[1]); - b = new Ext.draw.Point(arguments[2], arguments[3]) - } - var d = b.sub(c).normalize(), - a = c.sub(this); - return a.sub(d.mul(a.dot(d))) - }, - isZero: function() { - return this.x === 0 && this.y === 0 - }, - isNumber: function() { - return Ext.isNumber(this.x + this.y) - } -}); -Ext.define("Ext.draw.plugin.SpriteEvents", { - extend: "Ext.plugin.Abstract", - alias: "plugin.spriteevents", - requires: ["Ext.draw.PathUtil"], - mouseMoveEvents: { - mousemove: true, - mouseover: true, - mouseout: true - }, - spriteMouseMoveEvents: { - spritemousemove: true, - spritemouseover: true, - spritemouseout: true - }, - init: function(a) { - var b = "handleEvent"; - this.drawContainer = a; - a.addElementListener({ - click: b, - dblclick: b, - mousedown: b, - mousemove: b, - mouseup: b, - mouseover: b, - mouseout: b, - priority: 1001, - scope: this - }) - }, - hasSpriteMouseMoveListeners: function() { - var b = this.drawContainer.hasListeners, - a; - for (a in this.spriteMouseMoveEvents) { - if (a in b) { - return true - } - } - return false - }, - hitTestEvent: function(f) { - var b = this.drawContainer.getItems(), - a, d, c; - for (c = b.length - 1; c >= 0; c--) { - a = b.get(c); - d = a.hitTestEvent(f); - if (d) { - return d - } - } - return null - }, - handleEvent: function(f) { - var d = this, - b = d.drawContainer, - g = f.type in d.mouseMoveEvents, - a = d.lastSprite, - c; - if (g && !d.hasSpriteMouseMoveListeners()) { - return - } - c = d.hitTestEvent(f); - if (g && !Ext.Object.equals(c, a)) { - if (a) { - b.fireEvent("spritemouseout", a, f) - } - if (c) { - b.fireEvent("spritemouseover", c, f) - } - } - if (c) { - b.fireEvent("sprite" + f.type, c, f) - } - d.lastSprite = c - } -}); -Ext.define("Ext.chart.TipSurface", { - extend: "Ext.draw.Container", - spriteArray: false, - renderFirst: true, - constructor: function(a) { - this.callParent([a]); - if (a.sprites) { - this.spriteArray = [].concat(a.sprites); - delete a.sprites - } - }, - onRender: function() { - var c = this, - b = 0, - a = 0, - d, e; - this.callParent(arguments); - e = c.spriteArray; - if (c.renderFirst && e) { - c.renderFirst = false; - for (a = e.length; b < a; b++) { - d = c.surface.add(e[b]); - d.setAttributes({ - hidden: false - }, true) - } - } - } -}); -Ext.define("Ext.chart.interactions.ItemInfo", { - extend: "Ext.chart.interactions.Abstract", - type: "iteminfo", - alias: "interaction.iteminfo", - config: { - extjsGestures: { - start: { - event: "click", - handler: "onInfoGesture" - }, - move: { - event: "mousemove", - handler: "onInfoGesture" - }, - end: { - event: "mouseleave", - handler: "onInfoGesture" - } - } - }, - item: null, - onInfoGesture: function(f, a) { - var c = this, - b = c.getItemForEvent(f), - d = b && b.series.tooltip; - if (d) { - d.onMouseMove.call(d, f) - } - if (b !== c.item) { - if (b) { - b.series.showTip(b) - } else { - c.item.series.hideTip(c.item) - } - c.item = b - } - return false - } -}); diff --git a/serverside/jsmod/5.4-3/charts.js.original b/serverside/jsmod/5.4-3/charts.js.original deleted file mode 100644 index a7eb089..0000000 --- a/serverside/jsmod/5.4-3/charts.js.original +++ /dev/null @@ -1,22010 +0,0 @@ -Ext.define("Ext.draw.ContainerBase", { - extend: "Ext.panel.Panel", - requires: ["Ext.window.Window"], - previewTitleText: "Chart Preview", - previewAltText: "Chart preview", - layout: "container", - addElementListener: function() { - var b = this, - a = arguments; - if (b.rendered) { - b.el.on.apply(b.el, a) - } else { - b.on("render", function() { - b.el.on.apply(b.el, a) - }) - } - }, - removeElementListener: function() { - var b = this, - a = arguments; - if (b.rendered) { - b.el.un.apply(b.el, a) - } - }, - afterRender: function() { - this.callParent(arguments); - this.initAnimator() - }, - getItems: function() { - var b = this, - a = b.items; - if (!a || !a.isMixedCollection) { - b.initItems() - } - return b.items - }, - onRender: function() { - this.callParent(arguments); - this.element = this.el; - this.innerElement = this.body - }, - setItems: function(a) { - this.items = a; - return a - }, - setSurfaceSize: function(b, a) { - this.resizeHandler({ - width: b, - height: a - }); - this.renderFrame() - }, - onResize: function(c, a, b, e) { - var d = this; - d.callParent([c, a, b, e]); - d.setBodySize({ - width: c, - height: a - }) - }, - preview: function() { - var a = this.getImage(); - new Ext.window.Window({ - title: this.previewTitleText, - closeable: true, - renderTo: Ext.getBody(), - autoShow: true, - maximizeable: true, - maximized: true, - border: true, - layout: { - type: "hbox", - pack: "center", - align: "middle" - }, - items: { - xtype: "container", - items: { - xtype: "image", - mode: "img", - cls: Ext.baseCSSPrefix + "chart-image", - alt: this.previewAltText, - src: a.data, - listeners: { - afterrender: function() { - var e = this, - b = e.imgEl.dom, - d = a.type === "svg" ? 1 : (window.devicePixelRatio || 1), - c; - if (!b.naturalWidth || !b.naturalHeight) { - b.onload = function() { - var g = b.naturalWidth, - f = b.naturalHeight; - e.setWidth(Math.floor(g / d)); - e.setHeight(Math.floor(f / d)) - } - } else { - c = e.getSize(); - e.setWidth(Math.floor(c.width / d)); - e.setHeight(Math.floor(c.height / d)) - } - } - } - } - } - }) - }, - privates: { - getTargetEl: function() { - return this.innerElement - }, - reattachToBody: function() { - var a = this; - if (a.pendingDetachSize) { - a.onBodyResize() - } - a.pendingDetachSize = false; - a.callParent() - } - } -}); -Ext.define("Ext.draw.SurfaceBase", { - extend: "Ext.Widget", - getOwnerBody: function() { - return this.ownerCt.body - }, - destroy: function() { - var a = this; - if (a.hasListeners.destroy) { - a.fireEvent("destroy", a) - } - a.callParent() - } -}); -Ext.define("Ext.draw.Color", { - statics: { - colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/, - rgbToHexRe: /\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/, - rgbaToHexRe: /\s*rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\.\d]+)\)/, - hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/, - NONE: "none", - RGBA_NONE: "rgba(0, 0, 0, 0)" - }, - isColor: true, - lightnessFactor: 0.2, - constructor: function(d, b, a, c) { - this.setRGB(d, b, a, c) - }, - setRGB: function(e, c, a, d) { - var b = this; - b.r = Math.min(255, Math.max(0, e)); - b.g = Math.min(255, Math.max(0, c)); - b.b = Math.min(255, Math.max(0, a)); - if (d === undefined) { - b.a = 1 - } else { - b.a = Math.min(1, Math.max(0, d)) - } - }, - getGrayscale: function() { - return this.r * 0.3 + this.g * 0.59 + this.b * 0.11 - }, - getHSL: function() { - var i = this, - a = i.r / 255, - f = i.g / 255, - j = i.b / 255, - k = Math.max(a, f, j), - d = Math.min(a, f, j), - m = k - d, - e, n = 0, - c = 0.5 * (k + d); - if (d !== k) { - n = (c <= 0.5) ? m / (k + d) : m / (2 - k - d); - if (a === k) { - e = 60 * (f - j) / m - } else { - if (f === k) { - e = 120 + 60 * (j - a) / m - } else { - e = 240 + 60 * (a - f) / m - } - } - if (e < 0) { - e += 360 - } - if (e >= 360) { - e -= 360 - } - } - return [e, n, c] - }, - getHSV: function() { - var i = this, - a = i.r / 255, - f = i.g / 255, - j = i.b / 255, - k = Math.max(a, f, j), - d = Math.min(a, f, j), - c = k - d, - e, m = 0, - l = k; - if (d != k) { - m = l ? c / l : 0; - if (a === k) { - e = 60 * (f - j) / c - } else { - if (f === k) { - e = 60 * (j - a) / c + 120 - } else { - e = 60 * (a - f) / c + 240 - } - } - if (e < 0) { - e += 360 - } - if (e >= 360) { - e -= 360 - } - } - return [e, m, l] - }, - setHSL: function(g, f, e) { - var i = this, - d = Math.abs, - j, b, a; - g = (g % 360 + 360) % 360; - f = f > 1 ? 1 : f < 0 ? 0 : f; - e = e > 1 ? 1 : e < 0 ? 0 : e; - if (f === 0 || g === null) { - e *= 255; - i.setRGB(e, e, e) - } else { - g /= 60; - j = f * (1 - d(2 * e - 1)); - b = j * (1 - d(g % 2 - 1)); - a = e - j / 2; - a *= 255; - j *= 255; - b *= 255; - switch (Math.floor(g)) { - case 0: - i.setRGB(j + a, b + a, a); - break; - case 1: - i.setRGB(b + a, j + a, a); - break; - case 2: - i.setRGB(a, j + a, b + a); - break; - case 3: - i.setRGB(a, b + a, j + a); - break; - case 4: - i.setRGB(b + a, a, j + a); - break; - case 5: - i.setRGB(j + a, a, b + a); - break - } - } - return i - }, - setHSV: function(f, e, d) { - var g = this, - i, b, a; - f = (f % 360 + 360) % 360; - e = e > 1 ? 1 : e < 0 ? 0 : e; - d = d > 1 ? 1 : d < 0 ? 0 : d; - if (e === 0 || f === null) { - d *= 255; - g.setRGB(d, d, d) - } else { - f /= 60; - i = d * e; - b = i * (1 - Math.abs(f % 2 - 1)); - a = d - i; - a *= 255; - i *= 255; - b *= 255; - switch (Math.floor(f)) { - case 0: - g.setRGB(i + a, b + a, a); - break; - case 1: - g.setRGB(b + a, i + a, a); - break; - case 2: - g.setRGB(a, i + a, b + a); - break; - case 3: - g.setRGB(a, b + a, i + a); - break; - case 4: - g.setRGB(b + a, a, i + a); - break; - case 5: - g.setRGB(i + a, a, b + a); - break - } - } - return g - }, - createLighter: function(b) { - if (!b && b !== 0) { - b = this.lightnessFactor - } - var a = this.getHSL(); - a[2] = Ext.Number.constrain(a[2] + b, 0, 1); - return Ext.draw.Color.fromHSL(a[0], a[1], a[2]) - }, - createDarker: function(a) { - if (!a && a !== 0) { - a = this.lightnessFactor - } - return this.createLighter(-a) - }, - toString: function() { - var f = this, - c = Math.round; - if (f.a === 1) { - var e = c(f.r).toString(16), - d = c(f.g).toString(16), - a = c(f.b).toString(16); - e = (e.length === 1) ? "0" + e : e; - d = (d.length === 1) ? "0" + d : d; - a = (a.length === 1) ? "0" + a : a; - return ["#", e, d, a].join("") - } else { - return "rgba(" + [c(f.r), c(f.g), c(f.b), f.a === 0 ? 0 : f.a.toFixed(15)].join(", ") + ")" - } - }, - toHex: function(b) { - if (Ext.isArray(b)) { - b = b[0] - } - if (!Ext.isString(b)) { - return "" - } - if (b.substr(0, 1) === "#") { - return b - } - var e = Ext.draw.Color.colorToHexRe.exec(b); - if (Ext.isArray(e)) { - var f = parseInt(e[2], 10), - d = parseInt(e[3], 10), - a = parseInt(e[4], 10), - c = a | (d << 8) | (f << 16); - return e[1] + "#" + ("000000" + c.toString(16)).slice(-6) - } else { - return "" - } - }, - setFromString: function(j) { - var e, h, f, c, d = 1, - i = parseInt; - if (j === Ext.draw.Color.NONE) { - this.r = this.g = this.b = this.a = 0; - return this - } - if ((j.length === 4 || j.length === 7) && j.substr(0, 1) === "#") { - e = j.match(Ext.draw.Color.hexRe); - if (e) { - h = i(e[1], 16) >> 0; - f = i(e[2], 16) >> 0; - c = i(e[3], 16) >> 0; - if (j.length === 4) { - h += (h * 16); - f += (f * 16); - c += (c * 16) - } - } - } else { - if ((e = j.match(Ext.draw.Color.rgbToHexRe))) { - h = +e[1]; - f = +e[2]; - c = +e[3] - } else { - if ((e = j.match(Ext.draw.Color.rgbaToHexRe))) { - h = +e[1]; - f = +e[2]; - c = +e[3]; - d = +e[4] - } else { - if (Ext.draw.Color.ColorList.hasOwnProperty(j.toLowerCase())) { - return this.setFromString(Ext.draw.Color.ColorList[j.toLowerCase()]) - } - } - } - } - if (typeof h === "undefined") { - return this - } - this.r = h; - this.g = f; - this.b = c; - this.a = d; - return this - } -}, function() { - var a = new this(); - this.addStatics({ - fly: function(f, e, c, d) { - switch (arguments.length) { - case 1: - a.setFromString(f); - break; - case 3: - case 4: - a.setRGB(f, e, c, d); - break; - default: - return null - } - return a - }, - ColorList: { - aliceblue: "#f0f8ff", - antiquewhite: "#faebd7", - aqua: "#00ffff", - aquamarine: "#7fffd4", - azure: "#f0ffff", - beige: "#f5f5dc", - bisque: "#ffe4c4", - black: "#000000", - blanchedalmond: "#ffebcd", - blue: "#0000ff", - blueviolet: "#8a2be2", - brown: "#a52a2a", - burlywood: "#deb887", - cadetblue: "#5f9ea0", - chartreuse: "#7fff00", - chocolate: "#d2691e", - coral: "#ff7f50", - cornflowerblue: "#6495ed", - cornsilk: "#fff8dc", - crimson: "#dc143c", - cyan: "#00ffff", - darkblue: "#00008b", - darkcyan: "#008b8b", - darkgoldenrod: "#b8860b", - darkgray: "#a9a9a9", - darkgreen: "#006400", - darkkhaki: "#bdb76b", - darkmagenta: "#8b008b", - darkolivegreen: "#556b2f", - darkorange: "#ff8c00", - darkorchid: "#9932cc", - darkred: "#8b0000", - darksalmon: "#e9967a", - darkseagreen: "#8fbc8f", - darkslateblue: "#483d8b", - darkslategray: "#2f4f4f", - darkturquoise: "#00ced1", - darkviolet: "#9400d3", - deeppink: "#ff1493", - deepskyblue: "#00bfff", - dimgray: "#696969", - dodgerblue: "#1e90ff", - firebrick: "#b22222", - floralwhite: "#fffaf0", - forestgreen: "#228b22", - fuchsia: "#ff00ff", - gainsboro: "#dcdcdc", - ghostwhite: "#f8f8ff", - gold: "#ffd700", - goldenrod: "#daa520", - gray: "#808080", - green: "#008000", - greenyellow: "#adff2f", - honeydew: "#f0fff0", - hotpink: "#ff69b4", - indianred: "#cd5c5c", - indigo: "#4b0082", - ivory: "#fffff0", - khaki: "#f0e68c", - lavender: "#e6e6fa", - lavenderblush: "#fff0f5", - lawngreen: "#7cfc00", - lemonchiffon: "#fffacd", - lightblue: "#add8e6", - lightcoral: "#f08080", - lightcyan: "#e0ffff", - lightgoldenrodyellow: "#fafad2", - lightgray: "#d3d3d3", - lightgrey: "#d3d3d3", - lightgreen: "#90ee90", - lightpink: "#ffb6c1", - lightsalmon: "#ffa07a", - lightseagreen: "#20b2aa", - lightskyblue: "#87cefa", - lightslategray: "#778899", - lightsteelblue: "#b0c4de", - lightyellow: "#ffffe0", - lime: "#00ff00", - limegreen: "#32cd32", - linen: "#faf0e6", - magenta: "#ff00ff", - maroon: "#800000", - mediumaquamarine: "#66cdaa", - mediumblue: "#0000cd", - mediumorchid: "#ba55d3", - mediumpurple: "#9370d8", - mediumseagreen: "#3cb371", - mediumslateblue: "#7b68ee", - mediumspringgreen: "#00fa9a", - mediumturquoise: "#48d1cc", - mediumvioletred: "#c71585", - midnightblue: "#191970", - mintcream: "#f5fffa", - mistyrose: "#ffe4e1", - moccasin: "#ffe4b5", - navajowhite: "#ffdead", - navy: "#000080", - oldlace: "#fdf5e6", - olive: "#808000", - olivedrab: "#6b8e23", - orange: "#ffa500", - orangered: "#ff4500", - orchid: "#da70d6", - palegoldenrod: "#eee8aa", - palegreen: "#98fb98", - paleturquoise: "#afeeee", - palevioletred: "#d87093", - papayawhip: "#ffefd5", - peachpuff: "#ffdab9", - peru: "#cd853f", - pink: "#ffc0cb", - plum: "#dda0dd", - powderblue: "#b0e0e6", - purple: "#800080", - red: "#ff0000", - rosybrown: "#bc8f8f", - royalblue: "#4169e1", - saddlebrown: "#8b4513", - salmon: "#fa8072", - sandybrown: "#f4a460", - seagreen: "#2e8b57", - seashell: "#fff5ee", - sienna: "#a0522d", - silver: "#c0c0c0", - skyblue: "#87ceeb", - slateblue: "#6a5acd", - slategray: "#708090", - snow: "#fffafa", - springgreen: "#00ff7f", - steelblue: "#4682b4", - tan: "#d2b48c", - teal: "#008080", - thistle: "#d8bfd8", - tomato: "#ff6347", - turquoise: "#40e0d0", - violet: "#ee82ee", - wheat: "#f5deb3", - white: "#ffffff", - whitesmoke: "#f5f5f5", - yellow: "#ffff00", - yellowgreen: "#9acd32" - }, - fromHSL: function(d, c, b) { - return (new this(0, 0, 0, 0)).setHSL(d, c, b) - }, - fromHSV: function(d, c, b) { - return (new this(0, 0, 0, 0)).setHSL(d, c, b) - }, - fromString: function(b) { - return (new this(0, 0, 0, 0)).setFromString(b) - }, - create: function(b) { - if (b instanceof this) { - return b - } else { - if (Ext.isArray(b)) { - return new Ext.draw.Color(b[0], b[1], b[2], b[3]) - } else { - if (Ext.isString(b)) { - return Ext.draw.Color.fromString(b) - } else { - if (arguments.length > 2) { - return new Ext.draw.Color(arguments[0], arguments[1], arguments[2], arguments[3]) - } else { - return new Ext.draw.Color(0, 0, 0, 0) - } - } - } - } - } - }) -}); -Ext.define("Ext.draw.sprite.AnimationParser", function() { - function a(d, c, b) { - return d + (c - d) * b - } - return { - singleton: true, - attributeRe: /^url\(#([a-zA-Z\-]+)\)$/, - requires: ["Ext.draw.Color"], - color: { - parseInitial: function(c, b) { - if (Ext.isString(c)) { - c = Ext.draw.Color.create(c) - } - if (Ext.isString(b)) { - b = Ext.draw.Color.create(b) - } - if ((c instanceof Ext.draw.Color) && (b instanceof Ext.draw.Color)) { - return [ - [c.r, c.g, c.b, c.a], - [b.r, b.g, b.b, b.a] - ] - } else { - return [c || b, b || c] - } - }, - compute: function(d, c, b) { - if (!Ext.isArray(d) || !Ext.isArray(c)) { - return c || d - } else { - return [a(d[0], c[0], b), a(d[1], c[1], b), a(d[2], c[2], b), a(d[3], c[3], b)] - } - }, - serve: function(c) { - var b = Ext.draw.Color.fly(c[0], c[1], c[2], c[3]); - return b.toString() - } - }, - number: { - parse: function(b) { - return b === null ? null : +b - }, - compute: function(d, c, b) { - if (!Ext.isNumber(d) || !Ext.isNumber(c)) { - return c || d - } else { - return a(d, c, b) - } - } - }, - angle: { - parseInitial: function(c, b) { - if (b - c > Math.PI) { - b -= Math.PI * 2 - } else { - if (b - c < -Math.PI) { - b += Math.PI * 2 - } - } - return [c, b] - }, - compute: function(d, c, b) { - if (!Ext.isNumber(d) || !Ext.isNumber(c)) { - return c || d - } else { - return a(d, c, b) - } - } - }, - path: { - parseInitial: function(m, n) { - var c = m.toStripes(), - o = n.toStripes(), - e, d, k = c.length, - p = o.length, - h, f, b, g = o[p - 1], - l = [g[g.length - 2], g[g.length - 1]]; - for (e = k; e < p; e++) { - c.push(c[k - 1].slice(0)) - } - for (e = p; e < k; e++) { - o.push(l.slice(0)) - } - b = c.length; - o.path = n; - o.temp = new Ext.draw.Path(); - for (e = 0; e < b; e++) { - h = c[e]; - f = o[e]; - k = h.length; - p = f.length; - o.temp.commands.push("M"); - for (d = p; d < k; d += 6) { - f.push(l[0], l[1], l[0], l[1], l[0], l[1]) - } - g = o[o.length - 1]; - l = [g[g.length - 2], g[g.length - 1]]; - for (d = k; d < p; d += 6) { - h.push(l[0], l[1], l[0], l[1], l[0], l[1]) - } - for (e = 0; e < f.length; e++) { - f[e] -= h[e] - } - for (e = 2; e < f.length; e += 6) { - o.temp.commands.push("C") - } - } - return [c, o] - }, - compute: function(c, l, m) { - if (m >= 1) { - return l.path - } - var e = 0, - f = c.length, - d = 0, - b, k, h, n = l.temp.params, - g = 0; - for (; e < f; e++) { - k = c[e]; - h = l[e]; - b = k.length; - for (d = 0; d < b; d++) { - n[g++] = h[d] * m + k[d] - } - } - return l.temp - } - }, - data: { - compute: function(h, j, k, g) { - var m = h.length - 1, - b = j.length - 1, - e = Math.max(m, b), - d, l, c; - if (!g || g === h) { - g = [] - } - g.length = e + 1; - for (c = 0; c <= e; c++) { - d = h[Math.min(c, m)]; - l = j[Math.min(c, b)]; - if (Ext.isNumber(d)) { - if (!Ext.isNumber(l)) { - l = 0 - } - g[c] = (l - d) * k + d - } else { - g[c] = l - } - } - return g - } - }, - text: { - compute: function(d, c, b) { - return d.substr(0, Math.round(d.length * (1 - b))) + c.substr(Math.round(c.length * (1 - b))) - } - }, - limited: "number", - limited01: "number" - } -}); -(function() { - if (!Ext.global.Float32Array) { - var a = function(d) { - if (typeof d === "number") { - this.length = d - } else { - if ("length" in d) { - this.length = d.length; - for (var c = 0, b = d.length; c < b; c++) { - this[c] = +d[c] - } - } - } - }; - a.prototype = []; - Ext.global.Float32Array = a - } -})(); -Ext.define("Ext.draw.Draw", { - singleton: true, - radian: Math.PI / 180, - pi2: Math.PI * 2, - reflectFn: function(b) { - return b - }, - rad: function(a) { - return (a % 360) * this.radian - }, - degrees: function(a) { - return (a / this.radian) % 360 - }, - isBBoxIntersect: function(b, a, c) { - c = c || 0; - return (Math.max(b.x, a.x) - c > Math.min(b.x + b.width, a.x + a.width)) || (Math.max(b.y, a.y) - c > Math.min(b.y + b.height, a.y + a.height)) - }, - isPointInBBox: function(a, c, b) { - return !!b && a >= b.x && a <= (b.x + b.width) && c >= b.y && c <= (b.y + b.height) - }, - spline: function(m) { - var e, c, k = m.length, - b, h, l, f, a = 0, - g = new Float32Array(m.length), - n = new Float32Array(m.length * 3 - 2); - g[0] = 0; - g[k - 1] = 0; - for (e = 1; e < k - 1; e++) { - g[e] = (m[e + 1] + m[e - 1] - 2 * m[e]) - g[e - 1]; - a = 1 / (4 - a); - g[e] *= a - } - for (e = k - 2; e > 0; e--) { - a = 3.732050807568877 + 48.248711305964385 / (-13.928203230275537 + Math.pow(0.07179676972449123, e)); - g[e] -= g[e + 1] * a - } - f = m[0]; - b = f - g[0]; - for (e = 0, c = 0; e < k - 1; c += 3) { - l = f; - h = b; - e++; - f = m[e]; - b = f - g[e]; - n[c] = l; - n[c + 1] = (b + 2 * h) / 3; - n[c + 2] = (b * 2 + h) / 3 - } - n[c] = f; - return n - }, - getAnchors: function(e, d, i, h, t, s, o) { - o = o || 4; - var n = Math.PI, - p = n / 2, - k = Math.abs, - a = Math.sin, - b = Math.cos, - f = Math.atan, - r, q, g, j, m, l, v, u, c; - r = (i - e) / o; - q = (t - i) / o; - if ((h >= d && h >= s) || (h <= d && h <= s)) { - g = j = p - } else { - g = f((i - e) / k(h - d)); - if (d < h) { - g = n - g - } - j = f((t - i) / k(h - s)); - if (s < h) { - j = n - j - } - } - c = p - ((g + j) % (n * 2)) / 2; - if (c > p) { - c -= n - } - g += c; - j += c; - m = i - r * a(g); - l = h + r * b(g); - v = i + q * a(j); - u = h + q * b(j); - if ((h > d && l < d) || (h < d && l > d)) { - m += k(d - l) * (m - i) / (l - h); - l = d - } - if ((h > s && u < s) || (h < s && u > s)) { - v -= k(s - u) * (v - i) / (u - h); - u = s - } - return { - x1: m, - y1: l, - x2: v, - y2: u - } - }, - smooth: function(l, j, o) { - var k = l.length, - h, g, c, b, q, p, n, m, f = [], - e = [], - d, a; - for (d = 0; d < k - 1; d++) { - h = l[d]; - g = j[d]; - if (d === 0) { - n = h; - m = g; - f.push(n); - e.push(m); - if (k === 1) { - break - } - } - c = l[d + 1]; - b = j[d + 1]; - q = l[d + 2]; - p = j[d + 2]; - if (!Ext.isNumber(q + p)) { - f.push(n, c, c); - e.push(m, b, b); - break - } - a = this.getAnchors(h, g, c, b, q, p, o); - f.push(n, a.x1, c); - e.push(m, a.y1, b); - n = a.x2; - m = a.y2 - } - return { - smoothX: f, - smoothY: e - } - }, - beginUpdateIOS: Ext.os.is.iOS ? function() { - this.iosUpdateEl = Ext.getBody().createChild({ - style: "position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; background: rgba(0,0,0,0.001); z-index: 100000" - }) - } : Ext.emptyFn, - endUpdateIOS: function() { - this.iosUpdateEl = Ext.destroy(this.iosUpdateEl) - } -}); -Ext.define("Ext.draw.gradient.Gradient", { - requires: ["Ext.draw.Color"], - isGradient: true, - config: { - stops: [] - }, - applyStops: function(f) { - var e = [], - d = f.length, - c, b, a; - for (c = 0; c < d; c++) { - b = f[c]; - a = b.color; - if (!(a && a.isColor)) { - a = Ext.draw.Color.fly(a || Ext.draw.Color.NONE) - } - e.push({ - offset: Math.min(1, Math.max(0, "offset" in b ? b.offset : b.position || 0)), - color: a.toString() - }) - } - e.sort(function(h, g) { - return h.offset - g.offset - }); - return e - }, - onClassExtended: function(a, b) { - if (!b.alias && b.type) { - b.alias = "gradient." + b.type - } - }, - constructor: function(a) { - this.initConfig(a) - }, - generateGradient: Ext.emptyFn -}); -Ext.define("Ext.draw.gradient.GradientDefinition", { - singleton: true, - urlStringRe: /^url\(#([\w\-]+)\)$/, - gradients: {}, - add: function(a) { - var b = this.gradients, - c, e, d; - for (c = 0, e = a.length; c < e; c++) { - d = a[c]; - if (Ext.isString(d.id)) { - b[d.id] = d - } - } - }, - get: function(d) { - var a = this.gradients, - b = d.match(this.urlStringRe), - c; - if (b && b[1] && (c = a[b[1]])) { - return c || d - } - return d - } -}); -Ext.define("Ext.draw.sprite.AttributeParser", { - singleton: true, - attributeRe: /^url\(#([a-zA-Z\-]+)\)$/, - requires: ["Ext.draw.Color", "Ext.draw.gradient.GradientDefinition"], - "default": Ext.identityFn, - string: function(a) { - return String(a) - }, - number: function(a) { - if (Ext.isNumber(+a)) { - return a - } - }, - angle: function(a) { - if (Ext.isNumber(a)) { - a %= Math.PI * 2; - if (a < -Math.PI) { - a += Math.PI * 2 - } else { - if (a >= Math.PI) { - a -= Math.PI * 2 - } - } - return a - } - }, - data: function(a) { - if (Ext.isArray(a)) { - return a.slice() - } else { - if (a instanceof Float32Array) { - return new Float32Array(a) - } - } - }, - bool: function(a) { - return !!a - }, - color: function(a) { - if (a instanceof Ext.draw.Color) { - return a.toString() - } else { - if (a instanceof Ext.draw.gradient.Gradient) { - return a - } else { - if (!a) { - return Ext.draw.Color.NONE - } else { - if (Ext.isString(a)) { - if (a.substr(0, 3) === "url") { - a = Ext.draw.gradient.GradientDefinition.get(a); - if (Ext.isString(a)) { - return a - } - } else { - return Ext.draw.Color.fly(a).toString() - } - } - } - } - } - if (a.type === "linear") { - return Ext.create("Ext.draw.gradient.Linear", a) - } else { - if (a.type === "radial") { - return Ext.create("Ext.draw.gradient.Radial", a) - } else { - if (a.type === "pattern") { - return Ext.create("Ext.draw.gradient.Pattern", a) - } else { - return Ext.draw.Color.NONE - } - } - } - }, - limited: function(a, b) { - return function(c) { - c = +c; - return Ext.isNumber(c) ? Math.min(Math.max(c, a), b) : undefined - } - }, - limited01: function(a) { - a = +a; - return Ext.isNumber(a) ? Math.min(Math.max(a, 0), 1) : undefined - }, - enums: function() { - var d = {}, - a = Array.prototype.slice.call(arguments, 0), - b, c; - for (b = 0, c = a.length; b < c; b++) { - d[a[b]] = true - } - return function(e) { - return e in d ? e : undefined - } - } -}); -Ext.define("Ext.draw.sprite.AttributeDefinition", { - requires: ["Ext.draw.sprite.AttributeParser", "Ext.draw.sprite.AnimationParser"], - config: { - defaults: { - $value: {}, - lazy: true - }, - aliases: {}, - animationProcessors: {}, - processors: { - $value: {}, - lazy: true - }, - dirtyTriggers: {}, - triggers: {}, - updaters: {} - }, - inheritableStatics: { - processorFactoryRe: /^(\w+)\(([\w\-,]*)\)$/ - }, - spriteClass: null, - constructor: function(a) { - var b = this; - b.initConfig(a) - }, - applyDefaults: function(b, a) { - a = Ext.apply(a || {}, this.normalize(b)); - return a - }, - applyAliases: function(b, a) { - return Ext.apply(a || {}, b) - }, - applyProcessors: function(e, i) { - this.getAnimationProcessors(); - var j = i || {}, - h = Ext.draw.sprite.AttributeParser, - a = this.self.processorFactoryRe, - g = {}, - d, b, c, f; - for (b in e) { - f = e[b]; - if (typeof f === "string") { - c = f.match(a); - if (c) { - f = h[c[1]].apply(h, c[2].split(",")) - } else { - if (h[f]) { - g[b] = f; - d = true; - f = h[f] - } - } - } - j[b] = f - } - if (d) { - this.setAnimationProcessors(g) - } - return j - }, - applyAnimationProcessors: function(c, a) { - var e = Ext.draw.sprite.AnimationParser, - b, d; - if (!a) { - a = {} - } - for (b in c) { - d = c[b]; - if (d === "none") { - a[b] = null - } else { - if (Ext.isString(d) && !(b in a)) { - if (d in e) { - while (Ext.isString(e[d])) { - d = e[d] - } - a[b] = e[d] - } - } else { - if (Ext.isObject(d)) { - a[b] = d - } - } - } - } - return a - }, - updateDirtyTriggers: function(a) { - this.setTriggers(a) - }, - applyTriggers: function(b, c) { - if (!c) { - c = {} - } - for (var a in b) { - c[a] = b[a].split(",") - } - return c - }, - applyUpdaters: function(b, a) { - return Ext.apply(a || {}, b) - }, - batchedNormalize: function(f, n) { - if (!f) { - return {} - } - var j = this.getProcessors(), - d = this.getAliases(), - a = f.translation || f.translate, - o = {}, - g, h, b, e, p, c, m, l, k; - if ("rotation" in f) { - p = f.rotation - } else { - p = ("rotate" in f) ? f.rotate : undefined - } - if ("scaling" in f) { - c = f.scaling - } else { - c = ("scale" in f) ? f.scale : undefined - } - if (typeof c !== "undefined") { - if (Ext.isNumber(c)) { - o.scalingX = c; - o.scalingY = c - } else { - if ("x" in c) { - o.scalingX = c.x - } - if ("y" in c) { - o.scalingY = c.y - } - if ("centerX" in c) { - o.scalingCenterX = c.centerX - } - if ("centerY" in c) { - o.scalingCenterY = c.centerY - } - } - } - if (typeof p !== "undefined") { - if (Ext.isNumber(p)) { - p = Ext.draw.Draw.rad(p); - o.rotationRads = p - } else { - if ("rads" in p) { - o.rotationRads = p.rads - } else { - if ("degrees" in p) { - if (Ext.isArray(p.degrees)) { - o.rotationRads = Ext.Array.map(p.degrees, function(i) { - return Ext.draw.Draw.rad(i) - }) - } else { - o.rotationRads = Ext.draw.Draw.rad(p.degrees) - } - } - } - if ("centerX" in p) { - o.rotationCenterX = p.centerX - } - if ("centerY" in p) { - o.rotationCenterY = p.centerY - } - } - } - if (typeof a !== "undefined") { - if ("x" in a) { - o.translationX = a.x - } - if ("y" in a) { - o.translationY = a.y - } - } - if ("matrix" in f) { - m = Ext.draw.Matrix.create(f.matrix); - k = m.split(); - o.matrix = m; - o.rotationRads = k.rotation; - o.rotationCenterX = 0; - o.rotationCenterY = 0; - o.scalingX = k.scaleX; - o.scalingY = k.scaleY; - o.scalingCenterX = 0; - o.scalingCenterY = 0; - o.translationX = k.translateX; - o.translationY = k.translateY - } - for (b in f) { - e = f[b]; - if (typeof e === "undefined") { - continue - } else { - if (Ext.isArray(e)) { - if (b in d) { - b = d[b] - } - if (b in j) { - o[b] = []; - for (g = 0, h = e.length; g < h; g++) { - l = j[b].call(this, e[g]); - if (typeof l !== "undefined") { - o[b][g] = l - } - } - } else { - if (n) { - o[b] = e - } - } - } else { - if (b in d) { - b = d[b] - } - if (b in j) { - e = j[b].call(this, e); - if (typeof e !== "undefined") { - o[b] = e - } - } else { - if (n) { - o[b] = e - } - } - } - } - } - return o - }, - normalize: function(i, j) { - if (!i) { - return {} - } - var f = this.getProcessors(), - d = this.getAliases(), - a = i.translation || i.translate, - k = {}, - b, e, l, c, h, g; - if ("rotation" in i) { - l = i.rotation - } else { - l = ("rotate" in i) ? i.rotate : undefined - } - if ("scaling" in i) { - c = i.scaling - } else { - c = ("scale" in i) ? i.scale : undefined - } - if (a) { - if ("x" in a) { - k.translationX = a.x - } - if ("y" in a) { - k.translationY = a.y - } - } - if (typeof c !== "undefined") { - if (Ext.isNumber(c)) { - k.scalingX = c; - k.scalingY = c - } else { - if ("x" in c) { - k.scalingX = c.x - } - if ("y" in c) { - k.scalingY = c.y - } - if ("centerX" in c) { - k.scalingCenterX = c.centerX - } - if ("centerY" in c) { - k.scalingCenterY = c.centerY - } - } - } - if (typeof l !== "undefined") { - if (Ext.isNumber(l)) { - l = Ext.draw.Draw.rad(l); - k.rotationRads = l - } else { - if ("rads" in l) { - k.rotationRads = l.rads - } else { - if ("degrees" in l) { - k.rotationRads = Ext.draw.Draw.rad(l.degrees) - } - } - if ("centerX" in l) { - k.rotationCenterX = l.centerX - } - if ("centerY" in l) { - k.rotationCenterY = l.centerY - } - } - } - if ("matrix" in i) { - h = Ext.draw.Matrix.create(i.matrix); - g = h.split(); - k.matrix = h; - k.rotationRads = g.rotation; - k.rotationCenterX = 0; - k.rotationCenterY = 0; - k.scalingX = g.scaleX; - k.scalingY = g.scaleY; - k.scalingCenterX = 0; - k.scalingCenterY = 0; - k.translationX = g.translateX; - k.translationY = g.translateY - } - for (b in i) { - e = i[b]; - if (typeof e === "undefined") { - continue - } - if (b in d) { - b = d[b] - } - if (b in f) { - e = f[b].call(this, e); - if (typeof e !== "undefined") { - k[b] = e - } - } else { - if (j) { - k[b] = e - } - } - } - return k - }, - setBypassingNormalization: function(a, c, b) { - return c.pushDown(a, b) - }, - set: function(a, c, b) { - b = this.normalize(b); - return this.setBypassingNormalization(a, c, b) - } -}); -Ext.define("Ext.draw.Matrix", { - isMatrix: true, - statics: { - createAffineMatrixFromTwoPair: function(h, t, g, s, k, o, i, j) { - var v = g - h, - u = s - t, - e = i - k, - q = j - o, - d = 1 / (v * v + u * u), - p = v * e + u * q, - n = e * u - v * q, - m = -p * h - n * t, - l = n * h - p * t; - return new this(p * d, -n * d, n * d, p * d, m * d + k, l * d + o) - }, - createPanZoomFromTwoPair: function(q, e, p, c, h, s, n, g) { - if (arguments.length === 2) { - return this.createPanZoomFromTwoPair.apply(this, q.concat(e)) - } - var k = p - q, - j = c - e, - d = (q + p) * 0.5, - b = (e + c) * 0.5, - o = n - h, - a = g - s, - f = (h + n) * 0.5, - l = (s + g) * 0.5, - m = k * k + j * j, - i = o * o + a * a, - t = Math.sqrt(i / m); - return new this(t, 0, 0, t, f - t * d, l - t * b) - }, - fly: (function() { - var a = null, - b = function(c) { - a.elements = c; - return a - }; - return function(c) { - if (!a) { - a = new Ext.draw.Matrix() - } - a.elements = c; - Ext.draw.Matrix.fly = b; - return a - } - })(), - create: function(a) { - if (a instanceof this) { - return a - } - return new this(a) - } - }, - constructor: function(e, d, a, f, c, b) { - if (e && e.length === 6) { - this.elements = e.slice() - } else { - if (e !== undefined) { - this.elements = [e, d, a, f, c, b] - } else { - this.elements = [1, 0, 0, 1, 0, 0] - } - } - }, - prepend: function(a, l, h, g, m, k) { - var b = this.elements, - d = b[0], - j = b[1], - e = b[2], - c = b[3], - i = b[4], - f = b[5]; - b[0] = a * d + h * j; - b[1] = l * d + g * j; - b[2] = a * e + h * c; - b[3] = l * e + g * c; - b[4] = a * i + h * f + m; - b[5] = l * i + g * f + k; - return this - }, - prependMatrix: function(a) { - return this.prepend.apply(this, a.elements) - }, - append: function(a, l, h, g, m, k) { - var b = this.elements, - d = b[0], - j = b[1], - e = b[2], - c = b[3], - i = b[4], - f = b[5]; - b[0] = a * d + l * e; - b[1] = a * j + l * c; - b[2] = h * d + g * e; - b[3] = h * j + g * c; - b[4] = m * d + k * e + i; - b[5] = m * j + k * c + f; - return this - }, - appendMatrix: function(a) { - return this.append.apply(this, a.elements) - }, - set: function(f, e, a, g, c, b) { - var d = this.elements; - d[0] = f; - d[1] = e; - d[2] = a; - d[3] = g; - d[4] = c; - d[5] = b; - return this - }, - inverse: function(i) { - var g = this.elements, - o = g[0], - m = g[1], - l = g[2], - k = g[3], - j = g[4], - h = g[5], - n = 1 / (o * k - m * l); - o *= n; - m *= n; - l *= n; - k *= n; - if (i) { - i.set(k, -m, -l, o, l * h - k * j, m * j - o * h); - return i - } else { - return new Ext.draw.Matrix(k, -m, -l, o, l * h - k * j, m * j - o * h) - } - }, - translate: function(a, c, b) { - if (b) { - return this.prepend(1, 0, 0, 1, a, c) - } else { - return this.append(1, 0, 0, 1, a, c) - } - }, - scale: function(f, e, c, a, b) { - var d = this; - if (e == null) { - e = f - } - if (c === undefined) { - c = 0 - } - if (a === undefined) { - a = 0 - } - if (b) { - return d.prepend(f, 0, 0, e, c - c * f, a - a * e) - } else { - return d.append(f, 0, 0, e, c - c * f, a - a * e) - } - }, - rotate: function(g, e, c, b) { - var d = this, - f = Math.cos(g), - a = Math.sin(g); - e = e || 0; - c = c || 0; - if (b) { - return d.prepend(f, a, -a, f, e - f * e + c * a, c - f * c - e * a) - } else { - return d.append(f, a, -a, f, e - f * e + c * a, c - f * c - e * a) - } - }, - rotateFromVector: function(a, h, c) { - var e = this, - g = Math.sqrt(a * a + h * h), - f = a / g, - b = h / g; - if (c) { - return e.prepend(f, b, -b, f, 0, 0) - } else { - return e.append(f, b, -b, f, 0, 0) - } - }, - clone: function() { - return new Ext.draw.Matrix(this.elements) - }, - flipX: function() { - return this.append(-1, 0, 0, 1, 0, 0) - }, - flipY: function() { - return this.append(1, 0, 0, -1, 0, 0) - }, - skewX: function(a) { - return this.append(1, 0, Math.tan(a), 1, 0, 0) - }, - skewY: function(a) { - return this.append(1, Math.tan(a), 0, 1, 0, 0) - }, - shearX: function(a) { - return this.append(1, 0, a, 1, 0, 0) - }, - shearY: function(a) { - return this.append(1, a, 0, 1, 0, 0) - }, - reset: function() { - return this.set(1, 0, 0, 1, 0, 0) - }, - precisionCompensate: function(j, g) { - var c = this.elements, - f = c[0], - e = c[1], - i = c[2], - h = c[3], - d = c[4], - b = c[5], - a = e * i - f * h; - g.b = j * e / f; - g.c = j * i / h; - g.d = j; - g.xx = f / j; - g.yy = h / j; - g.dx = (b * f * i - d * f * h) / a / j; - g.dy = (d * e * h - b * f * h) / a / j - }, - precisionCompensateRect: function(j, g) { - var b = this.elements, - f = b[0], - e = b[1], - i = b[2], - h = b[3], - c = b[4], - a = b[5], - d = i / f; - g.b = j * e / f; - g.c = j * d; - g.d = j * h / f; - g.xx = f / j; - g.yy = f / j; - g.dx = (a * i - c * h) / (e * d - h) / j; - g.dy = -(a * f - c * e) / (e * d - h) / j - }, - x: function(a, c) { - var b = this.elements; - return a * b[0] + c * b[2] + b[4] - }, - y: function(a, c) { - var b = this.elements; - return a * b[1] + c * b[3] + b[5] - }, - get: function(b, a) { - return +this.elements[b + a * 2].toFixed(4) - }, - transformPoint: function(b) { - var c = this.elements, - a, d; - if (b.isPoint) { - a = b.x; - d = b.y - } else { - a = b[0]; - d = b[1] - } - return [a * c[0] + d * c[2] + c[4], a * c[1] + d * c[3] + c[5]] - }, - transformBBox: function(q, i, j) { - var b = this.elements, - d = q.x, - r = q.y, - g = q.width * 0.5, - o = q.height * 0.5, - a = b[0], - s = b[1], - n = b[2], - k = b[3], - e = d + g, - c = r + o, - p, f, m; - if (i) { - g -= i; - o -= i; - m = [Math.sqrt(b[0] * b[0] + b[2] * b[2]), Math.sqrt(b[1] * b[1] + b[3] * b[3])]; - p = Math.abs(g * a) + Math.abs(o * n) + Math.abs(m[0] * i); - f = Math.abs(g * s) + Math.abs(o * k) + Math.abs(m[1] * i) - } else { - p = Math.abs(g * a) + Math.abs(o * n); - f = Math.abs(g * s) + Math.abs(o * k) - } - if (!j) { - j = {} - } - j.x = e * a + c * n + b[4] - p; - j.y = e * s + c * k + b[5] - f; - j.width = p + p; - j.height = f + f; - return j - }, - transformList: function(e) { - var b = this.elements, - a = b[0], - h = b[2], - l = b[4], - k = b[1], - g = b[3], - j = b[5], - f = e.length, - c, d; - for (d = 0; d < f; d++) { - c = e[d]; - e[d] = [c[0] * a + c[1] * h + l, c[0] * k + c[1] * g + j] - } - return e - }, - isIdentity: function() { - var a = this.elements; - return a[0] === 1 && a[1] === 0 && a[2] === 0 && a[3] === 1 && a[4] === 0 && a[5] === 0 - }, - isEqual: function(a) { - var c = a && a.isMatrix ? a.elements : a, - b = this.elements; - return b[0] === c[0] && b[1] === c[1] && b[2] === c[2] && b[3] === c[3] && b[4] === c[4] && b[5] === c[5] - }, - equals: function(a) { - return this.isEqual(a) - }, - toArray: function() { - var a = this.elements; - return [a[0], a[2], a[4], a[1], a[3], a[5]] - }, - toVerticalArray: function() { - return this.elements.slice() - }, - toString: function() { - var a = this; - return [a.get(0, 0), a.get(0, 1), a.get(1, 0), a.get(1, 1), a.get(2, 0), a.get(2, 1)].join(",") - }, - toContext: function(a) { - a.transform.apply(a, this.elements); - return this - }, - toSvg: function() { - var a = this.elements; - return "matrix(" + a[0].toFixed(9) + "," + a[1].toFixed(9) + "," + a[2].toFixed(9) + "," + a[3].toFixed(9) + "," + a[4].toFixed(9) + "," + a[5].toFixed(9) + ")" - }, - getScaleX: function() { - var a = this.elements; - return Math.sqrt(a[0] * a[0] + a[2] * a[2]) - }, - getScaleY: function() { - var a = this.elements; - return Math.sqrt(a[1] * a[1] + a[3] * a[3]) - }, - getXX: function() { - return this.elements[0] - }, - getXY: function() { - return this.elements[1] - }, - getYX: function() { - return this.elements[2] - }, - getYY: function() { - return this.elements[3] - }, - getDX: function() { - return this.elements[4] - }, - getDY: function() { - return this.elements[5] - }, - split: function() { - var b = this.elements, - d = b[0], - c = b[1], - e = b[3], - a = { - translateX: b[4], - translateY: b[5] - }; - a.rotate = a.rotation = Math.atan2(c, d); - a.scaleX = d / Math.cos(a.rotate); - a.scaleY = e / d * a.scaleX; - return a - } -}, function() { - function b(e, c, d) { - e[c] = { - get: function() { - return this.elements[d] - }, - set: function(f) { - this.elements[d] = f - } - } - } - if (Object.defineProperties) { - var a = {}; - b(a, "a", 0); - b(a, "b", 1); - b(a, "c", 2); - b(a, "d", 3); - b(a, "e", 4); - b(a, "f", 5); - Object.defineProperties(this.prototype, a) - } - this.prototype.multiply = this.prototype.appendMatrix -}); -Ext.define("Ext.draw.modifier.Modifier", { - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - previous: null, - next: null, - sprite: null - }, - constructor: function(a) { - this.mixins.observable.constructor.call(this, a) - }, - updateNext: function(a) { - if (a) { - a.setPrevious(this) - } - }, - updatePrevious: function(a) { - if (a) { - a.setNext(this) - } - }, - prepareAttributes: function(a) { - if (this._previous) { - this._previous.prepareAttributes(a) - } - }, - popUp: function(a, b) { - if (this._next) { - this._next.popUp(a, b) - } else { - Ext.apply(a, b) - } - }, - pushDown: function(a, c) { - if (this._previous) { - return this._previous.pushDown(a, c) - } else { - for (var b in c) { - if (c[b] === a[b]) { - delete c[b] - } - } - return c - } - } -}); -Ext.define("Ext.draw.modifier.Target", { - requires: ["Ext.draw.Matrix"], - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.target", - statics: { - uniqueId: 0 - }, - prepareAttributes: function(a) { - var b = this.getPrevious(); - if (b) { - b.prepareAttributes(a) - } - a.attributeId = "attribute-" + Ext.draw.modifier.Target.uniqueId++; - if (!a.hasOwnProperty("canvasAttributes")) { - a.bbox = { - plain: { - dirty: true - }, - transform: { - dirty: true - } - }; - a.dirty = true; - a.pendingUpdaters = {}; - a.canvasAttributes = {}; - a.matrix = new Ext.draw.Matrix(); - a.inverseMatrix = new Ext.draw.Matrix() - } - }, - applyChanges: function(f, k) { - Ext.apply(f, k); - var l = this.getSprite(), - o = f.pendingUpdaters, - h = l.self.def.getTriggers(), - p, a, m, b, e, n, d, c, g; - for (b in k) { - e = true; - if ((p = h[b])) { - l.scheduleUpdaters(f, p, [b]) - } - if (f.template && k.removeFromInstance && k.removeFromInstance[b]) { - delete f[b] - } - } - if (!e) { - return - } - if (o.canvas) { - n = o.canvas; - delete o.canvas; - for (d = 0, g = n.length; d < g; d++) { - b = n[d]; - f.canvasAttributes[b] = f[b] - } - } - if (f.hasOwnProperty("children")) { - a = f.children; - for (d = 0, g = a.length; d < g; d++) { - m = a[d]; - Ext.apply(m.pendingUpdaters, o); - if (n) { - for (c = 0; c < n.length; c++) { - b = n[c]; - m.canvasAttributes[b] = m[b] - } - } - l.callUpdaters(m) - } - } - l.setDirty(true); - l.callUpdaters(f) - }, - popUp: function(a, b) { - this.applyChanges(a, b) - }, - pushDown: function(a, b) { - var c = this.getPrevious(); - if (c) { - b = c.pushDown(a, b) - } - this.applyChanges(a, b); - return b - } -}); -Ext.define("Ext.draw.TimingFunctions", function() { - var g = Math.pow, - j = Math.sin, - m = Math.cos, - l = Math.sqrt, - e = Math.PI, - b = ["quad", "cube", "quart", "quint"], - c = { - pow: function(o, i) { - return g(o, i || 6) - }, - expo: function(i) { - return g(2, 8 * (i - 1)) - }, - circ: function(i) { - return 1 - l(1 - i * i) - }, - sine: function(i) { - return 1 - j((1 - i) * e / 2) - }, - back: function(i, o) { - o = o || 1.616; - return i * i * ((o + 1) * i - o) - }, - bounce: function(q) { - for (var o = 0, i = 1; 1; o += i, i /= 2) { - if (q >= (7 - 4 * o) / 11) { - return i * i - g((11 - 6 * o - 11 * q) / 4, 2) - } - } - }, - elastic: function(o, i) { - return g(2, 10 * --o) * m(20 * o * e * (i || 1) / 3) - } - }, - k = {}, - a, f, d; - - function h(i) { - return function(o) { - return g(o, i) - } - } - - function n(i, o) { - k[i + "In"] = function(p) { - return o(p) - }; - k[i + "Out"] = function(p) { - return 1 - o(1 - p) - }; - k[i + "InOut"] = function(p) { - return (p <= 0.5) ? o(2 * p) / 2 : (2 - o(2 * (1 - p))) / 2 - } - } - for (d = 0, f = b.length; d < f; ++d) { - c[b[d]] = h(d + 2) - } - for (a in c) { - n(a, c[a]) - } - k.linear = Ext.identityFn; - k.easeIn = k.quadIn; - k.easeOut = k.quadOut; - k.easeInOut = k.quadInOut; - return { - singleton: true, - easingMap: k - } -}, function(a) { - Ext.apply(a, a.easingMap) -}); -Ext.define("Ext.draw.Animator", { - uses: ["Ext.draw.Draw"], - singleton: true, - frameCallbacks: {}, - frameCallbackId: 0, - scheduled: 0, - frameStartTimeOffset: Ext.now(), - animations: [], - running: false, - animationTime: function() { - return Ext.AnimationQueue.frameStartTime - this.frameStartTimeOffset - }, - add: function(b) { - var a = this; - if (!a.contains(b)) { - a.animations.push(b); - a.ignite(); - if ("fireEvent" in b) { - b.fireEvent("animationstart", b) - } - } - }, - remove: function(d) { - var c = this, - e = c.animations, - b = 0, - a = e.length; - for (; b < a; ++b) { - if (e[b] === d) { - e.splice(b, 1); - if ("fireEvent" in d) { - d.fireEvent("animationend", d) - } - return - } - } - }, - contains: function(a) { - return Ext.Array.indexOf(this.animations, a) > -1 - }, - empty: function() { - return this.animations.length === 0 - }, - step: function(d) { - var c = this, - f = c.animations, - e, a = 0, - b = f.length; - for (; a < b; a++) { - e = f[a]; - e.step(d); - if (!e.animating) { - f.splice(a, 1); - a--; - b--; - if (e.fireEvent) { - e.fireEvent("animationend", e) - } - } - } - }, - schedule: function(c, a) { - a = a || this; - var b = "frameCallback" + (this.frameCallbackId++); - if (Ext.isString(c)) { - c = a[c] - } - Ext.draw.Animator.frameCallbacks[b] = { - fn: c, - scope: a, - once: true - }; - this.scheduled++; - Ext.draw.Animator.ignite(); - return b - }, - scheduleIf: function(e, b) { - b = b || this; - var c = Ext.draw.Animator.frameCallbacks, - a, d; - if (Ext.isString(e)) { - e = b[e] - } - for (d in c) { - a = c[d]; - if (a.once && a.fn === e && a.scope === b) { - return null - } - } - return this.schedule(e, b) - }, - cancel: function(a) { - if (Ext.draw.Animator.frameCallbacks[a] && Ext.draw.Animator.frameCallbacks[a].once) { - this.scheduled--; - delete Ext.draw.Animator.frameCallbacks[a] - } - }, - addFrameCallback: function(c, a) { - a = a || this; - if (Ext.isString(c)) { - c = a[c] - } - var b = "frameCallback" + (this.frameCallbackId++); - Ext.draw.Animator.frameCallbacks[b] = { - fn: c, - scope: a - }; - return b - }, - removeFrameCallback: function(a) { - delete Ext.draw.Animator.frameCallbacks[a] - }, - fireFrameCallbacks: function() { - var c = this.frameCallbacks, - d, b, a; - for (d in c) { - a = c[d]; - b = a.fn; - if (Ext.isString(b)) { - b = a.scope[b] - } - b.call(a.scope); - if (c[d] && a.once) { - this.scheduled--; - delete c[d] - } - } - }, - handleFrame: function() { - this.step(this.animationTime()); - this.fireFrameCallbacks(); - if (!this.scheduled && this.empty()) { - Ext.AnimationQueue.stop(this.handleFrame, this); - this.running = false; - Ext.draw.Draw.endUpdateIOS() - } - }, - ignite: function() { - if (!this.running) { - this.running = true; - Ext.AnimationQueue.start(this.handleFrame, this); - Ext.draw.Draw.beginUpdateIOS() - } - } -}); -Ext.define("Ext.draw.modifier.Animation", { - requires: ["Ext.draw.TimingFunctions", "Ext.draw.Animator"], - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.animation", - config: { - easing: Ext.identityFn, - duration: 0, - customEasings: {}, - customDurations: {}, - customDuration: null - }, - constructor: function(a) { - var b = this; - b.anyAnimation = b.anySpecialAnimations = false; - b.animating = 0; - b.animatingPool = []; - b.callParent([a]) - }, - prepareAttributes: function(a) { - if (!a.hasOwnProperty("timers")) { - a.animating = false; - a.timers = {}; - a.animationOriginal = Ext.Object.chain(a); - a.animationOriginal.prototype = a - } - if (this._previous) { - this._previous.prepareAttributes(a.animationOriginal) - } - }, - updateSprite: function(a) { - this.setConfig(a.config.fx) - }, - updateDuration: function(a) { - this.anyAnimation = a > 0 - }, - applyEasing: function(a) { - if (typeof a === "string") { - a = Ext.draw.TimingFunctions.easingMap[a] - } - return a - }, - applyCustomEasings: function(a, e) { - e = e || {}; - var g, d, b, h, c, f; - for (d in a) { - g = true; - h = a[d]; - b = d.split(","); - if (typeof h === "string") { - h = Ext.draw.TimingFunctions.easingMap[h] - } - for (c = 0, f = b.length; c < f; c++) { - e[b[c]] = h - } - } - if (g) { - this.anySpecialAnimations = g - } - return e - }, - setEasingOn: function(a, e) { - a = Ext.Array.from(a).slice(); - var c = {}, - d = a.length, - b = 0; - for (; b < d; b++) { - c[a[b]] = e - } - this.setCustomEasings(c) - }, - clearEasingOn: function(a) { - a = Ext.Array.from(a, true); - var b = 0, - c = a.length; - for (; b < c; b++) { - delete this._customEasings[a[b]] - } - }, - applyCustomDurations: function(g, h) { - h = h || {}; - var e, c, f, a, b, d; - for (c in g) { - e = true; - f = g[c]; - a = c.split(","); - for (b = 0, d = a.length; b < d; b++) { - h[a[b]] = f - } - } - if (e) { - this.anySpecialAnimations = e - } - return h - }, - applyCustomDuration: function(a, b) { - if (a) { - this.getCustomDurations(); - this.setCustomDurations(a) - } - }, - setDurationOn: function(b, e) { - b = Ext.Array.from(b).slice(); - var a = {}, - c = 0, - d = b.length; - for (; c < d; c++) { - a[b[c]] = e - } - this.setCustomDurations(a) - }, - clearDurationOn: function(a) { - a = Ext.Array.from(a, true); - var b = 0, - c = a.length; - for (; b < c; b++) { - delete this._customDurations[a[b]] - } - }, - setAnimating: function(a, b) { - var e = this, - d = e.animatingPool; - if (a.animating !== b) { - a.animating = b; - if (b) { - d.push(a); - if (e.animating === 0) { - Ext.draw.Animator.add(e) - } - e.animating++ - } else { - for (var c = d.length; c--;) { - if (d[c] === a) { - d.splice(c, 1) - } - } - e.animating = d.length - } - } - }, - setAttrs: function(r, t) { - var s = this, - m = r.timers, - h = s._sprite.self.def._animationProcessors, - f = s._easing, - e = s._duration, - j = s._customDurations, - i = s._customEasings, - g = s.anySpecialAnimations, - n = s.anyAnimation || g, - o = r.animationOriginal, - d = false, - k, u, l, p, c, q, a; - if (!n) { - for (u in t) { - if (r[u] === t[u]) { - delete t[u] - } else { - r[u] = t[u] - } - delete o[u]; - delete m[u] - } - return t - } else { - for (u in t) { - l = t[u]; - p = r[u]; - if (l !== p && p !== undefined && p !== null && (c = h[u])) { - q = f; - a = e; - if (g) { - if (u in i) { - q = i[u] - } - if (u in j) { - a = j[u] - } - } - if (p && p.isGradient || l && l.isGradient) { - a = 0 - } - if (a) { - if (!m[u]) { - m[u] = {} - } - k = m[u]; - k.start = 0; - k.easing = q; - k.duration = a; - k.compute = c.compute; - k.serve = c.serve || Ext.identityFn; - k.remove = t.removeFromInstance && t.removeFromInstance[u]; - if (c.parseInitial) { - var b = c.parseInitial(p, l); - k.source = b[0]; - k.target = b[1] - } else { - if (c.parse) { - k.source = c.parse(p); - k.target = c.parse(l) - } else { - k.source = p; - k.target = l - } - } - o[u] = l; - delete t[u]; - d = true; - continue - } else { - delete o[u] - } - } else { - delete o[u] - } - delete m[u] - } - } - if (d && !r.animating) { - s.setAnimating(r, true) - } - return t - }, - updateAttributes: function(g) { - if (!g.animating) { - return {} - } - var h = {}, - e = false, - d = g.timers, - f = g.animationOriginal, - c = Ext.draw.Animator.animationTime(), - a, b, i; - if (g.lastUpdate === c) { - return null - } - for (a in d) { - b = d[a]; - if (!b.start) { - b.start = c; - i = 0 - } else { - i = (c - b.start) / b.duration - } - if (i >= 1) { - h[a] = f[a]; - delete f[a]; - if (d[a].remove) { - h.removeFromInstance = h.removeFromInstance || {}; - h.removeFromInstance[a] = true - } - delete d[a] - } else { - h[a] = b.serve(b.compute(b.source, b.target, b.easing(i), g[a])); - e = true - } - } - g.lastUpdate = c; - this.setAnimating(g, e); - return h - }, - pushDown: function(a, b) { - b = this.callParent([a.animationOriginal, b]); - return this.setAttrs(a, b) - }, - popUp: function(a, b) { - a = a.prototype; - b = this.setAttrs(a, b); - if (this._next) { - return this._next.popUp(a, b) - } else { - return Ext.apply(a, b) - } - }, - step: function(g) { - var f = this, - c = f.animatingPool.slice(), - e = c.length, - b = 0, - a, d; - for (; b < e; b++) { - a = c[b]; - d = f.updateAttributes(a); - if (d && f._next) { - f._next.popUp(a, d) - } - } - }, - stop: function() { - this.step(); - var d = this, - b = d.animatingPool, - a, c; - for (a = 0, c = b.length; a < c; a++) { - b[a].animating = false - } - d.animatingPool.length = 0; - d.animating = 0; - Ext.draw.Animator.remove(d) - }, - destroy: function() { - this.animatingPool.length = 0; - this.animating = 0; - this.callParent() - } -}); -Ext.define("Ext.draw.modifier.Highlight", { - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.highlight", - config: { - enabled: false, - highlightStyle: null - }, - preFx: true, - applyHighlightStyle: function(b, a) { - a = a || {}; - if (this.getSprite()) { - Ext.apply(a, this.getSprite().self.def.normalize(b)) - } else { - Ext.apply(a, b) - } - return a - }, - prepareAttributes: function(a) { - if (!a.hasOwnProperty("highlightOriginal")) { - a.highlighted = false; - a.highlightOriginal = Ext.Object.chain(a); - a.highlightOriginal.prototype = a; - a.highlightOriginal.removeFromInstance = {} - } - if (this._previous) { - this._previous.prepareAttributes(a.highlightOriginal) - } - }, - updateSprite: function(b, a) { - if (b) { - if (this.getHighlightStyle()) { - this._highlightStyle = b.self.def.normalize(this.getHighlightStyle()) - } - this.setHighlightStyle(b.config.highlight) - } - b.self.def.setConfig({ - defaults: { - highlighted: false - }, - processors: { - highlighted: "bool" - } - }); - this.setSprite(b) - }, - filterChanges: function(a, d) { - var e = this, - f = a.highlightOriginal, - c = e.getHighlightStyle(), - b; - if (a.highlighted) { - for (b in d) { - if (c.hasOwnProperty(b)) { - f[b] = d[b]; - delete d[b] - } - } - } - for (b in d) { - if (b !== "highlighted" && f[b] === d[b]) { - delete d[b] - } - } - return d - }, - pushDown: function(e, g) { - var f = this.getHighlightStyle(), - c = e.highlightOriginal, - i = c.removeFromInstance, - d, a, h, b; - if (g.hasOwnProperty("highlighted")) { - d = g.highlighted; - delete g.highlighted; - if (this._previous) { - g = this._previous.pushDown(c, g) - } - g = this.filterChanges(e, g); - if (d !== e.highlighted) { - if (d) { - for (a in f) { - if (a in g) { - c[a] = g[a] - } else { - h = e.template && e.template.ownAttr; - if (h && !e.prototype.hasOwnProperty(a)) { - i[a] = true; - c[a] = h.animationOriginal[a] - } else { - b = c.timers[a]; - if (b && b.remove) { - i[a] = true - } - c[a] = e[a] - } - } - if (c[a] !== f[a]) { - g[a] = f[a] - } - } - } else { - for (a in f) { - if (!(a in g)) { - g[a] = c[a] - } - delete c[a] - } - g.removeFromInstance = g.removeFromInstance || {}; - Ext.apply(g.removeFromInstance, i); - c.removeFromInstance = {} - } - g.highlighted = d - } - } else { - if (this._previous) { - g = this._previous.pushDown(c, g) - } - g = this.filterChanges(e, g) - } - return g - }, - popUp: function(a, b) { - b = this.filterChanges(a, b); - Ext.draw.modifier.Modifier.prototype.popUp.call(this, a, b) - } -}); -Ext.define("Ext.draw.sprite.Sprite", { - alias: "sprite.sprite", - mixins: { - observable: "Ext.mixin.Observable" - }, - requires: ["Ext.draw.Draw", "Ext.draw.gradient.Gradient", "Ext.draw.sprite.AttributeDefinition", "Ext.draw.modifier.Target", "Ext.draw.modifier.Animation", "Ext.draw.modifier.Highlight"], - isSprite: true, - statics: { - defaultHitTestOptions: { - fill: true, - stroke: true - } - }, - inheritableStatics: { - def: { - processors: { - strokeStyle: "color", - fillStyle: "color", - strokeOpacity: "limited01", - fillOpacity: "limited01", - lineWidth: "number", - lineCap: "enums(butt,round,square)", - lineJoin: "enums(round,bevel,miter)", - lineDash: "data", - lineDashOffset: "number", - miterLimit: "number", - shadowColor: "color", - shadowOffsetX: "number", - shadowOffsetY: "number", - shadowBlur: "number", - globalAlpha: "limited01", - globalCompositeOperation: "enums(source-over,destination-over,source-in,destination-in,source-out,destination-out,source-atop,destination-atop,lighter,xor,copy)", - hidden: "bool", - transformFillStroke: "bool", - zIndex: "number", - translationX: "number", - translationY: "number", - rotationRads: "number", - rotationCenterX: "number", - rotationCenterY: "number", - scalingX: "number", - scalingY: "number", - scalingCenterX: "number", - scalingCenterY: "number", - constrainGradients: "bool" - }, - aliases: { - stroke: "strokeStyle", - fill: "fillStyle", - color: "fillStyle", - "stroke-width": "lineWidth", - "stroke-linecap": "lineCap", - "stroke-linejoin": "lineJoin", - "stroke-miterlimit": "miterLimit", - "text-anchor": "textAlign", - opacity: "globalAlpha", - translateX: "translationX", - translateY: "translationY", - rotateRads: "rotationRads", - rotateCenterX: "rotationCenterX", - rotateCenterY: "rotationCenterY", - scaleX: "scalingX", - scaleY: "scalingY", - scaleCenterX: "scalingCenterX", - scaleCenterY: "scalingCenterY" - }, - defaults: { - hidden: false, - zIndex: 0, - strokeStyle: "none", - fillStyle: "none", - lineWidth: 1, - lineDash: [], - lineDashOffset: 0, - lineCap: "butt", - lineJoin: "miter", - miterLimit: 10, - shadowColor: "none", - shadowOffsetX: 0, - shadowOffsetY: 0, - shadowBlur: 0, - globalAlpha: 1, - strokeOpacity: 1, - fillOpacity: 1, - transformFillStroke: false, - translationX: 0, - translationY: 0, - rotationRads: 0, - rotationCenterX: null, - rotationCenterY: null, - scalingX: 1, - scalingY: 1, - scalingCenterX: null, - scalingCenterY: null, - constrainGradients: false - }, - triggers: { - zIndex: "zIndex", - globalAlpha: "canvas", - globalCompositeOperation: "canvas", - transformFillStroke: "canvas", - strokeStyle: "canvas", - fillStyle: "canvas", - strokeOpacity: "canvas", - fillOpacity: "canvas", - lineWidth: "canvas", - lineCap: "canvas", - lineJoin: "canvas", - lineDash: "canvas", - lineDashOffset: "canvas", - miterLimit: "canvas", - shadowColor: "canvas", - shadowOffsetX: "canvas", - shadowOffsetY: "canvas", - shadowBlur: "canvas", - translationX: "transform", - translationY: "transform", - rotationRads: "transform", - rotationCenterX: "transform", - rotationCenterY: "transform", - scalingX: "transform", - scalingY: "transform", - scalingCenterX: "transform", - scalingCenterY: "transform", - constrainGradients: "canvas" - }, - updaters: { - bbox: "bboxUpdater", - zIndex: function(a) { - a.dirtyZIndex = true - }, - transform: function(a) { - a.dirtyTransform = true; - a.bbox.transform.dirty = true - } - } - } - }, - config: { - parent: null, - surface: null - }, - onClassExtended: function(d, c) { - var b = d.superclass.self.def.initialConfig, - e = c.inheritableStatics && c.inheritableStatics.def, - a; - if (e) { - a = Ext.Object.merge({}, b, e); - d.def = new Ext.draw.sprite.AttributeDefinition(a); - delete c.inheritableStatics.def - } else { - d.def = new Ext.draw.sprite.AttributeDefinition(b) - } - d.def.spriteClass = d - }, - constructor: function(b) { - var d = this, - c = d.self.def, - e = c.getDefaults(), - a; - b = Ext.isObject(b) ? b : {}; - d.id = b.id || Ext.id(null, "ext-sprite-"); - d.attr = {}; - d.mixins.observable.constructor.apply(d, arguments); - a = Ext.Array.from(b.modifiers, true); - d.prepareModifiers(a); - d.initializeAttributes(); - d.setAttributes(e, true); - d.setAttributes(b) - }, - getDirty: function() { - return this.attr.dirty - }, - setDirty: function(b) { - this.attr.dirty = b; - if (b) { - var a = this.getParent(); - if (a) { - a.setDirty(true) - } - } - }, - addModifier: function(a, b) { - var c = this; - if (!(a instanceof Ext.draw.modifier.Modifier)) { - a = Ext.factory(a, null, null, "modifier") - } - a.setSprite(c); - if (a.preFx || a.config && a.config.preFx) { - if (c.fx.getPrevious()) { - c.fx.getPrevious().setNext(a) - } - a.setNext(c.fx) - } else { - c.topModifier.getPrevious().setNext(a); - a.setNext(c.topModifier) - } - if (b) { - c.initializeAttributes() - } - return a - }, - prepareModifiers: function(d) { - var c = this, - a, b; - c.topModifier = new Ext.draw.modifier.Target({ - sprite: c - }); - c.fx = new Ext.draw.modifier.Animation({ - sprite: c - }); - c.fx.setNext(c.topModifier); - for (a = 0, b = d.length; a < b; a++) { - c.addModifier(d[a], false) - } - }, - getAnimation: function() { - return this.fx - }, - setAnimation: function(a) { - this.fx.setConfig(a) - }, - initializeAttributes: function() { - this.topModifier.prepareAttributes(this.attr) - }, - callUpdaters: function(d) { - var e = this, - h = d.pendingUpdaters, - i = e.self.def.getUpdaters(), - c = false, - a = false, - b, g, f; - e.callUpdaters = Ext.emptyFn; - do { - c = false; - for (g in h) { - c = true; - b = h[g]; - delete h[g]; - f = i[g]; - if (typeof f === "string") { - f = e[f] - } - if (f) { - f.call(e, d, b) - } - } - a = a || c - } while (c); - delete e.callUpdaters; - if (a) { - e.setDirty(true) - } - }, - scheduleUpdaters: function(a, e, c) { - var f; - if (c) { - for (var b = 0, d = e.length; b < d; b++) { - f = e[b]; - this.scheduleUpdater(a, f, c) - } - } else { - for (f in e) { - c = e[f]; - this.scheduleUpdater(a, f, c) - } - } - }, - scheduleUpdater: function(a, c, b) { - b = b || []; - var d = a.pendingUpdaters; - if (c in d) { - if (b.length) { - d[c] = Ext.Array.merge(d[c], b) - } - } else { - d[c] = b - } - }, - setAttributes: function(d, g, c) { - var a = this.attr, - b, e, f; - if (g) { - if (c) { - this.topModifier.pushDown(a, d) - } else { - f = {}; - for (b in d) { - e = d[b]; - if (e !== a[b]) { - f[b] = e - } - } - this.topModifier.pushDown(a, f) - } - } else { - this.topModifier.pushDown(a, this.self.def.normalize(d)) - } - }, - setAttributesBypassingNormalization: function(b, a) { - return this.setAttributes(b, true, a) - }, - bboxUpdater: function(b) { - var c = b.rotationRads !== 0, - a = b.scalingX !== 1 || b.scalingY !== 1, - d = b.rotationCenterX === null || b.rotationCenterY === null, - e = b.scalingCenterX === null || b.scalingCenterY === null; - b.bbox.plain.dirty = true; - b.bbox.transform.dirty = true; - if (c && d || a && e) { - this.scheduleUpdater(b, "transform") - } - }, - getBBox: function(d) { - var e = this, - a = e.attr, - f = a.bbox, - c = f.plain, - b = f.transform; - if (c.dirty) { - e.updatePlainBBox(c); - c.dirty = false - } - if (!d) { - e.applyTransformations(); - if (b.dirty) { - e.updateTransformedBBox(b, c); - b.dirty = false - } - return b - } - return c - }, - updatePlainBBox: Ext.emptyFn, - updateTransformedBBox: function(a, b) { - this.attr.matrix.transformBBox(b, 0, a) - }, - getBBoxCenter: function(a) { - var b = this.getBBox(a); - if (b) { - return [b.x + b.width * 0.5, b.y + b.height * 0.5] - } else { - return [0, 0] - } - }, - hide: function() { - this.attr.hidden = true; - this.setDirty(true); - return this - }, - show: function() { - this.attr.hidden = false; - this.setDirty(true); - return this - }, - useAttributes: function(i, f) { - this.applyTransformations(); - var d = this.attr, - h = d.canvasAttributes, - e = h.strokeStyle, - g = h.fillStyle, - b = h.lineDash, - c = h.lineDashOffset, - a; - if (e) { - if (e.isGradient) { - i.strokeStyle = "black"; - i.strokeGradient = e - } else { - i.strokeGradient = false - } - } - if (g) { - if (g.isGradient) { - i.fillStyle = "black"; - i.fillGradient = g - } else { - i.fillGradient = false - } - } - if (b) { - i.setLineDash(b) - } - if (Ext.isNumber(c + i.lineDashOffset)) { - i.lineDashOffset = c - } - for (a in h) { - if (h[a] !== undefined && h[a] !== i[a]) { - i[a] = h[a] - } - } - this.setGradientBBox(i, f) - }, - setGradientBBox: function(b, c) { - var a = this.attr; - if (a.constrainGradients) { - b.setGradientBBox({ - x: c[0], - y: c[1], - width: c[2], - height: c[3] - }) - } else { - b.setGradientBBox(this.getBBox(a.transformFillStroke)) - } - }, - applyTransformations: function(b) { - if (!b && !this.attr.dirtyTransform) { - return - } - var r = this, - k = r.attr, - p = r.getBBoxCenter(true), - g = p[0], - f = p[1], - q = k.translationX, - o = k.translationY, - j = k.scalingX, - i = k.scalingY === null ? k.scalingX : k.scalingY, - m = k.scalingCenterX === null ? g : k.scalingCenterX, - l = k.scalingCenterY === null ? f : k.scalingCenterY, - s = k.rotationRads, - e = k.rotationCenterX === null ? g : k.rotationCenterX, - d = k.rotationCenterY === null ? f : k.rotationCenterY, - c = Math.cos(s), - a = Math.sin(s), - n, h; - if (j === 1 && i === 1) { - m = 0; - l = 0 - } - if (s === 0) { - e = 0; - d = 0 - } - n = m * (1 - j) - e; - h = l * (1 - i) - d; - k.matrix.elements = [c * j, a * j, -a * i, c * i, c * n - a * h + e + q, a * n + c * h + d + o]; - k.matrix.inverse(k.inverseMatrix); - k.dirtyTransform = false; - k.bbox.transform.dirty = true - }, - transform: function(b, c) { - var a = this.attr, - e = a.matrix, - d; - if (b && b.isMatrix) { - d = b.elements - } else { - d = b - } - e.prepend.apply(e, d.slice()); - e.inverse(a.inverseMatrix); - if (c) { - this.updateTransformAttributes() - } - a.dirtyTransform = false; - a.bbox.transform.dirty = true; - this.setDirty(true); - return this - }, - updateTransformAttributes: function() { - var a = this.attr, - b = a.matrix.split(); - a.rotationRads = b.rotate; - a.rotationCenterX = 0; - a.rotationCenterY = 0; - a.scalingX = b.scaleX; - a.scalingY = b.scaleY; - a.scalingCenterX = 0; - a.scalingCenterY = 0; - a.translationX = b.translateX; - a.translationY = b.translateY - }, - resetTransform: function(b) { - var a = this.attr; - a.matrix.reset(); - a.inverseMatrix.reset(); - if (!b) { - this.updateTransformAttributes() - } - a.dirtyTransform = false; - a.bbox.transform.dirty = true; - this.setDirty(true); - return this - }, - setTransform: function(a, b) { - this.resetTransform(true); - this.transform.call(this, a, b); - return this - }, - preRender: Ext.emptyFn, - render: Ext.emptyFn, - hitTest: function(b, c) { - if (this.isVisible()) { - var a = b[0], - f = b[1], - e = this.getBBox(), - d = e && a >= e.x && a <= (e.x + e.width) && f >= e.y && f <= (e.y + e.height); - if (d) { - return { - sprite: this - } - } - } - return null - }, - isVisible: function() { - var e = this.attr, - f = this.getParent(), - g = f && (f.isSurface || f.isVisible()), - d = g && !e.hidden && e.globalAlpha, - b = Ext.draw.Color.NONE, - a = Ext.draw.Color.RGBA_NONE, - c = e.fillOpacity && e.fillStyle !== b && e.fillStyle !== a, - i = e.strokeOpacity && e.strokeStyle !== b && e.strokeStyle !== a, - h = d && (c || i); - return !!h - }, - repaint: function() { - var a = this.getSurface(); - if (a) { - a.renderFrame() - } - }, - remove: function() { - var a = this.getSurface(); - if (a && a.isSurface) { - return a.remove(this) - } - return null - }, - destroy: function() { - var b = this, - a = b.topModifier, - c; - while (a) { - c = a; - a = a.getPrevious(); - c.destroy() - } - delete b.attr; - b.remove(); - if (b.fireEvent("beforedestroy", b) !== false) { - b.fireEvent("destroy", b) - } - b.callParent() - } -}, function() { - this.def = new Ext.draw.sprite.AttributeDefinition(this.def); - this.def.spriteClass = this -}); -Ext.define("Ext.draw.Path", { - requires: ["Ext.draw.Draw"], - statics: { - pathRe: /,?([achlmqrstvxz]),?/gi, - pathRe2: /-/gi, - pathSplitRe: /\s|,/g - }, - svgString: "", - constructor: function(a) { - var b = this; - b.commands = []; - b.params = []; - b.cursor = null; - b.startX = 0; - b.startY = 0; - if (a) { - b.fromSvgString(a) - } - }, - clear: function() { - var a = this; - a.params.length = 0; - a.commands.length = 0; - a.cursor = null; - a.startX = 0; - a.startY = 0; - a.dirt() - }, - dirt: function() { - this.svgString = "" - }, - moveTo: function(a, c) { - var b = this; - if (!b.cursor) { - b.cursor = [a, c] - } - b.params.push(a, c); - b.commands.push("M"); - b.startX = a; - b.startY = c; - b.cursor[0] = a; - b.cursor[1] = c; - b.dirt() - }, - lineTo: function(a, c) { - var b = this; - if (!b.cursor) { - b.cursor = [a, c]; - b.params.push(a, c); - b.commands.push("M") - } else { - b.params.push(a, c); - b.commands.push("L") - } - b.cursor[0] = a; - b.cursor[1] = c; - b.dirt() - }, - bezierCurveTo: function(c, e, b, d, a, g) { - var f = this; - if (!f.cursor) { - f.moveTo(c, e) - } - f.params.push(c, e, b, d, a, g); - f.commands.push("C"); - f.cursor[0] = a; - f.cursor[1] = g; - f.dirt() - }, - quadraticCurveTo: function(b, e, a, d) { - var c = this; - if (!c.cursor) { - c.moveTo(b, e) - } - c.bezierCurveTo((2 * b + c.cursor[0]) / 3, (2 * e + c.cursor[1]) / 3, (2 * b + a) / 3, (2 * e + d) / 3, a, d) - }, - closePath: function() { - var a = this; - if (a.cursor) { - a.cursor = null; - a.commands.push("Z"); - a.dirt() - } - }, - arcTo: function(A, f, z, d, j, i, v) { - var E = this; - if (i === undefined) { - i = j - } - if (v === undefined) { - v = 0 - } - if (!E.cursor) { - E.moveTo(A, f); - return - } - if (j === 0 || i === 0) { - E.lineTo(A, f); - return - } - z -= A; - d -= f; - var B = E.cursor[0] - A, - g = E.cursor[1] - f, - C = z * g - d * B, - b, a, l, r, k, q, x = Math.sqrt(B * B + g * g), - u = Math.sqrt(z * z + d * d), - t, e, c; - if (C === 0) { - E.lineTo(A, f); - return - } - if (i !== j) { - b = Math.cos(v); - a = Math.sin(v); - l = b / j; - r = a / i; - k = -a / j; - q = b / i; - var D = l * B + r * g; - g = k * B + q * g; - B = D; - D = l * z + r * d; - d = k * z + q * d; - z = D - } else { - B /= j; - g /= i; - z /= j; - d /= i - } - e = B * u + z * x; - c = g * u + d * x; - t = 1 / (Math.sin(Math.asin(Math.abs(C) / (x * u)) * 0.5) * Math.sqrt(e * e + c * c)); - e *= t; - c *= t; - var o = (e * B + c * g) / (B * B + g * g), - m = (e * z + c * d) / (z * z + d * d); - var n = B * o - e, - p = g * o - c, - h = z * m - e, - y = d * m - c, - w = Math.atan2(p, n), - s = Math.atan2(y, h); - if (C > 0) { - if (s < w) { - s += Math.PI * 2 - } - } else { - if (w < s) { - w += Math.PI * 2 - } - } - if (i !== j) { - e = b * e * j - a * c * i + A; - c = a * c * i + b * c * i + f; - E.lineTo(b * j * n - a * i * p + e, a * j * n + b * i * p + c); - E.ellipse(e, c, j, i, v, w, s, C < 0) - } else { - e = e * j + A; - c = c * i + f; - E.lineTo(j * n + e, i * p + c); - E.ellipse(e, c, j, i, v, w, s, C < 0) - } - }, - ellipse: function(h, f, c, a, q, n, d, e) { - var o = this, - g = o.params, - b = g.length, - m, l, k; - if (d - n >= Math.PI * 2) { - o.ellipse(h, f, c, a, q, n, n + Math.PI, e); - o.ellipse(h, f, c, a, q, n + Math.PI, d, e); - return - } - if (!e) { - if (d < n) { - d += Math.PI * 2 - } - m = o.approximateArc(g, h, f, c, a, q, n, d) - } else { - if (n < d) { - n += Math.PI * 2 - } - m = o.approximateArc(g, h, f, c, a, q, d, n); - for (l = b, k = g.length - 2; l < k; l += 2, k -= 2) { - var p = g[l]; - g[l] = g[k]; - g[k] = p; - p = g[l + 1]; - g[l + 1] = g[k + 1]; - g[k + 1] = p - } - } - if (!o.cursor) { - o.cursor = [g[g.length - 2], g[g.length - 1]]; - o.commands.push("M") - } else { - o.cursor[0] = g[g.length - 2]; - o.cursor[1] = g[g.length - 1]; - o.commands.push("L") - } - for (l = 2; l < m; l += 6) { - o.commands.push("C") - } - o.dirt() - }, - arc: function(b, f, a, d, c, e) { - this.ellipse(b, f, a, a, 0, d, c, e) - }, - rect: function(b, e, c, a) { - if (c == 0 || a == 0) { - return - } - var d = this; - d.moveTo(b, e); - d.lineTo(b + c, e); - d.lineTo(b + c, e + a); - d.lineTo(b, e + a); - d.closePath() - }, - approximateArc: function(s, i, f, o, n, d, x, v) { - var e = Math.cos(d), - z = Math.sin(d), - k = Math.cos(x), - l = Math.sin(x), - q = e * k * o - z * l * n, - y = -e * l * o - z * k * n, - p = z * k * o + e * l * n, - w = -z * l * o + e * k * n, - m = Math.PI / 2, - r = 2, - j = q, - u = y, - h = p, - t = w, - b = 0.547443256150549, - C, g, A, a, B, c; - v -= x; - if (v < 0) { - v += Math.PI * 2 - } - s.push(q + i, p + f); - while (v >= m) { - s.push(j + u * b + i, h + t * b + f, j * b + u + i, h * b + t + f, u + i, t + f); - r += 6; - v -= m; - C = j; - j = u; - u = -C; - C = h; - h = t; - t = -C - } - if (v) { - g = (0.3294738052815987 + 0.012120855841304373 * v) * v; - A = Math.cos(v); - a = Math.sin(v); - B = A + g * a; - c = a - g * A; - s.push(j + u * g + i, h + t * g + f, j * B + u * c + i, h * B + t * c + f, j * A + u * a + i, h * A + t * a + f); - r += 6 - } - return r - }, - arcSvg: function(j, h, r, m, w, t, c) { - if (j < 0) { - j = -j - } - if (h < 0) { - h = -h - } - var x = this, - u = x.cursor[0], - f = x.cursor[1], - a = (u - t) / 2, - y = (f - c) / 2, - d = Math.cos(r), - s = Math.sin(r), - o = a * d + y * s, - v = -a * s + y * d, - i = o / j, - g = v / h, - p = i * i + g * g, - e = (u + t) * 0.5, - b = (f + c) * 0.5, - l = 0, - k = 0; - if (p >= 1) { - p = Math.sqrt(p); - j *= p; - h *= p - } else { - p = Math.sqrt(1 / p - 1); - if (m === w) { - p = -p - } - l = p * j * g; - k = -p * h * i; - e += d * l - s * k; - b += s * l + d * k - } - var q = Math.atan2((v - k) / h, (o - l) / j), - n = Math.atan2((-v - k) / h, (-o - l) / j) - q; - if (w) { - if (n <= 0) { - n += Math.PI * 2 - } - } else { - if (n >= 0) { - n -= Math.PI * 2 - } - } - x.ellipse(e, b, j, h, r, q, q + n, 1 - w) - }, - fromSvgString: function(e) { - if (!e) { - return - } - var m = this, - h, l = { - a: 7, - c: 6, - h: 1, - l: 2, - m: 2, - q: 4, - s: 4, - t: 2, - v: 1, - z: 0, - A: 7, - C: 6, - H: 1, - L: 2, - M: 2, - Q: 4, - S: 4, - T: 2, - V: 1, - Z: 0 - }, - k = "", - g, f, c = 0, - b = 0, - d = false, - j, n, a; - if (Ext.isString(e)) { - h = e.replace(Ext.draw.Path.pathRe, " $1 ").replace(Ext.draw.Path.pathRe2, " -").split(Ext.draw.Path.pathSplitRe) - } else { - if (Ext.isArray(e)) { - h = e.join(",").split(Ext.draw.Path.pathSplitRe) - } - } - for (j = 0, n = 0; j < h.length; j++) { - if (h[j] !== "") { - h[n++] = h[j] - } - } - h.length = n; - m.clear(); - for (j = 0; j < h.length;) { - k = d; - d = h[j]; - a = (d.toUpperCase() !== d); - j++; - switch (d) { - case "M": - m.moveTo(c = +h[j], b = +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2 - } - break; - case "L": - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2 - } - break; - case "A": - while (j < n && !l.hasOwnProperty(h[j])) { - m.arcSvg(+h[j], +h[j + 1], +h[j + 2] * Math.PI / 180, +h[j + 3], +h[j + 4], c = +h[j + 5], b = +h[j + 6]); - j += 7 - } - break; - case "C": - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(+h[j], +h[j + 1], g = +h[j + 2], f = +h[j + 3], c = +h[j + 4], b = +h[j + 5]); - j += 6 - } - break; - case "Z": - m.closePath(); - break; - case "m": - m.moveTo(c += +h[j], b += +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2 - } - break; - case "l": - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2 - } - break; - case "a": - while (j < n && !l.hasOwnProperty(h[j])) { - m.arcSvg(+h[j], +h[j + 1], +h[j + 2] * Math.PI / 180, +h[j + 3], +h[j + 4], c += +h[j + 5], b += +h[j + 6]); - j += 7 - } - break; - case "c": - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + (+h[j]), b + (+h[j + 1]), g = c + (+h[j + 2]), f = b + (+h[j + 3]), c += +h[j + 4], b += +h[j + 5]); - j += 6 - } - break; - case "z": - m.closePath(); - break; - case "s": - if (!(k === "c" || k === "C" || k === "s" || k === "S")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + c - g, b + b - f, g = c + (+h[j]), f = b + (+h[j + 1]), c += +h[j + 2], b += +h[j + 3]); - j += 4 - } - break; - case "S": - if (!(k === "c" || k === "C" || k === "s" || k === "S")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + c - g, b + b - f, g = +h[j], f = +h[j + 1], c = (+h[j + 2]), b = (+h[j + 3])); - j += 4 - } - break; - case "q": - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + (+h[j]), f = b + (+h[j + 1]), c += +h[j + 2], b += +h[j + 3]); - j += 4 - } - break; - case "Q": - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = +h[j], f = +h[j + 1], c = +h[j + 2], b = +h[j + 3]); - j += 4 - } - break; - case "t": - if (!(k === "q" || k === "Q" || k === "t" || k === "T")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + c - g, f = b + b - f, c += +h[j + 1], b += +h[j + 2]); - j += 2 - } - break; - case "T": - if (!(k === "q" || k === "Q" || k === "t" || k === "T")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + c - g, f = b + b - f, c = (+h[j + 1]), b = (+h[j + 2])); - j += 2 - } - break; - case "h": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b); - j++ - } - break; - case "H": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b); - j++ - } - break; - case "v": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c, b += +h[j]); - j++ - } - break; - case "V": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c, b = +h[j]); - j++ - } - break - } - } - }, - clone: function() { - var a = this, - b = new Ext.draw.Path(); - b.params = a.params.slice(0); - b.commands = a.commands.slice(0); - b.cursor = a.cursor ? a.cursor.slice(0) : null; - b.startX = a.startX; - b.startY = a.startY; - b.svgString = a.svgString; - return b - }, - transform: function(j) { - if (j.isIdentity()) { - return - } - var a = j.getXX(), - f = j.getYX(), - m = j.getDX(), - l = j.getXY(), - e = j.getYY(), - k = j.getDY(), - b = this.params, - c = 0, - d = b.length, - h, g; - for (; c < d; c += 2) { - h = b[c]; - g = b[c + 1]; - b[c] = h * a + g * f + m; - b[c + 1] = h * l + g * e + k - } - this.dirt() - }, - getDimension: function(f) { - if (!f) { - f = {} - } - if (!this.commands || !this.commands.length) { - f.x = 0; - f.y = 0; - f.width = 0; - f.height = 0; - return f - } - f.left = Infinity; - f.top = Infinity; - f.right = -Infinity; - f.bottom = -Infinity; - var d = 0, - c = 0, - b = this.commands, - g = this.params, - e = b.length, - a, h; - for (; d < e; d++) { - switch (b[d]) { - case "M": - case "L": - a = g[c]; - h = g[c + 1]; - f.left = Math.min(a, f.left); - f.top = Math.min(h, f.top); - f.right = Math.max(a, f.right); - f.bottom = Math.max(h, f.bottom); - c += 2; - break; - case "C": - this.expandDimension(f, a, h, g[c], g[c + 1], g[c + 2], g[c + 3], a = g[c + 4], h = g[c + 5]); - c += 6; - break - } - } - f.x = f.left; - f.y = f.top; - f.width = f.right - f.left; - f.height = f.bottom - f.top; - return f - }, - getDimensionWithTransform: function(n, f) { - if (!this.commands || !this.commands.length) { - if (!f) { - f = {} - } - f.x = 0; - f.y = 0; - f.width = 0; - f.height = 0; - return f - } - f.left = Infinity; - f.top = Infinity; - f.right = -Infinity; - f.bottom = -Infinity; - var a = n.getXX(), - k = n.getYX(), - q = n.getDX(), - p = n.getXY(), - h = n.getYY(), - o = n.getDY(), - e = 0, - d = 0, - b = this.commands, - c = this.params, - g = b.length, - m, l; - for (; e < g; e++) { - switch (b[e]) { - case "M": - case "L": - m = c[d] * a + c[d + 1] * k + q; - l = c[d] * p + c[d + 1] * h + o; - f.left = Math.min(m, f.left); - f.top = Math.min(l, f.top); - f.right = Math.max(m, f.right); - f.bottom = Math.max(l, f.bottom); - d += 2; - break; - case "C": - this.expandDimension(f, m, l, c[d] * a + c[d + 1] * k + q, c[d] * p + c[d + 1] * h + o, c[d + 2] * a + c[d + 3] * k + q, c[d + 2] * p + c[d + 3] * h + o, m = c[d + 4] * a + c[d + 5] * k + q, l = c[d + 4] * p + c[d + 5] * h + o); - d += 6; - break - } - } - if (!f) { - f = {} - } - f.x = f.left; - f.y = f.top; - f.width = f.right - f.left; - f.height = f.bottom - f.top; - return f - }, - expandDimension: function(i, d, p, k, g, j, e, c, o) { - var m = this, - f = i.left, - a = i.right, - q = i.top, - n = i.bottom, - h = m.dim || (m.dim = []); - m.curveDimension(d, k, j, c, h); - f = Math.min(f, h[0]); - a = Math.max(a, h[1]); - m.curveDimension(p, g, e, o, h); - q = Math.min(q, h[0]); - n = Math.max(n, h[1]); - i.left = f; - i.right = a; - i.top = q; - i.bottom = n - }, - curveDimension: function(p, n, k, j, h) { - var i = 3 * (-p + 3 * (n - k) + j), - g = 6 * (p - 2 * n + k), - f = -3 * (p - n), - o, m, e = Math.min(p, j), - l = Math.max(p, j), - q; - if (i === 0) { - if (g === 0) { - h[0] = e; - h[1] = l; - return - } else { - o = -f / g; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - } - } else { - q = g * g - 4 * i * f; - if (q >= 0) { - q = Math.sqrt(q); - o = (q - g) / 2 / i; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - if (q > 0) { - o -= q / i; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - } - } - } - h[0] = e; - h[1] = l - }, - interpolate: function(f, e, j, i, g) { - if (g === 0) { - return f - } - if (g === 1) { - return i - } - var h = (1 - g) / g; - return g * g * g * (i + h * (3 * j + h * (3 * e + h * f))) - }, - fromStripes: function(g) { - var e = this, - c = 0, - d = g.length, - b, a, f; - e.clear(); - for (; c < d; c++) { - f = g[c]; - e.params.push.apply(e.params, f); - e.commands.push("M"); - for (b = 2, a = f.length; b < a; b += 6) { - e.commands.push("C") - } - } - if (!e.cursor) { - e.cursor = [] - } - e.cursor[0] = e.params[e.params.length - 2]; - e.cursor[1] = e.params[e.params.length - 1]; - e.dirt() - }, - toStripes: function(k) { - var o = k || [], - p, n, m, b, a, h, g, f, e, c = this.commands, - d = this.params, - l = c.length; - for (f = 0, e = 0; f < l; f++) { - switch (c[f]) { - case "M": - p = [h = b = d[e++], g = a = d[e++]]; - o.push(p); - break; - case "L": - n = d[e++]; - m = d[e++]; - p.push((b + b + n) / 3, (a + a + m) / 3, (b + n + n) / 3, (a + m + m) / 3, b = n, a = m); - break; - case "C": - p.push(d[e++], d[e++], d[e++], d[e++], b = d[e++], a = d[e++]); - break; - case "Z": - n = h; - m = g; - p.push((b + b + n) / 3, (a + a + m) / 3, (b + n + n) / 3, (a + m + m) / 3, b = n, a = m); - break - } - } - return o - }, - updateSvgString: function() { - var b = [], - a = this.commands, - f = this.params, - e = a.length, - d = 0, - c = 0; - for (; d < e; d++) { - switch (a[d]) { - case "M": - b.push("M" + f[c] + "," + f[c + 1]); - c += 2; - break; - case "L": - b.push("L" + f[c] + "," + f[c + 1]); - c += 2; - break; - case "C": - b.push("C" + f[c] + "," + f[c + 1] + " " + f[c + 2] + "," + f[c + 3] + " " + f[c + 4] + "," + f[c + 5]); - c += 6; - break; - case "Z": - b.push("Z"); - break - } - } - this.svgString = b.join("") - }, - toString: function() { - if (!this.svgString) { - this.updateSvgString() - } - return this.svgString - } -}); -Ext.define("Ext.draw.overrides.Path", { - override: "Ext.draw.Path", - rayOrigin: { - x: -10000, - y: -10000 - }, - isPointInPath: function(o, n) { - var m = this, - c = m.commands, - q = Ext.draw.PathUtil, - p = m.rayOrigin, - f = m.params, - l = c.length, - e = null, - d = null, - b = 0, - a = 0, - k = 0, - h, g; - for (h = 0, g = 0; h < l; h++) { - switch (c[h]) { - case "M": - if (e !== null) { - if (q.linesIntersection(e, d, b, a, p.x, p.y, o, n)) { - k += 1 - } - } - e = b = f[g]; - d = a = f[g + 1]; - g += 2; - break; - case "L": - if (q.linesIntersection(b, a, f[g], f[g + 1], p.x, p.y, o, n)) { - k += 1 - } - b = f[g]; - a = f[g + 1]; - g += 2; - break; - case "C": - k += q.cubicLineIntersections(b, f[g], f[g + 2], f[g + 4], a, f[g + 1], f[g + 3], f[g + 5], p.x, p.y, o, n).length; - b = f[g + 4]; - a = f[g + 5]; - g += 6; - break; - case "Z": - if (e !== null) { - if (q.linesIntersection(e, d, b, a, p.x, p.y, o, n)) { - k += 1 - } - } - break - } - } - return k % 2 === 1 - }, - isPointOnPath: function(n, m) { - var l = this, - c = l.commands, - o = Ext.draw.PathUtil, - f = l.params, - k = c.length, - e = null, - d = null, - b = 0, - a = 0, - h, g; - for (h = 0, g = 0; h < k; h++) { - switch (c[h]) { - case "M": - if (e !== null) { - if (o.pointOnLine(e, d, b, a, n, m)) { - return true - } - } - e = b = f[g]; - d = a = f[g + 1]; - g += 2; - break; - case "L": - if (o.pointOnLine(b, a, f[g], f[g + 1], n, m)) { - return true - } - b = f[g]; - a = f[g + 1]; - g += 2; - break; - case "C": - if (o.pointOnCubic(b, f[g], f[g + 2], f[g + 4], a, f[g + 1], f[g + 3], f[g + 5], n, m)) { - return true - } - b = f[g + 4]; - a = f[g + 5]; - g += 6; - break; - case "Z": - if (e !== null) { - if (o.pointOnLine(e, d, b, a, n, m)) { - return true - } - } - break - } - } - return false - }, - getSegmentIntersections: function(t, d, s, c, r, b, o, a) { - var w = this, - g = arguments.length, - v = Ext.draw.PathUtil, - f = w.commands, - u = w.params, - k = f.length, - m = null, - l = null, - h = 0, - e = 0, - x = [], - q, n, p; - for (q = 0, n = 0; q < k; q++) { - switch (f[q]) { - case "M": - if (m !== null) { - switch (g) { - case 4: - p = v.linesIntersection(m, l, h, e, t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, m, l, h, e); - x.push.apply(x, p); - break - } - } - m = h = u[n]; - l = e = u[n + 1]; - n += 2; - break; - case "L": - switch (g) { - case 4: - p = v.linesIntersection(h, e, u[n], u[n + 1], t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, h, e, u[n], u[n + 1]); - x.push.apply(x, p); - break - } - h = u[n]; - e = u[n + 1]; - n += 2; - break; - case "C": - switch (g) { - case 4: - p = v.cubicLineIntersections(h, u[n], u[n + 2], u[n + 4], e, u[n + 1], u[n + 3], u[n + 5], t, d, s, c); - x.push.apply(x, p); - break; - case 8: - p = v.cubicsIntersections(h, u[n], u[n + 2], u[n + 4], e, u[n + 1], u[n + 3], u[n + 5], t, s, r, o, d, c, b, a); - x.push.apply(x, p); - break - } - h = u[n + 4]; - e = u[n + 5]; - n += 6; - break; - case "Z": - if (m !== null) { - switch (g) { - case 4: - p = v.linesIntersection(m, l, h, e, t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, m, l, h, e); - x.push.apply(x, p); - break - } - } - break - } - } - return x - }, - getIntersections: function(o) { - var m = this, - c = m.commands, - g = m.params, - l = c.length, - f = null, - e = null, - b = 0, - a = 0, - d = [], - k, h, n; - for (k = 0, h = 0; k < l; k++) { - switch (c[k]) { - case "M": - if (f !== null) { - n = o.getSegmentIntersections.call(o, f, e, b, a); - d.push.apply(d, n) - } - f = b = g[h]; - e = a = g[h + 1]; - h += 2; - break; - case "L": - n = o.getSegmentIntersections.call(o, b, a, g[h], g[h + 1]); - d.push.apply(d, n); - b = g[h]; - a = g[h + 1]; - h += 2; - break; - case "C": - n = o.getSegmentIntersections.call(o, b, a, g[h], g[h + 1], g[h + 2], g[h + 3], g[h + 4], g[h + 5]); - d.push.apply(d, n); - b = g[h + 4]; - a = g[h + 5]; - h += 6; - break; - case "Z": - if (f !== null) { - n = o.getSegmentIntersections.call(o, f, e, b, a); - d.push.apply(d, n) - } - break - } - } - return d - } -}); -Ext.define("Ext.draw.sprite.Path", { - extend: "Ext.draw.sprite.Sprite", - requires: ["Ext.draw.Draw", "Ext.draw.Path"], - alias: ["sprite.path", "Ext.draw.Sprite"], - type: "path", - isPath: true, - inheritableStatics: { - def: { - processors: { - path: function(b, a) { - if (!(b instanceof Ext.draw.Path)) { - b = new Ext.draw.Path(b) - } - return b - } - }, - aliases: { - d: "path" - }, - triggers: { - path: "bbox" - }, - updaters: { - path: function(a) { - var b = a.path; - if (!b || b.bindAttr !== a) { - b = new Ext.draw.Path(); - b.bindAttr = a; - a.path = b - } - b.clear(); - this.updatePath(b, a); - this.scheduleUpdater(a, "bbox", ["path"]) - } - } - } - }, - updatePlainBBox: function(a) { - if (this.attr.path) { - this.attr.path.getDimension(a) - } - }, - updateTransformedBBox: function(a) { - if (this.attr.path) { - this.attr.path.getDimensionWithTransform(this.attr.matrix, a) - } - }, - render: function(b, c) { - var d = this.attr.matrix, - a = this.attr; - if (!a.path || a.path.params.length === 0) { - return - } - d.toContext(c); - c.appendPath(a.path); - c.fillStroke(a) - }, - updatePath: function(b, a) {} -}); -Ext.define("Ext.draw.overrides.sprite.Path", { - override: "Ext.draw.sprite.Path", - requires: ["Ext.draw.Color"], - isPointInPath: function(c, g) { - var b = this.attr; - if (b.fillStyle === Ext.draw.Color.RGBA_NONE) { - return this.isPointOnPath(c, g) - } - var e = b.path, - d = b.matrix, - f, a; - if (!d.isIdentity()) { - f = e.params.slice(0); - e.transform(b.matrix) - } - a = e.isPointInPath(c, g); - if (f) { - e.params = f - } - return a - }, - isPointOnPath: function(c, g) { - var b = this.attr, - e = b.path, - d = b.matrix, - f, a; - if (!d.isIdentity()) { - f = e.params.slice(0); - e.transform(b.matrix) - } - a = e.isPointOnPath(c, g); - if (f) { - e.params = f - } - return a - }, - hitTest: function(i, l) { - var e = this, - c = e.attr, - k = c.path, - g = c.matrix, - h = i[0], - f = i[1], - d = e.callParent([i, l]), - j = null, - a, b; - if (!d) { - return j - } - l = l || Ext.draw.sprite.Sprite.defaultHitTestOptions; - if (!g.isIdentity()) { - a = k.params.slice(0); - k.transform(c.matrix) - } - if (l.fill && l.stroke) { - b = c.fillStyle !== Ext.draw.Color.NONE && c.fillStyle !== Ext.draw.Color.RGBA_NONE; - if (b) { - if (k.isPointInPath(h, f)) { - j = { - sprite: e - } - } - } else { - if (k.isPointInPath(h, f) || k.isPointOnPath(h, f)) { - j = { - sprite: e - } - } - } - } else { - if (l.stroke && !l.fill) { - if (k.isPointOnPath(h, f)) { - j = { - sprite: e - } - } - } else { - if (l.fill && !l.stroke) { - if (k.isPointInPath(h, f)) { - j = { - sprite: e - } - } - } - } - } - if (a) { - k.params = a - } - return j - }, - getIntersections: function(j) { - if (!(j.isSprite && j.isPath)) { - return [] - } - var e = this.attr, - d = j.attr, - i = e.path, - h = d.path, - g = e.matrix, - a = d.matrix, - c, f, b; - if (!g.isIdentity()) { - c = i.params.slice(0); - i.transform(e.matrix) - } - if (!a.isIdentity()) { - f = h.params.slice(0); - h.transform(d.matrix) - } - b = i.getIntersections(h); - if (c) { - i.params = c - } - if (f) { - h.params = f - } - return b - } -}); -Ext.define("Ext.draw.sprite.Circle", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.circle", - type: "circle", - inheritableStatics: { - def: { - processors: { - cx: "number", - cy: "number", - r: "number" - }, - aliases: { - radius: "r", - x: "cx", - y: "cy", - centerX: "cx", - centerY: "cy" - }, - defaults: { - cx: 0, - cy: 0, - r: 4 - }, - triggers: { - cx: "path", - cy: "path", - r: "path" - } - } - }, - updatePlainBBox: function(c) { - var b = this.attr, - a = b.cx, - e = b.cy, - d = b.r; - c.x = a - d; - c.y = e - d; - c.width = d + d; - c.height = d + d - }, - updateTransformedBBox: function(d) { - var g = this.attr, - f = g.cx, - e = g.cy, - a = g.r, - h = g.matrix, - j = h.getScaleX(), - i = h.getScaleY(), - c, b; - c = j * a; - b = i * a; - d.x = h.x(f, e) - c; - d.y = h.y(f, e) - b; - d.width = c + c; - d.height = b + b - }, - updatePath: function(b, a) { - b.arc(a.cx, a.cy, a.r, 0, Math.PI * 2, false) - } -}); -Ext.define("Ext.draw.sprite.Arc", { - extend: "Ext.draw.sprite.Circle", - alias: "sprite.arc", - type: "arc", - inheritableStatics: { - def: { - processors: { - startAngle: "number", - endAngle: "number", - anticlockwise: "bool" - }, - aliases: { - from: "startAngle", - to: "endAngle", - start: "startAngle", - end: "endAngle" - }, - defaults: { - startAngle: 0, - endAngle: Math.PI * 2, - anticlockwise: false - }, - triggers: { - startAngle: "path", - endAngle: "path", - anticlockwise: "path" - } - } - }, - updatePath: function(b, a) { - b.arc(a.cx, a.cy, a.r, a.startAngle, a.endAngle, a.anticlockwise) - } -}); -Ext.define("Ext.draw.sprite.Arrow", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.arrow", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 1.5, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c * 0.7, ",", e - c * 0.4, "l", [c * 0.6, 0, 0, -c * 0.4, c, c * 0.8, -c, c * 0.8, 0, -c * 0.4, -c * 0.6, 0], "z")) - } -}); -Ext.define("Ext.draw.sprite.Composite", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.composite", - type: "composite", - isComposite: true, - config: { - sprites: [] - }, - constructor: function() { - this.sprites = []; - this.sprites.map = {}; - this.callParent(arguments) - }, - add: function(c) { - if (!c) { - return null - } - if (!c.isSprite) { - c = Ext.create("sprite." + c.type, c); - c.setParent(this); - c.setSurface(this.getSurface()) - } - var d = this, - a = d.attr, - b = c.applyTransformations; - c.applyTransformations = function() { - if (c.attr.dirtyTransform) { - a.dirtyTransform = true; - a.bbox.plain.dirty = true; - a.bbox.transform.dirty = true - } - b.call(c) - }; - d.sprites.push(c); - d.sprites.map[c.id] = c.getId(); - a.bbox.plain.dirty = true; - a.bbox.transform.dirty = true; - return c - }, - updateSurface: function(a) { - for (var b = 0, c = this.sprites.length; b < c; b++) { - this.sprites[b].setSurface(a) - } - }, - addAll: function(b) { - if (b.isSprite || b.type) { - this.add(b) - } else { - if (Ext.isArray(b)) { - var a = 0; - while (a < b.length) { - this.add(b[a++]) - } - } - } - }, - updatePlainBBox: function(g) { - var e = this, - b = Infinity, - h = -Infinity, - f = Infinity, - a = -Infinity, - j, k, c, d; - for (c = 0, d = e.sprites.length; c < d; c++) { - j = e.sprites[c]; - j.applyTransformations(); - k = j.getBBox(); - if (b > k.x) { - b = k.x - } - if (h < k.x + k.width) { - h = k.x + k.width - } - if (f > k.y) { - f = k.y - } - if (a < k.y + k.height) { - a = k.y + k.height - } - } - g.x = b; - g.y = f; - g.width = h - b; - g.height = a - f - }, - render: function(a, b, f) { - var d = this.attr.matrix, - c, e; - d.toContext(b); - for (c = 0, e = this.sprites.length; c < e; c++) { - a.renderSprite(this.sprites[c], f) - } - }, - destroy: function() { - var c = this, - d = c.sprites, - b = d.length, - a; - c.callParent(); - for (a = 0; a < b; a++) { - d[a].destroy() - } - d.length = 0 - } -}); -Ext.define("Ext.draw.sprite.Cross", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.cross", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size / 1.7, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c, ",", e, "l", [-c, -c, c, -c, c, c, c, -c, c, c, -c, c, c, c, -c, c, -c, -c, -c, c, -c, -c, "z"])) - } -}); -Ext.define("Ext.draw.sprite.Diamond", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.diamond", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 1.25, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString(["M", a, e - c, "l", c, c, -c, c, -c, -c, c, -c, "z"]) - } -}); -Ext.define("Ext.draw.sprite.Ellipse", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.ellipse", - type: "ellipse", - inheritableStatics: { - def: { - processors: { - cx: "number", - cy: "number", - rx: "number", - ry: "number", - axisRotation: "number" - }, - aliases: { - radius: "r", - x: "cx", - y: "cy", - centerX: "cx", - centerY: "cy", - radiusX: "rx", - radiusY: "ry" - }, - defaults: { - cx: 0, - cy: 0, - rx: 1, - ry: 1, - axisRotation: 0 - }, - triggers: { - cx: "path", - cy: "path", - rx: "path", - ry: "path", - axisRotation: "path" - } - } - }, - updatePlainBBox: function(c) { - var b = this.attr, - a = b.cx, - f = b.cy, - e = b.rx, - d = b.ry; - c.x = a - e; - c.y = f - d; - c.width = e + e; - c.height = d + d - }, - updateTransformedBBox: function(d) { - var i = this.attr, - f = i.cx, - e = i.cy, - c = i.rx, - b = i.ry, - l = b / c, - m = i.matrix.clone(), - a, q, k, j, p, o, n, g; - m.append(1, 0, 0, l, 0, e * (1 - l)); - a = m.getXX(); - k = m.getYX(); - p = m.getDX(); - q = m.getXY(); - j = m.getYY(); - o = m.getDY(); - n = Math.sqrt(a * a + k * k) * c; - g = Math.sqrt(q * q + j * j) * c; - d.x = f * a + e * k + p - n; - d.y = f * q + e * j + o - g; - d.width = n + n; - d.height = g + g - }, - updatePath: function(b, a) { - b.ellipse(a.cx, a.cy, a.rx, a.ry, a.axisRotation, 0, Math.PI * 2, false) - } -}); -Ext.define("Ext.draw.sprite.EllipticalArc", { - extend: "Ext.draw.sprite.Ellipse", - alias: "sprite.ellipticalArc", - type: "ellipticalArc", - inheritableStatics: { - def: { - processors: { - startAngle: "number", - endAngle: "number", - anticlockwise: "bool" - }, - aliases: { - from: "startAngle", - to: "endAngle", - start: "startAngle", - end: "endAngle" - }, - defaults: { - startAngle: 0, - endAngle: Math.PI * 2, - anticlockwise: false - }, - triggers: { - startAngle: "path", - endAngle: "path", - anticlockwise: "path" - } - } - }, - updatePath: function(b, a) { - b.ellipse(a.cx, a.cy, a.rx, a.ry, a.axisRotation, a.startAngle, a.endAngle, a.anticlockwise) - } -}); -Ext.define("Ext.draw.sprite.Rect", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.rect", - type: "rect", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number", - radius: "number" - }, - aliases: {}, - triggers: { - x: "path", - y: "path", - width: "path", - height: "path", - radius: "path" - }, - defaults: { - x: 0, - y: 0, - width: 8, - height: 8, - radius: 0 - } - } - }, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.x; - b.y = a.y; - b.width = a.width; - b.height = a.height - }, - updateTransformedBBox: function(a, b) { - this.attr.matrix.transformBBox(b, this.attr.radius, a) - }, - updatePath: function(f, d) { - var c = d.x, - g = d.y, - e = d.width, - b = d.height, - a = Math.min(d.radius, Math.abs(d.height) * 0.5, Math.abs(d.width) * 0.5); - if (a === 0) { - f.rect(c, g, e, b) - } else { - f.moveTo(c + a, g); - f.arcTo(c + e, g, c + e, g + b, a); - f.arcTo(c + e, g + b, c, g + b, a); - f.arcTo(c, g + b, c, g, a); - f.arcTo(c, g, c + a, g, a) - } - } -}); -Ext.define("Ext.draw.sprite.Image", { - extend: "Ext.draw.sprite.Rect", - alias: "sprite.image", - type: "image", - statics: { - imageLoaders: {} - }, - inheritableStatics: { - def: { - processors: { - src: "string" - }, - defaults: { - src: "", - width: null, - height: null - } - } - }, - render: function(c, o) { - var j = this, - h = j.attr, - n = h.matrix, - a = h.src, - l = h.x, - k = h.y, - b = h.width, - m = h.height, - g = Ext.draw.sprite.Image.imageLoaders[a], - f, d, e; - if (g && g.done) { - n.toContext(o); - d = g.image; - o.drawImage(d, l, k, b || (d.naturalWidth || d.width) / c.devicePixelRatio, m || (d.naturalHeight || d.height) / c.devicePixelRatio) - } else { - if (!g) { - f = new Image(); - g = Ext.draw.sprite.Image.imageLoaders[a] = { - image: f, - done: false, - pendingSprites: [j], - pendingSurfaces: [c] - }; - f.width = b; - f.height = m; - f.onload = function() { - if (!g.done) { - g.done = true; - for (e = 0; e < g.pendingSprites.length; e++) { - g.pendingSprites[e].setDirty(true) - } - for (e in g.pendingSurfaces) { - g.pendingSurfaces[e].renderFrame() - } - } - }; - f.src = a - } else { - Ext.Array.include(g.pendingSprites, j); - Ext.Array.include(g.pendingSurfaces, c) - } - } - } -}); -Ext.define("Ext.draw.sprite.Instancing", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.instancing", - type: "instancing", - isInstancing: true, - config: { - template: null - }, - instances: null, - applyTemplate: function(a) { - if (!a.isSprite) { - if (!a.xclass && !a.type) { - a.type = "circle" - } - a = Ext.create(a.xclass || "sprite." + a.type, a) - } - a.setParent(this); - return a - }, - updateTemplate: function(a, b) { - if (b) { - delete b.ownAttr - } - a.setSurface(this.getSurface()); - a.ownAttr = a.attr; - this.clearAll() - }, - updateSurface: function(a) { - var b = this.getTemplate(); - if (b) { - b.setSurface(a) - } - }, - get: function(a) { - return this.instances[a] - }, - getCount: function() { - return this.instances.length - }, - clearAll: function() { - var a = this.getTemplate(); - a.attr.children = this.instances = []; - this.position = 0 - }, - createInstance: function(d, f, c) { - var e = this.getTemplate(), - b = e.attr, - a = Ext.Object.chain(b); - e.topModifier.prepareAttributes(a); - e.attr = a; - e.setAttributes(d, f, c); - a.template = e; - this.instances.push(a); - e.attr = b; - this.position++; - return a - }, - getBBox: function() { - return null - }, - getBBoxFor: function(b, d) { - var c = this.getTemplate(), - a = c.attr, - e; - c.attr = this.instances[b]; - e = c.getBBox(d); - c.attr = a; - return e - }, - isVisible: function() { - var b = this.attr, - c = this.getParent(), - a; - a = c && c.isSurface && !b.hidden && b.globalAlpha; - return !!a - }, - isInstanceVisible: function(c) { - var e = this, - d = e.getTemplate(), - b = d.attr, - f = e.instances, - a = false; - if (!Ext.isNumber(c) || c < 0 || c >= f.length || !e.isVisible()) { - return a - } - d.attr = f[c]; - a = d.isVisible(point, options); - d.attr = b; - return a - }, - render: function(b, l, d, h) { - var g = this, - j = g.getTemplate(), - k = g.attr.matrix, - c = j.attr, - a = g.instances, - e, f = g.position; - k.toContext(l); - j.preRender(b, l, d, h); - j.useAttributes(l, h); - for (e = 0; e < f; e++) { - if (a[e].dirtyZIndex) { - break - } - } - for (e = 0; e < f; e++) { - if (a[e].hidden) { - continue - } - l.save(); - j.attr = a[e]; - j.useAttributes(l, h); - j.render(b, l, d, h); - l.restore() - } - j.attr = c - }, - setAttributesFor: function(c, e, f) { - var d = this.getTemplate(), - b = d.attr, - a = this.instances[c]; - if (!a) { - return - } - d.attr = a; - if (f) { - e = Ext.apply({}, e) - } else { - e = d.self.def.normalize(e) - } - d.topModifier.pushDown(a, e); - d.attr = b - }, - destroy: function() { - var b = this, - a = b.getTemplate(); - b.instances = null; - if (a) { - a.destroy() - } - b.callParent() - } -}); -Ext.define("Ext.draw.overrides.sprite.Instancing", { - override: "Ext.draw.sprite.Instancing", - hitTest: function(f, j) { - var e = this, - g = e.getTemplate(), - b = g.attr, - a = e.instances, - d = a.length, - c = 0, - h = null; - if (!e.isVisible()) { - return h - } - for (; c < d; c++) { - g.attr = a[c]; - h = g.hitTest(f, j); - if (h) { - h.isInstance = true; - h.template = h.sprite; - h.sprite = this; - h.instance = a[c]; - h.index = c; - return h - } - } - g.attr = b; - return h - } -}); -Ext.define("Ext.draw.sprite.Line", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.line", - type: "line", - inheritableStatics: { - def: { - processors: { - fromX: "number", - fromY: "number", - toX: "number", - toY: "number" - }, - defaults: { - fromX: 0, - fromY: 0, - toX: 1, - toY: 1, - strokeStyle: "black" - }, - aliases: { - x1: "fromX", - y1: "fromY", - x2: "toX", - y2: "toY" - } - } - }, - updateLineBBox: function(b, i, s, g, r, f) { - var o = this.attr, - q = o.matrix, - h = o.lineWidth / 2, - m, l, d, c, k, j, n; - if (i) { - n = q.transformPoint([s, g]); - s = n[0]; - g = n[1]; - n = q.transformPoint([r, f]); - r = n[0]; - f = n[1] - } - m = Math.min(s, r); - d = Math.max(s, r); - l = Math.min(g, f); - c = Math.max(g, f); - var t = Math.atan2(d - m, c - l), - a = Math.sin(t), - e = Math.cos(t), - k = h * e, - j = h * a; - m -= k; - l -= j; - d += k; - c += j; - b.x = m; - b.y = l; - b.width = d - m; - b.height = c - l - }, - updatePlainBBox: function(b) { - var a = this.attr; - this.updateLineBBox(b, false, a.fromX, a.fromY, a.toX, a.toY) - }, - updateTransformedBBox: function(b, c) { - var a = this.attr; - this.updateLineBBox(b, true, a.fromX, a.fromY, a.toX, a.toY) - }, - render: function(b, c) { - var a = this.attr, - d = this.attr.matrix; - d.toContext(c); - c.beginPath(); - c.moveTo(a.fromX, a.fromY); - c.lineTo(a.toX, a.toY); - c.stroke() - } -}); -Ext.define("Ext.draw.sprite.Plus", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.plus", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size / 1.3, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c / 2, ",", e - c / 2, "l", [0, -c, c, 0, 0, c, c, 0, 0, c, -c, 0, 0, c, -c, 0, 0, -c, -c, 0, 0, -c, "z"])) - } -}); -Ext.define("Ext.draw.sprite.Sector", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.sector", - type: "sector", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - margin: "number" - }, - aliases: { - rho: "endRho" - }, - triggers: { - centerX: "path,bbox", - centerY: "path,bbox", - startAngle: "path,bbox", - endAngle: "path,bbox", - startRho: "path,bbox", - endRho: "path,bbox", - margin: "path,bbox" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: 0, - endAngle: 0, - startRho: 0, - endRho: 150, - margin: 0, - path: "M 0,0" - } - } - }, - getMidAngle: function() { - return this.midAngle || 0 - }, - updatePath: function(j, h) { - var g = Math.min(h.startAngle, h.endAngle), - c = Math.max(h.startAngle, h.endAngle), - b = this.midAngle = (g + c) * 0.5, - d = h.margin, - f = h.centerX, - e = h.centerY, - i = Math.min(h.startRho, h.endRho), - a = Math.max(h.startRho, h.endRho); - if (d) { - f += d * Math.cos(b); - e += d * Math.sin(b) - } - j.moveTo(f + i * Math.cos(g), e + i * Math.sin(g)); - j.lineTo(f + a * Math.cos(g), e + a * Math.sin(g)); - j.arc(f, e, a, g, c, false); - j.lineTo(f + i * Math.cos(c), e + i * Math.sin(c)); - j.arc(f, e, i, c, g, true) - } -}); -Ext.define("Ext.draw.sprite.Square", { - extend: "Ext.draw.sprite.Rect", - alias: "sprite.square", - inheritableStatics: { - def: { - processors: { - size: "number" - }, - defaults: { - size: 4 - }, - triggers: { - size: "size" - }, - updaters: { - size: function(a) { - var c = a.size, - b = a.lineWidth / 2; - this.setAttributes({ - x: a.x - c - b, - y: a.y - c, - height: 2 * c, - width: 2 * c - }) - } - } - } - } -}); -Ext.define("Ext.draw.TextMeasurer", { - singleton: true, - requires: ["Ext.util.TextMetrics"], - measureDiv: null, - measureCache: {}, - precise: Ext.isIE8, - measureDivTpl: { - tag: "div", - style: { - overflow: "hidden", - position: "relative", - "float": "left", - width: 0, - height: 0 - }, - children: { - tag: "div", - style: { - display: "block", - position: "absolute", - x: -100000, - y: -100000, - padding: 0, - margin: 0, - "z-index": -100000, - "white-space": "nowrap" - } - } - }, - actualMeasureText: function(g, b) { - var e = Ext.draw.TextMeasurer, - f = e.measureDiv, - a = 100000, - c; - if (!f) { - var d = Ext.Element.create({ - style: { - overflow: "hidden", - position: "relative", - "float": "left", - width: 0, - height: 0 - } - }); - e.measureDiv = f = Ext.Element.create({ - style: { - position: "absolute", - x: a, - y: a, - "z-index": -a, - "white-space": "nowrap", - display: "block", - padding: 0, - margin: 0 - } - }); - Ext.getBody().appendChild(d); - d.appendChild(f) - } - if (b) { - f.setStyle({ - font: b, - lineHeight: "normal" - }) - } - f.setText("(" + g + ")"); - c = f.getSize(); - f.setText("()"); - c.width -= f.getSize().width; - return c - }, - measureTextSingleLine: function(h, d) { - if (this.precise) { - return this.preciseMeasureTextSingleLine(h, d) - } - h = h.toString(); - var a = this.measureCache, - g = h.split(""), - c = 0, - j = 0, - l, b, e, f, k; - if (!a[d]) { - a[d] = {} - } - a = a[d]; - if (a[h]) { - return a[h] - } - for (e = 0, f = g.length; e < f; e++) { - b = g[e]; - if (!(l = a[b])) { - k = this.actualMeasureText(b, d); - l = a[b] = k - } - c += l.width; - j = Math.max(j, l.height) - } - return a[h] = { - width: c, - height: j - } - }, - preciseMeasureTextSingleLine: function(c, a) { - c = c.toString(); - var b = this.measureDiv || (this.measureDiv = Ext.getBody().createChild(this.measureDivTpl).down("div")); - b.setStyle({ - font: a || "" - }); - return Ext.util.TextMetrics.measure(b, c) - }, - measureText: function(e, b) { - var h = e.split("\n"), - d = h.length, - f = 0, - a = 0, - j, c, g; - if (d === 1) { - return this.measureTextSingleLine(e, b) - } - g = []; - for (c = 0; c < d; c++) { - j = this.measureTextSingleLine(h[c], b); - g.push(j); - f += j.height; - a = Math.max(a, j.width) - } - return { - width: a, - height: f, - sizes: g - } - } -}); -Ext.define("Ext.draw.sprite.Text", function() { - var d = { - "xx-small": true, - "x-small": true, - small: true, - medium: true, - large: true, - "x-large": true, - "xx-large": true - }; - var b = { - normal: true, - bold: true, - bolder: true, - lighter: true, - 100: true, - 200: true, - 300: true, - 400: true, - 500: true, - 600: true, - 700: true, - 800: true, - 900: true - }; - var a = { - start: "start", - left: "start", - center: "center", - middle: "center", - end: "end", - right: "end" - }; - var c = { - top: "top", - hanging: "hanging", - middle: "middle", - center: "middle", - alphabetic: "alphabetic", - ideographic: "ideographic", - bottom: "bottom" - }; - return { - extend: "Ext.draw.sprite.Sprite", - requires: ["Ext.draw.TextMeasurer", "Ext.draw.Color"], - alias: "sprite.text", - type: "text", - lineBreakRe: /\r?\n/g, - inheritableStatics: { - def: { - animationProcessors: { - text: "text" - }, - processors: { - x: "number", - y: "number", - text: "string", - fontSize: function(e) { - if (Ext.isNumber(+e)) { - return e + "px" - } else { - if (e.match(Ext.dom.Element.unitRe)) { - return e - } else { - if (e in d) { - return e - } - } - } - }, - fontStyle: "enums(,italic,oblique)", - fontVariant: "enums(,small-caps)", - fontWeight: function(e) { - if (e in b) { - return String(e) - } else { - return "" - } - }, - fontFamily: "string", - textAlign: function(e) { - return a[e] || "center" - }, - textBaseline: function(e) { - return c[e] || "alphabetic" - }, - font: "string" - }, - aliases: { - "font-size": "fontSize", - "font-family": "fontFamily", - "font-weight": "fontWeight", - "font-variant": "fontVariant", - "text-anchor": "textAlign" - }, - defaults: { - fontStyle: "", - fontVariant: "", - fontWeight: "", - fontSize: "10px", - fontFamily: "sans-serif", - font: "10px sans-serif", - textBaseline: "alphabetic", - textAlign: "start", - strokeStyle: "rgba(0, 0, 0, 0)", - fillStyle: "#000", - x: 0, - y: 0, - text: "" - }, - triggers: { - fontStyle: "fontX,bbox", - fontVariant: "fontX,bbox", - fontWeight: "fontX,bbox", - fontSize: "fontX,bbox", - fontFamily: "fontX,bbox", - font: "font,bbox,canvas", - textBaseline: "bbox", - textAlign: "bbox", - x: "bbox", - y: "bbox", - text: "bbox" - }, - updaters: { - fontX: "makeFontShorthand", - font: "parseFontShorthand" - } - } - }, - constructor: function(e) { - if (e && e.font) { - e = Ext.clone(e); - for (var f in e) { - if (f !== "font" && f.indexOf("font") === 0) { - delete e[f] - } - } - } - Ext.draw.sprite.Sprite.prototype.constructor.call(this, e) - }, - fontValuesMap: { - italic: "fontStyle", - oblique: "fontStyle", - "small-caps": "fontVariant", - bold: "fontWeight", - bolder: "fontWeight", - lighter: "fontWeight", - "100": "fontWeight", - "200": "fontWeight", - "300": "fontWeight", - "400": "fontWeight", - "500": "fontWeight", - "600": "fontWeight", - "700": "fontWeight", - "800": "fontWeight", - "900": "fontWeight", - "xx-small": "fontSize", - "x-small": "fontSize", - small: "fontSize", - medium: "fontSize", - large: "fontSize", - "x-large": "fontSize", - "xx-large": "fontSize" - }, - makeFontShorthand: function(e) { - var f = []; - if (e.fontStyle) { - f.push(e.fontStyle) - } - if (e.fontVariant) { - f.push(e.fontVariant) - } - if (e.fontWeight) { - f.push(e.fontWeight) - } - if (e.fontSize) { - f.push(e.fontSize) - } - if (e.fontFamily) { - f.push(e.fontFamily) - } - this.setAttributes({ - font: f.join(" ") - }, true) - }, - parseFontShorthand: function(j) { - var m = j.font, - k = m.length, - l = {}, - n = this.fontValuesMap, - e = 0, - i, g, f, h; - while (e < k && i !== -1) { - i = m.indexOf(" ", e); - if (i < 0) { - f = m.substr(e) - } else { - if (i > e) { - f = m.substr(e, i - e) - } else { - continue - } - } - g = f.indexOf("/"); - if (g > 0) { - f = f.substr(0, g) - } else { - if (g === 0) { - continue - } - } - if (f !== "normal" && f !== "inherit") { - h = n[f]; - if (h) { - l[h] = f - } else { - if (f.match(Ext.dom.Element.unitRe)) { - l.fontSize = f - } else { - l.fontFamily = m.substr(e); - break - } - } - } - e = i + 1 - } - if (!l.fontStyle) { - l.fontStyle = "" - } - if (!l.fontVariant) { - l.fontVariant = "" - } - if (!l.fontWeight) { - l.fontWeight = "" - } - this.setAttributes(l, true) - }, - fontProperties: { - fontStyle: true, - fontVariant: true, - fontWeight: true, - fontSize: true, - fontFamily: true - }, - setAttributes: function(g, i, e) { - var f, h; - if (g && g.font) { - h = {}; - for (f in g) { - if (!(f in this.fontProperties)) { - h[f] = g[f] - } - } - g = h - } - this.callParent([g, i, e]) - }, - getBBox: function(g) { - var h = this, - f = h.attr.bbox.plain, - e = h.getSurface(); - if (f.dirty) { - h.updatePlainBBox(f); - f.dirty = false - } - if (e.getInherited().rtl && e.getFlipRtlText()) { - h.updatePlainBBox(f, true) - } - return h.callParent([g]) - }, - rtlAlignments: { - start: "end", - center: "center", - end: "start" - }, - updatePlainBBox: function(k, B) { - var C = this, - w = C.attr, - o = w.x, - n = w.y, - q = [], - t = w.font, - r = w.text, - s = w.textBaseline, - l = w.textAlign, - u = (B && C.oldSize) ? C.oldSize : (C.oldSize = Ext.draw.TextMeasurer.measureText(r, t)), - z = C.getSurface(), - p = z.getInherited().rtl, - v = p && z.getFlipRtlText(), - h = z.getRect(), - f = u.sizes, - g = u.height, - j = u.width, - m = f ? f.length : 0, - e, A = 0; - switch (s) { - case "hanging": - case "top": - break; - case "ideographic": - case "bottom": - n -= g; - break; - case "alphabetic": - n -= g * 0.8; - break; - case "middle": - n -= g * 0.5; - break - } - if (v) { - o = h[2] - h[0] - o; - l = C.rtlAlignments[l] - } - switch (l) { - case "start": - if (p) { - for (; A < m; A++) { - e = f[A].width; - q.push(-(j - e)) - } - } - break; - case "end": - o -= j; - if (p) { - break - } - for (; A < m; A++) { - e = f[A].width; - q.push(j - e) - } - break; - case "center": - o -= j * 0.5; - for (; A < m; A++) { - e = f[A].width; - q.push((p ? -1 : 1) * (j - e) * 0.5) - } - break - } - w.textAlignOffsets = q; - k.x = o; - k.y = n; - k.width = j; - k.height = g - }, - setText: function(e) { - this.setAttributes({ - text: e - }, true) - }, - render: function(e, q, k) { - var h = this, - g = h.attr, - p = Ext.draw.Matrix.fly(g.matrix.elements.slice(0)), - o = h.getBBox(true), - s = g.textAlignOffsets, - m = Ext.draw.Color.RGBA_NONE, - l, j, f, r, n; - if (g.text.length === 0) { - return - } - r = g.text.split(h.lineBreakRe); - n = o.height / r.length; - l = g.bbox.plain.x; - j = g.bbox.plain.y + n * 0.78; - p.toContext(q); - if (e.getInherited().rtl) { - l += g.bbox.plain.width - } - for (f = 0; f < r.length; f++) { - if (q.fillStyle !== m) { - q.fillText(r[f], l + (s[f] || 0), j + n * f) - } - if (q.strokeStyle !== m) { - q.strokeText(r[f], l + (s[f] || 0), j + n * f) - } - } - } - } -}); -Ext.define("Ext.draw.sprite.Tick", { - extend: "Ext.draw.sprite.Line", - alias: "sprite.tick", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "tick", - y: "tick", - size: "tick" - }, - updaters: { - tick: function(b) { - var d = b.size * 1.5, - c = b.lineWidth / 2, - a = b.x, - e = b.y; - this.setAttributes({ - fromX: a - c, - fromY: e - d, - toX: a - c, - toY: e + d - }) - } - } - } - } -}); -Ext.define("Ext.draw.sprite.Triangle", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.triangle", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 2.2, - a = b.x, - e = b.y; - d.fromSvgString("M".concat(a, ",", e, "m0-", c * 0.58, "l", c * 0.5, ",", c * 0.87, "-", c, ",0z")) - } -}); -Ext.define("Ext.draw.gradient.Linear", { - extend: "Ext.draw.gradient.Gradient", - requires: ["Ext.draw.Color"], - type: "linear", - config: { - degrees: 0, - radians: 0 - }, - applyRadians: function(b, a) { - if (Ext.isNumber(b)) { - return b - } - return a - }, - applyDegrees: function(b, a) { - if (Ext.isNumber(b)) { - return b - } - return a - }, - updateRadians: function(a) { - this.setDegrees(Ext.draw.Draw.degrees(a)) - }, - updateDegrees: function(a) { - this.setRadians(Ext.draw.Draw.rad(a)) - }, - generateGradient: function(q, o) { - var c = this.getRadians(), - p = Math.cos(c), - j = Math.sin(c), - m = o.width, - f = o.height, - d = o.x + m * 0.5, - b = o.y + f * 0.5, - n = this.getStops(), - g = n.length, - k, a, e; - if (Ext.isNumber(d + b) && f > 0 && m > 0) { - a = (Math.sqrt(f * f + m * m) * Math.abs(Math.cos(c - Math.atan(f / m)))) / 2; - k = q.createLinearGradient(d + p * a, b + j * a, d - p * a, b - j * a); - for (e = 0; e < g; e++) { - k.addColorStop(n[e].offset, n[e].color) - } - return k - } - return Ext.draw.Color.NONE - } -}); -Ext.define("Ext.draw.gradient.Radial", { - extend: "Ext.draw.gradient.Gradient", - type: "radial", - config: { - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - } - }, - applyStart: function(a, b) { - if (!b) { - return a - } - var c = { - x: b.x, - y: b.y, - r: b.r - }; - if ("x" in a) { - c.x = a.x - } else { - if ("centerX" in a) { - c.x = a.centerX - } - } - if ("y" in a) { - c.y = a.y - } else { - if ("centerY" in a) { - c.y = a.centerY - } - } - if ("r" in a) { - c.r = a.r - } else { - if ("radius" in a) { - c.r = a.radius - } - } - return c - }, - applyEnd: function(b, a) { - if (!a) { - return b - } - var c = { - x: a.x, - y: a.y, - r: a.r - }; - if ("x" in b) { - c.x = b.x - } else { - if ("centerX" in b) { - c.x = b.centerX - } - } - if ("y" in b) { - c.y = b.y - } else { - if ("centerY" in b) { - c.y = b.centerY - } - } - if ("r" in b) { - c.r = b.r - } else { - if ("radius" in b) { - c.r = b.radius - } - } - return c - }, - generateGradient: function(n, m) { - var a = this.getStart(), - b = this.getEnd(), - k = m.width * 0.5, - d = m.height * 0.5, - j = m.x + k, - f = m.y + d, - g = n.createRadialGradient(j + a.x * k, f + a.y * d, a.r * Math.max(k, d), j + b.x * k, f + b.y * d, b.r * Math.max(k, d)), - l = this.getStops(), - e = l.length, - c; - for (c = 0; c < e; c++) { - g.addColorStop(l[c].offset, l[c].color) - } - return g - } -}); -Ext.define("Ext.draw.Surface", { - extend: "Ext.draw.SurfaceBase", - xtype: "surface", - requires: ["Ext.draw.sprite.*", "Ext.draw.gradient.*", "Ext.draw.sprite.AttributeDefinition", "Ext.draw.Matrix", "Ext.draw.Draw"], - uses: ["Ext.draw.engine.Canvas"], - devicePixelRatio: window.devicePixelRatio || window.screen.deviceXDPI / window.screen.logicalXDPI, - deprecated: { - "5.1.0": { - statics: { - methods: { - stableSort: function(a) { - return Ext.Array.sort(a, function(d, c) { - return d.attr.zIndex - c.attr.zIndex - }) - } - } - } - } - }, - config: { - cls: Ext.baseCSSPrefix + "surface", - rect: null, - background: null, - items: [], - dirty: false, - flipRtlText: false - }, - isSurface: true, - isPendingRenderFrame: false, - dirtyPredecessorCount: 0, - constructor: function(a) { - var b = this; - b.predecessors = []; - b.successors = []; - b.map = {}; - b.callParent([a]); - b.matrix = new Ext.draw.Matrix(); - b.inverseMatrix = b.matrix.inverse() - }, - roundPixel: function(a) { - return Math.round(this.devicePixelRatio * a) / this.devicePixelRatio - }, - waitFor: function(a) { - var b = this, - c = b.predecessors; - if (!Ext.Array.contains(c, a)) { - c.push(a); - a.successors.push(b); - if (a.getDirty()) { - b.dirtyPredecessorCount++ - } - } - }, - updateDirty: function(d) { - var c = this.successors, - e = c.length, - b = 0, - a; - for (; b < e; b++) { - a = c[b]; - if (d) { - a.dirtyPredecessorCount++; - a.setDirty(true) - } else { - a.dirtyPredecessorCount--; - if (a.dirtyPredecessorCount === 0 && a.isPendingRenderFrame) { - a.renderFrame() - } - } - } - }, - applyBackground: function(a, b) { - this.setDirty(true); - if (Ext.isString(a)) { - a = { - fillStyle: a - } - } - return Ext.factory(a, Ext.draw.sprite.Rect, b) - }, - applyRect: function(a, b) { - if (b && a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]) { - return - } - if (Ext.isArray(a)) { - return [a[0], a[1], a[2], a[3]] - } else { - if (Ext.isObject(a)) { - return [a.x || a.left, a.y || a.top, a.width || (a.right - a.left), a.height || (a.bottom - a.top)] - } - } - }, - updateRect: function(i) { - var h = this, - c = i[0], - f = i[1], - g = c + i[2], - a = f + i[3], - e = h.getBackground(), - d = h.element; - d.setLocalXY(Math.floor(c), Math.floor(f)); - d.setSize(Math.ceil(g - Math.floor(c)), Math.ceil(a - Math.floor(f))); - if (e) { - e.setAttributes({ - x: 0, - y: 0, - width: Math.ceil(g - Math.floor(c)), - height: Math.ceil(a - Math.floor(f)) - }) - } - h.setDirty(true) - }, - resetTransform: function() { - this.matrix.set(1, 0, 0, 1, 0, 0); - this.inverseMatrix.set(1, 0, 0, 1, 0, 0); - this.setDirty(true) - }, - get: function(a) { - return this.map[a] || this.getItems()[a] - }, - add: function() { - var g = this, - e = Array.prototype.slice.call(arguments), - j = Ext.isArray(e[0]), - a = g.map, - c = [], - f, k, h, b, d; - f = Ext.Array.clean(j ? e[0] : e); - if (!f.length) { - return c - } - for (b = 0, d = f.length; b < d; b++) { - k = f[b]; - h = null; - if (k.isSprite && !a[k.getId()]) { - h = k - } else { - if (!a[k.id]) { - h = this.createItem(k) - } - } - if (h) { - a[h.getId()] = h; - c.push(h); - h.setParent(g); - h.setSurface(g); - g.onAdd(h) - } - } - f = g.getItems(); - if (f) { - f.push.apply(f, c) - } - g.dirtyZIndex = true; - g.setDirty(true); - if (!j && c.length === 1) { - return c[0] - } else { - return c - } - }, - onAdd: Ext.emptyFn, - remove: function(a, c) { - var b = this, - e, d; - if (a) { - if (a.charAt) { - a = b.map[a] - } - if (!a || !a.isSprite) { - return null - } - if (a.isDestroyed || a.isDestroying) { - return a - } - e = a.getId(); - d = b.map[e]; - delete b.map[e]; - if (c) { - a.destroy() - } - if (!d) { - return a - } - a.setParent(null); - a.setSurface(null); - Ext.Array.remove(b.getItems(), a); - b.dirtyZIndex = true; - b.setDirty(true) - } - return a || null - }, - removeAll: function(d) { - var a = this.getItems(), - b = a.length - 1, - c; - if (d) { - for (; b >= 0; b--) { - a[b].destroy() - } - } else { - for (; b >= 0; b--) { - c = a[b]; - c.setParent(null); - c.setSurface(null) - } - } - a.length = 0; - this.map = {}; - this.dirtyZIndex = true - }, - applyItems: function(a) { - if (this.getItems()) { - this.removeAll(true) - } - return Ext.Array.from(this.add(a)) - }, - createItem: function(a) { - return Ext.create(a.xclass || "sprite." + a.type, a) - }, - getBBox: function(f, b) { - var f = Ext.Array.from(f), - c = Infinity, - h = -Infinity, - g = Infinity, - a = -Infinity, - j, k, d, e; - for (d = 0, e = f.length; d < e; d++) { - j = f[d]; - k = j.getBBox(b); - if (c > k.x) { - c = k.x - } - if (h < k.x + k.width) { - h = k.x + k.width - } - if (g > k.y) { - g = k.y - } - if (a < k.y + k.height) { - a = k.y + k.height - } - } - return { - x: c, - y: g, - width: h - c, - height: a - g - } - }, - emptyRect: [0, 0, 0, 0], - getEventXY: function(d) { - var g = this, - f = g.getInherited().rtl, - c = d.getXY(), - a = g.getOwnerBody(), - i = a.getXY(), - h = g.getRect() || g.emptyRect, - j = [], - b; - if (f) { - b = a.getWidth(); - j[0] = i[0] - c[0] - h[0] + b - } else { - j[0] = c[0] - i[0] - h[0] - } - j[1] = c[1] - i[1] - h[1]; - return j - }, - clear: Ext.emptyFn, - orderByZIndex: function() { - var d = this, - a = d.getItems(), - e = false, - b, c; - if (d.getDirty()) { - for (b = 0, c = a.length; b < c; b++) { - if (a[b].attr.dirtyZIndex) { - e = true; - break - } - } - if (e) { - Ext.Array.sort(a, function(g, f) { - return g.attr.zIndex - f.attr.zIndex - }); - this.setDirty(true) - } - for (b = 0, c = a.length; b < c; b++) { - a[b].attr.dirtyZIndex = false - } - } - }, - repaint: function() { - var a = this; - a.repaint = Ext.emptyFn; - Ext.defer(function() { - delete a.repaint; - a.element.repaint() - }, 1) - }, - renderFrame: function() { - var g = this; - if (!g.element) { - return - } - if (g.dirtyPredecessorCount > 0) { - g.isPendingRenderFrame = true; - return - } - var f = g.getRect(), - c = g.getBackground(), - a = g.getItems(), - e, b, d; - if (!f) { - return - } - g.orderByZIndex(); - if (g.getDirty()) { - g.clear(); - g.clearTransform(); - if (c) { - g.renderSprite(c) - } - for (b = 0, d = a.length; b < d; b++) { - e = a[b]; - if (g.renderSprite(e) === false) { - return - } - e.attr.textPositionCount = g.textPosition - } - g.setDirty(false) - } - }, - renderSprite: Ext.emptyFn, - clearTransform: Ext.emptyFn, - destroy: function() { - var a = this; - a.removeAll(true); - a.predecessors = null; - a.successors = null; - a.callParent() - } -}); -Ext.define("Ext.draw.overrides.Surface", { - override: "Ext.draw.Surface", - hitTest: function(b, c) { - var f = this, - g = f.getItems(), - e, d, a; - c = c || Ext.draw.sprite.Sprite.defaultHitTestOptions; - for (e = g.length - 1; e >= 0; e--) { - d = g[e]; - if (d.hitTest) { - a = d.hitTest(b, c); - if (a) { - return a - } - } - } - return null - }, - hitTestEvent: function(b, a) { - var c = this.getEventXY(b); - return this.hitTest(c, a) - } -}); -Ext.define("Ext.draw.engine.SvgContext", { - requires: ["Ext.draw.Color"], - toSave: ["strokeOpacity", "strokeStyle", "fillOpacity", "fillStyle", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "lineDash", "lineDashOffset", "miterLimit", "shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor", "globalCompositeOperation", "position", "fillGradient", "strokeGradient"], - strokeOpacity: 1, - strokeStyle: "none", - fillOpacity: 1, - fillStyle: "none", - lineDash: [], - lineDashOffset: 0, - globalAlpha: 1, - lineWidth: 1, - lineCap: "butt", - lineJoin: "miter", - miterLimit: 10, - shadowOffsetX: 0, - shadowOffsetY: 0, - shadowBlur: 0, - shadowColor: "none", - globalCompositeOperation: "src", - urlStringRe: /^url\(#([\w\-]+)\)$/, - constructor: function(a) { - this.surface = a; - this.state = []; - this.matrix = new Ext.draw.Matrix(); - this.path = null; - this.clear() - }, - clear: function() { - this.group = this.surface.mainGroup; - this.position = 0; - this.path = null - }, - getElement: function(a) { - return this.surface.getSvgElement(this.group, a, this.position++) - }, - removeElement: function(d) { - var d = Ext.fly(d), - h, g, b, f, a, e, c; - if (!d) { - return - } - if (d.dom.tagName === "g") { - a = d.dom.gradients; - for (c in a) { - a[c].destroy() - } - } else { - h = d.getAttribute("fill"); - g = d.getAttribute("stroke"); - b = h && h.match(this.urlStringRe); - f = g && g.match(this.urlStringRe); - if (b && b[1]) { - e = Ext.fly(b[1]); - if (e) { - e.destroy() - } - } - if (f && f[1]) { - e = Ext.fly(f[1]); - if (e) { - e.destroy() - } - } - } - d.destroy() - }, - save: function() { - var c = this.toSave, - e = {}, - d = this.getElement("g"), - b, a; - for (a = 0; a < c.length; a++) { - b = c[a]; - if (b in this) { - e[b] = this[b] - } - } - this.position = 0; - e.matrix = this.matrix.clone(); - this.state.push(e); - this.group = d; - return d - }, - restore: function() { - var d = this.toSave, - e = this.state.pop(), - c = this.group.dom.childNodes, - b, a; - while (c.length > this.position) { - this.removeElement(c[c.length - 1]) - } - for (a = 0; a < d.length; a++) { - b = d[a]; - if (b in e) { - this[b] = e[b] - } else { - delete this[b] - } - } - this.setTransform.apply(this, e.matrix.elements); - this.group = this.group.getParent() - }, - transform: function(f, b, e, g, d, c) { - if (this.path) { - var a = Ext.draw.Matrix.fly([f, b, e, g, d, c]).inverse(); - this.path.transform(a) - } - this.matrix.append(f, b, e, g, d, c) - }, - setTransform: function(e, a, d, f, c, b) { - if (this.path) { - this.path.transform(this.matrix) - } - this.matrix.reset(); - this.transform(e, a, d, f, c, b) - }, - scale: function(a, b) { - this.transform(a, 0, 0, b, 0, 0) - }, - rotate: function(d) { - var c = Math.cos(d), - a = Math.sin(d), - b = -Math.sin(d), - e = Math.cos(d); - this.transform(c, a, b, e, 0, 0) - }, - translate: function(a, b) { - this.transform(1, 0, 0, 1, a, b) - }, - setGradientBBox: function(a) { - this.bbox = a - }, - beginPath: function() { - this.path = new Ext.draw.Path() - }, - moveTo: function(a, b) { - if (!this.path) { - this.beginPath() - } - this.path.moveTo(a, b); - this.path.element = null - }, - lineTo: function(a, b) { - if (!this.path) { - this.beginPath() - } - this.path.lineTo(a, b); - this.path.element = null - }, - rect: function(b, d, c, a) { - this.moveTo(b, d); - this.lineTo(b + c, d); - this.lineTo(b + c, d + a); - this.lineTo(b, d + a); - this.closePath() - }, - strokeRect: function(b, d, c, a) { - this.beginPath(); - this.rect(b, d, c, a); - this.stroke() - }, - fillRect: function(b, d, c, a) { - this.beginPath(); - this.rect(b, d, c, a); - this.fill() - }, - closePath: function() { - if (!this.path) { - this.beginPath() - } - this.path.closePath(); - this.path.element = null - }, - arcSvg: function(d, a, f, g, c, b, e) { - if (!this.path) { - this.beginPath() - } - this.path.arcSvg(d, a, f, g, c, b, e); - this.path.element = null - }, - arc: function(b, f, a, d, c, e) { - if (!this.path) { - this.beginPath() - } - this.path.arc(b, f, a, d, c, e); - this.path.element = null - }, - ellipse: function(a, h, g, f, d, c, b, e) { - if (!this.path) { - this.beginPath() - } - this.path.ellipse(a, h, g, f, d, c, b, e); - this.path.element = null - }, - arcTo: function(b, e, a, d, g, f, c) { - if (!this.path) { - this.beginPath() - } - this.path.arcTo(b, e, a, d, g, f, c); - this.path.element = null - }, - bezierCurveTo: function(d, f, b, e, a, c) { - if (!this.path) { - this.beginPath() - } - this.path.bezierCurveTo(d, f, b, e, a, c); - this.path.element = null - }, - strokeText: function(d, a, e) { - d = String(d); - if (this.strokeStyle) { - var b = this.getElement("text"), - c = this.surface.getSvgElement(b, "tspan", 0); - this.surface.setElementAttributes(b, { - x: a, - y: e, - transform: this.matrix.toSvg(), - stroke: this.strokeStyle, - fill: "none", - opacity: this.globalAlpha, - "stroke-opacity": this.strokeOpacity, - style: "font: " + this.font, - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }); - if (this.lineDash.length) { - this.surface.setElementAttributes(b, { - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }) - } - if (c.dom.firstChild) { - c.dom.removeChild(c.dom.firstChild) - } - this.surface.setElementAttributes(c, { - "alignment-baseline": "alphabetic" - }); - c.dom.appendChild(document.createTextNode(Ext.String.htmlDecode(d))) - } - }, - fillText: function(d, a, e) { - d = String(d); - if (this.fillStyle) { - var b = this.getElement("text"), - c = this.surface.getSvgElement(b, "tspan", 0); - this.surface.setElementAttributes(b, { - x: a, - y: e, - transform: this.matrix.toSvg(), - fill: this.fillStyle, - opacity: this.globalAlpha, - "fill-opacity": this.fillOpacity, - style: "font: " + this.font - }); - if (c.dom.firstChild) { - c.dom.removeChild(c.dom.firstChild) - } - this.surface.setElementAttributes(c, { - "alignment-baseline": "alphabetic" - }); - c.dom.appendChild(document.createTextNode(Ext.String.htmlDecode(d))) - } - }, - drawImage: function(c, k, i, l, e, p, n, a, g) { - var f = this, - d = f.getElement("image"), - j = k, - h = i, - b = typeof l === "undefined" ? c.width : l, - m = typeof e === "undefined" ? c.height : e, - o = null; - if (typeof g !== "undefined") { - o = k + " " + i + " " + l + " " + e; - j = p; - h = n; - b = a; - m = g - } - d.dom.setAttributeNS("http://www.w3.org/1999/xlink", "href", c.src); - f.surface.setElementAttributes(d, { - viewBox: o, - x: j, - y: h, - width: b, - height: m, - opacity: f.globalAlpha, - transform: f.matrix.toSvg() - }) - }, - fill: function() { - if (!this.path) { - return - } - if (this.fillStyle) { - var c, a = this.fillGradient, - d = this.bbox, - b = this.path.element; - if (!b) { - c = this.path.toString(); - b = this.path.element = this.getElement("path"); - this.surface.setElementAttributes(b, { - d: c, - transform: this.matrix.toSvg() - }) - } - this.surface.setElementAttributes(b, { - fill: a && d ? a.generateGradient(this, d) : this.fillStyle, - "fill-opacity": this.fillOpacity * this.globalAlpha - }) - } - }, - stroke: function() { - if (!this.path) { - return - } - if (this.strokeStyle) { - var c, b = this.strokeGradient, - d = this.bbox, - a = this.path.element; - if (!a || !this.path.svgString) { - c = this.path.toString(); - if (!c) { - return - } - a = this.path.element = this.getElement("path"); - this.surface.setElementAttributes(a, { - fill: "none", - d: c, - transform: this.matrix.toSvg() - }) - } - this.surface.setElementAttributes(a, { - stroke: b && d ? b.generateGradient(this, d) : this.strokeStyle, - "stroke-linecap": this.lineCap, - "stroke-linejoin": this.lineJoin, - "stroke-width": this.lineWidth, - "stroke-opacity": this.strokeOpacity * this.globalAlpha, - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }); - if (this.lineDash.length) { - this.surface.setElementAttributes(a, { - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }) - } - } - }, - fillStroke: function(a, e) { - var b = this, - d = b.fillStyle, - g = b.strokeStyle, - c = b.fillOpacity, - f = b.strokeOpacity; - if (e === undefined) { - e = a.transformFillStroke - } - if (!e) { - a.inverseMatrix.toContext(b) - } - if (d && c !== 0) { - b.fill() - } - if (g && f !== 0) { - b.stroke() - } - }, - appendPath: function(a) { - this.path = a.clone() - }, - setLineDash: function(a) { - this.lineDash = a - }, - getLineDash: function() { - return this.lineDash - }, - createLinearGradient: function(d, g, b, e) { - var f = this, - c = f.surface.getNextDef("linearGradient"), - a = f.group.dom.gradients || (f.group.dom.gradients = {}), - h; - f.surface.setElementAttributes(c, { - x1: d, - y1: g, - x2: b, - y2: e, - gradientUnits: "userSpaceOnUse" - }); - h = new Ext.draw.engine.SvgContext.Gradient(f, f.surface, c); - a[c.dom.id] = h; - return h - }, - createRadialGradient: function(b, j, d, a, i, c) { - var g = this, - e = g.surface.getNextDef("radialGradient"), - f = g.group.dom.gradients || (g.group.dom.gradients = {}), - h; - g.surface.setElementAttributes(e, { - fx: b, - fy: j, - cx: a, - cy: i, - r: c, - gradientUnits: "userSpaceOnUse" - }); - h = new Ext.draw.engine.SvgContext.Gradient(g, g.surface, e, d / c); - f[e.dom.id] = h; - return h - } -}); -Ext.define("Ext.draw.engine.SvgContext.Gradient", { - statics: { - map: {} - }, - constructor: function(c, a, d, b) { - var f = this.statics().map, - e; - e = f[d.dom.id]; - if (e) { - e.element = null - } - f[d.dom.id] = this; - this.ctx = c; - this.surface = a; - this.element = d; - this.position = 0; - this.compression = b || 0 - }, - addColorStop: function(d, b) { - var c = this.surface.getSvgElement(this.element, "stop", this.position++), - a = this.compression; - this.surface.setElementAttributes(c, { - offset: (((1 - a) * d + a) * 100).toFixed(2) + "%", - "stop-color": b, - "stop-opacity": Ext.draw.Color.fly(b).a.toFixed(15) - }) - }, - toString: function() { - var a = this.element.dom.childNodes; - while (a.length > this.position) { - Ext.fly(a[a.length - 1]).destroy() - } - return "url(#" + this.element.getId() + ")" - }, - destroy: function() { - var b = this.statics().map, - a = this.element; - if (a && a.dom) { - delete b[a.dom.id]; - a.destroy() - } - this.callParent() - } -}); -Ext.define("Ext.draw.engine.Svg", { - extend: "Ext.draw.Surface", - requires: ["Ext.draw.engine.SvgContext"], - statics: { - BBoxTextCache: {} - }, - config: { - highPrecision: false - }, - getElementConfig: function() { - return { - reference: "element", - style: { - position: "absolute" - }, - children: [{ - reference: "innerElement", - style: { - width: "100%", - height: "100%", - position: "relative" - }, - children: [{ - tag: "svg", - reference: "svgElement", - namespace: "http://www.w3.org/2000/svg", - width: "100%", - height: "100%", - version: 1.1 - }] - }] - } - }, - constructor: function(a) { - var b = this; - b.callParent([a]); - b.mainGroup = b.createSvgNode("g"); - b.defElement = b.createSvgNode("defs"); - b.svgElement.appendChild(b.mainGroup); - b.svgElement.appendChild(b.defElement); - b.ctx = new Ext.draw.engine.SvgContext(b) - }, - createSvgNode: function(a) { - var b = document.createElementNS("http://www.w3.org/2000/svg", a); - return Ext.get(b) - }, - getSvgElement: function(d, b, a) { - var c; - if (d.dom.childNodes.length > a) { - c = d.dom.childNodes[a]; - if (c.tagName === b) { - return Ext.get(c) - } else { - Ext.destroy(c) - } - } - c = Ext.get(this.createSvgNode(b)); - if (a === 0) { - d.insertFirst(c) - } else { - c.insertAfter(Ext.fly(d.dom.childNodes[a - 1])) - } - c.cache = {}; - return c - }, - setElementAttributes: function(d, b) { - var f = d.dom, - a = d.cache, - c, e; - for (c in b) { - e = b[c]; - if (a[c] !== e) { - a[c] = e; - f.setAttribute(c, e) - } - } - }, - getNextDef: function(a) { - return this.getSvgElement(this.defElement, a, this.defPosition++) - }, - clearTransform: function() { - var a = this; - a.mainGroup.set({ - transform: a.matrix.toSvg() - }) - }, - clear: function() { - this.ctx.clear(); - this.defPosition = 0 - }, - renderSprite: function(b) { - var d = this, - c = d.getRect(), - a = d.ctx; - if (b.attr.hidden || b.attr.globalAlpha === 0) { - a.save(); - a.restore(); - return - } - b.element = a.save(); - b.preRender(this); - b.useAttributes(a, c); - if (false === b.render(this, a, [0, 0, c[2], c[3]])) { - return false - } - b.setDirty(false); - a.restore() - }, - flatten: function(e, b) { - var c = '', - f = Ext.getClassName(this), - a, g, d; - c += ''; - for (d = 0; d < b.length; d++) { - a = b[d]; - if (Ext.getClassName(a) !== f) { - continue - } - g = a.getRect(); - c += ''; - c += this.serializeNode(a.svgElement.dom); - c += "" - } - c += ""; - return { - data: "data:image/svg+xml;utf8," + encodeURIComponent(c), - type: "svg" - } - }, - serializeNode: function(d) { - var b = "", - c, f, a, e; - if (d.nodeType === document.TEXT_NODE) { - return d.nodeValue - } - b += "<" + d.nodeName; - if (d.attributes.length) { - for (c = 0, f = d.attributes.length; c < f; c++) { - a = d.attributes[c]; - b += " " + a.name + '="' + a.value + '"' - } - } - b += ">"; - if (d.childNodes && d.childNodes.length) { - for (c = 0, f = d.childNodes.length; c < f; c++) { - e = d.childNodes[c]; - b += this.serializeNode(e) - } - } - b += ""; - return b - }, - destroy: function() { - var a = this; - a.ctx.destroy(); - a.mainGroup.destroy(); - delete a.mainGroup; - delete a.ctx; - a.callParent() - }, - remove: function(a, b) { - if (a && a.element) { - if (this.ctx) { - this.ctx.removeElement(a.element) - } else { - a.element.destroy() - } - a.element = null - } - this.callParent(arguments) - } -}); -Ext.draw || (Ext.draw = {}); -Ext.draw.engine || (Ext.draw.engine = {}); -Ext.draw.engine.excanvas = true; -if (!document.createElement("canvas").getContext) { - (function() { - var ab = Math; - var n = ab.round; - var l = ab.sin; - var A = ab.cos; - var H = ab.abs; - var N = ab.sqrt; - var d = 10; - var f = d / 2; - var z = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1]; - - function y() { - return this.context_ || (this.context_ = new D(this)) - } - var t = Array.prototype.slice; - - function g(j, m, p) { - var i = t.call(arguments, 2); - return function() { - return j.apply(m, i.concat(t.call(arguments))) - } - } - - function af(i) { - return String(i).replace(/&/g, "&").replace(/"/g, """) - } - - function Y(m, j, i) { - Ext.onReady(function() { - if (!m.namespaces[j]) { - m.namespaces.add(j, i, "#default#VML") - } - }) - } - - function R(j) { - Y(j, "g_vml_", "urn:schemas-microsoft-com:vml"); - Y(j, "g_o_", "urn:schemas-microsoft-com:office:office"); - if (!j.styleSheets.ex_canvas_) { - var i = j.createStyleSheet(); - i.owningElement.id = "ex_canvas_"; - i.cssText = "canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}" - } - } - R(document); - var e = { - init: function(i) { - var j = i || document; - j.createElement("canvas"); - j.attachEvent("onreadystatechange", g(this.init_, this, j)) - }, - init_: function(p) { - var m = p.getElementsByTagName("canvas"); - for (var j = 0; j < m.length; j++) { - this.initElement(m[j]) - } - }, - initElement: function(j) { - if (!j.getContext) { - j.getContext = y; - R(j.ownerDocument); - j.innerHTML = ""; - j.attachEvent("onpropertychange", x); - j.attachEvent("onresize", W); - var i = j.attributes; - if (i.width && i.width.specified) { - j.style.width = i.width.nodeValue + "px" - } else { - j.width = j.clientWidth - } - if (i.height && i.height.specified) { - j.style.height = i.height.nodeValue + "px" - } else { - j.height = j.clientHeight - } - } - return j - } - }; - - function x(j) { - var i = j.srcElement; - switch (j.propertyName) { - case "width": - i.getContext().clearRect(); - i.style.width = i.attributes.width.nodeValue + "px"; - i.firstChild.style.width = i.clientWidth + "px"; - break; - case "height": - i.getContext().clearRect(); - i.style.height = i.attributes.height.nodeValue + "px"; - i.firstChild.style.height = i.clientHeight + "px"; - break - } - } - - function W(j) { - var i = j.srcElement; - if (i.firstChild) { - i.firstChild.style.width = i.clientWidth + "px"; - i.firstChild.style.height = i.clientHeight + "px" - } - } - e.init(); - var k = []; - for (var ae = 0; ae < 16; ae++) { - for (var ad = 0; ad < 16; ad++) { - k[ae * 16 + ad] = ae.toString(16) + ad.toString(16) - } - } - - function B() { - return [ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1] - ] - } - - function J(p, m) { - var j = B(); - for (var i = 0; i < 3; i++) { - for (var ah = 0; ah < 3; ah++) { - var Z = 0; - for (var ag = 0; ag < 3; ag++) { - Z += p[i][ag] * m[ag][ah] - } - j[i][ah] = Z - } - } - return j - } - - function v(j, i) { - i.fillStyle = j.fillStyle; - i.lineCap = j.lineCap; - i.lineJoin = j.lineJoin; - i.lineDash = j.lineDash; - i.lineWidth = j.lineWidth; - i.miterLimit = j.miterLimit; - i.shadowBlur = j.shadowBlur; - i.shadowColor = j.shadowColor; - i.shadowOffsetX = j.shadowOffsetX; - i.shadowOffsetY = j.shadowOffsetY; - i.strokeStyle = j.strokeStyle; - i.globalAlpha = j.globalAlpha; - i.font = j.font; - i.textAlign = j.textAlign; - i.textBaseline = j.textBaseline; - i.arcScaleX_ = j.arcScaleX_; - i.arcScaleY_ = j.arcScaleY_; - i.lineScale_ = j.lineScale_ - } - var b = { - aliceblue: "#F0F8FF", - antiquewhite: "#FAEBD7", - aquamarine: "#7FFFD4", - azure: "#F0FFFF", - beige: "#F5F5DC", - bisque: "#FFE4C4", - black: "#000000", - blanchedalmond: "#FFEBCD", - blueviolet: "#8A2BE2", - brown: "#A52A2A", - burlywood: "#DEB887", - cadetblue: "#5F9EA0", - chartreuse: "#7FFF00", - chocolate: "#D2691E", - coral: "#FF7F50", - cornflowerblue: "#6495ED", - cornsilk: "#FFF8DC", - crimson: "#DC143C", - cyan: "#00FFFF", - darkblue: "#00008B", - darkcyan: "#008B8B", - darkgoldenrod: "#B8860B", - darkgray: "#A9A9A9", - darkgreen: "#006400", - darkgrey: "#A9A9A9", - darkkhaki: "#BDB76B", - darkmagenta: "#8B008B", - darkolivegreen: "#556B2F", - darkorange: "#FF8C00", - darkorchid: "#9932CC", - darkred: "#8B0000", - darksalmon: "#E9967A", - darkseagreen: "#8FBC8F", - darkslateblue: "#483D8B", - darkslategray: "#2F4F4F", - darkslategrey: "#2F4F4F", - darkturquoise: "#00CED1", - darkviolet: "#9400D3", - deeppink: "#FF1493", - deepskyblue: "#00BFFF", - dimgray: "#696969", - dimgrey: "#696969", - dodgerblue: "#1E90FF", - firebrick: "#B22222", - floralwhite: "#FFFAF0", - forestgreen: "#228B22", - gainsboro: "#DCDCDC", - ghostwhite: "#F8F8FF", - gold: "#FFD700", - goldenrod: "#DAA520", - grey: "#808080", - greenyellow: "#ADFF2F", - honeydew: "#F0FFF0", - hotpink: "#FF69B4", - indianred: "#CD5C5C", - indigo: "#4B0082", - ivory: "#FFFFF0", - khaki: "#F0E68C", - lavender: "#E6E6FA", - lavenderblush: "#FFF0F5", - lawngreen: "#7CFC00", - lemonchiffon: "#FFFACD", - lightblue: "#ADD8E6", - lightcoral: "#F08080", - lightcyan: "#E0FFFF", - lightgoldenrodyellow: "#FAFAD2", - lightgreen: "#90EE90", - lightgrey: "#D3D3D3", - lightpink: "#FFB6C1", - lightsalmon: "#FFA07A", - lightseagreen: "#20B2AA", - lightskyblue: "#87CEFA", - lightslategray: "#778899", - lightslategrey: "#778899", - lightsteelblue: "#B0C4DE", - lightyellow: "#FFFFE0", - limegreen: "#32CD32", - linen: "#FAF0E6", - magenta: "#FF00FF", - mediumaquamarine: "#66CDAA", - mediumblue: "#0000CD", - mediumorchid: "#BA55D3", - mediumpurple: "#9370DB", - mediumseagreen: "#3CB371", - mediumslateblue: "#7B68EE", - mediumspringgreen: "#00FA9A", - mediumturquoise: "#48D1CC", - mediumvioletred: "#C71585", - midnightblue: "#191970", - mintcream: "#F5FFFA", - mistyrose: "#FFE4E1", - moccasin: "#FFE4B5", - navajowhite: "#FFDEAD", - oldlace: "#FDF5E6", - olivedrab: "#6B8E23", - orange: "#FFA500", - orangered: "#FF4500", - orchid: "#DA70D6", - palegoldenrod: "#EEE8AA", - palegreen: "#98FB98", - paleturquoise: "#AFEEEE", - palevioletred: "#DB7093", - papayawhip: "#FFEFD5", - peachpuff: "#FFDAB9", - peru: "#CD853F", - pink: "#FFC0CB", - plum: "#DDA0DD", - powderblue: "#B0E0E6", - rosybrown: "#BC8F8F", - royalblue: "#4169E1", - saddlebrown: "#8B4513", - salmon: "#FA8072", - sandybrown: "#F4A460", - seagreen: "#2E8B57", - seashell: "#FFF5EE", - sienna: "#A0522D", - skyblue: "#87CEEB", - slateblue: "#6A5ACD", - slategray: "#708090", - slategrey: "#708090", - snow: "#FFFAFA", - springgreen: "#00FF7F", - steelblue: "#4682B4", - tan: "#D2B48C", - thistle: "#D8BFD8", - tomato: "#FF6347", - turquoise: "#40E0D0", - violet: "#EE82EE", - wheat: "#F5DEB3", - whitesmoke: "#F5F5F5", - yellowgreen: "#9ACD32" - }; - - function M(j) { - var p = j.indexOf("(", 3); - var i = j.indexOf(")", p + 1); - var m = j.substring(p + 1, i).split(","); - if (m.length != 4 || j.charAt(3) != "a") { - m[3] = 1 - } - return m - } - - function c(i) { - return parseFloat(i) / 100 - } - - function r(j, m, i) { - return Math.min(i, Math.max(m, j)) - } - - function I(ag) { - var i, ai, aj, ah, ak, Z; - ah = parseFloat(ag[0]) / 360 % 360; - if (ah < 0) { - ah++ - } - ak = r(c(ag[1]), 0, 1); - Z = r(c(ag[2]), 0, 1); - if (ak == 0) { - i = ai = aj = Z - } else { - var j = Z < 0.5 ? Z * (1 + ak) : Z + ak - Z * ak; - var m = 2 * Z - j; - i = a(m, j, ah + 1 / 3); - ai = a(m, j, ah); - aj = a(m, j, ah - 1 / 3) - } - return "#" + k[Math.floor(i * 255)] + k[Math.floor(ai * 255)] + k[Math.floor(aj * 255)] - } - - function a(j, i, m) { - if (m < 0) { - m++ - } - if (m > 1) { - m-- - } - if (6 * m < 1) { - return j + (i - j) * 6 * m - } else { - if (2 * m < 1) { - return i - } else { - if (3 * m < 2) { - return j + (i - j) * (2 / 3 - m) * 6 - } else { - return j - } - } - } - } - var C = {}; - - function F(j) { - if (j in C) { - return C[j] - } - var ag, Z = 1; - j = String(j); - if (j.charAt(0) == "#") { - ag = j - } else { - if (/^rgb/.test(j)) { - var p = M(j); - var ag = "#", - ah; - for (var m = 0; m < 3; m++) { - if (p[m].indexOf("%") != -1) { - ah = Math.floor(c(p[m]) * 255) - } else { - ah = +p[m] - } - ag += k[r(ah, 0, 255)] - } - Z = +p[3] - } else { - if (/^hsl/.test(j)) { - var p = M(j); - ag = I(p); - Z = p[3] - } else { - ag = b[j] || j - } - } - } - return C[j] = { - color: ag, - alpha: Z - } - } - var o = { - style: "normal", - variant: "normal", - weight: "normal", - size: 10, - family: "sans-serif" - }; - var L = {}; - - function E(i) { - if (L[i]) { - return L[i] - } - var p = document.createElement("div"); - var m = p.style; - try { - m.font = i - } catch (j) {} - return L[i] = { - style: m.fontStyle || o.style, - variant: m.fontVariant || o.variant, - weight: m.fontWeight || o.weight, - size: m.fontSize || o.size, - family: m.fontFamily || o.family - } - } - - function u(m, j) { - var i = {}; - for (var ah in m) { - i[ah] = m[ah] - } - var ag = parseFloat(j.currentStyle.fontSize), - Z = parseFloat(m.size); - if (typeof m.size == "number") { - i.size = m.size - } else { - if (m.size.indexOf("px") != -1) { - i.size = Z - } else { - if (m.size.indexOf("em") != -1) { - i.size = ag * Z - } else { - if (m.size.indexOf("%") != -1) { - i.size = (ag / 100) * Z - } else { - if (m.size.indexOf("pt") != -1) { - i.size = Z / 0.75 - } else { - i.size = ag - } - } - } - } - } - i.size *= 0.981; - return i - } - - function ac(i) { - return i.style + " " + i.variant + " " + i.weight + " " + i.size + "px " + i.family - } - var s = { - butt: "flat", - round: "round" - }; - - function S(i) { - return s[i] || "square" - } - - function D(i) { - this.m_ = B(); - this.mStack_ = []; - this.aStack_ = []; - this.currentPath_ = []; - this.strokeStyle = "#000"; - this.fillStyle = "#000"; - this.lineWidth = 1; - this.lineJoin = "miter"; - this.lineDash = []; - this.lineCap = "butt"; - this.miterLimit = d * 1; - this.globalAlpha = 1; - this.font = "10px sans-serif"; - this.textAlign = "left"; - this.textBaseline = "alphabetic"; - this.canvas = i; - var m = "width:" + i.clientWidth + "px;height:" + i.clientHeight + "px;overflow:hidden;position:absolute"; - var j = i.ownerDocument.createElement("div"); - j.style.cssText = m; - i.appendChild(j); - var p = j.cloneNode(false); - p.style.backgroundColor = "red"; - p.style.filter = "alpha(opacity=0)"; - i.appendChild(p); - this.element_ = j; - this.arcScaleX_ = 1; - this.arcScaleY_ = 1; - this.lineScale_ = 1 - } - var q = D.prototype; - q.clearRect = function() { - if (this.textMeasureEl_) { - this.textMeasureEl_.removeNode(true); - this.textMeasureEl_ = null - } - this.element_.innerHTML = "" - }; - q.beginPath = function() { - this.currentPath_ = [] - }; - q.moveTo = function(j, i) { - var m = V(this, j, i); - this.currentPath_.push({ - type: "moveTo", - x: m.x, - y: m.y - }); - this.currentX_ = m.x; - this.currentY_ = m.y - }; - q.lineTo = function(j, i) { - var m = V(this, j, i); - this.currentPath_.push({ - type: "lineTo", - x: m.x, - y: m.y - }); - this.currentX_ = m.x; - this.currentY_ = m.y - }; - q.bezierCurveTo = function(m, j, ak, aj, ai, ag) { - var i = V(this, ai, ag); - var ah = V(this, m, j); - var Z = V(this, ak, aj); - K(this, ah, Z, i) - }; - - function K(i, Z, m, j) { - i.currentPath_.push({ - type: "bezierCurveTo", - cp1x: Z.x, - cp1y: Z.y, - cp2x: m.x, - cp2y: m.y, - x: j.x, - y: j.y - }); - i.currentX_ = j.x; - i.currentY_ = j.y - } - q.quadraticCurveTo = function(ai, m, j, i) { - var ah = V(this, ai, m); - var ag = V(this, j, i); - var aj = { - x: this.currentX_ + 2 / 3 * (ah.x - this.currentX_), - y: this.currentY_ + 2 / 3 * (ah.y - this.currentY_) - }; - var Z = { - x: aj.x + (ag.x - this.currentX_) / 3, - y: aj.y + (ag.y - this.currentY_) / 3 - }; - K(this, aj, Z, ag) - }; - q.arc = function(al, aj, ak, ag, j, m) { - ak *= d; - var ap = m ? "at" : "wa"; - var am = al + A(ag) * ak - f; - var ao = aj + l(ag) * ak - f; - var i = al + A(j) * ak - f; - var an = aj + l(j) * ak - f; - if (am == i && !m) { - am += 0.125 - } - var Z = V(this, al, aj); - var ai = V(this, am, ao); - var ah = V(this, i, an); - this.currentPath_.push({ - type: ap, - x: Z.x, - y: Z.y, - radius: ak, - xStart: ai.x, - yStart: ai.y, - xEnd: ah.x, - yEnd: ah.y - }) - }; - q.rect = function(m, j, i, p) { - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath() - }; - q.strokeRect = function(m, j, i, p) { - var Z = this.currentPath_; - this.beginPath(); - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath(); - this.stroke(); - this.currentPath_ = Z - }; - q.fillRect = function(m, j, i, p) { - var Z = this.currentPath_; - this.beginPath(); - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath(); - this.fill(); - this.currentPath_ = Z - }; - q.createLinearGradient = function(j, p, i, m) { - var Z = new U("gradient"); - Z.x0_ = j; - Z.y0_ = p; - Z.x1_ = i; - Z.y1_ = m; - return Z - }; - q.createRadialGradient = function(p, ag, m, j, Z, i) { - var ah = new U("gradientradial"); - ah.x0_ = p; - ah.y0_ = ag; - ah.r0_ = m; - ah.x1_ = j; - ah.y1_ = Z; - ah.r1_ = i; - return ah - }; - q.drawImage = function(an, j) { - var ah, Z, aj, ar, al, ak, ao, av; - var ai = an.runtimeStyle.width; - var am = an.runtimeStyle.height; - an.runtimeStyle.width = "auto"; - an.runtimeStyle.height = "auto"; - var ag = an.width; - var aq = an.height; - an.runtimeStyle.width = ai; - an.runtimeStyle.height = am; - if (arguments.length == 3) { - ah = arguments[1]; - Z = arguments[2]; - al = ak = 0; - ao = aj = ag; - av = ar = aq - } else { - if (arguments.length == 5) { - ah = arguments[1]; - Z = arguments[2]; - aj = arguments[3]; - ar = arguments[4]; - al = ak = 0; - ao = ag; - av = aq - } else { - if (arguments.length == 9) { - al = arguments[1]; - ak = arguments[2]; - ao = arguments[3]; - av = arguments[4]; - ah = arguments[5]; - Z = arguments[6]; - aj = arguments[7]; - ar = arguments[8] - } else { - throw Error("Invalid number of arguments") - } - } - } - var au = V(this, ah, Z); - var at = []; - var i = 10; - var p = 10; - var ap = this.m_; - at.push(" ', '", ""); - this.element_.insertAdjacentHTML("BeforeEnd", at.join("")) - }; - q.setLineDash = function(i) { - if (i.length === 1) { - i = i.slice(); - i[1] = i[0] - } - this.lineDash = i - }; - q.getLineDash = function() { - return this.lineDash - }; - q.stroke = function(ak) { - var ai = []; - var m = 10; - var al = 10; - ai.push(" aj.x) { - aj.x = j.x - } - if (Z.y == null || j.y < Z.y) { - Z.y = j.y - } - if (aj.y == null || j.y > aj.y) { - aj.y = j.y - } - } - } - ai.push(' ">'); - if (!ak) { - w(this, ai) - } else { - G(this, ai, Z, aj) - } - ai.push(""); - this.element_.insertAdjacentHTML("beforeEnd", ai.join("")) - }; - - function w(m, ag) { - var j = F(m.strokeStyle); - var p = j.color; - var Z = j.alpha * m.globalAlpha; - var i = m.lineScale_ * m.lineWidth; - if (i < 1) { - Z *= i - } - ag.push("') - } - - function G(aq, ai, aK, ar) { - var aj = aq.fillStyle; - var aB = aq.arcScaleX_; - var aA = aq.arcScaleY_; - var j = ar.x - aK.x; - var p = ar.y - aK.y; - if (aj instanceof U) { - var an = 0; - var aF = { - x: 0, - y: 0 - }; - var ax = 0; - var am = 1; - if (aj.type_ == "gradient") { - var al = aj.x0_ / aB; - var m = aj.y0_ / aA; - var ak = aj.x1_ / aB; - var aM = aj.y1_ / aA; - var aJ = V(aq, al, m); - var aI = V(aq, ak, aM); - var ag = aI.x - aJ.x; - var Z = aI.y - aJ.y; - an = Math.atan2(ag, Z) * 180 / Math.PI; - if (an < 0) { - an += 360 - } - if (an < 0.000001) { - an = 0 - } - } else { - var aJ = V(aq, aj.x0_, aj.y0_); - aF = { - x: (aJ.x - aK.x) / j, - y: (aJ.y - aK.y) / p - }; - j /= aB * d; - p /= aA * d; - var aD = ab.max(j, p); - ax = 2 * aj.r0_ / aD; - am = 2 * aj.r1_ / aD - ax - } - var av = aj.colors_; - av.sort(function(aN, i) { - return aN.offset - i.offset - }); - var ap = av.length; - var au = av[0].color; - var at = av[ap - 1].color; - var az = av[0].alpha * aq.globalAlpha; - var ay = av[ap - 1].alpha * aq.globalAlpha; - var aE = []; - for (var aH = 0; aH < ap; aH++) { - var ao = av[aH]; - aE.push(ao.offset * am + ax + " " + ao.color) - } - ai.push('') - } else { - if (aj instanceof T) { - if (j && p) { - var ah = -aK.x; - var aC = -aK.y; - ai.push("') - } - } else { - var aL = F(aq.fillStyle); - var aw = aL.color; - var aG = aL.alpha * aq.globalAlpha; - ai.push('') - } - } - } - q.fill = function() { - this.$stroke(true) - }; - q.closePath = function() { - this.currentPath_.push({ - type: "close" - }) - }; - - function V(j, Z, p) { - var i = j.m_; - return { - x: d * (Z * i[0][0] + p * i[1][0] + i[2][0]) - f, - y: d * (Z * i[0][1] + p * i[1][1] + i[2][1]) - f - } - } - q.save = function() { - var i = {}; - v(this, i); - this.aStack_.push(i); - this.mStack_.push(this.m_); - this.m_ = J(B(), this.m_) - }; - q.restore = function() { - if (this.aStack_.length) { - v(this.aStack_.pop(), this); - this.m_ = this.mStack_.pop() - } - }; - - function h(i) { - return isFinite(i[0][0]) && isFinite(i[0][1]) && isFinite(i[1][0]) && isFinite(i[1][1]) && isFinite(i[2][0]) && isFinite(i[2][1]) - } - - function aa(j, i, p) { - if (!h(i)) { - return - } - j.m_ = i; - if (p) { - var Z = i[0][0] * i[1][1] - i[0][1] * i[1][0]; - j.lineScale_ = N(H(Z)) - } - } - q.translate = function(m, j) { - var i = [ - [1, 0, 0], - [0, 1, 0], - [m, j, 1] - ]; - aa(this, J(i, this.m_), false) - }; - q.rotate = function(j) { - var p = A(j); - var m = l(j); - var i = [ - [p, m, 0], - [-m, p, 0], - [0, 0, 1] - ]; - aa(this, J(i, this.m_), false) - }; - q.scale = function(m, j) { - this.arcScaleX_ *= m; - this.arcScaleY_ *= j; - var i = [ - [m, 0, 0], - [0, j, 0], - [0, 0, 1] - ]; - aa(this, J(i, this.m_), true) - }; - q.transform = function(Z, p, ah, ag, j, i) { - var m = [ - [Z, p, 0], - [ah, ag, 0], - [j, i, 1] - ]; - aa(this, J(m, this.m_), true) - }; - q.setTransform = function(ag, Z, ai, ah, p, j) { - var i = [ - [ag, Z, 0], - [ai, ah, 0], - [p, j, 1] - ]; - aa(this, i, true) - }; - q.drawText_ = function(am, ak, aj, ap, ai) { - var ao = this.m_, - at = 1000, - j = 0, - ar = at, - ah = { - x: 0, - y: 0 - }, - ag = []; - var i = u(E(this.font), this.element_); - var p = ac(i); - var au = this.element_.currentStyle; - var Z = this.textAlign.toLowerCase(); - switch (Z) { - case "left": - case "center": - case "right": - break; - case "end": - Z = au.direction == "ltr" ? "right" : "left"; - break; - case "start": - Z = au.direction == "rtl" ? "right" : "left"; - break; - default: - Z = "left" - } - switch (this.textBaseline) { - case "hanging": - case "top": - ah.y = i.size / 1.75; - break; - case "middle": - break; - default: - case null: - case "alphabetic": - case "ideographic": - case "bottom": - ah.y = -i.size / 3; - break - } - switch (Z) { - case "right": - j = at; - ar = 0.05; - break; - case "center": - j = ar = at / 2; - break - } - var aq = V(this, ak + ah.x, aj + ah.y); - ag.push(''); - if (ai) { - w(this, ag) - } else { - G(this, ag, { - x: -j, - y: 0 - }, { - x: ar, - y: i.size - }) - } - var an = ao[0][0].toFixed(3) + "," + ao[1][0].toFixed(3) + "," + ao[0][1].toFixed(3) + "," + ao[1][1].toFixed(3) + ",0,0"; - var al = n(aq.x / d) + "," + n(aq.y / d); - ag.push('', '', ''); - this.element_.insertAdjacentHTML("beforeEnd", ag.join("")) - }; - q.fillText = function(m, i, p, j) { - this.drawText_(m, i, p, j, false) - }; - q.strokeText = function(m, i, p, j) { - this.drawText_(m, i, p, j, true) - }; - q.measureText = function(m) { - if (!this.textMeasureEl_) { - var i = ''; - this.element_.insertAdjacentHTML("beforeEnd", i); - this.textMeasureEl_ = this.element_.lastChild - } - var j = this.element_.ownerDocument; - this.textMeasureEl_.innerHTML = ""; - this.textMeasureEl_.style.font = this.font; - this.textMeasureEl_.appendChild(j.createTextNode(m)); - return { - width: this.textMeasureEl_.offsetWidth - } - }; - q.clip = function() {}; - q.arcTo = function() {}; - q.createPattern = function(j, i) { - return new T(j, i) - }; - - function U(i) { - this.type_ = i; - this.x0_ = 0; - this.y0_ = 0; - this.r0_ = 0; - this.x1_ = 0; - this.y1_ = 0; - this.r1_ = 0; - this.colors_ = [] - } - U.prototype.addColorStop = function(j, i) { - i = F(i); - this.colors_.push({ - offset: j, - color: i.color, - alpha: i.alpha - }) - }; - - function T(j, i) { - Q(j); - switch (i) { - case "repeat": - case null: - case "": - this.repetition_ = "repeat"; - break; - case "repeat-x": - case "repeat-y": - case "no-repeat": - this.repetition_ = i; - break; - default: - O("SYNTAX_ERR") - } - this.src_ = j.src; - this.width_ = j.width; - this.height_ = j.height - } - - function O(i) { - throw new P(i) - } - - function Q(i) { - if (!i || i.nodeType != 1 || i.tagName != "IMG") { - O("TYPE_MISMATCH_ERR") - } - if (i.readyState != "complete") { - O("INVALID_STATE_ERR") - } - } - - function P(i) { - this.code = this[i]; - this.message = i + ": DOM Exception " + this.code - } - var X = P.prototype = new Error(); - X.INDEX_SIZE_ERR = 1; - X.DOMSTRING_SIZE_ERR = 2; - X.HIERARCHY_REQUEST_ERR = 3; - X.WRONG_DOCUMENT_ERR = 4; - X.INVALID_CHARACTER_ERR = 5; - X.NO_DATA_ALLOWED_ERR = 6; - X.NO_MODIFICATION_ALLOWED_ERR = 7; - X.NOT_FOUND_ERR = 8; - X.NOT_SUPPORTED_ERR = 9; - X.INUSE_ATTRIBUTE_ERR = 10; - X.INVALID_STATE_ERR = 11; - X.SYNTAX_ERR = 12; - X.INVALID_MODIFICATION_ERR = 13; - X.NAMESPACE_ERR = 14; - X.INVALID_ACCESS_ERR = 15; - X.VALIDATION_ERR = 16; - X.TYPE_MISMATCH_ERR = 17; - G_vmlCanvasManager = e; - CanvasRenderingContext2D = D; - CanvasGradient = U; - CanvasPattern = T; - DOMException = P - })() -} -Ext.define("Ext.draw.engine.Canvas", { - extend: "Ext.draw.Surface", - requires: ["Ext.draw.engine.excanvas", "Ext.draw.Animator", "Ext.draw.Color"], - config: { - highPrecision: false - }, - statics: { - contextOverrides: { - setGradientBBox: function(a) { - this.bbox = a - }, - fill: function() { - var c = this.fillStyle, - a = this.fillGradient, - b = this.fillOpacity, - d = this.globalAlpha, - e = this.bbox; - if (c !== Ext.draw.Color.RGBA_NONE && b !== 0) { - if (a && e) { - this.fillStyle = a.generateGradient(this, e) - } - if (b !== 1) { - this.globalAlpha = d * b - } - this.$fill(); - if (b !== 1) { - this.globalAlpha = d - } - if (a && e) { - this.fillStyle = c - } - } - }, - stroke: function() { - var e = this.strokeStyle, - c = this.strokeGradient, - a = this.strokeOpacity, - b = this.globalAlpha, - d = this.bbox; - if (e !== Ext.draw.Color.RGBA_NONE && a !== 0) { - if (c && d) { - this.strokeStyle = c.generateGradient(this, d) - } - if (a !== 1) { - this.globalAlpha = b * a - } - this.$stroke(); - if (a !== 1) { - this.globalAlpha = b - } - if (c && d) { - this.strokeStyle = e - } - } - }, - fillStroke: function(d, e) { - var j = this, - i = this.fillStyle, - h = this.fillOpacity, - f = this.strokeStyle, - c = this.strokeOpacity, - b = j.shadowColor, - a = j.shadowBlur, - g = Ext.draw.Color.RGBA_NONE; - if (e === undefined) { - e = d.transformFillStroke - } - if (!e) { - d.inverseMatrix.toContext(j) - } - if (i !== g && h !== 0) { - j.fill(); - j.shadowColor = g; - j.shadowBlur = 0 - } - if (f !== g && c !== 0) { - j.stroke() - } - j.shadowColor = b; - j.shadowBlur = a - }, - setLineDash: function(a) { - if (this.$setLineDash) { - this.$setLineDash(a) - } - }, - getLineDash: function() { - if (this.$getLineDash) { - return this.$getLineDash() - } - }, - ellipse: function(g, e, c, a, j, b, f, d) { - var i = Math.cos(j), - h = Math.sin(j); - this.transform(i * c, h * c, -h * a, i * a, g, e); - this.arc(0, 0, 1, b, f, d); - this.transform(i / c, -h / a, h / c, i / a, -(i * g + h * e) / c, (h * g - i * e) / a) - }, - appendPath: function(f) { - var e = this, - c = 0, - b = 0, - a = f.commands, - g = f.params, - d = a.length; - e.beginPath(); - for (; c < d; c++) { - switch (a[c]) { - case "M": - e.moveTo(g[b], g[b + 1]); - b += 2; - break; - case "L": - e.lineTo(g[b], g[b + 1]); - b += 2; - break; - case "C": - e.bezierCurveTo(g[b], g[b + 1], g[b + 2], g[b + 3], g[b + 4], g[b + 5]); - b += 6; - break; - case "Z": - e.closePath(); - break - } - } - }, - save: function() { - var c = this.toSave, - d = c.length, - e = d && {}, - b = 0, - a; - for (; b < d; b++) { - a = c[b]; - if (a in this) { - e[a] = this[a] - } - } - this.state.push(e); - this.$save() - }, - restore: function() { - var b = this.state.pop(), - a; - if (b) { - for (a in b) { - this[a] = b[a] - } - } - this.$restore() - } - } - }, - splitThreshold: 3000, - toSave: ["fillGradient", "strokeGradient"], - element: { - reference: "element", - style: { - position: "absolute" - }, - children: [{ - reference: "innerElement", - style: { - width: "100%", - height: "100%", - position: "relative" - } - }] - }, - createCanvas: function() { - var c = Ext.Element.create({ - tag: "canvas", - cls: Ext.baseCSSPrefix + "surface-canvas" - }); - window.G_vmlCanvasManager && G_vmlCanvasManager.initElement(c.dom); - var d = Ext.draw.engine.Canvas.contextOverrides, - a = c.dom.getContext("2d"), - b; - if (a.ellipse) { - delete d.ellipse - } - a.state = []; - a.toSave = this.toSave; - for (b in d) { - a["$" + b] = a[b] - } - Ext.apply(a, d); - if (this.getHighPrecision()) { - this.enablePrecisionCompensation(a) - } else { - this.disablePrecisionCompensation(a) - } - this.innerElement.appendChild(c); - this.canvases.push(c); - this.contexts.push(a) - }, - updateHighPrecision: function(d) { - var e = this.contexts, - c = e.length, - b, a; - for (b = 0; b < c; b++) { - a = e[b]; - if (d) { - this.enablePrecisionCompensation(a) - } else { - this.disablePrecisionCompensation(a) - } - } - }, - precisionNames: ["rect", "fillRect", "strokeRect", "clearRect", "moveTo", "lineTo", "arc", "arcTo", "save", "restore", "updatePrecisionCompensate", "setTransform", "transform", "scale", "translate", "rotate", "quadraticCurveTo", "bezierCurveTo", "createLinearGradient", "createRadialGradient", "fillText", "strokeText", "drawImage"], - disablePrecisionCompensation: function(b) { - var a = Ext.draw.engine.Canvas.contextOverrides, - f = this.precisionNames, - e = f.length, - d, c; - for (d = 0; d < e; d++) { - c = f[d]; - if (!(c in a)) { - delete b[c] - } - } - this.setDirty(true) - }, - enablePrecisionCompensation: function(j) { - var c = this, - a = 1, - g = 1, - l = 0, - k = 0, - i = new Ext.draw.Matrix(), - b = [], - e = {}, - d = Ext.draw.engine.Canvas.contextOverrides, - h = j.constructor.prototype; - var f = { - toSave: c.toSave, - rect: function(m, p, n, o) { - return h.rect.call(this, m * a + l, p * g + k, n * a, o * g) - }, - fillRect: function(m, p, n, o) { - this.updatePrecisionCompensateRect(); - h.fillRect.call(this, m * a + l, p * g + k, n * a, o * g); - this.updatePrecisionCompensate() - }, - strokeRect: function(m, p, n, o) { - this.updatePrecisionCompensateRect(); - h.strokeRect.call(this, m * a + l, p * g + k, n * a, o * g); - this.updatePrecisionCompensate() - }, - clearRect: function(m, p, n, o) { - return h.clearRect.call(this, m * a + l, p * g + k, n * a, o * g) - }, - moveTo: function(m, n) { - return h.moveTo.call(this, m * a + l, n * g + k) - }, - lineTo: function(m, n) { - return h.lineTo.call(this, m * a + l, n * g + k) - }, - arc: function(n, r, m, p, o, q) { - this.updatePrecisionCompensateRect(); - h.arc.call(this, n * a + l, r * a + k, m * a, p, o, q); - this.updatePrecisionCompensate() - }, - arcTo: function(o, q, n, p, m) { - this.updatePrecisionCompensateRect(); - h.arcTo.call(this, o * a + l, q * g + k, n * a + l, p * g + k, m * a); - this.updatePrecisionCompensate() - }, - save: function() { - b.push(i); - i = i.clone(); - d.save.call(this); - h.save.call(this) - }, - restore: function() { - i = b.pop(); - d.restore.call(this); - h.restore.call(this); - this.updatePrecisionCompensate() - }, - updatePrecisionCompensate: function() { - i.precisionCompensate(c.devicePixelRatio, e); - a = e.xx; - g = e.yy; - l = e.dx; - k = e.dy; - h.setTransform.call(this, c.devicePixelRatio, e.b, e.c, e.d, 0, 0) - }, - updatePrecisionCompensateRect: function() { - i.precisionCompensateRect(c.devicePixelRatio, e); - a = e.xx; - g = e.yy; - l = e.dx; - k = e.dy; - h.setTransform.call(this, c.devicePixelRatio, e.b, e.c, e.d, 0, 0) - }, - setTransform: function(q, o, n, m, r, p) { - i.set(q, o, n, m, r, p); - this.updatePrecisionCompensate() - }, - transform: function(q, o, n, m, r, p) { - i.append(q, o, n, m, r, p); - this.updatePrecisionCompensate() - }, - scale: function(n, m) { - this.transform(n, 0, 0, m, 0, 0) - }, - translate: function(n, m) { - this.transform(1, 0, 0, 1, n, m) - }, - rotate: function(o) { - var n = Math.cos(o), - m = Math.sin(o); - this.transform(n, m, -m, n, 0, 0) - }, - quadraticCurveTo: function(n, p, m, o) { - h.quadraticCurveTo.call(this, n * a + l, p * g + k, m * a + l, o * g + k) - }, - bezierCurveTo: function(r, p, o, n, m, q) { - h.bezierCurveTo.call(this, r * a + l, p * g + k, o * a + l, n * g + k, m * a + l, q * g + k) - }, - createLinearGradient: function(n, p, m, o) { - this.updatePrecisionCompensateRect(); - var q = h.createLinearGradient.call(this, n * a + l, p * g + k, m * a + l, o * g + k); - this.updatePrecisionCompensate(); - return q - }, - createRadialGradient: function(p, r, o, n, q, m) { - this.updatePrecisionCompensateRect(); - var s = h.createLinearGradient.call(this, p * a + l, r * a + k, o * a, n * a + l, q * a + k, m * a); - this.updatePrecisionCompensate(); - return s - }, - fillText: function(o, m, p, n) { - h.setTransform.apply(this, i.elements); - if (typeof n === "undefined") { - h.fillText.call(this, o, m, p) - } else { - h.fillText.call(this, o, m, p, n) - } - this.updatePrecisionCompensate() - }, - strokeText: function(o, m, p, n) { - h.setTransform.apply(this, i.elements); - if (typeof n === "undefined") { - h.strokeText.call(this, o, m, p) - } else { - h.strokeText.call(this, o, m, p, n) - } - this.updatePrecisionCompensate() - }, - fill: function() { - var m = this.fillGradient, - n = this.bbox; - this.updatePrecisionCompensateRect(); - if (m && n) { - this.fillStyle = m.generateGradient(this, n) - } - h.fill.call(this); - this.updatePrecisionCompensate() - }, - stroke: function() { - var m = this.strokeGradient, - n = this.bbox; - this.updatePrecisionCompensateRect(); - if (m && n) { - this.strokeStyle = m.generateGradient(this, n) - } - h.stroke.call(this); - this.updatePrecisionCompensate() - }, - drawImage: function(u, s, r, q, p, o, n, m, t) { - switch (arguments.length) { - case 3: - return h.drawImage.call(this, u, s * a + l, r * g + k); - case 5: - return h.drawImage.call(this, u, s * a + l, r * g + k, q * a, p * g); - case 9: - return h.drawImage.call(this, u, s, r, q, p, o * a + l, n * g * k, m * a, t * g) - } - } - }; - Ext.apply(j, f); - this.setDirty(true) - }, - updateRect: function(a) { - this.callParent([a]); - var C = this, - p = Math.floor(a[0]), - e = Math.floor(a[1]), - g = Math.ceil(a[0] + a[2]), - B = Math.ceil(a[1] + a[3]), - u = C.devicePixelRatio, - D = C.canvases, - d = g - p, - y = B - e, - n = Math.round(C.splitThreshold / u), - c = C.xSplits = Math.ceil(d / n), - f = C.ySplits = Math.ceil(y / n), - v, s, q, A, z, x, o, m; - for (s = 0, z = 0; s < f; s++, z += n) { - for (v = 0, A = 0; v < c; v++, A += n) { - q = s * c + v; - if (q >= D.length) { - C.createCanvas() - } - x = D[q].dom; - x.style.left = A + "px"; - x.style.top = z + "px"; - m = Math.min(n, y - z); - if (m * u !== x.height) { - x.height = m * u; - x.style.height = m + "px" - } - o = Math.min(n, d - A); - if (o * u !== x.width) { - x.width = o * u; - x.style.width = o + "px" - } - C.applyDefaults(C.contexts[q]) - } - } - for (q += 1; q < D.length; q++) { - D[q].destroy() - } - C.activeCanvases = c * f; - D.length = C.activeCanvases; - C.clear() - }, - clearTransform: function() { - var f = this, - a = f.xSplits, - g = f.ySplits, - d = f.contexts, - h = f.splitThreshold, - l = f.devicePixelRatio, - e, c, b, m; - for (e = 0; e < a; e++) { - for (c = 0; c < g; c++) { - b = c * a + e; - m = d[b]; - m.translate(-h * e, -h * c); - m.scale(l, l); - f.matrix.toContext(m) - } - } - }, - renderSprite: function(q) { - var C = this, - b = C.getRect(), - e = C.matrix, - g = q.getParent(), - v = Ext.draw.Matrix.fly([1, 0, 0, 1, 0, 0]), - p = C.splitThreshold / C.devicePixelRatio, - c = C.xSplits, - m = C.ySplits, - A, z, s, a, r, o, d = 0, - B, n = 0, - f, l = b[2], - y = b[3], - x, u, t; - while (g && (g !== C)) { - v.prependMatrix(g.matrix || g.attr && g.attr.matrix); - g = g.getParent() - } - v.prependMatrix(e); - a = q.getBBox(); - if (a) { - a = v.transformBBox(a) - } - q.preRender(C); - if (q.attr.hidden || q.attr.globalAlpha === 0) { - q.setDirty(false); - return - } - for (u = 0, z = 0; u < m; u++, z += p) { - for (x = 0, A = 0; x < c; x++, A += p) { - t = u * c + x; - s = C.contexts[t]; - r = Math.min(p, l - A); - o = Math.min(p, y - z); - d = A; - B = d + r; - n = z; - f = n + o; - if (a) { - if (a.x > B || a.x + a.width < d || a.y > f || a.y + a.height < n) { - continue - } - } - s.save(); - q.useAttributes(s, b); - if (false === q.render(C, s, [d, n, r, o], b)) { - return false - } - s.restore() - } - } - q.setDirty(false) - }, - flatten: function(n, a) { - var k = document.createElement("canvas"), - f = Ext.getClassName(this), - g = this.devicePixelRatio, - l = k.getContext("2d"), - b, c, h, e, d, m; - k.width = Math.ceil(n.width * g); - k.height = Math.ceil(n.height * g); - for (e = 0; e < a.length; e++) { - b = a[e]; - if (Ext.getClassName(b) !== f) { - continue - } - h = b.getRect(); - for (d = 0; d < b.canvases.length; d++) { - c = b.canvases[d]; - m = c.getOffsetsTo(c.getParent()); - l.drawImage(c.dom, (h[0] + m[0]) * g, (h[1] + m[1]) * g) - } - } - return { - data: k.toDataURL(), - type: "png" - } - }, - applyDefaults: function(a) { - var b = Ext.draw.Color.RGBA_NONE; - a.strokeStyle = b; - a.fillStyle = b; - a.textAlign = "start"; - a.textBaseline = "alphabetic"; - a.miterLimit = 1 - }, - clear: function() { - var d = this, - e = d.activeCanvases, - c, b, a; - for (c = 0; c < e; c++) { - b = d.canvases[c].dom; - a = d.contexts[c]; - a.setTransform(1, 0, 0, 1, 0, 0); - a.clearRect(0, 0, b.width, b.height) - } - d.setDirty(true) - }, - destroy: function() { - var c = this, - a, b = c.canvases.length; - for (a = 0; a < b; a++) { - c.contexts[a] = null; - c.canvases[a].destroy(); - c.canvases[a] = null - } - delete c.contexts; - delete c.canvases; - c.callParent() - }, - privates: { - initElement: function() { - var a = this; - a.callParent(); - a.canvases = []; - a.contexts = []; - a.activeCanvases = (a.xSplits = 0) * (a.ySplits = 0) - } - } -}, function() { - var c = this, - b = c.prototype, - a = 10000000000; - if (Ext.os.is.Android4 && Ext.browser.is.Chrome) { - a = 3000 - } else { - if (Ext.is.iOS) { - a = 2200 - } - } - b.splitThreshold = a -}); -Ext.define("Ext.draw.Container", { - extend: "Ext.draw.ContainerBase", - alternateClassName: "Ext.draw.Component", - xtype: "draw", - defaultType: "surface", - isDrawContainer: true, - requires: ["Ext.draw.Surface", "Ext.draw.engine.Svg", "Ext.draw.engine.Canvas", "Ext.draw.gradient.GradientDefinition"], - engine: "Ext.draw.engine.Canvas", - config: { - cls: Ext.baseCSSPrefix + "draw-container", - resizeHandler: null, - sprites: null, - gradients: [] - }, - defaultDownloadServerUrl: "http://svg.sencha.io", - supportedFormats: ["png", "pdf", "jpeg", "gif"], - supportedOptions: { - version: Ext.isNumber, - data: Ext.isString, - format: function(a) { - return Ext.Array.indexOf(this.supportedFormats, a) >= 0 - }, - filename: Ext.isString, - width: Ext.isNumber, - height: Ext.isNumber, - scale: Ext.isNumber, - pdf: Ext.isObject, - jpeg: Ext.isObject - }, - initAnimator: function() { - this.frameCallbackId = Ext.draw.Animator.addFrameCallback("renderFrame", this) - }, - applyGradients: function(b) { - var a = [], - c, f, d, e; - if (!Ext.isArray(b)) { - return a - } - for (c = 0, f = b.length; c < f; c++) { - d = b[c]; - if (!Ext.isObject(d)) { - continue - } - if (typeof d.type !== "string") { - d.type = "linear" - } - if (d.angle) { - d.degrees = d.angle; - delete d.angle - } - if (Ext.isObject(d.stops)) { - d.stops = (function(i) { - var g = [], - h; - for (e in i) { - h = i[e]; - h.offset = e / 100; - g.push(h) - } - return g - })(d.stops) - } - a.push(d) - } - Ext.draw.gradient.GradientDefinition.add(a); - return a - }, - applySprites: function(f) { - if (!f) { - return - } - f = Ext.Array.from(f); - var e = f.length, - b = [], - d, a, c; - for (d = 0; d < e; d++) { - c = f[d]; - a = c.surface; - if (!(a && a.isSurface)) { - if (Ext.isString(a)) { - a = this.getSurface(a) - } else { - a = this.getSurface("main") - } - } - c = a.add(c); - b.push(c) - } - return b - }, - onBodyResize: function() { - var b = this.element, - a; - if (!b) { - return - } - a = b.getSize(); - if (a.width && a.height) { - this.setBodySize(a) - } - }, - setBodySize: function(c) { - var d = this, - b = d.getResizeHandler() || d.defaultResizeHandler, - a; - d.fireEvent("bodyresize", d, c); - a = b.call(d, c); - if (a !== false) { - d.renderFrame() - } - }, - defaultResizeHandler: function(a) { - this.getItems().each(function(b) { - b.setRect([0, 0, a.width, a.height]) - }) - }, - getSurface: function(d) { - d = this.getId() + "-" + (d || "main"); - var c = this, - b = c.getItems(), - a = b.get(d); - if (!a) { - a = c.add({ - xclass: c.engine, - id: d - }); - c.onBodyResize() - } - return a - }, - renderFrame: function() { - var e = this, - a = e.getItems(), - b, d, c; - for (b = 0, d = a.length; b < d; b++) { - c = a.items[b]; - if (c.isSurface) { - c.renderFrame() - } - } - }, - getImage: function(k) { - var l = this.innerElement.getSize(), - a = Array.prototype.slice.call(this.items.items), - d, g, c = this.surfaceZIndexes, - f, e, b, h; - for (e = 1; e < a.length; e++) { - b = a[e]; - h = c[b.type]; - f = e - 1; - while (f >= 0 && c[a[f].type] > h) { - a[f + 1] = a[f]; - f-- - } - a[f + 1] = b - } - d = a[0].flatten(l, a); - if (k === "image") { - g = new Image(); - g.src = d.data; - d.data = g; - return d - } - if (k === "stream") { - d.data = d.data.replace(/^data:image\/[^;]+/, "data:application/octet-stream"); - return d - } - return d - }, - download: function(d) { - var e = this, - a = [], - b, c, f; - d = Ext.apply({ - version: 2, - data: e.getImage().data - }, d); - for (c in d) { - if (d.hasOwnProperty(c)) { - f = d[c]; - if (c in e.supportedOptions) { - if (e.supportedOptions[c].call(e, f)) { - a.push({ - tag: "input", - type: "hidden", - name: c, - value: Ext.String.htmlEncode(Ext.isObject(f) ? Ext.JSON.encode(f) : f) - }) - } - } - } - } - b = Ext.dom.Helper.markup({ - tag: "html", - children: [{ - tag: "head" - }, { - tag: "body", - children: [{ - tag: "form", - method: "POST", - action: d.url || e.defaultDownloadServerUrl, - children: a - }, { - tag: "script", - type: "text/javascript", - children: 'document.getElementsByTagName("form")[0].submit();' - }] - }] - }); - window.open("", "ImageDownload_" + Date.now()).document.write(b) - }, - destroy: function() { - var a = this.frameCallbackId; - if (a) { - Ext.draw.Animator.removeFrameCallback(a) - } - this.callParent() - } -}, function() { - if (location.search.match("svg")) { - Ext.draw.Container.prototype.engine = "Ext.draw.engine.Svg" - } else { - if ((Ext.os.is.BlackBerry && Ext.os.version.getMajor() === 10) || (Ext.browser.is.AndroidStock4 && (Ext.os.version.getMinor() === 1 || Ext.os.version.getMinor() === 2 || Ext.os.version.getMinor() === 3))) { - Ext.draw.Container.prototype.engine = "Ext.draw.engine.Svg" - } - } -}); -Ext.define("Ext.chart.theme.Base", { - mixins: { - factoryable: "Ext.mixin.Factoryable" - }, - requires: ["Ext.draw.Color"], - factoryConfig: { - type: "chart.theme" - }, - isTheme: true, - config: { - baseColor: null, - colors: undefined, - gradients: null, - chart: { - defaults: { - background: "white" - } - }, - axis: { - defaults: { - label: { - x: 0, - y: 0, - textBaseline: "middle", - textAlign: "center", - fontSize: "default", - fontFamily: "default", - fontWeight: "default", - fillStyle: "black" - }, - title: { - fillStyle: "black", - fontSize: "default*1.23", - fontFamily: "default", - fontWeight: "default" - }, - style: { - strokeStyle: "black" - }, - grid: { - strokeStyle: "rgb(221, 221, 221)" - } - }, - top: { - style: { - textPadding: 5 - } - }, - bottom: { - style: { - textPadding: 5 - } - } - }, - series: { - defaults: { - label: { - fillStyle: "black", - strokeStyle: "none", - fontFamily: "default", - fontWeight: "default", - fontSize: "default*1.077", - textBaseline: "middle", - textAlign: "center" - }, - labelOverflowPadding: 5 - } - }, - sprites: { - text: { - fontSize: "default", - fontWeight: "default", - fontFamily: "default", - fillStyle: "black" - } - }, - seriesThemes: undefined, - markerThemes: { - type: ["circle", "cross", "plus", "square", "triangle", "diamond"] - }, - useGradients: false, - background: null - }, - colorDefaults: ["#94ae0a", "#115fa6", "#a61120", "#ff8809", "#ffd13e", "#a61187", "#24ad9a", "#7c7474", "#a66111"], - constructor: function(a) { - this.initConfig(a); - this.resolveDefaults() - }, - defaultRegEx: /^default([+\-/\*]\d+(?:\.\d+)?)?$/, - defaultOperators: { - "*": function(b, a) { - return b * a - }, - "+": function(b, a) { - return b + a - }, - "-": function(b, a) { - return b - a - } - }, - resolveDefaults: function() { - var a = this; - Ext.onReady(function() { - var f = Ext.clone(a.getSprites()), - e = Ext.clone(a.getAxis()), - d = Ext.clone(a.getSeries()), - g, c, b; - if (!a.superclass.defaults) { - g = Ext.getBody().createChild({ - tag: "div", - cls: "x-component" - }); - a.superclass.defaults = { - fontFamily: g.getStyle("fontFamily"), - fontWeight: g.getStyle("fontWeight"), - fontSize: parseFloat(g.getStyle("fontSize")), - fontVariant: g.getStyle("fontVariant"), - fontStyle: g.getStyle("fontStyle") - }; - g.destroy() - } - a.replaceDefaults(f.text); - a.setSprites(f); - for (c in e) { - b = e[c]; - a.replaceDefaults(b.label); - a.replaceDefaults(b.title) - } - a.setAxis(e); - for (c in d) { - b = d[c]; - a.replaceDefaults(b.label) - } - a.setSeries(d) - }) - }, - replaceDefaults: function(h) { - var e = this, - g = e.superclass.defaults, - a = e.defaultRegEx, - d, f, c, b; - if (Ext.isObject(h)) { - for (d in g) { - c = a.exec(h[d]); - if (c) { - f = g[d]; - c = c[1]; - if (c) { - b = e.defaultOperators[c.charAt(0)]; - f = Math.round(b(f, parseFloat(c.substr(1)))) - } - h[d] = f - } - } - } - }, - applyBaseColor: function(c) { - var a, b; - if (c) { - a = c.isColor ? c : Ext.draw.Color.fromString(c); - b = a.getHSL()[2]; - if (b < 0.15) { - a = a.createLighter(0.3) - } else { - if (b < 0.3) { - a = a.createLighter(0.15) - } else { - if (b > 0.85) { - a = a.createDarker(0.3) - } else { - if (b > 0.7) { - a = a.createDarker(0.15) - } - } - } - } - this.setColors([a.createDarker(0.3).toString(), a.createDarker(0.15).toString(), a.toString(), a.createLighter(0.12).toString(), a.createLighter(0.24).toString(), a.createLighter(0.31).toString()]) - } - return c - }, - applyColors: function(a) { - return a || this.colorDefaults - }, - updateUseGradients: function(a) { - if (a) { - this.updateGradients({ - type: "linear", - degrees: 90 - }) - } - }, - updateBackground: function(a) { - if (a) { - var b = this.getChart(); - b.defaults.background = a; - this.setChart(b) - } - }, - updateGradients: function(a) { - var c = this.getColors(), - e = [], - h, b, d, f, g; - if (Ext.isObject(a)) { - for (f = 0, g = c && c.length || 0; f < g; f++) { - b = Ext.draw.Color.fromString(c[f]); - if (b) { - d = b.createLighter(0.15).toString(); - h = Ext.apply(Ext.Object.chain(a), { - stops: [{ - offset: 1, - color: b.toString() - }, { - offset: 0, - color: d.toString() - }] - }); - e.push(h) - } - } - this.setColors(e) - } - }, - applySeriesThemes: function(a) { - this.getBaseColor(); - this.getUseGradients(); - this.getGradients(); - var b = this.getColors(); - if (!a) { - a = { - fillStyle: Ext.Array.clone(b), - strokeStyle: Ext.Array.map(b, function(d) { - var c = Ext.draw.Color.fromString(d.stops ? d.stops[0].color : d); - return c.createDarker(0.15).toString() - }) - } - } - return a - } -}); -Ext.define("Ext.chart.theme.Default", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.default", "chart.theme.Base"] -}); -Ext.define("Ext.chart.Markers", { - extend: "Ext.draw.sprite.Instancing", - isMarkers: true, - defaultCategory: "default", - constructor: function() { - this.callParent(arguments); - this.categories = {}; - this.revisions = {} - }, - destroy: function() { - this.categories = null; - this.revisions = null; - this.callParent() - }, - getMarkerFor: function(b, a) { - if (b in this.categories) { - var c = this.categories[b]; - if (a in c) { - return this.get(c[a]) - } - } - }, - clear: function(a) { - a = a || this.defaultCategory; - if (!(a in this.revisions)) { - this.revisions[a] = 1 - } else { - this.revisions[a]++ - } - }, - putMarkerFor: function(e, b, c, h, f) { - e = e || this.defaultCategory; - var d = this, - g = d.categories[e] || (d.categories[e] = {}), - a; - if (c in g) { - d.setAttributesFor(g[c], b, h) - } else { - g[c] = d.getCount(); - d.createInstance(b, h) - } - a = d.get(g[c]); - if (a) { - a.category = e; - if (!f) { - a.revision = d.revisions[e] || (d.revisions[e] = 1) - } - } - }, - getMarkerBBoxFor: function(c, a, b) { - if (c in this.categories) { - var d = this.categories[c]; - if (a in d) { - return this.getBBoxFor(d[a], b) - } - } - }, - getBBox: function() { - return null - }, - render: function(a, l, b) { - var f = this, - k = f.revisions, - j = f.attr.matrix, - h = f.getTemplate(), - d = h.attr, - g, c, e; - j.toContext(l); - h.preRender(a, l, b); - h.useAttributes(l, b); - for (c = 0, e = f.instances.length; c < e; c++) { - g = f.get(c); - if (g.hidden || g.revision !== k[g.category]) { - continue - } - l.save(); - h.attr = g; - h.useAttributes(l, b); - h.render(a, l, b); - l.restore() - } - h.attr = d - } -}); -Ext.define("Ext.chart.label.Callout", { - extend: "Ext.draw.modifier.Modifier", - prepareAttributes: function(a) { - if (!a.hasOwnProperty("calloutOriginal")) { - a.calloutOriginal = Ext.Object.chain(a); - a.calloutOriginal.prototype = a - } - if (this._previous) { - this._previous.prepareAttributes(a.calloutOriginal) - } - }, - setAttrs: function(e, h) { - var d = e.callout, - i = e.calloutOriginal, - l = e.bbox.plain, - c = (l.width || 0) + e.labelOverflowPadding, - m = (l.height || 0) + e.labelOverflowPadding, - p, o; - if ("callout" in h) { - d = h.callout - } - if ("callout" in h || "calloutPlaceX" in h || "calloutPlaceY" in h || "x" in h || "y" in h) { - var n = "rotationRads" in h ? i.rotationRads = h.rotationRads : i.rotationRads, - g = "x" in h ? (i.x = h.x) : i.x, - f = "y" in h ? (i.y = h.y) : i.y, - b = "calloutPlaceX" in h ? h.calloutPlaceX : e.calloutPlaceX, - a = "calloutPlaceY" in h ? h.calloutPlaceY : e.calloutPlaceY, - k = "calloutVertical" in h ? h.calloutVertical : e.calloutVertical, - j; - n %= Math.PI * 2; - if (Math.cos(n) < 0) { - n = (n + Math.PI) % (Math.PI * 2) - } - if (n > Math.PI) { - n -= Math.PI * 2 - } - if (k) { - n = n * (1 - d) - Math.PI / 2 * d; - j = c; - c = m; - m = j - } else { - n = n * (1 - d) - } - h.rotationRads = n; - h.x = g * (1 - d) + b * d; - h.y = f * (1 - d) + a * d; - p = b - g; - o = a - f; - if (Math.abs(o * c) > Math.abs(p * m)) { - if (o > 0) { - h.calloutEndX = h.x - (m / 2) * (p / o) * d; - h.calloutEndY = h.y - (m / 2) * d - } else { - h.calloutEndX = h.x + (m / 2) * (p / o) * d; - h.calloutEndY = h.y + (m / 2) * d - } - } else { - if (p > 0) { - h.calloutEndX = h.x - c / 2; - h.calloutEndY = h.y - (c / 2) * (o / p) * d - } else { - h.calloutEndX = h.x + c / 2; - h.calloutEndY = h.y + (c / 2) * (o / p) * d - } - } - if (h.calloutStartX && h.calloutStartY) { - h.calloutHasLine = (p > 0 && h.calloutStartX < h.calloutEndX) || (p <= 0 && h.calloutStartX > h.calloutEndX) || (o > 0 && h.calloutStartY < h.calloutEndY) || (o <= 0 && h.calloutStartY > h.calloutEndY) - } else { - h.calloutHasLine = true - } - } - return h - }, - pushDown: function(a, b) { - b = this.callParent([a.calloutOriginal, b]); - return this.setAttrs(a, b) - }, - popUp: function(a, b) { - a = a.prototype; - b = this.setAttrs(a, b); - if (this._next) { - return this._next.popUp(a, b) - } else { - return Ext.apply(a, b) - } - } -}); -Ext.define("Ext.chart.label.Label", { - extend: "Ext.draw.sprite.Text", - requires: ["Ext.chart.label.Callout"], - inheritableStatics: { - def: { - processors: { - callout: "limited01", - calloutHasLine: "bool", - calloutPlaceX: "number", - calloutPlaceY: "number", - calloutStartX: "number", - calloutStartY: "number", - calloutEndX: "number", - calloutEndY: "number", - calloutColor: "color", - calloutWidth: "number", - calloutVertical: "bool", - labelOverflowPadding: "number", - display: "enums(none,under,over,rotate,insideStart,insideEnd,inside,outside)", - orientation: "enums(horizontal,vertical)", - renderer: "default" - }, - defaults: { - callout: 0, - calloutHasLine: true, - calloutPlaceX: 0, - calloutPlaceY: 0, - calloutStartX: 0, - calloutStartY: 0, - calloutEndX: 0, - calloutEndY: 0, - calloutWidth: 1, - calloutVertical: false, - calloutColor: "black", - labelOverflowPadding: 5, - display: "none", - orientation: "", - renderer: null - }, - triggers: { - callout: "transform", - calloutPlaceX: "transform", - calloutPlaceY: "transform", - labelOverflowPadding: "transform", - calloutRotation: "transform", - display: "hidden" - }, - updaters: { - hidden: function(a) { - a.hidden = a.display === "none" - } - } - } - }, - config: { - fx: { - customDurations: { - callout: 200 - } - }, - field: null, - calloutLine: true - }, - applyCalloutLine: function(a) { - if (a) { - return Ext.apply({}, a) - } - }, - prepareModifiers: function() { - this.callParent(arguments); - this.calloutModifier = new Ext.chart.label.Callout({ - sprite: this - }); - this.fx.setNext(this.calloutModifier); - this.calloutModifier.setNext(this.topModifier) - }, - render: function(b, c) { - var e = this, - a = e.attr, - d = a.calloutColor; - c.save(); - c.globalAlpha *= a.callout; - if (c.globalAlpha > 0 && a.calloutHasLine) { - if (d && d.isGradient) { - d = d.getStops()[0].color - } - c.strokeStyle = d; - c.fillStyle = d; - c.lineWidth = a.calloutWidth; - c.beginPath(); - c.moveTo(e.attr.calloutStartX, e.attr.calloutStartY); - c.lineTo(e.attr.calloutEndX, e.attr.calloutEndY); - c.stroke(); - c.beginPath(); - c.arc(e.attr.calloutStartX, e.attr.calloutStartY, 1 * a.calloutWidth, 0, 2 * Math.PI, true); - c.fill(); - c.beginPath(); - c.arc(e.attr.calloutEndX, e.attr.calloutEndY, 1 * a.calloutWidth, 0, 2 * Math.PI, true); - c.fill() - } - c.restore(); - Ext.draw.sprite.Text.prototype.render.apply(e, arguments) - } -}); -Ext.define("Ext.chart.series.Series", { - requires: ["Ext.chart.Markers", "Ext.chart.label.Label", "Ext.tip.ToolTip"], - mixins: ["Ext.mixin.Observable", "Ext.mixin.Bindable"], - isSeries: true, - defaultBindProperty: "store", - type: null, - seriesType: "sprite", - identifiablePrefix: "ext-line-", - observableType: "series", - darkerStrokeRatio: 0.15, - config: { - chart: null, - title: null, - renderer: null, - showInLegend: true, - triggerAfterDraw: false, - style: {}, - subStyle: {}, - themeStyle: {}, - colors: null, - useDarkerStrokeColor: true, - store: null, - label: {}, - labelOverflowPadding: null, - showMarkers: true, - marker: null, - markerSubStyle: null, - itemInstancing: null, - background: null, - highlightItem: null, - surface: null, - overlaySurface: null, - hidden: false, - highlight: false, - highlightCfg: { - merge: function(a) { - return a - }, - $value: { - fillStyle: "yellow", - strokeStyle: "red" - } - }, - animation: null, - tooltip: null - }, - directions: [], - sprites: null, - themeColorCount: function() { - return 1 - }, - isStoreDependantColorCount: false, - themeMarkerCount: function() { - return 0 - }, - getFields: function(f) { - var e = this, - a = [], - c, b, d; - for (b = 0, d = f.length; b < d; b++) { - c = e["get" + f[b] + "Field"](); - if (Ext.isArray(c)) { - a.push.apply(a, c) - } else { - a.push(c) - } - } - return a - }, - applyAnimation: function(a, b) { - if (!a) { - a = { - duration: 0 - } - } else { - if (a === true) { - a = { - easing: "easeInOut", - duration: 500 - } - } - } - return b ? Ext.apply({}, a, b) : a - }, - getAnimation: function() { - var a = this.getChart(); - if (a && a.animationSuspendCount) { - return { - duration: 0 - } - } else { - return this.callParent() - } - }, - updateTitle: function(a) { - var j = this, - g = j.getChart(); - if (!g || g.isInitializing) { - return - } - a = Ext.Array.from(a); - var c = g.getSeries(), - b = Ext.Array.indexOf(c, j), - e = g.getLegendStore(), - h = j.getYField(), - d, l, k, f; - if (e.getCount() && b !== -1) { - f = h ? Math.min(a.length, h.length) : a.length; - for (d = 0; d < f; d++) { - k = a[d]; - l = e.getAt(b + d); - if (k && l) { - l.set("name", k) - } - } - } - }, - applyHighlight: function(a, b) { - if (Ext.isObject(a)) { - a = Ext.merge({}, this.config.highlightCfg, a) - } else { - if (a === true) { - a = this.config.highlightCfg - } - } - return Ext.apply(b || {}, a) - }, - updateHighlight: function(a) { - this.getStyle(); - if (!Ext.Object.isEmpty(a)) { - this.addItemHighlight() - } - }, - updateHighlightCfg: function(a) { - if (!Ext.Object.equals(a, this.defaultConfig.highlightCfg)) { - this.addItemHighlight() - } - }, - applyItemInstancing: function(a, b) { - return Ext.merge(b || {}, a) - }, - setAttributesForItem: function(c, d) { - var b = c && c.sprite, - a; - if (b) { - if (b.itemsMarker && c.category === "items") { - b.putMarker(c.category, d, c.index, false, true) - } - if (b.isMarkerHolder && c.category === "markers") { - b.putMarker(c.category, d, c.index, false, true) - } else { - if (b.isInstancing) { - b.setAttributesFor(c.index, d) - } else { - if (Ext.isArray(b)) { - for (a = 0; a < b.length; a++) { - b[a].setAttributes(d) - } - } else { - b.setAttributes(d) - } - } - } - } - }, - getBBoxForItem: function(a) { - if (a && a.sprite) { - if (a.sprite.itemsMarker && a.category === "items") { - return a.sprite.getMarkerBBox(a.category, a.index) - } else { - if (a.sprite instanceof Ext.draw.sprite.Instancing) { - return a.sprite.getBBoxFor(a.index) - } else { - return a.sprite.getBBox() - } - } - } - return null - }, - applyHighlightItem: function(d, a) { - if (d === a) { - return - } - if (Ext.isObject(d) && Ext.isObject(a)) { - var c = d.sprite === a.sprite, - b = d.index === a.index; - if (c && b) { - return - } - } - return d - }, - updateHighlightItem: function(b, a) { - this.setAttributesForItem(a, { - highlighted: false - }); - this.setAttributesForItem(b, { - highlighted: true - }) - }, - constructor: function(a) { - var b = this, - c; - a = a || {}; - if (a.tips) { - a = Ext.apply({ - tooltip: a.tips - }, a) - } - if (a.highlightCfg) { - a = Ext.apply({ - highlight: a.highlightCfg - }, a) - } - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.sprites = []; - b.dataRange = []; - b.mixins.observable.constructor.call(b, a); - b.initBindable() - }, - lookupViewModel: function(a) { - var b = this.getChart(); - return b ? b.lookupViewModel(a) : null - }, - applyTooltip: function(c, b) { - var a = Ext.apply({ - xtype: "tooltip", - renderer: Ext.emptyFn, - constrainPosition: true, - shrinkWrapDock: true, - autoHide: true, - offsetX: 10, - offsetY: 10 - }, c); - return Ext.create(a) - }, - updateTooltip: function() { - this.addItemHighlight() - }, - addItemHighlight: function() { - var d = this.getChart(); - if (!d) { - return - } - var e = d.getInteractions(), - c, a, b; - for (c = 0; c < e.length; c++) { - a = e[c]; - if (a.isItemHighlight || a.isItemEdit) { - b = true; - break - } - } - if (!b) { - e.push("itemhighlight"); - d.setInteractions(e) - } - }, - showTooltip: function(l, m) { - var d = this, - n = d.getTooltip(), - j, a, i, f, h, k, g, e, b, c; - if (!n) { - return - } - clearTimeout(d.tooltipTimeout); - b = n.config; - if (n.trackMouse) { - m[0] += b.offsetX; - m[1] += b.offsetY - } else { - j = l.sprite; - a = j.getSurface(); - i = Ext.get(a.getId()); - if (i) { - k = l.series.getBBoxForItem(l); - g = k.x + k.width / 2; - e = k.y + k.height / 2; - h = a.matrix.transformPoint([g, e]); - f = i.getXY(); - c = a.getInherited().rtl; - g = c ? f[0] + i.getWidth() - h[0] : f[0] + h[0]; - e = f[1] + h[1]; - m = [g, e] - } - } - Ext.callback(n.renderer, n.scope, [n, l.record, l], 0, d); - n.show(m) - }, - hideTooltip: function(b) { - var a = this, - c = a.getTooltip(); - if (!c) { - return - } - clearTimeout(a.tooltipTimeout); - a.tooltipTimeout = Ext.defer(function() { - c.hide() - }, 1) - }, - applyStore: function(a) { - return a && Ext.StoreManager.lookup(a) - }, - getStore: function() { - return this._store || this.getChart() && this.getChart().getStore() - }, - updateStore: function(b, a) { - var h = this, - g = h.getChart(), - c = g && g.getStore(), - f, j, e, d; - a = a || c; - if (a && a !== b) { - a.un({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: h - }) - } - if (b) { - b.on({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: h - }); - f = h.getSprites(); - for (d = 0, e = f.length; d < e; d++) { - j = f[d]; - if (j.setStore) { - j.setStore(b) - } - } - h.onDataChanged() - } - h.fireEvent("storechange", h, b, a) - }, - onStoreChange: function(b, a, c) { - if (!this._store) { - this.updateStore(a, c) - } - }, - coordinate: function(o, m, e) { - var l = this, - p = l.getStore(), - h = l.getHidden(), - k = p.getData().items, - b = l["get" + o + "Axis"](), - f = { - min: Infinity, - max: -Infinity - }, - q = l["fieldCategory" + o] || [o], - g = l.getFields(q), - d, n, c, a = {}, - j = l.getSprites(); - if (j.length > 0) { - if (!Ext.isBoolean(h) || !h) { - for (d = 0; d < q.length; d++) { - n = g[d]; - c = l.coordinateData(k, n, b); - l.getRangeOfData(c, f); - a["data" + q[d]] = c - } - } - l.dataRange[m] = f.min; - l.dataRange[m + e] = f.max; - a["dataMin" + o] = f.min; - a["dataMax" + o] = f.max; - if (b) { - b.range = null; - a["range" + o] = b.getRange() - } - for (d = 0; d < j.length; d++) { - j[d].setAttributes(a) - } - } - }, - coordinateData: function(b, h, d) { - var g = [], - f = b.length, - e = d && d.getLayout(), - c, a; - for (c = 0; c < f; c++) { - a = b[c].data[h]; - if (!Ext.isEmpty(a, true)) { - if (e) { - g[c] = e.getCoordFor(a, h, c, b) - } else { - g[c] = +a - } - } else { - g[c] = a - } - } - return g - }, - getRangeOfData: function(g, b) { - var e = g.length, - d = b.min, - a = b.max, - c, f; - for (c = 0; c < e; c++) { - f = g[c]; - if (f < d) { - d = f - } - if (f > a) { - a = f - } - } - b.min = d; - b.max = a - }, - updateLabelData: function() { - var h = this, - l = h.getStore(), - g = l.getData().items, - f = h.getSprites(), - a = h.getLabel().getTemplate(), - n = Ext.Array.from(a.getField()), - c, b, e, d, m, k; - if (!f.length || !n.length) { - return - } - for (c = 0; c < f.length; c++) { - d = []; - m = f[c]; - k = m.getField(); - if (Ext.Array.indexOf(n, k) < 0) { - k = n[c] - } - for (b = 0, e = g.length; b < e; b++) { - d.push(g[b].get(k)) - } - m.setAttributes({ - labels: d - }) - } - }, - processData: function() { - if (!this.getStore()) { - return - } - var d = this, - f = this.directions, - a, c = f.length, - e, b; - for (a = 0; a < c; a++) { - e = f[a]; - b = d["get" + e + "Axis"](); - if (b) { - b.processData(d); - continue - } - if (d["coordinate" + e]) { - d["coordinate" + e]() - } - } - d.updateLabelData() - }, - applyBackground: function(a) { - if (this.getChart()) { - this.getSurface().setBackground(a); - return this.getSurface().getBackground() - } else { - return a - } - }, - updateChart: function(d, a) { - var c = this, - b = c._store; - if (a) { - a.un("axeschange", "onAxesChange", c); - c.clearSprites(); - c.setSurface(null); - c.setOverlaySurface(null); - a.unregister(c); - c.onChartDetached(a); - if (!b) { - c.updateStore(null) - } - } - if (d) { - c.setSurface(d.getSurface("series")); - c.setOverlaySurface(d.getSurface("overlay")); - d.on("axeschange", "onAxesChange", c); - if (d.getAxes()) { - c.onAxesChange(d) - } - c.onChartAttached(d); - d.register(c); - if (!b) { - c.updateStore(d.getStore()) - } - } - }, - onAxesChange: function(h) { - var k = this, - g = h.getAxes(), - c, a = {}, - b = {}, - e = false, - j = this.directions, - l, d, f; - for (d = 0, f = j.length; d < f; d++) { - l = j[d]; - b[l] = k.getFields(k["fieldCategory" + l]) - } - for (d = 0, f = g.length; d < f; d++) { - c = g[d]; - if (!a[c.getDirection()]) { - a[c.getDirection()] = [c] - } else { - a[c.getDirection()].push(c) - } - } - for (d = 0, f = j.length; d < f; d++) { - l = j[d]; - if (k["get" + l + "Axis"]()) { - continue - } - if (a[l]) { - c = k.findMatchingAxis(a[l], b[l]); - if (c) { - k["set" + l + "Axis"](c); - if (c.getNeedHighPrecision()) { - e = true - } - } - } - } - this.getSurface().setHighPrecision(e) - }, - findMatchingAxis: function(f, e) { - var d, c, b, a; - for (b = 0; b < f.length; b++) { - d = f[b]; - c = d.getFields(); - if (!c.length) { - return d - } else { - if (e) { - for (a = 0; a < e.length; a++) { - if (Ext.Array.indexOf(c, e[a]) >= 0) { - return d - } - } - } - } - } - }, - onChartDetached: function(a) { - var b = this; - b.fireEvent("chartdetached", a, b); - a.un("storechange", "onStoreChange", b) - }, - onChartAttached: function(a) { - var b = this; - b.setBackground(b.getBackground()); - b.fireEvent("chartattached", a, b); - a.on("storechange", "onStoreChange", b); - b.processData() - }, - updateOverlaySurface: function(a) { - var b = this; - if (a) { - if (b.getLabel()) { - b.getOverlaySurface().add(b.getLabel()) - } - } - }, - applyLabel: function(a, b) { - if (!b) { - b = new Ext.chart.Markers({ - zIndex: 10 - }); - b.setTemplate(new Ext.chart.label.Label(a)) - } else { - b.getTemplate().setAttributes(a) - } - return b - }, - createItemInstancingSprite: function(c, b) { - var e = this, - f = new Ext.chart.Markers(), - a, d; - f.setAttributes({ - zIndex: Number.MAX_VALUE - }); - a = Ext.apply({}, b); - if (e.getHighlight()) { - a.highlight = e.getHighlight(); - a.modifiers = ["highlight"] - } - f.setTemplate(a); - d = f.getTemplate(); - d.setAttributes(e.getStyle()); - d.fx.on("animationstart", "onSpriteAnimationStart", this); - d.fx.on("animationend", "onSpriteAnimationEnd", this); - c.bindMarker("items", f); - e.getSurface().add(f); - return f - }, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer() - } - }, - updateRenderer: function(c) { - var b = this, - a = b.getChart(), - d; - if (a && a.isInitializing) { - return - } - d = b.getSprites(); - if (d.length) { - d[0].setAttributes({ - renderer: c || null - }); - if (a && !a.isInitializing) { - a.redraw() - } - } - }, - updateShowMarkers: function(a) { - var d = this.getSprites(), - b = d && d[0], - c = b && b.getMarker("markers"); - if (c) { - c.getTemplate().setAttributes({ - hidden: !a - }) - } - }, - createSprite: function() { - var f = this, - a = f.getSurface(), - e = f.getItemInstancing(), - d = a.add(f.getDefaultSpriteConfig()), - b = f.getMarker(), - g, c; - d.setAttributes(f.getStyle()); - d.setSeries(f); - if (e) { - d.itemsMarker = f.createItemInstancingSprite(d, e) - } - if (d.bindMarker) { - if (b) { - g = new Ext.chart.Markers(); - c = Ext.Object.merge({}, b); - if (f.getHighlight()) { - c.highlight = f.getHighlight(); - c.modifiers = ["highlight"] - } - g.setTemplate(c); - g.getTemplate().fx.setCustomDurations({ - translationX: 0, - translationY: 0 - }); - d.dataMarker = g; - d.bindMarker("markers", g); - f.getOverlaySurface().add(g) - } - if (f.getLabel().getTemplate().getField()) { - d.bindMarker("labels", f.getLabel()) - } - } - if (d.setStore) { - d.setStore(f.getStore()) - } - d.fx.on("animationstart", "onSpriteAnimationStart", f); - d.fx.on("animationend", "onSpriteAnimationEnd", f); - f.sprites.push(d); - return d - }, - getSprites: Ext.emptyFn, - onDataChanged: function() { - var d = this, - c = d.getChart(), - b = c && c.getStore(), - a = d.getStore(); - if (a !== b) { - d.processData() - } - }, - isXType: function(a) { - return a === "series" - }, - getItemId: function() { - return this.getId() - }, - applyThemeStyle: function(e, a) { - var b = this, - d, c; - d = e && e.subStyle && e.subStyle.fillStyle; - c = d && e.subStyle.strokeStyle; - if (d && !c) { - e.subStyle.strokeStyle = b.getStrokeColorsFromFillColors(d) - } - d = e && e.markerSubStyle && e.markerSubStyle.fillStyle; - c = d && e.markerSubStyle.strokeStyle; - if (d && !c) { - e.markerSubStyle.strokeStyle = b.getStrokeColorsFromFillColors(d) - } - return Ext.apply(a || {}, e) - }, - applyStyle: function(c, b) { - var a = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + this.seriesType)); - if (a && a.def) { - c = a.def.normalize(c) - } - return Ext.apply({}, c, b) - }, - applySubStyle: function(b, c) { - var a = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + this.seriesType)); - if (a && a.def) { - b = a.def.batchedNormalize(b, true) - } - return Ext.merge({}, c, b) - }, - applyMarker: function(c, a) { - var d = (c && c.type) || (a && a.type) || "circle", - b = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + d)); - if (b && b.def) { - c = b.def.normalize(Ext.isObject(c) ? c : {}, true); - c.type = d - } - return Ext.merge(a || {}, c) - }, - applyMarkerSubStyle: function(c, a) { - var d = (c && c.type) || (a && a.type) || "circle", - b = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + d)); - if (b && b.def) { - c = b.def.batchedNormalize(c, true) - } - return Ext.merge(a || {}, c) - }, - updateHidden: function(b) { - var a = this; - a.getColors(); - a.getSubStyle(); - a.setSubStyle({ - hidden: b - }); - a.processData(); - a.doUpdateStyles(); - if (!Ext.isArray(b)) { - a.updateLegendStore(b) - } - }, - updateLegendStore: function(f, b) { - var e = this, - d = e.getChart(), - c = d.getLegendStore(), - g = e.getId(), - a; - if (c) { - if (arguments.length > 1) { - a = c.findBy(function(h) { - return h.get("series") === g && h.get("index") === b - }); - if (a !== -1) { - a = c.getAt(a) - } - } else { - a = c.findRecord("series", g) - } - if (a && a.get("disabled") !== f) { - a.set("disabled", f) - } - } - }, - setHiddenByIndex: function(a, c) { - var b = this; - if (Ext.isArray(b.getHidden())) { - b.getHidden()[a] = c; - b.updateHidden(b.getHidden()); - b.updateLegendStore(c, a) - } else { - b.setHidden(c) - } - }, - getStrokeColorsFromFillColors: function(a) { - var c = this, - e = c.getUseDarkerStrokeColor(), - b = (Ext.isNumber(e) ? e : c.darkerStrokeRatio), - d; - if (e) { - d = Ext.Array.map(a, function(f) { - f = Ext.isString(f) ? f : f.stops[0].color; - f = Ext.draw.Color.fromString(f); - return f.createDarker(b).toString() - }) - } else { - d = Ext.Array.clone(a) - } - return d - }, - updateThemeColors: function(b) { - var c = this, - d = c.getThemeStyle(), - a = Ext.Array.clone(b), - f = c.getStrokeColorsFromFillColors(b), - e = { - fillStyle: a, - strokeStyle: f - }; - d.subStyle = Ext.apply(d.subStyle || {}, e); - d.markerSubStyle = Ext.apply(d.markerSubStyle || {}, e); - c.doUpdateStyles() - }, - themeOnlyIfConfigured: {}, - updateTheme: function(d) { - var h = this, - a = d.getSeries(), - n = h.getInitialConfig(), - c = h.defaultConfig, - f = h.getConfigurator().configs, - j = a.defaults, - k = a[h.type], - g = h.themeOnlyIfConfigured, - l, i, o, b, m, e; - a = Ext.merge({}, j, k); - for (l in a) { - i = a[l]; - e = f[l]; - if (i !== null && i !== undefined && e) { - m = n[l]; - o = Ext.isObject(i); - b = m === c[l]; - if (o) { - if (b && g[l]) { - continue - } - i = Ext.merge({}, i, m) - } - if (b || o) { - h[e.names.set](i) - } - } - } - }, - updateChartColors: function(a) { - var b = this; - if (!b.getColors()) { - b.updateThemeColors(a) - } - }, - updateColors: function(a) { - this.updateThemeColors(a) - }, - updateStyle: function() { - this.doUpdateStyles() - }, - updateSubStyle: function() { - this.doUpdateStyles() - }, - updateThemeStyle: function() { - this.doUpdateStyles() - }, - doUpdateStyles: function() { - var g = this, - h = g.sprites, - d = g.getItemInstancing(), - c = 0, - f = h && h.length, - a = g.getConfig("showMarkers", true), - b = g.getMarker(), - e; - for (; c < f; c++) { - e = g.getStyleByIndex(c); - if (d) { - h[c].itemsMarker.getTemplate().setAttributes(e) - } - h[c].setAttributes(e); - if (b && h[c].dataMarker) { - h[c].dataMarker.getTemplate().setAttributes(g.getMarkerStyleByIndex(c)) - } - } - }, - getStyleWithTheme: function() { - var b = this, - c = b.getThemeStyle(), - d = (c && c.style) || {}, - a = Ext.applyIf(Ext.apply({}, b.getStyle()), d); - return a - }, - getSubStyleWithTheme: function() { - var c = this, - d = c.getThemeStyle(), - a = (d && d.subStyle) || {}, - b = Ext.applyIf(Ext.apply({}, c.getSubStyle()), a); - return b - }, - getStyleByIndex: function(b) { - var e = this, - h = e.getThemeStyle(), - d, g, c, f, a = {}; - d = e.getStyle(); - g = (h && h.style) || {}; - c = e.styleDataForIndex(e.getSubStyle(), b); - f = e.styleDataForIndex((h && h.subStyle), b); - Ext.apply(a, g); - Ext.apply(a, f); - Ext.apply(a, d); - Ext.apply(a, c); - return a - }, - getMarkerStyleByIndex: function(d) { - var g = this, - c = g.getThemeStyle(), - a, e, k, j, b, l, h, f, m = {}; - a = g.getStyle(); - e = (c && c.style) || {}; - k = g.styleDataForIndex(g.getSubStyle(), d); - if (k.hasOwnProperty("hidden")) { - k.hidden = k.hidden || !this.getConfig("showMarkers", true) - } - j = g.styleDataForIndex((c && c.subStyle), d); - b = g.getMarker(); - l = (c && c.marker) || {}; - h = g.getMarkerSubStyle(); - f = g.styleDataForIndex((c && c.markerSubStyle), d); - Ext.apply(m, e); - Ext.apply(m, j); - Ext.apply(m, l); - Ext.apply(m, f); - Ext.apply(m, a); - Ext.apply(m, k); - Ext.apply(m, b); - Ext.apply(m, h); - return m - }, - styleDataForIndex: function(d, c) { - var e, b, a = {}; - if (d) { - for (b in d) { - e = d[b]; - if (Ext.isArray(e)) { - a[b] = e[c % e.length] - } else { - a[b] = e - } - } - } - return a - }, - getItemForPoint: Ext.emptyFn, - getItemByIndex: function(a, e) { - var d = this, - f = d.getSprites(), - b = f && f[0], - c; - if (!b) { - return - } - if (e === undefined && b.isMarkerHolder) { - e = d.getItemInstancing() ? "items" : "markers" - } else { - if (!e || e === "" || e === "sprites") { - b = f[a] - } - } - if (b) { - c = { - series: d, - category: e, - index: a, - record: d.getStore().getData().items[a], - field: d.getYField(), - sprite: b - }; - return c - } - }, - onSpriteAnimationStart: function(a) { - this.fireEvent("animationstart", this, a) - }, - onSpriteAnimationEnd: function(a) { - this.fireEvent("animationend", this, a) - }, - resolveListenerScope: function(e) { - var d = this, - a = Ext._namedScopes[e], - c = d.getChart(), - b; - if (!a) { - b = c ? c.resolveListenerScope(e, false) : (e || d) - } else { - if (a.isThis) { - b = d - } else { - if (a.isController) { - b = c ? c.resolveListenerScope(e, false) : d - } else { - if (a.isSelf) { - b = c ? c.resolveListenerScope(e, false) : d; - if (b === c && !c.getInheritedConfig("defaultListenerScope")) { - b = d - } - } - } - } - } - return b - }, - provideLegendInfo: function(a) { - a.push({ - name: this.getTitle() || this.getId(), - mark: "black", - disabled: this.getHidden(), - series: this.getId(), - index: 0 - }) - }, - clearSprites: function() { - var d = this.sprites, - b, a, c; - for (a = 0, c = d.length; a < c; a++) { - b = d[a]; - if (b && b.isSprite) { - b.destroy() - } - } - this.sprites = [] - }, - destroy: function() { - var b = this, - a = b._store, - c = b.getConfig("tooltip", true); - if (a && a.getAutoDestroy()) { - Ext.destroy(a) - } - b.setChart(null); - b.clearListeners(); - if (c) { - Ext.destroy(c); - clearTimeout(b.tooltipTimeout) - } - b.callParent() - } -}); -Ext.define("Ext.chart.interactions.Abstract", { - xtype: "interaction", - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - gestures: { - tap: "onGesture" - }, - chart: null, - enabled: true - }, - throttleGap: 0, - stopAnimationBeforeSync: false, - constructor: function(a) { - var b = this, - c; - a = a || {}; - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.mixins.observable.constructor.call(b, a) - }, - initialize: Ext.emptyFn, - updateChart: function(c, a) { - var b = this; - if (a === c) { - return - } - if (a) { - a.unregister(b); - b.removeChartListener(a) - } - if (c) { - c.register(b); - b.addChartListener() - } - }, - updateEnabled: function(a) { - var c = this, - b = c.getChart(); - if (b) { - if (a) { - c.addChartListener() - } else { - c.removeChartListener(b) - } - } - }, - onGesture: Ext.emptyFn, - getItemForEvent: function(d) { - var b = this, - a = b.getChart(), - c = a.getEventXY(d); - return a.getItemForPoint(c[0], c[1]) - }, - getItemsForEvent: function(d) { - var b = this, - a = b.getChart(), - c = a.getEventXY(d); - return a.getItemsForPoint(c[0], c[1]) - }, - addChartListener: function() { - var c = this, - b = c.getChart(), - e = c.getGestures(), - a; - if (!c.getEnabled()) { - return - } - - function d(f, g) { - b.addElementListener(f, c.listeners[f] = function(j) { - var i = c.getLocks(), - h; - if (c.getEnabled() && (!(f in i) || i[f] === c)) { - h = (Ext.isFunction(g) ? g : c[g]).apply(this, arguments); - if (h === false && j && j.stopPropagation) { - j.stopPropagation() - } - return h - } - }, c) - } - c.listeners = c.listeners || {}; - for (a in e) { - d(a, e[a]) - } - }, - removeChartListener: function(c) { - var d = this, - e = d.getGestures(), - b; - - function a(f) { - var g = d.listeners[f]; - if (g) { - c.removeElementListener(f, g); - delete d.listeners[f] - } - } - if (d.listeners) { - for (b in e) { - a(b) - } - } - }, - lockEvents: function() { - var d = this, - c = d.getLocks(), - a = Array.prototype.slice.call(arguments), - b = a.length; - while (b--) { - c[a[b]] = d - } - }, - unlockEvents: function() { - var c = this.getLocks(), - a = Array.prototype.slice.call(arguments), - b = a.length; - while (b--) { - delete c[a[b]] - } - }, - getLocks: function() { - var a = this.getChart(); - return a.lockedEvents || (a.lockedEvents = {}) - }, - isMultiTouch: function() { - if (Ext.browser.is.IE10) { - return true - } - return !Ext.os.is.Desktop - }, - initializeDefaults: Ext.emptyFn, - doSync: function() { - var b = this, - a = b.getChart(); - if (b.syncTimer) { - clearTimeout(b.syncTimer); - b.syncTimer = null - } - if (b.stopAnimationBeforeSync) { - a.animationSuspendCount++ - } - a.redraw(); - if (b.stopAnimationBeforeSync) { - a.animationSuspendCount-- - } - b.syncThrottle = Date.now() + b.throttleGap - }, - sync: function() { - var a = this; - if (a.throttleGap && Ext.frameStartTime < a.syncThrottle) { - if (a.syncTimer) { - return - } - a.syncTimer = Ext.defer(function() { - a.doSync() - }, a.throttleGap) - } else { - a.doSync() - } - }, - getItemId: function() { - return this.getId() - }, - isXType: function(a) { - return a === "interaction" - }, - destroy: function() { - var a = this; - a.setChart(null); - delete a.listeners; - a.callParent() - } -}, function() { - if (Ext.os.is.Android4) { - this.prototype.throttleGap = 40 - } -}); -Ext.define("Ext.chart.MarkerHolder", { - extend: "Ext.Mixin", - mixinConfig: { - id: "markerHolder", - after: { - constructor: "constructor", - preRender: "preRender" - }, - before: { - destroy: "destroy" - } - }, - isMarkerHolder: true, - surfaceMatrix: null, - inverseSurfaceMatrix: null, - deprecated: { - 6: { - methods: { - getBoundMarker: { - message: "Please use the 'getMarker' method instead.", - fn: function(b) { - var a = this.boundMarkers[b]; - return a ? [a] : a - } - } - } - } - }, - constructor: function() { - this.boundMarkers = {}; - this.cleanRedraw = false - }, - bindMarker: function(b, a) { - var c = this, - d = c.boundMarkers; - if (a && a.isMarkers) { - c.releaseMarker(b); - d[b] = a; - a.on("destroy", c.onMarkerDestroy, c) - } - }, - onMarkerDestroy: function(a) { - this.releaseMarker(a) - }, - releaseMarker: function(a) { - var c = this.boundMarkers, - b; - if (a && a.isMarkers) { - for (b in c) { - if (c[b] === a) { - delete c[b]; - break - } - } - } else { - b = a; - a = c[b]; - delete c[b] - } - return a || null - }, - getMarker: function(a) { - return this.boundMarkers[a] || null - }, - preRender: function() { - var f = this, - g = f.getId(), - d = f.boundMarkers, - e = f.getParent(), - c, a, b; - if (f.surfaceMatrix) { - b = f.surfaceMatrix.set(1, 0, 0, 1, 0, 0) - } else { - b = f.surfaceMatrix = new Ext.draw.Matrix() - } - f.cleanRedraw = !f.attr.dirty; - if (!f.cleanRedraw) { - for (c in d) { - a = d[c]; - if (a) { - a.clear(g) - } - } - } - while (e && e.attr && e.attr.matrix) { - b.prependMatrix(e.attr.matrix); - e = e.getParent() - } - b.prependMatrix(e.matrix); - f.surfaceMatrix = b; - f.inverseSurfaceMatrix = b.inverse(f.inverseSurfaceMatrix) - }, - putMarker: function(d, a, c, g, e) { - var b = this.boundMarkers[d], - f = this.getId(); - if (b) { - b.putMarkerFor(f, a, c, g, e) - } - }, - getMarkerBBox: function(c, b, d) { - var a = this.boundMarkers[c], - e = this.getId(); - if (a) { - return a.getMarkerBBoxFor(e, b, d) - } - }, - destroy: function() { - var c = this.boundMarkers, - b, a; - for (b in c) { - a = c[b]; - a.destroy() - } - } -}); -Ext.define("Ext.chart.axis.sprite.Axis", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.axis", - type: "axis", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - requires: ["Ext.draw.sprite.Text"], - inheritableStatics: { - def: { - processors: { - grid: "bool", - axisLine: "bool", - minorTicks: "bool", - minorTickSize: "number", - majorTicks: "bool", - majorTickSize: "number", - length: "number", - startGap: "number", - endGap: "number", - dataMin: "number", - dataMax: "number", - visibleMin: "number", - visibleMax: "number", - position: "enums(left,right,top,bottom,angular,radial,gauge)", - minStepSize: "number", - estStepSize: "number", - titleOffset: "number", - textPadding: "number", - min: "number", - max: "number", - centerX: "number", - centerY: "number", - radius: "number", - totalAngle: "number", - baseRotation: "number", - data: "default", - enlargeEstStepSizeByText: "bool" - }, - defaults: { - grid: false, - axisLine: true, - minorTicks: false, - minorTickSize: 3, - majorTicks: true, - majorTickSize: 5, - length: 0, - startGap: 0, - endGap: 0, - visibleMin: 0, - visibleMax: 1, - dataMin: 0, - dataMax: 1, - position: "", - minStepSize: 0, - estStepSize: 20, - min: 0, - max: 1, - centerX: 0, - centerY: 0, - radius: 1, - baseRotation: 0, - data: null, - titleOffset: 0, - textPadding: 0, - scalingCenterY: 0, - scalingCenterX: 0, - strokeStyle: "black", - enlargeEstStepSizeByText: false - }, - triggers: { - minorTickSize: "bbox", - majorTickSize: "bbox", - position: "bbox,layout", - axisLine: "bbox,layout", - min: "layout", - max: "layout", - length: "layout", - minStepSize: "layout", - estStepSize: "layout", - data: "layout", - dataMin: "layout", - dataMax: "layout", - visibleMin: "layout", - visibleMax: "layout", - enlargeEstStepSizeByText: "layout" - }, - updaters: { - layout: "layoutUpdater" - } - } - }, - config: { - label: null, - layout: null, - segmenter: null, - renderer: null, - layoutContext: null, - axis: null - }, - thickness: 0, - stepSize: 0, - getBBox: function() { - return null - }, - defaultRenderer: function(a) { - return this.segmenter.renderer(a, this) - }, - layoutUpdater: function() { - var h = this, - f = h.getAxis().getChart(); - if (f.isInitializing) { - return - } - var e = h.attr, - d = h.getLayout(), - g = f.getInherited().rtl, - b = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMin, - i = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMax, - c = e.position, - a = { - attr: e, - segmenter: h.getSegmenter(), - renderer: h.defaultRenderer - }; - if (c === "left" || c === "right") { - e.translationX = 0; - e.translationY = i * e.length / (i - b); - e.scalingX = 1; - e.scalingY = -e.length / (i - b); - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } else { - if (c === "top" || c === "bottom") { - if (g) { - e.translationX = e.length + b * e.length / (i - b) + 1 - } else { - e.translationX = -b * e.length / (i - b) - } - e.translationY = 0; - e.scalingX = (g ? -1 : 1) * e.length / (i - b); - e.scalingY = 1; - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } - } - if (d) { - d.calculateLayout(a); - h.setLayoutContext(a) - } - }, - iterate: function(e, j) { - var c, g, a, b, h, d, k = Ext.Array.some, - m = Math.abs, - f; - if (e.getLabel) { - if (e.min < e.from) { - j.call(this, e.min, e.getLabel(e.min), -1, e) - } - for (c = 0; c <= e.steps; c++) { - j.call(this, e.get(c), e.getLabel(c), c, e) - } - if (e.max > e.to) { - j.call(this, e.max, e.getLabel(e.max), e.steps + 1, e) - } - } else { - b = this.getAxis(); - h = b.floatingAxes; - d = []; - f = (e.to - e.from) / (e.steps + 1); - if (b.getFloating()) { - for (a in h) { - d.push(h[a]) - } - } - - function l(i) { - return !d.length || k(d, function(n) { - return m(n - i) > f - }) - } - if (e.min < e.from && l(e.min)) { - j.call(this, e.min, e.min, -1, e) - } - for (c = 0; c <= e.steps; c++) { - g = e.get(c); - if (l(g)) { - j.call(this, g, g, c, e) - } - } - if (e.max > e.to && l(e.max)) { - j.call(this, e.max, e.max, e.steps + 1, e) - } - } - }, - renderTicks: function(l, m, s, p) { - var v = this, - k = v.attr, - u = k.position, - n = k.matrix, - e = 0.5 * k.lineWidth, - f = n.getXX(), - i = n.getDX(), - j = n.getYY(), - h = n.getDY(), - o = s.majorTicks, - d = k.majorTickSize, - a = s.minorTicks, - r = k.minorTickSize; - if (o) { - switch (u) { - case "right": - function q(w) { - return function(x, z, y) { - x = l.roundPixel(x * j + h) + e; - m.moveTo(0, x); - m.lineTo(w, x) - } - } - v.iterate(o, q(d)); - a && v.iterate(a, q(r)); - break; - case "left": - function t(w) { - return function(x, z, y) { - x = l.roundPixel(x * j + h) + e; - m.moveTo(p[2] - w, x); - m.lineTo(p[2], x) - } - } - v.iterate(o, t(d)); - a && v.iterate(a, t(r)); - break; - case "bottom": - function c(w) { - return function(x, z, y) { - x = l.roundPixel(x * f + i) - e; - m.moveTo(x, 0); - m.lineTo(x, w) - } - } - v.iterate(o, c(d)); - a && v.iterate(a, c(r)); - break; - case "top": - function b(w) { - return function(x, z, y) { - x = l.roundPixel(x * f + i) - e; - m.moveTo(x, p[3]); - m.lineTo(x, p[3] - w) - } - } - v.iterate(o, b(d)); - a && v.iterate(a, b(r)); - break; - case "angular": - v.iterate(o, function(w, y, x) { - w = w / (k.max + 1) * Math.PI * 2 + k.baseRotation; - m.moveTo(k.centerX + (k.length) * Math.cos(w), k.centerY + (k.length) * Math.sin(w)); - m.lineTo(k.centerX + (k.length + d) * Math.cos(w), k.centerY + (k.length + d) * Math.sin(w)) - }); - break; - case "gauge": - var g = v.getGaugeAngles(); - v.iterate(o, function(w, y, x) { - w = (w - k.min) / (k.max - k.min + 1) * k.totalAngle - k.totalAngle + g.start; - m.moveTo(k.centerX + (k.length) * Math.cos(w), k.centerY + (k.length) * Math.sin(w)); - m.lineTo(k.centerX + (k.length + d) * Math.cos(w), k.centerY + (k.length + d) * Math.sin(w)) - }); - break - } - } - }, - renderLabels: function(E, q, D, K) { - var o = this, - k = o.attr, - i = 0.5 * k.lineWidth, - u = k.position, - y = k.matrix, - A = k.textPadding, - x = y.getXX(), - d = y.getDX(), - g = y.getYY(), - c = y.getDY(), - n = 0, - I = D.majorTicks, - G = Math.max(k.majorTickSize, k.minorTickSize) + k.lineWidth, - f = Ext.draw.Draw.isBBoxIntersect, - F = o.getLabel(), - J, s, r = null, - w = 0, - b = 0, - m = D.segmenter, - B = o.getRenderer(), - t = o.getAxis(), - z = t.getTitle(), - a = z && z.attr.text !== "" && z.getBBox(), - l, h = null, - p, C, v, e, H; - if (I && F && !F.attr.hidden) { - J = F.attr.font; - if (q.font !== J) { - q.font = J - } - F.setAttributes({ - translationX: 0, - translationY: 0 - }, true); - F.applyTransformations(); - l = F.attr.inverseMatrix.elements.slice(0); - switch (u) { - case "left": - e = a ? a.x + a.width : 0; - switch (F.attr.textAlign) { - case "start": - H = E.roundPixel(e + d) - i; - break; - case "end": - H = E.roundPixel(K[2] - G + d) - i; - break; - default: - H = E.roundPixel(e + (K[2] - e - G) / 2 + d) - i - } - F.setAttributes({ - translationX: H - }, true); - break; - case "right": - e = a ? K[2] - a.x : 0; - switch (F.attr.textAlign) { - case "start": - H = E.roundPixel(G + d) + i; - break; - case "end": - H = E.roundPixel(K[2] - e + d) + i; - break; - default: - H = E.roundPixel(G + (K[2] - G - e) / 2 + d) + i - } - F.setAttributes({ - translationX: H - }, true); - break; - case "top": - e = a ? a.y + a.height : 0; - F.setAttributes({ - translationY: E.roundPixel(e + (K[3] - e - G) / 2) - i - }, true); - break; - case "bottom": - e = a ? K[3] - a.y : 0; - F.setAttributes({ - translationY: E.roundPixel(G + (K[3] - G - e) / 2) + i - }, true); - break; - case "radial": - F.setAttributes({ - translationX: k.centerX - }, true); - break; - case "angular": - F.setAttributes({ - translationY: k.centerY - }, true); - break; - case "gauge": - F.setAttributes({ - translationY: k.centerY - }, true); - break - } - if (u === "left" || u === "right") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - F.setAttributes({ - text: String(v), - translationY: E.roundPixel(L * g + c) - }, true); - F.applyTransformations(); - n = Math.max(n, F.getBBox().width + G); - if (n <= o.thickness) { - C = Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0)); - p = C.prepend.apply(C, l).transformBBox(F.getBBox(true)); - if (h && !f(p, h, A)) { - return - } - E.renderSprite(F); - h = p; - w += p.height; - b++ - } - }) - } else { - if (u === "top" || u === "bottom") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - F.setAttributes({ - text: String(v), - translationX: E.roundPixel(L * x + d) - }, true); - F.applyTransformations(); - n = Math.max(n, F.getBBox().height + G); - if (n <= o.thickness) { - C = Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0)); - p = C.prepend.apply(C, l).transformBBox(F.getBBox(true)); - if (h && !f(p, h, A)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "radial") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - if (typeof v !== "undefined") { - F.setAttributes({ - text: String(v), - translationX: k.centerX - E.roundPixel(L) / k.max * k.length * Math.cos(k.baseRotation + Math.PI / 2), - translationY: k.centerY - E.roundPixel(L) / k.max * k.length * Math.sin(k.baseRotation + Math.PI / 2) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "angular") { - s = k.majorTickSize + k.lineWidth * 0.5 + (parseInt(F.attr.fontSize, 10) || 10) / 2; - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - n = Math.max(n, Math.max(k.majorTickSize, k.minorTickSize) + (k.lineCap !== "butt" ? k.lineWidth * 0.5 : 0)); - if (typeof v !== "undefined") { - var O = L / (k.max + 1) * Math.PI * 2 + k.baseRotation; - F.setAttributes({ - text: String(v), - translationX: k.centerX + (k.length + s) * Math.cos(O), - translationY: k.centerY + (k.length + s) * Math.sin(O) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "gauge") { - var j = o.getGaugeAngles(); - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - if (typeof v !== "undefined") { - var O = (L - k.min) / (k.max - k.min + 1) * k.totalAngle - k.totalAngle + j.start; - F.setAttributes({ - text: String(v), - translationX: k.centerX + (k.length + 10) * Math.cos(O), - translationY: k.centerY + (k.length + 10) * Math.sin(O) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } - } - } - } - } - if (k.enlargeEstStepSizeByText && b) { - w /= b; - w += G; - w *= 2; - if (k.estStepSize < w) { - k.estStepSize = w - } - } - if (Math.abs(o.thickness - (n)) > 1) { - o.thickness = n; - k.bbox.plain.dirty = true; - k.bbox.transform.dirty = true; - o.doThicknessChanged(); - return false - } - } - }, - renderAxisLine: function(a, i, e, c) { - var h = this, - g = h.attr, - b = g.lineWidth * 0.5, - j = g.position, - d, f; - if (g.axisLine && g.length) { - switch (j) { - case "left": - d = a.roundPixel(c[2]) - b; - i.moveTo(d, -g.endGap); - i.lineTo(d, g.length + g.startGap + 1); - break; - case "right": - i.moveTo(b, -g.endGap); - i.lineTo(b, g.length + g.startGap + 1); - break; - case "bottom": - i.moveTo(-g.startGap, b); - i.lineTo(g.length + g.endGap, b); - break; - case "top": - d = a.roundPixel(c[3]) - b; - i.moveTo(-g.startGap, d); - i.lineTo(g.length + g.endGap, d); - break; - case "angular": - i.moveTo(g.centerX + g.length, g.centerY); - i.arc(g.centerX, g.centerY, g.length, 0, Math.PI * 2, true); - break; - case "gauge": - f = h.getGaugeAngles(); - i.moveTo(g.centerX + Math.cos(f.start) * g.length, g.centerY + Math.sin(f.start) * g.length); - i.arc(g.centerX, g.centerY, g.length, f.start, f.end, true); - break - } - } - }, - getGaugeAngles: function() { - var a = this, - c = a.attr.totalAngle, - b; - if (c <= Math.PI) { - b = (Math.PI - c) * 0.5 - } else { - b = -(Math.PI * 2 - c) * 0.5 - } - b = Math.PI * 2 - b; - return { - start: b, - end: b - c - } - }, - renderGridLines: function(m, n, s, r) { - var t = this, - b = t.getAxis(), - l = t.attr, - p = l.matrix, - d = l.startGap, - a = l.endGap, - c = p.getXX(), - k = p.getYY(), - h = p.getDX(), - g = p.getDY(), - u = l.position, - f = b.getGridAlignment(), - q = s.majorTicks, - e, o, i; - if (l.grid) { - if (q) { - if (u === "left" || u === "right") { - i = l.min * k + g + a + d; - t.iterate(q, function(j, w, v) { - e = j * k + g + a; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - y: e, - height: i - e - }, o = v, true); - i = e - }); - o++; - e = 0; - t.putMarker(f + "-" + (o % 2 ? "odd" : "even"), { - y: e, - height: i - e - }, o, true) - } else { - if (u === "top" || u === "bottom") { - i = l.min * c + h + d; - if (d) { - t.putMarker(f + "-even", { - x: 0, - width: i - }, -1, true) - } - t.iterate(q, function(j, w, v) { - e = j * c + h + d; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - x: e, - width: i - e - }, o = v, true); - i = e - }); - o++; - e = l.length + l.startGap + l.endGap; - t.putMarker(f + "-" + (o % 2 ? "odd" : "even"), { - x: e, - width: i - e - }, o, true) - } else { - if (u === "radial") { - t.iterate(q, function(j, w, v) { - if (!j) { - return - } - e = j / l.max * l.length; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - scalingX: e, - scalingY: e - }, v, true); - i = e - }) - } else { - if (u === "angular") { - t.iterate(q, function(j, w, v) { - if (!l.length) { - return - } - e = j / (l.max + 1) * Math.PI * 2 + l.baseRotation; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - rotationRads: e, - rotationCenterX: 0, - rotationCenterY: 0, - scalingX: l.length, - scalingY: l.length - }, v, true); - i = e - }) - } - } - } - } - } - } - }, - renderLimits: function(o) { - var t = this, - a = t.getAxis(), - h = a.getChart(), - p = h.getInnerPadding(), - d = Ext.Array.from(a.getLimits()); - if (!d.length) { - return - } - var r = a.limits.surface.getRect(), - m = t.attr, - n = m.matrix, - u = m.position, - k = Ext.Object.chain, - v = a.limits.titles, - c, j, b, s, l, q, f, g, e; - v.instances = []; - v.position = 0; - if (u === "left" || u === "right") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l * n.getYY() + n.getDY(); - s.line.y = l + p.top; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("horizontal-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - j = s.line.title.position || (u === "left" ? "start" : "end"); - switch (j) { - case "start": - g = 10; - break; - case "end": - g = r[2] - 10; - break; - case "middle": - g = r[2] / 2; - break - } - v.setAttributesFor(v.position - 1, { - x: g, - y: s.line.y - c.height / 2, - textAlign: j, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "top" || u === "bottom") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l * n.getXX() + n.getDX(); - s.line.x = l + p.left; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("vertical-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - j = s.line.title.position || (u === "top" ? "end" : "start"); - switch (j) { - case "start": - e = r[3] - c.width / 2 - 10; - break; - case "end": - e = c.width / 2 + 10; - break; - case "middle": - e = r[3] / 2; - break - } - v.setAttributesFor(v.position - 1, { - x: s.line.x + c.height / 2, - y: e, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle, - rotationRads: Math.PI / 2 - }) - } - } - } else { - if (u === "radial") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - if (l > m.max) { - continue - } - l = l / m.max * m.length; - s.line.cx = m.centerX; - s.line.cy = m.centerY; - s.line.scalingX = l; - s.line.scalingY = l; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("circular-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - v.setAttributesFor(v.position - 1, { - x: m.centerX, - y: m.centerY - l - c.height / 2, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "angular") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l / (m.max + 1) * Math.PI * 2 + m.baseRotation; - s.line.translationX = m.centerX; - s.line.translationY = m.centerY; - s.line.rotationRads = l; - s.line.rotationCenterX = 0; - s.line.rotationCenterY = 0; - s.line.scalingX = m.length; - s.line.scalingY = m.length; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("radial-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - b = ((l > -0.5 * Math.PI && l < 0.5 * Math.PI) || (l > 1.5 * Math.PI && l < 2 * Math.PI)) ? 1 : -1; - v.setAttributesFor(v.position - 1, { - x: m.centerX + 0.5 * m.length * Math.cos(l) + b * c.height / 2 * Math.sin(l), - y: m.centerY + 0.5 * m.length * Math.sin(l) - b * c.height / 2 * Math.cos(l), - rotationRads: b === 1 ? l : l - Math.PI, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "gauge") {} - } - } - } - } - }, - doThicknessChanged: function() { - var a = this.getAxis(); - if (a) { - a.onThicknessChanged() - } - }, - render: function(a, c, d) { - var e = this, - b = e.getLayoutContext(); - if (b) { - if (false === e.renderLabels(a, c, b, d)) { - return false - } - c.beginPath(); - e.renderTicks(a, c, b, d); - e.renderAxisLine(a, c, b, d); - e.renderGridLines(a, c, b, d); - e.renderLimits(d); - c.stroke() - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Segmenter", { - config: { - axis: null - }, - constructor: function(a) { - this.initConfig(a) - }, - renderer: function(b, a) { - return String(b) - }, - from: function(a) { - return a - }, - diff: Ext.emptyFn, - align: Ext.emptyFn, - add: Ext.emptyFn, - preferredStep: Ext.emptyFn -}); -Ext.define("Ext.chart.axis.segmenter.Names", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.names", - renderer: function(b, a) { - return b - }, - diff: function(b, a, c) { - return Math.floor(a - b) - }, - align: function(c, b, a) { - return Math.floor(c) - }, - add: function(c, b, a) { - return c + b - }, - preferredStep: function(c, a, b, d) { - return { - unit: 1, - step: 1 - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Numeric", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.numeric", - isNumeric: true, - renderer: function(b, a) { - return b.toFixed(Math.max(0, a.majorTicks.unit.fixes)) - }, - diff: function(b, a, c) { - return Math.floor((a - b) / c.scale) - }, - align: function(c, b, a) { - return Math.floor(c / (a.scale * b)) * a.scale * b - }, - add: function(c, b, a) { - return c + b * a.scale - }, - preferredStep: function(c, b) { - var a = Math.floor(Math.log(b) * Math.LOG10E), - d = Math.pow(10, a); - b /= d; - if (b < 2) { - b = 2 - } else { - if (b < 5) { - b = 5 - } else { - if (b < 10) { - b = 10; - a++ - } - } - } - return { - unit: { - fixes: -a, - scale: d - }, - step: b - } - }, - exactStep: function(c, b) { - var a = Math.floor(Math.log(b) * Math.LOG10E), - d = Math.pow(10, a); - return { - unit: { - fixes: -a + (b % d === 0 ? 0 : 1), - scale: 1 - }, - step: b - } - }, - adjustByMajorUnit: function(e, g, c) { - var d = c[0], - b = c[1], - a = e * g, - f = d % a; - if (f !== 0) { - c[0] = d - f + (d < 0 ? -a : 0) - } - f = b % a; - if (f !== 0) { - c[1] = b - f + (b > 0 ? a : 0) - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Time", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.time", - config: { - step: null - }, - renderer: function(c, b) { - var a = Ext.Date; - switch (b.majorTicks.unit) { - case "y": - return a.format(c, "Y"); - case "mo": - return a.format(c, "Y-m"); - case "d": - return a.format(c, "Y-m-d") - } - return a.format(c, "Y-m-d\nH:i:s") - }, - from: function(a) { - return new Date(a) - }, - diff: function(b, a, c) { - if (isFinite(b)) { - b = new Date(b) - } - if (isFinite(a)) { - a = new Date(a) - } - return Ext.Date.diff(b, a, c) - }, - align: function(a, c, b) { - if (b === "d" && c >= 7) { - a = Ext.Date.align(a, "d", c); - a.setDate(a.getDate() - a.getDay() + 1); - return a - } else { - return Ext.Date.align(a, b, c) - } - }, - add: function(c, b, a) { - return Ext.Date.add(new Date(c), a, b) - }, - stepUnits: [ - [Ext.Date.YEAR, 1, 2, 5, 10, 20, 50, 100, 200, 500], - [Ext.Date.MONTH, 1, 3, 6], - [Ext.Date.DAY, 1, 7, 14], - [Ext.Date.HOUR, 1, 6, 12], - [Ext.Date.MINUTE, 1, 5, 15, 30], - [Ext.Date.SECOND, 1, 5, 15, 30], - [Ext.Date.MILLI, 1, 2, 5, 10, 20, 50, 100, 200, 500] - ], - preferredStep: function(b, e) { - if (this.getStep()) { - return this.getStep() - } - var f = new Date(+b), - g = new Date(+b + Math.ceil(e)), - d = this.stepUnits, - l, k, h, c, a; - for (c = 0; c < d.length; c++) { - k = d[c][0]; - h = this.diff(f, g, k); - if (h > 0) { - for (a = 1; a < d[c].length; a++) { - if (h <= d[c][a]) { - l = { - unit: k, - step: d[c][a] - }; - break - } - } - if (!l) { - c--; - l = { - unit: d[c][0], - step: 1 - } - } - break - } - } - if (!l) { - l = { - unit: Ext.Date.DAY, - step: 1 - } - } - return l - } -}); -Ext.define("Ext.chart.axis.layout.Layout", { - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - axis: null - }, - constructor: function(a) { - this.mixins.observable.constructor.call(this, a) - }, - processData: function(b) { - var e = this, - c = e.getAxis(), - f = c.getDirection(), - g = c.boundSeries, - a, d; - if (b) { - b["coordinate" + f]() - } else { - for (a = 0, d = g.length; a < d; a++) { - g[a]["coordinate" + f]() - } - } - }, - calculateMajorTicks: function(a) { - var f = this, - e = a.attr, - d = e.max - e.min, - i = d / Math.max(1, e.length) * (e.visibleMax - e.visibleMin), - h = e.min + d * e.visibleMin, - b = e.min + d * e.visibleMax, - g = e.estStepSize * i, - c = f.snapEnds(a, e.min, e.max, g); - if (c) { - f.trimByRange(a, c, h, b); - a.majorTicks = c - } - }, - calculateMinorTicks: function(a) { - if (this.snapMinorEnds) { - a.minorTicks = this.snapMinorEnds(a) - } - }, - calculateLayout: function(b) { - var c = this, - a = b.attr; - if (a.length === 0) { - return null - } - if (a.majorTicks) { - c.calculateMajorTicks(b); - if (a.minorTicks) { - c.calculateMinorTicks(b) - } - } - }, - snapEnds: Ext.emptyFn, - trimByRange: function(b, f, i, a) { - var g = b.segmenter, - j = f.unit, - h = g.diff(f.from, i, j), - d = g.diff(f.from, a, j), - c = Math.max(0, Math.ceil(h / f.step)), - e = Math.min(f.steps, Math.floor(d / f.step)); - if (e < f.steps) { - f.to = g.add(f.from, e * f.step, j) - } - if (f.max > a) { - f.max = f.to - } - if (f.from < i) { - f.from = g.add(f.from, c * f.step, j); - while (f.from < i) { - c++; - f.from = g.add(f.from, f.step, j) - } - } - if (f.min < i) { - f.min = f.from - } - f.steps = e - c - } -}); -Ext.define("Ext.chart.axis.layout.Discrete", { - extend: "Ext.chart.axis.layout.Layout", - alias: "axisLayout.discrete", - isDiscrete: true, - processData: function() { - var f = this, - d = f.getAxis(), - c = d.boundSeries, - g = d.getDirection(), - b, e, a; - f.labels = []; - f.labelMap = {}; - for (b = 0, e = c.length; b < e; b++) { - a = c[b]; - if (a["get" + g + "Axis"]() === d) { - a["coordinate" + g]() - } - } - d.getSprites()[0].setAttributes({ - data: f.labels - }); - f.fireEvent("datachange", f.labels) - }, - calculateLayout: function(a) { - a.data = this.labels; - this.callParent([a]) - }, - calculateMajorTicks: function(a) { - var g = this, - f = a.attr, - d = a.data, - e = f.max - f.min, - j = e / Math.max(1, f.length) * (f.visibleMax - f.visibleMin), - i = f.min + e * f.visibleMin, - b = f.min + e * f.visibleMax, - h = f.estStepSize * j; - var c = g.snapEnds(a, Math.max(0, f.min), Math.min(f.max, d.length - 1), h); - if (c) { - g.trimByRange(a, c, i, b); - a.majorTicks = c - } - }, - snapEnds: function(e, d, a, b) { - b = Math.ceil(b); - var c = Math.floor((a - d) / b), - f = e.data; - return { - min: d, - max: a, - from: d, - to: c * b + d, - step: b, - steps: c, - unit: 1, - getLabel: function(g) { - return f[this.from + this.step * g] - }, - get: function(g) { - return this.from + this.step * g - } - } - }, - trimByRange: function(b, f, h, a) { - var i = f.unit, - g = Math.ceil((h - f.from) / i) * i, - d = Math.floor((a - f.from) / i) * i, - c = Math.max(0, Math.ceil(g / f.step)), - e = Math.min(f.steps, Math.floor(d / f.step)); - if (e < f.steps) { - f.to = e - } - if (f.max > a) { - f.max = f.to - } - if (f.from < h && f.step > 0) { - f.from = f.from + c * f.step * i; - while (f.from < h) { - c++; - f.from += f.step * i - } - } - if (f.min < h) { - f.min = f.from - } - f.steps = e - c - }, - getCoordFor: function(c, d, a, b) { - this.labels.push(c); - return this.labels.length - 1 - } -}); -Ext.define("Ext.chart.axis.layout.CombineDuplicate", { - extend: "Ext.chart.axis.layout.Discrete", - alias: "axisLayout.combineDuplicate", - getCoordFor: function(d, e, b, c) { - if (!(d in this.labelMap)) { - var a = this.labelMap[d] = this.labels.length; - this.labels.push(d); - return a - } - return this.labelMap[d] - } -}); -Ext.define("Ext.chart.axis.layout.Continuous", { - extend: "Ext.chart.axis.layout.Layout", - alias: "axisLayout.continuous", - isContinuous: true, - config: { - adjustMinimumByMajorUnit: false, - adjustMaximumByMajorUnit: false - }, - getCoordFor: function(c, d, a, b) { - return +c - }, - snapEnds: function(a, d, i, h) { - var f = a.segmenter, - c = this.getAxis(), - l = c.getMajorTickSteps(), - e = l && f.exactStep ? f.exactStep(d, (i - d) / l) : f.preferredStep(d, h), - k = e.unit, - b = e.step, - j = f.align(d, b, k), - g = (l || f.diff(d, i, k)) + 1; - return { - min: f.from(d), - max: f.from(i), - from: j, - to: f.add(j, g * b, k), - step: b, - steps: g, - unit: k, - get: function(m) { - return f.add(this.from, this.step * m, k) - } - } - }, - snapMinorEnds: function(a) { - var e = a.majorTicks, - m = this.getAxis().getMinorTickSteps(), - f = a.segmenter, - d = e.min, - i = e.max, - k = e.from, - l = e.unit, - b = e.step / m, - n = b * l.scale, - j = k - d, - c = Math.floor(j / n), - h = c + Math.floor((i - e.to) / n) + 1, - g = e.steps * m + h; - return { - min: d, - max: i, - from: d + j % n, - to: f.add(k, g * b, l), - step: b, - steps: g, - unit: l, - get: function(o) { - return (o % m + c + 1 !== 0) ? f.add(this.from, this.step * o, l) : null - } - } - } -}); -Ext.define("Ext.chart.axis.Axis", { - xtype: "axis", - mixins: { - observable: "Ext.mixin.Observable" - }, - requires: ["Ext.chart.axis.sprite.Axis", "Ext.chart.axis.segmenter.*", "Ext.chart.axis.layout.*"], - isAxis: true, - config: { - position: "bottom", - fields: [], - label: undefined, - grid: false, - limits: null, - renderer: null, - chart: null, - style: null, - margin: 0, - titleMargin: 4, - background: null, - minimum: NaN, - maximum: NaN, - reconcileRange: false, - minZoom: 1, - maxZoom: 10000, - layout: "continuous", - segmenter: "numeric", - hidden: false, - majorTickSteps: 0, - minorTickSteps: 0, - adjustByMajorUnit: true, - title: null, - increment: 0.5, - length: 0, - center: null, - radius: null, - totalAngle: Math.PI, - rotation: null, - labelInSpan: null, - visibleRange: [0, 1], - needHighPrecision: false, - linkedTo: null, - floating: null - }, - titleOffset: 0, - spriteAnimationCount: 0, - prevMin: 0, - prevMax: 1, - boundSeries: [], - sprites: null, - surface: null, - range: null, - xValues: [], - yValues: [], - masterAxis: null, - applyRotation: function(b) { - var a = Math.PI * 2; - return (b % a + Math.PI) % a - Math.PI - }, - updateRotation: function(b) { - var c = this.getSprites(), - a = this.getPosition(); - if (!this.getHidden() && a === "angular" && c[0]) { - c[0].setAttributes({ - baseRotation: b - }) - } - }, - applyTitle: function(c, b) { - var a; - if (Ext.isString(c)) { - c = { - text: c - } - } - if (!b) { - b = Ext.create("sprite.text", c); - if ((a = this.getSurface())) { - a.add(b) - } - } else { - b.setAttributes(c) - } - return b - }, - applyFloating: function(b, a) { - if (b === null) { - b = { - value: null, - alongAxis: null - } - } else { - if (Ext.isNumber(b)) { - b = { - value: b, - alongAxis: null - } - } - } - if (Ext.isObject(b)) { - if (a && a.alongAxis) { - delete this.getChart().getAxis(a.alongAxis).floatingAxes[this.getId()] - } - return b - } - return a - }, - constructor: function(a) { - var b = this, - c; - b.sprites = []; - b.labels = []; - b.floatingAxes = {}; - a = a || {}; - if (a.position === "angular") { - a.style = a.style || {}; - a.style.estStepSize = 1 - } - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.mixins.observable.constructor.apply(b, arguments) - }, - getAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "vertical"; - case "top": - case "bottom": - return "horizontal"; - case "radial": - return "radial"; - case "angular": - return "angular" - } - }, - getGridAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "horizontal"; - case "top": - case "bottom": - return "vertical"; - case "radial": - return "circular"; - case "angular": - return "radial" - } - }, - getSurface: function() { - var e = this, - d = e.getChart(); - if (d && !e.surface) { - var b = e.surface = d.getSurface(e.getId(), "axis"), - c = e.gridSurface = d.getSurface("main"), - a = e.getSprites()[0], - f = e.getGridAlignment(); - c.waitFor(b); - e.getGrid(); - if (e.getLimits() && f) { - f = f.replace("3d", ""); - e.limits = { - surface: d.getSurface("overlay"), - lines: new Ext.chart.Markers(), - titles: new Ext.draw.sprite.Instancing() - }; - e.limits.lines.setTemplate({ - xclass: "grid." + f - }); - e.limits.lines.getTemplate().setAttributes({ - strokeStyle: "black" - }, true); - e.limits.surface.add(e.limits.lines); - a.bindMarker(f + "-limit-lines", e.limits.lines); - e.limitTitleTpl = new Ext.draw.sprite.Text(); - e.limits.titles.setTemplate(e.limitTitleTpl); - e.limits.surface.add(e.limits.titles); - d.on("redraw", e.renderLimits, e) - } - } - return e.surface - }, - applyGrid: function(a) { - if (a === true) { - return {} - } - return a - }, - updateGrid: function(b) { - var e = this, - d = e.getChart(); - if (!d) { - e.on({ - chartattached: Ext.bind(e.updateGrid, e, [b]), - single: true - }); - return - } - var c = e.gridSurface, - a = e.getSprites()[0], - f = e.getGridAlignment(), - g; - if (b) { - g = e.gridSpriteEven; - if (!g) { - g = e.gridSpriteEven = new Ext.chart.Markers(); - g.setTemplate({ - xclass: "grid." + f - }); - c.add(g); - a.bindMarker(f + "-even", g) - } - if (Ext.isObject(b)) { - g.getTemplate().setAttributes(b); - if (Ext.isObject(b.even)) { - g.getTemplate().setAttributes(b.even) - } - } - g = e.gridSpriteOdd; - if (!g) { - g = e.gridSpriteOdd = new Ext.chart.Markers(); - g.setTemplate({ - xclass: "grid." + f - }); - c.add(g); - a.bindMarker(f + "-odd", g) - } - if (Ext.isObject(b)) { - g.getTemplate().setAttributes(b); - if (Ext.isObject(b.odd)) { - g.getTemplate().setAttributes(b.odd) - } - } - } - }, - renderLimits: function() { - this.getSprites()[0].renderLimits() - }, - getCoordFor: function(c, d, a, b) { - return this.getLayout().getCoordFor(c, d, a, b) - }, - applyPosition: function(a) { - return a.toLowerCase() - }, - applyLength: function(b, a) { - return b > 0 ? b : a - }, - applyLabel: function(b, a) { - if (!a) { - a = new Ext.draw.sprite.Text({}) - } - if (this.limitTitleTpl) { - this.limitTitleTpl.setAttributes(b) - } - a.setAttributes(b); - return a - }, - applyLayout: function(b, a) { - b = Ext.factory(b, null, a, "axisLayout"); - b.setAxis(this); - return b - }, - applySegmenter: function(a, b) { - a = Ext.factory(a, null, b, "segmenter"); - a.setAxis(this); - return a - }, - updateMinimum: function() { - this.range = null - }, - updateMaximum: function() { - this.range = null - }, - hideLabels: function() { - this.getSprites()[0].setDirty(true); - this.setLabel({ - hidden: true - }) - }, - showLabels: function() { - this.getSprites()[0].setDirty(true); - this.setLabel({ - hidden: false - }) - }, - renderFrame: function() { - this.getSurface().renderFrame() - }, - updateChart: function(d, b) { - var c = this, - a; - if (b) { - b.unregister(c); - b.un("serieschange", c.onSeriesChange, c); - b.un("redraw", c.renderLimits, c); - c.linkAxis(); - c.fireEvent("chartdetached", b, c) - } - if (d) { - d.on("serieschange", c.onSeriesChange, c); - c.surface = null; - a = c.getSurface(); - c.getLabel().setSurface(a); - a.add(c.getSprites()); - a.add(c.getTitle()); - d.register(c); - c.fireEvent("chartattached", d, c) - } - }, - applyBackground: function(a) { - var b = Ext.ClassManager.getByAlias("sprite.rect"); - return b.def.normalize(a) - }, - processData: function() { - this.getLayout().processData(); - this.range = null - }, - getDirection: function() { - return this.getChart().getDirectionForAxis(this.getPosition()) - }, - isSide: function() { - var a = this.getPosition(); - return a === "left" || a === "right" - }, - applyFields: function(a) { - return Ext.Array.from(a) - }, - applyVisibleRange: function(a, c) { - this.getChart(); - if (a[0] > a[1]) { - var b = a[0]; - a[0] = a[1]; - a[0] = b - } - if (a[1] === a[0]) { - a[1] += 1 / this.getMaxZoom() - } - if (a[1] > a[0] + 1) { - a[0] = 0; - a[1] = 1 - } else { - if (a[0] < 0) { - a[1] -= a[0]; - a[0] = 0 - } else { - if (a[1] > 1) { - a[0] -= a[1] - 1; - a[1] = 1 - } - } - } - if (c && a[0] === c[0] && a[1] === c[1]) { - return undefined - } - return a - }, - updateVisibleRange: function(a) { - this.fireEvent("visiblerangechange", this, a) - }, - onSeriesChange: function(e) { - var f = this, - b = e.getSeries(), - j = "get" + f.getDirection() + "Axis", - g = [], - c, d = b.length, - a, h; - for (c = 0; c < d; c++) { - if (this === b[c][j]()) { - g.push(b[c]) - } - } - f.boundSeries = g; - a = f.getLinkedTo(); - h = !Ext.isEmpty(a) && e.getAxis(a); - if (h) { - f.linkAxis(h) - } else { - f.getLayout().processData() - } - }, - linkAxis: function(a) { - var c = this; - - function b(f, d, e) { - e.getLayout()[f]("datachange", "onDataChange", d); - e[f]("rangechange", "onMasterAxisRangeChange", d) - } - if (c.masterAxis) { - b("un", c, c.masterAxis); - c.masterAxis = null - } - if (a) { - if (a.type !== this.type) { - Ext.Error.raise("Linked axes must be of the same type.") - } - b("on", c, a); - c.onDataChange(a.getLayout().labels); - c.onMasterAxisRangeChange(a, a.range); - c.setStyle(Ext.apply({}, c.config.style, a.config.style)); - c.setTitle(Ext.apply({}, c.config.title, a.config.title)); - c.setLabel(Ext.apply({}, c.config.label, a.config.label)); - c.masterAxis = a - } - }, - onDataChange: function(a) { - this.getLayout().labels = a - }, - onMasterAxisRangeChange: function(b, a) { - this.range = a - }, - applyRange: function(a) { - if (!a) { - return this.dataRange.slice(0) - } else { - return [a[0] === null ? this.dataRange[0] : a[0], a[1] === null ? this.dataRange[1] : a[1]] - } - }, - getRange: function() { - var m = this; - if (m.range) { - return m.range - } else { - if (m.masterAxis) { - return m.masterAxis.range - } - } - if (Ext.isNumber(m.getMinimum() + m.getMaximum())) { - return m.range = [m.getMinimum(), m.getMaximum()] - } - var d = Infinity, - n = -Infinity, - o = m.boundSeries, - h = m.getLayout(), - l = m.getSegmenter(), - p = m.getVisibleRange(), - b = "get" + m.getDirection() + "Range", - a, j, g, f, e, k; - for (e = 0, k = o.length; e < k; e++) { - f = o[e]; - var c = f[b](); - if (c) { - if (c[0] < d) { - d = c[0] - } - if (c[1] > n) { - n = c[1] - } - } - } - if (!isFinite(n)) { - n = m.prevMax - } - if (!isFinite(d)) { - d = m.prevMin - } - if (m.getLabelInSpan() || d === n) { - n += m.getIncrement(); - d -= m.getIncrement() - } - if (Ext.isNumber(m.getMinimum())) { - d = m.getMinimum() - } else { - m.prevMin = d - } - if (Ext.isNumber(m.getMaximum())) { - n = m.getMaximum() - } else { - m.prevMax = n - } - m.range = [Ext.Number.correctFloat(d), Ext.Number.correctFloat(n)]; - if (m.getReconcileRange()) { - m.reconcileRange() - } - if (m.getAdjustByMajorUnit() && l.adjustByMajorUnit && !m.getMajorTickSteps()) { - j = Ext.Object.chain(m.getSprites()[0].attr); - j.min = m.range[0]; - j.max = m.range[1]; - j.visibleMin = p[0]; - j.visibleMax = p[1]; - a = { - attr: j, - segmenter: l - }; - h.calculateLayout(a); - g = a.majorTicks; - if (g) { - l.adjustByMajorUnit(g.step, g.unit.scale, m.range); - j.min = m.range[0]; - j.max = m.range[1]; - delete a.majorTicks; - h.calculateLayout(a); - g = a.majorTicks; - l.adjustByMajorUnit(g.step, g.unit.scale, m.range) - } else { - if (!m.hasClearRangePending) { - m.hasClearRangePending = true; - m.getChart().on("layout", "clearRange", m) - } - } - } - if (!Ext.Array.equals(m.range, m.oldRange || [])) { - m.fireEvent("rangechange", m, m.range); - m.oldRange = m.range - } - return m.range - }, - clearRange: function() { - delete this.hasClearRangePending; - this.range = null - }, - reconcileRange: function() { - var e = this, - g = e.getChart().getAxes(), - f = e.getDirection(), - b, d, c, a; - if (!g) { - return - } - for (b = 0, d = g.length; b < d; b++) { - c = g[b]; - a = c.getRange(); - if (c === e || c.getDirection() !== f || !a || !c.getReconcileRange()) { - continue - } - if (a[0] < e.range[0]) { - e.range[0] = a[0] - } - if (a[1] > e.range[1]) { - e.range[1] = a[1] - } - } - }, - applyStyle: function(c, b) { - var a = Ext.ClassManager.getByAlias("sprite." + this.seriesType); - if (a && a.def) { - c = a.def.normalize(c) - } - b = Ext.apply(b || {}, c); - return b - }, - themeOnlyIfConfigured: { - grid: true - }, - updateTheme: function(d) { - var i = this, - k = d.getAxis(), - e = i.getPosition(), - o = i.getInitialConfig(), - c = i.defaultConfig, - g = i.getConfigurator().configs, - a = k.defaults, - n = k[e], - h = i.themeOnlyIfConfigured, - l, j, p, b, m, f; - k = Ext.merge({}, a, n); - for (l in k) { - j = k[l]; - f = g[l]; - if (j !== null && j !== undefined && f) { - m = o[l]; - p = Ext.isObject(j); - b = m === c[l]; - if (p) { - if (b && h[l]) { - continue - } - j = Ext.merge({}, j, m) - } - if (b || p) { - i[f.names.set](j) - } - } - } - }, - updateCenter: function(b) { - var e = this.getSprites(), - a = e[0], - d = b[0], - c = b[1]; - if (a) { - a.setAttributes({ - centerX: d, - centerY: c - }) - } - if (this.gridSpriteEven) { - this.gridSpriteEven.getTemplate().setAttributes({ - translationX: d, - translationY: c, - rotationCenterX: d, - rotationCenterY: c - }) - } - if (this.gridSpriteOdd) { - this.gridSpriteOdd.getTemplate().setAttributes({ - translationX: d, - translationY: c, - rotationCenterX: d, - rotationCenterY: c - }) - } - }, - getSprites: function() { - if (!this.getChart()) { - return - } - var i = this, - e = i.getRange(), - f = i.getPosition(), - g = i.getChart(), - c = g.getAnimation(), - d, a, b = i.getLength(), - h = i.superclass; - if (c === false) { - c = { - duration: 0 - } - } - if (e) { - a = Ext.applyIf({ - position: f, - axis: i, - min: e[0], - max: e[1], - length: b, - grid: i.getGrid(), - hidden: i.getHidden(), - titleOffset: i.titleOffset, - layout: i.getLayout(), - segmenter: i.getSegmenter(), - totalAngle: i.getTotalAngle(), - label: i.getLabel() - }, i.getStyle()); - if (!i.sprites.length) { - while (!h.xtype) { - h = h.superclass - } - d = Ext.create("sprite." + h.xtype, a); - d.fx.setCustomDurations({ - baseRotation: 0 - }); - d.fx.on("animationstart", "onAnimationStart", i); - d.fx.on("animationend", "onAnimationEnd", i); - d.setLayout(i.getLayout()); - d.setSegmenter(i.getSegmenter()); - d.setLabel(i.getLabel()); - i.sprites.push(d); - i.updateTitleSprite() - } else { - d = i.sprites[0]; - d.setAnimation(c); - d.setAttributes(a) - } - if (i.getRenderer()) { - d.setRenderer(i.getRenderer()) - } - } - return i.sprites - }, - updateTitleSprite: function() { - var f = this, - b = f.getLength(); - if (!f.sprites[0] || !Ext.isNumber(b)) { - return - } - var h = this.sprites[0].thickness, - a = f.getSurface(), - g = f.getTitle(), - e = f.getPosition(), - c = f.getMargin(), - i = f.getTitleMargin(), - d = a.roundPixel(b / 2); - if (g) { - switch (e) { - case "top": - g.setAttributes({ - x: d, - y: c + i / 2, - textBaseline: "top", - textAlign: "center" - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().height + i; - break; - case "bottom": - g.setAttributes({ - x: d, - y: h + i / 2, - textBaseline: "top", - textAlign: "center" - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().height + i; - break; - case "left": - g.setAttributes({ - x: c + i / 2, - y: d, - textBaseline: "top", - textAlign: "center", - rotationCenterX: c + i / 2, - rotationCenterY: d, - rotationRads: -Math.PI / 2 - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().width + i; - break; - case "right": - g.setAttributes({ - x: h - c + i / 2, - y: d, - textBaseline: "bottom", - textAlign: "center", - rotationCenterX: h + i / 2, - rotationCenterY: d, - rotationRads: Math.PI / 2 - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().width + i; - break - } - } - }, - onThicknessChanged: function() { - this.getChart().onThicknessChanged() - }, - getThickness: function() { - if (this.getHidden()) { - return 0 - } - return (this.sprites[0] && this.sprites[0].thickness || 1) + this.titleOffset + this.getMargin() - }, - onAnimationStart: function() { - this.spriteAnimationCount++; - if (this.spriteAnimationCount === 1) { - this.fireEvent("animationstart", this) - } - }, - onAnimationEnd: function() { - this.spriteAnimationCount--; - if (this.spriteAnimationCount === 0) { - this.fireEvent("animationend", this) - } - }, - getItemId: function() { - return this.getId() - }, - getAncestorIds: function() { - return [this.getChart().getId()] - }, - isXType: function(a) { - return a === "axis" - }, - resolveListenerScope: function(e) { - var d = this, - a = Ext._namedScopes[e], - c = d.getChart(), - b; - if (!a) { - b = c ? c.resolveListenerScope(e, false) : (e || d) - } else { - if (a.isThis) { - b = d - } else { - if (a.isController) { - b = c ? c.resolveListenerScope(e, false) : d - } else { - if (a.isSelf) { - b = c ? c.resolveListenerScope(e, false) : d; - if (b === c && !c.getInheritedConfig("defaultListenerScope")) { - b = d - } - } - } - } - } - return b - }, - destroy: function() { - var a = this; - a.setChart(null); - a.surface.destroy(); - a.surface = null; - a.callParent() - } -}); -Ext.define("Ext.chart.LegendBase", { - extend: "Ext.view.View", - config: { - tpl: ['
', '', '
', "', "{name}", "
", "
", "
"], - nodeContainerSelector: "div." + Ext.baseCSSPrefix + "legend-container", - itemSelector: "div." + Ext.baseCSSPrefix + "legend-item", - docked: "bottom" - }, - setDocked: function(d) { - var c = this, - a = c.ownerCt, - b; - c.docked = d; - switch (d) { - case "top": - case "bottom": - c.addCls(Ext.baseCSSPrefix + "horizontal"); - b = "hbox"; - break; - case "left": - case "right": - c.removeCls(Ext.baseCSSPrefix + "horizontal"); - b = "vbox"; - break - } - if (a) { - a.setDocked(d) - } - }, - setStore: function(a) { - this.bindStore(a) - }, - clearViewEl: function() { - this.callParent(arguments); - Ext.removeNode(this.getNodeContainer()) - }, - onItemClick: function(a, c, b, d) { - this.callParent(arguments); - this.toggleItem(b) - } -}); -Ext.define("Ext.chart.Legend", { - xtype: "legend", - extend: "Ext.chart.LegendBase", - config: { - baseCls: Ext.baseCSSPrefix + "legend", - padding: 5, - rect: null, - disableSelection: true, - toggleable: true - }, - toggleItem: function(c) { - if (!this.getToggleable()) { - return - } - var b = this.getStore(), - h = 0, - e, g = true, - d, f, a; - if (b) { - f = b.getCount(); - for (d = 0; d < f; d++) { - a = b.getAt(d); - if (a.get("disabled")) { - h++ - } - } - g = f - h > 1; - a = b.getAt(c); - if (a) { - e = a.get("disabled"); - if (e || g) { - a.set("disabled", !e) - } - } - } - } -}); -Ext.define("Ext.chart.AbstractChart", { - extend: "Ext.draw.Container", - requires: ["Ext.chart.theme.Default", "Ext.chart.series.Series", "Ext.chart.interactions.Abstract", "Ext.chart.axis.Axis", "Ext.data.StoreManager", "Ext.chart.Legend", "Ext.data.Store"], - isChart: true, - defaultBindProperty: "store", - config: { - store: "ext-empty-store", - theme: "default", - style: null, - animation: !Ext.isIE8, - series: [], - axes: [], - legend: null, - colors: null, - insetPadding: { - top: 10, - left: 10, - right: 10, - bottom: 10 - }, - background: null, - interactions: [], - mainRect: null, - resizeHandler: null, - highlightItem: null - }, - animationSuspendCount: 0, - chartLayoutSuspendCount: 0, - axisThicknessSuspendCount: 0, - isThicknessChanged: false, - surfaceZIndexes: { - background: 0, - main: 1, - grid: 2, - series: 3, - axis: 4, - chart: 5, - overlay: 6, - events: 7 - }, - constructor: function(a) { - var b = this; - b.itemListeners = {}; - b.surfaceMap = {}; - b.chartComponents = {}; - b.isInitializing = true; - b.suspendChartLayout(); - b.animationSuspendCount++; - b.callParent(arguments); - delete b.isInitializing; - b.getSurface("main"); - b.getSurface("chart").setFlipRtlText(b.getInherited().rtl); - b.getSurface("overlay").waitFor(b.getSurface("series")); - b.animationSuspendCount--; - b.resumeChartLayout() - }, - applyAnimation: function(a, b) { - if (!a) { - a = { - duration: 0 - } - } else { - if (a === true) { - a = { - easing: "easeInOut", - duration: 500 - } - } - } - return b ? Ext.apply({}, a, b) : a - }, - getAnimation: function() { - if (this.animationSuspendCount) { - return { - duration: 0 - } - } else { - return this.callParent() - } - }, - applyInsetPadding: function(b, a) { - if (!Ext.isObject(b)) { - return Ext.util.Format.parseBox(b) - } else { - if (!a) { - return b - } else { - return Ext.apply(a, b) - } - } - }, - suspendAnimation: function() { - var d = this, - c = d.getSeries(), - e = c.length, - b = -1, - a; - d.animationSuspendCount++; - if (d.animationSuspendCount === 1) { - while (++b < e) { - a = c[b]; - a.setAnimation(a.getAnimation()) - } - } - }, - resumeAnimation: function() { - var d = this, - c = d.getSeries(), - f = c.length, - b = -1, - a, e; - d.animationSuspendCount--; - if (d.animationSuspendCount === 0) { - while (++b < f) { - a = c[b]; - e = a.getAnimation(); - a.setAnimation(e.duration && e || d.getAnimation()) - } - } - }, - suspendChartLayout: function() { - this.chartLayoutSuspendCount++; - if (this.chartLayoutSuspendCount === 1) { - if (this.scheduledLayoutId) { - this.layoutInSuspension = true; - this.cancelChartLayout() - } else { - this.layoutInSuspension = false - } - } - }, - resumeChartLayout: function() { - this.chartLayoutSuspendCount--; - if (this.chartLayoutSuspendCount === 0) { - if (this.layoutInSuspension) { - this.scheduleLayout() - } - } - }, - cancelChartLayout: function() { - if (this.scheduledLayoutId) { - Ext.draw.Animator.cancel(this.scheduledLayoutId); - this.scheduledLayoutId = null - } - }, - scheduleLayout: function() { - var a = this; - if (a.allowSchedule() && !a.scheduledLayoutId) { - a.scheduledLayoutId = Ext.draw.Animator.schedule("doScheduleLayout", a) - } - }, - allowSchedule: function() { - return true - }, - doScheduleLayout: function() { - if (this.chartLayoutSuspendCount) { - this.layoutInSuspension = true - } else { - this.performLayout() - } - }, - suspendThicknessChanged: function() { - this.axisThicknessSuspendCount++ - }, - resumeThicknessChanged: function() { - if (this.axisThicknessSuspendCount > 0) { - this.axisThicknessSuspendCount--; - if (this.axisThicknessSuspendCount === 0 && this.isThicknessChanged) { - this.onThicknessChanged() - } - } - }, - onThicknessChanged: function() { - if (this.axisThicknessSuspendCount === 0) { - this.isThicknessChanged = false; - this.performLayout() - } else { - this.isThicknessChanged = true - } - }, - applySprites: function(b) { - var a = this.getSurface("chart"); - b = Ext.Array.from(b); - a.removeAll(true); - a.add(b); - return b - }, - initItems: function() { - var a = this.items, - b, d, c; - if (a && !a.isMixedCollection) { - this.items = []; - a = Ext.Array.from(a); - for (b = 0, d = a.length; b < d; b++) { - c = a[b]; - if (c.type) { - Ext.raise("To add custom sprites to the chart use the 'sprites' config.") - } else { - this.items.push(c) - } - } - } - this.callParent() - }, - applyBackground: function(c, e) { - var b = this.getSurface("background"), - d, a, f; - if (c) { - if (e) { - d = e.attr.width; - a = e.attr.height; - f = e.type === (c.type || "rect") - } - if (c.isSprite) { - e = c - } else { - if (c.type === "image" && Ext.isString(c.src)) { - if (f) { - e.setAttributes({ - src: c.src - }) - } else { - b.remove(e, true); - e = b.add(c) - } - } else { - if (f) { - e.setAttributes({ - fillStyle: c - }) - } else { - b.remove(e, true); - e = b.add({ - type: "rect", - fillStyle: c, - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0 - } - } - }) - } - } - } - } - if (d && a) { - e.setAttributes({ - width: d, - height: a - }) - } - e.setAnimation(this.getAnimation()); - return e - }, - getLegendStore: function() { - return this.legendStore - }, - refreshLegendStore: function() { - if (this.getLegendStore()) { - var d, e, c = this.getSeries(), - b, a = []; - if (c) { - for (d = 0, e = c.length; d < e; d++) { - b = c[d]; - if (b.getShowInLegend()) { - b.provideLegendInfo(a) - } - } - } - this.getLegendStore().setData(a) - } - }, - resetLegendStore: function() { - var c = this.getLegendStore(), - e, d, a, b; - if (c) { - e = this.getLegendStore().getData().items; - for (d = 0, a = e.length; d < a; d++) { - b = e[d]; - b.beginEdit(); - b.set("disabled", false); - b.commit() - } - } - }, - onUpdateLegendStore: function(b, a) { - var d = this.getSeries(), - c; - if (a && d) { - c = d.map[a.get("series")]; - if (c) { - c.setHiddenByIndex(a.get("index"), a.get("disabled")); - this.redraw() - } - } - }, - defaultResizeHandler: function(a) { - this.scheduleLayout(); - return false - }, - applyMainRect: function(a, b) { - if (!b) { - return a - } - this.getSeries(); - this.getAxes(); - if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]) { - return b - } else { - return a - } - }, - register: function(a) { - var b = this.chartComponents, - c = a.getId(); - b[c] = a - }, - unregister: function(a) { - var b = this.chartComponents, - c = a.getId(); - delete b[c] - }, - get: function(a) { - return this.chartComponents[a] - }, - getAxis: function(a) { - if (a instanceof Ext.chart.axis.Axis) { - return a - } else { - if (Ext.isNumber(a)) { - return this.getAxes()[a] - } else { - if (Ext.isString(a)) { - return this.get(a) - } - } - } - }, - getSurface: function(b, c) { - b = b || "main"; - c = c || b; - var d = this, - a = this.callParent([b]), - f = d.surfaceZIndexes, - e = d.surfaceMap; - if (c in f) { - a.element.setStyle("zIndex", f[c]) - } - if (!e[c]) { - e[c] = [] - } - if (Ext.Array.indexOf(e[c], a) < 0) { - a.type = c; - e[c].push(a); - a.on("destroy", d.forgetSurface, d) - } - return a - }, - forgetSurface: function(a) { - var d = this.surfaceMap; - if (!d || this.isDestroying) { - return - } - var c = d[a.type], - b = c ? Ext.Array.indexOf(c, a) : -1; - if (b >= 0) { - c.splice(b, 1) - } - }, - applyAxes: function(b, k) { - var l = this, - g = { - left: "right", - right: "left" - }, - m = [], - c, d, e, a, f, h, j; - l.animationSuspendCount++; - l.getStore(); - if (!k) { - k = []; - k.map = {} - } - j = k.map; - m.map = {}; - b = Ext.Array.from(b, true); - for (f = 0, h = b.length; f < h; f++) { - c = b[f]; - if (!c) { - continue - } - if (c instanceof Ext.chart.axis.Axis) { - d = j[c.getId()]; - c.setChart(l) - } else { - c = Ext.Object.chain(c); - e = c.linkedTo; - a = c.id; - if (Ext.isNumber(e)) { - c = Ext.merge({}, b[e], c) - } else { - if (Ext.isString(e)) { - Ext.Array.each(b, function(i) { - if (i.id === c.linkedTo) { - c = Ext.merge({}, i, c); - return false - } - }) - } - } - c.id = a; - c.chart = l; - if (l.getInherited().rtl) { - c.position = g[c.position] || c.position - } - a = c.getId && c.getId() || c.id; - c = Ext.factory(c, null, d = j[a], "axis") - } - if (c) { - m.push(c); - m.map[c.getId()] = c; - if (!d) { - c.on("animationstart", "onAnimationStart", l); - c.on("animationend", "onAnimationEnd", l) - } - } - } - for (f in j) { - if (!m.map[f]) { - j[f].destroy() - } - } - l.animationSuspendCount--; - return m - }, - updateAxes: function() { - if (!this.isDestroying) { - this.scheduleLayout() - } - }, - circularCopyArray: function(e, f, d) { - var c = [], - b, a = e && e.length; - if (a) { - for (b = 0; b < d; b++) { - c.push(e[(f + b) % a]) - } - } - return c - }, - circularCopyObject: function(f, g, d) { - var c = this, - b, e, a = {}; - if (d) { - for (b in f) { - if (f.hasOwnProperty(b)) { - e = f[b]; - if (Ext.isArray(e)) { - a[b] = c.circularCopyArray(e, g, d) - } else { - a[b] = e - } - } - } - } - return a - }, - getColors: function() { - var b = this, - a = b.config.colors, - c = b.getTheme(); - if (Ext.isArray(a) && a.length > 0) { - a = b.applyColors(a) - } - return a || (c && c.getColors()) - }, - applyColors: function(a) { - a = Ext.Array.map(a, function(b) { - if (Ext.isString(b)) { - return b - } else { - return b.toString() - } - }); - return a - }, - updateColors: function(c) { - var k = this, - e = k.getTheme(), - a = c || (e && e.getColors()), - l = 0, - f = k.getSeries(), - d = f && f.length, - g, j, b, h; - if (a.length) { - for (g = 0; g < d; g++) { - j = f[g]; - h = j.themeColorCount(); - b = k.circularCopyArray(a, l, h); - l += h; - j.updateChartColors(b) - } - } - k.refreshLegendStore() - }, - applyTheme: function(a) { - if (a && a.isTheme) { - return a - } - return Ext.Factory.chartTheme(a) - }, - updateTheme: function(g) { - var e = this, - f = e.getAxes(), - d = e.getSeries(), - a = e.getColors(), - c, b; - e.updateChartTheme(g); - for (b = 0; b < f.length; b++) { - f[b].updateTheme(g) - } - for (b = 0; b < d.length; b++) { - c = d[b]; - c.updateTheme(g) - } - e.updateSpriteTheme(g); - e.updateColors(a); - e.redraw() - }, - themeOnlyIfConfigured: {}, - updateChartTheme: function(c) { - var i = this, - k = c.getChart(), - n = i.getInitialConfig(), - b = i.defaultConfig, - e = i.getConfigurator().configs, - f = k.defaults, - g = k[i.xtype], - h = i.themeOnlyIfConfigured, - l, j, o, a, m, d; - k = Ext.merge({}, f, g); - for (l in k) { - j = k[l]; - d = e[l]; - if (j !== null && j !== undefined && d) { - m = n[l]; - o = Ext.isObject(j); - a = m === b[l]; - if (o) { - if (a && h[l]) { - continue - } - j = Ext.merge({}, j, m) - } - if (a || o) { - i[d.names.set](j) - } - } - } - }, - updateSpriteTheme: function(c) { - this.getSprites(); - var j = this, - e = j.getSurface("chart"), - h = e.getItems(), - m = c.getSprites(), - k, a, l, f, d, b, g; - for (b = 0, g = h.length; b < g; b++) { - k = h[b]; - a = m[k.type]; - if (a) { - f = {}; - d = k.type === "text"; - for (l in a) { - if (!(l in k.config)) { - if (!(d && l.indexOf("font") === 0 && k.config.font)) { - f[l] = a[l] - } - } - } - k.setAttributes(f) - } - } - }, - addSeries: function(b) { - var a = this.getSeries(); - Ext.Array.push(a, b); - this.setSeries(a) - }, - removeSeries: function(d) { - d = Ext.Array.from(d); - var b = this.getSeries(), - f = [], - a = d.length, - g = {}, - c, e; - for (c = 0; c < a; c++) { - e = d[c]; - if (typeof e !== "string") { - e = e.getId() - } - g[e] = true - } - for (c = 0, a = b.length; c < a; c++) { - if (!g[b[c].getId()]) { - f.push(b[c]) - } - } - this.setSeries(f) - }, - applySeries: function(e, d) { - var g = this, - j = [], - h, a, c, f, b; - g.animationSuspendCount++; - g.getAxes(); - if (d) { - h = d.map - } else { - d = []; - h = d.map = {} - } - j.map = {}; - e = Ext.Array.from(e, true); - for (c = 0, f = e.length; c < f; c++) { - b = e[c]; - if (!b) { - continue - } - a = h[b.getId && b.getId() || b.id]; - if (b instanceof Ext.chart.series.Series) { - if (a && a !== b) { - a.destroy() - } - b.setChart(g) - } else { - if (Ext.isObject(b)) { - if (a) { - a.setConfig(b); - b = a - } else { - if (Ext.isString(b)) { - b = { - type: b - } - } - b.chart = g; - b = Ext.create(b.xclass || ("series." + b.type), b); - b.on("animationstart", "onAnimationStart", g); - b.on("animationend", "onAnimationEnd", g) - } - } - } - j.push(b); - j.map[b.getId()] = b - } - for (c in h) { - if (!j.map[h[c].getId()]) { - h[c].destroy() - } - } - g.animationSuspendCount--; - return j - }, - applyLegend: function(b, a) { - return Ext.factory(b, Ext.chart.Legend, a) - }, - updateLegend: function(b, a) { - if (a) { - a.destroy() - } - if (b) { - this.getItems(); - this.legendStore = new Ext.data.Store({ - autoDestroy: true, - fields: ["id", "name", "mark", "disabled", "series", "index"] - }); - b.setStore(this.legendStore); - this.refreshLegendStore(); - this.legendStore.on("update", "onUpdateLegendStore", this) - } - }, - updateSeries: function(b, a) { - var c = this; - if (c.isDestroying) { - return - } - c.animationSuspendCount++; - c.fireEvent("serieschange", c, b, a); - c.refreshLegendStore(); - if (!Ext.isEmpty(b)) { - c.updateTheme(c.getTheme()) - } - c.scheduleLayout(); - c.animationSuspendCount-- - }, - applyInteractions: function(h, d) { - if (!d) { - d = []; - d.map = {} - } - var g = this, - a = [], - c = d.map, - e, f, b; - a.map = {}; - h = Ext.Array.from(h, true); - for (e = 0, f = h.length; e < f; e++) { - b = h[e]; - if (!b) { - continue - } - b = Ext.factory(b, null, c[b.getId && b.getId() || b.id], "interaction"); - if (b) { - b.setChart(g); - a.push(b); - a.map[b.getId()] = b - } - } - for (e in c) { - if (!a.map[e]) { - c[e].destroy() - } - } - return a - }, - getInteraction: function(e) { - var f = this.getInteractions(), - a = f && f.length, - c = null, - b, d; - if (a) { - for (d = 0; d < a; ++d) { - b = f[d]; - if (b.type === e) { - c = b; - break - } - } - } - return c - }, - applyStore: function(a) { - return a && Ext.StoreManager.lookup(a) - }, - updateStore: function(a, c) { - var b = this; - if (c) { - c.un({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: b, - order: "after" - }); - if (c.autoDestroy) { - c.destroy() - } - } - if (a) { - a.on({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: b, - order: "after" - }) - } - b.fireEvent("storechange", b, a, c); - b.onDataChanged() - }, - redraw: function() { - this.fireEvent("redraw", this) - }, - performLayout: function() { - var d = this, - b = d.getChartSize(true), - c = [0, 0, b.width, b.height], - a = d.getBackground(); - d.hasFirstLayout = true; - d.fireEvent("layout", d); - d.cancelChartLayout(); - d.getSurface("background").setRect(c); - d.getSurface("chart").setRect(c); - a.setAttributes({ - width: b.width, - height: b.height - }) - }, - getChartSize: function(b) { - var a = this; - if (b) { - a.chartSize = null - } - return a.chartSize || (a.chartSize = a.innerElement.getSize()) - }, - getEventXY: function(a) { - return this.getSurface().getEventXY(a) - }, - getItemForPoint: function(h, g) { - var f = this, - a = f.getSeries(), - e = f.getMainRect(), - d = a.length, - b = f.hasFirstLayout ? d - 1 : -1, - c, j; - if (!(e && h >= 0 && h <= e[2] && g >= 0 && g <= e[3])) { - return null - } - for (; b >= 0; b--) { - c = a[b]; - j = c.getItemForPoint(h, g); - if (j) { - return j - } - } - return null - }, - getItemsForPoint: function(h, g) { - var f = this, - a = f.getSeries(), - d = a.length, - b = f.hasFirstLayout ? d - 1 : -1, - e = [], - c, j; - for (; b >= 0; b--) { - c = a[b]; - j = c.getItemForPoint(h, g); - if (j) { - e.push(j) - } - } - return e - }, - onAnimationStart: function() { - this.fireEvent("animationstart", this) - }, - onAnimationEnd: function() { - this.fireEvent("animationend", this) - }, - onDataChanged: function() { - var d = this; - if (d.isInitializing) { - return - } - var c = d.getMainRect(), - a = d.getStore(), - b = d.getSeries(), - e = d.getAxes(); - if (!a || !e || !b) { - return - } - if (!c) { - d.on({ - redraw: d.onDataChanged, - scope: d, - single: true - }); - return - } - d.processData(); - d.redraw() - }, - recordCount: 0, - processData: function() { - var g = this, - e = g.getStore().getCount(), - c = g.getSeries(), - f = c.length, - d = false, - b = 0, - a; - for (; b < f; b++) { - a = c[b]; - a.processData(); - if (!d && a.isStoreDependantColorCount) { - d = true - } - } - if (d && e > g.recordCount) { - g.updateColors(g.getColors()); - g.recordCount = e - } - }, - bindStore: function(a) { - this.setStore(a) - }, - applyHighlightItem: function(f, a) { - if (f === a) { - return - } - if (Ext.isObject(f) && Ext.isObject(a)) { - var e = f, - d = a, - c = e.sprite && (e.sprite[0] || e.sprite), - b = d.sprite && (d.sprite[0] || d.sprite); - if (c === b && e.index === d.index) { - return - } - } - return f - }, - updateHighlightItem: function(b, a) { - if (a) { - a.series.setAttributesForItem(a, { - highlighted: false - }) - } - if (b) { - b.series.setAttributesForItem(b, { - highlighted: true - }); - this.fireEvent("itemhighlight", this, b, a) - } - this.fireEvent("itemhighlightchange", this, b, a) - }, - destroyChart: function() { - var f = this, - d = f.getLegend(), - g = f.getAxes(), - c = f.getSeries(), - h = f.getInteractions(), - b = [], - a, e; - f.surfaceMap = null; - for (a = 0, e = h.length; a < e; a++) { - h[a].destroy() - } - for (a = 0, e = g.length; a < e; a++) { - g[a].destroy() - } - for (a = 0, e = c.length; a < e; a++) { - c[a].destroy() - } - f.setInteractions(b); - f.setAxes(b); - f.setSeries(b); - if (d) { - d.destroy(); - f.setLegend(null) - } - f.legendStore = null; - f.setStore(null); - f.cancelChartLayout() - }, - getRefItems: function(b) { - var g = this, - e = g.getSeries(), - h = g.getAxes(), - a = g.getInteractions(), - c = [], - d, f; - for (d = 0, f = e.length; d < f; d++) { - c.push(e[d]); - if (e[d].getRefItems) { - c.push.apply(c, e[d].getRefItems(b)) - } - } - for (d = 0, f = h.length; d < f; d++) { - c.push(h[d]); - if (h[d].getRefItems) { - c.push.apply(c, h[d].getRefItems(b)) - } - } - for (d = 0, f = a.length; d < f; d++) { - c.push(a[d]); - if (a[d].getRefItems) { - c.push.apply(c, a[d].getRefItems(b)) - } - } - return c - } -}); -Ext.define("Ext.chart.overrides.AbstractChart", { - override: "Ext.chart.AbstractChart", - updateLegend: function(b, a) { - var c; - this.callParent([b, a]); - if (b) { - c = b.docked; - this.addDocked({ - dock: c, - xtype: "panel", - shrinkWrap: true, - scrollable: true, - layout: { - type: c === "top" || c === "bottom" ? "hbox" : "vbox", - pack: "center" - }, - items: b, - cls: Ext.baseCSSPrefix + "legend-panel" - }) - } - }, - performLayout: function() { - if (this.isVisible(true)) { - return this.callParent() - } - this.cancelChartLayout(); - return false - }, - afterComponentLayout: function(c, a, b, d) { - this.callParent([c, a, b, d]); - this.scheduleLayout() - }, - allowSchedule: function() { - return this.rendered - }, - onDestroy: function() { - this.destroyChart(); - this.callParent(arguments) - } -}); -Ext.define("Ext.chart.grid.HorizontalGrid", { - extend: "Ext.draw.sprite.Sprite", - alias: "grid.horizontal", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number" - }, - defaults: { - x: 0, - y: 0, - width: 1, - height: 1, - strokeStyle: "#DDD" - } - } - }, - render: function(b, c, e) { - var a = this.attr, - f = b.roundPixel(a.y), - d = c.lineWidth * 0.5; - c.beginPath(); - c.rect(e[0] - b.matrix.getDX(), f + d, +e[2], a.height); - c.fill(); - c.beginPath(); - c.moveTo(e[0] - b.matrix.getDX(), f + d); - c.lineTo(e[0] + e[2] - b.matrix.getDX(), f + d); - c.stroke() - } -}); -Ext.define("Ext.chart.grid.VerticalGrid", { - extend: "Ext.draw.sprite.Sprite", - alias: "grid.vertical", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number" - }, - defaults: { - x: 0, - y: 0, - width: 1, - height: 1, - strokeStyle: "#DDD" - } - } - }, - render: function(c, d, f) { - var b = this.attr, - a = c.roundPixel(b.x), - e = d.lineWidth * 0.5; - d.beginPath(); - d.rect(a - e, f[1] - c.matrix.getDY(), b.width, f[3]); - d.fill(); - d.beginPath(); - d.moveTo(a - e, f[1] - c.matrix.getDY()); - d.lineTo(a - e, f[1] + f[3] - c.matrix.getDY()); - d.stroke() - } -}); -Ext.define("Ext.chart.CartesianChart", { - extend: "Ext.chart.AbstractChart", - alternateClassName: "Ext.chart.Chart", - requires: ["Ext.chart.grid.HorizontalGrid", "Ext.chart.grid.VerticalGrid"], - xtype: ["cartesian", "chart"], - isCartesian: true, - config: { - flipXY: false, - innerRect: [0, 0, 1, 1], - innerPadding: { - top: 0, - left: 0, - right: 0, - bottom: 0 - } - }, - applyInnerPadding: function(b, a) { - if (!Ext.isObject(b)) { - return Ext.util.Format.parseBox(b) - } else { - if (!a) { - return b - } else { - return Ext.apply(a, b) - } - } - }, - getDirectionForAxis: function(a) { - var b = this.getFlipXY(); - if (a === "left" || a === "right") { - if (b) { - return "X" - } else { - return "Y" - } - } else { - if (b) { - return "Y" - } else { - return "X" - } - } - }, - performLayout: function() { - var A = this; - A.animationSuspendCount++; - if (A.callParent() === false) { - --A.animationSuspendCount; - return - } - A.suspendThicknessChanged(); - var d = A.getSurface("chart").getRect(), - o = d[2], - n = d[3], - z = A.getAxes(), - b, q = A.getSeries(), - h, l, a, f = A.getInsetPadding(), - v = A.getInnerPadding(), - r, c, e = Ext.apply({}, f), - u, p, s, k, m, y, t, x, g, j = A.getInherited().rtl, - w = A.getFlipXY(); - if (o <= 0 || n <= 0) { - return - } - for (x = 0; x < z.length; x++) { - b = z[x]; - l = b.getSurface(); - m = b.getFloating(); - y = m ? m.value : null; - a = b.getThickness(); - switch (b.getPosition()) { - case "top": - l.setRect([0, e.top + 1, o, a]); - break; - case "bottom": - l.setRect([0, n - (e.bottom + a), o, a]); - break; - case "left": - l.setRect([e.left, 0, a, n]); - break; - case "right": - l.setRect([o - (e.right + a), 0, a, n]); - break - } - if (y === null) { - e[b.getPosition()] += a - } - } - o -= e.left + e.right; - n -= e.top + e.bottom; - u = [e.left, e.top, o, n]; - e.left += v.left; - e.top += v.top; - e.right += v.right; - e.bottom += v.bottom; - p = o - v.left - v.right; - s = n - v.top - v.bottom; - A.setInnerRect([e.left, e.top, p, s]); - if (p <= 0 || s <= 0) { - return - } - A.setMainRect(u); - A.getSurface().setRect(u); - for (x = 0, g = A.surfaceMap.grid && A.surfaceMap.grid.length; x < g; x++) { - c = A.surfaceMap.grid[x]; - c.setRect(u); - c.matrix.set(1, 0, 0, 1, v.left, v.top); - c.matrix.inverse(c.inverseMatrix) - } - for (x = 0; x < z.length; x++) { - b = z[x]; - l = b.getSurface(); - t = l.matrix; - k = t.elements; - switch (b.getPosition()) { - case "top": - case "bottom": - k[4] = e.left; - b.setLength(p); - break; - case "left": - case "right": - k[5] = e.top; - b.setLength(s); - break - } - b.updateTitleSprite(); - t.inverse(l.inverseMatrix) - } - for (x = 0, g = q.length; x < g; x++) { - h = q[x]; - r = h.getSurface(); - r.setRect(u); - if (w) { - if (j) { - r.matrix.set(0, -1, -1, 0, v.left + p, v.top + s) - } else { - r.matrix.set(0, -1, 1, 0, v.left, v.top + s) - } - } else { - r.matrix.set(1, 0, 0, -1, v.left, v.top + s) - } - r.matrix.inverse(r.inverseMatrix); - h.getOverlaySurface().setRect(u) - } - A.redraw(); - A.animationSuspendCount--; - A.resumeThicknessChanged() - }, - refloatAxes: function() { - var h = this, - g = h.getAxes(), - o = (g && g.length) || 0, - c, d, n, f, l, b, k, r = h.getChartSize(), - q = h.getInsetPadding(), - p = h.getInnerPadding(), - a = r.width - q.left - q.right, - m = r.height - q.top - q.bottom, - j, e; - for (e = 0; e < o; e++) { - c = g[e]; - f = c.getFloating(); - l = f ? f.value : null; - if (l === null) { - delete c.floatingAtCoord; - continue - } - d = c.getSurface(); - n = d.getRect(); - if (!n) { - continue - } - n = n.slice(); - b = h.getAxis(f.alongAxis); - if (b) { - j = b.getAlignment() === "horizontal"; - if (Ext.isString(l)) { - l = b.getCoordFor(l) - } - b.floatingAxes[c.getId()] = l; - k = b.getSprites()[0].attr.matrix; - if (j) { - l = l * k.getXX() + k.getDX(); - c.floatingAtCoord = l + p.left + p.right - } else { - l = l * k.getYY() + k.getDY(); - c.floatingAtCoord = l + p.top + p.bottom - } - } else { - j = c.getAlignment() === "horizontal"; - if (j) { - c.floatingAtCoord = l + p.top + p.bottom - } else { - c.floatingAtCoord = l + p.left + p.right - } - l = d.roundPixel(0.01 * l * (j ? m : a)) - } - switch (c.getPosition()) { - case "top": - n[1] = q.top + p.top + l - n[3] + 1; - break; - case "bottom": - n[1] = q.top + p.top + (b ? l : m - l); - break; - case "left": - n[0] = q.left + p.left + l - n[2]; - break; - case "right": - n[0] = q.left + p.left + (b ? l : a - l) - 1; - break - } - d.setRect(n) - } - }, - redraw: function() { - var C = this, - r = C.getSeries(), - z = C.getAxes(), - b = C.getMainRect(), - p, t, w = C.getInnerPadding(), - f, l, s, e, q, A, v, g, d, c, a, k, n, y = C.getFlipXY(), - x = 1000, - m, u, h, o, B; - if (!b) { - return - } - p = b[2] - w.left - w.right; - t = b[3] - w.top - w.bottom; - for (A = 0; A < r.length; A++) { - h = r[A]; - if ((c = h.getXAxis())) { - n = c.getVisibleRange(); - l = c.getRange(); - l = [l[0] + (l[1] - l[0]) * n[0], l[0] + (l[1] - l[0]) * n[1]] - } else { - l = h.getXRange() - } - if ((a = h.getYAxis())) { - n = a.getVisibleRange(); - s = a.getRange(); - s = [s[0] + (s[1] - s[0]) * n[0], s[0] + (s[1] - s[0]) * n[1]] - } else { - s = h.getYRange() - } - q = { - visibleMinX: l[0], - visibleMaxX: l[1], - visibleMinY: s[0], - visibleMaxY: s[1], - innerWidth: p, - innerHeight: t, - flipXY: y - }; - f = h.getSprites(); - for (v = 0, g = f.length; v < g; v++) { - o = f[v]; - m = o.attr.zIndex; - if (m < x) { - m += (A + 1) * 100 + x; - o.attr.zIndex = m; - B = o.getMarker("items"); - if (B) { - u = B.attr.zIndex; - if (u === Number.MAX_VALUE) { - B.attr.zIndex = m - } else { - if (u < x) { - B.attr.zIndex = m + u - } - } - } - } - o.setAttributes(q, true) - } - } - for (A = 0; A < z.length; A++) { - d = z[A]; - e = d.isSide(); - f = d.getSprites(); - k = d.getRange(); - n = d.getVisibleRange(); - q = { - dataMin: k[0], - dataMax: k[1], - visibleMin: n[0], - visibleMax: n[1] - }; - if (e) { - q.length = t; - q.startGap = w.bottom; - q.endGap = w.top - } else { - q.length = p; - q.startGap = w.left; - q.endGap = w.right - } - for (v = 0, g = f.length; v < g; v++) { - f[v].setAttributes(q, true) - } - } - C.renderFrame(); - C.callParent(arguments) - }, - renderFrame: function() { - this.refloatAxes(); - this.callParent() - } -}); -Ext.define("Ext.chart.grid.CircularGrid", { - extend: "Ext.draw.sprite.Circle", - alias: "grid.circular", - inheritableStatics: { - def: { - defaults: { - r: 1, - strokeStyle: "#DDD" - } - } - } -}); -Ext.define("Ext.chart.grid.RadialGrid", { - extend: "Ext.draw.sprite.Path", - alias: "grid.radial", - inheritableStatics: { - def: { - processors: { - startRadius: "number", - endRadius: "number" - }, - defaults: { - startRadius: 0, - endRadius: 1, - scalingCenterX: 0, - scalingCenterY: 0, - strokeStyle: "#DDD" - }, - triggers: { - startRadius: "path,bbox", - endRadius: "path,bbox" - } - } - }, - render: function() { - this.callParent(arguments) - }, - updatePath: function(d, a) { - var b = a.startRadius, - c = a.endRadius; - d.moveTo(b, 0); - d.lineTo(c, 0) - } -}); -Ext.define("Ext.chart.PolarChart", { - extend: "Ext.chart.AbstractChart", - requires: ["Ext.chart.grid.CircularGrid", "Ext.chart.grid.RadialGrid"], - xtype: "polar", - isPolar: true, - config: { - center: [0, 0], - radius: 0, - innerPadding: 0 - }, - getDirectionForAxis: function(a) { - return a === "radial" ? "Y" : "X" - }, - applyCenter: function(a, b) { - if (b && a[0] === b[0] && a[1] === b[1]) { - return - } - return [+a[0], +a[1]] - }, - updateCenter: function(a) { - var g = this, - h = g.getAxes(), - d = g.getSeries(), - c, f, e, b; - for (c = 0, f = h.length; c < f; c++) { - e = h[c]; - e.setCenter(a) - } - for (c = 0, f = d.length; c < f; c++) { - b = d[c]; - b.setCenter(a) - } - }, - applyInnerPadding: function(b, a) { - return Ext.isNumber(b) ? b : a - }, - doSetSurfaceRect: function(b, c) { - var a = this.getMainRect(); - b.setRect(c); - b.matrix.set(1, 0, 0, 1, a[0] - c[0], a[1] - c[1]); - b.inverseMatrix.set(1, 0, 0, 1, c[0] - a[0], c[1] - a[1]) - }, - applyAxes: function(f, h) { - var e = this, - g = Ext.Array.from(e.config.series)[0], - b, d, c, a; - if (g.type === "radar" && f && f.length) { - for (b = 0, d = f.length; b < d; b++) { - c = f[b]; - if (c.position === "angular") { - a = true; - break - } - } - if (!a) { - f.push({ - type: "category", - position: "angular", - fields: g.xField || g.angleField, - style: { - estStepSize: 1 - }, - grid: true - }) - } - } - return this.callParent(arguments) - }, - performLayout: function() { - var F = this, - g = true; - try { - F.animationSuspendCount++; - if (this.callParent() === false) { - g = false; - return - } - F.suspendThicknessChanged(); - var h = F.getSurface("chart").getRect(), - v = F.getInsetPadding(), - G = F.getInnerPadding(), - l = Ext.apply({}, v), - d, s = h[2] - v.left - v.right, - r = h[3] - v.top - v.bottom, - x = [v.left, v.top, s, r], - u = F.getSeries(), - p, t = s - G * 2, - w = r - G * 2, - D = [t * 0.5 + G, w * 0.5 + G], - j = Math.min(t, w) * 0.5, - A = F.getAxes(), - f, a, k, m = [], - o = [], - E = j - G, - z, n, b, q, y, c, C; - F.setMainRect(x); - F.doSetSurfaceRect(F.getSurface(), x); - for (z = 0, n = F.surfaceMap.grid && F.surfaceMap.grid.length; z < n; z++) { - F.doSetSurfaceRect(F.surfaceMap.grid[z], h) - } - for (z = 0, n = A.length; z < n; z++) { - f = A[z]; - switch (f.getPosition()) { - case "angular": - m.push(f); - break; - case "radial": - o.push(f); - break - } - } - for (z = 0, n = m.length; z < n; z++) { - f = m[z]; - q = f.getFloating(); - y = q ? q.value : null; - F.doSetSurfaceRect(f.getSurface(), h); - a = f.getThickness(); - for (d in l) { - l[d] += a - } - s = h[2] - l.left - l.right; - r = h[3] - l.top - l.bottom; - b = Math.min(s, r) * 0.5; - if (z === 0) { - E = b - G - } - f.setMinimum(0); - f.setLength(b); - f.getSprites(); - k = f.sprites[0].attr.lineWidth * 0.5; - for (d in l) { - l[d] += k - } - } - for (z = 0, n = o.length; z < n; z++) { - f = o[z]; - F.doSetSurfaceRect(f.getSurface(), h); - f.setMinimum(0); - f.setLength(E); - f.getSprites() - } - for (z = 0, n = u.length; z < n; z++) { - p = u[z]; - if (p.type === "gauge" && !c) { - c = p - } else { - p.setRadius(E) - } - F.doSetSurfaceRect(p.getSurface(), x) - } - F.doSetSurfaceRect(F.getSurface("overlay"), h); - if (c) { - c.setRect(x); - C = c.getRadius() - G; - F.setRadius(C); - F.setCenter(c.getCenter()); - c.setRadius(C); - if (A.length && A[0].getPosition() === "gauge") { - f = A[0]; - F.doSetSurfaceRect(f.getSurface(), h); - f.setTotalAngle(c.getTotalAngle()); - f.setLength(C) - } - } else { - F.setRadius(j); - F.setCenter(D) - } - F.redraw() - } catch (B) { - throw B - } finally { - F.animationSuspendCount--; - if (g) { - F.resumeThicknessChanged() - } - } - }, - refloatAxes: function() { - var j = this, - g = j.getAxes(), - h = j.getMainRect(), - f, k, b, d, a, c, e; - if (!h) { - return - } - e = 0.5 * Math.min(h[2], h[3]); - for (d = 0, a = g.length; d < a; d++) { - c = g[d]; - f = c.getFloating(); - k = f ? f.value : null; - if (k !== null) { - b = j.getAxis(f.alongAxis); - if (c.getPosition() === "angular") { - if (b) { - k = b.getLength() * k / b.getRange()[1] - } else { - k = 0.01 * k * e - } - c.sprites[0].setAttributes({ - length: k - }, true) - } else { - if (b) { - if (Ext.isString(k)) { - k = b.getCoordFor(k) - } - k = k / (b.getRange()[1] + 1) * Math.PI * 2 - Math.PI * 1.5 + c.getRotation() - } else { - k = Ext.draw.Draw.rad(k) - } - c.sprites[0].setAttributes({ - baseRotation: k - }, true) - } - } - } - }, - redraw: function() { - var f = this, - g = f.getAxes(), - d, c = f.getSeries(), - b, a, e; - for (a = 0, e = g.length; a < e; a++) { - d = g[a]; - d.getSprites() - } - for (a = 0, e = c.length; a < e; a++) { - b = c[a]; - b.getSprites() - } - f.renderFrame(); - f.callParent(arguments) - }, - renderFrame: function() { - this.refloatAxes(); - this.callParent() - } -}); -Ext.define("Ext.chart.SpaceFillingChart", { - extend: "Ext.chart.AbstractChart", - xtype: "spacefilling", - config: {}, - performLayout: function() { - var j = this; - try { - j.animationSuspendCount++; - if (j.callParent() === false) { - return - } - var k = j.getSurface("chart").getRect(), - l = j.getInsetPadding(), - a = k[2] - l.left - l.right, - m = k[3] - l.top - l.bottom, - h = [l.left, l.top, a, m], - b = j.getSeries(), - d, c, g; - j.getSurface().setRect(h); - j.setMainRect(h); - for (c = 0, g = b.length; c < g; c++) { - d = b[c]; - d.getSurface().setRect(h); - if (d.setRect) { - d.setRect(h) - } - d.getOverlaySurface().setRect(k) - } - j.redraw() - } catch (f) { - throw f - } finally { - j.animationSuspendCount-- - } - }, - redraw: function() { - var e = this, - c = e.getSeries(), - b, a, d; - for (a = 0, d = c.length; a < d; a++) { - b = c[a]; - b.getSprites() - } - e.renderFrame(); - e.callParent(arguments) - } -}); -Ext.define("Ext.chart.axis.sprite.Axis3D", { - extend: "Ext.chart.axis.sprite.Axis", - alias: "sprite.axis3d", - type: "axis3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - }, - triggers: { - depth: "layout" - } - } - }, - config: { - fx: { - customDurations: { - depth: 0 - } - } - }, - layoutUpdater: function() { - var h = this, - f = h.getAxis().getChart(); - if (f.isInitializing) { - return - } - var e = h.attr, - d = h.getLayout(), - c = d.isDiscrete ? 0 : e.depth, - g = f.getInherited().rtl, - b = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMin, - i = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMax, - a = { - attr: e, - segmenter: h.getSegmenter(), - renderer: h.defaultRenderer - }; - if (e.position === "left" || e.position === "right") { - e.translationX = 0; - e.translationY = i * (e.length - c) / (i - b) + c; - e.scalingX = 1; - e.scalingY = (-e.length + c) / (i - b); - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } else { - if (e.position === "top" || e.position === "bottom") { - if (g) { - e.translationX = e.length + b * e.length / (i - b) + 1 - } else { - e.translationX = -b * e.length / (i - b) - } - e.translationY = 0; - e.scalingX = (g ? -1 : 1) * (e.length - c) / (i - b); - e.scalingY = 1; - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } - } - if (d) { - d.calculateLayout(a); - h.setLayoutContext(a) - } - }, - renderAxisLine: function(a, j, f, c) { - var i = this, - h = i.attr, - b = h.lineWidth * 0.5, - f = i.getLayout(), - d = f.isDiscrete ? 0 : h.depth, - k = h.position, - e, g; - if (h.axisLine && h.length) { - switch (k) { - case "left": - e = a.roundPixel(c[2]) - b; - j.moveTo(e, -h.endGap + d); - j.lineTo(e, h.length + h.startGap); - break; - case "right": - j.moveTo(b, -h.endGap); - j.lineTo(b, h.length + h.startGap); - break; - case "bottom": - j.moveTo(-h.startGap, b); - j.lineTo(h.length - d + h.endGap, b); - break; - case "top": - e = a.roundPixel(c[3]) - b; - j.moveTo(-h.startGap, e); - j.lineTo(h.length + h.endGap, e); - break; - case "angular": - j.moveTo(h.centerX + h.length, h.centerY); - j.arc(h.centerX, h.centerY, h.length, 0, Math.PI * 2, true); - break; - case "gauge": - g = i.getGaugeAngles(); - j.moveTo(h.centerX + Math.cos(g.start) * h.length, h.centerY + Math.sin(g.start) * h.length); - j.arc(h.centerX, h.centerY, h.length, g.start, g.end, true); - break - } - } - } -}); -Ext.define("Ext.chart.axis.Axis3D", { - extend: "Ext.chart.axis.Axis", - xtype: "axis3d", - requires: ["Ext.chart.axis.sprite.Axis3D"], - config: { - depth: 0 - }, - onSeriesChange: function(e) { - var g = this, - b = "depthchange", - f = "onSeriesDepthChange", - d, c; - - function a(h) { - var i = g.boundSeries; - for (d = 0; d < i.length; d++) { - c = i[d]; - c[h](b, f, g) - } - } - a("un"); - g.callParent(arguments); - a("on") - }, - onSeriesDepthChange: function(b, f) { - var d = this, - g = f, - e = d.boundSeries, - a, c; - if (f > d.getDepth()) { - g = f - } else { - for (a = 0; a < e.length; a++) { - c = e[a]; - if (c !== b && c.getDepth) { - f = c.getDepth(); - if (f > g) { - g = f - } - } - } - } - d.setDepth(g) - }, - updateDepth: function(d) { - var b = this, - c = b.getSprites(), - a = { - depth: d - }; - if (c && c.length) { - c[0].setAttributes(a) - } - if (b.gridSpriteEven && b.gridSpriteOdd) { - b.gridSpriteEven.getTemplate().setAttributes(a); - b.gridSpriteOdd.getTemplate().setAttributes(a) - } - }, - getGridAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "horizontal3d"; - case "top": - case "bottom": - return "vertical3d" - } - } -}); -Ext.define("Ext.chart.axis.Category", { - requires: ["Ext.chart.axis.layout.CombineDuplicate", "Ext.chart.axis.segmenter.Names"], - extend: "Ext.chart.axis.Axis", - alias: "axis.category", - type: "category", - config: { - layout: "combineDuplicate", - segmenter: "names" - } -}); -Ext.define("Ext.chart.axis.Category3D", { - requires: ["Ext.chart.axis.layout.CombineDuplicate", "Ext.chart.axis.segmenter.Names"], - extend: "Ext.chart.axis.Axis3D", - alias: "axis.category3d", - type: "category3d", - config: { - layout: "combineDuplicate", - segmenter: "names" - } -}); -Ext.define("Ext.chart.axis.Numeric", { - extend: "Ext.chart.axis.Axis", - type: "numeric", - alias: ["axis.numeric", "axis.radial"], - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Numeric"], - config: { - layout: "continuous", - segmenter: "numeric", - aggregator: "double" - } -}); -Ext.define("Ext.chart.axis.Numeric3D", { - extend: "Ext.chart.axis.Axis3D", - alias: ["axis.numeric3d"], - type: "numeric3d", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Numeric"], - config: { - layout: "continuous", - segmenter: "numeric", - aggregator: "double" - } -}); -Ext.define("Ext.chart.axis.Time", { - extend: "Ext.chart.axis.Numeric", - alias: "axis.time", - type: "time", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Time"], - config: { - calculateByLabelSize: true, - dateFormat: null, - fromDate: null, - toDate: null, - step: [Ext.Date.DAY, 1], - layout: "continuous", - segmenter: "time", - aggregator: "time" - }, - updateDateFormat: function(a) { - this.setRenderer(function(c, b) { - return Ext.Date.format(new Date(b), a) - }) - }, - updateFromDate: function(a) { - this.setMinimum(+a) - }, - updateToDate: function(a) { - this.setMaximum(+a) - }, - getCoordFor: function(a) { - if (Ext.isString(a)) { - a = new Date(a) - } - return +a - } -}); -Ext.define("Ext.chart.axis.Time3D", { - extend: "Ext.chart.axis.Numeric3D", - alias: "axis.time3d", - type: "time3d", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Time"], - config: { - calculateByLabelSize: true, - dateFormat: null, - fromDate: null, - toDate: null, - step: [Ext.Date.DAY, 1], - layout: "continuous", - segmenter: "time", - aggregator: "time" - }, - updateDateFormat: function(a) { - this.setRenderer(function(c, b) { - return Ext.Date.format(new Date(b), a) - }) - }, - updateFromDate: function(a) { - this.setMinimum(+a) - }, - updateToDate: function(a) { - this.setMaximum(+a) - }, - getCoordFor: function(a) { - if (Ext.isString(a)) { - a = new Date(a) - } - return +a - } -}); -Ext.define("Ext.chart.grid.HorizontalGrid3D", { - extend: "Ext.chart.grid.HorizontalGrid", - alias: "grid.horizontal3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - } - } - }, - render: function(a, k, d) { - var f = this.attr, - i = a.roundPixel(f.x), - h = a.roundPixel(f.y), - l = a.matrix.getDX(), - c = k.lineWidth * 0.5, - j = f.height, - e = f.depth, - b, g; - if (h <= d[1]) { - return - } - b = d[0] + e - l; - g = h + c - e; - k.beginPath(); - k.rect(b, g, d[2], j); - k.fill(); - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + d[2], g); - k.stroke(); - b = d[0] + i - l; - g = h + c; - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + e, g - e); - k.lineTo(b + e, g - e + j); - k.lineTo(b, g + j); - k.closePath(); - k.fill(); - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + e, g - e); - k.stroke() - } -}); -Ext.define("Ext.chart.grid.VerticalGrid3D", { - extend: "Ext.chart.grid.VerticalGrid", - alias: "grid.vertical3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - } - } - }, - render_: function(c, d, f) { - var b = this.attr, - a = c.roundPixel(b.x), - e = d.lineWidth * 0.5; - d.beginPath(); - d.rect(a - e, f[1] - c.matrix.getDY(), b.width, f[3]); - d.fill(); - d.beginPath(); - d.moveTo(a - e, f[1] - c.matrix.getDY()); - d.lineTo(a - e, f[1] + f[3] - c.matrix.getDY()); - d.stroke() - }, - render: function(b, j, e) { - var g = this.attr, - i = b.roundPixel(g.x), - k = b.matrix.getDY(), - d = j.lineWidth * 0.5, - a = g.width, - f = g.depth, - c, h; - if (i >= e[2]) { - return - } - c = i - d + f; - h = e[1] - f - k; - j.beginPath(); - j.rect(c, h, a, e[3]); - j.fill(); - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c, h + e[3]); - j.stroke(); - c = i - d; - h = e[3]; - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c + f, h - f); - j.lineTo(c + f + a, h - f); - j.lineTo(c + a, h); - j.closePath(); - j.fill(); - c = i - d; - h = e[3]; - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c + f, h - f); - j.stroke() - } -}); -Ext.define("Ext.chart.interactions.CrossZoom", { - extend: "Ext.chart.interactions.Abstract", - type: "crosszoom", - alias: "interaction.crosszoom", - isCrossZoom: true, - config: { - axes: true, - gestures: { - dragstart: "onGestureStart", - drag: "onGesture", - dragend: "onGestureEnd", - dblclick: "onDoubleTap" - }, - undoButton: {} - }, - stopAnimationBeforeSync: false, - zoomAnimationInProgress: false, - constructor: function() { - this.callParent(arguments); - this.zoomHistory = [] - }, - applyAxes: function(b) { - var a = {}; - if (b === true) { - return { - top: {}, - right: {}, - bottom: {}, - left: {} - } - } else { - if (Ext.isArray(b)) { - a = {}; - Ext.each(b, function(c) { - a[c] = {} - }) - } else { - if (Ext.isObject(b)) { - Ext.iterate(b, function(c, d) { - if (d === true) { - a[c] = {} - } else { - if (d !== false) { - a[c] = d - } - } - }) - } - } - } - return a - }, - applyUndoButton: function(b, a) { - var c = this; - if (a) { - a.destroy() - } - if (b) { - return Ext.create("Ext.Button", Ext.apply({ - cls: [], - text: "Undo Zoom", - disabled: true, - handler: function() { - c.undoZoom() - } - }, b)) - } - }, - getSurface: function() { - return this.getChart() && this.getChart().getSurface("main") - }, - setSeriesOpacity: function(b) { - var a = this.getChart() && this.getChart().getSurface("series"); - if (a) { - a.element.setStyle("opacity", b) - } - }, - onGestureStart: function(h) { - var j = this, - i = j.getChart(), - d = j.getSurface(), - l = i.getInnerRect(), - c = i.getInnerPadding(), - g = c.left, - b = g + l[2], - f = c.top, - a = f + l[3], - n = i.getEventXY(h), - m = n[0], - k = n[1]; - if (j.zoomAnimationInProgress) { - return - } - if (m > g && m < b && k > f && k < a) { - j.gestureEvent = "drag"; - j.lockEvents(j.gestureEvent); - j.startX = m; - j.startY = k; - j.selectionRect = d.add({ - type: "rect", - globalAlpha: 0.5, - fillStyle: "rgba(80,80,140,0.5)", - strokeStyle: "rgba(80,80,140,1)", - lineWidth: 2, - x: m, - y: k, - width: 0, - height: 0, - zIndex: 10000 - }); - j.setSeriesOpacity(0.8); - return false - } - }, - onGesture: function(h) { - var j = this; - if (j.zoomAnimationInProgress) { - return - } - if (j.getLocks()[j.gestureEvent] === j) { - var i = j.getChart(), - d = j.getSurface(), - l = i.getInnerRect(), - c = i.getInnerPadding(), - g = c.left, - b = g + l[2], - f = c.top, - a = f + l[3], - n = i.getEventXY(h), - m = n[0], - k = n[1]; - if (m < g) { - m = g - } else { - if (m > b) { - m = b - } - } - if (k < f) { - k = f - } else { - if (k > a) { - k = a - } - } - j.selectionRect.setAttributes({ - width: m - j.startX, - height: k - j.startY - }); - if (Math.abs(j.startX - m) < 11 || Math.abs(j.startY - k) < 11) { - j.selectionRect.setAttributes({ - globalAlpha: 0.5 - }) - } else { - j.selectionRect.setAttributes({ - globalAlpha: 1 - }) - } - d.renderFrame(); - return false - } - }, - onGestureEnd: function(i) { - var l = this; - if (l.zoomAnimationInProgress) { - return - } - if (l.getLocks()[l.gestureEvent] === l) { - var k = l.getChart(), - d = l.getSurface(), - n = k.getInnerRect(), - c = k.getInnerPadding(), - g = c.left, - b = g + n[2], - f = c.top, - a = f + n[3], - h = n[2], - j = n[3], - p = k.getEventXY(i), - o = p[0], - m = p[1]; - if (o < g) { - o = g - } else { - if (o > b) { - o = b - } - } - if (m < f) { - m = f - } else { - if (m > a) { - m = a - } - } - if (Math.abs(l.startX - o) < 11 || Math.abs(l.startY - m) < 11) { - d.remove(l.selectionRect) - } else { - l.zoomBy([Math.min(l.startX, o) / h, 1 - Math.max(l.startY, m) / j, Math.max(l.startX, o) / h, 1 - Math.min(l.startY, m) / j]); - l.selectionRect.setAttributes({ - x: Math.min(l.startX, o), - y: Math.min(l.startY, m), - width: Math.abs(l.startX - o), - height: Math.abs(l.startY - m) - }); - l.selectionRect.setAnimation(k.getAnimation() || { - duration: 0 - }); - l.selectionRect.setAttributes({ - globalAlpha: 0, - x: 0, - y: 0, - width: h, - height: j - }); - l.zoomAnimationInProgress = true; - k.suspendThicknessChanged(); - l.selectionRect.fx.on("animationend", function() { - k.resumeThicknessChanged(); - d.remove(l.selectionRect); - l.selectionRect = null; - l.zoomAnimationInProgress = false - }) - } - d.renderFrame(); - l.sync(); - l.unlockEvents(l.gestureEvent); - l.setSeriesOpacity(1); - if (!l.zoomAnimationInProgress) { - d.remove(l.selectionRect); - l.selectionRect = null - } - } - }, - zoomBy: function(o) { - var n = this, - a = n.getAxes(), - k = n.getChart(), - j = k.getAxes(), - l = k.getInherited().rtl, - f, d = {}, - c, b; - if (l) { - o = o.slice(); - c = 1 - o[0]; - b = 1 - o[2]; - o[0] = Math.min(c, b); - o[2] = Math.max(c, b) - } - for (var h = 0; h < j.length; h++) { - var g = j[h]; - f = a[g.getPosition()]; - if (f && f.allowZoom !== false) { - var e = g.isSide(), - m = g.getVisibleRange(); - d[g.getId()] = m.slice(0); - if (!e) { - g.setVisibleRange([(m[1] - m[0]) * o[0] + m[0], (m[1] - m[0]) * o[2] + m[0]]) - } else { - g.setVisibleRange([(m[1] - m[0]) * o[1] + m[0], (m[1] - m[0]) * o[3] + m[0]]) - } - } - } - n.zoomHistory.push(d); - n.getUndoButton().setDisabled(false) - }, - undoZoom: function() { - var c = this.zoomHistory.pop(), - d = this.getChart().getAxes(); - if (c) { - for (var a = 0; a < d.length; a++) { - var b = d[a]; - if (c[b.getId()]) { - b.setVisibleRange(c[b.getId()]) - } - } - } - this.getUndoButton().setDisabled(this.zoomHistory.length === 0); - this.sync() - }, - onDoubleTap: function(a) { - this.undoZoom() - }, - destroy: function() { - this.setUndoButton(null); - this.callParent(arguments) - } -}); -Ext.define("Ext.chart.interactions.Crosshair", { - extend: "Ext.chart.interactions.Abstract", - requires: ["Ext.chart.grid.HorizontalGrid", "Ext.chart.grid.VerticalGrid", "Ext.chart.CartesianChart", "Ext.chart.axis.layout.Discrete"], - type: "crosshair", - alias: "interaction.crosshair", - config: { - axes: { - top: { - label: {}, - rect: {} - }, - right: { - label: {}, - rect: {} - }, - bottom: { - label: {}, - rect: {} - }, - left: { - label: {}, - rect: {} - } - }, - lines: { - horizontal: { - strokeStyle: "black", - lineDash: [5, 5] - }, - vertical: { - strokeStyle: "black", - lineDash: [5, 5] - } - }, - gesture: "drag" - }, - applyAxes: function(b, a) { - return Ext.merge(a || {}, b) - }, - applyLines: function(a, b) { - return Ext.merge(b || {}, a) - }, - updateChart: function(a) { - if (a && !a.isCartesian) { - Ext.raise("Crosshair interaction can only be used on cartesian charts.") - } - this.callParent(arguments) - }, - getGestures: function() { - var a = this, - b = {}; - b[a.getGesture()] = "onGesture"; - b[a.getGesture() + "start"] = "onGestureStart"; - b[a.getGesture() + "end"] = "onGestureEnd"; - return b - }, - onGestureStart: function(N) { - var m = this, - O = m.getChart(), - B = O.getTheme().getAxis(), - A, F = O.getSurface("overlay"), - s = O.getInnerRect(), - n = s[2], - M = s[3], - r = O.getEventXY(N), - D = r[0], - C = r[1], - E = O.getAxes(), - u = m.getAxes(), - h = m.getLines(), - q, v, b, d, k, z, G, L, J, o, I, w, l, f, p, j, t, a, g, H, c, K; - if (D > 0 && D < n && C > 0 && C < M) { - m.lockEvents(m.getGesture()); - H = Ext.apply({ - xclass: "Ext.chart.grid.HorizontalGrid", - x: 0, - y: C, - width: n - }, h.horizontal); - c = Ext.apply({ - xclass: "Ext.chart.grid.VerticalGrid", - x: D, - y: 0, - height: M - }, h.vertical); - m.axesLabels = m.axesLabels || {}; - for (K = 0; K < E.length; K++) { - q = E[K]; - v = q.getSurface(); - b = v.getRect(); - w = q.getSprites()[0]; - d = b[2]; - k = b[3]; - z = q.getPosition(); - G = q.getAlignment(); - t = q.getTitle(); - a = t && t.attr.text !== "" && t.getBBox(); - l = w.attr; - f = w.thickness; - p = l.axisLine ? l.lineWidth : 0; - j = p / 2; - I = Math.max(l.majorTickSize, l.minorTickSize) + p; - L = m.axesLabels[z] = v.add({ - type: "composite" - }); - L.labelRect = L.add(Ext.apply({ - type: "rect", - fillStyle: "white", - x: z === "right" ? p : 0, - y: z === "bottom" ? p : 0, - width: d - p - (G === "vertical" && a ? a.width : 0), - height: k - p - (G === "horizontal" && a ? a.height : 0), - translationX: z === "left" && a ? a.width : 0, - translationY: z === "top" && a ? a.height : 0 - }, u.rect || u[z].rect)); - if (G === "vertical" && !c.strokeStyle) { - c.strokeStyle = l.strokeStyle - } - if (G === "horizontal" && !H.strokeStyle) { - H.strokeStyle = l.strokeStyle - } - A = Ext.merge({}, B.defaults, B[z]); - J = Ext.apply({}, q.config.label, A.label); - o = u.label || u[z].label; - L.labelText = L.add(Ext.apply(J, o, { - type: "text", - x: (function() { - switch (z) { - case "left": - g = a ? a.x + a.width : 0; - return g + (d - g - I) / 2 - j; - case "right": - g = a ? d - a.x : 0; - return I + (d - I - g) / 2 + j; - default: - return 0 - } - })(), - y: (function() { - switch (z) { - case "top": - g = a ? a.y + a.height : 0; - return g + (k - g - I) / 2 - j; - case "bottom": - g = a ? k - a.y : 0; - return I + (k - I - g) / 2 + j; - default: - return 0 - } - })() - })) - } - m.horizontalLine = F.add(H); - m.verticalLine = F.add(c); - return false - } - }, - onGesture: function(G) { - var K = this; - if (K.getLocks()[K.getGesture()] !== K) { - return - } - var u = K.getChart(), - z = u.getSurface("overlay"), - a = Ext.Array.slice(u.getInnerRect()), - r = u.getInnerPadding(), - t = r.left, - q = r.top, - E = a[2], - f = a[3], - d = u.getEventXY(G), - k = d[0], - j = d[1], - D = u.getAxes(), - c, h, m, p, J, w, I, H, s, b, C, g, v, n, l, A, F, o, B; - if (k < 0) { - k = 0 - } else { - if (k > E) { - k = E - } - } - if (j < 0) { - j = 0 - } else { - if (j > f) { - j = f - } - } - k += t; - j += q; - for (B = 0; B < D.length; B++) { - c = D[B]; - h = c.getPosition(); - m = c.getAlignment(); - p = c.getSurface(); - J = c.getSprites()[0]; - w = J.attr.matrix; - C = J.attr.textPadding * 2; - s = K.axesLabels[h]; - I = J.getLayoutContext(); - H = c.getSegmenter(); - if (s) { - if (m === "vertical") { - v = w.getYY(); - l = w.getDY(); - F = (j - l - q) / v; - if (c.getLayout() instanceof Ext.chart.axis.layout.Discrete) { - j = Math.round(F) * v + l + q; - F = H.from(Math.round(F)); - F = J.attr.data[F] - } else { - F = H.from(F) - } - o = H.renderer(F, I); - s.setAttributes({ - translationY: j - q - }); - s.labelText.setAttributes({ - text: o - }); - b = s.labelText.getBBox(); - s.labelRect.setAttributes({ - height: b.height + C, - y: -(b.height + C) / 2 - }); - p.renderFrame() - } else { - g = w.getXX(); - n = w.getDX(); - A = (k - n - t) / g; - if (c.getLayout() instanceof Ext.chart.axis.layout.Discrete) { - k = Math.round(A) * g + n + t; - A = H.from(Math.round(A)); - A = J.attr.data[A] - } else { - A = H.from(A) - } - o = H.renderer(A, I); - s.setAttributes({ - translationX: k - t - }); - s.labelText.setAttributes({ - text: o - }); - b = s.labelText.getBBox(); - s.labelRect.setAttributes({ - width: b.width + C, - x: -(b.width + C) / 2 - }); - p.renderFrame() - } - } - } - K.horizontalLine.setAttributes({ - y: j, - strokeStyle: J.attr.strokeStyle - }); - K.verticalLine.setAttributes({ - x: k, - strokeStyle: J.attr.strokeStyle - }); - z.renderFrame(); - return false - }, - onGestureEnd: function(h) { - var l = this, - k = l.getChart(), - a = k.getSurface("overlay"), - j = k.getAxes(), - c, g, d, b, f; - a.remove(l.verticalLine); - a.remove(l.horizontalLine); - for (f = 0; f < j.length; f++) { - c = j[f]; - g = c.getPosition(); - d = c.getSurface(); - b = l.axesLabels[g]; - if (b) { - delete l.axesLabels[g]; - d.remove(b) - } - d.renderFrame() - } - a.renderFrame(); - l.unlockEvents(l.getGesture()) - } -}); -Ext.define("Ext.chart.interactions.ItemHighlight", { - extend: "Ext.chart.interactions.Abstract", - type: "itemhighlight", - alias: "interaction.itemhighlight", - isItemHighlight: true, - config: { - gestures: { - tap: "onTapGesture", - mousemove: "onMouseMoveGesture", - mousedown: "onMouseDownGesture", - mouseup: "onMouseUpGesture", - mouseleave: "onMouseUpGesture" - }, - sticky: false - }, - stickyHighlightItem: null, - onMouseMoveGesture: function(g) { - var d = this, - h = d.tipItem, - a = g.pointerType === "mouse", - c, f, b; - if (d.getSticky()) { - return true - } - if (d.isDragging) { - if (h && a) { - h.series.hideTooltip(h); - d.tipItem = null - } - } else { - if (!d.stickyHighlightItem) { - c = d.getItemForEvent(g); - b = d.getChart(); - if (c !== b.getHighlightItem()) { - d.highlight(c); - d.sync() - } - if (a) { - if (h && (!c || h.field !== c.field || h.record !== c.record)) { - h.series.hideTooltip(h); - d.tipItem = h = null - } - if (c && (f = c.series.getTooltip())) { - if (f.trackMouse || !h) { - c.series.showTooltip(c, g.getXY()) - } - d.tipItem = c - } - } - return false - } - } - }, - highlight: function(a) { - this.getChart().setHighlightItem(a) - }, - showTooltip: function(b, a) { - a.series.showTooltip(a, b.getXY()); - this.tipItem = a - }, - onMouseDownGesture: function() { - this.isDragging = true - }, - onMouseUpGesture: function() { - this.isDragging = false - }, - onTapGesture: function(c) { - var b = this; - if (c.pointerType === "mouse" && !b.getSticky()) { - return - } - var a = b.getItemForEvent(c); - if (b.stickyHighlightItem && a && (b.stickyHighlightItem.index === a.index)) { - a = null - } - b.stickyHighlightItem = a; - b.highlight(a) - } -}); -Ext.define("Ext.chart.interactions.ItemEdit", { - extend: "Ext.chart.interactions.ItemHighlight", - requires: ["Ext.tip.ToolTip"], - type: "itemedit", - alias: "interaction.itemedit", - isItemEdit: true, - config: { - style: null, - renderer: null, - tooltip: true, - gestures: { - dragstart: "onDragStart", - drag: "onDrag", - dragend: "onDragEnd" - }, - cursors: { - ewResize: "ew-resize", - nsResize: "ns-resize", - move: "move" - } - }, - item: null, - applyTooltip: function(b) { - if (b) { - var a = Ext.apply({}, b, { - renderer: this.defaultTooltipRenderer, - constrainPosition: true, - shrinkWrapDock: true, - autoHide: true, - offsetX: 10, - offsetY: 10 - }); - b = new Ext.tip.ToolTip(a) - } - return b - }, - defaultTooltipRenderer: function(b, a, f, d) { - var c = []; - if (f.xField) { - c.push(f.xField + ": " + f.xValue) - } - if (f.yField) { - c.push(f.yField + ": " + f.yValue) - } - b.setHtml(c.join("
")) - }, - onDragStart: function(d) { - var c = this, - a = c.getChart(), - b = a.getHighlightItem(); - if (b) { - a.fireEvent("beginitemedit", a, c, c.item = b); - return false - } - }, - onDrag: function(f) { - var d = this, - b = d.getChart(), - c = b.getHighlightItem(), - a = c && c.sprite.type; - if (c) { - switch (a) { - case "barSeries": - return d.onDragBar(f); - break; - case "scatterSeries": - return d.onDragScatter(f); - break - } - } - }, - highlight: function(f) { - var e = this, - d = e.getChart(), - a = d.getFlipXY(), - g = e.getCursors(), - c = f && f.sprite.type, - b = d.el.dom.style; - e.callParent([f]); - if (f) { - switch (c) { - case "barSeries": - if (a) { - b.cursor = g.ewResize - } else { - b.cursor = g.nsResize - } - break; - case "scatterSeries": - b.cursor = g.move; - break - } - } else { - d.el.dom.style.cursor = "default" - } - }, - onDragBar: function(i) { - var m = this, - k = m.getChart(), - l = k.getInherited().rtl, - f = k.isCartesian && k.getFlipXY(), - q = k.getHighlightItem(), - g = q.sprite.getMarker("items"), - p = g.getMarkerFor(q.sprite.getId(), q.index), - b = q.sprite.getSurface(), - c = b.getRect(), - r = b.getEventXY(i), - o = q.sprite.attr.matrix, - j = m.getRenderer(), - a, n, d, h; - if (f) { - h = l ? c[2] - r[0] : r[0] - } else { - h = c[3] - r[1] - } - a = { - x: p.x, - y: h, - width: p.width, - height: p.height + (p.y - h), - radius: p.radius, - fillStyle: "none", - lineDash: [4, 4], - zIndex: 100 - }; - Ext.apply(a, m.getStyle()); - if (Ext.isArray(q.series.getYField())) { - h = h - p.y - p.height - } - m.target = { - index: q.index, - yField: q.field, - yValue: (h - o.getDY()) / o.getYY() - }; - d = [k, { - target: m.target, - style: a, - item: q - }]; - n = Ext.callback(j, null, d, 0, k); - if (n) { - Ext.apply(a, n) - } - q.sprite.putMarker("items", a, "itemedit"); - m.showTooltip(i, m.target, q); - b.renderFrame() - }, - onDragScatter: function(n) { - var t = this, - g = t.getChart(), - d = g.getInherited().rtl, - l = g.isCartesian && g.getFlipXY(), - o = g.getHighlightItem(), - b = o.sprite.getMarker("items"), - p = b.getMarkerFor(o.sprite.getId(), o.index), - j = o.sprite.getSurface(), - h = j.getRect(), - a = j.getEventXY(n), - k = o.sprite.attr.matrix, - c = o.series.getXAxis(), - f = c && c.getLayout().isContinuous, - i = t.getRenderer(), - m, u, q, s, r; - if (l) { - r = d ? h[2] - a[0] : a[0] - } else { - r = h[3] - a[1] - } - if (f) { - if (l) { - s = h[3] - a[1] - } else { - s = a[0] - } - } else { - s = p.translationX - } - m = { - translationX: s, - translationY: r, - scalingX: p.scalingX, - scalingY: p.scalingY, - r: p.r, - fillStyle: "none", - lineDash: [4, 4], - zIndex: 100 - }; - Ext.apply(m, t.getStyle()); - t.target = { - index: o.index, - yField: o.field, - yValue: (r - k.getDY()) / k.getYY() - }; - if (f) { - Ext.apply(t.target, { - xField: o.series.getXField(), - xValue: (s - k.getDX()) / k.getXX() - }) - } - q = [g, { - target: t.target, - style: m, - item: o - }]; - u = Ext.callback(i, null, q, 0, g); - if (u) { - Ext.apply(m, u) - } - o.sprite.putMarker("items", m, "itemedit"); - t.showTooltip(n, t.target, o); - j.renderFrame() - }, - showTooltip: function(g, f, c) { - var d = this.getTooltip(), - a, b; - if (d && Ext.toolkit !== "modern") { - a = d.config; - b = this.getChart(); - Ext.callback(a.renderer, null, [d, c, f, g], 0, b); - d.show([g.x + a.offsetX, g.y + a.offsetY]) - } - }, - hideTooltip: function() { - var a = this.getTooltip(); - if (a && Ext.toolkit !== "modern") { - a.hide() - } - }, - onDragEnd: function(g) { - var d = this, - f = d.target, - c = d.getChart(), - b = c.getStore(), - a; - if (f) { - a = b.getAt(f.index); - if (f.yField) { - a.set(f.yField, f.yValue, { - convert: false - }) - } - if (f.xField) { - a.set(f.xField, f.xValue, { - convert: false - }) - } - if (f.yField || f.xField) { - d.getChart().onDataChanged() - } - d.target = null - } - d.hideTooltip(); - if (d.item) { - c.fireEvent("enditemedit", c, d, d.item, f) - } - d.highlight(d.item = null) - }, - destroy: function() { - var a = this.getConfig("tooltip", true); - Ext.destroy(a); - this.callParent() - } -}); -Ext.define("Ext.chart.interactions.PanZoom", { - extend: "Ext.chart.interactions.Abstract", - type: "panzoom", - alias: "interaction.panzoom", - requires: ["Ext.draw.Animator"], - config: { - axes: { - top: {}, - right: {}, - bottom: {}, - left: {} - }, - minZoom: null, - maxZoom: null, - showOverflowArrows: true, - panGesture: "drag", - zoomGesture: "pinch", - zoomOnPanGesture: false, - modeToggleButton: { - xtype: "segmentedbutton", - width: 200, - defaults: { - ui: "default-toolbar" - }, - cls: Ext.baseCSSPrefix + "panzoom-toggle", - items: [{ - text: "Pan" - }, { - text: "Zoom" - }] - }, - hideLabelInGesture: false - }, - stopAnimationBeforeSync: true, - applyAxes: function(b, a) { - return Ext.merge(a || {}, b) - }, - applyZoomOnPanGesture: function(a) { - this.getChart(); - if (this.isMultiTouch()) { - return false - } - return a - }, - updateZoomOnPanGesture: function(b) { - var a = this.getModeToggleButton(); - if (!this.isMultiTouch()) { - a.show(); - a.setValue(b ? 1 : 0) - } else { - a.hide() - } - }, - toggleMode: function() { - var a = this; - if (!a.isMultiTouch()) { - a.setZoomOnPanGesture(!a.getZoomOnPanGesture()) - } - }, - applyModeToggleButton: function(c, b) { - var d = this, - a = Ext.factory(c, "Ext.button.Segmented", b); - if (!a && b) { - b.destroy() - } - if (a && !b) { - a.addListener("toggle", function(e) { - d.setZoomOnPanGesture(e.getValue() === 1) - }) - } - return a - }, - getGestures: function() { - var c = this, - e = {}, - d = c.getPanGesture(), - b = c.getZoomGesture(), - a = Ext.supports.Touch; - e[b] = "onZoomGestureMove"; - e[b + "start"] = "onZoomGestureStart"; - e[b + "end"] = "onZoomGestureEnd"; - e[d] = "onPanGestureMove"; - e[d + "start"] = "onPanGestureStart"; - e[d + "end"] = "onPanGestureEnd"; - e.doubletap = "onDoubleTap"; - return e - }, - onDoubleTap: function(h) { - var f = this, - c = f.getChart(), - g = c.getAxes(), - b, a, d; - for (a = 0, d = g.length; a < d; a++) { - b = g[a]; - b.setVisibleRange([0, 1]) - } - c.redraw() - }, - onPanGestureStart: function(d) { - if (!d || !d.touches || d.touches.length < 2) { - var b = this, - a = b.getChart().getInnerRect(), - c = b.getChart().element.getXY(); - b.startX = d.getX() - c[0] - a[0]; - b.startY = d.getY() - c[1] - a[1]; - b.oldVisibleRanges = null; - b.hideLabels(); - b.getChart().suspendThicknessChanged(); - b.lockEvents(b.getPanGesture()); - return false - } - }, - onPanGestureMove: function(d) { - var b = this; - if (b.getLocks()[b.getPanGesture()] === b) { - var a = b.getChart().getInnerRect(), - c = b.getChart().element.getXY(); - if (b.getZoomOnPanGesture()) { - b.transformAxesBy(b.getZoomableAxes(d), 0, 0, (d.getX() - c[0] - a[0]) / b.startX, b.startY / (d.getY() - c[1] - a[1])) - } else { - b.transformAxesBy(b.getPannableAxes(d), d.getX() - c[0] - a[0] - b.startX, d.getY() - c[1] - a[1] - b.startY, 1, 1) - } - b.sync(); - return false - } - }, - onPanGestureEnd: function(b) { - var a = this, - c = a.getPanGesture(); - if (a.getLocks()[c] === a) { - a.getChart().resumeThicknessChanged(); - a.showLabels(); - a.sync(); - a.unlockEvents(c); - return false - } - }, - onZoomGestureStart: function(b) { - if (b.touches && b.touches.length === 2) { - var c = this, - i = c.getChart().element.getXY(), - f = c.getChart().getInnerRect(), - h = i[0] + f[0], - d = i[1] + f[1], - j = [b.touches[0].point.x - h, b.touches[0].point.y - d, b.touches[1].point.x - h, b.touches[1].point.y - d], - g = Math.max(44, Math.abs(j[2] - j[0])), - a = Math.max(44, Math.abs(j[3] - j[1])); - c.getChart().suspendThicknessChanged(); - c.lastZoomDistances = [g, a]; - c.lastPoints = j; - c.oldVisibleRanges = null; - c.hideLabels(); - c.lockEvents(c.getZoomGesture()); - return false - } - }, - onZoomGestureMove: function(d) { - var f = this; - if (f.getLocks()[f.getZoomGesture()] === f) { - var i = f.getChart().getInnerRect(), - n = f.getChart().element.getXY(), - k = n[0] + i[0], - h = n[1] + i[1], - o = Math.abs, - c = f.lastPoints, - m = [d.touches[0].point.x - k, d.touches[0].point.y - h, d.touches[1].point.x - k, d.touches[1].point.y - h], - g = Math.max(44, o(m[2] - m[0])), - b = Math.max(44, o(m[3] - m[1])), - a = this.lastZoomDistances || [g, b], - l = g / a[0], - j = b / a[1]; - f.transformAxesBy(f.getZoomableAxes(d), i[2] * (l - 1) / 2 + m[2] - c[2] * l, i[3] * (j - 1) / 2 + m[3] - c[3] * j, l, j); - f.sync(); - return false - } - }, - onZoomGestureEnd: function(c) { - var b = this, - a = b.getZoomGesture(); - if (b.getLocks()[a] === b) { - b.getChart().resumeThicknessChanged(); - b.showLabels(); - b.sync(); - b.unlockEvents(a); - return false - } - }, - hideLabels: function() { - if (this.getHideLabelInGesture()) { - this.eachInteractiveAxes(function(a) { - a.hideLabels() - }) - } - }, - showLabels: function() { - if (this.getHideLabelInGesture()) { - this.eachInteractiveAxes(function(a) { - a.showLabels() - }) - } - }, - isEventOnAxis: function(c, a) { - var b = a.getSurface().getRect(); - return b[0] <= c.getX() && c.getX() <= b[0] + b[2] && b[1] <= c.getY() && c.getY() <= b[1] + b[3] - }, - getPannableAxes: function(d) { - var h = this, - a = h.getAxes(), - f = h.getChart().getAxes(), - c, g = f.length, - k = [], - j = false, - b; - if (d) { - for (c = 0; c < g; c++) { - if (this.isEventOnAxis(d, f[c])) { - j = true; - break - } - } - } - for (c = 0; c < g; c++) { - b = a[f[c].getPosition()]; - if (b && b.allowPan !== false && (!j || this.isEventOnAxis(d, f[c]))) { - k.push(f[c]) - } - } - return k - }, - getZoomableAxes: function(f) { - var j = this, - a = j.getAxes(), - g = j.getChart().getAxes(), - l = [], - d, h = g.length, - c, k = false, - b; - if (f) { - for (d = 0; d < h; d++) { - if (this.isEventOnAxis(f, g[d])) { - k = true; - break - } - } - } - for (d = 0; d < h; d++) { - c = g[d]; - b = a[c.getPosition()]; - if (b && b.allowZoom !== false && (!k || this.isEventOnAxis(f, c))) { - l.push(c) - } - } - return l - }, - eachInteractiveAxes: function(c) { - var d = this, - b = d.getAxes(), - e = d.getChart().getAxes(); - for (var a = 0; a < e.length; a++) { - if (b[e[a].getPosition()]) { - if (false === c.call(this, e[a])) { - return - } - } - } - }, - transformAxesBy: function(d, j, g, h, e) { - var f = this.getChart().getInnerRect(), - a = this.getAxes(), - k, b = this.oldVisibleRanges, - l = false; - if (!b) { - this.oldVisibleRanges = b = {}; - this.eachInteractiveAxes(function(i) { - b[i.getId()] = i.getVisibleRange() - }) - } - if (!f) { - return - } - for (var c = 0; c < d.length; c++) { - k = a[d[c].getPosition()]; - l = this.transformAxisBy(d[c], b[d[c].getId()], j, g, h, e, this.minZoom || k.minZoom, this.maxZoom || k.maxZoom) || l - } - return l - }, - transformAxisBy: function(c, o, r, q, k, i, h, m) { - var s = this, - b = o[1] - o[0], - l = c.getVisibleRange(), - g = h || s.getMinZoom() || c.config.minZoom, - j = m || s.getMaxZoom() || c.config.maxZoom, - a = s.getChart().getInnerRect(), - f, p; - if (!a) { - return - } - var d = c.isSide(), - e = d ? a[3] : a[2], - n = d ? -q : r; - b /= d ? i : k; - if (b < 0) { - b = -b - } - if (b * g > 1) { - b = 1 - } - if (b * j < 1) { - b = 1 / j - } - f = o[0]; - p = o[1]; - l = l[1] - l[0]; - if (b === l && l === 1) { - return - } - c.setVisibleRange([(o[0] + o[1] - b) * 0.5 - n / e * b, (o[0] + o[1] + b) * 0.5 - n / e * b]); - return (Math.abs(f - c.getVisibleRange()[0]) > 1e-10 || Math.abs(p - c.getVisibleRange()[1]) > 1e-10) - }, - destroy: function() { - this.setModeToggleButton(null); - this.callParent() - } -}); -Ext.define("Ext.chart.interactions.Rotate", { - extend: "Ext.chart.interactions.Abstract", - type: "rotate", - alias: "interaction.rotate", - config: { - gesture: "rotate", - gestures: { - rotate: "onRotate", - rotateend: "onRotate", - dragstart: "onGestureStart", - drag: "onGesture", - dragend: "onGestureEnd" - }, - rotation: 0 - }, - oldRotations: null, - getAngle: function(f) { - var c = this, - b = c.getChart(), - d = b.getEventXY(f), - a = b.getCenter(); - return Math.atan2(d[1] - a[1], d[0] - a[0]) - }, - getRadius: function(a) { - return this.getChart().getRadius() - }, - getEventRadius: function(h) { - var f = this, - d = f.getChart(), - g = d.getEventXY(h), - a = d.getCenter(), - c = g[0] - a[0], - b = g[1] - a[1]; - return Math.sqrt(c * c + b * b) - }, - onGestureStart: function(d) { - var c = this, - b = c.getRadius(d), - a = c.getEventRadius(d); - if (b >= a) { - c.lockEvents("drag"); - c.angle = c.getAngle(d); - c.oldRotations = {}; - return false - } - }, - onGesture: function(b) { - var a = this, - c = a.getAngle(b) - a.angle; - if (a.getLocks().drag === a) { - a.doRotateTo(c, true); - return false - } - }, - doRotateTo: function(d, a, b) { - var n = this, - l = n.getChart(), - k = l.getAxes(), - f = l.getSeries(), - m = n.oldRotations, - c, j, g, e, h; - if (!b) { - l.suspendAnimation() - } - for (e = 0, h = k.length; e < h; e++) { - c = k[e]; - g = m[c.getId()] || (m[c.getId()] = c.getRotation()); - c.setRotation(d + (a ? g : 0)) - } - for (e = 0, h = f.length; e < h; e++) { - j = f[e]; - g = m[j.getId()] || (m[j.getId()] = j.getRotation()); - j.setRotation(d + (a ? g : 0)) - } - n.setRotation(d + (a ? g : 0)); - n.fireEvent("rotate", n, n.getRotation()); - n.sync(); - if (!b) { - l.resumeAnimation() - } - }, - rotateTo: function(c, b, a) { - this.doRotateTo(c, b, a); - this.oldRotations = {} - }, - onGestureEnd: function(b) { - var a = this; - if (a.getLocks().drag === a) { - a.onGesture(b); - a.unlockEvents("drag"); - a.fireEvent("rotationEnd", a, a.getRotation()); - return false - } - }, - onRotate: function(a) {} -}); -Ext.define("Ext.chart.interactions.RotatePie3D", { - extend: "Ext.chart.interactions.Rotate", - type: "rotatePie3d", - alias: "interaction.rotatePie3d", - getAngle: function(g) { - var a = this.getChart(), - f = a.getInherited().rtl, - d = f ? -1 : 1, - h = g.getXY(), - c = a.element.getXY(), - b = a.getMainRect(); - return d * Math.atan2(h[1] - c[1] - b[3] * 0.5, h[0] - c[0] - b[2] * 0.5) - }, - getRadius: function(j) { - var f = this.getChart(), - a = f.getRadius(), - d = f.getSeries(), - h = d.length, - c = 0, - b, g; - for (; c < h; c++) { - b = d[c]; - if (b.isPie3D) { - g = b.getRadius(); - if (g > a) { - a = g - } - } - } - return a - } -}); -Ext.define("Ext.chart.plugin.ItemEvents", { - extend: "Ext.plugin.Abstract", - alias: "plugin.chartitemevents", - moveEvents: false, - mouseMoveEvents: { - mousemove: true, - mouseover: true, - mouseout: true - }, - itemMouseMoveEvents: { - itemmousemove: true, - itemmouseover: true, - itemmouseout: true - }, - init: function(b) { - var a = "handleEvent"; - this.chart = b; - b.addElementListener({ - click: a, - dblclick: a, - mousedown: a, - mousemove: a, - mouseup: a, - mouseover: a, - mouseout: a, - priority: 1001, - scope: this - }) - }, - hasItemMouseMoveListeners: function() { - var b = this.chart.hasListeners, - a; - for (a in this.itemMouseMoveEvents) { - if (a in b) { - return true - } - } - return false - }, - handleEvent: function(g) { - var d = this, - a = d.chart, - h = g.type in d.mouseMoveEvents, - c = d.lastItem, - f, b; - if (h && !d.hasItemMouseMoveListeners() && !d.moveEvents) { - return - } - f = a.getEventXY(g); - b = a.getItemForPoint(f[0], f[1]); - if (h && !Ext.Object.equals(b, c)) { - if (c) { - a.fireEvent("itemmouseout", a, c, g); - c.series.fireEvent("itemmouseout", c.series, c, g) - } - if (b) { - a.fireEvent("itemmouseover", a, b, g); - b.series.fireEvent("itemmouseover", b.series, b, g) - } - } - if (b) { - a.fireEvent("item" + g.type, a, b, g); - b.series.fireEvent("item" + g.type, b.series, b, g) - } - d.lastItem = b - } -}); -Ext.define("Ext.chart.series.Cartesian", { - extend: "Ext.chart.series.Series", - config: { - xField: null, - yField: null, - xAxis: null, - yAxis: null - }, - directions: ["X", "Y"], - fieldCategoryX: ["X"], - fieldCategoryY: ["Y"], - applyXAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - applyYAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - updateXAxis: function(a) { - a.processData(this) - }, - updateYAxis: function(a) { - a.processData(this) - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - getItemForPoint: function(a, g) { - if (this.getSprites()) { - var f = this, - d = f.getSprites()[0], - b = f.getStore(), - e, c; - if (f.getHidden()) { - return null - } - if (d) { - c = d.getIndexNearPoint(a, g); - if (c !== -1) { - e = { - series: f, - category: f.getItemInstancing() ? "items" : "markers", - index: c, - record: b.getData().items[c], - field: f.getYField(), - sprite: d - }; - return e - } - } - } - }, - createSprite: function() { - var c = this, - a = c.callParent(), - b = c.getChart(), - d = c.getXAxis(); - a.setAttributes({ - flipXY: b.getFlipXY(), - xAxis: d - }); - if (a.setAggregator && d && d.getAggregator) { - if (d.getAggregator) { - a.setAggregator({ - strategy: d.getAggregator() - }) - } else { - a.setAggregator({}) - } - } - return a - }, - getSprites: function() { - var d = this, - c = this.getChart(), - e = d.getAnimation() || c && c.getAnimation(), - b = d.getItemInstancing(), - f = d.sprites, - a; - if (!c) { - return [] - } - if (!f.length) { - a = d.createSprite() - } else { - a = f[0] - } - if (e) { - if (b) { - a.itemsMarker.getTemplate().setAnimation(e) - } - a.setAnimation(e) - } - return f - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getSubStyleWithTheme(), - c = a.fillStyle; - if (Ext.isArray(c)) { - c = c[0] - } - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - }, - getXRange: function() { - return [this.dataRange[0], this.dataRange[2]] - }, - getYRange: function() { - return [this.dataRange[1], this.dataRange[3]] - } -}); -Ext.define("Ext.chart.series.StackedCartesian", { - extend: "Ext.chart.series.Cartesian", - config: { - stacked: true, - splitStacks: true, - fullStack: false, - fullStackTotal: 100, - hidden: [] - }, - spriteAnimationCount: 0, - themeColorCount: function() { - var b = this, - a = b.getYField(); - return Ext.isArray(a) ? a.length : 1 - }, - updateStacked: function() { - this.processData() - }, - updateSplitStacks: function() { - this.processData() - }, - coordinateY: function() { - return this.coordinateStacked("Y", 1, 2) - }, - coordinateStacked: function(D, e, m) { - var F = this, - f = F.getStore(), - r = f.getData().items, - B = r.length, - c = F["get" + D + "Axis"](), - x = F.getHidden(), - a = F.getSplitStacks(), - z = F.getFullStack(), - l = F.getFullStackTotal(), - p = { - min: 0, - max: 0 - }, - n = F["fieldCategory" + D], - C = [], - o = [], - E = [], - h, A = F.getStacked(), - g = F.getSprites(), - q = [], - w, v, u, s, H, y, b, d, G, t; - if (!g.length) { - return - } - for (w = 0; w < n.length; w++) { - d = n[w]; - s = F.getFields([d]); - H = s.length; - for (v = 0; v < B; v++) { - C[v] = 0; - o[v] = 0; - E[v] = 0 - } - for (v = 0; v < H; v++) { - if (!x[v]) { - q[v] = F.coordinateData(r, s[v], c) - } - } - if (A && z) { - y = []; - if (a) { - b = [] - } - for (v = 0; v < B; v++) { - y[v] = 0; - if (a) { - b[v] = 0 - } - for (u = 0; u < H; u++) { - G = q[u]; - if (!G) { - continue - } - G = G[v]; - if (G >= 0 || !a) { - y[v] += G - } else { - if (G < 0) { - b[v] += G - } - } - } - } - } - for (v = 0; v < H; v++) { - t = {}; - if (x[v]) { - t["dataStart" + d] = C; - t["data" + d] = C; - g[v].setAttributes(t); - continue - } - G = q[v]; - if (A) { - h = []; - for (u = 0; u < B; u++) { - if (!G[u]) { - G[u] = 0 - } - if (G[u] >= 0 || !a) { - if (z && y[u]) { - G[u] *= l / y[u] - } - C[u] = o[u]; - o[u] += G[u]; - h[u] = o[u] - } else { - if (z && b[u]) { - G[u] *= l / b[u] - } - C[u] = E[u]; - E[u] += G[u]; - h[u] = E[u] - } - } - t["dataStart" + d] = C; - t["data" + d] = h; - F.getRangeOfData(C, p); - F.getRangeOfData(h, p) - } else { - t["dataStart" + d] = C; - t["data" + d] = G; - F.getRangeOfData(G, p) - } - g[v].setAttributes(t) - } - } - F.dataRange[e] = p.min; - F.dataRange[e + m] = p.max; - t = {}; - t["dataMin" + D] = p.min; - t["dataMax" + D] = p.max; - for (w = 0; w < g.length; w++) { - g[w].setAttributes(t) - } - }, - getFields: function(f) { - var e = this, - a = [], - c, b, d; - for (b = 0, d = f.length; b < d; b++) { - c = e["get" + f[b] + "Field"](); - if (Ext.isArray(c)) { - a.push.apply(a, c) - } else { - a.push(c) - } - } - return a - }, - updateLabelOverflowPadding: function(a) { - this.getLabel().setAttributes({ - labelOverflowPadding: a - }) - }, - getSprites: function() { - var k = this, - j = k.getChart(), - c = k.getAnimation() || j && j.getAnimation(), - f = k.getFields(k.fieldCategoryY), - b = k.getItemInstancing(), - h = k.sprites, - l, e = k.getHidden(), - g = false, - d, a = f.length; - if (!j) { - return [] - } - for (d = 0; d < a; d++) { - l = h[d]; - if (!l) { - l = k.createSprite(); - l.setAttributes({ - zIndex: -d - }); - l.setField(f[d]); - g = true; - e.push(false); - if (b) { - l.itemsMarker.getTemplate().setAttributes(k.getStyleByIndex(d)) - } else { - l.setAttributes(k.getStyleByIndex(d)) - } - } - if (c) { - if (b) { - l.itemsMarker.getTemplate().setAnimation(c) - } - l.setAnimation(c) - } - } - if (g) { - k.updateHidden(e) - } - return h - }, - getItemForPoint: function(k, j) { - if (this.getSprites()) { - var h = this, - b, g, m, a = h.getItemInstancing(), - f = h.getSprites(), - l = h.getStore(), - c = h.getHidden(), - n, d, e; - for (b = 0, g = f.length; b < g; b++) { - if (!c[b]) { - m = f[b]; - d = m.getIndexNearPoint(k, j); - if (d !== -1) { - e = h.getYField(); - n = { - series: h, - index: d, - category: a ? "items" : "markers", - record: l.getData().items[d], - field: typeof e === "string" ? e : e[b], - sprite: m - }; - return n - } - } - } - return null - } - }, - provideLegendInfo: function(e) { - var g = this, - f = g.getSprites(), - h = g.getTitle(), - j = g.getYField(), - d = g.getHidden(), - k = f.length === 1, - b, l, c, a; - for (c = 0; c < f.length; c++) { - b = g.getStyleByIndex(c); - l = b.fillStyle; - if (h) { - if (Ext.isArray(h)) { - a = h[c] - } else { - if (k) { - a = h - } - } - } else { - if (Ext.isArray(j)) { - a = j[c] - } else { - a = g.getId() - } - } - e.push({ - name: a, - mark: (Ext.isObject(l) ? l.stops && l.stops[0].color : l) || b.strokeStyle || "black", - disabled: d[c], - series: g.getId(), - index: c - }) - } - }, - onSpriteAnimationStart: function(a) { - this.spriteAnimationCount++; - if (this.spriteAnimationCount === 1) { - this.fireEvent("animationstart") - } - }, - onSpriteAnimationEnd: function(a) { - this.spriteAnimationCount--; - if (this.spriteAnimationCount === 0) { - this.fireEvent("animationend") - } - } -}); -Ext.define("Ext.chart.series.sprite.Series", { - extend: "Ext.draw.sprite.Sprite", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - inheritableStatics: { - def: { - processors: { - dataMinX: "number", - dataMaxX: "number", - dataMinY: "number", - dataMaxY: "number", - rangeX: "data", - rangeY: "data", - dataX: "data", - dataY: "data" - }, - defaults: { - dataMinX: 0, - dataMaxX: 1, - dataMinY: 0, - dataMaxY: 1, - rangeX: null, - rangeY: null, - dataX: null, - dataY: null - }, - triggers: { - dataX: "bbox", - dataY: "bbox", - dataMinX: "bbox", - dataMaxX: "bbox", - dataMinY: "bbox", - dataMaxY: "bbox" - } - } - }, - config: { - store: null, - series: null, - field: null - } -}); -Ext.define("Ext.chart.series.sprite.Cartesian", { - extend: "Ext.chart.series.sprite.Series", - inheritableStatics: { - def: { - processors: { - labels: "default", - labelOverflowPadding: "number", - selectionTolerance: "number", - flipXY: "bool", - renderer: "default", - visibleMinX: "number", - visibleMinY: "number", - visibleMaxX: "number", - visibleMaxY: "number", - innerWidth: "number", - innerHeight: "number" - }, - defaults: { - labels: null, - labelOverflowPadding: 10, - selectionTolerance: 20, - flipXY: false, - renderer: null, - transformFillStroke: false, - visibleMinX: 0, - visibleMinY: 0, - visibleMaxX: 1, - visibleMaxY: 1, - innerWidth: 1, - innerHeight: 1 - }, - triggers: { - dataX: "dataX,bbox", - dataY: "dataY,bbox", - visibleMinX: "panzoom", - visibleMinY: "panzoom", - visibleMaxX: "panzoom", - visibleMaxY: "panzoom", - innerWidth: "panzoom", - innerHeight: "panzoom" - }, - updaters: { - dataX: function(a) { - this.processDataX(); - this.scheduleUpdater(a, "dataY", ["dataY"]) - }, - dataY: function() { - this.processDataY() - }, - panzoom: function(c) { - var e = c.visibleMaxX - c.visibleMinX, - d = c.visibleMaxY - c.visibleMinY, - b = c.flipXY ? c.innerHeight : c.innerWidth, - g = !c.flipXY ? c.innerHeight : c.innerWidth, - a = this.getSurface(), - f = a ? a.getInherited().rtl : false; - if (f && !c.flipXY) { - c.translationX = b + c.visibleMinX * b / e - } else { - c.translationX = -c.visibleMinX * b / e - } - c.translationY = -c.visibleMinY * g / d; - c.scalingX = (f && !c.flipXY ? -1 : 1) * b / e; - c.scalingY = g / d; - c.scalingCenterX = 0; - c.scalingCenterY = 0; - this.applyTransformations(true) - } - } - } - }, - processDataY: Ext.emptyFn, - processDataX: Ext.emptyFn, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.dataMinX; - b.y = a.dataMinY; - b.width = a.dataMaxX - a.dataMinX; - b.height = a.dataMaxY - a.dataMinY - }, - binarySearch: function(d) { - var b = this.attr.dataX, - f = 0, - a = b.length; - if (d <= b[0]) { - return f - } - if (d >= b[a - 1]) { - return a - 1 - } - while (f + 1 < a) { - var c = (f + a) >> 1, - e = b[c]; - if (e === d) { - return c - } else { - if (e < d) { - f = c - } else { - a = c - } - } - } - return f - }, - render: function(b, c, g) { - var f = this, - a = f.attr, - e = a.inverseMatrix.clone(); - e.appendMatrix(b.inverseMatrix); - if (a.dataX === null || a.dataX === undefined) { - return - } - if (a.dataY === null || a.dataY === undefined) { - return - } - if (e.getXX() * e.getYX() || e.getXY() * e.getYY()) { - console.log("Cartesian Series sprite does not support rotation/sheering"); - return - } - var d = e.transformList([ - [g[0] - 1, g[3] + 1], - [g[0] + g[2] + 1, -1] - ]); - d = d[0].concat(d[1]); - f.renderClipped(b, c, d, g) - }, - renderClipped: Ext.emptyFn, - getIndexNearPoint: function(f, e) { - var w = this, - q = w.attr.matrix, - h = w.attr.dataX, - g = w.attr.dataY, - k = w.attr.selectionTolerance, - t, r, c = -1, - j = q.clone().prependMatrix(w.surfaceMatrix).inverse(), - u = j.transformPoint([f, e]), - b = j.transformPoint([f - k, e - k]), - n = j.transformPoint([f + k, e + k]), - a = Math.min(b[0], n[0]), - s = Math.max(b[0], n[0]), - l = Math.min(b[1], n[1]), - d = Math.max(b[1], n[1]), - m, v, o, p; - for (o = 0, p = h.length; o < p; o++) { - m = h[o]; - v = g[o]; - if (m > a && m < s && v > l && v < d) { - if (c === -1 || (Math.abs(m - u[0]) < t) && (Math.abs(v - u[1]) < r)) { - t = Math.abs(m - u[0]); - r = Math.abs(v - u[1]); - c = o - } - } - } - return c - } -}); -Ext.define("Ext.chart.series.sprite.StackedCartesian", { - extend: "Ext.chart.series.sprite.Cartesian", - inheritableStatics: { - def: { - processors: { - groupCount: "number", - groupOffset: "number", - dataStartY: "data" - }, - defaults: { - selectionTolerance: 20, - groupCount: 1, - groupOffset: 0, - dataStartY: null - }, - triggers: { - dataStartY: "dataY,bbox" - } - } - }, - getIndexNearPoint: function(e, d) { - var o = this, - q = o.attr.matrix, - h = o.attr.dataX, - f = o.attr.dataY, - u = o.attr.dataStartY, - l = o.attr.selectionTolerance, - s = 0.5, - r = Infinity, - b = -1, - k = q.clone().prependMatrix(this.surfaceMatrix).inverse(), - t = k.transformPoint([e, d]), - a = k.transformPoint([e - l, d - l]), - n = k.transformPoint([e + l, d + l]), - m = Math.min(a[1], n[1]), - c = Math.max(a[1], n[1]), - j, g; - for (var p = 0; p < h.length; p++) { - if (Math.min(u[p], f[p]) <= c && m <= Math.max(u[p], f[p])) { - j = Math.abs(h[p] - t[0]); - g = Math.max(-Math.min(f[p] - t[1], t[1] - u[p]), 0); - if (j < s && g <= r) { - s = j; - r = g; - b = p - } - } - } - return b - } -}); -Ext.define("Ext.chart.series.sprite.Area", { - alias: "sprite.areaSeries", - extend: "Ext.chart.series.sprite.StackedCartesian", - inheritableStatics: { - def: { - processors: { - step: "bool" - }, - defaults: { - step: false - } - } - }, - renderClipped: function(q, s, A) { - var B = this, - p = B.attr, - l = p.dataX, - j = p.dataY, - C = p.dataStartY, - t = p.matrix, - h, g, v, f, d, z, w, e = t.elements[0], - m = t.elements[4], - o = t.elements[3], - k = t.elements[5], - c = B.surfaceMatrix, - n = {}, - r = Math.min(A[0], A[2]), - u = Math.max(A[0], A[2]), - b = Math.max(0, this.binarySearch(r)), - a = Math.min(l.length - 1, this.binarySearch(u) + 1); - s.beginPath(); - z = l[b] * e + m; - w = j[b] * o + k; - s.moveTo(z, w); - if (p.step) { - d = w; - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, d); - s.lineTo(h, d = g) - } - } else { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, g) - } - } - if (C) { - if (p.step) { - f = l[a] * e + m; - for (v = a; v >= b; v--) { - h = l[v] * e + m; - g = C[v] * o + k; - s.lineTo(f, g); - s.lineTo(f = h, g) - } - } else { - for (v = a; v >= b; v--) { - h = l[v] * e + m; - g = C[v] * o + k; - s.lineTo(h, g) - } - } - } else { - s.lineTo(l[a] * e + m, g); - s.lineTo(l[a] * e + m, k); - s.lineTo(z, k); - s.lineTo(z, j[v] * o + k) - } - if (p.transformFillStroke) { - p.matrix.toContext(s) - } - s.fill(); - if (p.transformFillStroke) { - p.inverseMatrix.toContext(s) - } - s.beginPath(); - s.moveTo(z, w); - if (p.step) { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, d); - s.lineTo(h, d = g); - n.translationX = c.x(h, g); - n.translationY = c.y(h, g); - B.putMarker("markers", n, v, !p.renderer) - } - } else { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, g); - n.translationX = c.x(h, g); - n.translationY = c.y(h, g); - B.putMarker("markers", n, v, !p.renderer) - } - } - if (p.transformFillStroke) { - p.matrix.toContext(s) - } - s.stroke() - } -}); -Ext.define("Ext.chart.series.Area", { - extend: "Ext.chart.series.StackedCartesian", - alias: "series.area", - type: "area", - seriesType: "areaSeries", - requires: ["Ext.chart.series.sprite.Area"], - config: { - splitStacks: false - } -}); -Ext.define("Ext.chart.series.sprite.Bar", { - alias: "sprite.barSeries", - extend: "Ext.chart.series.sprite.StackedCartesian", - inheritableStatics: { - def: { - processors: { - minBarWidth: "number", - maxBarWidth: "number", - minGapWidth: "number", - radius: "number", - inGroupGapWidth: "number" - }, - defaults: { - minBarWidth: 2, - maxBarWidth: 100, - minGapWidth: 5, - inGroupGapWidth: 3, - radius: 0 - } - } - }, - drawLabel: function(k, i, s, h, o) { - var q = this, - n = q.attr, - f = q.getMarker("labels"), - d = f.getTemplate(), - l = q.labelCfg || (q.labelCfg = {}), - c = q.surfaceMatrix, - j = n.labelOverflowPadding, - b = d.attr.display, - m = d.attr.orientation, - g, e, a, r, t, p; - l.x = c.x(i, h); - l.y = c.y(i, h); - if (!n.flipXY) { - l.rotationRads = -Math.PI * 0.5 - } else { - l.rotationRads = 0 - } - l.calloutVertical = !n.flipXY; - switch (m) { - case "horizontal": - l.rotationRads = 0; - l.calloutVertical = false; - break; - case "vertical": - l.rotationRads = -Math.PI * 0.5; - l.calloutVertical = true; - break - } - l.text = k; - if (d.attr.renderer) { - p = [k, f, l, { - store: q.getStore() - }, o]; - r = Ext.callback(d.attr.renderer, null, p, 0, q.getSeries()); - if (typeof r === "string") { - l.text = r - } else { - if (typeof r === "object") { - if ("text" in r) { - l.text = r.text - } - t = true - } - } - } - a = q.getMarkerBBox("labels", o, true); - if (!a) { - q.putMarker("labels", l, o); - a = q.getMarkerBBox("labels", o, true) - } - e = (a.width / 2 + j); - if (s > h) { - e = -e - } - if ((m === "horizontal" && n.flipXY) || (m === "vertical" && !n.flipXY) || !m) { - g = (b === "insideStart") ? s + e : h - e - } else { - g = (b === "insideStart") ? s + j * 2 : h - j * 2 - } - l.x = c.x(i, g); - l.y = c.y(i, g); - g = (b === "insideStart") ? s - e : h + e; - l.calloutPlaceX = c.x(i, g); - l.calloutPlaceY = c.y(i, g); - g = (b === "insideStart") ? s : h; - l.calloutStartX = c.x(i, g); - l.calloutStartY = c.y(i, g); - if (s > h) { - e = -e - } - if (Math.abs(h - s) <= e * 2 || b === "outside") { - l.callout = 1 - } else { - l.callout = 0 - } - if (t) { - Ext.apply(l, r) - } - q.putMarker("labels", l, o) - }, - drawBar: function(l, b, d, c, h, k, a, e) { - var g = this, - j = {}, - f = g.attr.renderer, - i; - j.x = c; - j.y = h; - j.width = k - c; - j.height = a - h; - j.radius = g.attr.radius; - if (f) { - i = Ext.callback(f, null, [g, j, { - store: g.getStore() - }, e], 0, g.getSeries()); - Ext.apply(j, i) - } - g.putMarker("items", j, e, !f) - }, - renderClipped: function(G, u, F, C) { - if (this.cleanRedraw) { - return - } - var q = this, - o = q.attr, - w = o.dataX, - v = o.dataY, - H = o.labels, - n = o.dataStartY, - m = o.groupCount, - E = o.groupOffset - (m - 1) * 0.5, - z = o.inGroupGapWidth, - t = u.lineWidth, - D = o.matrix, - B = D.elements[0], - j = D.elements[3], - e = D.elements[4], - d = G.roundPixel(D.elements[5]) - 1, - J = (B < 0 ? -1 : 1) * B - o.minGapWidth, - k = (Math.min(J, o.maxBarWidth) - z * (m - 1)) / m, - A = G.roundPixel(Math.max(o.minBarWidth, k)), - c = q.surfaceMatrix, - g, I, b, h, K, a, l = 0.5 * o.lineWidth, - L = Math.min(F[0], F[2]), - x = Math.max(F[0], F[2]), - y = Math.max(0, Math.floor(L)), - p = Math.min(w.length - 1, Math.ceil(x)), - f = H && q.getMarker("labels"), - s, r; - for (K = y; K <= p; K++) { - s = n ? n[K] : 0; - r = v[K]; - a = w[K] * B + e + E * (A + z); - g = G.roundPixel(a - A / 2) + l; - h = G.roundPixel(r * j + d + t); - I = G.roundPixel(a + A / 2) - l; - b = G.roundPixel(s * j + d + t); - q.drawBar(u, G, F, g, h - l, I, b - l, K); - if (f && H[K] != null) { - q.drawLabel(H[K], a, b, h, K) - } - q.putMarker("markers", { - translationX: c.x(a, h), - translationY: c.y(a, h) - }, K, true) - } - }, - getIndexNearPoint: function(l, k) { - var m = this, - g = m.attr, - h = g.dataX, - a = m.getSurface(), - b = a.getRect() || [0, 0, 0, 0], - j = b[3], - e, d, c, n, f = -1; - if (g.flipXY) { - e = j - k; - if (a.getInherited().rtl) { - d = b[2] - l - } else { - d = l - } - } else { - e = l; - d = j - k - } - for (c = 0; c < h.length; c++) { - n = m.getMarkerBBox("items", c); - if (Ext.draw.Draw.isPointInBBox(e, d, n)) { - f = c; - break - } - } - return f - } -}); -Ext.define("Ext.chart.series.Bar", { - extend: "Ext.chart.series.StackedCartesian", - alias: "series.bar", - type: "bar", - seriesType: "barSeries", - requires: ["Ext.chart.series.sprite.Bar", "Ext.draw.sprite.Rect"], - config: { - itemInstancing: { - type: "rect", - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0, - radius: 0 - } - } - } - }, - getItemForPoint: function(a, f) { - if (this.getSprites()) { - var d = this, - c = d.getChart(), - e = c.getInnerPadding(), - b = c.getInherited().rtl; - arguments[0] = a + (b ? e.right : -e.left); - arguments[1] = f + e.bottom; - return d.callParent(arguments) - } - }, - updateXAxis: function(a) { - a.setLabelInSpan(true); - this.callParent(arguments) - }, - updateHidden: function(a) { - this.callParent(arguments); - this.updateStacked() - }, - updateStacked: function(c) { - var e = this, - g = e.getSprites(), - d = g.length, - f = [], - a = {}, - b; - for (b = 0; b < d; b++) { - if (!g[b].attr.hidden) { - f.push(g[b]) - } - } - d = f.length; - if (e.getStacked()) { - a.groupCount = 1; - a.groupOffset = 0; - for (b = 0; b < d; b++) { - f[b].setAttributes(a) - } - } else { - a.groupCount = f.length; - for (b = 0; b < d; b++) { - a.groupOffset = b; - f[b].setAttributes(a) - } - } - e.callParent(arguments) - } -}); -Ext.define("Ext.chart.series.sprite.Bar3D", { - extend: "Ext.chart.series.sprite.Bar", - alias: "sprite.bar3dSeries", - requires: ["Ext.draw.gradient.Linear"], - inheritableStatics: { - def: { - processors: { - depthWidthRatio: "number", - saturationFactor: "number", - brightnessFactor: "number", - colorSpread: "number" - }, - defaults: { - depthWidthRatio: 1 / 3, - saturationFactor: 1, - brightnessFactor: 1, - colorSpread: 1, - transformFillStroke: true - }, - triggers: { - groupCount: "panzoom" - }, - updaters: { - panzoom: function(c) { - var g = this, - e = c.visibleMaxX - c.visibleMinX, - d = c.visibleMaxY - c.visibleMinY, - b = c.flipXY ? c.innerHeight : c.innerWidth, - h = !c.flipXY ? c.innerHeight : c.innerWidth, - a = g.getSurface(), - f = a ? a.getInherited().rtl : false; - if (f && !c.flipXY) { - c.translationX = b + c.visibleMinX * b / e - } else { - c.translationX = -c.visibleMinX * b / e - } - c.translationY = -c.visibleMinY * (h - g.depth) / d; - c.scalingX = (f && !c.flipXY ? -1 : 1) * b / e; - c.scalingY = (h - g.depth) / d; - c.scalingCenterX = 0; - c.scalingCenterY = 0; - g.applyTransformations(true) - } - } - } - }, - config: { - showStroke: false - }, - depth: 0, - drawBar: function(p, b, d, c, l, o, a, h) { - var k = this, - i = k.attr, - n = {}, - j = i.renderer, - m, g, f, e; - n.x = (c + o) * 0.5; - n.y = l; - n.width = (o - c) * 0.75; - n.height = a - l; - n.depth = g = n.width * i.depthWidthRatio; - n.orientation = i.flipXY ? "horizontal" : "vertical"; - n.saturationFactor = i.saturationFactor; - n.brightnessFactor = i.brightnessFactor; - n.colorSpread = i.colorSpread; - if (g !== k.depth) { - k.depth = g; - f = k.getSeries(); - f.fireEvent("depthchange", f, g) - } - if (j) { - e = [k, n, { - store: k.getStore() - }, h]; - m = Ext.callback(j, null, e, 0, k.getSeries()); - Ext.apply(n, m) - } - k.putMarker("items", n, h, !j) - } -}); -Ext.define("Ext.chart.series.sprite.Box", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.box", - type: "box", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number", - depth: "number", - orientation: "enums(vertical,horizontal)", - showStroke: "bool", - saturationFactor: "number", - brightnessFactor: "number", - colorSpread: "number" - }, - triggers: { - x: "bbox", - y: "bbox", - width: "bbox", - height: "bbox", - depth: "bbox", - orientation: "bbox" - }, - defaults: { - x: 0, - y: 0, - width: 8, - height: 8, - depth: 8, - orientation: "vertical", - showStroke: false, - saturationFactor: 1, - brightnessFactor: 1, - colorSpread: 1, - lineJoin: "bevel" - } - } - }, - constructor: function(a) { - this.callParent([a]); - this.topGradient = new Ext.draw.gradient.Linear({}); - this.rightGradient = new Ext.draw.gradient.Linear({}); - this.frontGradient = new Ext.draw.gradient.Linear({}) - }, - updatePlainBBox: function(d) { - var c = this.attr, - b = c.x, - g = c.y, - e = c.width, - a = c.height, - f = c.depth; - d.x = b - e * 0.5; - d.width = e + f; - if (a > 0) { - d.y = g; - d.height = a + f - } else { - d.y = g + f; - d.height = a - f - } - }, - render: function(l, m) { - var u = this, - k = u.attr, - r = k.x, - j = k.y, - f = j + k.height, - i = j < f, - e = k.width * 0.5, - v = k.depth, - d = k.orientation === "horizontal", - g = k.globalAlpha < 1, - c = k.fillStyle, - n = Ext.draw.Color.create(c.isGradient ? c.getStops()[0].color : c), - h = k.saturationFactor, - o = k.brightnessFactor, - t = k.colorSpread, - b = n.getHSV(), - a = {}, - s, q, p; - if (!k.showStroke) { - m.strokeStyle = Ext.draw.Color.RGBA_NONE - } - if (i) { - p = j; - j = f; - f = p - } - u.topGradient.setDegrees(d ? 0 : 80); - u.topGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 + t * 0.1) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.11) * o, 0, 1)) - }]); - u.rightGradient.setDegrees(d ? 45 : 90); - u.rightGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.14) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 + t * 0.4) * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.32) * o, 0, 1)) - }]); - if (d) { - u.frontGradient.setDegrees(0) - } else { - u.frontGradient.setRadians(Math.atan2(j - f, e * 2)) - } - u.frontGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 - t * 0.1) * h, 0, 1), Ext.Number.constrain((0.5 + t * 0.1) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 + t * 0.1) * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.23) * o, 0, 1)) - }]); - if (g || i) { - m.beginPath(); - m.moveTo(r - e, f); - m.lineTo(r - e + v, f + v); - m.lineTo(r + e + v, f + v); - m.lineTo(r + e, f); - m.closePath(); - a.x = r - e; - a.y = j; - a.width = e + v; - a.height = v; - m.fillStyle = (d ? u.rightGradient : u.topGradient).generateGradient(m, a); - m.fillStroke(k) - } - if (g) { - m.beginPath(); - m.moveTo(r - e, j); - m.lineTo(r - e + v, j + v); - m.lineTo(r - e + v, f + v); - m.lineTo(r - e, f); - m.closePath(); - a.x = r + e; - a.y = f; - a.width = v; - a.height = j + v - f; - m.fillStyle = (d ? u.topGradient : u.rightGradient).generateGradient(m, a); - m.fillStroke(k) - } - q = l.roundPixel(j); - m.beginPath(); - m.moveTo(r - e, q); - m.lineTo(r - e + v, j + v); - m.lineTo(r + e + v, j + v); - m.lineTo(r + e, q); - m.closePath(); - a.x = r - e; - a.y = j; - a.width = e + v; - a.height = v; - m.fillStyle = (d ? u.rightGradient : u.topGradient).generateGradient(m, a); - m.fillStroke(k); - s = l.roundPixel(r + e); - m.beginPath(); - m.moveTo(s, l.roundPixel(j)); - m.lineTo(r + e + v, j + v); - m.lineTo(r + e + v, f + v); - m.lineTo(s, f); - m.closePath(); - a.x = r + e; - a.y = f; - a.width = v; - a.height = j + v - f; - m.fillStyle = (d ? u.topGradient : u.rightGradient).generateGradient(m, a); - m.fillStroke(k); - s = l.roundPixel(r + e); - q = l.roundPixel(j); - m.beginPath(); - m.moveTo(r - e, f); - m.lineTo(r - e, q); - m.lineTo(s, q); - m.lineTo(s, f); - m.closePath(); - a.x = r - e; - a.y = f; - a.width = e * 2; - a.height = j - f; - m.fillStyle = u.frontGradient.generateGradient(m, a); - m.fillStroke(k) - } -}); -Ext.define("Ext.chart.series.Bar3D", { - extend: "Ext.chart.series.Bar", - requires: ["Ext.chart.series.sprite.Bar3D", "Ext.chart.series.sprite.Box"], - alias: "series.bar3d", - type: "bar3d", - seriesType: "bar3dSeries", - config: { - itemInstancing: { - type: "box", - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0, - depth: 0 - } - } - }, - highlightCfg: { - opacity: 0.8 - } - }, - getSprites: function() { - var c = this.callParent(arguments), - b, d, a; - for (a = 0; a < c.length; a++) { - b = c[a]; - d = b.attr.zIndex; - if (d < 0) { - b.setAttributes({ - zIndex: -d - }) - } - if (b.setSeries) { - b.setSeries(this) - } - } - return c - }, - getDepth: function() { - var a = this.getSprites()[0]; - return a ? (a.depth || 0) : 0 - }, - getItemForPoint: function(m, k) { - if (this.getSprites()) { - var j = this, - b, o, a = j.getItemInstancing(), - h = j.getSprites(), - n = j.getStore(), - c = j.getHidden(), - g = j.getChart(), - l = g.getInnerPadding(), - f = g.getInherited().rtl, - p, d, e; - m = m + (f ? l.right : -l.left); - k = k + l.bottom; - for (b = h.length - 1; b >= 0; b--) { - if (!c[b]) { - o = h[b]; - d = o.getIndexNearPoint(m, k); - if (d !== -1) { - e = j.getYField(); - p = { - series: j, - index: d, - category: a ? "items" : "markers", - record: n.getData().items[d], - field: typeof e === "string" ? e : e[b], - sprite: o - }; - return p - } - } - } - return null - } - } -}); -Ext.define("Ext.draw.LimitedCache", { - config: { - limit: 40, - feeder: function() { - return 0 - }, - scope: null - }, - cache: null, - constructor: function(a) { - this.cache = {}; - this.cache.list = []; - this.cache.tail = 0; - this.initConfig(a) - }, - get: function(e) { - var c = this.cache, - b = this.getLimit(), - a = this.getFeeder(), - d = this.getScope() || this; - if (c[e]) { - return c[e].value - } - if (c.list[c.tail]) { - delete c[c.list[c.tail].cacheId] - } - c[e] = c.list[c.tail] = { - value: a.apply(d, Array.prototype.slice.call(arguments, 1)), - cacheId: e - }; - c.tail++; - if (c.tail === b) { - c.tail = 0 - } - return c[e].value - }, - clear: function() { - this.cache = {}; - this.cache.list = []; - this.cache.tail = 0 - } -}); -Ext.define("Ext.draw.SegmentTree", { - config: { - strategy: "double" - }, - time: function(m, l, n, c, E, d, e) { - var f = 0, - o, A, s = new Date(n[m.startIdx[0]]), - x = new Date(n[m.endIdx[l - 1]]), - D = Ext.Date, - u = [ - [D.MILLI, 1, "ms1", null], - [D.MILLI, 2, "ms2", "ms1"], - [D.MILLI, 5, "ms5", "ms1"], - [D.MILLI, 10, "ms10", "ms5"], - [D.MILLI, 50, "ms50", "ms10"], - [D.MILLI, 100, "ms100", "ms50"], - [D.MILLI, 500, "ms500", "ms100"], - [D.SECOND, 1, "s1", "ms500"], - [D.SECOND, 10, "s10", "s1"], - [D.SECOND, 30, "s30", "s10"], - [D.MINUTE, 1, "mi1", "s10"], - [D.MINUTE, 5, "mi5", "mi1"], - [D.MINUTE, 10, "mi10", "mi5"], - [D.MINUTE, 30, "mi30", "mi10"], - [D.HOUR, 1, "h1", "mi30"], - [D.HOUR, 6, "h6", "h1"], - [D.HOUR, 12, "h12", "h6"], - [D.DAY, 1, "d1", "h12"], - [D.DAY, 7, "d7", "d1"], - [D.MONTH, 1, "mo1", "d1"], - [D.MONTH, 3, "mo3", "mo1"], - [D.MONTH, 6, "mo6", "mo3"], - [D.YEAR, 1, "y1", "mo3"], - [D.YEAR, 5, "y5", "y1"], - [D.YEAR, 10, "y10", "y5"], - [D.YEAR, 100, "y100", "y10"] - ], - z, b, k = f, - F = l, - j = false, - r = m.startIdx, - h = m.endIdx, - w = m.minIdx, - C = m.maxIdx, - a = m.open, - y = m.close, - g = m.minX, - q = m.minY, - p = m.maxX, - B = m.maxY, - v, t; - for (z = 0; l > f + 1 && z < u.length; z++) { - s = new Date(n[r[0]]); - b = u[z]; - s = D.align(s, b[0], b[1]); - if (D.diff(s, x, b[0]) > n.length * 2 * b[1]) { - continue - } - if (b[3] && m.map["time_" + b[3]]) { - o = m.map["time_" + b[3]][0]; - A = m.map["time_" + b[3]][1] - } else { - o = k; - A = F - } - f = l; - t = s; - j = true; - r[l] = r[o]; - h[l] = h[o]; - w[l] = w[o]; - C[l] = C[o]; - a[l] = a[o]; - y[l] = y[o]; - g[l] = g[o]; - q[l] = q[o]; - p[l] = p[o]; - B[l] = B[o]; - t = Ext.Date.add(t, b[0], b[1]); - for (v = o + 1; v < A; v++) { - if (n[h[v]] < +t) { - h[l] = h[v]; - y[l] = y[v]; - if (B[v] > B[l]) { - B[l] = B[v]; - p[l] = p[v]; - C[l] = C[v] - } - if (q[v] < q[l]) { - q[l] = q[v]; - g[l] = g[v]; - w[l] = w[v] - } - } else { - l++; - r[l] = r[v]; - h[l] = h[v]; - w[l] = w[v]; - C[l] = C[v]; - a[l] = a[v]; - y[l] = y[v]; - g[l] = g[v]; - q[l] = q[v]; - p[l] = p[v]; - B[l] = B[v]; - t = Ext.Date.add(t, b[0], b[1]) - } - } - if (l > f) { - m.map["time_" + b[2]] = [f, l] - } - } - }, - "double": function(h, u, j, a, t, b, c) { - var e = 0, - k, f = 1, - n, d, v, g, s, l, m, r, q, p, o; - while (u > e + 1) { - k = e; - e = u; - f += f; - for (n = k; n < e; n += 2) { - if (n === e - 1) { - d = h.startIdx[n]; - v = h.endIdx[n]; - g = h.minIdx[n]; - s = h.maxIdx[n]; - l = h.open[n]; - m = h.close[n]; - r = h.minX[n]; - q = h.minY[n]; - p = h.maxX[n]; - o = h.maxY[n] - } else { - d = h.startIdx[n]; - v = h.endIdx[n + 1]; - l = h.open[n]; - m = h.close[n]; - if (h.minY[n] <= h.minY[n + 1]) { - g = h.minIdx[n]; - r = h.minX[n]; - q = h.minY[n] - } else { - g = h.minIdx[n + 1]; - r = h.minX[n + 1]; - q = h.minY[n + 1] - } - if (h.maxY[n] >= h.maxY[n + 1]) { - s = h.maxIdx[n]; - p = h.maxX[n]; - o = h.maxY[n] - } else { - s = h.maxIdx[n + 1]; - p = h.maxX[n + 1]; - o = h.maxY[n + 1] - } - } - h.startIdx[u] = d; - h.endIdx[u] = v; - h.minIdx[u] = g; - h.maxIdx[u] = s; - h.open[u] = l; - h.close[u] = m; - h.minX[u] = r; - h.minY[u] = q; - h.maxX[u] = p; - h.maxY[u] = o; - u++ - } - h.map["double_" + f] = [e, u] - } - }, - none: Ext.emptyFn, - aggregateData: function(h, a, r, c, d) { - var b = h.length, - e = [], - s = [], - f = [], - q = [], - j = [], - p = [], - n = [], - o = [], - m = [], - k = [], - g = { - startIdx: e, - endIdx: s, - minIdx: f, - maxIdx: q, - open: j, - minX: p, - minY: n, - maxX: o, - maxY: m, - close: k - }, - l; - for (l = 0; l < b; l++) { - e[l] = l; - s[l] = l; - f[l] = l; - q[l] = l; - j[l] = a[l]; - p[l] = h[l]; - n[l] = c[l]; - o[l] = h[l]; - m[l] = r[l]; - k[l] = d[l] - } - g.map = { - original: [0, b] - }; - if (b) { - this[this.getStrategy()](g, b, h, a, r, c, d) - } - return g - }, - binarySearchMin: function(c, g, a, e) { - var b = this.dataX; - if (e <= b[c.startIdx[0]]) { - return g - } - if (e >= b[c.startIdx[a - 1]]) { - return a - 1 - } - while (g + 1 < a) { - var d = (g + a) >> 1, - f = b[c.startIdx[d]]; - if (f === e) { - return d - } else { - if (f < e) { - g = d - } else { - a = d - } - } - } - return g - }, - binarySearchMax: function(c, g, a, e) { - var b = this.dataX; - if (e <= b[c.endIdx[0]]) { - return g - } - if (e >= b[c.endIdx[a - 1]]) { - return a - 1 - } - while (g + 1 < a) { - var d = (g + a) >> 1, - f = b[c.endIdx[d]]; - if (f === e) { - return d - } else { - if (f < e) { - g = d - } else { - a = d - } - } - } - return a - }, - constructor: function(a) { - this.initConfig(a) - }, - setData: function(d, a, b, c, e) { - if (!b) { - e = c = b = a - } - this.dataX = d; - this.dataOpen = a; - this.dataHigh = b; - this.dataLow = c; - this.dataClose = e; - if (d.length === b.length && d.length === c.length) { - this.cache = this.aggregateData(d, a, b, c, e) - } - }, - getAggregation: function(d, k, i) { - if (!this.cache) { - return null - } - var c = Infinity, - g = this.dataX[this.dataX.length - 1] - this.dataX[0], - l = this.cache.map, - m = l.original, - a, e, j, b, f, h; - for (a in l) { - e = l[a]; - j = e[1] - e[0] - 1; - b = g / j; - if (i <= b && b < c) { - m = e; - c = b - } - } - f = Math.max(this.binarySearchMin(this.cache, m[0], m[1], d), m[0]); - h = Math.min(this.binarySearchMax(this.cache, m[0], m[1], k) + 1, m[1]); - return { - data: this.cache, - start: f, - end: h - } - } -}); -Ext.define("Ext.chart.series.sprite.Aggregative", { - extend: "Ext.chart.series.sprite.Cartesian", - requires: ["Ext.draw.LimitedCache", "Ext.draw.SegmentTree"], - inheritableStatics: { - def: { - processors: { - dataHigh: "data", - dataLow: "data", - dataClose: "data" - }, - aliases: { - dataOpen: "dataY" - }, - defaults: { - dataHigh: null, - dataLow: null, - dataClose: null - } - } - }, - config: { - aggregator: {} - }, - applyAggregator: function(b, a) { - return Ext.factory(b, Ext.draw.SegmentTree, a) - }, - constructor: function() { - this.callParent(arguments) - }, - processDataY: function() { - var d = this, - b = d.attr, - e = b.dataHigh, - a = b.dataLow, - f = b.dataClose, - c = b.dataY; - d.callParent(arguments); - if (b.dataX && c && c.length > 0) { - if (e) { - d.getAggregator().setData(b.dataX, b.dataY, e, a, f) - } else { - d.getAggregator().setData(b.dataX, b.dataY) - } - } - }, - getGapWidth: function() { - return 1 - }, - renderClipped: function(b, c, g, f) { - var e = this, - d = Math.min(g[0], g[2]), - a = Math.max(g[0], g[2]), - h = e.getAggregator() && e.getAggregator().getAggregation(d, a, (a - d) / f[2] * e.getGapWidth()); - if (h) { - e.dataStart = h.data.startIdx[h.start]; - e.dataEnd = h.data.endIdx[h.end - 1]; - e.renderAggregates(h.data, h.start, h.end, b, c, g, f) - } - } -}); -Ext.define("Ext.chart.series.sprite.CandleStick", { - alias: "sprite.candlestickSeries", - extend: "Ext.chart.series.sprite.Aggregative", - inheritableStatics: { - def: { - processors: { - raiseStyle: function(b, a) { - return Ext.merge({}, a || {}, b) - }, - dropStyle: function(b, a) { - return Ext.merge({}, a || {}, b) - }, - barWidth: "number", - padding: "number", - ohlcType: "enums(candlestick,ohlc)" - }, - defaults: { - raiseStyle: { - strokeStyle: "green", - fillStyle: "green" - }, - dropStyle: { - strokeStyle: "red", - fillStyle: "red" - }, - planar: false, - barWidth: 15, - padding: 3, - lineJoin: "miter", - miterLimit: 5, - ohlcType: "candlestick" - }, - triggers: { - raiseStyle: "raiseStyle", - dropStyle: "dropStyle" - }, - updaters: { - raiseStyle: function() { - this.raiseTemplate && this.raiseTemplate.setAttributes(this.attr.raiseStyle) - }, - dropStyle: function() { - this.dropTemplate && this.dropTemplate.setAttributes(this.attr.dropStyle) - } - } - } - }, - candlestick: function(i, c, a, e, h, f, b) { - var d = Math.min(c, h), - g = Math.max(c, h); - i.moveTo(f, e); - i.lineTo(f, g); - i.moveTo(f + b, g); - i.lineTo(f + b, d); - i.lineTo(f - b, d); - i.lineTo(f - b, g); - i.closePath(); - i.moveTo(f, a); - i.lineTo(f, d) - }, - ohlc: function(b, d, e, a, f, c, g) { - b.moveTo(c, e); - b.lineTo(c, a); - b.moveTo(c, d); - b.lineTo(c - g, d); - b.moveTo(c, f); - b.lineTo(c + g, f) - }, - constructor: function() { - this.callParent(arguments); - this.raiseTemplate = new Ext.draw.sprite.Rect({ - parent: this - }); - this.dropTemplate = new Ext.draw.sprite.Rect({ - parent: this - }) - }, - getGapWidth: function() { - var a = this.attr, - b = a.barWidth, - c = a.padding; - return b + c - }, - renderAggregates: function(d, c, b, t, u, z) { - var D = this, - s = this.attr, - j = s.dataX, - v = s.matrix, - e = v.getXX(), - r = v.getYY(), - l = v.getDX(), - h = v.getDY(), - o = s.barWidth / e, - C, k = s.ohlcType, - f = Math.round(o * 0.5 * e), - a = d.open, - y = d.close, - B = d.maxY, - p = d.minY, - q = d.startIdx, - m, g, E, n, A, x, w = s.lineWidth * t.devicePixelRatio / 2; - w -= Math.floor(w); - u.save(); - C = this.raiseTemplate; - C.useAttributes(u, z); - u.beginPath(); - for (x = c; x < b; x++) { - if (a[x] <= y[x]) { - m = Math.round(a[x] * r + h) + w; - g = Math.round(B[x] * r + h) + w; - E = Math.round(p[x] * r + h) + w; - n = Math.round(y[x] * r + h) + w; - A = Math.round(j[q[x]] * e + l) + w; - D[k](u, m, g, E, n, A, f) - } - } - u.fillStroke(C.attr); - u.restore(); - u.save(); - C = this.dropTemplate; - C.useAttributes(u, z); - u.beginPath(); - for (x = c; x < b; x++) { - if (a[x] > y[x]) { - m = Math.round(a[x] * r + h) + w; - g = Math.round(B[x] * r + h) + w; - E = Math.round(p[x] * r + h) + w; - n = Math.round(y[x] * r + h) + w; - A = Math.round(j[q[x]] * e + l) + w; - D[k](u, m, g, E, n, A, f) - } - } - u.fillStroke(C.attr); - u.restore() - } -}); -Ext.define("Ext.chart.series.CandleStick", { - extend: "Ext.chart.series.Cartesian", - requires: ["Ext.chart.series.sprite.CandleStick"], - alias: "series.candlestick", - type: "candlestick", - seriesType: "candlestickSeries", - config: { - openField: null, - highField: null, - lowField: null, - closeField: null - }, - fieldCategoryY: ["Open", "High", "Low", "Close"], - themeColorCount: function() { - return 2 - } -}); -Ext.define("Ext.chart.series.Polar", { - extend: "Ext.chart.series.Series", - config: { - rotation: 0, - radius: null, - center: [0, 0], - offsetX: 0, - offsetY: 0, - showInLegend: true, - xField: null, - yField: null, - angleField: null, - radiusField: null, - xAxis: null, - yAxis: null - }, - directions: ["X", "Y"], - fieldCategoryX: ["X"], - fieldCategoryY: ["Y"], - deprecatedConfigs: { - field: "angleField", - lengthField: "radiusField" - }, - constructor: function(b) { - var c = this, - a = c.getConfigurator(), - e = a.configs, - d; - if (b) { - for (d in c.deprecatedConfigs) { - if (d in b && !(b in e)) { - Ext.raise("'" + d + "' config has been deprecated. Please use the '" + c.deprecatedConfigs[d] + "' config instead.") - } - } - } - c.callParent([b]) - }, - getXField: function() { - return this.getAngleField() - }, - updateXField: function(a) { - this.setAngleField(a) - }, - getYField: function() { - return this.getRadiusField() - }, - updateYField: function(a) { - this.setRadiusField(a) - }, - applyXAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - applyYAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - getXRange: function() { - return [this.dataRange[0], this.dataRange[2]] - }, - getYRange: function() { - return [this.dataRange[1], this.dataRange[3]] - }, - themeColorCount: function() { - var c = this, - a = c.getStore(), - b = a && a.getCount() || 0; - return b - }, - isStoreDependantColorCount: true, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer(), - centerX: 0, - centerY: 0, - rotationCenterX: 0, - rotationCenterY: 0 - } - }, - applyRotation: function(a) { - return Ext.draw.sprite.AttributeParser.angle(a) - }, - updateRotation: function(a) { - var b = this.getSprites(); - if (b && b[0]) { - b[0].setAttributes({ - baseRotation: a - }) - } - } -}); -Ext.define("Ext.chart.series.Gauge", { - alias: "series.gauge", - extend: "Ext.chart.series.Polar", - type: "gauge", - seriesType: "pieslice", - requires: ["Ext.draw.sprite.Sector"], - config: { - needle: false, - needleLength: 90, - needleWidth: 4, - donut: 30, - showInLegend: false, - value: null, - colors: null, - sectors: null, - minimum: 0, - maximum: 100, - rotation: 0, - totalAngle: Math.PI / 2, - rect: [0, 0, 1, 1], - center: [0.5, 0.75], - radius: 0.5, - wholeDisk: false - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - updateNeedle: function(b) { - var a = this, - d = a.getSprites(), - c = a.valueToAngle(a.getValue()); - if (d && d.length) { - d[0].setAttributes({ - startAngle: (b ? c : 0), - endAngle: c, - strokeOpacity: (b ? 1 : 0), - lineWidth: (b ? a.getNeedleWidth() : 0) - }); - a.doUpdateStyles() - } - }, - themeColorCount: function() { - var c = this, - a = c.getStore(), - b = a && a.getCount() || 0; - return b + (c.getNeedle() ? 0 : 1) - }, - updateColors: function(a, b) { - var f = this, - h = f.getSectors(), - j = h && h.length, - e = f.getSprites(), - c = Ext.Array.clone(a), - g = a && a.length, - d; - if (!g || !a[0]) { - return - } - for (d = 0; d < j; d++) { - c[d + 1] = h[d].color || c[d + 1] || a[d % g] - } - if (e.length) { - e[0].setAttributes({ - strokeStyle: c[0] - }) - } - this.setSubStyle({ - fillStyle: c, - strokeStyle: c - }); - this.doUpdateStyles() - }, - updateRect: function(f) { - var d = this.getWholeDisk(), - c = d ? Math.PI : this.getTotalAngle() / 2, - g = this.getDonut() / 100, - e, b, a; - if (c <= Math.PI / 2) { - e = 2 * Math.sin(c); - b = 1 - g * Math.cos(c) - } else { - e = 2; - b = 1 - Math.cos(c) - } - a = Math.min(f[2] / e, f[3] / b); - this.setRadius(a); - this.setCenter([f[2] / 2, a + (f[3] - b * a) / 2]) - }, - updateCenter: function(a) { - this.setStyle({ - centerX: a[0], - centerY: a[1], - rotationCenterX: a[0], - rotationCenterY: a[1] - }); - this.doUpdateStyles() - }, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a - (this.getTotalAngle() + Math.PI) / 2 - }); - this.doUpdateStyles() - }, - doUpdateShape: function(b, f) { - var a, d = this.getSectors(), - c = (d && d.length) || 0, - e = this.getNeedleLength() / 100; - a = [b * e, b]; - while (c--) { - a.push(b) - } - this.setSubStyle({ - endRho: a, - startRho: b / 100 * f - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - var b = this.getDonut(); - this.doUpdateShape(a, b) - }, - updateDonut: function(b) { - var a = this.getRadius(); - this.doUpdateShape(a, b) - }, - valueToAngle: function(a) { - a = this.applyValue(a); - return this.getTotalAngle() * (a - this.getMinimum()) / (this.getMaximum() - this.getMinimum()) - }, - applyValue: function(a) { - return Math.min(this.getMaximum(), Math.max(a, this.getMinimum())) - }, - updateValue: function(b) { - var a = this, - c = a.getNeedle(), - e = a.valueToAngle(b), - d = a.getSprites(); - d[0].rendererData.value = b; - d[0].setAttributes({ - startAngle: (c ? e : 0), - endAngle: e - }); - a.doUpdateStyles() - }, - processData: function() { - var f = this, - j = f.getStore(), - a, d, h, b, g, e = j && j.first(), - c, i; - if (e) { - c = f.getXField(); - if (c) { - i = e.get(c) - } - } - if (a = f.getXAxis()) { - d = a.getMinimum(); - h = a.getMaximum(); - b = a.getSprites()[0].fx; - g = b.getDuration(); - b.setDuration(0); - if (Ext.isNumber(d)) { - f.setMinimum(d) - } else { - a.setMinimum(f.getMinimum()) - } - if (Ext.isNumber(h)) { - f.setMaximum(h) - } else { - a.setMaximum(f.getMaximum()) - } - b.setDuration(g) - } - if (!Ext.isNumber(i)) { - i = f.getMinimum() - } - f.setValue(i) - }, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer(), - fx: { - customDurations: { - translationX: 0, - translationY: 0, - rotationCenterX: 0, - rotationCenterY: 0, - centerX: 0, - centerY: 0, - startRho: 0, - endRho: 0, - baseRotation: 0 - } - } - } - }, - normalizeSectors: function(f) { - var d = this, - c = (f && f.length) || 0, - b, e, g, a; - if (c) { - for (b = 0; b < c; b++) { - e = f[b]; - if (typeof e === "number") { - f[b] = { - start: (b > 0 ? f[b - 1].end : d.getMinimum()), - end: Math.min(e, d.getMaximum()) - }; - if (b == (c - 1) && f[b].end < d.getMaximum()) { - f[b + 1] = { - start: f[b].end, - end: d.getMaximum() - } - } - } else { - if (typeof e.start === "number") { - g = Math.max(e.start, d.getMinimum()) - } else { - g = (b > 0 ? f[b - 1].end : d.getMinimum()) - } - if (typeof e.end === "number") { - a = Math.min(e.end, d.getMaximum()) - } else { - a = d.getMaximum() - } - f[b].start = g; - f[b].end = a - } - } - } else { - f = [{ - start: d.getMinimum(), - end: d.getMaximum() - }] - } - return f - }, - getSprites: function() { - var j = this, - m = j.getStore(), - l = j.getValue(), - c, g; - if (!m && !Ext.isNumber(l)) { - return [] - } - var h = j.getChart(), - b = j.getAnimation() || h && h.getAnimation(), - f = j.sprites, - k = 0, - o, n, e, d, a = []; - if (f && f.length) { - f[0].setAnimation(b); - return f - } - d = { - store: m, - field: j.getXField(), - angleField: j.getXField(), - value: l, - series: j - }; - o = j.createSprite(); - o.setAttributes({ - zIndex: 10 - }, true); - o.rendererData = d; - o.rendererIndex = k++; - a.push(j.getNeedleWidth()); - j.getLabel().getTemplate().setField(true); - n = j.normalizeSectors(j.getSectors()); - for (c = 0, g = n.length; c < g; c++) { - e = { - startAngle: j.valueToAngle(n[c].start), - endAngle: j.valueToAngle(n[c].end), - label: n[c].label, - fillStyle: n[c].color, - strokeOpacity: 0, - doCallout: false, - labelOverflowPadding: -1 - }; - Ext.apply(e, n[c].style); - o = j.createSprite(); - o.rendererData = d; - o.rendererIndex = k++; - o.setAttributes(e, true); - a.push(e.lineWidth) - } - j.setSubStyle({ - lineWidth: a - }); - j.doUpdateStyles(); - return f - } -}); -Ext.define("Ext.chart.series.sprite.Line", { - alias: "sprite.lineSeries", - extend: "Ext.chart.series.sprite.Aggregative", - inheritableStatics: { - def: { - processors: { - smooth: "bool", - fillArea: "bool", - step: "bool", - preciseStroke: "bool", - xAxis: "default", - yCap: "default" - }, - defaults: { - smooth: false, - fillArea: false, - step: false, - preciseStroke: true, - xAxis: null, - yCap: Math.pow(2, 20), - yJump: 50 - }, - triggers: { - dataX: "dataX,bbox,smooth", - dataY: "dataY,bbox,smooth", - smooth: "smooth" - }, - updaters: { - smooth: function(a) { - var c = a.dataX, - b = a.dataY; - if (a.smooth && c && b && c.length > 2 && b.length > 2) { - this.smoothX = Ext.draw.Draw.spline(c); - this.smoothY = Ext.draw.Draw.spline(b) - } else { - delete this.smoothX; - delete this.smoothY - } - } - } - } - }, - list: null, - updatePlainBBox: function(d) { - var b = this.attr, - c = Math.min(0, b.dataMinY), - a = Math.max(0, b.dataMaxY); - d.x = b.dataMinX; - d.y = c; - d.width = b.dataMaxX - b.dataMinX; - d.height = a - c - }, - drawStrip: function(a, c) { - a.moveTo(c[0], c[1]); - for (var b = 2, d = c.length; b < d; b += 2) { - a.lineTo(c[b], c[b + 1]) - } - }, - drawStraightStroke: function(p, q, e, d, u, h) { - var w = this, - o = w.attr, - n = o.renderer, - g = o.step, - a = true, - l = { - type: "line", - smooth: false, - step: g - }, - m = [], - l, z, v, f, k, j, t, c, s, b, r; - for (r = 3; r < u.length; r += 3) { - t = u[r - 3]; - c = u[r - 2]; - k = u[r]; - j = u[r + 1]; - s = u[r + 3]; - b = u[r + 4]; - if (n) { - l.x = k; - l.y = j; - l.x0 = t; - l.y0 = c; - v = [w, l, w.rendererData, e + r / 3]; - z = Ext.callback(n, null, v, 0, w.getSeries()) - } - if (Ext.isNumber(k + j + t + c)) { - if (a) { - q.beginPath(); - q.moveTo(t, c); - m.push(t, c); - f = t; - a = false - } - } else { - continue - } - if (g) { - q.lineTo(k, c); - m.push(k, c) - } - q.lineTo(k, j); - m.push(k, j); - if (z || !(Ext.isNumber(s + b))) { - q.save(); - Ext.apply(q, z); - if (o.fillArea) { - q.lineTo(k, h); - q.lineTo(f, h); - q.closePath(); - q.fill() - } - q.beginPath(); - w.drawStrip(q, m); - m = []; - q.stroke(); - q.restore(); - q.beginPath(); - a = true - } - } - }, - calculateScale: function(c, a) { - var b = 0, - d = c; - while (d < a && c > 0) { - b++; - d += c >> b - } - return Math.pow(2, b > 0 ? b - 1 : b) - }, - drawSmoothStroke: function(u, v, c, b, C, f) { - var G = this, - t = G.attr, - d = t.step, - z = t.matrix, - s = t.renderer, - e = z.getXX(), - p = z.getYY(), - m = z.getDX(), - k = z.getDY(), - r = G.smoothX, - q = G.smoothY, - I = G.calculateScale(t.dataX.length, b), - o, F, n, E, h, g, B, a, A, w, H, D, l = { - type: "line", - smooth: true, - step: d - }; - v.beginPath(); - v.moveTo(r[c * 3] * e + m, q[c * 3] * p + k); - for (A = 0, w = c * 3 + 1; A < C.length - 3; A += 3, w += 3 * I) { - o = r[w] * e + m; - F = q[w] * p + k; - n = r[w + 1] * e + m; - E = q[w + 1] * p + k; - h = u.roundPixel(C[A + 3]); - g = C[A + 4]; - B = u.roundPixel(C[A]); - a = C[A + 1]; - if (s) { - l.x0 = B; - l.y0 = a; - l.cx1 = o; - l.cy1 = F; - l.cx2 = n; - l.cy2 = E; - l.x = h; - l.y = g; - D = [G, l, G.rendererData, c + A / 3 + 1]; - H = Ext.callback(s, null, D, 0, G.getSeries()); - v.save(); - Ext.apply(v, H) - } - if (t.fillArea) { - v.moveTo(B, a); - v.bezierCurveTo(o, F, n, E, h, g); - v.lineTo(h, f); - v.lineTo(B, f); - v.lineTo(B, a); - v.closePath(); - v.fill(); - v.beginPath() - } - v.moveTo(B, a); - v.bezierCurveTo(o, F, n, E, h, g); - v.stroke(); - v.moveTo(B, a); - v.closePath(); - if (s) { - v.restore() - } - v.beginPath(); - v.moveTo(h, g) - } - v.beginPath() - }, - drawLabel: function(k, i, h, o, a) { - var q = this, - n = q.attr, - e = q.getMarker("labels"), - d = e.getTemplate(), - m = q.labelCfg || (q.labelCfg = {}), - c = q.surfaceMatrix, - g, f, j = n.labelOverflowPadding, - l, b, r, p, s; - m.x = c.x(i, h); - m.y = c.y(i, h); - if (n.flipXY) { - m.rotationRads = Math.PI * 0.5 - } else { - m.rotationRads = 0 - } - m.text = k; - if (d.attr.renderer) { - p = [k, e, m, q.rendererData, o]; - r = Ext.callback(d.attr.renderer, null, p, 0, q.getSeries()); - if (typeof r === "string") { - m.text = r - } else { - if (typeof r === "object") { - if ("text" in r) { - m.text = r.text - } - s = true - } - } - } - b = q.getMarkerBBox("labels", o, true); - if (!b) { - q.putMarker("labels", m, o); - b = q.getMarkerBBox("labels", o, true) - } - l = b.height / 2; - g = i; - switch (d.attr.display) { - case "under": - f = h - l - j; - break; - case "rotate": - g += j; - f = h - j; - m.rotationRads = -Math.PI / 4; - break; - default: - f = h + l + j - } - m.x = c.x(g, f); - m.y = c.y(g, f); - if (s) { - Ext.apply(m, r) - } - q.putMarker("labels", m, o) - }, - drawMarker: function(j, h, d) { - var g = this, - e = g.attr, - f = e.renderer, - c = g.surfaceMatrix, - b = {}, - i, a; - if (f && g.getMarker("markers")) { - b.type = "marker"; - b.x = j; - b.y = h; - a = [g, b, g.rendererData, d]; - i = Ext.callback(f, null, a, 0, g.getSeries()); - if (i) { - Ext.apply(b, i) - } - } - b.translationX = c.x(j, h); - b.translationY = c.y(j, h); - delete b.x; - delete b.y; - g.putMarker("markers", b, d, !f) - }, - drawStroke: function(a, c, h, b, f, e) { - var d = this, - g = d.attr.smooth && d.smoothX && d.smoothY; - if (g) { - d.drawSmoothStroke(a, c, h, b, f, e) - } else { - d.drawStraightStroke(a, c, h, b, f, e) - } - }, - renderAggregates: function(B, w, l, N, o, I, D) { - var m = this, - k = m.attr, - s = k.dataX, - r = k.dataY, - h = k.labels, - v = k.xAxis, - a = k.yCap, - g = k.smooth && m.smoothX && m.smoothY, - d = h && m.getMarker("labels"), - t = m.getMarker("markers"), - E = k.matrix, - u = N.devicePixelRatio, - C = E.getXX(), - f = E.getYY(), - c = E.getDX(), - b = E.getDY(), - q = m.list || (m.list = []), - F = B.minX, - e = B.maxX, - j = B.minY, - P = B.maxY, - U = B.startIdx, - S = true, - Q, T, L, K, R, G; - m.rendererData = { - store: m.getStore() - }; - q.length = 0; - for (R = w; R < l; R++) { - var O = F[R], - p = e[R], - M = j[R], - n = P[R]; - if (O < p) { - q.push(O * C + c, M * f + b, U[R]); - q.push(p * C + c, n * f + b, U[R]) - } else { - if (O > p) { - q.push(p * C + c, n * f + b, U[R]); - q.push(O * C + c, M * f + b, U[R]) - } else { - q.push(p * C + c, n * f + b, U[R]) - } - } - } - if (q.length) { - for (R = 0; R < q.length; R += 3) { - L = q[R]; - K = q[R + 1]; - if (Ext.isNumber(L + K)) { - if (K > a) { - K = a - } else { - if (K < -a) { - K = -a - } - } - q[R + 1] = K - } else { - S = false; - continue - } - G = q[R + 2]; - if (t) { - m.drawMarker(L, K, G) - } - if (d && h[G]) { - m.drawLabel(h[G], L, K, G, D) - } - } - m.isContinuousLine = S; - if (g && !S) { - Ext.raise("Line smoothing in only supported for gapless data, where all data points are finite numbers.") - } - if (v) { - T = v.getAlignment() === "vertical"; - if (Ext.isNumber(v.floatingAtCoord)) { - Q = (T ? D[2] : D[3]) - v.floatingAtCoord - } else { - Q = T ? D[0] : D[1] - } - } else { - Q = k.flipXY ? D[0] : D[1] - } - if (k.preciseStroke) { - if (k.fillArea) { - o.fill() - } - if (k.transformFillStroke) { - k.inverseMatrix.toContext(o) - } - m.drawStroke(N, o, w, l, q, Q); - if (k.transformFillStroke) { - k.matrix.toContext(o) - } - o.stroke() - } else { - m.drawStroke(N, o, w, l, q, Q); - if (S && g && k.fillArea && !k.renderer) { - var A = s[s.length - 1] * C + c + u, - z = r[r.length - 1] * f + b, - J = s[0] * C + c - u, - H = r[0] * f + b; - o.lineTo(A, z); - o.lineTo(A, Q - k.lineWidth); - o.lineTo(J, Q - k.lineWidth); - o.lineTo(J, H) - } - if (k.transformFillStroke) { - k.matrix.toContext(o) - } - if (k.fillArea) { - o.fillStroke(k, true) - } else { - o.stroke(true) - } - } - } - } -}); -Ext.define("Ext.chart.series.Line", { - extend: "Ext.chart.series.Cartesian", - alias: "series.line", - type: "line", - seriesType: "lineSeries", - requires: ["Ext.chart.series.sprite.Line"], - config: { - selectionTolerance: 20, - smooth: false, - step: false, - fill: undefined, - aggregator: { - strategy: "double" - } - }, - defaultSmoothness: 3, - overflowBuffer: 1, - themeMarkerCount: function() { - return 1 - }, - getDefaultSpriteConfig: function() { - var d = this, - e = d.callParent(arguments), - c = Ext.apply({}, d.getStyle()), - b, a = false; - if (typeof d.config.fill != "undefined") { - if (d.config.fill) { - a = true; - if (typeof c.fillStyle == "undefined") { - if (typeof c.strokeStyle == "undefined") { - b = d.getStyleWithTheme(); - c.fillStyle = b.fillStyle; - c.strokeStyle = b.strokeStyle - } else { - c.fillStyle = c.strokeStyle - } - } - } - } else { - if (c.fillStyle) { - a = true - } - } - if (!a) { - delete c.fillStyle - } - c = Ext.apply(e || {}, c); - return Ext.apply(c, { - fillArea: a, - step: d.config.step, - smooth: d.config.smooth, - selectionTolerance: d.config.selectionTolerance - }) - }, - updateStep: function(b) { - var a = this.getSprites()[0]; - if (a && a.attr.step !== b) { - a.setAttributes({ - step: b - }) - } - }, - updateFill: function(b) { - var a = this.getSprites()[0]; - if (a && a.attr.fillArea !== b) { - a.setAttributes({ - fillArea: b - }) - } - }, - updateSmooth: function(a) { - var b = this.getSprites()[0]; - if (b && b.attr.smooth !== a) { - b.setAttributes({ - smooth: a - }) - } - } -}); -Ext.define("Ext.chart.series.sprite.PieSlice", { - extend: "Ext.draw.sprite.Sector", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - alias: "sprite.pieslice", - inheritableStatics: { - def: { - processors: { - doCallout: "bool", - label: "string", - rotateLabels: "bool", - labelOverflowPadding: "number", - renderer: "default" - }, - defaults: { - doCallout: true, - rotateLabels: true, - label: "", - labelOverflowPadding: 10, - renderer: null - } - } - }, - config: { - rendererData: null, - rendererIndex: 0, - series: null - }, - setGradientBBox: function(q, k) { - var j = this, - i = j.attr, - g = (i.fillStyle && i.fillStyle.isGradient) || (i.strokeStyle && i.strokeStyle.isGradient); - if (g && !i.constrainGradients) { - var b = j.getMidAngle(), - d = i.margin, - e = i.centerX, - c = i.centerY, - a = i.endRho, - l = i.matrix, - o = l.getScaleX(), - n = l.getScaleY(), - m = o * a, - f = n * a, - p = { - width: m + m, - height: f + f - }; - if (d) { - e += d * Math.cos(b); - c += d * Math.sin(b) - } - p.x = l.x(e, c) - m; - p.y = l.y(e, c) - f; - q.setGradientBBox(p) - } else { - j.callParent([q, k]) - } - }, - render: function(b, c, g, f) { - var e = this, - a = e.attr, - h = {}, - d; - if (a.renderer) { - h = { - type: "sector", - text: a.text, - centerX: a.centerX, - centerY: a.centerY, - margin: a.margin, - startAngle: Math.min(a.startAngle, a.endAngle), - endAngle: Math.max(a.startAngle, a.endAngle), - startRho: Math.min(a.startRho, a.endRho), - endRho: Math.max(a.startRho, a.endRho) - }; - d = Ext.callback(a.renderer, null, [e, h, e.rendererData, e.rendererIndex], 0, e.getSeries()); - e.setAttributes(d); - e.useAttributes(c, g) - } - e.callParent([b, c, g, f]); - if (a.label && e.getMarker("labels")) { - e.placeLabel() - } - }, - placeLabel: function() { - var z = this, - s = z.attr, - r = s.attributeId, - t = Math.min(s.startAngle, s.endAngle), - p = Math.max(s.startAngle, s.endAngle), - k = (t + p) * 0.5, - n = s.margin, - h = s.centerX, - g = s.centerY, - f = Math.sin(k), - c = Math.cos(k), - v = Math.min(s.startRho, s.endRho) + n, - m = Math.max(s.startRho, s.endRho) + n, - l = (v + m) * 0.5, - b = z.surfaceMatrix, - o = z.labelCfg || (z.labelCfg = {}), - e = z.getMarker("labels"), - d = e.getTemplate(), - a = d.getCalloutLine(), - q = a && a.length || 40, - u, j, i, A, w; - b.appendMatrix(s.matrix); - o.text = s.label; - j = h + c * l; - i = g + f * l; - o.x = b.x(j, i); - o.y = b.y(j, i); - j = h + c * m; - i = g + f * m; - o.calloutStartX = b.x(j, i); - o.calloutStartY = b.y(j, i); - j = h + c * (m + q); - i = g + f * (m + q); - o.calloutPlaceX = b.x(j, i); - o.calloutPlaceY = b.y(j, i); - if (!s.rotateLabels) { - o.rotationRads = 0 - } else { - switch (d.attr.orientation) { - case "horizontal": - o.rotationRads = k + Math.atan2(b.y(1, 0) - b.y(0, 0), b.x(1, 0) - b.x(0, 0)) + Math.PI / 2; - break; - case "vertical": - o.rotationRads = k + Math.atan2(b.y(1, 0) - b.y(0, 0), b.x(1, 0) - b.x(0, 0)); - break - } - } - o.calloutColor = (a && a.color) || z.attr.fillStyle; - if (a) { - if (a.width) { - o.calloutWidth = a.width - } - } else { - o.calloutHasLine = false - } - o.globalAlpha = s.globalAlpha * s.fillOpacity; - o.hidden = (s.startAngle == s.endAngle); - if (d.attr.renderer) { - w = [z.attr.label, e, o, z.rendererData, z.rendererIndex]; - A = Ext.callback(d.attr.renderer, null, w, 0, z.getSeries()); - if (typeof A === "string") { - o.text = A - } else { - Ext.apply(o, A) - } - } - z.putMarker("labels", o, r); - u = z.getMarkerBBox("labels", r, true); - if (u) { - if (s.doCallout) { - if (d.attr.display === "outside") { - z.putMarker("labels", { - callout: 1 - }, r) - } else { - if (d.attr.display === "inside") { - z.putMarker("labels", { - callout: 0 - }, r) - } else { - z.putMarker("labels", { - callout: 1 - z.sliceContainsLabel(s, u) - }, r) - } - } - } else { - z.putMarker("labels", { - globalAlpha: z.sliceContainsLabel(s, u) - }, r) - } - } - }, - sliceContainsLabel: function(d, f) { - var e = d.labelOverflowPadding, - h = (d.endRho + d.startRho) / 2, - g = h + (f.width + e) / 2, - i = h - (f.width + e) / 2, - j, c, b, a; - if (e < 0) { - return 1 - } - if (f.width + e * 2 > (d.endRho - d.startRho)) { - return 0 - } - c = Math.sqrt(d.endRho * d.endRho - g * g); - b = Math.sqrt(d.endRho * d.endRho - i * i); - j = Math.abs(d.endAngle - d.startAngle); - a = (j > Math.PI / 2 ? i : Math.abs(Math.tan(j / 2)) * i); - if (f.height + e * 2 > Math.min(c, b, a) * 2) { - return 0 - } - return 1 - } -}); -Ext.define("Ext.chart.series.Pie", { - extend: "Ext.chart.series.Polar", - requires: ["Ext.chart.series.sprite.PieSlice"], - type: "pie", - alias: "series.pie", - seriesType: "pieslice", - config: { - donut: 0, - rotation: 0, - clockwise: true, - totalAngle: 2 * Math.PI, - hidden: [], - radiusFactor: 100, - highlightCfg: { - margin: 20 - }, - style: {} - }, - directions: ["X"], - applyLabel: function(a, b) { - if (Ext.isObject(a) && !Ext.isString(a.orientation)) { - Ext.apply(a = Ext.Object.chain(a), { - orientation: "vertical" - }) - } - return this.callParent([a, b]) - }, - updateLabelData: function() { - var h = this, - j = h.getStore(), - g = j.getData().items, - e = h.getSprites(), - a = h.getLabel().getTemplate().getField(), - d = h.getHidden(), - b, f, c, k; - if (e.length && a) { - c = []; - for (b = 0, f = g.length; b < f; b++) { - c.push(g[b].get(a)) - } - for (b = 0, f = e.length; b < f; b++) { - k = e[b]; - k.setAttributes({ - label: c[b] - }); - k.putMarker("labels", { - hidden: d[b] - }, k.attr.attributeId) - } - } - }, - coordinateX: function() { - var t = this, - f = t.getStore(), - q = f.getData().items, - c = q.length, - b = t.getXField(), - e = t.getYField(), - l, a = 0, - m, k, s = 0, - o = t.getHidden(), - d = [], - p, g = 0, - h = t.getTotalAngle(), - r = t.getClockwise() ? 1 : -1, - j = t.getSprites(), - n; - if (!j) { - return - } - for (p = 0; p < c; p++) { - l = Math.abs(Number(q[p].get(b))) || 0; - k = e && Math.abs(Number(q[p].get(e))) || 0; - if (!o[p]) { - a += l; - if (k > s) { - s = k - } - } - d[p] = a; - if (p >= o.length) { - o[p] = false - } - } - o.length = c; - t.maxY = s; - if (a !== 0) { - m = h / a - } - for (p = 0; p < c; p++) { - j[p].setAttributes({ - startAngle: g, - endAngle: g = (m ? r * d[p] * m : 0), - globalAlpha: 1 - }) - } - if (c < t.sprites.length) { - for (p = c; p < t.sprites.length; p++) { - n = t.sprites[p]; - n.getMarker("labels").clear(n.getId()); - n.releaseMarker("labels"); - n.destroy() - } - t.sprites.length = c - } - for (p = c; p < t.sprites.length; p++) { - j[p].setAttributes({ - startAngle: h, - endAngle: h, - globalAlpha: 0 - }) - } - t.getChart().refreshLegendStore() - }, - updateCenter: function(a) { - this.setStyle({ - translationX: a[0] + this.getOffsetX(), - translationY: a[1] + this.getOffsetY() - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - this.setStyle({ - startRho: a * this.getDonut() * 0.01, - endRho: a * this.getRadiusFactor() * 0.01 - }); - this.doUpdateStyles() - }, - getStyleByIndex: function(c) { - var g = this, - j = g.getStore(), - k = j.getAt(c), - f = g.getYField(), - d = g.getRadius(), - a = {}, - e, b, h; - if (k) { - h = f && Math.abs(Number(k.get(f))) || 0; - e = d * g.getDonut() * 0.01; - b = d * g.getRadiusFactor() * 0.01; - a = g.callParent([c]); - a.startRho = e; - a.endRho = g.maxY ? (e + (b - e) * h / g.maxY) : b - } - return a - }, - updateDonut: function(b) { - var a = this.getRadius(); - this.setStyle({ - startRho: a * b * 0.01, - endRho: a * this.getRadiusFactor() * 0.01 - }); - this.doUpdateStyles() - }, - rotationOffset: -Math.PI / 2, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a + this.rotationOffset - }); - this.doUpdateStyles() - }, - updateTotalAngle: function(a) { - this.processData() - }, - getSprites: function() { - var k = this, - h = k.getChart(), - n = k.getStore(); - if (!h || !n) { - return [] - } - k.getColors(); - k.getSubStyle(); - var j = n.getData().items, - b = j.length, - d = k.getAnimation() || h && h.getAnimation(), - g = k.sprites, - o, l = 0, - f, e, c = false, - m = k.getLabel(), - a = m.getTemplate(); - f = { - store: n, - field: k.getXField(), - angleField: k.getXField(), - radiusField: k.getYField(), - series: k - }; - for (e = 0; e < b; e++) { - o = g[e]; - if (!o) { - o = k.createSprite(); - if (k.getHighlight()) { - o.config.highlight = k.getHighlight(); - o.addModifier("highlight", true) - } - if (a.getField()) { - a.setAttributes({ - labelOverflowPadding: k.getLabelOverflowPadding() - }); - a.fx.setCustomDurations({ - callout: 200 - }) - } - o.setAttributes(k.getStyleByIndex(e)); - o.rendererData = f; - o.rendererIndex = l++; - c = true - } - o.setAnimation(d) - } - if (c) { - k.doUpdateStyles() - } - return k.sprites - }, - betweenAngle: function(d, f, c) { - var e = Math.PI * 2, - g = this.rotationOffset; - if (!this.getClockwise()) { - d *= -1; - f *= -1; - c *= -1; - f -= g; - c -= g - } else { - f += g; - c += g - } - d -= f; - c -= f; - d %= e; - c %= e; - d += e; - c += e; - d %= e; - c %= e; - return d < c || c === 0 - }, - getItemForAngle: function(a) { - var h = this, - f = h.getSprites(), - d; - a %= Math.PI * 2; - while (a < 0) { - a += Math.PI * 2 - } - if (f) { - var j = h.getStore(), - g = j.getData().items, - c = h.getHidden(), - b = 0, - e = j.getCount(); - for (; b < e; b++) { - if (!c[b]) { - d = f[b].attr; - if (d.startAngle <= a && d.endAngle >= a) { - return { - series: h, - sprite: f[b], - index: b, - record: g[b], - field: h.getXField() - } - } - } - } - } - return null - }, - getItemForPoint: function(f, e) { - var t = this, - c = t.getSprites(); - if (c) { - var s = t.getCenter(), - q = t.getOffsetX(), - p = t.getOffsetY(), - j = f - s[0] + q, - h = e - s[1] + p, - b = t.getStore(), - g = t.getDonut(), - o = b.getData().items, - r = Math.atan2(h, j) - t.getRotation(), - a = Math.sqrt(j * j + h * h), - l = t.getRadius() * g * 0.01, - m = t.getHidden(), - n, d, k; - for (n = 0, d = o.length; n < d; n++) { - if (!m[n]) { - k = c[n].attr; - if (a >= l + k.margin && a <= k.endRho + k.margin) { - if (t.betweenAngle(r, k.startAngle, k.endAngle)) { - return { - series: t, - sprite: c[n], - index: n, - record: o[n], - field: t.getXField() - } - } - } - } - } - return null - } - }, - provideLegendInfo: function(f) { - var h = this, - j = h.getStore(); - if (j) { - var g = j.getData().items, - b = h.getLabel().getTemplate().getField(), - c = h.getXField(), - e = h.getHidden(), - d, a, k; - for (d = 0; d < g.length; d++) { - a = h.getStyleByIndex(d); - k = a.fillStyle; - if (Ext.isObject(k)) { - k = k.stops && k.stops[0].color - } - f.push({ - name: b ? String(g[d].get(b)) : c + " " + d, - mark: k || a.strokeStyle || "black", - disabled: e[d], - series: h.getId(), - index: d - }) - } - } - } -}); -Ext.define("Ext.chart.series.sprite.Pie3DPart", { - extend: "Ext.draw.sprite.Path", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - alias: "sprite.pie3dPart", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - margin: "number", - thickness: "number", - bevelWidth: "number", - distortion: "number", - baseColor: "color", - colorSpread: "number", - baseRotation: "number", - part: "enums(top,bottom,start,end,innerFront,innerBack,outerFront,outerBack)", - label: "string" - }, - aliases: { - rho: "endRho" - }, - triggers: { - centerX: "path,bbox", - centerY: "path,bbox", - startAngle: "path,partZIndex", - endAngle: "path,partZIndex", - startRho: "path", - endRho: "path,bbox", - margin: "path,bbox", - thickness: "path", - distortion: "path", - baseRotation: "path,partZIndex", - baseColor: "partZIndex,partColor", - colorSpread: "partColor", - part: "path,partZIndex", - globalAlpha: "canvas,alpha" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: Math.PI * 2, - endAngle: Math.PI * 2, - startRho: 0, - endRho: 150, - margin: 0, - thickness: 35, - distortion: 0.5, - baseRotation: 0, - baseColor: "white", - colorSpread: 1, - miterLimit: 1, - bevelWidth: 5, - strokeOpacity: 0, - part: "top", - label: "" - }, - updaters: { - alpha: "alphaUpdater", - partColor: "partColorUpdater", - partZIndex: "partZIndexUpdater" - } - } - }, - bevelParams: [], - constructor: function(a) { - this.callParent([a]); - this.bevelGradient = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: "rgba(255,255,255,0)" - }, { - offset: 0.7, - color: "rgba(255,255,255,0.6)" - }, { - offset: 1, - color: "rgba(255,255,255,0)" - }] - }) - }, - alphaUpdater: function(a) { - var d = this, - c = a.globalAlpha, - b = d.oldOpacity; - if (c !== b && (c === 1 || b === 1)) { - d.scheduleUpdater(a, "path", ["globalAlpha"]); - d.oldOpacity = c - } - }, - partColorUpdater: function(a) { - var d = Ext.draw.Color.fly(a.baseColor), - b = d.toString(), - e = a.colorSpread, - c; - switch (a.part) { - case "top": - c = new Ext.draw.gradient.Radial({ - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - }, - stops: [{ - offset: 0, - color: d.createLighter(0.1 * e) - }, { - offset: 1, - color: d.createDarker(0.1 * e) - }] - }); - break; - case "bottom": - c = new Ext.draw.gradient.Radial({ - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - }, - stops: [{ - offset: 0, - color: d.createDarker(0.2 * e) - }, { - offset: 1, - color: d.toString() - }] - }); - break; - case "outerFront": - case "outerBack": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.15 * e).toString() - }, { - offset: 0.3, - color: b - }, { - offset: 0.8, - color: d.createLighter(0.2 * e).toString() - }, { - offset: 1, - color: d.createDarker(0.25 * e).toString() - }] - }); - break; - case "start": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 1, - color: d.createLighter(0.2 * e).toString() - }] - }); - break; - case "end": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 1, - color: d.createLighter(0.2 * e).toString() - }] - }); - break; - case "innerFront": - case "innerBack": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 0.2, - color: d.createLighter(0.2 * e).toString() - }, { - offset: 0.7, - color: b - }, { - offset: 1, - color: d.createDarker(0.1 * e).toString() - }] - }); - break - } - a.fillStyle = c; - a.canvasAttributes.fillStyle = c - }, - partZIndexUpdater: function(a) { - var c = Ext.draw.sprite.AttributeParser.angle, - e = a.baseRotation, - d = a.startAngle, - b = a.endAngle, - f; - switch (a.part) { - case "top": - a.zIndex = 5; - break; - case "outerFront": - d = c(d + e); - b = c(b + e); - if (d >= 0 && b < 0) { - f = Math.sin(d) - } else { - if (d <= 0 && b > 0) { - f = Math.sin(b) - } else { - if (d >= 0 && b > 0) { - if (d > b) { - f = 0 - } else { - f = Math.max(Math.sin(d), Math.sin(b)) - } - } else { - f = 1 - } - } - } - a.zIndex = 4 + f; - break; - case "outerBack": - a.zIndex = 1; - break; - case "start": - a.zIndex = 4 + Math.sin(c(d + e)); - break; - case "end": - a.zIndex = 4 + Math.sin(c(b + e)); - break; - case "innerFront": - a.zIndex = 2; - break; - case "innerBack": - a.zIndex = 4 + Math.sin(c((d + b) / 2 + e)); - break; - case "bottom": - a.zIndex = 0; - break - } - a.dirtyZIndex = true - }, - updatePlainBBox: function(k) { - var f = this.attr, - a = f.part, - b = f.baseRotation, - e = f.centerX, - d = f.centerY, - j, c, i, h, g, l; - if (a === "start") { - c = f.startAngle + b - } else { - if (a === "end") { - c = f.endAngle + b - } - } - if (Ext.isNumber(c)) { - g = Math.sin(c); - l = Math.cos(c); - i = Math.min(e + l * f.startRho, e + l * f.endRho); - h = d + g * f.startRho * f.distortion; - k.x = i; - k.y = h; - k.width = l * (f.endRho - f.startRho); - k.height = f.thickness + g * (f.endRho - f.startRho) * 2; - return - } - if (a === "innerFront" || a === "innerBack") { - j = f.startRho - } else { - j = f.endRho - } - k.width = j * 2; - k.height = j * f.distortion * 2 + f.thickness; - k.x = f.centerX - j; - k.y = f.centerY - j * f.distortion - }, - updateTransformedBBox: function(a) { - if (this.attr.part === "start" || this.attr.part === "end") { - return this.callParent(arguments) - } - return this.updatePlainBBox(a) - }, - updatePath: function(a) { - if (!this.attr.globalAlpha) { - return - } - if (this.attr.endAngle < this.attr.startAngle) { - return - } - this[this.attr.part + "Renderer"](a) - }, - render: function(b, c) { - var d = this, - a = d.attr; - if (!a.globalAlpha) { - return - } - d.callParent([b, c]); - d.bevelRenderer(b, c); - if (a.label && d.getMarker("labels")) { - d.placeLabel() - } - }, - placeLabel: function() { - var z = this, - u = z.attr, - t = u.attributeId, - p = u.margin, - c = u.distortion, - i = u.centerX, - h = u.centerY, - j = u.baseRotation, - v = u.startAngle + j, - r = u.endAngle + j, - m = (v + r) / 2, - w = u.startRho + p, - o = u.endRho + p, - n = (w + o) / 2, - a = Math.sin(m), - b = Math.cos(m), - e = z.surfaceMatrix, - g = z.getMarker("labels"), - f = g.getTemplate(), - d = f.getCalloutLine(), - s = d && d.length || 40, - q = {}, - l, k; - e.appendMatrix(u.matrix); - q.text = u.label; - l = i + b * n; - k = h + a * n * c; - q.x = e.x(l, k); - q.y = e.y(l, k); - l = i + b * o; - k = h + a * o * c; - q.calloutStartX = e.x(l, k); - q.calloutStartY = e.y(l, k); - l = i + b * (o + s); - k = h + a * (o + s) * c; - q.calloutPlaceX = e.x(l, k); - q.calloutPlaceY = e.y(l, k); - q.calloutWidth = 2; - z.putMarker("labels", q, t); - z.putMarker("labels", { - callout: 1 - }, t) - }, - bevelRenderer: function(b, c) { - var f = this, - a = f.attr, - e = a.bevelWidth, - g = f.bevelParams, - d; - for (d = 0; d < g.length; d++) { - c.beginPath(); - c.ellipse.apply(c, g[d]); - c.save(); - c.lineWidth = e; - c.strokeOpacity = e ? 1 : 0; - c.strokeGradient = f.bevelGradient; - c.stroke(a); - c.restore() - } - }, - lidRenderer: function(o, m) { - var k = this.attr, - g = k.margin, - c = k.distortion, - i = k.centerX, - h = k.centerY, - f = k.baseRotation, - j = k.startAngle + f, - e = k.endAngle + f, - d = (j + e) / 2, - l = k.startRho, - b = k.endRho, - n = Math.sin(e), - a = Math.cos(e); - i += Math.cos(d) * g; - h += Math.sin(d) * g * c; - o.ellipse(i, h + m, l, l * c, 0, j, e, false); - o.lineTo(i + a * b, h + m + n * b * c); - o.ellipse(i, h + m, b, b * c, 0, e, j, true); - o.closePath() - }, - topRenderer: function(a) { - this.lidRenderer(a, 0) - }, - bottomRenderer: function(b) { - var a = this.attr; - if (a.globalAlpha < 1 || a.shadowColor !== Ext.draw.Color.RGBA_NONE) { - this.lidRenderer(b, a.thickness) - } - }, - sideRenderer: function(l, s) { - var o = this.attr, - k = o.margin, - g = o.centerX, - f = o.centerY, - e = o.distortion, - h = o.baseRotation, - p = o.startAngle + h, - m = o.endAngle + h, - a = o.thickness, - q = o.startRho, - j = o.endRho, - r = (s === "start" && p) || (s === "end" && m), - b = Math.sin(r), - d = Math.cos(r), - c = o.globalAlpha < 1, - n = s === "start" && d < 0 || s === "end" && d > 0 || c, - i; - if (n) { - i = (p + m) / 2; - g += Math.cos(i) * k; - f += Math.sin(i) * k * e; - l.moveTo(g + d * q, f + b * q * e); - l.lineTo(g + d * j, f + b * j * e); - l.lineTo(g + d * j, f + b * j * e + a); - l.lineTo(g + d * q, f + b * q * e + a); - l.closePath() - } - }, - startRenderer: function(a) { - this.sideRenderer(a, "start") - }, - endRenderer: function(a) { - this.sideRenderer(a, "end") - }, - rimRenderer: function(q, e, o, j) { - var w = this, - s = w.attr, - p = s.margin, - h = s.centerX, - g = s.centerY, - d = s.distortion, - i = s.baseRotation, - t = Ext.draw.sprite.AttributeParser.angle, - u = s.startAngle + i, - r = s.endAngle + i, - k = t((u + r) / 2), - a = s.thickness, - b = s.globalAlpha < 1, - c, n, v; - w.bevelParams = []; - u = t(u); - r = t(r); - h += Math.cos(k) * p; - g += Math.sin(k) * p * d; - c = u >= 0 && r >= 0; - n = u <= 0 && r <= 0; - - function l() { - q.ellipse(h, g + a, e, e * d, 0, Math.PI, u, true); - q.lineTo(h + Math.cos(u) * e, g + Math.sin(u) * e * d); - v = [h, g, e, e * d, 0, u, Math.PI, false]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function f() { - q.ellipse(h, g + a, e, e * d, 0, 0, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, 0, true]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function x() { - q.ellipse(h, g + a, e, e * d, 0, Math.PI, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, Math.PI, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function m() { - q.ellipse(h, g + a, e, e * d, 0, u, 0, false); - q.lineTo(h + e, g); - v = [h, g, e, e * d, 0, 0, u, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - if (j) { - if (!o || b) { - if (u >= 0 && r < 0) { - l() - } else { - if (u <= 0 && r > 0) { - f() - } else { - if (u <= 0 && r < 0) { - if (u > r) { - q.ellipse(h, g + a, e, e * d, 0, 0, Math.PI, false); - q.lineTo(h - e, g); - v = [h, g, e, e * d, 0, Math.PI, 0, true]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } else { - if (u > r) { - l(); - f() - } else { - v = [h, g, e, e * d, 0, u, r, false]; - if (c && !o || n && o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d + a); - q.ellipse(h, g + a, e, e * d, 0, r, u, true); - q.closePath() - } - } - } - } - } - } else { - if (o || b) { - if (u >= 0 && r < 0) { - x() - } else { - if (u <= 0 && r > 0) { - m() - } else { - if (u <= 0 && r < 0) { - if (u > r) { - x(); - m() - } else { - q.ellipse(h, g + a, e, e * d, 0, u, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, u, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } else { - if (u > r) { - q.ellipse(h, g + a, e, e * d, 0, -Math.PI, 0, false); - q.lineTo(h + e, g); - v = [h, g, e, e * d, 0, 0, -Math.PI, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } - } - } - } - } - }, - innerFrontRenderer: function(a) { - this.rimRenderer(a, this.attr.startRho, true, true) - }, - innerBackRenderer: function(a) { - this.rimRenderer(a, this.attr.startRho, true, false) - }, - outerFrontRenderer: function(a) { - this.rimRenderer(a, this.attr.endRho, false, true) - }, - outerBackRenderer: function(a) { - this.rimRenderer(a, this.attr.endRho, false, false) - } -}); -Ext.define("Ext.draw.PathUtil", function() { - var a = Math.abs, - c = Math.pow, - e = Math.cos, - b = Math.acos, - d = Math.sqrt, - f = Math.PI; - return { - singleton: true, - requires: ["Ext.draw.overrides.Path", "Ext.draw.overrides.sprite.Path", "Ext.draw.overrides.sprite.Instancing", "Ext.draw.overrides.Surface"], - cubicRoots: function(m) { - var z = m[0], - x = m[1], - w = m[2], - v = m[3]; - if (z === 0) { - return this.quadraticRoots(x, w, v) - } - var s = x / z, - r = w / z, - q = v / z, - k = (3 * r - c(s, 2)) / 9, - j = (9 * s * r - 27 * q - 2 * c(s, 3)) / 54, - p = c(k, 3) + c(j, 2), - n = [], - h, g, o, l, u, y = Ext.Number.sign; - if (p >= 0) { - h = y(j + d(p)) * c(a(j + d(p)), 1 / 3); - g = y(j - d(p)) * c(a(j - d(p)), 1 / 3); - n[0] = -s / 3 + (h + g); - n[1] = -s / 3 - (h + g) / 2; - n[2] = n[1]; - o = a(d(3) * (h - g) / 2); - if (o !== 0) { - n[1] = -1; - n[2] = -1 - } - } else { - l = b(j / d(-c(k, 3))); - n[0] = 2 * d(-k) * e(l / 3) - s / 3; - n[1] = 2 * d(-k) * e((l + 2 * f) / 3) - s / 3; - n[2] = 2 * d(-k) * e((l + 4 * f) / 3) - s / 3 - } - for (u = 0; u < 3; u++) { - if (n[u] < 0 || n[u] > 1) { - n[u] = -1 - } - } - return n - }, - quadraticRoots: function(h, g, n) { - var m, l, k, j; - if (h === 0) { - return this.linearRoot(g, n) - } - m = g * g - 4 * h * n; - if (m === 0) { - k = [-g / (2 * h)] - } else { - if (m > 0) { - l = d(m); - k = [(-g - l) / (2 * h), (-g + l) / (2 * h)] - } else { - return [] - } - } - for (j = 0; j < k.length; j++) { - if (k[j] < 0 || k[j] > 1) { - k[j] = -1 - } - } - return k - }, - linearRoot: function(h, g) { - var i = -g / h; - if (h === 0 || i < 0 || i > 1) { - return [] - } - return [i] - }, - bezierCoeffs: function(h, g, k, j) { - var i = []; - i[0] = -h + 3 * g - 3 * k + j; - i[1] = 3 * h - 6 * g + 3 * k; - i[2] = -3 * h + 3 * g; - i[3] = h; - return i - }, - cubicLineIntersections: function(I, G, F, E, l, k, j, h, M, p, K, n) { - var u = [], - N = [], - D = p - n, - z = K - M, - y = M * (n - p) - p * (K - M), - L = this.bezierCoeffs(I, G, F, E), - J = this.bezierCoeffs(l, k, j, h), - H, x, w, v, g, q, o, m; - u[0] = D * L[0] + z * J[0]; - u[1] = D * L[1] + z * J[1]; - u[2] = D * L[2] + z * J[2]; - u[3] = D * L[3] + z * J[3] + y; - x = this.cubicRoots(u); - for (H = 0; H < x.length; H++) { - v = x[H]; - if (v < 0 || v > 1) { - continue - } - g = v * v; - q = g * v; - o = L[0] * q + L[1] * g + L[2] * v + L[3]; - m = J[0] * q + J[1] * g + J[2] * v + J[3]; - if ((K - M) !== 0) { - w = (o - M) / (K - M) - } else { - w = (m - p) / (n - p) - } - if (!(w < 0 || w > 1)) { - N.push([o, m]) - } - } - return N - }, - splitCubic: function(g, q, p, o, m) { - var j = m * m, - n = m * j, - i = m - 1, - h = i * i, - k = i * h, - l = n * o - 3 * j * i * p + 3 * m * h * q - k * g; - return [ - [g, m * q - i * g, j * p - 2 * m * i * q + h * g, l], - [l, j * o - 2 * m * i * p + h * q, m * o - i * p, o] - ] - }, - cubicDimension: function(p, o, l, k) { - var j = 3 * (-p + 3 * (o - l) + k), - i = 6 * (p - 2 * o + l), - h = -3 * (p - o), - q, n, g = Math.min(p, k), - m = Math.max(p, k), - r; - if (j === 0) { - if (i === 0) { - return [g, m] - } else { - q = -h / i; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - } - } else { - r = i * i - 4 * j * h; - if (r >= 0) { - r = d(r); - q = (r - i) / 2 / j; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - if (r > 0) { - q -= r / j; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - } - } - } - return [g, m] - }, - interpolateCubic: function(h, g, l, k, i) { - if (i === 0) { - return h - } - if (i === 1) { - return k - } - var j = (1 - i) / i; - return i * i * i * (k + j * (3 * l + j * (3 * g + j * h))) - }, - cubicsIntersections: function(r, q, p, o, A, z, y, v, g, F, E, D, m, l, k, i) { - var C = this, - x = C.cubicDimension(r, q, p, o), - B = C.cubicDimension(A, z, y, v), - n = C.cubicDimension(g, F, E, D), - s = C.cubicDimension(m, l, k, i), - j, h, u, t, w = []; - if (x[0] > n[1] || x[1] < n[0] || B[0] > s[1] || B[1] < s[0]) { - return [] - } - if (a(A - z) < 1 && a(y - v) < 1 && a(r - o) < 1 && a(q - p) < 1 && a(m - l) < 1 && a(k - i) < 1 && a(g - D) < 1 && a(F - E) < 1) { - return [ - [(r + o) * 0.5, (A + z) * 0.5] - ] - } - j = C.splitCubic(r, q, p, o, 0.5); - h = C.splitCubic(A, z, y, v, 0.5); - u = C.splitCubic(g, F, E, D, 0.5); - t = C.splitCubic(m, l, k, i, 0.5); - w.push.apply(w, C.cubicsIntersections.apply(C, j[0].concat(h[0], u[0], t[0]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[0].concat(h[0], u[1], t[1]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[1].concat(h[1], u[0], t[0]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[1].concat(h[1], u[1], t[1]))); - return w - }, - linesIntersection: function(k, p, j, o, h, n, q, m) { - var l = (j - k) * (m - n) - (o - p) * (q - h), - i, g; - if (l === 0) { - return null - } - i = ((q - h) * (p - n) - (k - h) * (m - n)) / l; - g = ((j - k) * (p - n) - (o - p) * (k - h)) / l; - if (i >= 0 && i <= 1 && g >= 0 && g <= 1) { - return [k + i * (j - k), p + i * (o - p)] - } - return null - }, - pointOnLine: function(j, m, h, l, g, n) { - var k, i; - if (a(h - j) < a(l - m)) { - i = j; - j = m; - m = i; - i = h; - h = l; - l = i; - i = g; - g = n; - n = i - } - k = (g - j) / (h - j); - if (k < 0 || k > 1) { - return false - } - return a(m + k * (l - m) - n) < 4 - }, - pointOnCubic: function(w, u, s, r, l, k, h, g, p, o) { - var C = this, - B = C.bezierCoeffs(w, u, s, r), - A = C.bezierCoeffs(l, k, h, g), - z, v, n, m, q; - B[3] -= p; - A[3] -= o; - n = C.cubicRoots(B); - m = C.cubicRoots(A); - for (z = 0; z < n.length; z++) { - q = n[z]; - for (v = 0; v < m.length; v++) { - if (q >= 0 && q <= 1 && a(q - m[v]) < 0.05) { - return true - } - } - } - return false - } - } -}); -Ext.define("Ext.chart.series.Pie3D", { - extend: "Ext.chart.series.Polar", - requires: ["Ext.chart.series.sprite.Pie3DPart", "Ext.draw.PathUtil"], - type: "pie3d", - seriesType: "pie3d", - alias: "series.pie3d", - isPie3D: true, - config: { - rect: [0, 0, 0, 0], - thickness: 35, - distortion: 0.5, - donut: false, - hidden: [], - highlightCfg: { - margin: 20 - }, - shadow: false - }, - rotationOffset: -Math.PI / 2, - setField: function(a) { - return this.setXField(a) - }, - getField: function() { - return this.getXField() - }, - updateRotation: function(a) { - this.setStyle({ - baseRotation: a + this.rotationOffset - }); - this.doUpdateStyles() - }, - updateDistortion: function() { - this.setRadius() - }, - updateThickness: function() { - this.setRadius() - }, - updateColors: function(a) { - this.setSubStyle({ - baseColor: a - }) - }, - applyShadow: function(a) { - if (a === true) { - a = { - shadowColor: "rgba(0,0,0,0.8)", - shadowBlur: 30 - } - } else { - if (!Ext.isObject(a)) { - a = { - shadowColor: Ext.draw.Color.RGBA_NONE - } - } - } - return a - }, - updateShadow: function(g) { - var e = this, - f = e.getSprites(), - d = e.spritesPerSlice, - c = f && f.length, - b, a; - for (b = 1; b < c; b += d) { - a = f[b]; - if (a.attr.part = "bottom") { - a.setAttributes(g) - } - } - }, - getStyleByIndex: function(b) { - var d = this.callParent([b]), - c = this.getStyle(), - a = d.fillStyle || d.fill || d.color, - e = c.strokeStyle || c.stroke; - if (a) { - d.baseColor = a; - delete d.fillStyle; - delete d.fill; - delete d.color - } - if (e) { - d.strokeStyle = e - } - return d - }, - doUpdateStyles: function() { - var g = this, - h = g.getSprites(), - f = g.spritesPerSlice, - e = h && h.length, - c = 0, - b = 0, - a, d; - for (; c < e; c += f, b++) { - d = g.getStyleByIndex(b); - for (a = 0; a < f; a++) { - h[c + a].setAttributes(d) - } - } - }, - coordinateX: function() { - var w = this, - m = w.getChart(), - u = m && m.getAnimation(), - f = w.getStore(), - t = f.getData().items, - d = t.length, - b = w.getXField(), - p = w.getRotation(), - s = w.getHidden(), - n, c = 0, - h, e = [], - k = w.getSprites(), - a = k.length, - l = w.spritesPerSlice, - g = 0, - o = Math.PI * 2, - v = 1e-10, - r, q; - for (r = 0; r < d; r++) { - n = Math.abs(Number(t[r].get(b))) || 0; - if (!s[r]) { - c += n - } - e[r] = c; - if (r >= s.length) { - s[r] = false - } - } - s.length = d; - if (c === 0) { - return - } - h = 2 * Math.PI / c; - for (r = 0; r < d; r++) { - e[r] *= h - } - for (r = 0; r < a; r++) { - k[r].setAnimation(u) - } - for (r = 0; r < d; r++) { - for (q = 0; q < l; q++) { - k[r * l + q].setAttributes({ - startAngle: g, - endAngle: e[r] - v, - globalAlpha: 1, - baseRotation: p - }) - } - g = e[r] - } - for (r *= l; r < a; r++) { - k[r].setAnimation(u); - k[r].setAttributes({ - startAngle: o, - endAngle: o, - globalAlpha: 0, - baseRotation: p - }) - } - }, - updateLabelData: function() { - var l = this, - m = l.getStore(), - k = m.getData().items, - h = l.getSprites(), - b = l.getLabel().getTemplate().getField(), - f = l.getHidden(), - a = l.spritesPerSlice, - d, c, g, e, n; - if (h.length && b) { - e = []; - for (d = 0, g = k.length; d < g; d++) { - e.push(k[d].get(b)) - } - for (d = 0, c = 0, g = h.length; d < g; d += a, c++) { - n = h[d]; - n.setAttributes({ - label: e[c] - }); - n.putMarker("labels", { - hidden: f[c] - }, n.attr.attributeId) - } - } - }, - applyRadius: function() { - var f = this, - d = f.getChart(), - h = d.getInnerPadding(), - e = d.getMainRect() || [0, 0, 1, 1], - c = e[2] - h * 2, - a = e[3] - h * 2 - f.getThickness(), - g = c / 2, - b = g * f.getDistortion(); - if (b > a / 2) { - return a / (f.getDistortion() * 2) - } else { - return g - } - }, - getSprites: function() { - var y = this, - e = y.getStore(); - if (!e) { - return [] - } - var n = y.getChart(), - p = y.getSurface(), - t = e.getData().items, - l = y.spritesPerSlice, - a = t.length, - v = y.getAnimation() || n && n.getAnimation(), - x = y.getCenter(), - w = y.getOffsetX(), - u = y.getOffsetY(), - b = y.getRadius(), - q = y.getRotation(), - d = y.getHighlight(), - c = { - centerX: x[0] + w, - centerY: x[1] + u - y.getThickness() / 2, - endRho: b, - startRho: b * y.getDonut() / 100, - thickness: y.getThickness(), - distortion: y.getDistortion() - }, - k = y.sprites, - h = y.getLabel(), - f = h.getTemplate(), - m, g, o, s, r; - for (s = 0; s < a; s++) { - g = Ext.apply({}, this.getStyleByIndex(s), c); - if (!k[s * l]) { - for (r = 0; r < y.partNames.length; r++) { - o = p.add({ - type: "pie3dPart", - part: y.partNames[r] - }); - if (r === 0 && f.getField()) { - o.bindMarker("labels", h) - } - o.fx.setDurationOn("baseRotation", q); - if (d) { - o.config.highlight = d; - o.addModifier("highlight", true) - } - o.setAttributes(g); - k.push(o) - } - } else { - m = k.slice(s * l, (s + 1) * l); - for (r = 0; r < m.length; r++) { - o = m[r]; - if (v) { - o.setAnimation(v) - } - o.setAttributes(g) - } - } - } - return k - }, - betweenAngle: function(d, f, c) { - var e = Math.PI * 2, - g = this.rotationOffset; - f += g; - c += g; - d -= f; - c -= f; - d %= e; - c %= e; - d += e; - c += e; - d %= e; - c %= e; - return d < c || c === 0 - }, - getItemForPoint: function(k, j) { - var h = this, - g = h.getSprites(); - if (g) { - var l = h.getStore(), - b = l.getData().items, - a = h.spritesPerSlice, - e = h.getHidden(), - c, f, m, d; - for (c = 0, f = b.length; c < f; c++) { - if (!e[c]) { - d = c * a; - m = g[d]; - if (m.hitTest([k, j])) { - return { - series: h, - sprite: g.slice(d, d + a), - index: c, - record: b[c], - category: "sprites", - field: h.getXField() - } - } - } - } - return null - } - }, - provideLegendInfo: function(f) { - var h = this, - k = h.getStore(); - if (k) { - var g = k.getData().items, - b = h.getLabel().getTemplate().getField(), - j = h.getField(), - e = h.getHidden(), - d, a, c; - for (d = 0; d < g.length; d++) { - a = h.getStyleByIndex(d); - c = a.baseColor; - f.push({ - name: b ? String(g[d].get(b)) : j + " " + d, - mark: c || "black", - disabled: e[d], - series: h.getId(), - index: d - }) - } - } - } -}, function() { - var b = this.prototype, - a = Ext.chart.series.sprite.Pie3DPart.def.getInitialConfig().processors.part; - b.partNames = a.replace(/^enums\(|\)/g, "").split(","); - b.spritesPerSlice = b.partNames.length -}); -Ext.define("Ext.chart.series.sprite.Polar", { - extend: "Ext.chart.series.sprite.Series", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - baseRotation: "number", - labels: "default", - labelOverflowPadding: "number" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: 0, - endAngle: Math.PI, - startRho: 0, - endRho: 150, - baseRotation: 0, - labels: null, - labelOverflowPadding: 10 - }, - triggers: { - centerX: "bbox", - centerY: "bbox", - startAngle: "bbox", - endAngle: "bbox", - startRho: "bbox", - endRho: "bbox", - baseRotation: "bbox" - } - } - }, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.centerX - a.endRho; - b.y = a.centerY + a.endRho; - b.width = a.endRho * 2; - b.height = a.endRho * 2 - } -}); -Ext.define("Ext.chart.series.sprite.Radar", { - alias: "sprite.radar", - extend: "Ext.chart.series.sprite.Polar", - getDataPointXY: function(d) { - var u = this, - n = u.attr, - f = n.centerX, - e = n.centerY, - o = n.matrix, - t = n.dataMinX, - s = n.dataMaxX, - k = n.dataX, - j = n.dataY, - l = n.endRho, - p = n.startRho, - g = n.baseRotation, - i, h, m, c, b, a, q; - if (n.rangeY) { - q = n.rangeY[1] - } else { - q = n.dataMaxY - } - c = (k[d] - t) / (s - t + 1) * 2 * Math.PI + g; - m = j[d] / q * (l - p) + p; - b = f + Math.cos(c) * m; - a = e + Math.sin(c) * m; - i = o.x(b, a); - h = o.y(b, a); - return [i, h] - }, - render: function(a, l) { - var h = this, - f = h.attr, - g = f.dataX, - b = g.length, - e = h.surfaceMatrix, - d = {}, - c, k, j, m; - l.beginPath(); - for (c = 0; c < b; c++) { - m = h.getDataPointXY(c); - k = m[0]; - j = m[1]; - if (c === 0) { - l.moveTo(k, j) - } - l.lineTo(k, j); - d.translationX = e.x(k, j); - d.translationY = e.y(k, j); - h.putMarker("markers", d, c, true) - } - l.closePath(); - l.fillStroke(f) - } -}); -Ext.define("Ext.chart.series.Radar", { - extend: "Ext.chart.series.Polar", - type: "radar", - seriesType: "radar", - alias: "series.radar", - requires: ["Ext.chart.series.sprite.Radar"], - themeColorCount: function() { - return 1 - }, - isStoreDependantColorCount: false, - themeMarkerCount: function() { - return 1 - }, - updateAngularAxis: function(a) { - a.processData(this) - }, - updateRadialAxis: function(a) { - a.processData(this) - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - updateCenter: function(a) { - this.setStyle({ - translationX: a[0] + this.getOffsetX(), - translationY: a[1] + this.getOffsetY() - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - this.setStyle({ - endRho: a - }); - this.doUpdateStyles() - }, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a - }); - this.doUpdateStyles() - }, - updateTotalAngle: function(a) { - this.processData() - }, - getItemForPoint: function(k, j) { - var h = this, - m = h.sprites && h.sprites[0], - f = m.attr, - g = f.dataX, - a = g.length, - l = h.getStore(), - e = h.getMarker(), - b, o, p, d, n, c; - if (h.getHidden()) { - return null - } - if (m && e) { - c = m.getMarker("markers"); - for (d = 0; d < a; d++) { - n = c.getBBoxFor(d); - b = (n.width + n.height) * 0.25; - p = m.getDataPointXY(d); - if (Math.abs(p[0] - k) < b && Math.abs(p[1] - j) < b) { - o = { - series: h, - sprite: m, - index: d, - category: "markers", - record: l.getData().items[d], - field: h.getYField() - }; - return o - } - } - } - return h.callParent(arguments) - }, - getDefaultSpriteConfig: function() { - var a = this.callParent(), - b = { - customDurations: { - translationX: 0, - translationY: 0, - rotationRads: 0, - dataMinX: 0, - dataMaxX: 0 - } - }; - if (a.fx) { - Ext.apply(a.fx, b) - } else { - a.fx = b - } - return a - }, - getSprites: function() { - var d = this, - c = d.getChart(), - e = d.getAnimation() || c && c.getAnimation(), - b = d.sprites[0], - a; - if (!c) { - return [] - } - if (!b) { - b = d.createSprite() - } - if (e) { - a = b.getMarker("markers"); - if (a) { - a.getTemplate().setAnimation(e) - } - b.setAnimation(e) - } - return d.sprites - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getSubStyleWithTheme(), - c = a.fillStyle; - if (Ext.isArray(c)) { - c = c[0] - } - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - } -}); -Ext.define("Ext.chart.series.sprite.Scatter", { - alias: "sprite.scatterSeries", - extend: "Ext.chart.series.sprite.Cartesian", - renderClipped: function(r, s, w, u) { - if (this.cleanRedraw) { - return - } - var C = this, - q = C.attr, - l = q.dataX, - h = q.dataY, - z = q.labels, - j = C.getSeries(), - b = z && C.getMarker("labels"), - t = C.attr.matrix, - c = t.getXX(), - p = t.getYY(), - m = t.getDX(), - k = t.getDY(), - n = {}, - D, B, d = r.getInherited().rtl && !q.flipXY ? -1 : 1, - a, A, o, e, g, f, v; - if (q.flipXY) { - a = u[1] - c * d; - A = u[1] + u[3] + c * d; - o = u[0] - p; - e = u[0] + u[2] + p - } else { - a = u[0] - c * d; - A = u[0] + u[2] + c * d; - o = u[1] - p; - e = u[1] + u[3] + p - } - for (v = 0; v < l.length; v++) { - g = l[v]; - f = h[v]; - g = g * c + m; - f = f * p + k; - if (a <= g && g <= A && o <= f && f <= e) { - if (q.renderer) { - n = { - type: "items", - translationX: g, - translationY: f - }; - B = [C, n, { - store: C.getStore() - }, v]; - D = Ext.callback(q.renderer, null, B, 0, j); - n = Ext.apply(n, D) - } else { - n.translationX = g; - n.translationY = f - } - C.putMarker("items", n, v, !q.renderer); - if (b && z[v]) { - C.drawLabel(z[v], g, f, v, u) - } - } - } - }, - drawLabel: function(j, h, g, p, a) { - var r = this, - m = r.attr, - d = r.getMarker("labels"), - c = d.getTemplate(), - l = r.labelCfg || (r.labelCfg = {}), - b = r.surfaceMatrix, - f, e, i = m.labelOverflowPadding, - o = m.flipXY, - k, n, s, q; - l.text = j; - n = r.getMarkerBBox("labels", p, true); - if (!n) { - r.putMarker("labels", l, p); - n = r.getMarkerBBox("labels", p, true) - } - if (o) { - l.rotationRads = Math.PI * 0.5 - } else { - l.rotationRads = 0 - } - k = n.height / 2; - f = h; - switch (c.attr.display) { - case "under": - e = g - k - i; - break; - case "rotate": - f += i; - e = g - i; - l.rotationRads = -Math.PI / 4; - break; - default: - e = g + k + i - } - l.x = b.x(f, e); - l.y = b.y(f, e); - if (c.attr.renderer) { - q = [j, d, l, { - store: r.getStore() - }, p]; - s = Ext.callback(c.attr.renderer, null, q, 0, r.getSeries()); - if (typeof s === "string") { - l.text = s - } else { - Ext.apply(l, s) - } - } - r.putMarker("labels", l, p) - } -}); -Ext.define("Ext.chart.series.Scatter", { - extend: "Ext.chart.series.Cartesian", - alias: "series.scatter", - type: "scatter", - seriesType: "scatterSeries", - requires: ["Ext.chart.series.sprite.Scatter"], - config: { - itemInstancing: { - fx: { - customDurations: { - translationX: 0, - translationY: 0 - } - } - } - }, - themeMarkerCount: function() { - return 1 - }, - applyMarker: function(b, a) { - this.getItemInstancing(); - this.setItemInstancing(b); - return this.callParent(arguments) - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getMarkerStyleByIndex(0), - c = a.fillStyle; - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - } -}); -Ext.define("Ext.chart.theme.Blue", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.blue", "chart.theme.Blue"], - config: { - baseColor: "#4d7fe6" - } -}); -Ext.define("Ext.chart.theme.BlueGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.blue-gradients", "chart.theme.Blue:gradients"], - config: { - baseColor: "#4d7fe6", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category1", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category1", "chart.theme.Category1"], - config: { - colors: ["#f0a50a", "#c20024", "#2044ba", "#810065", "#7eae29"] - } -}); -Ext.define("Ext.chart.theme.Category1Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category1-gradients", "chart.theme.Category1:gradients"], - config: { - colors: ["#f0a50a", "#c20024", "#2044ba", "#810065", "#7eae29"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category2", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category2", "chart.theme.Category2"], - config: { - colors: ["#6d9824", "#87146e", "#2a9196", "#d39006", "#1e40ac"] - } -}); -Ext.define("Ext.chart.theme.Category2Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category2-gradients", "chart.theme.Category2:gradients"], - config: { - colors: ["#6d9824", "#87146e", "#2a9196", "#d39006", "#1e40ac"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category3", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category3", "chart.theme.Category3"], - config: { - colors: ["#fbbc29", "#ce2e4e", "#7e0062", "#158b90", "#57880e"] - } -}); -Ext.define("Ext.chart.theme.Category3Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category3-gradients", "chart.theme.Category3:gradients"], - config: { - colors: ["#fbbc29", "#ce2e4e", "#7e0062", "#158b90", "#57880e"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category4", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category4", "chart.theme.Category4"], - config: { - colors: ["#ef5773", "#fcbd2a", "#4f770d", "#1d3eaa", "#9b001f"] - } -}); -Ext.define("Ext.chart.theme.Category4Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category4-gradients", "chart.theme.Category4:gradients"], - config: { - colors: ["#ef5773", "#fcbd2a", "#4f770d", "#1d3eaa", "#9b001f"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category5", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category5", "chart.theme.Category5"], - config: { - colors: ["#7eae29", "#fdbe2a", "#910019", "#27b4bc", "#d74dbc"] - } -}); -Ext.define("Ext.chart.theme.Category5Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category5-gradients", "chart.theme.Category5:gradients"], - config: { - colors: ["#7eae29", "#fdbe2a", "#910019", "#27b4bc", "#d74dbc"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category6", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category6", "chart.theme.Category6"], - config: { - colors: ["#44dce1", "#0b2592", "#996e05", "#7fb325", "#b821a1"] - } -}); -Ext.define("Ext.chart.theme.Category6Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category6-gradients", "chart.theme.Category6:gradients"], - config: { - colors: ["#44dce1", "#0b2592", "#996e05", "#7fb325", "#b821a1"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.DefaultGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.default-gradients", "chart.theme.Base:gradients"], - config: { - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Green", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.green", "chart.theme.Green"], - config: { - baseColor: "#b1da5a" - } -}); -Ext.define("Ext.chart.theme.GreenGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.green-gradients", "chart.theme.Green:gradients"], - config: { - baseColor: "#b1da5a", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Midnight", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.midnight", "chart.theme.Midnight"], - config: { - colors: ["#A837FF", "#4AC0F2", "#FF4D35", "#FF8809", "#61C102", "#FF37EA"], - chart: { - defaults: { - background: "rgb(52, 52, 53)" - } - }, - axis: { - defaults: { - style: { - strokeStyle: "rgb(224, 224, 227)" - }, - label: { - fillStyle: "rgb(224, 224, 227)" - }, - title: { - fillStyle: "rgb(224, 224, 227)" - }, - grid: { - strokeStyle: "rgb(112, 112, 115)" - } - } - }, - series: { - defaults: { - label: { - fillStyle: "rgb(224, 224, 227)" - } - } - }, - sprites: { - text: { - fillStyle: "rgb(224, 224, 227)" - } - } - } -}); -Ext.define("Ext.chart.theme.Muted", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.muted", "chart.theme.Muted"], - config: { - colors: ["#8ca640", "#974144", "#4091ba", "#8e658e", "#3b8d8b", "#b86465", "#d2af69", "#6e8852", "#3dcc7e", "#a6bed1", "#cbaa4b", "#998baa"] - } -}); -Ext.define("Ext.chart.theme.Purple", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.purple", "chart.theme.Purple"], - config: { - baseColor: "#da5abd" - } -}); -Ext.define("Ext.chart.theme.PurpleGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.purple-gradients", "chart.theme.Purple:gradients"], - config: { - baseColor: "#da5abd", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Red", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.red", "chart.theme.Red"], - config: { - baseColor: "#e84b67" - } -}); -Ext.define("Ext.chart.theme.RedGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.red-gradients", "chart.theme.Red:gradients"], - config: { - baseColor: "#e84b67", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Sky", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.sky", "chart.theme.Sky"], - config: { - baseColor: "#4ce0e7" - } -}); -Ext.define("Ext.chart.theme.SkyGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.sky-gradients", "chart.theme.Sky:gradients"], - config: { - baseColor: "#4ce0e7", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Yellow", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.yellow", "chart.theme.Yellow"], - config: { - baseColor: "#fec935" - } -}); -Ext.define("Ext.chart.theme.YellowGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.yellow-gradients", "chart.theme.Yellow:gradients"], - config: { - baseColor: "#fec935", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.draw.Point", { - requires: ["Ext.draw.Draw", "Ext.draw.Matrix"], - isPoint: true, - x: 0, - y: 0, - length: 0, - angle: 0, - angleUnits: "degrees", - statics: { - fly: (function() { - var a = null; - return function(b, c) { - if (!a) { - a = new Ext.draw.Point() - } - a.constructor(b, c); - return a - } - })() - }, - constructor: function(a, c) { - var b = this; - if (typeof a === "number") { - b.x = a; - if (typeof c === "number") { - b.y = c - } else { - b.y = a - } - } else { - if (Ext.isArray(a)) { - b.x = a[0]; - b.y = a[1] - } else { - if (a) { - b.x = a.x; - b.y = a.y - } - } - } - b.calculatePolar() - }, - calculateCartesian: function() { - var b = this, - a = b.length, - c = b.angle; - if (b.angleUnits === "degrees") { - c = Ext.draw.Draw.rad(c) - } - b.x = Math.cos(c) * a; - b.y = Math.sin(c) * a - }, - calculatePolar: function() { - var b = this, - a = b.x, - c = b.y; - b.length = Math.sqrt(a * a + c * c); - b.angle = Math.atan2(c, a); - if (b.angleUnits === "degrees") { - b.angle = Ext.draw.Draw.degrees(b.angle) - } - }, - setX: function(a) { - this.x = a; - this.calculatePolar() - }, - setY: function(a) { - this.y = a; - this.calculatePolar() - }, - set: function(a, b) { - this.constructor(a, b) - }, - setAngle: function(a) { - this.angle = a; - this.calculateCartesian() - }, - setLength: function(a) { - this.length = a; - this.calculateCartesian() - }, - setPolar: function(b, a) { - this.angle = b; - this.length = a; - this.calculateCartesian() - }, - clone: function() { - return new Ext.draw.Point(this.x, this.y) - }, - add: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return new Ext.draw.Point(this.x + b.x, this.y + b.y) - }, - sub: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return new Ext.draw.Point(this.x - b.x, this.y - b.y) - }, - mul: function(a) { - return new Ext.draw.Point(this.x * a, this.y * a) - }, - div: function(a) { - return new Ext.draw.Point(this.x / a, this.y / a) - }, - dot: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return this.x * b.x + this.y * b.y - }, - equals: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return this.x === b.x && this.y === b.y - }, - rotate: function(f, c) { - var d, e, b, g, a; - if (this.angleUnits === "degrees") { - f = Ext.draw.Draw.rad(f); - d = Math.sin(f); - e = Math.cos(f) - } - if (c) { - b = c.x; - g = c.y - } else { - b = 0; - g = 0 - } - a = Ext.draw.Matrix.fly([e, d, -d, e, b - e * b + g * d, g - e * g + b * -d]).transformPoint(this); - return new Ext.draw.Point(a) - }, - transform: function(a) { - if (a && a.isMatrix) { - return new Ext.draw.Point(a.transformPoint(this)) - } else { - if (arguments.length === 6) { - return new Ext.draw.Point(Ext.draw.Matrix.fly(arguments).transformPoint(this)) - } else { - Ext.raise("Invalid parameters.") - } - } - }, - round: function() { - return new Ext.draw.Point(Math.round(this.x), Math.round(this.y)) - }, - ceil: function() { - return new Ext.draw.Point(Math.ceil(this.x), Math.ceil(this.y)) - }, - floor: function() { - return new Ext.draw.Point(Math.floor(this.x), Math.floor(this.y)) - }, - abs: function(a, b) { - return new Ext.draw.Point(Math.abs(this.x), Math.abs(this.y)) - }, - normalize: function(c) { - var b = this.x, - f = this.y, - a, e, d; - c = c || 1; - if (b === 0) { - a = 0; - e = c * Ext.Number.sign(f) - } else { - d = f / b; - a = c / Math.sqrt(1 + d * d); - e = a * d - } - return new Ext.draw.Point(a, e) - }, - getDistanceToLine: function(c, b) { - if (arguments.length === 4) { - c = new Ext.draw.Point(arguments[0], arguments[1]); - b = new Ext.draw.Point(arguments[2], arguments[3]) - } - var d = b.sub(c).normalize(), - a = c.sub(this); - return a.sub(d.mul(a.dot(d))) - }, - isZero: function() { - return this.x === 0 && this.y === 0 - }, - isNumber: function() { - return Ext.isNumber(this.x + this.y) - } -}); -Ext.define("Ext.draw.plugin.SpriteEvents", { - extend: "Ext.plugin.Abstract", - alias: "plugin.spriteevents", - requires: ["Ext.draw.PathUtil"], - mouseMoveEvents: { - mousemove: true, - mouseover: true, - mouseout: true - }, - spriteMouseMoveEvents: { - spritemousemove: true, - spritemouseover: true, - spritemouseout: true - }, - init: function(a) { - var b = "handleEvent"; - this.drawContainer = a; - a.addElementListener({ - click: b, - dblclick: b, - mousedown: b, - mousemove: b, - mouseup: b, - mouseover: b, - mouseout: b, - priority: 1001, - scope: this - }) - }, - hasSpriteMouseMoveListeners: function() { - var b = this.drawContainer.hasListeners, - a; - for (a in this.spriteMouseMoveEvents) { - if (a in b) { - return true - } - } - return false - }, - hitTestEvent: function(f) { - var b = this.drawContainer.getItems(), - a, d, c; - for (c = b.length - 1; c >= 0; c--) { - a = b.get(c); - d = a.hitTestEvent(f); - if (d) { - return d - } - } - return null - }, - handleEvent: function(f) { - var d = this, - b = d.drawContainer, - g = f.type in d.mouseMoveEvents, - a = d.lastSprite, - c; - if (g && !d.hasSpriteMouseMoveListeners()) { - return - } - c = d.hitTestEvent(f); - if (g && !Ext.Object.equals(c, a)) { - if (a) { - b.fireEvent("spritemouseout", a, f) - } - if (c) { - b.fireEvent("spritemouseover", c, f) - } - } - if (c) { - b.fireEvent("sprite" + f.type, c, f) - } - d.lastSprite = c - } -}); -Ext.define("Ext.chart.TipSurface", { - extend: "Ext.draw.Container", - spriteArray: false, - renderFirst: true, - constructor: function(a) { - this.callParent([a]); - if (a.sprites) { - this.spriteArray = [].concat(a.sprites); - delete a.sprites - } - }, - onRender: function() { - var c = this, - b = 0, - a = 0, - d, e; - this.callParent(arguments); - e = c.spriteArray; - if (c.renderFirst && e) { - c.renderFirst = false; - for (a = e.length; b < a; b++) { - d = c.surface.add(e[b]); - d.setAttributes({ - hidden: false - }, true) - } - } - } -}); -Ext.define("Ext.chart.interactions.ItemInfo", { - extend: "Ext.chart.interactions.Abstract", - type: "iteminfo", - alias: "interaction.iteminfo", - config: { - extjsGestures: { - start: { - event: "click", - handler: "onInfoGesture" - }, - move: { - event: "mousemove", - handler: "onInfoGesture" - }, - end: { - event: "mouseleave", - handler: "onInfoGesture" - } - } - }, - item: null, - onInfoGesture: function(f, a) { - var c = this, - b = c.getItemForEvent(f), - d = b && b.series.tooltip; - if (d) { - d.onMouseMove.call(d, f) - } - if (b !== c.item) { - if (b) { - b.series.showTip(b) - } else { - c.item.series.hideTip(c.item) - } - c.item = b - } - return false - } -}); \ No newline at end of file diff --git a/serverside/jsmod/5.4-3/proxmoxlib.js b/serverside/jsmod/5.4-3/proxmoxlib.js deleted file mode 100644 index 6e316d7..0000000 --- a/serverside/jsmod/5.4-3/proxmoxlib.js +++ /dev/null @@ -1,6757 +0,0 @@ -// 1.0-25 -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)) { - var console = { - dir: function() {}, - log: function() {} - }; -} - -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', { utilities: { - - // this singleton contains miscellaneous utilities - - yesText: gettext('Yes'), - noText: gettext('No'), - enabledText: gettext('Enabled'), - disabledText: gettext('Disabled'), - 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: { - zh_CN: 'Chinese (Simplified)', - zh_TW: 'Chinese (Traditional)', - ca: 'Catalan', - da: 'Danish', - en: 'English', - eu: 'Euskera (Basque)', - fr: 'French', - de: 'German', - it: 'Italian', - es: 'Spanish', - ja: 'Japanese', - nb: 'Norwegian (Bokmal)', - nn: 'Norwegian (Nynorsk)', - fa: 'Persian (Farsi)', - pl: 'Polish', - pt_BR: 'Portuguese (Brazil)', - ru: 'Russian', - sl: 'Slovenian', - sv: 'Swedish', - tr: 'Turkish' - }, - - render_language: function (value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (English)'; - } - var text = Proxmox.Utils.language_map[value]; - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - language_array: function() { - var 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; - }, - - 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 || 'http://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"); - }, - - format_duration_long: function(ut) { - - var days = Math.floor(ut / 86400); - ut -= days*86400; - var hours = Math.floor(ut / 3600); - ut -= hours*3600; - var mins = Math.floor(ut / 60); - ut -= mins*60; - - var hours_str = '00' + hours.toString(); - hours_str = hours_str.substr(hours_str.length - 2); - var mins_str = "00" + mins.toString(); - mins_str = mins_str.substr(mins_str.length - 2); - var ut_str = "00" + ut.toString(); - ut_str = ut_str.substr(ut_str.length - 2); - - if (days) { - var 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; } - - var tm = new Ext.util.TextMetrics(); - var 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; - } - return (Proxmox.UserName !== '') && Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name); - }, - - authClear: function() { - if (Proxmox.LoggedOut) { - return undefined; - } - 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) { - var el = comp.el; - if (!el) { - return; - } - if (!msg) { - el.unmask(); - } else { - if (msg === true) { - el.mask(gettext("Loading...")); - } else { - el.mask(msg); - } - } - }, - - monStoreErrors: function(me, store, clearMaskBeforeLoad) { - if (clearMaskBeforeLoad) { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - Proxmox.Utils.setErrorMask(me, false); - }); - } else { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - if (!me.loadCount) { - me.loadCount = 0; // make sure it is numeric - Proxmox.Utils.setErrorMask(me, true); - } - }); - } - - // only works with 'proxmox' proxy - me.mon(store.proxy, 'afterload', function(proxy, request, success) { - me.loadCount++; - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - - var msg; - /*jslint nomen: true */ - var operation = request._operation; - var error = operation.getError(); - if (error.statusText) { - msg = error.statusText + ' (' + error.status + ')'; - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - extractRequestError: function(result, verbose) { - var 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) { - - var newopts = Ext.apply({ - waitMsg: gettext('Please wait...') - }, reqOpts); - - if (!newopts.url.match(/^\/api2/)) { - newopts.url = '/api2/extjs' + newopts.url; - } - delete newopts.callback; - - var 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); - } - } - var 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) {} - var 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); - - var 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', - //waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data; - - if (data.status !== 'Active') { - Ext.Msg.show({ - title: gettext('No valid subscription'), - icon: Ext.Msg.WARNING, - msg: 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)) { - Ext.Object.each(data, function(name, val) { - if (values.hasOwnProperty(name)) { - var 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; - } - }); - } - }, - - 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') ], - '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') ], - 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') ], - vzsuspend: [ 'CT', gettext('Suspend') ], - vzresume: [ 'CT', gettext('Resume') ], - hamigrate: [ 'HA', gettext('Migrate') ], - hastart: [ 'HA', gettext('Start') ], - hastop: [ 'HA', gettext('Stop') ], - 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: ['VM/CT', gettext('Backup') ], - 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') ] - }, - - format_task_description: function(type, id) { - var farray = Proxmox.Utils.task_desc_table[type]; - var text; - if (!farray) { - text = type; - if (id) { - type += ' ' + id; - } - return text; - } - var prefix = farray[0]; - text = farray[1]; - if (prefix) { - return prefix + ' ' + id + ' - ' + text; - } - return text; - }, - - format_size: function(size) { - /*jslint confusion: true */ - - var units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; - var num = 0; - - while (size >= 1024 && ((num++)+1) < units.length) { - size = size / 1024; - } - - return size.toFixed((num > 0)?2:0) + " " + units[num] + "B"; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - render_uptime: function(value) { - - var uptime = value; - - if (uptime === undefined) { - return ''; - } - - if (uptime <= 0) { - return '-'; - } - - return Proxmox.Utils.format_duration_long(uptime); - }, - - parse_task_upid: function(upid) { - var task = {}; - - var res = upid.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):([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); - task.starttime = parseInt(res[4], 16); - task.type = res[5]; - task.id = res[6]; - task.user = res[7]; - - task.desc = Proxmox.Utils.format_task_description(task.type, task.id); - - return task; - }, - - render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) { - var servertime = new Date(value * 1000); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }, - - openXtermJsViewer: function(vmtype, vmid, nodename, vmname, cmd) { - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - xtermjs: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - cmd: cmd, - - }); - var 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() { - var me = this; - Ext.apply(me, me.utilities); - - var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])"; - var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")"; - var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})"; - var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")"; - - - me.IP4_match = new RegExp("^(?:" + IPV4_REGEXP + ")$"); - me.IP4_cidr_match = new RegExp("^(?:" + IPV4_REGEXP + ")\/([0-9]{1,2})$"); - - var 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 + ")?::" + ")" + ")" + - ")"; - - me.IP6_match = new RegExp("^(?:" + IPV6_REGEXP + ")$"); - me.IP6_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + ")\/([0-9]{1,3})$"); - me.IP6_bracket_match = new RegExp("^\\[(" + IPV6_REGEXP + ")\\]"); - - me.IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$"); - - var 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+)?$"); - } -}); -// 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] <= 128); - }, - IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "
" + gettext('Valid CIDR Range') + ': 8-128', - 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', - - MacPrefix: function(v) { - return (/^[a-f0-9][02468ace](?::[a-f0-9]{2}){0,2}:?$/i).test(v); - }, - MacPrefixMask: /[a-fA-F0-9:]/, - MacPrefixText: gettext('Example') + ': 02:8f - ' + gettext('only unicast addresses are allowed'), - - 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'", - - 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", - - DnsOrIp: function(v) { - if (!Proxmox.Utils.DnsName_match.test(v) && - !Proxmox.Utils.IP64_match.test(v)) { - return false; - } - - return true; - }, - DnsOrIpText: gettext('Not a valid DNS name or IP address.'), - - 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'), - - password: function(val, field) { - if (field.initialPassField) { - var pwd = field.up('form').down( - '[name=' + field.initialPassField + ']'); - return (val == pwd.getValue()); - } - return true; - }, - - passwordText: gettext('Passwords do not match') -}); - -// Firefox 52+ Touchscreen bug -// see https://www.sencha.com/forum/showthread.php?336762-Examples-don-t-work-in-Firefox-52-touchscreen/page2 -// and https://bugzilla.proxmox.com/show_bug.cgi?id=1223 -Ext.define('EXTJS_23846.Element', { - override: 'Ext.dom.Element' -}, function(Element) { - var supports = Ext.supports, - proto = Element.prototype, - eventMap = proto.eventMap, - additiveEvents = proto.additiveEvents; - - if (Ext.os.is.Desktop && supports.TouchEvents && !supports.PointerEvents) { - eventMap.touchstart = 'mousedown'; - eventMap.touchmove = 'mousemove'; - eventMap.touchend = 'mouseup'; - eventMap.touchcancel = 'mouseup'; - - additiveEvents.mousedown = 'mousedown'; - additiveEvents.mousemove = 'mousemove'; - additiveEvents.mouseup = 'mouseup'; - additiveEvents.touchstart = 'touchstart'; - additiveEvents.touchmove = 'touchmove'; - additiveEvents.touchend = 'touchend'; - additiveEvents.touchcancel = 'touchcancel'; - - additiveEvents.pointerdown = 'mousedown'; - additiveEvents.pointermove = 'mousemove'; - additiveEvents.pointerup = 'mouseup'; - additiveEvents.pointercancel = 'mouseup'; - } -}); - -Ext.define('EXTJS_23846.Gesture', { - override: 'Ext.event.publisher.Gesture' -}, function(Gesture) { - var me = Gesture.instance; - - if (Ext.supports.TouchEvents && !Ext.isWebKit && Ext.os.is.Desktop) { - me.handledDomEvents.push('mousedown', 'mousemove', 'mouseup'); - me.registerEvents(); - } -}); - -// 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 -// 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; - } -}); - -// '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('Proxmox.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 a grid/tree view, restoring the focus moves the view back to -// the previously focused item. Save scroll position before refocusing. -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('Proxmox.Datepicker', { - override: 'Ext.picker.Date', - hideMode: 'visibility' -}); - -// ExtJS 6.0.1 has no setSubmitValue() (although you find it in the docs). -// Note: this.submitValue is a boolean flag, whereas getSubmitValue() returns -// data to be submitted. -Ext.define('Proxmox.form.field.Text', { - override: 'Ext.form.field.Text', - - setSubmitValue: function(v) { - this.submitValue = v; - }, -}); - -// this should be fixed with ExtJS 6.0.2 -// make mousescrolling work in firefox in the containers overflowhandler -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. differs from the default onMouseWheel - // handler by using deltaY instead of wheelDeltaY and no normalizing, - // because it is already - 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 -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); - } - } -}); -Ext.define('Proxmox.Mixin.CBind', { - extend: 'Ext.Mixin', - - mixinConfig: { - before: { - initComponent: 'cloneTemplates' - } - }, - - cloneTemplates: function() { - var me = this; - - if (typeof(me.cbindData) == "function") { - me.cbindData = me.cbindData(me.initialConfig) || {}; - } - - var getConfigValue = function(cname) { - - if (cname in me.initialConfig) { - return me.initialConfig[cname]; - } - if (cname in me.cbindData) { - return me.cbindData[cname]; - } - if (cname in me) { - return me[cname]; - } - throw "unable to get cbind data for '" + cname + "'"; - }; - - var applyCBind = function(obj) { - var cbind = obj.cbind, prop, cdata, cvalue, match, found; - if (!cbind) return; - - for (prop in cbind) { - cdata = cbind[prop]; - - found = false; - if (match = /^\{(!)?([a-z_][a-z0-9_]*)\}$/i.exec(cdata)) { - var cvalue = getConfigValue(match[2]); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else if (match = /^\{(!)?([a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)+)\}$/i.exec(cdata)) { - var keys = match[2].split('.'); - var cvalue = getConfigValue(keys.shift()); - keys.forEach(function(k) { - if (k in cvalue) { - cvalue = cvalue[k]; - } else { - throw "unable to get cbind data for '" + match[2] + "'"; - } - }); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else { - obj[prop] = cdata.replace(/{([a-z_][a-z0-9_]*)\}/ig, function(match, cname) { - var cvalue = getConfigValue(cname); - found = true; - return cvalue; - }); - } - if (!found) { - throw "unable to parse cbind template '" + cdata + "'"; - } - - } - }; - - if (me.cbind) { - applyCBind(me); - } - - var cloneTemplateArray = function(org) { - var copy, i, found, el, elcopy, arrayLength; - - arrayLength = org.length; - found = false; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - found = true; - break; - } - } - - if (!found) return org; // no need to copy - - copy = []; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - elcopy = cloneTemplateObject(el); - if (elcopy.cbind) { - applyCBind(elcopy); - } - copy.push(elcopy); - } else if (el.constructor == Array) { - elcopy = cloneTemplateArray(el); - copy.push(elcopy); - } else { - copy.push(el); - } - } - return copy; - }; - - var cloneTemplateObject = function(org) { - var res = {}, prop, el, copy; - for (prop in org) { - el = org[prop]; - if (el.constructor == Object && el.xtype) { - copy = cloneTemplateObject(el); - if (copy.cbind) { - applyCBind(copy); - } - res[prop] = copy; - } else if (el.constructor == Array) { - copy = cloneTemplateArray(el); - res[prop] = copy; - } else { - res[prop] = el; - } - } - return res; - }; - - var condCloneProperties = function() { - var prop, el, i, tmp; - - for (prop in me) { - el = me[prop]; - if (el === undefined || el === null) continue; - if (typeof(el) === 'object' && el.constructor == Object) { - if (el.xtype && prop != 'config') { - me[prop] = cloneTemplateObject(el); - } - } else if (el.constructor == Array) { - tmp = cloneTemplateArray(el); - me[prop] = tmp; - } - } - }; - - condCloneProperties(); - } -}); -/* A reader to store a single JSON Object (hash) into a storage. - * Also accepts an array containing a single hash. - * - * So it can read: - * - * example1: {data1: "xyz", data2: "abc"} - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * example2: [ {data1: "xyz", data2: "abc"} ] - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * If you set 'readArray', the reader expexts the object as array: - * - * example3: [ { key: "data1", value: "xyz", p2: "cde" }, { key: "data2", value: "abc", p2: "efg" }] - * returns [{key: "data1", value: "xyz", p2: "cde}, {key: "data2", value: "abc", p2: "efg"}] - * - * Note: The records can contain additional properties (like 'p2' above) when you use 'readArray' - * - * Additional feature: specify allowed properties with default values with 'rows' object - * - * var rows = { - * memory: { - * required: true, - * defaultValue: 512 - * } - * } - * - */ - -Ext.define('Proxmox.data.reader.JsonObject', { - extend: 'Ext.data.reader.Json', - alias : 'reader.jsonobject', - - readArray: false, - - rows: undefined, - - constructor: function(config) { - var me = this; - - Ext.apply(me, config || {}); - - me.callParent([config]); - }, - - getResponseData: function(response) { - var me = this; - - var data = []; - try { - var result = Ext.decode(response.responseText); - // get our data items inside the server response - var root = result[me.getRootProperty()]; - - if (me.readArray) { - - var rec_hash = {}; - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - rec_hash[rec.key] = rec; - } - }); - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - var rec = rec_hash[key]; - if (Ext.isDefined(rec)) { - if (!Ext.isDefined(rec.value)) { - rec.value = rowdef.defaultValue; - } - data.push(rec); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue} ); - } else if (rowdef.required) { - data.push({key: key, value: undefined }); - } - }); - } else { - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - data.push(rec); - } - }); - } - - } else { - - var org_root = root; - - if (Ext.isArray(org_root)) { - if (root.length == 1) { - root = org_root[0]; - } else { - root = {}; - } - } - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - if (Ext.isDefined(root[key])) { - data.push({key: key, value: root[key]}); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue}); - } else if (rowdef.required) { - data.push({key: key, value: undefined}); - } - }); - } else { - Ext.Object.each(root, function(key, value) { - data.push({key: key, value: value }); - }); - } - } - } - catch (ex) { - Ext.Error.raise({ - response: response, - json: response.responseText, - parseError: ex, - msg: 'Unable to parse the JSON returned by the server: ' + ex.toString() - }); - } - - return data; - } -}); - -Ext.define('Proxmox.RestProxy', { - extend: 'Ext.data.RestProxy', - alias : 'proxy.proxmox', - - pageParam : null, - startParam: null, - limitParam: null, - groupParam: null, - sortParam: null, - filterParam: null, - noCache : false, - - afterRequest: function(request, success) { - this.fireEvent('afterload', this, request, success); - return; - }, - - constructor: function(config) { - - Ext.applyIf(config, { - reader: { - type: 'json', - rootProperty: config.root || 'data' - } - }); - - this.callParent([config]); - } -}, function() { - - Ext.define('KeyValue', { - extend: "Ext.data.Model", - fields: [ 'key', 'value' ], - idProperty: 'key' - }); - - Ext.define('KeyValuePendingDelete', { - extend: "Ext.data.Model", - fields: [ 'key', 'value', 'pending', 'delete' ], - idProperty: 'key' - }); - - Ext.define('proxmox-tasks', { - extend: 'Ext.data.Model', - fields: [ - { name: 'starttime', type : 'date', dateFormat: 'timestamp' }, - { name: 'endtime', type : 'date', dateFormat: 'timestamp' }, - { name: 'pid', type: 'int' }, - 'node', 'upid', 'user', 'status', 'type', 'id' - ], - idProperty: 'upid' - }); - - Ext.define('proxmox-cluster-log', { - extend: 'Ext.data.Model', - fields: [ - { name: 'uid' , type: 'int' }, - { name: 'time', type : 'date', dateFormat: 'timestamp' }, - { name: 'pri', type: 'int' }, - { name: 'pid', type: 'int' }, - 'node', 'user', 'tag', 'msg', - { - name: 'id', - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - // compute unique ID - return info.uid + ':' + info.node; - } - } - ], - idProperty: 'id' - }); - -}); -/* Extends the Ext.data.Store type - * with startUpdate() and stopUpdate() methods - * to refresh the store data in the background - * Components using this store directly will flicker - * due to the redisplay of the element ater 'config.interval' ms - * - * Note that you have to call yourself startUpdate() for the background load - * to begin - */ -Ext.define('Proxmox.data.UpdateStore', { - extend: 'Ext.data.Store', - alias: 'store.update', - - isStopped: true, - - autoStart: false, - - destroy: function() { - var me = this; - me.stopUpdate(); - me.callParent(); - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.interval) { - config.interval = 3000; - } - - if (!config.storeid) { - throw "no storeid specified"; - } - - var load_task = new Ext.util.DelayedTask(); - - var run_load_task = function() { - if (me.isStopped) { - return; - } - - if (Proxmox.Utils.authOK()) { - var start = new Date(); - me.load(function() { - var runtime = (new Date()) - start; - var interval = config.interval + runtime*2; - load_task.delay(interval, run_load_task); - }); - } else { - load_task.delay(200, run_load_task); - } - }; - - Ext.apply(config, { - startUpdate: function() { - me.isStopped = false; - // run_load_task(); this makes problems with chrome - load_task.delay(1, run_load_task); - }, - stopUpdate: function() { - me.isStopped = true; - load_task.cancel(); - } - }); - - me.callParent([config]); - - me.load_task = load_task; - - if (me.autoStart) { - me.startUpdate(); - } - } -}); -/* - * The DiffStore is a in-memory store acting as proxy between a real store - * instance and a component. - * Its purpose is to redisplay the component *only* if the data has been changed - * inside the real store, to avoid the annoying visual flickering of using - * the real store directly. - * - * Implementation: - * The DiffStore monitors via mon() the 'load' events sent by the real store. - * On each 'load' event, the DiffStore compares its own content with the target - * store (call to cond_add_item()) and then fires a 'refresh' event. - * The 'refresh' event will automatically trigger a view refresh on the component - * who binds to this store. - */ - -/* Config properties: - * rstore: the realstore which will autorefresh its content from the API - * Only works if rstore has a model and use 'idProperty' - * sortAfterUpdate: sort the diffstore before rendering the view - */ -Ext.define('Proxmox.data.DiffStore', { - extend: 'Ext.data.Store', - alias: 'store.diff', - - sortAfterUpdate: false, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.rstore) { - throw "no rstore specified"; - } - - if (!config.rstore.model) { - throw "no rstore model specified"; - } - - var rstore = config.rstore; - - Ext.apply(config, { - model: rstore.model, - proxy: { type: 'memory' } - }); - - me.callParent([config]); - - var first_load = true; - - var cond_add_item = function(data, id) { - var olditem = me.getById(id); - if (olditem) { - olditem.beginEdit(); - Ext.Array.each(me.model.prototype.fields, function(field) { - if (olditem.data[field.name] !== data[field.name]) { - olditem.set(field.name, data[field.name]); - } - }); - olditem.endEdit(true); - olditem.commit(); - } else { - var newrec = Ext.create(me.model, data); - var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length; - me.insert(pos, newrec); - } - }; - - var loadFn = function(s, records, success) { - - if (!success) { - return; - } - - me.suspendEvents(); - - // getSource returns null if data is not filtered - // if it is filtered it returns all records - var allItems = me.getData().getSource() || me.getData(); - - // remove vanished items - allItems.each(function(olditem) { - var item = rstore.getById(olditem.getId()); - if (!item) { - me.remove(olditem); - } - }); - - rstore.each(function(item) { - cond_add_item(item.data, item.getId()); - }); - - me.filter(); - - if (me.sortAfterUpdate) { - me.sort(); - } - - first_load = false; - - me.resumeEvents(); - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - }; - - if (rstore.isLoaded()) { - // if store is already loaded, - // insert items instantly - loadFn(rstore, [], true); - } - - me.mon(rstore, 'load', loadFn); - } -}); -/* This store encapsulates data items which are organized as an Array of key-values Objects - * ie data[0] contains something like {key: "keyboard", value: "da"} -* -* Designed to work with the KeyValue model and the JsonObject data reader -*/ -Ext.define('Proxmox.data.ObjectStore', { - extend: 'Proxmox.data.UpdateStore', - - getRecord: function() { - var me = this; - var record = Ext.create('Ext.data.Model'); - me.getData().each(function(item) { - record.set(item.data.key, item.data.value); - }); - record.commit(true); - return record; - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.storeid) { - config.storeid = 'proxmox-store-' + (++Ext.idSeed); - } - - Ext.applyIf(config, { - model: 'KeyValue', - proxy: { - type: 'proxmox', - url: config.url, - extraParams: config.extraParams, - reader: { - type: 'jsonobject', - rows: config.rows, - readArray: config.readArray, - rootProperty: config.root || 'data' - } - } - }); - - me.callParent([config]); - } -}); -/* Extends the Proxmox.data.UpdateStore type - * - * - */ -Ext.define('Proxmox.data.RRDStore', { - extend: 'Proxmox.data.UpdateStore', - alias: 'store.proxmoxRRDStore', - - setRRDUrl: function(timeframe, cf) { - var me = this; - if (!timeframe) { - timeframe = me.timeframe; - } - - if (!cf) { - cf = me.cf; - } - - me.proxy.url = me.rrdurl + "?timeframe=" + timeframe + "&cf=" + cf; - }, - - proxy: { - type: 'proxmox' - }, - - timeframe: 'hour', - - cf: 'AVERAGE', - - constructor: function(config) { - var me = this; - - config = config || {}; - - // set default interval to 30seconds - if (!config.interval) { - config.interval = 30000; - } - - // set a new storeid - if (!config.storeid) { - config.storeid = 'rrdstore-' + (++Ext.idSeed); - } - - // rrdurl is required - if (!config.rrdurl) { - throw "no rrdurl specified"; - } - - var stateid = 'proxmoxRRDTypeSelection'; - var sp = Ext.state.Manager.getProvider(); - var stateinit = sp.get(stateid); - - if (stateinit) { - if(stateinit.timeframe !== me.timeframe || stateinit.cf !== me.rrdcffn){ - me.timeframe = stateinit.timeframe; - me.rrdcffn = stateinit.cf; - } - } - - me.callParent([config]); - - me.setRRDUrl(); - me.mon(sp, 'statechange', function(prov, key, state){ - if (key === stateid) { - if (state && state.id) { - if (state.timeframe !== me.timeframe || state.cf !== me.cf) { - me.timeframe = state.timeframe; - me.cf = state.cf; - me.setRRDUrl(); - me.reload(); - } - } - } - }); - } -}); -Ext.define('Timezone', { - extend: 'Ext.data.Model', - fields: ['zone'] -}); - -Ext.define('Proxmox.data.TimezoneStore', { - extend: 'Ext.data.Store', - model: 'Timezone', - data: [ - ['Africa/Abidjan'], - ['Africa/Accra'], - ['Africa/Addis_Ababa'], - ['Africa/Algiers'], - ['Africa/Asmara'], - ['Africa/Bamako'], - ['Africa/Bangui'], - ['Africa/Banjul'], - ['Africa/Bissau'], - ['Africa/Blantyre'], - ['Africa/Brazzaville'], - ['Africa/Bujumbura'], - ['Africa/Cairo'], - ['Africa/Casablanca'], - ['Africa/Ceuta'], - ['Africa/Conakry'], - ['Africa/Dakar'], - ['Africa/Dar_es_Salaam'], - ['Africa/Djibouti'], - ['Africa/Douala'], - ['Africa/El_Aaiun'], - ['Africa/Freetown'], - ['Africa/Gaborone'], - ['Africa/Harare'], - ['Africa/Johannesburg'], - ['Africa/Kampala'], - ['Africa/Khartoum'], - ['Africa/Kigali'], - ['Africa/Kinshasa'], - ['Africa/Lagos'], - ['Africa/Libreville'], - ['Africa/Lome'], - ['Africa/Luanda'], - ['Africa/Lubumbashi'], - ['Africa/Lusaka'], - ['Africa/Malabo'], - ['Africa/Maputo'], - ['Africa/Maseru'], - ['Africa/Mbabane'], - ['Africa/Mogadishu'], - ['Africa/Monrovia'], - ['Africa/Nairobi'], - ['Africa/Ndjamena'], - ['Africa/Niamey'], - ['Africa/Nouakchott'], - ['Africa/Ouagadougou'], - ['Africa/Porto-Novo'], - ['Africa/Sao_Tome'], - ['Africa/Tripoli'], - ['Africa/Tunis'], - ['Africa/Windhoek'], - ['America/Adak'], - ['America/Anchorage'], - ['America/Anguilla'], - ['America/Antigua'], - ['America/Araguaina'], - ['America/Argentina/Buenos_Aires'], - ['America/Argentina/Catamarca'], - ['America/Argentina/Cordoba'], - ['America/Argentina/Jujuy'], - ['America/Argentina/La_Rioja'], - ['America/Argentina/Mendoza'], - ['America/Argentina/Rio_Gallegos'], - ['America/Argentina/Salta'], - ['America/Argentina/San_Juan'], - ['America/Argentina/San_Luis'], - ['America/Argentina/Tucuman'], - ['America/Argentina/Ushuaia'], - ['America/Aruba'], - ['America/Asuncion'], - ['America/Atikokan'], - ['America/Bahia'], - ['America/Bahia_Banderas'], - ['America/Barbados'], - ['America/Belem'], - ['America/Belize'], - ['America/Blanc-Sablon'], - ['America/Boa_Vista'], - ['America/Bogota'], - ['America/Boise'], - ['America/Cambridge_Bay'], - ['America/Campo_Grande'], - ['America/Cancun'], - ['America/Caracas'], - ['America/Cayenne'], - ['America/Cayman'], - ['America/Chicago'], - ['America/Chihuahua'], - ['America/Costa_Rica'], - ['America/Cuiaba'], - ['America/Curacao'], - ['America/Danmarkshavn'], - ['America/Dawson'], - ['America/Dawson_Creek'], - ['America/Denver'], - ['America/Detroit'], - ['America/Dominica'], - ['America/Edmonton'], - ['America/Eirunepe'], - ['America/El_Salvador'], - ['America/Fortaleza'], - ['America/Glace_Bay'], - ['America/Godthab'], - ['America/Goose_Bay'], - ['America/Grand_Turk'], - ['America/Grenada'], - ['America/Guadeloupe'], - ['America/Guatemala'], - ['America/Guayaquil'], - ['America/Guyana'], - ['America/Halifax'], - ['America/Havana'], - ['America/Hermosillo'], - ['America/Indiana/Indianapolis'], - ['America/Indiana/Knox'], - ['America/Indiana/Marengo'], - ['America/Indiana/Petersburg'], - ['America/Indiana/Tell_City'], - ['America/Indiana/Vevay'], - ['America/Indiana/Vincennes'], - ['America/Indiana/Winamac'], - ['America/Inuvik'], - ['America/Iqaluit'], - ['America/Jamaica'], - ['America/Juneau'], - ['America/Kentucky/Louisville'], - ['America/Kentucky/Monticello'], - ['America/La_Paz'], - ['America/Lima'], - ['America/Los_Angeles'], - ['America/Maceio'], - ['America/Managua'], - ['America/Manaus'], - ['America/Marigot'], - ['America/Martinique'], - ['America/Matamoros'], - ['America/Mazatlan'], - ['America/Menominee'], - ['America/Merida'], - ['America/Mexico_City'], - ['America/Miquelon'], - ['America/Moncton'], - ['America/Monterrey'], - ['America/Montevideo'], - ['America/Montreal'], - ['America/Montserrat'], - ['America/Nassau'], - ['America/New_York'], - ['America/Nipigon'], - ['America/Nome'], - ['America/Noronha'], - ['America/North_Dakota/Center'], - ['America/North_Dakota/New_Salem'], - ['America/Ojinaga'], - ['America/Panama'], - ['America/Pangnirtung'], - ['America/Paramaribo'], - ['America/Phoenix'], - ['America/Port-au-Prince'], - ['America/Port_of_Spain'], - ['America/Porto_Velho'], - ['America/Puerto_Rico'], - ['America/Rainy_River'], - ['America/Rankin_Inlet'], - ['America/Recife'], - ['America/Regina'], - ['America/Resolute'], - ['America/Rio_Branco'], - ['America/Santa_Isabel'], - ['America/Santarem'], - ['America/Santiago'], - ['America/Santo_Domingo'], - ['America/Sao_Paulo'], - ['America/Scoresbysund'], - ['America/Shiprock'], - ['America/St_Barthelemy'], - ['America/St_Johns'], - ['America/St_Kitts'], - ['America/St_Lucia'], - ['America/St_Thomas'], - ['America/St_Vincent'], - ['America/Swift_Current'], - ['America/Tegucigalpa'], - ['America/Thule'], - ['America/Thunder_Bay'], - ['America/Tijuana'], - ['America/Toronto'], - ['America/Tortola'], - ['America/Vancouver'], - ['America/Whitehorse'], - ['America/Winnipeg'], - ['America/Yakutat'], - ['America/Yellowknife'], - ['Antarctica/Casey'], - ['Antarctica/Davis'], - ['Antarctica/DumontDUrville'], - ['Antarctica/Macquarie'], - ['Antarctica/Mawson'], - ['Antarctica/McMurdo'], - ['Antarctica/Palmer'], - ['Antarctica/Rothera'], - ['Antarctica/South_Pole'], - ['Antarctica/Syowa'], - ['Antarctica/Vostok'], - ['Arctic/Longyearbyen'], - ['Asia/Aden'], - ['Asia/Almaty'], - ['Asia/Amman'], - ['Asia/Anadyr'], - ['Asia/Aqtau'], - ['Asia/Aqtobe'], - ['Asia/Ashgabat'], - ['Asia/Baghdad'], - ['Asia/Bahrain'], - ['Asia/Baku'], - ['Asia/Bangkok'], - ['Asia/Beirut'], - ['Asia/Bishkek'], - ['Asia/Brunei'], - ['Asia/Choibalsan'], - ['Asia/Chongqing'], - ['Asia/Colombo'], - ['Asia/Damascus'], - ['Asia/Dhaka'], - ['Asia/Dili'], - ['Asia/Dubai'], - ['Asia/Dushanbe'], - ['Asia/Gaza'], - ['Asia/Harbin'], - ['Asia/Ho_Chi_Minh'], - ['Asia/Hong_Kong'], - ['Asia/Hovd'], - ['Asia/Irkutsk'], - ['Asia/Jakarta'], - ['Asia/Jayapura'], - ['Asia/Jerusalem'], - ['Asia/Kabul'], - ['Asia/Kamchatka'], - ['Asia/Karachi'], - ['Asia/Kashgar'], - ['Asia/Kathmandu'], - ['Asia/Kolkata'], - ['Asia/Krasnoyarsk'], - ['Asia/Kuala_Lumpur'], - ['Asia/Kuching'], - ['Asia/Kuwait'], - ['Asia/Macau'], - ['Asia/Magadan'], - ['Asia/Makassar'], - ['Asia/Manila'], - ['Asia/Muscat'], - ['Asia/Nicosia'], - ['Asia/Novokuznetsk'], - ['Asia/Novosibirsk'], - ['Asia/Omsk'], - ['Asia/Oral'], - ['Asia/Phnom_Penh'], - ['Asia/Pontianak'], - ['Asia/Pyongyang'], - ['Asia/Qatar'], - ['Asia/Qyzylorda'], - ['Asia/Rangoon'], - ['Asia/Riyadh'], - ['Asia/Sakhalin'], - ['Asia/Samarkand'], - ['Asia/Seoul'], - ['Asia/Shanghai'], - ['Asia/Singapore'], - ['Asia/Taipei'], - ['Asia/Tashkent'], - ['Asia/Tbilisi'], - ['Asia/Tehran'], - ['Asia/Thimphu'], - ['Asia/Tokyo'], - ['Asia/Ulaanbaatar'], - ['Asia/Urumqi'], - ['Asia/Vientiane'], - ['Asia/Vladivostok'], - ['Asia/Yakutsk'], - ['Asia/Yekaterinburg'], - ['Asia/Yerevan'], - ['Atlantic/Azores'], - ['Atlantic/Bermuda'], - ['Atlantic/Canary'], - ['Atlantic/Cape_Verde'], - ['Atlantic/Faroe'], - ['Atlantic/Madeira'], - ['Atlantic/Reykjavik'], - ['Atlantic/South_Georgia'], - ['Atlantic/St_Helena'], - ['Atlantic/Stanley'], - ['Australia/Adelaide'], - ['Australia/Brisbane'], - ['Australia/Broken_Hill'], - ['Australia/Currie'], - ['Australia/Darwin'], - ['Australia/Eucla'], - ['Australia/Hobart'], - ['Australia/Lindeman'], - ['Australia/Lord_Howe'], - ['Australia/Melbourne'], - ['Australia/Perth'], - ['Australia/Sydney'], - ['Europe/Amsterdam'], - ['Europe/Andorra'], - ['Europe/Athens'], - ['Europe/Belgrade'], - ['Europe/Berlin'], - ['Europe/Bratislava'], - ['Europe/Brussels'], - ['Europe/Bucharest'], - ['Europe/Budapest'], - ['Europe/Chisinau'], - ['Europe/Copenhagen'], - ['Europe/Dublin'], - ['Europe/Gibraltar'], - ['Europe/Guernsey'], - ['Europe/Helsinki'], - ['Europe/Isle_of_Man'], - ['Europe/Istanbul'], - ['Europe/Jersey'], - ['Europe/Kaliningrad'], - ['Europe/Kiev'], - ['Europe/Lisbon'], - ['Europe/Ljubljana'], - ['Europe/London'], - ['Europe/Luxembourg'], - ['Europe/Madrid'], - ['Europe/Malta'], - ['Europe/Mariehamn'], - ['Europe/Minsk'], - ['Europe/Monaco'], - ['Europe/Moscow'], - ['Europe/Oslo'], - ['Europe/Paris'], - ['Europe/Podgorica'], - ['Europe/Prague'], - ['Europe/Riga'], - ['Europe/Rome'], - ['Europe/Samara'], - ['Europe/San_Marino'], - ['Europe/Sarajevo'], - ['Europe/Simferopol'], - ['Europe/Skopje'], - ['Europe/Sofia'], - ['Europe/Stockholm'], - ['Europe/Tallinn'], - ['Europe/Tirane'], - ['Europe/Uzhgorod'], - ['Europe/Vaduz'], - ['Europe/Vatican'], - ['Europe/Vienna'], - ['Europe/Vilnius'], - ['Europe/Volgograd'], - ['Europe/Warsaw'], - ['Europe/Zagreb'], - ['Europe/Zaporozhye'], - ['Europe/Zurich'], - ['Indian/Antananarivo'], - ['Indian/Chagos'], - ['Indian/Christmas'], - ['Indian/Cocos'], - ['Indian/Comoro'], - ['Indian/Kerguelen'], - ['Indian/Mahe'], - ['Indian/Maldives'], - ['Indian/Mauritius'], - ['Indian/Mayotte'], - ['Indian/Reunion'], - ['Pacific/Apia'], - ['Pacific/Auckland'], - ['Pacific/Chatham'], - ['Pacific/Chuuk'], - ['Pacific/Easter'], - ['Pacific/Efate'], - ['Pacific/Enderbury'], - ['Pacific/Fakaofo'], - ['Pacific/Fiji'], - ['Pacific/Funafuti'], - ['Pacific/Galapagos'], - ['Pacific/Gambier'], - ['Pacific/Guadalcanal'], - ['Pacific/Guam'], - ['Pacific/Honolulu'], - ['Pacific/Johnston'], - ['Pacific/Kiritimati'], - ['Pacific/Kosrae'], - ['Pacific/Kwajalein'], - ['Pacific/Majuro'], - ['Pacific/Marquesas'], - ['Pacific/Midway'], - ['Pacific/Nauru'], - ['Pacific/Niue'], - ['Pacific/Norfolk'], - ['Pacific/Noumea'], - ['Pacific/Pago_Pago'], - ['Pacific/Palau'], - ['Pacific/Pitcairn'], - ['Pacific/Pohnpei'], - ['Pacific/Port_Moresby'], - ['Pacific/Rarotonga'], - ['Pacific/Saipan'], - ['Pacific/Tahiti'], - ['Pacific/Tarawa'], - ['Pacific/Tongatapu'], - ['Pacific/Wake'], - ['Pacific/Wallis'] - ] -}); -Ext.define('Proxmox.form.field.Integer',{ - extend: 'Ext.form.field.Number', - alias: 'widget.proxmoxintegerfield', - - config: { - deleteEmpty: false - }, - - allowDecimals: false, - allowExponential: false, - step: 1, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== undefined && val !== null && val !== '') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - } - -}); -Ext.define('Proxmox.form.field.Textfield', { - extend: 'Ext.form.field.Text', - alias: ['widget.proxmoxtextfield'], - - config: { - skipEmptyText: true, - - deleteEmpty: false, - }, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - getSubmitValue: function() { - var me = this; - - var value = this.processRawValue(this.getRawValue()); - if (value !== '') { - return value; - } - - return me.getSkipEmptyText() ? null: value; - }, - - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - } -}); -Ext.define('Proxmox.DateTimeField', { - extend: 'Ext.form.FieldContainer', - xtype: 'promxoxDateTimeField', - - layout: 'hbox', - - referenceHolder: true, - - submitFormat: 'U', - - getValue: function() { - var me = this; - var d = me.lookupReference('dateentry').getValue(); - - if (d === undefined || d === null) { return null; } - - var t = me.lookupReference('timeentry').getValue(); - - if (t === undefined || t === null) { return null; } - - var offset = (t.getHours()*3600+t.getMinutes()*60)*1000; - - return new Date(d.getTime() + offset); - }, - - getSubmitValue: function() { - var me = this; - var format = me.submitFormat; - var value = me.getValue(); - - return value ? Ext.Date.format(value, format) : null; - }, - - items: [ - { - xtype: 'datefield', - editable: false, - reference: 'dateentry', - flex: 1, - format: 'Y-m-d' - }, - { - xtype: 'timefield', - reference: 'timeentry', - format: 'H:i', - width: 80, - value: '00:00', - increment: 60 - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - - var value = me.value || new Date(); - - me.lookupReference('dateentry').setValue(value); - me.lookupReference('timeentry').setValue(value); - - me.relayEvents(me.lookupReference('dateentry'), ['change']); - me.relayEvents(me.lookupReference('timeentry'), ['change']); - } -}); -Ext.define('Proxmox.form.Checkbox', { - extend: 'Ext.form.field.Checkbox', - alias: ['widget.proxmoxcheckbox'], - - config: { - defaultValue: undefined, - deleteDefaultValue: false, - deleteEmpty: false - }, - - inputValue: '1', - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - if ((val == me.getDefaultValue()) && me.getDeleteDefaultValue()) { - data['delete'] = me.getName(); - } else { - data[me.getName()] = val; - } - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - // also accept integer 1 as true - setRawValue: function(value) { - var me = this; - - if (value === 1) { - me.callParent([true]); - } else { - me.callParent([value]); - } - } - -}); -/* Key-Value ComboBox - * - * config properties: - * comboItems: an array of Key - Value pairs - * deleteEmpty: if set to true (default), an empty value received from the - * comboBox will reset the property to its default value - */ -Ext.define('Proxmox.form.KVComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.proxmoxKVComboBox', - - config: { - deleteEmpty: true - }, - - comboItems: undefined, - displayField: 'value', - valueField: 'key', - queryMode: 'local', - - // overide framework function to implement deleteEmpty behaviour - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null && val !== '' && val !== '__default__') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - validator: function(val) { - var me = this; - - if (me.editable || val === null || val === '') { - return true; - } - - if (me.store.getCount() > 0) { - var values = me.multiSelect ? val.split(me.delimiter) : [val]; - var items = me.store.getData().collect('value', 'data'); - if (Ext.Array.every(values, function(value) { - return Ext.Array.contains(items, value); - })) { - return true; - } - } - - // returns a boolean or string - /*jslint confusion: true */ - return "value '" + val + "' not allowed!"; - }, - - initComponent: function() { - var me = this; - - me.store = Ext.create('Ext.data.ArrayStore', { - model: 'KeyValue', - data : me.comboItems - }); - - if (me.initialConfig.editable === undefined) { - me.editable = false; - } - - me.callParent(); - } -}); -Ext.define('Proxmox.form.LanguageSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'proxmoxLanguageSelector', - - comboItems: Proxmox.Utils.language_array() -}); -/* - * ComboGrid component: a ComboBox where the dropdown menu (the - * "Picker") is a Grid with Rows and Columns expects a listConfig - * object with a columns property roughly based on the GridPicker from - * https://www.sencha.com/forum/showthread.php?299909 - * -*/ - -Ext.define('Proxmox.form.ComboGrid', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxComboGrid'], - - // this value is used as default value after load() - preferredValue: undefined, - - // hack: allow to select empty value - // seems extjs does not allow that when 'editable == false' - onKeyUp: function(e, t) { - var me = this; - var key = e.getKey(); - - if (!me.editable && me.allowBlank && !me.multiSelect && - (key == e.BACKSPACE || key == e.DELETE)) { - me.setValue(''); - } - - me.callParent(arguments); - }, - - // needed to trigger onKeyUp etc. - enableKeyEvents: true, - - editable: false, - - // override ExtJS method - // if the field has multiSelect enabled, the store is not loaded, and - // the displayfield == valuefield, it saves the rawvalue as an array - // but the getRawValue method is only defined in the textfield class - // (which has not to deal with arrays) an returns the string in the - // field (not an array) - // - // so if we have multiselect enabled, return the rawValue (which - // should be an array) and else we do callParent so - // it should not impact any other use of the class - getRawValue: function() { - var me = this; - if (me.multiSelect) { - return me.rawValue; - } else { - return me.callParent(); - } - }, - -// override ExtJS protected method - onBindStore: function(store, initial) { - var me = this, - picker = me.picker, - extraKeySpec, - valueCollectionConfig; - - // We're being bound, not unbound... - if (store) { - // If store was created from a 2 dimensional array with generated field names 'field1' and 'field2' - if (store.autoCreated) { - me.queryMode = 'local'; - me.valueField = me.displayField = 'field1'; - if (!store.expanded) { - me.displayField = 'field2'; - } - - // displayTpl config will need regenerating with the autogenerated displayField name 'field1' - me.setDisplayTpl(null); - } - if (!Ext.isDefined(me.valueField)) { - me.valueField = me.displayField; - } - - // Add a byValue index to the store so that we can efficiently look up records by the value field - // when setValue passes string value(s). - // The two indices (Ext.util.CollectionKeys) are configured unique: false, so that if duplicate keys - // are found, they are all returned by the get call. - // This is so that findByText and findByValue are able to return the *FIRST* matching value. By default, - // if unique is true, CollectionKey keeps the *last* matching value. - extraKeySpec = { - byValue: { - rootProperty: 'data', - unique: false - } - }; - extraKeySpec.byValue.property = me.valueField; - store.setExtraKeys(extraKeySpec); - - if (me.displayField === me.valueField) { - store.byText = store.byValue; - } else { - extraKeySpec.byText = { - rootProperty: 'data', - unique: false - }; - extraKeySpec.byText.property = me.displayField; - store.setExtraKeys(extraKeySpec); - } - - // We hold a collection of the values which have been selected, keyed by this field's valueField. - // This collection also functions as the selected items collection for the BoundList's selection model - valueCollectionConfig = { - rootProperty: 'data', - extraKeys: { - byInternalId: { - property: 'internalId' - }, - byValue: { - property: me.valueField, - rootProperty: 'data' - } - }, - // Whenever this collection is changed by anyone, whether by this field adding to it, - // or the BoundList operating, we must refresh our value. - listeners: { - beginupdate: me.onValueCollectionBeginUpdate, - endupdate: me.onValueCollectionEndUpdate, - scope: me - } - }; - - // This becomes our collection of selected records for the Field. - me.valueCollection = new Ext.util.Collection(valueCollectionConfig); - - // We use the selected Collection as our value collection and the basis - // for rendering the tag list. - - //proxmox override: since the picker is represented by a grid panel, - // we changed here the selection to RowModel - me.pickerSelectionModel = new Ext.selection.RowModel({ - mode: me.multiSelect ? 'SIMPLE' : 'SINGLE', - // There are situations when a row is selected on mousedown but then the mouse is dragged to another row - // and released. In these situations, the event target for the click event won't be the row where the mouse - // was released but the boundview. The view will then determine that it should fire a container click, and - // the DataViewModel will then deselect all prior selections. Setting `deselectOnContainerClick` here will - // prevent the model from deselecting. - deselectOnContainerClick: false, - enableInitialSelection: false, - pruneRemoved: false, - selected: me.valueCollection, - store: store, - listeners: { - scope: me, - lastselectedchanged: me.updateBindSelection - } - }); - - if (!initial) { - me.resetToDefault(); - } - - if (picker) { - picker.setSelectionModel(me.pickerSelectionModel); - if (picker.getStore() !== store) { - picker.bindStore(store); - } - } - } - }, - - // copied from ComboBox - createPicker: function() { - var me = this; - var picker; - - var pickerCfg = Ext.apply({ - // proxmox overrides: display a grid for selection - xtype: 'gridpanel', - id: me.pickerId, - pickerField: me, - floating: true, - hidden: true, - store: me.store, - displayField: me.displayField, - preserveScrollOnRefresh: true, - pageSize: me.pageSize, - tpl: me.tpl, - selModel: me.pickerSelectionModel, - focusOnToFront: false - }, me.listConfig, me.defaultListConfig); - - picker = me.picker || Ext.widget(pickerCfg); - - if (picker.getStore() !== me.store) { - picker.bindStore(me.store); - } - - if (me.pageSize) { - picker.pagingToolbar.on('beforechange', me.onPageChange, me); - } - - // proxmox overrides: pass missing method in gridPanel to its view - picker.refresh = function() { - picker.getSelectionModel().select(me.valueCollection.getRange()); - picker.getView().refresh(); - }; - picker.getNodeByRecord = function() { - picker.getView().getNodeByRecord(arguments); - }; - - // We limit the height of the picker to fit in the space above - // or below this field unless the picker has its own ideas about that. - if (!picker.initialConfig.maxHeight) { - picker.on({ - beforeshow: me.onBeforePickerShow, - scope: me - }); - } - picker.getSelectionModel().on({ - beforeselect: me.onBeforeSelect, - beforedeselect: me.onBeforeDeselect, - focuschange: me.onFocusChange, - selectionChange: function (sm, selectedRecords) { - var me = this; - if (selectedRecords.length) { - me.setValue(selectedRecords); - me.fireEvent('select', me, selectedRecords); - } - }, - scope: me - }); - - // hack for extjs6 - // when the clicked item is the same as the previously selected, - // it does not select the item - // instead we hide the picker - if (!me.multiSelect) { - picker.on('itemclick', function (sm,record) { - if (picker.getSelection()[0] === record) { - picker.hide(); - } - }); - } - - // when our store is not yet loaded, we increase - // the height of the gridpanel, so that we can see - // the loading mask - // - // we save the minheight to reset it after the load - picker.on('show', function() { - if (me.enableLoadMask) { - me.savedMinHeight = picker.getMinHeight(); - picker.setMinHeight(100); - } - }); - - picker.getNavigationModel().navigateOnSpace = false; - - return picker; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - queryMode: 'local', - matchFieldWidth: false - }); - - Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug - - Ext.applyIf(me.listConfig, { width: 400 }); - - me.callParent(); - - // Create the picker at an early stage, so it is available to store the previous selection - if (!me.picker) { - me.createPicker(); - } - - if (me.editable) { - // The trigger.picker causes first a focus event on the field then - // toggles the selection picker. Thus skip expanding in this case, - // else our focus listner expands and the picker.trigger then - // collapses it directly afterwards. - Ext.override(me.triggers.picker, { - onMouseDown : function (e) { - // copied "should we focus" check from Ext.form.trigger.Trigger - if (e.pointerType !== 'touch' && !this.field.owns(Ext.Element.getActiveElement())) { - me.skip_expand_on_focus = true; - } - this.callParent(arguments); - } - }); - - me.on("focus", function(me) { - if (!me.isExpanded && !me.skip_expand_on_focus) { - me.expand(); - } - me.skip_expand_on_focus = false; - }); - } - - me.mon(me.store, 'beforeload', function() { - if (!me.isDisabled()) { - me.enableLoadMask = true; - } - }); - - // hack: autoSelect does not work - me.mon(me.store, 'load', function(store, r, success, o) { - if (success) { - me.clearInvalid(); - - if (me.enableLoadMask) { - delete me.enableLoadMask; - - // if the picker exists, - // we reset its minheight to the saved var/0 - // we have to update the layout, otherwise the height - // gets not recalculated - if (me.picker) { - me.picker.setMinHeight(me.savedMinHeight || 0); - delete me.savedMinHeight; - me.picker.updateLayout(); - } - } - - var def = me.getValue() || me.preferredValue; - if (def) { - me.setValue(def, true); // sync with grid - } - var found = false; - if (def) { - if (Ext.isArray(def)) { - Ext.Array.each(def, function(v) { - if (store.findRecord(me.valueField, v)) { - found = true; - return false; // break - } - }); - } else { - found = store.findRecord(me.valueField, def); - } - } - - if (!found) { - var rec = me.store.first(); - if (me.autoSelect && rec && rec.data) { - def = rec.data[me.valueField]; - me.setValue(def, true); - } else { - me.setValue(me.editable ? def : '', true); - } - } - } - }); - } -}); -Ext.define('Proxmox.form.RRDTypeSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxRRDTypeSelector'], - - displayField: 'text', - valueField: 'id', - editable: false, - queryMode: 'local', - value: 'hour', - stateEvents: [ 'select' ], - stateful: true, - stateId: 'proxmoxRRDTypeSelection', - store: { - type: 'array', - fields: [ 'id', 'timeframe', 'cf', 'text' ], - data : [ - [ 'hour', 'hour', 'AVERAGE', - gettext('Hour') + ' (' + gettext('average') +')' ], - [ 'hourmax', 'hour', 'MAX', - gettext('Hour') + ' (' + gettext('maximum') + ')' ], - [ 'day', 'day', 'AVERAGE', - gettext('Day') + ' (' + gettext('average') + ')' ], - [ 'daymax', 'day', 'MAX', - gettext('Day') + ' (' + gettext('maximum') + ')' ], - [ 'week', 'week', 'AVERAGE', - gettext('Week') + ' (' + gettext('average') + ')' ], - [ 'weekmax', 'week', 'MAX', - gettext('Week') + ' (' + gettext('maximum') + ')' ], - [ 'month', 'month', 'AVERAGE', - gettext('Month') + ' (' + gettext('average') + ')' ], - [ 'monthmax', 'month', 'MAX', - gettext('Month') + ' (' + gettext('maximum') + ')' ], - [ 'year', 'year', 'AVERAGE', - gettext('Year') + ' (' + gettext('average') + ')' ], - [ 'yearmax', 'year', 'MAX', - gettext('Year') + ' (' + gettext('maximum') + ')' ] - ] - }, - // save current selection in the state Provider so RRDView can read it - getState: function() { - var ind = this.getStore().findExact('id', this.getValue()); - var rec = this.getStore().getAt(ind); - if (!rec) { - return; - } - return { - id: rec.data.id, - timeframe: rec.data.timeframe, - cf: rec.data.cf - }; - }, - // set selection based on last saved state - applyState : function(state) { - if (state && state.id) { - this.setValue(state.id); - } - } -}); -Ext.define('Proxmox.form.BondModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondModeSelector'], - - openvswitch: false, - - initComponent: function() { - var me = this; - - if (me.openvswitch) { - me.comboItems = [ - ['active-backup', 'active-backup'], - ['balance-slb', 'balance-slb'], - ['lacp-balance-slb', 'LACP (balance-slb)'], - ['lacp-balance-tcp', 'LACP (balance-tcp)'] - ]; - } else { - me.comboItems = [ - ['balance-rr', 'balance-rr'], - ['active-backup', 'active-backup'], - ['balance-xor', 'balance-xor'], - ['broadcast', 'broadcast'], - ['802.3ad', 'LACP (802.3ad)'], - ['balance-tlb', 'balance-tlb'], - ['balance-alb', 'balance-alb'] - ]; - } - - me.callParent(); - } -}); - -Ext.define('Proxmox.form.BondPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondPolicySelector'], - comboItems: [ - ['layer2', 'layer2'], - ['layer2+3', 'layer2+3'], - ['layer3+4', 'layer3+4'] - ] -}); - -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - */ -Ext.define('Proxmox.button.Button', { - extend: 'Ext.button.Button', - alias: 'widget.proxmoxButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - - // Note: me.realHandler may be a string (see named scopes) - var realHandler = me.handler; - - me.handler = function(button, event) { - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn !== 'yes') { - return; - } - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }); - } else { - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }; - } - - me.callParent(); - - var grid; - if (!me.selModel && me.selModel !== null) { - grid = me.up('grid'); - if (grid && grid.selModel) { - me.selModel = grid.selModel; - } - } - - if (me.waitMsgTarget === true) { - grid = me.up('grid'); - if (grid) { - me.waitMsgTarget = grid; - } else { - throw "unable to find waitMsgTarget"; - } - } - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); - - -Ext.define('Proxmox.button.StdRemoveButton', { - extend: 'Proxmox.button.Button', - alias: 'widget.proxmoxStdRemoveButton', - - text: gettext('Remove'), - - disabled: true, - - config: { - baseurl: undefined - }, - - getUrl: function(rec) { - var me = this; - - return me.baseurl + '/' + rec.getId(); - }, - - // also works with names scopes - callback: function(options, success, response) {}, - - getRecordName: function(rec) { return rec.getId() }, - - confirmMsg: function (rec) { - var me = this; - - var name = me.getRecordName(rec); - return Ext.String.format( - gettext('Are you sure you want to remove entry {0}'), - "'" + name + "'"); - }, - - handler: function(btn, event, rec) { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.getUrl(rec), - method: 'DELETE', - waitMsgTarget: me.waitMsgTarget, - callback: function(options, success, response) { - Ext.callback(me.callback, me.scope, [options, success, response], 0, me); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -/* help button pointing to an online documentation - for components contained in a modal window -*/ -/*global - proxmoxOnlineHelpInfo -*/ -Ext.define('Proxmox.button.Help', { - extend: 'Ext.button.Button', - xtype: 'proxmoxHelpButton', - - text: gettext('Help'), - - // make help button less flashy by styling it like toolbar buttons - iconCls: ' x-btn-icon-el-default-toolbar-small fa fa-question-circle', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - - hidden: true, - - listenToGlobalEvent: true, - - controller: { - xclass: 'Ext.app.ViewController', - listen: { - global: { - proxmoxShowHelp: 'onProxmoxShowHelp', - proxmoxHideHelp: 'onProxmoxHideHelp' - } - }, - onProxmoxShowHelp: function(helpLink) { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.setOnlineHelp(helpLink); - me.show(); - } - }, - onProxmoxHideHelp: function() { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.hide(); - } - } - }, - - getOnlineHelpInfo: function (ref) { - var helpMap; - if (typeof proxmoxOnlineHelpInfo !== 'undefined') { - helpMap = proxmoxOnlineHelpInfo; - } else if (typeof pveOnlineHelpInfo !== 'undefined') { - // be backward compatible with older pve-doc-generators - helpMap = pveOnlineHelpInfo; - } else { - throw "no global OnlineHelpInfo map declared"; - } - - return helpMap[ref]; - }, - - // this sets the link and the tooltip text - setOnlineHelp:function(blockid) { - var me = this; - - var info = me.getOnlineHelpInfo(blockid); - if (info) { - me.onlineHelp = blockid; - var title = info.title; - if (info.subtitle) { - title += ' - ' + info.subtitle; - } - me.setTooltip(title); - } - }, - - // helper to set the onlineHelp via a config object - setHelpConfig: function(config) { - var me = this; - me.setOnlineHelp(config.onlineHelp); - }, - - handler: function() { - var me = this; - var docsURI; - - if (me.onlineHelp) { - var info = me.getOnlineHelpInfo(me.onlineHelp); - if (info) { - docsURI = window.location.origin + info.link; - } - } - - if (docsURI) { - window.open(docsURI); - } else { - Ext.Msg.alert(gettext('Help'), gettext('No Help available')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.callParent(); - - if (me.onlineHelp) { - me.setOnlineHelp(me.onlineHelp); // set tooltip - } - } -}); -/* Renders a list of key values objets - -mandatory config parameters: -rows: an object container where each propery is a key-value object we want to render - var rows = { - keyboard: { - header: gettext('Keyboard Layout'), - editor: 'Your.KeyboardEdit', - required: true - }, - -optional: -disabled: setting this parameter to true will disable selection and focus on the -proxmoxObjectGrid as well as greying out input elements. -Useful for a readonly tabular display - -*/ - -Ext.define('Proxmox.grid.ObjectGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.proxmoxObjectGrid'], - disabled: false, - hideHeaders: true, - - monStoreErrors: false, - - add_combobox_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxKVComboBox', - name: name, - comboItems: opts.comboItems, - value: opts.defaultValue, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_text_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxtextfield', - name: name, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - vtype: opts.vtype, - fieldLabel: text - } - } - }; - }, - - add_boolean_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue || 0, - header: text, - renderer: opts.renderer || Proxmox.Utils.format_boolean, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxcheckbox', - name: name, - uncheckedValue: 0, - defaultValue: opts.defaultValue || 0, - checked: opts.defaultValue ? true : false, - deleteDefaultValue: opts.deleteDefaultValue ? true : false, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_integer_row: function(name, text, opts) { - var me = this; - - opts = opts || {} - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxintegerfield', - name: name, - minValue: opts.minValue, - maxValue: opts.maxValue, - emptyText: gettext('Default'), - deleteEmpty: opts.deleteEmpty ? true : false, - value: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - editorConfig: {}, // default config passed to editor - - run_editor: function() { - var me = this; - - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rows = me.rows; - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - var config; - if (Ext.isString(rowdef.editor)) { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - win = Ext.create(rowdef.editor, config); - } else { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - Ext.apply(config, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', me.reload, me); - }, - - reload: function() { - var me = this; - me.rstore.load(); - }, - - getObjectValue: function(key, defaultValue) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }, - - renderKey: function(key, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - return rowdef.header || key; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - - var renderer = rowdef.renderer; - if (renderer) { - return renderer(value, metaData, record, rowIndex, colIndex, store); - } - - return value; - }, - - listeners: { - itemkeydown: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER) { - this.pressedIndex = index; - } - }, - itemkeyup: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER && index == this.pressedIndex) { - this.run_editor(); - } - - this.pressedIndex = undefined; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - var rstore = me.rstore; - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore, - sorters: [], - filters: [] - }); - - if (rows) { - Ext.Object.each(rows, function(key, rowdef) { - if (Ext.isDefined(rowdef.defaultValue)) { - store.add({ key: key, value: rowdef.defaultValue }); - } else if (rowdef.required) { - store.add({ key: key, value: undefined }); - } - }); - } - - if (me.sorterFn) { - store.sorters.add(Ext.create('Ext.util.Sorter', { - sorterFn: me.sorterFn - })); - } - - store.filters.add(Ext.create('Ext.util.Filter', { - filterFn: function(item) { - if (rows) { - var rowdef = rows[item.data.key]; - if (!rowdef || (rowdef.visible === false)) { - return false; - } - } - return true; - } - })); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.applyIf(me, { - store: store, - stateful: false, - columns: [ - { - header: gettext('Name'), - width: me.cwidth1 || 200, - dataIndex: 'key', - renderer: me.renderKey - }, - { - flex: 1, - header: gettext('Value'), - dataIndex: 'value', - renderer: me.renderValue - } - ] - }); - - me.callParent(); - - if (me.monStoreErrors) { - Proxmox.Utils.monStoreErrors(me, me.store); - } - } -}); -Ext.define('Proxmox.grid.PendingObjectGrid', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxPendingObjectGrid'], - - getObjectValue: function(key, defaultValue, pending) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - var value = rec.data.value; - if (pending) { - if (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') { - value = rec.data.pending; - } else if (rec.data['delete'] === 1) { - value = defaultValue; - } - } - - if (Ext.isDefined(value) && (value !== '')) { - return value; - } else { - return defaultValue; - } - } - return defaultValue; - }, - - hasPendingChanges: function(key) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var keys = rowdef.multiKey || [ key ]; - var pending = false; - - Ext.Array.each(keys, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && ( - (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') || - rec.data['delete'] === 1 - )) { - pending = true; - return false; // break - } - }); - - return pending; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var renderer = rowdef.renderer; - var current = ''; - var pendingdelete = ''; - var pending = ''; - - if (renderer) { - current = renderer(value, metaData, record, rowIndex, colIndex, store, false); - if (me.hasPendingChanges(key)) { - pending = renderer(record.data.pending, metaData, record, rowIndex, colIndex, store, true); - } - if (pending == current) { - pending = undefined; - } - } else { - current = value || ''; - pending = record.data.pending; - } - - if (record.data['delete']) { - var delete_all = true; - if (rowdef.multiKey) { - Ext.Array.each(rowdef.multiKey, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && rec.data['delete'] !== 1) { - delete_all = false; - return false; // break - } - }); - } - if (delete_all) { - pending = '
'+ current +'
'; - } - } - - if (pending) { - return current + '
' + pending + '
'; - } else { - return current; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - model: 'KeyValuePendingDelete', - readArray: true, - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - me.callParent(); - } -}); -Ext.define('Proxmox.panel.InputPanel', { - extend: 'Ext.panel.Panel', - alias: ['widget.inputpanel'], - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - border: false, - - // override this with an URL to a relevant chapter of the pve manual - // setting this will display a help button in our parent panel - onlineHelp: undefined, - - // will be set if the inputpanel has advanced items - hasAdvanced: false, - - // if the panel has advanced items, - // this will determine if they are shown by default - showAdvanced: false, - - // overwrite this to modify submit data - onGetValues: function(values) { - return values; - }, - - getValues: function(dirtyOnly) { - var me = this; - - if (Ext.isFunction(me.onGetValues)) { - dirtyOnly = false; - } - - var values = {}; - - Ext.Array.each(me.query('[isFormField]'), function(field) { - if (!dirtyOnly || field.isDirty()) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - return me.onGetValues(values); - }, - - setAdvancedVisible: function(visible) { - var me = this; - var advItems = me.getComponent('advancedContainer'); - if (advItems) { - advItems.setVisible(visible); - } - }, - - setValues: function(values) { - var me = this; - - var form = me.up('form'); - - Ext.iterate(values, function(fieldId, val) { - var field = me.query('[isFormField][name=' + fieldId + ']')[0]; - if (field) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - }, - - initComponent: function() { - var me = this; - - var items; - - if (me.items) { - me.columns = 1; - items = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.items - } - ]; - me.items = undefined; - } else if (me.column4) { - me.columns = 4; - items = [ - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column2 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column3 - }, - { - columnWidth: 0.25, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column4 - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else if (me.column1) { - me.columns = 2; - items = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column2 || [] // allow empty column - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else { - throw "unsupported config"; - } - - var advItems; - if (me.advancedItems) { - advItems = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.advancedItems - } - ]; - me.advancedItems = undefined; - } else if (me.advancedColumn1) { - advItems = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.advancedColumn1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.advancedColumn2 || [] // allow empty column - } - ]; - - me.advancedColumn1 = undefined; - me.advancedColumn2 = undefined; - - if (me.advancedColumnB) { - advItems.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.advancedColumnB - }); - me.advancedColumnB = undefined; - } - } - - if (advItems) { - me.hasAdvanced = true; - advItems.unshift({ - columnWidth: 1, - xtype: 'box', - hidden: false, - border: true, - autoEl: { - tag: 'hr' - } - }); - items.push({ - columnWidth: 1, - xtype: 'container', - itemId: 'advancedContainer', - hidden: !me.showAdvanced, - layout: 'column', - defaults: { - border: false - }, - items: advItems - }); - } - - if (me.useFieldContainer) { - Ext.apply(me, { - layout: 'fit', - items: Ext.apply(me.useFieldContainer, { - layout: 'column', - defaultType: 'container', - items: items - }) - }); - } else { - Ext.apply(me, { - layout: 'column', - defaultType: 'container', - items: items - }); - } - - me.callParent(); - } -}); -/* - * Display log entries in a panel with scrollbar - * The log entries are automatically refreshed via a background task, - * with newest entries comming at the bottom - */ -Ext.define('Proxmox.panel.LogView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxLogView', - - pageSize: 500, - viewBuffer: 50, - lineHeight: 16, - - scrollToEnd: true, - - // callback for load failure, used for ceph - failCallback: undefined, - - controller: { - xclass: 'Ext.app.ViewController', - - updateParams: function() { - var me = this; - var viewModel = me.getViewModel(); - var since = viewModel.get('since'); - var until = viewModel.get('until'); - if (viewModel.get('hide_timespan')) { - return; - } - - if (since > until) { - Ext.Msg.alert('Error', 'Since date must be less equal than Until date.'); - return; - } - - viewModel.set('params.since', Ext.Date.format(since, 'Y-m-d')); - viewModel.set('params.until', Ext.Date.format(until, 'Y-m-d') + ' 23:59:59'); - me.getView().loadTask.delay(200); - }, - - scrollPosBottom: function() { - var view = this.getView(); - var pos = view.getScrollY(); - var maxPos = view.getScrollable().getMaxPosition().y; - return maxPos - pos; - }, - - updateView: function(text, first, total) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - var content = me.lookup('content'); - var data = viewModel.get('data'); - - if (first === data.first && total === data.total && text.length === data.textlen) { - return; // same content, skip setting and scrolling - } - viewModel.set('data', { - first: first, - total: total, - textlen: text.length - }); - - var scrollPos = me.scrollPosBottom(); - - content.update(text); - - if (view.scrollToEnd && scrollPos <= 0) { - // we use setTimeout to work around scroll handling on touchscreens - setTimeout(function() { view.scrollTo(0, Infinity); }, 10); - } - }, - - doLoad: function() { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - Proxmox.Utils.API2Request({ - url: me.getView().url, - params: viewModel.get('params'), - method: 'GET', - success: function(response) { - Proxmox.Utils.setErrorMask(me, false); - var total = response.result.total; - var lines = new Array(); - var first = Infinity; - - Ext.Array.each(response.result.data, function(line) { - if (first > line.n) { - first = line.n; - } - lines[line.n - 1] = Ext.htmlEncode(line.t); - }); - - lines.length = total; - me.updateView(lines.join('
'), first - 1, total); - }, - failure: function(response) { - if (view.failCallback) { - view.failCallback(response); - } else { - var msg = response.htmlStatus; - Proxmox.Utils.setErrorMask(me, msg); - } - } - }); - }, - - onScroll: function(x, y) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - - var lineHeight = view.lineHeight; - var line = view.getScrollY()/lineHeight; - var start = viewModel.get('params.start'); - var limit = viewModel.get('params.limit'); - var viewLines = view.getHeight()/lineHeight; - - var viewStart = Math.max(parseInt(line - 1 - view.viewBuffer, 10), 0); - var viewEnd = parseInt(line + viewLines + 1 + view.viewBuffer, 10); - - if (viewStart < start || viewEnd > (start+limit)) { - viewModel.set('params.start', - Math.max(parseInt(line - limit/2 + 10, 10), 0)); - view.loadTask.delay(200); - } - }, - - init: function(view) { - var me = this; - - if (!view.url) { - throw "no url specified"; - } - - var viewModel = this.getViewModel(); - var since = new Date(); - since.setDate(since.getDate() - 3); - viewModel.set('until', new Date()); - viewModel.set('since', since); - viewModel.set('params.limit', view.pageSize); - viewModel.set('hide_timespan', !view.log_select_timespan); - me.lookup('content').setStyle('line-height', view.lineHeight + 'px'); - - view.loadTask = new Ext.util.DelayedTask(me.doLoad, me); - - me.updateParams(); - view.task = Ext.TaskManager.start({ - run: function() { - if (!view.isVisible() || !view.scrollToEnd) { - return; - } - - if (me.scrollPosBottom() <= 1) { - view.loadTask.delay(200); - } - }, - interval: 1000 - }); - } - }, - - onDestroy: function() { - var me = this; - me.loadTask.cancel(); - Ext.TaskManager.stop(me.task); - }, - - // for user to initiate a load from outside - requestUpdate: function() { - var me = this; - me.loadTask.delay(200); - }, - - viewModel: { - data: { - until: null, - since: null, - hide_timespan: false, - data: { - start: 0, - total: 0, - textlen: 0 - }, - params: { - start: 0, - limit: 500, - } - } - }, - - layout: 'auto', - bodyPadding: 5, - scrollable: { - x: 'auto', - y: 'auto', - listeners: { - // we have to have this here, since we cannot listen to events - // of the scroller in the viewcontroller (extjs bug?), nor does - // the panel have a 'scroll' event' - scroll: { - fn: function(scroller, x, y) { - var controller = this.component.getController(); - if (controller) { // on destroy, controller can be gone - controller.onScroll(x,y); - } - }, - buffer: 200 - }, - } - }, - - tbar: { - bind: { - hidden: '{hide_timespan}' - }, - items: [ - '->', - 'Since: ', - { - xtype: 'datefield', - name: 'since_date', - reference: 'since', - format: 'Y-m-d', - bind: { - value: '{since}', - maxValue: '{until}' - } - }, - 'Until: ', - { - xtype: 'datefield', - name: 'until_date', - reference: 'until', - format: 'Y-m-d', - bind: { - value: '{until}', - minValue: '{since}' - } - }, - { - xtype: 'button', - text: 'Update', - handler: 'updateParams' - } - ], - }, - - items: [ - { - xtype: 'box', - reference: 'content', - style: { - font: 'normal 11px tahoma, arial, verdana, sans-serif', - 'white-space': 'pre' - }, - } - ] -}); -Ext.define('Proxmox.widget.RRDChart', { - extend: 'Ext.chart.CartesianChart', - alias: 'widget.proxmoxRRDChart', - - unit: undefined, // bytes, bytespersecond, percent - - controller: { - xclass: 'Ext.app.ViewController', - - convertToUnits: function(value) { - var units = ['', 'k','M','G','T', 'P']; - var si = 0; - while(value >= 1000 && si < (units.length -1)){ - value = value / 1000; - si++; - } - - // javascript floating point weirdness - value = Ext.Number.correctFloat(value); - - // limit to 2 decimal points - value = Ext.util.Format.number(value, "0.##"); - - return value.toString() + " " + units[si]; - }, - - leftAxisRenderer: function(axis, label, layoutContext) { - var me = this; - - return me.convertToUnits(label); - }, - - onSeriesTooltipRender: function(tooltip, record, item) { - var me = this.getView(); - - var suffix = ''; - - if (me.unit === 'percent') { - suffix = '%'; - } else if (me.unit === 'bytes') { - suffix = 'B'; - } else if (me.unit === 'bytespersecond') { - suffix = 'B/s'; - } - - var prefix = item.field; - if (me.fieldTitles && me.fieldTitles[me.fields.indexOf(item.field)]) { - prefix = me.fieldTitles[me.fields.indexOf(item.field)]; - } - tooltip.setHtml(prefix + ': ' + this.convertToUnits(record.get(item.field)) + suffix + - '
' + new Date(record.get('time'))); - }, - - onAfterAnimation: function(chart, eopts) { - // if the undobuton is disabled, - // disable our tool - - var ourUndoZoomButton = chart.tools[0]; - var undoButton = chart.interactions[0].getUndoButton(); - ourUndoZoomButton.setDisabled(undoButton.isDisabled()); - } - }, - - width: 770, - height: 300, - animation: false, - interactions: [{ - type: 'crosszoom' - }], - axes: [{ - type: 'numeric', - position: 'left', - grid: true, - renderer: 'leftAxisRenderer', - //renderer: function(axis, label) { return label; }, - minimum: 0 - }, { - type: 'time', - position: 'bottom', - grid: true, - fields: ['time'] - }], - legend: { - docked: 'bottom' - }, - listeners: { - animationend: 'onAfterAnimation' - }, - - - initComponent: function() { - var me = this; - var series = {}; - - if (!me.store) { - throw "cannot work without store"; - } - - if (!me.fields) { - throw "cannot work without fields"; - } - - me.callParent(); - - // add correct label for left axis - var axisTitle = ""; - if (me.unit === 'percent') { - axisTitle = "%"; - } else if (me.unit === 'bytes') { - axisTitle = "Bytes"; - } else if (me.unit === 'bytespersecond') { - axisTitle = "Bytes/s"; - } else if (me.fieldTitles && me.fieldTitles.length === 1) { - axisTitle = me.fieldTitles[0]; - } else if (me.fields.length === 1) { - axisTitle = me.fields[0]; - } - - me.axes[0].setTitle(axisTitle); - - if (!me.noTool) { - me.addTool([{ - type: 'minus', - disabled: true, - tooltip: gettext('Undo Zoom'), - handler: function(){ - var undoButton = me.interactions[0].getUndoButton(); - if (undoButton.handler) { - undoButton.handler(); - } - } - },{ - type: 'restore', - tooltip: gettext('Toggle Legend'), - handler: function(){ - if (me.legend) { - me.legend.setVisible(!me.legend.isVisible()); - } - } - }]); - } - - // add a series for each field we get - me.fields.forEach(function(item, index){ - var title = item; - if (me.fieldTitles && me.fieldTitles[index]) { - title = me.fieldTitles[index]; - } - me.addSeries(Ext.apply( - { - type: 'line', - xField: 'time', - yField: item, - title: title, - fill: true, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - }, - tooltip: { - trackMouse: true, - renderer: 'onSeriesTooltipRender' - } - }, - me.seriesConfig - )); - }); - - // enable animation after the store is loaded - me.store.onAfter('load', function() { - me.setAnimation(true); - }, this, {single: true}); - } -}); -Ext.define('Proxmox.panel.GaugeWidget', { - extend: 'Ext.panel.Panel', - alias: 'widget.proxmoxGauge', - - defaults: { - style: { - 'text-align':'center' - } - }, - items: [ - { - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}

' - }, - { - xtype: 'polar', - height: 120, - border: false, - itemId: 'chart', - series: [{ - type: 'gauge', - value: 0, - colors: ['#f5f5f5'], - sectors: [0], - donut: 90, - needleLength: 100, - totalAngle: Math.PI - }], - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '', - textAlign: 'center', - textBaseline: 'bottom', - x: 125, - y: 110, - fontSize: 30 - }] - }, - { - xtype: 'box', - itemId: 'text' - } - ], - - header: false, - border: false, - - warningThreshold: 0.6, - criticalThreshold: 0.9, - warningColor: '#fc0', - criticalColor: '#FF6C59', - defaultColor: '#7289DA', - backgroundColor: '#2C2F33', - - initialValue: 0, - - - updateValue: function(value, text) { - var me = this; - var color = me.defaultColor; - var attr = {}; - - if (value >= me.criticalThreshold) { - color = me.criticalColor; - } else if (value >= me.warningThreshold) { - color = me.warningColor; - } - - me.chart.series[0].setColors([color, me.backgroundColor]); - me.chart.series[0].setValue(value*100); - - me.valueSprite.setText(' '+(value*100).toFixed(0) + '%'); - attr.x = me.chart.getWidth()/2; - attr.y = me.chart.getHeight()-20; - if (me.spriteFontSize) { - attr.fontSize = me.spriteFontSize; - } - me.valueSprite.setAttributes(attr, true); - - if (text !== undefined) { - me.text.setHtml(text); - } - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.text = me.getComponent('text'); - me.chart = me.getComponent('chart'); - me.valueSprite = me.chart.getSurface('chart').get('valueSprite'); - } -}); -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ -Ext.define('Proxmox.window.Edit', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxWindowEdit', - - // autoLoad trigger a load() after component creation - autoLoad: false, - - resizable: false, - - // use this tio atimatically generate a title like - // Create: - subject: undefined, - - // set isCreate to true if you want a Create button (instead - // OK and RESET) - isCreate: false, - - // set to true if you want an Add button (instead of Create) - isAdd: false, - - // set to true if you want an Remove button (instead of Create) - isRemove: false, - - // custom submitText - submitText: undefined, - - backgroundDelay: 0, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - - // finds the first form field - defaultFocus: 'field[disabled=false][hidden=false]', - - showProgress: false, - - showTaskViewer: false, - - // gets called if we have a progress bar or taskview and it detected that - // the task finished. function(success) - taskDone: Ext.emptyFn, - - // gets called when the api call is finished, right at the beginning - // function(success, response, options) - apiCallDone: Ext.emptyFn, - - // assign a reference from docs, to add a help button docked to the - // bottom of the window. If undefined we magically fall back to the - // onlineHelp of our first item, if set. - onlineHelp: undefined, - - isValid: function() { - var me = this; - - var form = me.formPanel.getForm(); - return form.isValid(); - }, - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.formPanel.getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - setValues: function(values) { - var me = this; - - var form = me.formPanel.getForm(); - - Ext.iterate(values, function(fieldId, val) { - var field = form.findField(fieldId); - if (field && !field.up('inputpanel')) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setValues(values); - }); - }, - - submit: function() { - var me = this; - - var form = me.formPanel.getForm(); - - var values = me.getValues(); - Ext.Object.each(values, function(name, val) { - if (values.hasOwnProperty(name)) { - if (Ext.isArray(val) && !val.length) { - values[name] = ''; - } - } - }); - - if (me.digest) { - values.digest = me.digest; - } - - if (me.backgroundDelay) { - values.background_delay = me.backgroundDelay; - } - - var url = me.url; - if (me.method === 'DELETE') { - url = url + "?" + Ext.Object.toQueryString(values); - values = undefined; - } - - Proxmox.Utils.API2Request({ - url: url, - waitMsgTarget: me, - method: me.method || (me.backgroundDelay ? 'POST' : 'PUT'), - params: values, - failure: function(response, options) { - me.apiCallDone(false, response, options); - - if (response.result && response.result.errors) { - form.markInvalid(response.result.errors); - } - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = (me.backgroundDelay || me.showProgress || me.showTaskViewer) && - response.result.data ? true : false; - - me.apiCallDone(true, response, options); - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - me.hide(); - - var upid = response.result.data; - var viewerClass = me.showTaskViewer ? 'Viewer' : 'Progress'; - var win = Ext.create('Proxmox.window.Task' + viewerClass, { - upid: upid, - taskDone: me.taskDone, - listeners: { - destroy: function () { - me.close(); - } - } - }); - win.show(); - } else { - me.close(); - } - } - }); - }, - - load: function(options) { - var me = this; - - var form = me.formPanel.getForm(); - - options = options || {}; - - var newopts = Ext.apply({ - waitMsgTarget: me - }, options); - - var createWrapper = function(successFn) { - Ext.apply(newopts, { - url: me.url, - method: 'GET', - success: function(response, opts) { - form.clearInvalid(); - me.digest = response.result.data.digest; - if (successFn) { - successFn(response, opts); - } else { - me.setValues(response.result.data); - } - // hack: fix ExtJS bug - Ext.Array.each(me.query('radiofield'), function(f) { - f.resetOriginalValue(); - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() { - me.close(); - }); - } - }); - }; - - createWrapper(options.success); - - Proxmox.Utils.API2Request(newopts); - }, - - initComponent : function() { - var me = this; - - if (!me.url) { - throw "no url specified"; - } - - if (me.create) {throw "deprecated parameter, use isCreate";} - - var items = Ext.isArray(me.items) ? me.items : [ me.items ]; - - me.items = undefined; - - me.formPanel = Ext.create('Ext.form.Panel', { - url: me.url, - method: me.method || 'PUT', - trackResetOnLoad: true, - bodyPadding: 10, - border: false, - defaults: Ext.apply({}, me.defaults, { - border: false - }), - fieldDefaults: Ext.apply({}, me.fieldDefaults, { - labelWidth: 100, - anchor: '100%' - }), - items: items - }); - - var inputPanel = me.formPanel.down('inputpanel'); - - var form = me.formPanel.getForm(); - - var submitText; - if (me.isCreate) { - if (me.submitText) { - submitText = me.submitText; - } else if (me.isAdd) { - submitText = gettext('Add'); - } else if (me.isRemove) { - submitText = gettext('Remove'); - } else { - submitText = gettext('Create'); - } - } else { - submitText = me.submitText || gettext('OK'); - } - - var submitBtn = Ext.create('Ext.Button', { - reference: 'submitbutton', - text: submitText, - disabled: !me.isCreate, - handler: function() { - me.submit(); - } - }); - - var resetBtn = Ext.create('Ext.Button', { - text: 'Reset', - disabled: true, - handler: function(){ - form.reset(); - } - }); - - var set_button_status = function() { - var valid = form.isValid(); - var dirty = form.isDirty(); - submitBtn.setDisabled(!valid || !(dirty || me.isCreate)); - resetBtn.setDisabled(!dirty); - - if (inputPanel && inputPanel.hasAdvanced) { - // we want to show the advanced options - // as soon as some of it is not valid - var advancedItems = me.down('#advancedContainer').query('field'); - var valid = true; - advancedItems.forEach(function(field) { - if (!field.isValid()) { - valid = false; - } - }); - - if (!valid) { - inputPanel.setAdvancedVisible(true); - me.down('#advancedcb').setValue(true); - } - } - }; - - form.on('dirtychange', set_button_status); - form.on('validitychange', set_button_status); - - var colwidth = 300; - if (me.fieldDefaults && me.fieldDefaults.labelWidth) { - colwidth += me.fieldDefaults.labelWidth - 100; - } - - var twoColumn = inputPanel && - (inputPanel.column1 || inputPanel.column2); - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, me.isCreate, me.isAdd); - } - - if (me.isCreate) { - me.buttons = [ submitBtn ] ; - } else { - me.buttons = [ submitBtn, resetBtn ]; - } - - if (inputPanel && inputPanel.hasAdvanced) { - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - inputPanel.setAdvancedVisible(advchecked); - me.buttons.unshift( - { - xtype: 'proxmoxcheckbox', - itemId: 'advancedcb', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - stateId: 'proxmox-advanced-cb', - value: advchecked, - listeners: { - change: function(cb, val) { - inputPanel.setAdvancedVisible(val); - sp.set('proxmox-advanced-cb', val); - } - } - } - ); - } - - var onlineHelp = me.onlineHelp; - if (!onlineHelp && inputPanel && inputPanel.onlineHelp) { - onlineHelp = inputPanel.onlineHelp; - } - - if (onlineHelp) { - var helpButton = Ext.create('Proxmox.button.Help'); - me.buttons.unshift(helpButton, '->'); - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', onlineHelp); - } - - Ext.applyIf(me, { - modal: true, - width: twoColumn ? colwidth*2 : colwidth, - border: false, - items: [ me.formPanel ] - }); - - me.callParent(); - - // always mark invalid fields - me.on('afterlayout', function() { - // on touch devices, the isValid function - // triggers a layout, which triggers an isValid - // and so on - // to prevent this we disable the layouting here - // and enable it afterwards - me.suspendLayout = true; - me.isValid(); - me.suspendLayout = false; - }); - - if (me.autoLoad) { - me.load(); - } - } -}); -Ext.define('Proxmox.window.PasswordEdit', { - extend: 'Proxmox.window.Edit', - alias: 'proxmoxWindowPasswordEdit', - - subject: gettext('Password'), - - url: '/api2/extjs/access/password', - - fieldDefaults: { - labelWidth: 120 - }, - - items: [ - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - allowBlank: false, - name: 'password', - listeners: { - change: function(field){ - field.next().validate(); - }, - blur: function(field){ - field.next().validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - allowBlank: false, - vtype: 'password', - initialPassField: 'password', - submitValue: false - }, - { - xtype: 'hiddenfield', - name: 'userid' - } - ], - - initComponent : function() { - var me = this; - - if (!me.userid) { - throw "no userid specified"; - } - - me.callParent(); - me.down('[name=userid]').setValue(me.userid); - } -}); -Ext.define('Proxmox.window.TaskProgress', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskProgress', - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: { - status: { defaultValue: 'unknown' }, - exitstatus: { defaultValue: 'unknown' } - } - }); - - me.on('destroy', statstore.stopUpdate); - - var getObjectValue = function(key, defaultValue) { - var rec = statstore.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }; - - var pbar = Ext.create('Ext.ProgressBar', { text: 'running...' }); - - me.mon(statstore, 'load', function() { - var status = getObjectValue('status'); - if (status === 'stopped') { - var exitstatus = getObjectValue('exitstatus'); - if (exitstatus == 'OK') { - pbar.reset(); - pbar.updateText("Done!"); - Ext.Function.defer(me.close, 1000, me); - } else { - me.close(); - Ext.Msg.alert('Task failed', exitstatus); - } - me.taskDone(exitstatus == 'OK'); - } - }); - - var descr = Proxmox.Utils.format_task_description(task.type, task.id); - - Ext.apply(me, { - title: gettext('Task') + ': ' + descr, - width: 300, - layout: 'auto', - modal: true, - bodyPadding: 5, - items: pbar, - buttons: [ - { - text: gettext('Details'), - handler: function() { - var win = Ext.create('Proxmox.window.TaskViewer', { - taskDone: me.taskDone, - upid: me.upid - }); - win.show(); - me.close(); - } - } - ] - }); - - me.callParent(); - - statstore.startUpdate(); - - pbar.wait(); - } -}); - -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ - -Ext.define('Proxmox.window.TaskViewer', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskViewer', - - extraTitle: '', // string to prepend after the generic task title - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statgrid; - - var rows = { - status: { - header: gettext('Status'), - defaultValue: 'unknown', - renderer: function(value) { - if (value != 'stopped') { - return value; - } - var es = statgrid.getObjectValue('exitstatus'); - if (es) { - return value + ': ' + es; - } - } - }, - exitstatus: { - visible: false - }, - type: { - header: gettext('Task type'), - required: true - }, - user: { - header: gettext('User name'), - required: true - }, - node: { - header: gettext('Node'), - required: true - }, - pid: { - header: gettext('Process ID'), - required: true - }, - starttime: { - header: gettext('Start Time'), - required: true, - renderer: Proxmox.Utils.render_timestamp - }, - upid: { - header: gettext('Unique task ID') - } - }; - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: rows - }); - - me.on('destroy', statstore.stopUpdate); - - var stop_task = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + task.node + "/tasks/" + me.upid, - waitMsgTarget: me, - method: 'DELETE', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var stop_btn1 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - var stop_btn2 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - statgrid = Ext.create('Proxmox.grid.ObjectGrid', { - title: gettext('Status'), - layout: 'fit', - tbar: [ stop_btn1 ], - rstore: statstore, - rows: rows, - border: false - }); - - var logView = Ext.create('Proxmox.panel.LogView', { - title: gettext('Output'), - tbar: [ stop_btn2 ], - border: false, - url: "/api2/extjs/nodes/" + task.node + "/tasks/" + me.upid + "/log" - }); - - me.mon(statstore, 'load', function() { - var status = statgrid.getObjectValue('status'); - - if (status === 'stopped') { - logView.scrollToEnd = false; - logView.requestUpdate(); - statstore.stopUpdate(); - me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK'); - } - - stop_btn1.setDisabled(status !== 'running'); - stop_btn2.setDisabled(status !== 'running'); - }); - - statstore.startUpdate(); - - Ext.apply(me, { - title: "Task viewer: " + task.desc + me.extraTitle, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [{ - xtype: 'tabpanel', - region: 'center', - items: [ logView, statgrid ] - }] - }); - - me.callParent(); - - logView.fireEvent('show', logView); - } -}); - -Ext.define('apt-pkglist', { - extend: 'Ext.data.Model', - fields: [ 'Package', 'Title', 'Description', 'Section', 'Arch', - 'Priority', 'Version', 'OldVersion', 'ChangeLogUrl', 'Origin' ], - idProperty: 'Package' -}); - -Ext.define('Proxmox.node.APT', { - extend: 'Ext.grid.GridPanel', - - xtype: 'proxmoxNodeAPT', - - upgradeBtn: undefined, - - columns: [ - { - header: gettext('Package'), - width: 200, - sortable: true, - dataIndex: 'Package' - }, - { - text: gettext('Version'), - columns: [ - { - header: gettext('current'), - width: 100, - sortable: false, - dataIndex: 'OldVersion' - }, - { - header: gettext('new'), - width: 100, - sortable: false, - dataIndex: 'Version' - } - ] - }, - { - header: gettext('Description'), - sortable: false, - dataIndex: 'Title', - flex: 1 - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'apt-pkglist', - groupField: 'Origin', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/apt/update" - }, - sorters: [ - { - property : 'Package', - direction: 'ASC' - } - ] - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl: '{[ "Origin: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})', - enableGroupingMenu: false - }); - - var rowBodyFeature = Ext.create('Ext.grid.feature.RowBody', { - getAdditionalData: function (data, rowIndex, record, orig) { - var headerCt = this.view.headerCt; - var colspan = headerCt.getColumnCount(); - // Usually you would style the my-body-class in CSS file - return { - rowBody: '
' + - Ext.String.htmlEncode(data.Description) + - '
', - rowBodyColspan: colspan - }; - } - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store, true); - - var apt_command = function(cmd){ - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/apt/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.mon(win, 'close', reload); - } - }); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var update_btn = new Ext.Button({ - text: gettext('Refresh'), - handler: function(){ - Proxmox.Utils.checked_command(function() { apt_command('update'); }); - } - }); - - var show_changelog = function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return; - } - - var view = Ext.createWidget('component', { - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Changelog') + ": " + rec.data.Package, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + me.nodename + "/apt/changelog", - params: { - name: rec.data.Package, - version: rec.data.Version - }, - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - view.update(Ext.htmlEncode(response.result.data)); - } - }); - - }; - - var changelog_btn = new Proxmox.button.Button({ - text: gettext('Changelog'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return false; - } - return true; - }, - handler: function(b, e, rec) { - show_changelog(rec); - } - }); - - if (me.upgradeBtn) { - me.tbar = [ update_btn, me.upgradeBtn, changelog_btn ]; - } else { - me.tbar = [ update_btn, changelog_btn ]; - } - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-update', - selModel: sm, - viewConfig: { - stripeRows: false, - emptyText: '
' + gettext('No updates available.') + '
' - }, - features: [ groupingFeature, rowBodyFeature ], - listeners: { - activate: reload, - itemdblclick: function(v, rec) { - show_changelog(rec); - } - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.NetworkEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeNetworkEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.iftype) { - throw "no network device type specified"; - } - - me.isCreate = !me.iface; - - var iface_vtype; - - if (me.iftype === 'bridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'bond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'eth' && !me.isCreate) { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'vlan' && !me.isCreate) { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'OVSBridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'OVSBond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'OVSIntPort') { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'OVSPort') { - iface_vtype = 'InterfaceName'; - } else { - console.log(me.iftype); - throw "unknown network device type specified"; - } - - me.subject = Proxmox.Utils.render_network_iface_type(me.iftype); - - var column2 = []; - - if (!(me.iftype === 'OVSIntPort' || me.iftype === 'OVSPort' || - me.iftype === 'OVSBond')) { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Autostart'), - name: 'autostart', - uncheckedValue: 0, - checked: me.isCreate ? true : undefined - }); - } - - if (me.iftype === 'bridge') { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('VLAN aware'), - name: 'bridge_vlan_aware', - deleteEmpty: !me.isCreate - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'bridge_ports' - }); - } else if (me.iftype === 'OVSBridge') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'ovs_ports' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'OVSPort' || me.iftype === 'OVSIntPort') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'bond') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'slaves' - }); - - var policySelector = Ext.createWidget('bondPolicySelector', { - fieldLabel: gettext('Hash policy'), - name: 'bond_xmit_hash_policy', - deleteEmpty: !me.isCreate, - disabled: true - }); - - column2.push({ - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - value: me.isCreate ? 'balance-rr' : undefined, - listeners: { - change: function(f, value) { - if (value === 'balance-xor' || - value === '802.3ad') { - policySelector.setDisabled(false); - } else { - policySelector.setDisabled(true); - policySelector.setValue(''); - } - } - }, - allowBlank: false - }); - - column2.push(policySelector); - - } else if (me.iftype === 'OVSBond') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } - - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Comment'), - allowBlank: true, - nodename: me.nodename, - name: 'comments' - }); - - var url; - var method; - - if (me.isCreate) { - url = "/api2/extjs/nodes/" + me.nodename + "/network"; - method = 'POST'; - } else { - url = "/api2/extjs/nodes/" + me.nodename + "/network/" + me.iface; - method = 'PUT'; - } - - var column1 = [ - { - xtype: 'hiddenfield', - name: 'type', - value: me.iftype - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'iface', - value: me.iface, - vtype: iface_vtype, - allowBlank: false - } - ]; - - if (me.iftype === 'OVSBond') { - column1.push( - { - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - openvswitch: true, - value: me.isCreate ? 'active-backup' : undefined, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'ovs_bonds' - } - ); - } else { - - column1.push( - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('IP address'), - vtype: 'IPAddress', - name: 'address' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Subnet mask'), - vtype: 'IPAddress', - name: 'netmask', - validator: function(value) { - /*jslint confusion: true */ - if (!me.items) { - return true; - } - var address = me.down('field[name=address]').getValue(); - if (value !== '') { - if (address === '') { - return "Subnet mask requires option 'IP address'"; - } - } else { - if (address !== '') { - return "Option 'IP address' requires a subnet mask"; - } - } - - return true; - } - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway'), - vtype: 'IPAddress', - name: 'gateway' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('IPv6 address'), - vtype: 'IP6Address', - name: 'address6' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Prefix length'), - vtype: 'IP6PrefixLength', - name: 'netmask6', - value: '', - allowBlank: true, - validator: function(value) { - /*jslint confusion: true */ - if (!me.items) { - return true; - } - var address = me.down('field[name=address6]').getValue(); - if (value !== '') { - if (address === '') { - return "IPv6 prefix length requires option 'IPv6 address'"; - } - } else { - if (address !== '') { - return "Option 'IPv6 address' requires an IPv6 prefix length"; - } - } - - return true; - } - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway'), - vtype: 'IP6Address', - name: 'gateway6' - } - ); - } - - Ext.applyIf(me, { - url: url, - method: method, - items: { - xtype: 'inputpanel', - column1: column1, - column2: column2 - } - }); - - me.callParent(); - - if (me.isCreate) { - me.down('field[name=iface]').setValue(me.iface_default); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.type !== me.iftype) { - var msg = "Got unexpected device type"; - Ext.Msg.alert(gettext('Error'), msg, function() { - me.close(); - }); - return; - } - me.setValues(data); - me.isValid(); // trigger validation - } - }); - } - } -}); -Ext.define('proxmox-networks', { - extend: 'Ext.data.Model', - fields: [ - 'iface', 'type', 'active', 'autostart', - 'bridge_ports', 'slaves', - 'address', 'netmask', 'gateway', - 'address6', 'netmask6', 'gateway6', - 'comments' - ], - idProperty: 'iface' -}); - -Ext.define('Proxmox.node.NetworkView', { - extend: 'Ext.panel.Panel', - - alias: ['widget.proxmoxNodeNetworkView'], - - // defines what types of network devices we want to create - // order is always the same - types: ['bridge', 'bond', 'ovs'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseUrl = '/nodes/' + me.nodename + '/network'; - - var store = Ext.create('Ext.data.Store', { - model: 'proxmox-networks', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseUrl - }, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }); - - var reload = function() { - var changeitem = me.down('#changes'); - Proxmox.Utils.API2Request({ - url: baseUrl, - failure: function(response, opts) { - store.loadData({}); - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - changeitem.update(''); - changeitem.setHidden(true); - }, - success: function(response, opts) { - var result = Ext.decode(response.responseText); - store.loadData(result.data); - var changes = result.changes; - if (changes === undefined || changes === '') { - changes = gettext("No changes"); - changeitem.setHidden(true); - } else { - changeitem.update("
" + Ext.htmlEncode(changes) + "
"); - changeitem.setHidden(false); - } - } - }); - }; - - var run_editor = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iface: rec.data.iface, - iftype: rec.data.type - }); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: run_editor - }); - - var del_btn = new Ext.Button({ - text: gettext('Remove'), - disabled: true, - handler: function(){ - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var iface = rec.data.iface; - - Proxmox.Utils.API2Request({ - url: baseUrl + '/' + iface, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var set_button_status = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - - edit_btn.setDisabled(!rec); - del_btn.setDisabled(!rec); - }; - - var render_ports = function(value, metaData, record) { - if (value === 'bridge') { - return record.data.bridge_ports; - } else if (value === 'bond') { - return record.data.slaves; - } else if (value === 'OVSBridge') { - return record.data.ovs_ports; - } else if (value === 'OVSBond') { - return record.data.ovs_bonds; - } - }; - - var find_next_iface_id = function(prefix) { - var next; - for (next = 0; next <= 9999; next++) { - if (!store.getById(prefix + next.toString())) { - break; - } - } - return prefix + next.toString(); - }; - - var menu_items = []; - - if (me.types.indexOf('bridge') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bridge', - iface_default: find_next_iface_id('vmbr') - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('bond') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bond', - iface_default: find_next_iface_id('bond') - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('ovs') !== -1) { - if (menu_items.length > 0) { - menu_items.push({ xtype: 'menuseparator' }); - } - - menu_items.push( - { - text: Proxmox.Utils.render_network_iface_type('OVSBridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBridge', - iface_default: find_next_iface_id('vmbr') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSBond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBond', - iface_default: find_next_iface_id('bond') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSIntPort'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSIntPort' - }); - win.on('destroy', reload); - win.show(); - } - } - ); - } - - Ext.apply(me, { - layout: 'border', - tbar: [ - { - text: gettext('Create'), - menu: { - plain: true, - items: menu_items - } - }, ' ', - { - text: gettext('Revert'), - handler: function() { - Proxmox.Utils.API2Request({ - url: baseUrl, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - edit_btn, - del_btn - ], - items: [ - { - xtype: 'gridpanel', - stateful: true, - stateId: 'grid-node-network', - store: store, - region: 'center', - border: false, - columns: [ - { - header: gettext('Name'), - sortable: true, - dataIndex: 'iface' - }, - { - header: gettext('Type'), - sortable: true, - width: 120, - renderer: Proxmox.Utils.render_network_iface_type, - dataIndex: 'type' - }, - { - xtype: 'booleancolumn', - header: gettext('Active'), - width: 80, - sortable: true, - dataIndex: 'active', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText, - }, - { - xtype: 'booleancolumn', - header: gettext('Autostart'), - width: 80, - sortable: true, - dataIndex: 'autostart', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - xtype: 'booleancolumn', - header: gettext('VLAN aware'), - width: 80, - sortable: true, - dataIndex: 'bridge_vlan_aware', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - header: gettext('Ports/Slaves'), - dataIndex: 'type', - renderer: render_ports - }, - { - header: gettext('IP address'), - sortable: true, - width: 120, - dataIndex: 'address', - renderer: function(value, metaData, rec) { - if (rec.data.address && rec.data.address6) { - return rec.data.address + "
" - + rec.data.address6 + '/' + rec.data.netmask6; - } else if (rec.data.address6) { - return rec.data.address6 + '/' + rec.data.netmask6; - } else { - return rec.data.address; - } - } - }, - { - header: gettext('Subnet mask'), - width: 120, - sortable: true, - dataIndex: 'netmask' - }, - { - header: gettext('Gateway'), - width: 120, - sortable: true, - dataIndex: 'gateway', - renderer: function(value, metaData, rec) { - if (rec.data.gateway && rec.data.gateway6) { - return rec.data.gateway + "
" + rec.data.gateway6; - } else if (rec.data.gateway6) { - return rec.data.gateway6; - } else { - return rec.data.gateway; - } - } - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - flex: 1, - renderer: Ext.String.htmlEncode - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: run_editor - } - }, - { - border: false, - region: 'south', - autoScroll: true, - hidden: true, - itemId: 'changes', - tbar: [ - gettext('Pending changes') + ' (' + - gettext('Please reboot to activate changes') + ')' - ], - split: true, - bodyPadding: 5, - flex: 0.6, - html: gettext("No changes") - } - ], - }); - - me.callParent(); - reload(); - } -}); -Ext.define('Proxmox.node.DNSEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeDNSEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.items = [ - { - xtype: 'textfield', - fieldLabel: gettext('Search domain'), - name: 'search', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 1", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns1' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 2", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns2' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 3", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns3' - } - ]; - - Ext.applyIf(me, { - subject: gettext('DNS'), - url: "/api2/extjs/nodes/" + me.nodename + "/dns", - fieldDefaults: { - labelWidth: 120 - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('Proxmox.node.HostsView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxNodeHostsView', - - reload: function() { - var me = this; - me.store.load(); - }, - - tbar: [ - { - text: gettext('Save'), - disabled: true, - itemId: 'savebtn', - handler: function() { - var me = this.up('panel'); - Proxmox.Utils.API2Request({ - params: { - digest: me.digest, - data: me.down('#hostsfield').getValue() - }, - method: 'POST', - url: '/nodes/' + me.nodename + '/hosts', - waitMsgTarget: me, - success: function(response, opts) { - me.reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - text: gettext('Revert'), - disabled: true, - itemId: 'resetbtn', - handler: function() { - var me = this.up('panel'); - me.down('#hostsfield').reset(); - } - } - ], - - layout: 'fit', - - items: [ - { - xtype: 'textarea', - itemId: 'hostsfield', - fieldStyle: { - 'font-family': 'monospace', - 'white-space': 'pre' - }, - listeners: { - dirtychange: function(ta, dirty) { - var me = this.up('panel'); - me.down('#savebtn').setDisabled(!dirty); - me.down('#resetbtn').setDisabled(!dirty); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.store = Ext.create('Ext.data.Store', { - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/hosts", - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.store); - - me.mon(me.store, 'load', function(store, records, success) { - if (!success || records.length < 1) { - return; - } - me.digest = records[0].data.digest; - var data = records[0].data.data; - me.down('#hostsfield').setValue(data); - me.down('#hostsfield').resetOriginalValue(); - }); - - me.reload(); - } -}); -Ext.define('Proxmox.node.DNSView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeDNSView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var run_editor = function() { - var win = Ext.create('Proxmox.node.DNSEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/dns", - cwidth1: 130, - interval: 1000, - run_editor: run_editor, - rows: { - search: { - header: 'Search domain', - required: true, - renderer: Ext.htmlEncode - }, - dns1: { - header: gettext('DNS server') + " 1", - required: true, - renderer: Ext.htmlEncode - }, - dns2: { - header: gettext('DNS server') + " 2", - renderer: Ext.htmlEncode - }, - dns3: { - header: gettext('DNS server') + " 3", - renderer: Ext.htmlEncode - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); -Ext.define('Proxmox.node.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeTasks'], - stateful: true, - stateId: 'grid-node-tasks', - loadMask: true, - sortableColumns: false, - vmidFilter: 0, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.BufferedStore', { - pageSize: 500, - autoLoad: true, - remoteFilter: true, - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - startParam: 'start', - limitParam: 'limit', - url: "/api2/json/nodes/" + me.nodename + "/tasks" - } - }); - - var userfilter = ''; - var filter_errors = 0; - - var updateProxyParams = function() { - var params = { - errors: filter_errors - }; - if (userfilter) { - params.userfilter = userfilter; - } - if (me.vmidFilter) { - params.vmid = me.vmidFilter; - } - store.proxy.extraParams = params; - }; - - updateProxyParams(); - - var reload_task = Ext.create('Ext.util.DelayedTask',function() { - updateProxyParams(); - store.reload(); - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - var view_btn = new Ext.Button({ - text: gettext('View'), - disabled: true, - handler: run_task_viewer - }); - - Proxmox.Utils.monStoreErrors(me, store, true); - - Ext.apply(me, { - store: store, - viewConfig: { - trackOver: false, - stripeRows: false, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - tbar: [ - view_btn, '->', gettext('User name') +':', ' ', - { - xtype: 'textfield', - width: 200, - value: userfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - userfilter = field.getValue(); - reload_task.delay(500); - } - } - }, ' ', gettext('Only Errors') + ':', ' ', - { - xtype: 'checkbox', - hideLabel: true, - checked: filter_errors, - listeners: { - change: function(field, checked) { - filter_errors = checked ? 1 : 0; - reload_task.delay(10); - } - } - }, ' ' - ], - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 100, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 100, - renderer: function(value, metaData, record) { - return Ext.Date.format(value,"M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return "ERROR: " + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - selectionchange: function(v, selections) { - view_btn.setDisabled(!(selections && selections[0])); - }, - show: function() { reload_task.delay(10); }, - destroy: function() { reload_task.cancel(); } - } - }); - - me.callParent(); - - } -}); -Ext.define('proxmox-services', { - extend: 'Ext.data.Model', - fields: [ 'service', 'name', 'desc', 'state' ], - idProperty: 'service' -}); - -Ext.define('Proxmox.node.ServiceView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeServiceView'], - - startOnlyServices: {}, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 1000, - storeid: 'proxmox-services' + me.nodename, - model: 'proxmox-services', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/services" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - sortAfterUpdate: true, - sorters: [ - { - property : 'name', - direction: 'ASC' - } - ] - }); - - var view_service_log = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - var win = Ext.create('Ext.window.Window', { - title: gettext('Syslog') + ': ' + rec.data.service, - modal: true, - items: { - xtype: 'proxmoxLogView', - width: 800, - height: 400, - url: "/api2/extjs/nodes/" + me.nodename + "/syslog?service=" + - rec.data.service, - log_select_timespan: 1 - } - }); - win.show(); - }; - - var service_cmd = function(cmd) { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/services/" + rec.data.service + "/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.loading = true; - }, - success: function(response, opts) { - rstore.startUpdate(); - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - }; - - var start_btn = new Ext.Button({ - text: gettext('Start'), - disabled: true, - handler: function(){ - service_cmd("start"); - } - }); - - var stop_btn = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: function(){ - service_cmd("stop"); - } - }); - - var restart_btn = new Ext.Button({ - text: gettext('Restart'), - disabled: true, - handler: function(){ - service_cmd("restart"); - } - }); - - var syslog_btn = new Ext.Button({ - text: gettext('Syslog'), - disabled: true, - handler: view_service_log - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - start_btn.disable(); - stop_btn.disable(); - restart_btn.disable(); - syslog_btn.disable(); - return; - } - var service = rec.data.service; - var state = rec.data.state; - - syslog_btn.enable(); - - if (me.startOnlyServices[service]) { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - } - stop_btn.disable(); - } else { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - stop_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - stop_btn.disable(); - } - } - }; - - me.mon(store, 'refresh', set_button_status); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.apply(me, { - store: store, - stateful: false, - tbar: [ start_btn, stop_btn, restart_btn, syslog_btn ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: gettext('Status'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - dataIndex: 'desc', - flex: 2 - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: view_service_log, - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeTimeEdit'], - - subject: gettext('Time zone'), - - width: 400, - - autoLoad: true, - - fieldDefaults: { - labelWidth: 70 - }, - - items: { - xtype: 'combo', - fieldLabel: gettext('Time zone'), - name: 'timezone', - queryMode: 'local', - store: Ext.create('Proxmox.data.TimezoneStore'), - displayField: 'zone', - forceSelection: true, - editable: false, - allowBlank: false - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.url = "/api2/extjs/nodes/" + me.nodename + "/time"; - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeTimeView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var tzoffset = (new Date()).getTimezoneOffset()*60000; - var renderlocaltime = function(value) { - var servertime = new Date((value * 1000) + tzoffset); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }; - - var run_editor = function() { - var win = Ext.create('Proxmox.node.TimeEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/time", - cwidth1: 150, - interval: 1000, - run_editor: run_editor, - rows: { - timezone: { - header: gettext('Time zone'), - required: true - }, - localtime: { - header: gettext('Server time'), - required: true, - renderer: renderlocaltime - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); diff --git a/serverside/jsmod/5.4-3/proxmoxlib.js.original b/serverside/jsmod/5.4-3/proxmoxlib.js.original deleted file mode 100644 index 921391f..0000000 --- a/serverside/jsmod/5.4-3/proxmoxlib.js.original +++ /dev/null @@ -1,6757 +0,0 @@ -// 1.0-25 -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)) { - var console = { - dir: function() {}, - log: function() {} - }; -} - -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', { utilities: { - - // this singleton contains miscellaneous utilities - - yesText: gettext('Yes'), - noText: gettext('No'), - enabledText: gettext('Enabled'), - disabledText: gettext('Disabled'), - 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: { - zh_CN: 'Chinese (Simplified)', - zh_TW: 'Chinese (Traditional)', - ca: 'Catalan', - da: 'Danish', - en: 'English', - eu: 'Euskera (Basque)', - fr: 'French', - de: 'German', - it: 'Italian', - es: 'Spanish', - ja: 'Japanese', - nb: 'Norwegian (Bokmal)', - nn: 'Norwegian (Nynorsk)', - fa: 'Persian (Farsi)', - pl: 'Polish', - pt_BR: 'Portuguese (Brazil)', - ru: 'Russian', - sl: 'Slovenian', - sv: 'Swedish', - tr: 'Turkish' - }, - - render_language: function (value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (English)'; - } - var text = Proxmox.Utils.language_map[value]; - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - language_array: function() { - var 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; - }, - - 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 || 'http://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"); - }, - - format_duration_long: function(ut) { - - var days = Math.floor(ut / 86400); - ut -= days*86400; - var hours = Math.floor(ut / 3600); - ut -= hours*3600; - var mins = Math.floor(ut / 60); - ut -= mins*60; - - var hours_str = '00' + hours.toString(); - hours_str = hours_str.substr(hours_str.length - 2); - var mins_str = "00" + mins.toString(); - mins_str = mins_str.substr(mins_str.length - 2); - var ut_str = "00" + ut.toString(); - ut_str = ut_str.substr(ut_str.length - 2); - - if (days) { - var 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; } - - var tm = new Ext.util.TextMetrics(); - var 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; - } - return (Proxmox.UserName !== '') && Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name); - }, - - authClear: function() { - if (Proxmox.LoggedOut) { - return undefined; - } - 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) { - var el = comp.el; - if (!el) { - return; - } - if (!msg) { - el.unmask(); - } else { - if (msg === true) { - el.mask(gettext("Loading...")); - } else { - el.mask(msg); - } - } - }, - - monStoreErrors: function(me, store, clearMaskBeforeLoad) { - if (clearMaskBeforeLoad) { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - Proxmox.Utils.setErrorMask(me, false); - }); - } else { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - if (!me.loadCount) { - me.loadCount = 0; // make sure it is numeric - Proxmox.Utils.setErrorMask(me, true); - } - }); - } - - // only works with 'proxmox' proxy - me.mon(store.proxy, 'afterload', function(proxy, request, success) { - me.loadCount++; - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - - var msg; - /*jslint nomen: true */ - var operation = request._operation; - var error = operation.getError(); - if (error.statusText) { - msg = error.statusText + ' (' + error.status + ')'; - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - extractRequestError: function(result, verbose) { - var 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) { - - var newopts = Ext.apply({ - waitMsg: gettext('Please wait...') - }, reqOpts); - - if (!newopts.url.match(/^\/api2/)) { - newopts.url = '/api2/extjs' + newopts.url; - } - delete newopts.callback; - - var 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); - } - } - var 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) {} - var 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); - - var 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', - //waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data; - - if (data.status !== 'Active') { - Ext.Msg.show({ - title: gettext('No valid subscription'), - icon: Ext.Msg.WARNING, - msg: 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)) { - Ext.Object.each(data, function(name, val) { - if (values.hasOwnProperty(name)) { - var 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; - } - }); - } - }, - - 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') ], - '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') ], - 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') ], - vzsuspend: [ 'CT', gettext('Suspend') ], - vzresume: [ 'CT', gettext('Resume') ], - hamigrate: [ 'HA', gettext('Migrate') ], - hastart: [ 'HA', gettext('Start') ], - hastop: [ 'HA', gettext('Stop') ], - 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: ['VM/CT', gettext('Backup') ], - 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') ] - }, - - format_task_description: function(type, id) { - var farray = Proxmox.Utils.task_desc_table[type]; - var text; - if (!farray) { - text = type; - if (id) { - type += ' ' + id; - } - return text; - } - var prefix = farray[0]; - text = farray[1]; - if (prefix) { - return prefix + ' ' + id + ' - ' + text; - } - return text; - }, - - format_size: function(size) { - /*jslint confusion: true */ - - var units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; - var num = 0; - - while (size >= 1024 && ((num++)+1) < units.length) { - size = size / 1024; - } - - return size.toFixed((num > 0)?2:0) + " " + units[num] + "B"; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - render_uptime: function(value) { - - var uptime = value; - - if (uptime === undefined) { - return ''; - } - - if (uptime <= 0) { - return '-'; - } - - return Proxmox.Utils.format_duration_long(uptime); - }, - - parse_task_upid: function(upid) { - var task = {}; - - var res = upid.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):([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); - task.starttime = parseInt(res[4], 16); - task.type = res[5]; - task.id = res[6]; - task.user = res[7]; - - task.desc = Proxmox.Utils.format_task_description(task.type, task.id); - - return task; - }, - - render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) { - var servertime = new Date(value * 1000); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }, - - openXtermJsViewer: function(vmtype, vmid, nodename, vmname, cmd) { - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - xtermjs: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - cmd: cmd, - - }); - var 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() { - var me = this; - Ext.apply(me, me.utilities); - - var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])"; - var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")"; - var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})"; - var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")"; - - - me.IP4_match = new RegExp("^(?:" + IPV4_REGEXP + ")$"); - me.IP4_cidr_match = new RegExp("^(?:" + IPV4_REGEXP + ")\/([0-9]{1,2})$"); - - var 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 + ")?::" + ")" + ")" + - ")"; - - me.IP6_match = new RegExp("^(?:" + IPV6_REGEXP + ")$"); - me.IP6_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + ")\/([0-9]{1,3})$"); - me.IP6_bracket_match = new RegExp("^\\[(" + IPV6_REGEXP + ")\\]"); - - me.IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$"); - - var 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+)?$"); - } -}); -// 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] <= 128); - }, - IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "
" + gettext('Valid CIDR Range') + ': 8-128', - 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', - - MacPrefix: function(v) { - return (/^[a-f0-9][02468ace](?::[a-f0-9]{2}){0,2}:?$/i).test(v); - }, - MacPrefixMask: /[a-fA-F0-9:]/, - MacPrefixText: gettext('Example') + ': 02:8f - ' + gettext('only unicast addresses are allowed'), - - 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'", - - 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", - - DnsOrIp: function(v) { - if (!Proxmox.Utils.DnsName_match.test(v) && - !Proxmox.Utils.IP64_match.test(v)) { - return false; - } - - return true; - }, - DnsOrIpText: gettext('Not a valid DNS name or IP address.'), - - 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'), - - password: function(val, field) { - if (field.initialPassField) { - var pwd = field.up('form').down( - '[name=' + field.initialPassField + ']'); - return (val == pwd.getValue()); - } - return true; - }, - - passwordText: gettext('Passwords do not match') -}); - -// Firefox 52+ Touchscreen bug -// see https://www.sencha.com/forum/showthread.php?336762-Examples-don-t-work-in-Firefox-52-touchscreen/page2 -// and https://bugzilla.proxmox.com/show_bug.cgi?id=1223 -Ext.define('EXTJS_23846.Element', { - override: 'Ext.dom.Element' -}, function(Element) { - var supports = Ext.supports, - proto = Element.prototype, - eventMap = proto.eventMap, - additiveEvents = proto.additiveEvents; - - if (Ext.os.is.Desktop && supports.TouchEvents && !supports.PointerEvents) { - eventMap.touchstart = 'mousedown'; - eventMap.touchmove = 'mousemove'; - eventMap.touchend = 'mouseup'; - eventMap.touchcancel = 'mouseup'; - - additiveEvents.mousedown = 'mousedown'; - additiveEvents.mousemove = 'mousemove'; - additiveEvents.mouseup = 'mouseup'; - additiveEvents.touchstart = 'touchstart'; - additiveEvents.touchmove = 'touchmove'; - additiveEvents.touchend = 'touchend'; - additiveEvents.touchcancel = 'touchcancel'; - - additiveEvents.pointerdown = 'mousedown'; - additiveEvents.pointermove = 'mousemove'; - additiveEvents.pointerup = 'mouseup'; - additiveEvents.pointercancel = 'mouseup'; - } -}); - -Ext.define('EXTJS_23846.Gesture', { - override: 'Ext.event.publisher.Gesture' -}, function(Gesture) { - var me = Gesture.instance; - - if (Ext.supports.TouchEvents && !Ext.isWebKit && Ext.os.is.Desktop) { - me.handledDomEvents.push('mousedown', 'mousemove', 'mouseup'); - me.registerEvents(); - } -}); - -// 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 -// 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; - } -}); - -// '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('Proxmox.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 a grid/tree view, restoring the focus moves the view back to -// the previously focused item. Save scroll position before refocusing. -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('Proxmox.Datepicker', { - override: 'Ext.picker.Date', - hideMode: 'visibility' -}); - -// ExtJS 6.0.1 has no setSubmitValue() (although you find it in the docs). -// Note: this.submitValue is a boolean flag, whereas getSubmitValue() returns -// data to be submitted. -Ext.define('Proxmox.form.field.Text', { - override: 'Ext.form.field.Text', - - setSubmitValue: function(v) { - this.submitValue = v; - }, -}); - -// this should be fixed with ExtJS 6.0.2 -// make mousescrolling work in firefox in the containers overflowhandler -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. differs from the default onMouseWheel - // handler by using deltaY instead of wheelDeltaY and no normalizing, - // because it is already - 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 -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); - } - } -}); -Ext.define('Proxmox.Mixin.CBind', { - extend: 'Ext.Mixin', - - mixinConfig: { - before: { - initComponent: 'cloneTemplates' - } - }, - - cloneTemplates: function() { - var me = this; - - if (typeof(me.cbindData) == "function") { - me.cbindData = me.cbindData(me.initialConfig) || {}; - } - - var getConfigValue = function(cname) { - - if (cname in me.initialConfig) { - return me.initialConfig[cname]; - } - if (cname in me.cbindData) { - return me.cbindData[cname]; - } - if (cname in me) { - return me[cname]; - } - throw "unable to get cbind data for '" + cname + "'"; - }; - - var applyCBind = function(obj) { - var cbind = obj.cbind, prop, cdata, cvalue, match, found; - if (!cbind) return; - - for (prop in cbind) { - cdata = cbind[prop]; - - found = false; - if (match = /^\{(!)?([a-z_][a-z0-9_]*)\}$/i.exec(cdata)) { - var cvalue = getConfigValue(match[2]); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else if (match = /^\{(!)?([a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)+)\}$/i.exec(cdata)) { - var keys = match[2].split('.'); - var cvalue = getConfigValue(keys.shift()); - keys.forEach(function(k) { - if (k in cvalue) { - cvalue = cvalue[k]; - } else { - throw "unable to get cbind data for '" + match[2] + "'"; - } - }); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else { - obj[prop] = cdata.replace(/{([a-z_][a-z0-9_]*)\}/ig, function(match, cname) { - var cvalue = getConfigValue(cname); - found = true; - return cvalue; - }); - } - if (!found) { - throw "unable to parse cbind template '" + cdata + "'"; - } - - } - }; - - if (me.cbind) { - applyCBind(me); - } - - var cloneTemplateArray = function(org) { - var copy, i, found, el, elcopy, arrayLength; - - arrayLength = org.length; - found = false; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - found = true; - break; - } - } - - if (!found) return org; // no need to copy - - copy = []; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - elcopy = cloneTemplateObject(el); - if (elcopy.cbind) { - applyCBind(elcopy); - } - copy.push(elcopy); - } else if (el.constructor == Array) { - elcopy = cloneTemplateArray(el); - copy.push(elcopy); - } else { - copy.push(el); - } - } - return copy; - }; - - var cloneTemplateObject = function(org) { - var res = {}, prop, el, copy; - for (prop in org) { - el = org[prop]; - if (el.constructor == Object && el.xtype) { - copy = cloneTemplateObject(el); - if (copy.cbind) { - applyCBind(copy); - } - res[prop] = copy; - } else if (el.constructor == Array) { - copy = cloneTemplateArray(el); - res[prop] = copy; - } else { - res[prop] = el; - } - } - return res; - }; - - var condCloneProperties = function() { - var prop, el, i, tmp; - - for (prop in me) { - el = me[prop]; - if (el === undefined || el === null) continue; - if (typeof(el) === 'object' && el.constructor == Object) { - if (el.xtype && prop != 'config') { - me[prop] = cloneTemplateObject(el); - } - } else if (el.constructor == Array) { - tmp = cloneTemplateArray(el); - me[prop] = tmp; - } - } - }; - - condCloneProperties(); - } -}); -/* A reader to store a single JSON Object (hash) into a storage. - * Also accepts an array containing a single hash. - * - * So it can read: - * - * example1: {data1: "xyz", data2: "abc"} - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * example2: [ {data1: "xyz", data2: "abc"} ] - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * If you set 'readArray', the reader expexts the object as array: - * - * example3: [ { key: "data1", value: "xyz", p2: "cde" }, { key: "data2", value: "abc", p2: "efg" }] - * returns [{key: "data1", value: "xyz", p2: "cde}, {key: "data2", value: "abc", p2: "efg"}] - * - * Note: The records can contain additional properties (like 'p2' above) when you use 'readArray' - * - * Additional feature: specify allowed properties with default values with 'rows' object - * - * var rows = { - * memory: { - * required: true, - * defaultValue: 512 - * } - * } - * - */ - -Ext.define('Proxmox.data.reader.JsonObject', { - extend: 'Ext.data.reader.Json', - alias : 'reader.jsonobject', - - readArray: false, - - rows: undefined, - - constructor: function(config) { - var me = this; - - Ext.apply(me, config || {}); - - me.callParent([config]); - }, - - getResponseData: function(response) { - var me = this; - - var data = []; - try { - var result = Ext.decode(response.responseText); - // get our data items inside the server response - var root = result[me.getRootProperty()]; - - if (me.readArray) { - - var rec_hash = {}; - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - rec_hash[rec.key] = rec; - } - }); - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - var rec = rec_hash[key]; - if (Ext.isDefined(rec)) { - if (!Ext.isDefined(rec.value)) { - rec.value = rowdef.defaultValue; - } - data.push(rec); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue} ); - } else if (rowdef.required) { - data.push({key: key, value: undefined }); - } - }); - } else { - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - data.push(rec); - } - }); - } - - } else { - - var org_root = root; - - if (Ext.isArray(org_root)) { - if (root.length == 1) { - root = org_root[0]; - } else { - root = {}; - } - } - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - if (Ext.isDefined(root[key])) { - data.push({key: key, value: root[key]}); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue}); - } else if (rowdef.required) { - data.push({key: key, value: undefined}); - } - }); - } else { - Ext.Object.each(root, function(key, value) { - data.push({key: key, value: value }); - }); - } - } - } - catch (ex) { - Ext.Error.raise({ - response: response, - json: response.responseText, - parseError: ex, - msg: 'Unable to parse the JSON returned by the server: ' + ex.toString() - }); - } - - return data; - } -}); - -Ext.define('Proxmox.RestProxy', { - extend: 'Ext.data.RestProxy', - alias : 'proxy.proxmox', - - pageParam : null, - startParam: null, - limitParam: null, - groupParam: null, - sortParam: null, - filterParam: null, - noCache : false, - - afterRequest: function(request, success) { - this.fireEvent('afterload', this, request, success); - return; - }, - - constructor: function(config) { - - Ext.applyIf(config, { - reader: { - type: 'json', - rootProperty: config.root || 'data' - } - }); - - this.callParent([config]); - } -}, function() { - - Ext.define('KeyValue', { - extend: "Ext.data.Model", - fields: [ 'key', 'value' ], - idProperty: 'key' - }); - - Ext.define('KeyValuePendingDelete', { - extend: "Ext.data.Model", - fields: [ 'key', 'value', 'pending', 'delete' ], - idProperty: 'key' - }); - - Ext.define('proxmox-tasks', { - extend: 'Ext.data.Model', - fields: [ - { name: 'starttime', type : 'date', dateFormat: 'timestamp' }, - { name: 'endtime', type : 'date', dateFormat: 'timestamp' }, - { name: 'pid', type: 'int' }, - 'node', 'upid', 'user', 'status', 'type', 'id' - ], - idProperty: 'upid' - }); - - Ext.define('proxmox-cluster-log', { - extend: 'Ext.data.Model', - fields: [ - { name: 'uid' , type: 'int' }, - { name: 'time', type : 'date', dateFormat: 'timestamp' }, - { name: 'pri', type: 'int' }, - { name: 'pid', type: 'int' }, - 'node', 'user', 'tag', 'msg', - { - name: 'id', - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - // compute unique ID - return info.uid + ':' + info.node; - } - } - ], - idProperty: 'id' - }); - -}); -/* Extends the Ext.data.Store type - * with startUpdate() and stopUpdate() methods - * to refresh the store data in the background - * Components using this store directly will flicker - * due to the redisplay of the element ater 'config.interval' ms - * - * Note that you have to call yourself startUpdate() for the background load - * to begin - */ -Ext.define('Proxmox.data.UpdateStore', { - extend: 'Ext.data.Store', - alias: 'store.update', - - isStopped: true, - - autoStart: false, - - destroy: function() { - var me = this; - me.stopUpdate(); - me.callParent(); - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.interval) { - config.interval = 3000; - } - - if (!config.storeid) { - throw "no storeid specified"; - } - - var load_task = new Ext.util.DelayedTask(); - - var run_load_task = function() { - if (me.isStopped) { - return; - } - - if (Proxmox.Utils.authOK()) { - var start = new Date(); - me.load(function() { - var runtime = (new Date()) - start; - var interval = config.interval + runtime*2; - load_task.delay(interval, run_load_task); - }); - } else { - load_task.delay(200, run_load_task); - } - }; - - Ext.apply(config, { - startUpdate: function() { - me.isStopped = false; - // run_load_task(); this makes problems with chrome - load_task.delay(1, run_load_task); - }, - stopUpdate: function() { - me.isStopped = true; - load_task.cancel(); - } - }); - - me.callParent([config]); - - me.load_task = load_task; - - if (me.autoStart) { - me.startUpdate(); - } - } -}); -/* - * The DiffStore is a in-memory store acting as proxy between a real store - * instance and a component. - * Its purpose is to redisplay the component *only* if the data has been changed - * inside the real store, to avoid the annoying visual flickering of using - * the real store directly. - * - * Implementation: - * The DiffStore monitors via mon() the 'load' events sent by the real store. - * On each 'load' event, the DiffStore compares its own content with the target - * store (call to cond_add_item()) and then fires a 'refresh' event. - * The 'refresh' event will automatically trigger a view refresh on the component - * who binds to this store. - */ - -/* Config properties: - * rstore: the realstore which will autorefresh its content from the API - * Only works if rstore has a model and use 'idProperty' - * sortAfterUpdate: sort the diffstore before rendering the view - */ -Ext.define('Proxmox.data.DiffStore', { - extend: 'Ext.data.Store', - alias: 'store.diff', - - sortAfterUpdate: false, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.rstore) { - throw "no rstore specified"; - } - - if (!config.rstore.model) { - throw "no rstore model specified"; - } - - var rstore = config.rstore; - - Ext.apply(config, { - model: rstore.model, - proxy: { type: 'memory' } - }); - - me.callParent([config]); - - var first_load = true; - - var cond_add_item = function(data, id) { - var olditem = me.getById(id); - if (olditem) { - olditem.beginEdit(); - Ext.Array.each(me.model.prototype.fields, function(field) { - if (olditem.data[field.name] !== data[field.name]) { - olditem.set(field.name, data[field.name]); - } - }); - olditem.endEdit(true); - olditem.commit(); - } else { - var newrec = Ext.create(me.model, data); - var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length; - me.insert(pos, newrec); - } - }; - - var loadFn = function(s, records, success) { - - if (!success) { - return; - } - - me.suspendEvents(); - - // getSource returns null if data is not filtered - // if it is filtered it returns all records - var allItems = me.getData().getSource() || me.getData(); - - // remove vanished items - allItems.each(function(olditem) { - var item = rstore.getById(olditem.getId()); - if (!item) { - me.remove(olditem); - } - }); - - rstore.each(function(item) { - cond_add_item(item.data, item.getId()); - }); - - me.filter(); - - if (me.sortAfterUpdate) { - me.sort(); - } - - first_load = false; - - me.resumeEvents(); - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - }; - - if (rstore.isLoaded()) { - // if store is already loaded, - // insert items instantly - loadFn(rstore, [], true); - } - - me.mon(rstore, 'load', loadFn); - } -}); -/* This store encapsulates data items which are organized as an Array of key-values Objects - * ie data[0] contains something like {key: "keyboard", value: "da"} -* -* Designed to work with the KeyValue model and the JsonObject data reader -*/ -Ext.define('Proxmox.data.ObjectStore', { - extend: 'Proxmox.data.UpdateStore', - - getRecord: function() { - var me = this; - var record = Ext.create('Ext.data.Model'); - me.getData().each(function(item) { - record.set(item.data.key, item.data.value); - }); - record.commit(true); - return record; - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.storeid) { - config.storeid = 'proxmox-store-' + (++Ext.idSeed); - } - - Ext.applyIf(config, { - model: 'KeyValue', - proxy: { - type: 'proxmox', - url: config.url, - extraParams: config.extraParams, - reader: { - type: 'jsonobject', - rows: config.rows, - readArray: config.readArray, - rootProperty: config.root || 'data' - } - } - }); - - me.callParent([config]); - } -}); -/* Extends the Proxmox.data.UpdateStore type - * - * - */ -Ext.define('Proxmox.data.RRDStore', { - extend: 'Proxmox.data.UpdateStore', - alias: 'store.proxmoxRRDStore', - - setRRDUrl: function(timeframe, cf) { - var me = this; - if (!timeframe) { - timeframe = me.timeframe; - } - - if (!cf) { - cf = me.cf; - } - - me.proxy.url = me.rrdurl + "?timeframe=" + timeframe + "&cf=" + cf; - }, - - proxy: { - type: 'proxmox' - }, - - timeframe: 'hour', - - cf: 'AVERAGE', - - constructor: function(config) { - var me = this; - - config = config || {}; - - // set default interval to 30seconds - if (!config.interval) { - config.interval = 30000; - } - - // set a new storeid - if (!config.storeid) { - config.storeid = 'rrdstore-' + (++Ext.idSeed); - } - - // rrdurl is required - if (!config.rrdurl) { - throw "no rrdurl specified"; - } - - var stateid = 'proxmoxRRDTypeSelection'; - var sp = Ext.state.Manager.getProvider(); - var stateinit = sp.get(stateid); - - if (stateinit) { - if(stateinit.timeframe !== me.timeframe || stateinit.cf !== me.rrdcffn){ - me.timeframe = stateinit.timeframe; - me.rrdcffn = stateinit.cf; - } - } - - me.callParent([config]); - - me.setRRDUrl(); - me.mon(sp, 'statechange', function(prov, key, state){ - if (key === stateid) { - if (state && state.id) { - if (state.timeframe !== me.timeframe || state.cf !== me.cf) { - me.timeframe = state.timeframe; - me.cf = state.cf; - me.setRRDUrl(); - me.reload(); - } - } - } - }); - } -}); -Ext.define('Timezone', { - extend: 'Ext.data.Model', - fields: ['zone'] -}); - -Ext.define('Proxmox.data.TimezoneStore', { - extend: 'Ext.data.Store', - model: 'Timezone', - data: [ - ['Africa/Abidjan'], - ['Africa/Accra'], - ['Africa/Addis_Ababa'], - ['Africa/Algiers'], - ['Africa/Asmara'], - ['Africa/Bamako'], - ['Africa/Bangui'], - ['Africa/Banjul'], - ['Africa/Bissau'], - ['Africa/Blantyre'], - ['Africa/Brazzaville'], - ['Africa/Bujumbura'], - ['Africa/Cairo'], - ['Africa/Casablanca'], - ['Africa/Ceuta'], - ['Africa/Conakry'], - ['Africa/Dakar'], - ['Africa/Dar_es_Salaam'], - ['Africa/Djibouti'], - ['Africa/Douala'], - ['Africa/El_Aaiun'], - ['Africa/Freetown'], - ['Africa/Gaborone'], - ['Africa/Harare'], - ['Africa/Johannesburg'], - ['Africa/Kampala'], - ['Africa/Khartoum'], - ['Africa/Kigali'], - ['Africa/Kinshasa'], - ['Africa/Lagos'], - ['Africa/Libreville'], - ['Africa/Lome'], - ['Africa/Luanda'], - ['Africa/Lubumbashi'], - ['Africa/Lusaka'], - ['Africa/Malabo'], - ['Africa/Maputo'], - ['Africa/Maseru'], - ['Africa/Mbabane'], - ['Africa/Mogadishu'], - ['Africa/Monrovia'], - ['Africa/Nairobi'], - ['Africa/Ndjamena'], - ['Africa/Niamey'], - ['Africa/Nouakchott'], - ['Africa/Ouagadougou'], - ['Africa/Porto-Novo'], - ['Africa/Sao_Tome'], - ['Africa/Tripoli'], - ['Africa/Tunis'], - ['Africa/Windhoek'], - ['America/Adak'], - ['America/Anchorage'], - ['America/Anguilla'], - ['America/Antigua'], - ['America/Araguaina'], - ['America/Argentina/Buenos_Aires'], - ['America/Argentina/Catamarca'], - ['America/Argentina/Cordoba'], - ['America/Argentina/Jujuy'], - ['America/Argentina/La_Rioja'], - ['America/Argentina/Mendoza'], - ['America/Argentina/Rio_Gallegos'], - ['America/Argentina/Salta'], - ['America/Argentina/San_Juan'], - ['America/Argentina/San_Luis'], - ['America/Argentina/Tucuman'], - ['America/Argentina/Ushuaia'], - ['America/Aruba'], - ['America/Asuncion'], - ['America/Atikokan'], - ['America/Bahia'], - ['America/Bahia_Banderas'], - ['America/Barbados'], - ['America/Belem'], - ['America/Belize'], - ['America/Blanc-Sablon'], - ['America/Boa_Vista'], - ['America/Bogota'], - ['America/Boise'], - ['America/Cambridge_Bay'], - ['America/Campo_Grande'], - ['America/Cancun'], - ['America/Caracas'], - ['America/Cayenne'], - ['America/Cayman'], - ['America/Chicago'], - ['America/Chihuahua'], - ['America/Costa_Rica'], - ['America/Cuiaba'], - ['America/Curacao'], - ['America/Danmarkshavn'], - ['America/Dawson'], - ['America/Dawson_Creek'], - ['America/Denver'], - ['America/Detroit'], - ['America/Dominica'], - ['America/Edmonton'], - ['America/Eirunepe'], - ['America/El_Salvador'], - ['America/Fortaleza'], - ['America/Glace_Bay'], - ['America/Godthab'], - ['America/Goose_Bay'], - ['America/Grand_Turk'], - ['America/Grenada'], - ['America/Guadeloupe'], - ['America/Guatemala'], - ['America/Guayaquil'], - ['America/Guyana'], - ['America/Halifax'], - ['America/Havana'], - ['America/Hermosillo'], - ['America/Indiana/Indianapolis'], - ['America/Indiana/Knox'], - ['America/Indiana/Marengo'], - ['America/Indiana/Petersburg'], - ['America/Indiana/Tell_City'], - ['America/Indiana/Vevay'], - ['America/Indiana/Vincennes'], - ['America/Indiana/Winamac'], - ['America/Inuvik'], - ['America/Iqaluit'], - ['America/Jamaica'], - ['America/Juneau'], - ['America/Kentucky/Louisville'], - ['America/Kentucky/Monticello'], - ['America/La_Paz'], - ['America/Lima'], - ['America/Los_Angeles'], - ['America/Maceio'], - ['America/Managua'], - ['America/Manaus'], - ['America/Marigot'], - ['America/Martinique'], - ['America/Matamoros'], - ['America/Mazatlan'], - ['America/Menominee'], - ['America/Merida'], - ['America/Mexico_City'], - ['America/Miquelon'], - ['America/Moncton'], - ['America/Monterrey'], - ['America/Montevideo'], - ['America/Montreal'], - ['America/Montserrat'], - ['America/Nassau'], - ['America/New_York'], - ['America/Nipigon'], - ['America/Nome'], - ['America/Noronha'], - ['America/North_Dakota/Center'], - ['America/North_Dakota/New_Salem'], - ['America/Ojinaga'], - ['America/Panama'], - ['America/Pangnirtung'], - ['America/Paramaribo'], - ['America/Phoenix'], - ['America/Port-au-Prince'], - ['America/Port_of_Spain'], - ['America/Porto_Velho'], - ['America/Puerto_Rico'], - ['America/Rainy_River'], - ['America/Rankin_Inlet'], - ['America/Recife'], - ['America/Regina'], - ['America/Resolute'], - ['America/Rio_Branco'], - ['America/Santa_Isabel'], - ['America/Santarem'], - ['America/Santiago'], - ['America/Santo_Domingo'], - ['America/Sao_Paulo'], - ['America/Scoresbysund'], - ['America/Shiprock'], - ['America/St_Barthelemy'], - ['America/St_Johns'], - ['America/St_Kitts'], - ['America/St_Lucia'], - ['America/St_Thomas'], - ['America/St_Vincent'], - ['America/Swift_Current'], - ['America/Tegucigalpa'], - ['America/Thule'], - ['America/Thunder_Bay'], - ['America/Tijuana'], - ['America/Toronto'], - ['America/Tortola'], - ['America/Vancouver'], - ['America/Whitehorse'], - ['America/Winnipeg'], - ['America/Yakutat'], - ['America/Yellowknife'], - ['Antarctica/Casey'], - ['Antarctica/Davis'], - ['Antarctica/DumontDUrville'], - ['Antarctica/Macquarie'], - ['Antarctica/Mawson'], - ['Antarctica/McMurdo'], - ['Antarctica/Palmer'], - ['Antarctica/Rothera'], - ['Antarctica/South_Pole'], - ['Antarctica/Syowa'], - ['Antarctica/Vostok'], - ['Arctic/Longyearbyen'], - ['Asia/Aden'], - ['Asia/Almaty'], - ['Asia/Amman'], - ['Asia/Anadyr'], - ['Asia/Aqtau'], - ['Asia/Aqtobe'], - ['Asia/Ashgabat'], - ['Asia/Baghdad'], - ['Asia/Bahrain'], - ['Asia/Baku'], - ['Asia/Bangkok'], - ['Asia/Beirut'], - ['Asia/Bishkek'], - ['Asia/Brunei'], - ['Asia/Choibalsan'], - ['Asia/Chongqing'], - ['Asia/Colombo'], - ['Asia/Damascus'], - ['Asia/Dhaka'], - ['Asia/Dili'], - ['Asia/Dubai'], - ['Asia/Dushanbe'], - ['Asia/Gaza'], - ['Asia/Harbin'], - ['Asia/Ho_Chi_Minh'], - ['Asia/Hong_Kong'], - ['Asia/Hovd'], - ['Asia/Irkutsk'], - ['Asia/Jakarta'], - ['Asia/Jayapura'], - ['Asia/Jerusalem'], - ['Asia/Kabul'], - ['Asia/Kamchatka'], - ['Asia/Karachi'], - ['Asia/Kashgar'], - ['Asia/Kathmandu'], - ['Asia/Kolkata'], - ['Asia/Krasnoyarsk'], - ['Asia/Kuala_Lumpur'], - ['Asia/Kuching'], - ['Asia/Kuwait'], - ['Asia/Macau'], - ['Asia/Magadan'], - ['Asia/Makassar'], - ['Asia/Manila'], - ['Asia/Muscat'], - ['Asia/Nicosia'], - ['Asia/Novokuznetsk'], - ['Asia/Novosibirsk'], - ['Asia/Omsk'], - ['Asia/Oral'], - ['Asia/Phnom_Penh'], - ['Asia/Pontianak'], - ['Asia/Pyongyang'], - ['Asia/Qatar'], - ['Asia/Qyzylorda'], - ['Asia/Rangoon'], - ['Asia/Riyadh'], - ['Asia/Sakhalin'], - ['Asia/Samarkand'], - ['Asia/Seoul'], - ['Asia/Shanghai'], - ['Asia/Singapore'], - ['Asia/Taipei'], - ['Asia/Tashkent'], - ['Asia/Tbilisi'], - ['Asia/Tehran'], - ['Asia/Thimphu'], - ['Asia/Tokyo'], - ['Asia/Ulaanbaatar'], - ['Asia/Urumqi'], - ['Asia/Vientiane'], - ['Asia/Vladivostok'], - ['Asia/Yakutsk'], - ['Asia/Yekaterinburg'], - ['Asia/Yerevan'], - ['Atlantic/Azores'], - ['Atlantic/Bermuda'], - ['Atlantic/Canary'], - ['Atlantic/Cape_Verde'], - ['Atlantic/Faroe'], - ['Atlantic/Madeira'], - ['Atlantic/Reykjavik'], - ['Atlantic/South_Georgia'], - ['Atlantic/St_Helena'], - ['Atlantic/Stanley'], - ['Australia/Adelaide'], - ['Australia/Brisbane'], - ['Australia/Broken_Hill'], - ['Australia/Currie'], - ['Australia/Darwin'], - ['Australia/Eucla'], - ['Australia/Hobart'], - ['Australia/Lindeman'], - ['Australia/Lord_Howe'], - ['Australia/Melbourne'], - ['Australia/Perth'], - ['Australia/Sydney'], - ['Europe/Amsterdam'], - ['Europe/Andorra'], - ['Europe/Athens'], - ['Europe/Belgrade'], - ['Europe/Berlin'], - ['Europe/Bratislava'], - ['Europe/Brussels'], - ['Europe/Bucharest'], - ['Europe/Budapest'], - ['Europe/Chisinau'], - ['Europe/Copenhagen'], - ['Europe/Dublin'], - ['Europe/Gibraltar'], - ['Europe/Guernsey'], - ['Europe/Helsinki'], - ['Europe/Isle_of_Man'], - ['Europe/Istanbul'], - ['Europe/Jersey'], - ['Europe/Kaliningrad'], - ['Europe/Kiev'], - ['Europe/Lisbon'], - ['Europe/Ljubljana'], - ['Europe/London'], - ['Europe/Luxembourg'], - ['Europe/Madrid'], - ['Europe/Malta'], - ['Europe/Mariehamn'], - ['Europe/Minsk'], - ['Europe/Monaco'], - ['Europe/Moscow'], - ['Europe/Oslo'], - ['Europe/Paris'], - ['Europe/Podgorica'], - ['Europe/Prague'], - ['Europe/Riga'], - ['Europe/Rome'], - ['Europe/Samara'], - ['Europe/San_Marino'], - ['Europe/Sarajevo'], - ['Europe/Simferopol'], - ['Europe/Skopje'], - ['Europe/Sofia'], - ['Europe/Stockholm'], - ['Europe/Tallinn'], - ['Europe/Tirane'], - ['Europe/Uzhgorod'], - ['Europe/Vaduz'], - ['Europe/Vatican'], - ['Europe/Vienna'], - ['Europe/Vilnius'], - ['Europe/Volgograd'], - ['Europe/Warsaw'], - ['Europe/Zagreb'], - ['Europe/Zaporozhye'], - ['Europe/Zurich'], - ['Indian/Antananarivo'], - ['Indian/Chagos'], - ['Indian/Christmas'], - ['Indian/Cocos'], - ['Indian/Comoro'], - ['Indian/Kerguelen'], - ['Indian/Mahe'], - ['Indian/Maldives'], - ['Indian/Mauritius'], - ['Indian/Mayotte'], - ['Indian/Reunion'], - ['Pacific/Apia'], - ['Pacific/Auckland'], - ['Pacific/Chatham'], - ['Pacific/Chuuk'], - ['Pacific/Easter'], - ['Pacific/Efate'], - ['Pacific/Enderbury'], - ['Pacific/Fakaofo'], - ['Pacific/Fiji'], - ['Pacific/Funafuti'], - ['Pacific/Galapagos'], - ['Pacific/Gambier'], - ['Pacific/Guadalcanal'], - ['Pacific/Guam'], - ['Pacific/Honolulu'], - ['Pacific/Johnston'], - ['Pacific/Kiritimati'], - ['Pacific/Kosrae'], - ['Pacific/Kwajalein'], - ['Pacific/Majuro'], - ['Pacific/Marquesas'], - ['Pacific/Midway'], - ['Pacific/Nauru'], - ['Pacific/Niue'], - ['Pacific/Norfolk'], - ['Pacific/Noumea'], - ['Pacific/Pago_Pago'], - ['Pacific/Palau'], - ['Pacific/Pitcairn'], - ['Pacific/Pohnpei'], - ['Pacific/Port_Moresby'], - ['Pacific/Rarotonga'], - ['Pacific/Saipan'], - ['Pacific/Tahiti'], - ['Pacific/Tarawa'], - ['Pacific/Tongatapu'], - ['Pacific/Wake'], - ['Pacific/Wallis'] - ] -}); -Ext.define('Proxmox.form.field.Integer',{ - extend: 'Ext.form.field.Number', - alias: 'widget.proxmoxintegerfield', - - config: { - deleteEmpty: false - }, - - allowDecimals: false, - allowExponential: false, - step: 1, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== undefined && val !== null && val !== '') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - } - -}); -Ext.define('Proxmox.form.field.Textfield', { - extend: 'Ext.form.field.Text', - alias: ['widget.proxmoxtextfield'], - - config: { - skipEmptyText: true, - - deleteEmpty: false, - }, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - getSubmitValue: function() { - var me = this; - - var value = this.processRawValue(this.getRawValue()); - if (value !== '') { - return value; - } - - return me.getSkipEmptyText() ? null: value; - }, - - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - } -}); -Ext.define('Proxmox.DateTimeField', { - extend: 'Ext.form.FieldContainer', - xtype: 'promxoxDateTimeField', - - layout: 'hbox', - - referenceHolder: true, - - submitFormat: 'U', - - getValue: function() { - var me = this; - var d = me.lookupReference('dateentry').getValue(); - - if (d === undefined || d === null) { return null; } - - var t = me.lookupReference('timeentry').getValue(); - - if (t === undefined || t === null) { return null; } - - var offset = (t.getHours()*3600+t.getMinutes()*60)*1000; - - return new Date(d.getTime() + offset); - }, - - getSubmitValue: function() { - var me = this; - var format = me.submitFormat; - var value = me.getValue(); - - return value ? Ext.Date.format(value, format) : null; - }, - - items: [ - { - xtype: 'datefield', - editable: false, - reference: 'dateentry', - flex: 1, - format: 'Y-m-d' - }, - { - xtype: 'timefield', - reference: 'timeentry', - format: 'H:i', - width: 80, - value: '00:00', - increment: 60 - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - - var value = me.value || new Date(); - - me.lookupReference('dateentry').setValue(value); - me.lookupReference('timeentry').setValue(value); - - me.relayEvents(me.lookupReference('dateentry'), ['change']); - me.relayEvents(me.lookupReference('timeentry'), ['change']); - } -}); -Ext.define('Proxmox.form.Checkbox', { - extend: 'Ext.form.field.Checkbox', - alias: ['widget.proxmoxcheckbox'], - - config: { - defaultValue: undefined, - deleteDefaultValue: false, - deleteEmpty: false - }, - - inputValue: '1', - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - if ((val == me.getDefaultValue()) && me.getDeleteDefaultValue()) { - data['delete'] = me.getName(); - } else { - data[me.getName()] = val; - } - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - // also accept integer 1 as true - setRawValue: function(value) { - var me = this; - - if (value === 1) { - me.callParent([true]); - } else { - me.callParent([value]); - } - } - -}); -/* Key-Value ComboBox - * - * config properties: - * comboItems: an array of Key - Value pairs - * deleteEmpty: if set to true (default), an empty value received from the - * comboBox will reset the property to its default value - */ -Ext.define('Proxmox.form.KVComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.proxmoxKVComboBox', - - config: { - deleteEmpty: true - }, - - comboItems: undefined, - displayField: 'value', - valueField: 'key', - queryMode: 'local', - - // overide framework function to implement deleteEmpty behaviour - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null && val !== '' && val !== '__default__') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - validator: function(val) { - var me = this; - - if (me.editable || val === null || val === '') { - return true; - } - - if (me.store.getCount() > 0) { - var values = me.multiSelect ? val.split(me.delimiter) : [val]; - var items = me.store.getData().collect('value', 'data'); - if (Ext.Array.every(values, function(value) { - return Ext.Array.contains(items, value); - })) { - return true; - } - } - - // returns a boolean or string - /*jslint confusion: true */ - return "value '" + val + "' not allowed!"; - }, - - initComponent: function() { - var me = this; - - me.store = Ext.create('Ext.data.ArrayStore', { - model: 'KeyValue', - data : me.comboItems - }); - - if (me.initialConfig.editable === undefined) { - me.editable = false; - } - - me.callParent(); - } -}); -Ext.define('Proxmox.form.LanguageSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'proxmoxLanguageSelector', - - comboItems: Proxmox.Utils.language_array() -}); -/* - * ComboGrid component: a ComboBox where the dropdown menu (the - * "Picker") is a Grid with Rows and Columns expects a listConfig - * object with a columns property roughly based on the GridPicker from - * https://www.sencha.com/forum/showthread.php?299909 - * -*/ - -Ext.define('Proxmox.form.ComboGrid', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxComboGrid'], - - // this value is used as default value after load() - preferredValue: undefined, - - // hack: allow to select empty value - // seems extjs does not allow that when 'editable == false' - onKeyUp: function(e, t) { - var me = this; - var key = e.getKey(); - - if (!me.editable && me.allowBlank && !me.multiSelect && - (key == e.BACKSPACE || key == e.DELETE)) { - me.setValue(''); - } - - me.callParent(arguments); - }, - - // needed to trigger onKeyUp etc. - enableKeyEvents: true, - - editable: false, - - // override ExtJS method - // if the field has multiSelect enabled, the store is not loaded, and - // the displayfield == valuefield, it saves the rawvalue as an array - // but the getRawValue method is only defined in the textfield class - // (which has not to deal with arrays) an returns the string in the - // field (not an array) - // - // so if we have multiselect enabled, return the rawValue (which - // should be an array) and else we do callParent so - // it should not impact any other use of the class - getRawValue: function() { - var me = this; - if (me.multiSelect) { - return me.rawValue; - } else { - return me.callParent(); - } - }, - -// override ExtJS protected method - onBindStore: function(store, initial) { - var me = this, - picker = me.picker, - extraKeySpec, - valueCollectionConfig; - - // We're being bound, not unbound... - if (store) { - // If store was created from a 2 dimensional array with generated field names 'field1' and 'field2' - if (store.autoCreated) { - me.queryMode = 'local'; - me.valueField = me.displayField = 'field1'; - if (!store.expanded) { - me.displayField = 'field2'; - } - - // displayTpl config will need regenerating with the autogenerated displayField name 'field1' - me.setDisplayTpl(null); - } - if (!Ext.isDefined(me.valueField)) { - me.valueField = me.displayField; - } - - // Add a byValue index to the store so that we can efficiently look up records by the value field - // when setValue passes string value(s). - // The two indices (Ext.util.CollectionKeys) are configured unique: false, so that if duplicate keys - // are found, they are all returned by the get call. - // This is so that findByText and findByValue are able to return the *FIRST* matching value. By default, - // if unique is true, CollectionKey keeps the *last* matching value. - extraKeySpec = { - byValue: { - rootProperty: 'data', - unique: false - } - }; - extraKeySpec.byValue.property = me.valueField; - store.setExtraKeys(extraKeySpec); - - if (me.displayField === me.valueField) { - store.byText = store.byValue; - } else { - extraKeySpec.byText = { - rootProperty: 'data', - unique: false - }; - extraKeySpec.byText.property = me.displayField; - store.setExtraKeys(extraKeySpec); - } - - // We hold a collection of the values which have been selected, keyed by this field's valueField. - // This collection also functions as the selected items collection for the BoundList's selection model - valueCollectionConfig = { - rootProperty: 'data', - extraKeys: { - byInternalId: { - property: 'internalId' - }, - byValue: { - property: me.valueField, - rootProperty: 'data' - } - }, - // Whenever this collection is changed by anyone, whether by this field adding to it, - // or the BoundList operating, we must refresh our value. - listeners: { - beginupdate: me.onValueCollectionBeginUpdate, - endupdate: me.onValueCollectionEndUpdate, - scope: me - } - }; - - // This becomes our collection of selected records for the Field. - me.valueCollection = new Ext.util.Collection(valueCollectionConfig); - - // We use the selected Collection as our value collection and the basis - // for rendering the tag list. - - //proxmox override: since the picker is represented by a grid panel, - // we changed here the selection to RowModel - me.pickerSelectionModel = new Ext.selection.RowModel({ - mode: me.multiSelect ? 'SIMPLE' : 'SINGLE', - // There are situations when a row is selected on mousedown but then the mouse is dragged to another row - // and released. In these situations, the event target for the click event won't be the row where the mouse - // was released but the boundview. The view will then determine that it should fire a container click, and - // the DataViewModel will then deselect all prior selections. Setting `deselectOnContainerClick` here will - // prevent the model from deselecting. - deselectOnContainerClick: false, - enableInitialSelection: false, - pruneRemoved: false, - selected: me.valueCollection, - store: store, - listeners: { - scope: me, - lastselectedchanged: me.updateBindSelection - } - }); - - if (!initial) { - me.resetToDefault(); - } - - if (picker) { - picker.setSelectionModel(me.pickerSelectionModel); - if (picker.getStore() !== store) { - picker.bindStore(store); - } - } - } - }, - - // copied from ComboBox - createPicker: function() { - var me = this; - var picker; - - var pickerCfg = Ext.apply({ - // proxmox overrides: display a grid for selection - xtype: 'gridpanel', - id: me.pickerId, - pickerField: me, - floating: true, - hidden: true, - store: me.store, - displayField: me.displayField, - preserveScrollOnRefresh: true, - pageSize: me.pageSize, - tpl: me.tpl, - selModel: me.pickerSelectionModel, - focusOnToFront: false - }, me.listConfig, me.defaultListConfig); - - picker = me.picker || Ext.widget(pickerCfg); - - if (picker.getStore() !== me.store) { - picker.bindStore(me.store); - } - - if (me.pageSize) { - picker.pagingToolbar.on('beforechange', me.onPageChange, me); - } - - // proxmox overrides: pass missing method in gridPanel to its view - picker.refresh = function() { - picker.getSelectionModel().select(me.valueCollection.getRange()); - picker.getView().refresh(); - }; - picker.getNodeByRecord = function() { - picker.getView().getNodeByRecord(arguments); - }; - - // We limit the height of the picker to fit in the space above - // or below this field unless the picker has its own ideas about that. - if (!picker.initialConfig.maxHeight) { - picker.on({ - beforeshow: me.onBeforePickerShow, - scope: me - }); - } - picker.getSelectionModel().on({ - beforeselect: me.onBeforeSelect, - beforedeselect: me.onBeforeDeselect, - focuschange: me.onFocusChange, - selectionChange: function (sm, selectedRecords) { - var me = this; - if (selectedRecords.length) { - me.setValue(selectedRecords); - me.fireEvent('select', me, selectedRecords); - } - }, - scope: me - }); - - // hack for extjs6 - // when the clicked item is the same as the previously selected, - // it does not select the item - // instead we hide the picker - if (!me.multiSelect) { - picker.on('itemclick', function (sm,record) { - if (picker.getSelection()[0] === record) { - picker.hide(); - } - }); - } - - // when our store is not yet loaded, we increase - // the height of the gridpanel, so that we can see - // the loading mask - // - // we save the minheight to reset it after the load - picker.on('show', function() { - if (me.enableLoadMask) { - me.savedMinHeight = picker.getMinHeight(); - picker.setMinHeight(100); - } - }); - - picker.getNavigationModel().navigateOnSpace = false; - - return picker; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - queryMode: 'local', - matchFieldWidth: false - }); - - Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug - - Ext.applyIf(me.listConfig, { width: 400 }); - - me.callParent(); - - // Create the picker at an early stage, so it is available to store the previous selection - if (!me.picker) { - me.createPicker(); - } - - if (me.editable) { - // The trigger.picker causes first a focus event on the field then - // toggles the selection picker. Thus skip expanding in this case, - // else our focus listner expands and the picker.trigger then - // collapses it directly afterwards. - Ext.override(me.triggers.picker, { - onMouseDown : function (e) { - // copied "should we focus" check from Ext.form.trigger.Trigger - if (e.pointerType !== 'touch' && !this.field.owns(Ext.Element.getActiveElement())) { - me.skip_expand_on_focus = true; - } - this.callParent(arguments); - } - }); - - me.on("focus", function(me) { - if (!me.isExpanded && !me.skip_expand_on_focus) { - me.expand(); - } - me.skip_expand_on_focus = false; - }); - } - - me.mon(me.store, 'beforeload', function() { - if (!me.isDisabled()) { - me.enableLoadMask = true; - } - }); - - // hack: autoSelect does not work - me.mon(me.store, 'load', function(store, r, success, o) { - if (success) { - me.clearInvalid(); - - if (me.enableLoadMask) { - delete me.enableLoadMask; - - // if the picker exists, - // we reset its minheight to the saved var/0 - // we have to update the layout, otherwise the height - // gets not recalculated - if (me.picker) { - me.picker.setMinHeight(me.savedMinHeight || 0); - delete me.savedMinHeight; - me.picker.updateLayout(); - } - } - - var def = me.getValue() || me.preferredValue; - if (def) { - me.setValue(def, true); // sync with grid - } - var found = false; - if (def) { - if (Ext.isArray(def)) { - Ext.Array.each(def, function(v) { - if (store.findRecord(me.valueField, v)) { - found = true; - return false; // break - } - }); - } else { - found = store.findRecord(me.valueField, def); - } - } - - if (!found) { - var rec = me.store.first(); - if (me.autoSelect && rec && rec.data) { - def = rec.data[me.valueField]; - me.setValue(def, true); - } else { - me.setValue(me.editable ? def : '', true); - } - } - } - }); - } -}); -Ext.define('Proxmox.form.RRDTypeSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxRRDTypeSelector'], - - displayField: 'text', - valueField: 'id', - editable: false, - queryMode: 'local', - value: 'hour', - stateEvents: [ 'select' ], - stateful: true, - stateId: 'proxmoxRRDTypeSelection', - store: { - type: 'array', - fields: [ 'id', 'timeframe', 'cf', 'text' ], - data : [ - [ 'hour', 'hour', 'AVERAGE', - gettext('Hour') + ' (' + gettext('average') +')' ], - [ 'hourmax', 'hour', 'MAX', - gettext('Hour') + ' (' + gettext('maximum') + ')' ], - [ 'day', 'day', 'AVERAGE', - gettext('Day') + ' (' + gettext('average') + ')' ], - [ 'daymax', 'day', 'MAX', - gettext('Day') + ' (' + gettext('maximum') + ')' ], - [ 'week', 'week', 'AVERAGE', - gettext('Week') + ' (' + gettext('average') + ')' ], - [ 'weekmax', 'week', 'MAX', - gettext('Week') + ' (' + gettext('maximum') + ')' ], - [ 'month', 'month', 'AVERAGE', - gettext('Month') + ' (' + gettext('average') + ')' ], - [ 'monthmax', 'month', 'MAX', - gettext('Month') + ' (' + gettext('maximum') + ')' ], - [ 'year', 'year', 'AVERAGE', - gettext('Year') + ' (' + gettext('average') + ')' ], - [ 'yearmax', 'year', 'MAX', - gettext('Year') + ' (' + gettext('maximum') + ')' ] - ] - }, - // save current selection in the state Provider so RRDView can read it - getState: function() { - var ind = this.getStore().findExact('id', this.getValue()); - var rec = this.getStore().getAt(ind); - if (!rec) { - return; - } - return { - id: rec.data.id, - timeframe: rec.data.timeframe, - cf: rec.data.cf - }; - }, - // set selection based on last saved state - applyState : function(state) { - if (state && state.id) { - this.setValue(state.id); - } - } -}); -Ext.define('Proxmox.form.BondModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondModeSelector'], - - openvswitch: false, - - initComponent: function() { - var me = this; - - if (me.openvswitch) { - me.comboItems = [ - ['active-backup', 'active-backup'], - ['balance-slb', 'balance-slb'], - ['lacp-balance-slb', 'LACP (balance-slb)'], - ['lacp-balance-tcp', 'LACP (balance-tcp)'] - ]; - } else { - me.comboItems = [ - ['balance-rr', 'balance-rr'], - ['active-backup', 'active-backup'], - ['balance-xor', 'balance-xor'], - ['broadcast', 'broadcast'], - ['802.3ad', 'LACP (802.3ad)'], - ['balance-tlb', 'balance-tlb'], - ['balance-alb', 'balance-alb'] - ]; - } - - me.callParent(); - } -}); - -Ext.define('Proxmox.form.BondPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondPolicySelector'], - comboItems: [ - ['layer2', 'layer2'], - ['layer2+3', 'layer2+3'], - ['layer3+4', 'layer3+4'] - ] -}); - -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - */ -Ext.define('Proxmox.button.Button', { - extend: 'Ext.button.Button', - alias: 'widget.proxmoxButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - - // Note: me.realHandler may be a string (see named scopes) - var realHandler = me.handler; - - me.handler = function(button, event) { - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn !== 'yes') { - return; - } - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }); - } else { - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }; - } - - me.callParent(); - - var grid; - if (!me.selModel && me.selModel !== null) { - grid = me.up('grid'); - if (grid && grid.selModel) { - me.selModel = grid.selModel; - } - } - - if (me.waitMsgTarget === true) { - grid = me.up('grid'); - if (grid) { - me.waitMsgTarget = grid; - } else { - throw "unable to find waitMsgTarget"; - } - } - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); - - -Ext.define('Proxmox.button.StdRemoveButton', { - extend: 'Proxmox.button.Button', - alias: 'widget.proxmoxStdRemoveButton', - - text: gettext('Remove'), - - disabled: true, - - config: { - baseurl: undefined - }, - - getUrl: function(rec) { - var me = this; - - return me.baseurl + '/' + rec.getId(); - }, - - // also works with names scopes - callback: function(options, success, response) {}, - - getRecordName: function(rec) { return rec.getId() }, - - confirmMsg: function (rec) { - var me = this; - - var name = me.getRecordName(rec); - return Ext.String.format( - gettext('Are you sure you want to remove entry {0}'), - "'" + name + "'"); - }, - - handler: function(btn, event, rec) { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.getUrl(rec), - method: 'DELETE', - waitMsgTarget: me.waitMsgTarget, - callback: function(options, success, response) { - Ext.callback(me.callback, me.scope, [options, success, response], 0, me); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -/* help button pointing to an online documentation - for components contained in a modal window -*/ -/*global - proxmoxOnlineHelpInfo -*/ -Ext.define('Proxmox.button.Help', { - extend: 'Ext.button.Button', - xtype: 'proxmoxHelpButton', - - text: gettext('Help'), - - // make help button less flashy by styling it like toolbar buttons - iconCls: ' x-btn-icon-el-default-toolbar-small fa fa-question-circle', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - - hidden: true, - - listenToGlobalEvent: true, - - controller: { - xclass: 'Ext.app.ViewController', - listen: { - global: { - proxmoxShowHelp: 'onProxmoxShowHelp', - proxmoxHideHelp: 'onProxmoxHideHelp' - } - }, - onProxmoxShowHelp: function(helpLink) { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.setOnlineHelp(helpLink); - me.show(); - } - }, - onProxmoxHideHelp: function() { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.hide(); - } - } - }, - - getOnlineHelpInfo: function (ref) { - var helpMap; - if (typeof proxmoxOnlineHelpInfo !== 'undefined') { - helpMap = proxmoxOnlineHelpInfo; - } else if (typeof pveOnlineHelpInfo !== 'undefined') { - // be backward compatible with older pve-doc-generators - helpMap = pveOnlineHelpInfo; - } else { - throw "no global OnlineHelpInfo map declared"; - } - - return helpMap[ref]; - }, - - // this sets the link and the tooltip text - setOnlineHelp:function(blockid) { - var me = this; - - var info = me.getOnlineHelpInfo(blockid); - if (info) { - me.onlineHelp = blockid; - var title = info.title; - if (info.subtitle) { - title += ' - ' + info.subtitle; - } - me.setTooltip(title); - } - }, - - // helper to set the onlineHelp via a config object - setHelpConfig: function(config) { - var me = this; - me.setOnlineHelp(config.onlineHelp); - }, - - handler: function() { - var me = this; - var docsURI; - - if (me.onlineHelp) { - var info = me.getOnlineHelpInfo(me.onlineHelp); - if (info) { - docsURI = window.location.origin + info.link; - } - } - - if (docsURI) { - window.open(docsURI); - } else { - Ext.Msg.alert(gettext('Help'), gettext('No Help available')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.callParent(); - - if (me.onlineHelp) { - me.setOnlineHelp(me.onlineHelp); // set tooltip - } - } -}); -/* Renders a list of key values objets - -mandatory config parameters: -rows: an object container where each propery is a key-value object we want to render - var rows = { - keyboard: { - header: gettext('Keyboard Layout'), - editor: 'Your.KeyboardEdit', - required: true - }, - -optional: -disabled: setting this parameter to true will disable selection and focus on the -proxmoxObjectGrid as well as greying out input elements. -Useful for a readonly tabular display - -*/ - -Ext.define('Proxmox.grid.ObjectGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.proxmoxObjectGrid'], - disabled: false, - hideHeaders: true, - - monStoreErrors: false, - - add_combobox_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxKVComboBox', - name: name, - comboItems: opts.comboItems, - value: opts.defaultValue, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_text_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxtextfield', - name: name, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - vtype: opts.vtype, - fieldLabel: text - } - } - }; - }, - - add_boolean_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue || 0, - header: text, - renderer: opts.renderer || Proxmox.Utils.format_boolean, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxcheckbox', - name: name, - uncheckedValue: 0, - defaultValue: opts.defaultValue || 0, - checked: opts.defaultValue ? true : false, - deleteDefaultValue: opts.deleteDefaultValue ? true : false, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_integer_row: function(name, text, opts) { - var me = this; - - opts = opts || {} - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxintegerfield', - name: name, - minValue: opts.minValue, - maxValue: opts.maxValue, - emptyText: gettext('Default'), - deleteEmpty: opts.deleteEmpty ? true : false, - value: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - editorConfig: {}, // default config passed to editor - - run_editor: function() { - var me = this; - - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rows = me.rows; - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - var config; - if (Ext.isString(rowdef.editor)) { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - win = Ext.create(rowdef.editor, config); - } else { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - Ext.apply(config, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', me.reload, me); - }, - - reload: function() { - var me = this; - me.rstore.load(); - }, - - getObjectValue: function(key, defaultValue) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }, - - renderKey: function(key, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - return rowdef.header || key; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - - var renderer = rowdef.renderer; - if (renderer) { - return renderer(value, metaData, record, rowIndex, colIndex, store); - } - - return value; - }, - - listeners: { - itemkeydown: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER) { - this.pressedIndex = index; - } - }, - itemkeyup: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER && index == this.pressedIndex) { - this.run_editor(); - } - - this.pressedIndex = undefined; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - var rstore = me.rstore; - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore, - sorters: [], - filters: [] - }); - - if (rows) { - Ext.Object.each(rows, function(key, rowdef) { - if (Ext.isDefined(rowdef.defaultValue)) { - store.add({ key: key, value: rowdef.defaultValue }); - } else if (rowdef.required) { - store.add({ key: key, value: undefined }); - } - }); - } - - if (me.sorterFn) { - store.sorters.add(Ext.create('Ext.util.Sorter', { - sorterFn: me.sorterFn - })); - } - - store.filters.add(Ext.create('Ext.util.Filter', { - filterFn: function(item) { - if (rows) { - var rowdef = rows[item.data.key]; - if (!rowdef || (rowdef.visible === false)) { - return false; - } - } - return true; - } - })); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.applyIf(me, { - store: store, - stateful: false, - columns: [ - { - header: gettext('Name'), - width: me.cwidth1 || 200, - dataIndex: 'key', - renderer: me.renderKey - }, - { - flex: 1, - header: gettext('Value'), - dataIndex: 'value', - renderer: me.renderValue - } - ] - }); - - me.callParent(); - - if (me.monStoreErrors) { - Proxmox.Utils.monStoreErrors(me, me.store); - } - } -}); -Ext.define('Proxmox.grid.PendingObjectGrid', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxPendingObjectGrid'], - - getObjectValue: function(key, defaultValue, pending) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - var value = rec.data.value; - if (pending) { - if (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') { - value = rec.data.pending; - } else if (rec.data['delete'] === 1) { - value = defaultValue; - } - } - - if (Ext.isDefined(value) && (value !== '')) { - return value; - } else { - return defaultValue; - } - } - return defaultValue; - }, - - hasPendingChanges: function(key) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var keys = rowdef.multiKey || [ key ]; - var pending = false; - - Ext.Array.each(keys, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && ( - (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') || - rec.data['delete'] === 1 - )) { - pending = true; - return false; // break - } - }); - - return pending; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var renderer = rowdef.renderer; - var current = ''; - var pendingdelete = ''; - var pending = ''; - - if (renderer) { - current = renderer(value, metaData, record, rowIndex, colIndex, store, false); - if (me.hasPendingChanges(key)) { - pending = renderer(record.data.pending, metaData, record, rowIndex, colIndex, store, true); - } - if (pending == current) { - pending = undefined; - } - } else { - current = value || ''; - pending = record.data.pending; - } - - if (record.data['delete']) { - var delete_all = true; - if (rowdef.multiKey) { - Ext.Array.each(rowdef.multiKey, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && rec.data['delete'] !== 1) { - delete_all = false; - return false; // break - } - }); - } - if (delete_all) { - pending = '
'+ current +'
'; - } - } - - if (pending) { - return current + '
' + pending + '
'; - } else { - return current; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - model: 'KeyValuePendingDelete', - readArray: true, - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - me.callParent(); - } -}); -Ext.define('Proxmox.panel.InputPanel', { - extend: 'Ext.panel.Panel', - alias: ['widget.inputpanel'], - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - border: false, - - // override this with an URL to a relevant chapter of the pve manual - // setting this will display a help button in our parent panel - onlineHelp: undefined, - - // will be set if the inputpanel has advanced items - hasAdvanced: false, - - // if the panel has advanced items, - // this will determine if they are shown by default - showAdvanced: false, - - // overwrite this to modify submit data - onGetValues: function(values) { - return values; - }, - - getValues: function(dirtyOnly) { - var me = this; - - if (Ext.isFunction(me.onGetValues)) { - dirtyOnly = false; - } - - var values = {}; - - Ext.Array.each(me.query('[isFormField]'), function(field) { - if (!dirtyOnly || field.isDirty()) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - return me.onGetValues(values); - }, - - setAdvancedVisible: function(visible) { - var me = this; - var advItems = me.getComponent('advancedContainer'); - if (advItems) { - advItems.setVisible(visible); - } - }, - - setValues: function(values) { - var me = this; - - var form = me.up('form'); - - Ext.iterate(values, function(fieldId, val) { - var field = me.query('[isFormField][name=' + fieldId + ']')[0]; - if (field) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - }, - - initComponent: function() { - var me = this; - - var items; - - if (me.items) { - me.columns = 1; - items = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.items - } - ]; - me.items = undefined; - } else if (me.column4) { - me.columns = 4; - items = [ - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column2 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column3 - }, - { - columnWidth: 0.25, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column4 - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else if (me.column1) { - me.columns = 2; - items = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column2 || [] // allow empty column - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else { - throw "unsupported config"; - } - - var advItems; - if (me.advancedItems) { - advItems = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.advancedItems - } - ]; - me.advancedItems = undefined; - } else if (me.advancedColumn1) { - advItems = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.advancedColumn1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.advancedColumn2 || [] // allow empty column - } - ]; - - me.advancedColumn1 = undefined; - me.advancedColumn2 = undefined; - - if (me.advancedColumnB) { - advItems.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.advancedColumnB - }); - me.advancedColumnB = undefined; - } - } - - if (advItems) { - me.hasAdvanced = true; - advItems.unshift({ - columnWidth: 1, - xtype: 'box', - hidden: false, - border: true, - autoEl: { - tag: 'hr' - } - }); - items.push({ - columnWidth: 1, - xtype: 'container', - itemId: 'advancedContainer', - hidden: !me.showAdvanced, - layout: 'column', - defaults: { - border: false - }, - items: advItems - }); - } - - if (me.useFieldContainer) { - Ext.apply(me, { - layout: 'fit', - items: Ext.apply(me.useFieldContainer, { - layout: 'column', - defaultType: 'container', - items: items - }) - }); - } else { - Ext.apply(me, { - layout: 'column', - defaultType: 'container', - items: items - }); - } - - me.callParent(); - } -}); -/* - * Display log entries in a panel with scrollbar - * The log entries are automatically refreshed via a background task, - * with newest entries comming at the bottom - */ -Ext.define('Proxmox.panel.LogView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxLogView', - - pageSize: 500, - viewBuffer: 50, - lineHeight: 16, - - scrollToEnd: true, - - // callback for load failure, used for ceph - failCallback: undefined, - - controller: { - xclass: 'Ext.app.ViewController', - - updateParams: function() { - var me = this; - var viewModel = me.getViewModel(); - var since = viewModel.get('since'); - var until = viewModel.get('until'); - if (viewModel.get('hide_timespan')) { - return; - } - - if (since > until) { - Ext.Msg.alert('Error', 'Since date must be less equal than Until date.'); - return; - } - - viewModel.set('params.since', Ext.Date.format(since, 'Y-m-d')); - viewModel.set('params.until', Ext.Date.format(until, 'Y-m-d') + ' 23:59:59'); - me.getView().loadTask.delay(200); - }, - - scrollPosBottom: function() { - var view = this.getView(); - var pos = view.getScrollY(); - var maxPos = view.getScrollable().getMaxPosition().y; - return maxPos - pos; - }, - - updateView: function(text, first, total) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - var content = me.lookup('content'); - var data = viewModel.get('data'); - - if (first === data.first && total === data.total && text.length === data.textlen) { - return; // same content, skip setting and scrolling - } - viewModel.set('data', { - first: first, - total: total, - textlen: text.length - }); - - var scrollPos = me.scrollPosBottom(); - - content.update(text); - - if (view.scrollToEnd && scrollPos <= 0) { - // we use setTimeout to work around scroll handling on touchscreens - setTimeout(function() { view.scrollTo(0, Infinity); }, 10); - } - }, - - doLoad: function() { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - Proxmox.Utils.API2Request({ - url: me.getView().url, - params: viewModel.get('params'), - method: 'GET', - success: function(response) { - Proxmox.Utils.setErrorMask(me, false); - var total = response.result.total; - var lines = new Array(); - var first = Infinity; - - Ext.Array.each(response.result.data, function(line) { - if (first > line.n) { - first = line.n; - } - lines[line.n - 1] = Ext.htmlEncode(line.t); - }); - - lines.length = total; - me.updateView(lines.join('
'), first - 1, total); - }, - failure: function(response) { - if (view.failCallback) { - view.failCallback(response); - } else { - var msg = response.htmlStatus; - Proxmox.Utils.setErrorMask(me, msg); - } - } - }); - }, - - onScroll: function(x, y) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - - var lineHeight = view.lineHeight; - var line = view.getScrollY()/lineHeight; - var start = viewModel.get('params.start'); - var limit = viewModel.get('params.limit'); - var viewLines = view.getHeight()/lineHeight; - - var viewStart = Math.max(parseInt(line - 1 - view.viewBuffer, 10), 0); - var viewEnd = parseInt(line + viewLines + 1 + view.viewBuffer, 10); - - if (viewStart < start || viewEnd > (start+limit)) { - viewModel.set('params.start', - Math.max(parseInt(line - limit/2 + 10, 10), 0)); - view.loadTask.delay(200); - } - }, - - init: function(view) { - var me = this; - - if (!view.url) { - throw "no url specified"; - } - - var viewModel = this.getViewModel(); - var since = new Date(); - since.setDate(since.getDate() - 3); - viewModel.set('until', new Date()); - viewModel.set('since', since); - viewModel.set('params.limit', view.pageSize); - viewModel.set('hide_timespan', !view.log_select_timespan); - me.lookup('content').setStyle('line-height', view.lineHeight + 'px'); - - view.loadTask = new Ext.util.DelayedTask(me.doLoad, me); - - me.updateParams(); - view.task = Ext.TaskManager.start({ - run: function() { - if (!view.isVisible() || !view.scrollToEnd) { - return; - } - - if (me.scrollPosBottom() <= 1) { - view.loadTask.delay(200); - } - }, - interval: 1000 - }); - } - }, - - onDestroy: function() { - var me = this; - me.loadTask.cancel(); - Ext.TaskManager.stop(me.task); - }, - - // for user to initiate a load from outside - requestUpdate: function() { - var me = this; - me.loadTask.delay(200); - }, - - viewModel: { - data: { - until: null, - since: null, - hide_timespan: false, - data: { - start: 0, - total: 0, - textlen: 0 - }, - params: { - start: 0, - limit: 500, - } - } - }, - - layout: 'auto', - bodyPadding: 5, - scrollable: { - x: 'auto', - y: 'auto', - listeners: { - // we have to have this here, since we cannot listen to events - // of the scroller in the viewcontroller (extjs bug?), nor does - // the panel have a 'scroll' event' - scroll: { - fn: function(scroller, x, y) { - var controller = this.component.getController(); - if (controller) { // on destroy, controller can be gone - controller.onScroll(x,y); - } - }, - buffer: 200 - }, - } - }, - - tbar: { - bind: { - hidden: '{hide_timespan}' - }, - items: [ - '->', - 'Since: ', - { - xtype: 'datefield', - name: 'since_date', - reference: 'since', - format: 'Y-m-d', - bind: { - value: '{since}', - maxValue: '{until}' - } - }, - 'Until: ', - { - xtype: 'datefield', - name: 'until_date', - reference: 'until', - format: 'Y-m-d', - bind: { - value: '{until}', - minValue: '{since}' - } - }, - { - xtype: 'button', - text: 'Update', - handler: 'updateParams' - } - ], - }, - - items: [ - { - xtype: 'box', - reference: 'content', - style: { - font: 'normal 11px tahoma, arial, verdana, sans-serif', - 'white-space': 'pre' - }, - } - ] -}); -Ext.define('Proxmox.widget.RRDChart', { - extend: 'Ext.chart.CartesianChart', - alias: 'widget.proxmoxRRDChart', - - unit: undefined, // bytes, bytespersecond, percent - - controller: { - xclass: 'Ext.app.ViewController', - - convertToUnits: function(value) { - var units = ['', 'k','M','G','T', 'P']; - var si = 0; - while(value >= 1000 && si < (units.length -1)){ - value = value / 1000; - si++; - } - - // javascript floating point weirdness - value = Ext.Number.correctFloat(value); - - // limit to 2 decimal points - value = Ext.util.Format.number(value, "0.##"); - - return value.toString() + " " + units[si]; - }, - - leftAxisRenderer: function(axis, label, layoutContext) { - var me = this; - - return me.convertToUnits(label); - }, - - onSeriesTooltipRender: function(tooltip, record, item) { - var me = this.getView(); - - var suffix = ''; - - if (me.unit === 'percent') { - suffix = '%'; - } else if (me.unit === 'bytes') { - suffix = 'B'; - } else if (me.unit === 'bytespersecond') { - suffix = 'B/s'; - } - - var prefix = item.field; - if (me.fieldTitles && me.fieldTitles[me.fields.indexOf(item.field)]) { - prefix = me.fieldTitles[me.fields.indexOf(item.field)]; - } - tooltip.setHtml(prefix + ': ' + this.convertToUnits(record.get(item.field)) + suffix + - '
' + new Date(record.get('time'))); - }, - - onAfterAnimation: function(chart, eopts) { - // if the undobuton is disabled, - // disable our tool - - var ourUndoZoomButton = chart.tools[0]; - var undoButton = chart.interactions[0].getUndoButton(); - ourUndoZoomButton.setDisabled(undoButton.isDisabled()); - } - }, - - width: 770, - height: 300, - animation: false, - interactions: [{ - type: 'crosszoom' - }], - axes: [{ - type: 'numeric', - position: 'left', - grid: true, - renderer: 'leftAxisRenderer', - //renderer: function(axis, label) { return label; }, - minimum: 0 - }, { - type: 'time', - position: 'bottom', - grid: true, - fields: ['time'] - }], - legend: { - docked: 'bottom' - }, - listeners: { - animationend: 'onAfterAnimation' - }, - - - initComponent: function() { - var me = this; - var series = {}; - - if (!me.store) { - throw "cannot work without store"; - } - - if (!me.fields) { - throw "cannot work without fields"; - } - - me.callParent(); - - // add correct label for left axis - var axisTitle = ""; - if (me.unit === 'percent') { - axisTitle = "%"; - } else if (me.unit === 'bytes') { - axisTitle = "Bytes"; - } else if (me.unit === 'bytespersecond') { - axisTitle = "Bytes/s"; - } else if (me.fieldTitles && me.fieldTitles.length === 1) { - axisTitle = me.fieldTitles[0]; - } else if (me.fields.length === 1) { - axisTitle = me.fields[0]; - } - - me.axes[0].setTitle(axisTitle); - - if (!me.noTool) { - me.addTool([{ - type: 'minus', - disabled: true, - tooltip: gettext('Undo Zoom'), - handler: function(){ - var undoButton = me.interactions[0].getUndoButton(); - if (undoButton.handler) { - undoButton.handler(); - } - } - },{ - type: 'restore', - tooltip: gettext('Toggle Legend'), - handler: function(){ - if (me.legend) { - me.legend.setVisible(!me.legend.isVisible()); - } - } - }]); - } - - // add a series for each field we get - me.fields.forEach(function(item, index){ - var title = item; - if (me.fieldTitles && me.fieldTitles[index]) { - title = me.fieldTitles[index]; - } - me.addSeries(Ext.apply( - { - type: 'line', - xField: 'time', - yField: item, - title: title, - fill: true, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - }, - tooltip: { - trackMouse: true, - renderer: 'onSeriesTooltipRender' - } - }, - me.seriesConfig - )); - }); - - // enable animation after the store is loaded - me.store.onAfter('load', function() { - me.setAnimation(true); - }, this, {single: true}); - } -}); -Ext.define('Proxmox.panel.GaugeWidget', { - extend: 'Ext.panel.Panel', - alias: 'widget.proxmoxGauge', - - defaults: { - style: { - 'text-align':'center' - } - }, - items: [ - { - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}

' - }, - { - xtype: 'polar', - height: 120, - border: false, - itemId: 'chart', - series: [{ - type: 'gauge', - value: 0, - colors: ['#f5f5f5'], - sectors: [0], - donut: 90, - needleLength: 100, - totalAngle: Math.PI - }], - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '', - textAlign: 'center', - textBaseline: 'bottom', - x: 125, - y: 110, - fontSize: 30 - }] - }, - { - xtype: 'box', - itemId: 'text' - } - ], - - header: false, - border: false, - - warningThreshold: 0.6, - criticalThreshold: 0.9, - warningColor: '#fc0', - criticalColor: '#FF6C59', - defaultColor: '#c2ddf2', - backgroundColor: '#f5f5f5', - - initialValue: 0, - - - updateValue: function(value, text) { - var me = this; - var color = me.defaultColor; - var attr = {}; - - if (value >= me.criticalThreshold) { - color = me.criticalColor; - } else if (value >= me.warningThreshold) { - color = me.warningColor; - } - - me.chart.series[0].setColors([color, me.backgroundColor]); - me.chart.series[0].setValue(value*100); - - me.valueSprite.setText(' '+(value*100).toFixed(0) + '%'); - attr.x = me.chart.getWidth()/2; - attr.y = me.chart.getHeight()-20; - if (me.spriteFontSize) { - attr.fontSize = me.spriteFontSize; - } - me.valueSprite.setAttributes(attr, true); - - if (text !== undefined) { - me.text.setHtml(text); - } - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.text = me.getComponent('text'); - me.chart = me.getComponent('chart'); - me.valueSprite = me.chart.getSurface('chart').get('valueSprite'); - } -}); -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ -Ext.define('Proxmox.window.Edit', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxWindowEdit', - - // autoLoad trigger a load() after component creation - autoLoad: false, - - resizable: false, - - // use this tio atimatically generate a title like - // Create: - subject: undefined, - - // set isCreate to true if you want a Create button (instead - // OK and RESET) - isCreate: false, - - // set to true if you want an Add button (instead of Create) - isAdd: false, - - // set to true if you want an Remove button (instead of Create) - isRemove: false, - - // custom submitText - submitText: undefined, - - backgroundDelay: 0, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - - // finds the first form field - defaultFocus: 'field[disabled=false][hidden=false]', - - showProgress: false, - - showTaskViewer: false, - - // gets called if we have a progress bar or taskview and it detected that - // the task finished. function(success) - taskDone: Ext.emptyFn, - - // gets called when the api call is finished, right at the beginning - // function(success, response, options) - apiCallDone: Ext.emptyFn, - - // assign a reference from docs, to add a help button docked to the - // bottom of the window. If undefined we magically fall back to the - // onlineHelp of our first item, if set. - onlineHelp: undefined, - - isValid: function() { - var me = this; - - var form = me.formPanel.getForm(); - return form.isValid(); - }, - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.formPanel.getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - setValues: function(values) { - var me = this; - - var form = me.formPanel.getForm(); - - Ext.iterate(values, function(fieldId, val) { - var field = form.findField(fieldId); - if (field && !field.up('inputpanel')) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setValues(values); - }); - }, - - submit: function() { - var me = this; - - var form = me.formPanel.getForm(); - - var values = me.getValues(); - Ext.Object.each(values, function(name, val) { - if (values.hasOwnProperty(name)) { - if (Ext.isArray(val) && !val.length) { - values[name] = ''; - } - } - }); - - if (me.digest) { - values.digest = me.digest; - } - - if (me.backgroundDelay) { - values.background_delay = me.backgroundDelay; - } - - var url = me.url; - if (me.method === 'DELETE') { - url = url + "?" + Ext.Object.toQueryString(values); - values = undefined; - } - - Proxmox.Utils.API2Request({ - url: url, - waitMsgTarget: me, - method: me.method || (me.backgroundDelay ? 'POST' : 'PUT'), - params: values, - failure: function(response, options) { - me.apiCallDone(false, response, options); - - if (response.result && response.result.errors) { - form.markInvalid(response.result.errors); - } - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = (me.backgroundDelay || me.showProgress || me.showTaskViewer) && - response.result.data ? true : false; - - me.apiCallDone(true, response, options); - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - me.hide(); - - var upid = response.result.data; - var viewerClass = me.showTaskViewer ? 'Viewer' : 'Progress'; - var win = Ext.create('Proxmox.window.Task' + viewerClass, { - upid: upid, - taskDone: me.taskDone, - listeners: { - destroy: function () { - me.close(); - } - } - }); - win.show(); - } else { - me.close(); - } - } - }); - }, - - load: function(options) { - var me = this; - - var form = me.formPanel.getForm(); - - options = options || {}; - - var newopts = Ext.apply({ - waitMsgTarget: me - }, options); - - var createWrapper = function(successFn) { - Ext.apply(newopts, { - url: me.url, - method: 'GET', - success: function(response, opts) { - form.clearInvalid(); - me.digest = response.result.data.digest; - if (successFn) { - successFn(response, opts); - } else { - me.setValues(response.result.data); - } - // hack: fix ExtJS bug - Ext.Array.each(me.query('radiofield'), function(f) { - f.resetOriginalValue(); - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() { - me.close(); - }); - } - }); - }; - - createWrapper(options.success); - - Proxmox.Utils.API2Request(newopts); - }, - - initComponent : function() { - var me = this; - - if (!me.url) { - throw "no url specified"; - } - - if (me.create) {throw "deprecated parameter, use isCreate";} - - var items = Ext.isArray(me.items) ? me.items : [ me.items ]; - - me.items = undefined; - - me.formPanel = Ext.create('Ext.form.Panel', { - url: me.url, - method: me.method || 'PUT', - trackResetOnLoad: true, - bodyPadding: 10, - border: false, - defaults: Ext.apply({}, me.defaults, { - border: false - }), - fieldDefaults: Ext.apply({}, me.fieldDefaults, { - labelWidth: 100, - anchor: '100%' - }), - items: items - }); - - var inputPanel = me.formPanel.down('inputpanel'); - - var form = me.formPanel.getForm(); - - var submitText; - if (me.isCreate) { - if (me.submitText) { - submitText = me.submitText; - } else if (me.isAdd) { - submitText = gettext('Add'); - } else if (me.isRemove) { - submitText = gettext('Remove'); - } else { - submitText = gettext('Create'); - } - } else { - submitText = me.submitText || gettext('OK'); - } - - var submitBtn = Ext.create('Ext.Button', { - reference: 'submitbutton', - text: submitText, - disabled: !me.isCreate, - handler: function() { - me.submit(); - } - }); - - var resetBtn = Ext.create('Ext.Button', { - text: 'Reset', - disabled: true, - handler: function(){ - form.reset(); - } - }); - - var set_button_status = function() { - var valid = form.isValid(); - var dirty = form.isDirty(); - submitBtn.setDisabled(!valid || !(dirty || me.isCreate)); - resetBtn.setDisabled(!dirty); - - if (inputPanel && inputPanel.hasAdvanced) { - // we want to show the advanced options - // as soon as some of it is not valid - var advancedItems = me.down('#advancedContainer').query('field'); - var valid = true; - advancedItems.forEach(function(field) { - if (!field.isValid()) { - valid = false; - } - }); - - if (!valid) { - inputPanel.setAdvancedVisible(true); - me.down('#advancedcb').setValue(true); - } - } - }; - - form.on('dirtychange', set_button_status); - form.on('validitychange', set_button_status); - - var colwidth = 300; - if (me.fieldDefaults && me.fieldDefaults.labelWidth) { - colwidth += me.fieldDefaults.labelWidth - 100; - } - - var twoColumn = inputPanel && - (inputPanel.column1 || inputPanel.column2); - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, me.isCreate, me.isAdd); - } - - if (me.isCreate) { - me.buttons = [ submitBtn ] ; - } else { - me.buttons = [ submitBtn, resetBtn ]; - } - - if (inputPanel && inputPanel.hasAdvanced) { - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - inputPanel.setAdvancedVisible(advchecked); - me.buttons.unshift( - { - xtype: 'proxmoxcheckbox', - itemId: 'advancedcb', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - stateId: 'proxmox-advanced-cb', - value: advchecked, - listeners: { - change: function(cb, val) { - inputPanel.setAdvancedVisible(val); - sp.set('proxmox-advanced-cb', val); - } - } - } - ); - } - - var onlineHelp = me.onlineHelp; - if (!onlineHelp && inputPanel && inputPanel.onlineHelp) { - onlineHelp = inputPanel.onlineHelp; - } - - if (onlineHelp) { - var helpButton = Ext.create('Proxmox.button.Help'); - me.buttons.unshift(helpButton, '->'); - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', onlineHelp); - } - - Ext.applyIf(me, { - modal: true, - width: twoColumn ? colwidth*2 : colwidth, - border: false, - items: [ me.formPanel ] - }); - - me.callParent(); - - // always mark invalid fields - me.on('afterlayout', function() { - // on touch devices, the isValid function - // triggers a layout, which triggers an isValid - // and so on - // to prevent this we disable the layouting here - // and enable it afterwards - me.suspendLayout = true; - me.isValid(); - me.suspendLayout = false; - }); - - if (me.autoLoad) { - me.load(); - } - } -}); -Ext.define('Proxmox.window.PasswordEdit', { - extend: 'Proxmox.window.Edit', - alias: 'proxmoxWindowPasswordEdit', - - subject: gettext('Password'), - - url: '/api2/extjs/access/password', - - fieldDefaults: { - labelWidth: 120 - }, - - items: [ - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - allowBlank: false, - name: 'password', - listeners: { - change: function(field){ - field.next().validate(); - }, - blur: function(field){ - field.next().validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - allowBlank: false, - vtype: 'password', - initialPassField: 'password', - submitValue: false - }, - { - xtype: 'hiddenfield', - name: 'userid' - } - ], - - initComponent : function() { - var me = this; - - if (!me.userid) { - throw "no userid specified"; - } - - me.callParent(); - me.down('[name=userid]').setValue(me.userid); - } -}); -Ext.define('Proxmox.window.TaskProgress', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskProgress', - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: { - status: { defaultValue: 'unknown' }, - exitstatus: { defaultValue: 'unknown' } - } - }); - - me.on('destroy', statstore.stopUpdate); - - var getObjectValue = function(key, defaultValue) { - var rec = statstore.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }; - - var pbar = Ext.create('Ext.ProgressBar', { text: 'running...' }); - - me.mon(statstore, 'load', function() { - var status = getObjectValue('status'); - if (status === 'stopped') { - var exitstatus = getObjectValue('exitstatus'); - if (exitstatus == 'OK') { - pbar.reset(); - pbar.updateText("Done!"); - Ext.Function.defer(me.close, 1000, me); - } else { - me.close(); - Ext.Msg.alert('Task failed', exitstatus); - } - me.taskDone(exitstatus == 'OK'); - } - }); - - var descr = Proxmox.Utils.format_task_description(task.type, task.id); - - Ext.apply(me, { - title: gettext('Task') + ': ' + descr, - width: 300, - layout: 'auto', - modal: true, - bodyPadding: 5, - items: pbar, - buttons: [ - { - text: gettext('Details'), - handler: function() { - var win = Ext.create('Proxmox.window.TaskViewer', { - taskDone: me.taskDone, - upid: me.upid - }); - win.show(); - me.close(); - } - } - ] - }); - - me.callParent(); - - statstore.startUpdate(); - - pbar.wait(); - } -}); - -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ - -Ext.define('Proxmox.window.TaskViewer', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskViewer', - - extraTitle: '', // string to prepend after the generic task title - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statgrid; - - var rows = { - status: { - header: gettext('Status'), - defaultValue: 'unknown', - renderer: function(value) { - if (value != 'stopped') { - return value; - } - var es = statgrid.getObjectValue('exitstatus'); - if (es) { - return value + ': ' + es; - } - } - }, - exitstatus: { - visible: false - }, - type: { - header: gettext('Task type'), - required: true - }, - user: { - header: gettext('User name'), - required: true - }, - node: { - header: gettext('Node'), - required: true - }, - pid: { - header: gettext('Process ID'), - required: true - }, - starttime: { - header: gettext('Start Time'), - required: true, - renderer: Proxmox.Utils.render_timestamp - }, - upid: { - header: gettext('Unique task ID') - } - }; - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: rows - }); - - me.on('destroy', statstore.stopUpdate); - - var stop_task = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + task.node + "/tasks/" + me.upid, - waitMsgTarget: me, - method: 'DELETE', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var stop_btn1 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - var stop_btn2 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - statgrid = Ext.create('Proxmox.grid.ObjectGrid', { - title: gettext('Status'), - layout: 'fit', - tbar: [ stop_btn1 ], - rstore: statstore, - rows: rows, - border: false - }); - - var logView = Ext.create('Proxmox.panel.LogView', { - title: gettext('Output'), - tbar: [ stop_btn2 ], - border: false, - url: "/api2/extjs/nodes/" + task.node + "/tasks/" + me.upid + "/log" - }); - - me.mon(statstore, 'load', function() { - var status = statgrid.getObjectValue('status'); - - if (status === 'stopped') { - logView.scrollToEnd = false; - logView.requestUpdate(); - statstore.stopUpdate(); - me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK'); - } - - stop_btn1.setDisabled(status !== 'running'); - stop_btn2.setDisabled(status !== 'running'); - }); - - statstore.startUpdate(); - - Ext.apply(me, { - title: "Task viewer: " + task.desc + me.extraTitle, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [{ - xtype: 'tabpanel', - region: 'center', - items: [ logView, statgrid ] - }] - }); - - me.callParent(); - - logView.fireEvent('show', logView); - } -}); - -Ext.define('apt-pkglist', { - extend: 'Ext.data.Model', - fields: [ 'Package', 'Title', 'Description', 'Section', 'Arch', - 'Priority', 'Version', 'OldVersion', 'ChangeLogUrl', 'Origin' ], - idProperty: 'Package' -}); - -Ext.define('Proxmox.node.APT', { - extend: 'Ext.grid.GridPanel', - - xtype: 'proxmoxNodeAPT', - - upgradeBtn: undefined, - - columns: [ - { - header: gettext('Package'), - width: 200, - sortable: true, - dataIndex: 'Package' - }, - { - text: gettext('Version'), - columns: [ - { - header: gettext('current'), - width: 100, - sortable: false, - dataIndex: 'OldVersion' - }, - { - header: gettext('new'), - width: 100, - sortable: false, - dataIndex: 'Version' - } - ] - }, - { - header: gettext('Description'), - sortable: false, - dataIndex: 'Title', - flex: 1 - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'apt-pkglist', - groupField: 'Origin', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/apt/update" - }, - sorters: [ - { - property : 'Package', - direction: 'ASC' - } - ] - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl: '{[ "Origin: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})', - enableGroupingMenu: false - }); - - var rowBodyFeature = Ext.create('Ext.grid.feature.RowBody', { - getAdditionalData: function (data, rowIndex, record, orig) { - var headerCt = this.view.headerCt; - var colspan = headerCt.getColumnCount(); - // Usually you would style the my-body-class in CSS file - return { - rowBody: '
' + - Ext.String.htmlEncode(data.Description) + - '
', - rowBodyColspan: colspan - }; - } - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store, true); - - var apt_command = function(cmd){ - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/apt/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.mon(win, 'close', reload); - } - }); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var update_btn = new Ext.Button({ - text: gettext('Refresh'), - handler: function(){ - Proxmox.Utils.checked_command(function() { apt_command('update'); }); - } - }); - - var show_changelog = function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return; - } - - var view = Ext.createWidget('component', { - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Changelog') + ": " + rec.data.Package, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + me.nodename + "/apt/changelog", - params: { - name: rec.data.Package, - version: rec.data.Version - }, - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - view.update(Ext.htmlEncode(response.result.data)); - } - }); - - }; - - var changelog_btn = new Proxmox.button.Button({ - text: gettext('Changelog'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return false; - } - return true; - }, - handler: function(b, e, rec) { - show_changelog(rec); - } - }); - - if (me.upgradeBtn) { - me.tbar = [ update_btn, me.upgradeBtn, changelog_btn ]; - } else { - me.tbar = [ update_btn, changelog_btn ]; - } - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-update', - selModel: sm, - viewConfig: { - stripeRows: false, - emptyText: '
' + gettext('No updates available.') + '
' - }, - features: [ groupingFeature, rowBodyFeature ], - listeners: { - activate: reload, - itemdblclick: function(v, rec) { - show_changelog(rec); - } - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.NetworkEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeNetworkEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.iftype) { - throw "no network device type specified"; - } - - me.isCreate = !me.iface; - - var iface_vtype; - - if (me.iftype === 'bridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'bond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'eth' && !me.isCreate) { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'vlan' && !me.isCreate) { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'OVSBridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'OVSBond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'OVSIntPort') { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'OVSPort') { - iface_vtype = 'InterfaceName'; - } else { - console.log(me.iftype); - throw "unknown network device type specified"; - } - - me.subject = Proxmox.Utils.render_network_iface_type(me.iftype); - - var column2 = []; - - if (!(me.iftype === 'OVSIntPort' || me.iftype === 'OVSPort' || - me.iftype === 'OVSBond')) { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Autostart'), - name: 'autostart', - uncheckedValue: 0, - checked: me.isCreate ? true : undefined - }); - } - - if (me.iftype === 'bridge') { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('VLAN aware'), - name: 'bridge_vlan_aware', - deleteEmpty: !me.isCreate - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'bridge_ports' - }); - } else if (me.iftype === 'OVSBridge') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'ovs_ports' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'OVSPort' || me.iftype === 'OVSIntPort') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'bond') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'slaves' - }); - - var policySelector = Ext.createWidget('bondPolicySelector', { - fieldLabel: gettext('Hash policy'), - name: 'bond_xmit_hash_policy', - deleteEmpty: !me.isCreate, - disabled: true - }); - - column2.push({ - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - value: me.isCreate ? 'balance-rr' : undefined, - listeners: { - change: function(f, value) { - if (value === 'balance-xor' || - value === '802.3ad') { - policySelector.setDisabled(false); - } else { - policySelector.setDisabled(true); - policySelector.setValue(''); - } - } - }, - allowBlank: false - }); - - column2.push(policySelector); - - } else if (me.iftype === 'OVSBond') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } - - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Comment'), - allowBlank: true, - nodename: me.nodename, - name: 'comments' - }); - - var url; - var method; - - if (me.isCreate) { - url = "/api2/extjs/nodes/" + me.nodename + "/network"; - method = 'POST'; - } else { - url = "/api2/extjs/nodes/" + me.nodename + "/network/" + me.iface; - method = 'PUT'; - } - - var column1 = [ - { - xtype: 'hiddenfield', - name: 'type', - value: me.iftype - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'iface', - value: me.iface, - vtype: iface_vtype, - allowBlank: false - } - ]; - - if (me.iftype === 'OVSBond') { - column1.push( - { - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - openvswitch: true, - value: me.isCreate ? 'active-backup' : undefined, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'ovs_bonds' - } - ); - } else { - - column1.push( - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('IP address'), - vtype: 'IPAddress', - name: 'address' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Subnet mask'), - vtype: 'IPAddress', - name: 'netmask', - validator: function(value) { - /*jslint confusion: true */ - if (!me.items) { - return true; - } - var address = me.down('field[name=address]').getValue(); - if (value !== '') { - if (address === '') { - return "Subnet mask requires option 'IP address'"; - } - } else { - if (address !== '') { - return "Option 'IP address' requires a subnet mask"; - } - } - - return true; - } - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway'), - vtype: 'IPAddress', - name: 'gateway' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('IPv6 address'), - vtype: 'IP6Address', - name: 'address6' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Prefix length'), - vtype: 'IP6PrefixLength', - name: 'netmask6', - value: '', - allowBlank: true, - validator: function(value) { - /*jslint confusion: true */ - if (!me.items) { - return true; - } - var address = me.down('field[name=address6]').getValue(); - if (value !== '') { - if (address === '') { - return "IPv6 prefix length requires option 'IPv6 address'"; - } - } else { - if (address !== '') { - return "Option 'IPv6 address' requires an IPv6 prefix length"; - } - } - - return true; - } - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway'), - vtype: 'IP6Address', - name: 'gateway6' - } - ); - } - - Ext.applyIf(me, { - url: url, - method: method, - items: { - xtype: 'inputpanel', - column1: column1, - column2: column2 - } - }); - - me.callParent(); - - if (me.isCreate) { - me.down('field[name=iface]').setValue(me.iface_default); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.type !== me.iftype) { - var msg = "Got unexpected device type"; - Ext.Msg.alert(gettext('Error'), msg, function() { - me.close(); - }); - return; - } - me.setValues(data); - me.isValid(); // trigger validation - } - }); - } - } -}); -Ext.define('proxmox-networks', { - extend: 'Ext.data.Model', - fields: [ - 'iface', 'type', 'active', 'autostart', - 'bridge_ports', 'slaves', - 'address', 'netmask', 'gateway', - 'address6', 'netmask6', 'gateway6', - 'comments' - ], - idProperty: 'iface' -}); - -Ext.define('Proxmox.node.NetworkView', { - extend: 'Ext.panel.Panel', - - alias: ['widget.proxmoxNodeNetworkView'], - - // defines what types of network devices we want to create - // order is always the same - types: ['bridge', 'bond', 'ovs'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseUrl = '/nodes/' + me.nodename + '/network'; - - var store = Ext.create('Ext.data.Store', { - model: 'proxmox-networks', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseUrl - }, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }); - - var reload = function() { - var changeitem = me.down('#changes'); - Proxmox.Utils.API2Request({ - url: baseUrl, - failure: function(response, opts) { - store.loadData({}); - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - changeitem.update(''); - changeitem.setHidden(true); - }, - success: function(response, opts) { - var result = Ext.decode(response.responseText); - store.loadData(result.data); - var changes = result.changes; - if (changes === undefined || changes === '') { - changes = gettext("No changes"); - changeitem.setHidden(true); - } else { - changeitem.update("
" + Ext.htmlEncode(changes) + "
"); - changeitem.setHidden(false); - } - } - }); - }; - - var run_editor = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iface: rec.data.iface, - iftype: rec.data.type - }); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: run_editor - }); - - var del_btn = new Ext.Button({ - text: gettext('Remove'), - disabled: true, - handler: function(){ - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var iface = rec.data.iface; - - Proxmox.Utils.API2Request({ - url: baseUrl + '/' + iface, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var set_button_status = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - - edit_btn.setDisabled(!rec); - del_btn.setDisabled(!rec); - }; - - var render_ports = function(value, metaData, record) { - if (value === 'bridge') { - return record.data.bridge_ports; - } else if (value === 'bond') { - return record.data.slaves; - } else if (value === 'OVSBridge') { - return record.data.ovs_ports; - } else if (value === 'OVSBond') { - return record.data.ovs_bonds; - } - }; - - var find_next_iface_id = function(prefix) { - var next; - for (next = 0; next <= 9999; next++) { - if (!store.getById(prefix + next.toString())) { - break; - } - } - return prefix + next.toString(); - }; - - var menu_items = []; - - if (me.types.indexOf('bridge') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bridge', - iface_default: find_next_iface_id('vmbr') - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('bond') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bond', - iface_default: find_next_iface_id('bond') - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('ovs') !== -1) { - if (menu_items.length > 0) { - menu_items.push({ xtype: 'menuseparator' }); - } - - menu_items.push( - { - text: Proxmox.Utils.render_network_iface_type('OVSBridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBridge', - iface_default: find_next_iface_id('vmbr') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSBond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBond', - iface_default: find_next_iface_id('bond') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSIntPort'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSIntPort' - }); - win.on('destroy', reload); - win.show(); - } - } - ); - } - - Ext.apply(me, { - layout: 'border', - tbar: [ - { - text: gettext('Create'), - menu: { - plain: true, - items: menu_items - } - }, ' ', - { - text: gettext('Revert'), - handler: function() { - Proxmox.Utils.API2Request({ - url: baseUrl, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - edit_btn, - del_btn - ], - items: [ - { - xtype: 'gridpanel', - stateful: true, - stateId: 'grid-node-network', - store: store, - region: 'center', - border: false, - columns: [ - { - header: gettext('Name'), - sortable: true, - dataIndex: 'iface' - }, - { - header: gettext('Type'), - sortable: true, - width: 120, - renderer: Proxmox.Utils.render_network_iface_type, - dataIndex: 'type' - }, - { - xtype: 'booleancolumn', - header: gettext('Active'), - width: 80, - sortable: true, - dataIndex: 'active', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText, - }, - { - xtype: 'booleancolumn', - header: gettext('Autostart'), - width: 80, - sortable: true, - dataIndex: 'autostart', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - xtype: 'booleancolumn', - header: gettext('VLAN aware'), - width: 80, - sortable: true, - dataIndex: 'bridge_vlan_aware', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - header: gettext('Ports/Slaves'), - dataIndex: 'type', - renderer: render_ports - }, - { - header: gettext('IP address'), - sortable: true, - width: 120, - dataIndex: 'address', - renderer: function(value, metaData, rec) { - if (rec.data.address && rec.data.address6) { - return rec.data.address + "
" - + rec.data.address6 + '/' + rec.data.netmask6; - } else if (rec.data.address6) { - return rec.data.address6 + '/' + rec.data.netmask6; - } else { - return rec.data.address; - } - } - }, - { - header: gettext('Subnet mask'), - width: 120, - sortable: true, - dataIndex: 'netmask' - }, - { - header: gettext('Gateway'), - width: 120, - sortable: true, - dataIndex: 'gateway', - renderer: function(value, metaData, rec) { - if (rec.data.gateway && rec.data.gateway6) { - return rec.data.gateway + "
" + rec.data.gateway6; - } else if (rec.data.gateway6) { - return rec.data.gateway6; - } else { - return rec.data.gateway; - } - } - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - flex: 1, - renderer: Ext.String.htmlEncode - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: run_editor - } - }, - { - border: false, - region: 'south', - autoScroll: true, - hidden: true, - itemId: 'changes', - tbar: [ - gettext('Pending changes') + ' (' + - gettext('Please reboot to activate changes') + ')' - ], - split: true, - bodyPadding: 5, - flex: 0.6, - html: gettext("No changes") - } - ], - }); - - me.callParent(); - reload(); - } -}); -Ext.define('Proxmox.node.DNSEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeDNSEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.items = [ - { - xtype: 'textfield', - fieldLabel: gettext('Search domain'), - name: 'search', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 1", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns1' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 2", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns2' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 3", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns3' - } - ]; - - Ext.applyIf(me, { - subject: gettext('DNS'), - url: "/api2/extjs/nodes/" + me.nodename + "/dns", - fieldDefaults: { - labelWidth: 120 - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('Proxmox.node.HostsView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxNodeHostsView', - - reload: function() { - var me = this; - me.store.load(); - }, - - tbar: [ - { - text: gettext('Save'), - disabled: true, - itemId: 'savebtn', - handler: function() { - var me = this.up('panel'); - Proxmox.Utils.API2Request({ - params: { - digest: me.digest, - data: me.down('#hostsfield').getValue() - }, - method: 'POST', - url: '/nodes/' + me.nodename + '/hosts', - waitMsgTarget: me, - success: function(response, opts) { - me.reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - text: gettext('Revert'), - disabled: true, - itemId: 'resetbtn', - handler: function() { - var me = this.up('panel'); - me.down('#hostsfield').reset(); - } - } - ], - - layout: 'fit', - - items: [ - { - xtype: 'textarea', - itemId: 'hostsfield', - fieldStyle: { - 'font-family': 'monospace', - 'white-space': 'pre' - }, - listeners: { - dirtychange: function(ta, dirty) { - var me = this.up('panel'); - me.down('#savebtn').setDisabled(!dirty); - me.down('#resetbtn').setDisabled(!dirty); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.store = Ext.create('Ext.data.Store', { - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/hosts", - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.store); - - me.mon(me.store, 'load', function(store, records, success) { - if (!success || records.length < 1) { - return; - } - me.digest = records[0].data.digest; - var data = records[0].data.data; - me.down('#hostsfield').setValue(data); - me.down('#hostsfield').resetOriginalValue(); - }); - - me.reload(); - } -}); -Ext.define('Proxmox.node.DNSView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeDNSView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var run_editor = function() { - var win = Ext.create('Proxmox.node.DNSEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/dns", - cwidth1: 130, - interval: 1000, - run_editor: run_editor, - rows: { - search: { - header: 'Search domain', - required: true, - renderer: Ext.htmlEncode - }, - dns1: { - header: gettext('DNS server') + " 1", - required: true, - renderer: Ext.htmlEncode - }, - dns2: { - header: gettext('DNS server') + " 2", - renderer: Ext.htmlEncode - }, - dns3: { - header: gettext('DNS server') + " 3", - renderer: Ext.htmlEncode - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); -Ext.define('Proxmox.node.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeTasks'], - stateful: true, - stateId: 'grid-node-tasks', - loadMask: true, - sortableColumns: false, - vmidFilter: 0, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.BufferedStore', { - pageSize: 500, - autoLoad: true, - remoteFilter: true, - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - startParam: 'start', - limitParam: 'limit', - url: "/api2/json/nodes/" + me.nodename + "/tasks" - } - }); - - var userfilter = ''; - var filter_errors = 0; - - var updateProxyParams = function() { - var params = { - errors: filter_errors - }; - if (userfilter) { - params.userfilter = userfilter; - } - if (me.vmidFilter) { - params.vmid = me.vmidFilter; - } - store.proxy.extraParams = params; - }; - - updateProxyParams(); - - var reload_task = Ext.create('Ext.util.DelayedTask',function() { - updateProxyParams(); - store.reload(); - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - var view_btn = new Ext.Button({ - text: gettext('View'), - disabled: true, - handler: run_task_viewer - }); - - Proxmox.Utils.monStoreErrors(me, store, true); - - Ext.apply(me, { - store: store, - viewConfig: { - trackOver: false, - stripeRows: false, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - tbar: [ - view_btn, '->', gettext('User name') +':', ' ', - { - xtype: 'textfield', - width: 200, - value: userfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - userfilter = field.getValue(); - reload_task.delay(500); - } - } - }, ' ', gettext('Only Errors') + ':', ' ', - { - xtype: 'checkbox', - hideLabel: true, - checked: filter_errors, - listeners: { - change: function(field, checked) { - filter_errors = checked ? 1 : 0; - reload_task.delay(10); - } - } - }, ' ' - ], - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 100, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 100, - renderer: function(value, metaData, record) { - return Ext.Date.format(value,"M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return "ERROR: " + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - selectionchange: function(v, selections) { - view_btn.setDisabled(!(selections && selections[0])); - }, - show: function() { reload_task.delay(10); }, - destroy: function() { reload_task.cancel(); } - } - }); - - me.callParent(); - - } -}); -Ext.define('proxmox-services', { - extend: 'Ext.data.Model', - fields: [ 'service', 'name', 'desc', 'state' ], - idProperty: 'service' -}); - -Ext.define('Proxmox.node.ServiceView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeServiceView'], - - startOnlyServices: {}, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 1000, - storeid: 'proxmox-services' + me.nodename, - model: 'proxmox-services', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/services" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - sortAfterUpdate: true, - sorters: [ - { - property : 'name', - direction: 'ASC' - } - ] - }); - - var view_service_log = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - var win = Ext.create('Ext.window.Window', { - title: gettext('Syslog') + ': ' + rec.data.service, - modal: true, - items: { - xtype: 'proxmoxLogView', - width: 800, - height: 400, - url: "/api2/extjs/nodes/" + me.nodename + "/syslog?service=" + - rec.data.service, - log_select_timespan: 1 - } - }); - win.show(); - }; - - var service_cmd = function(cmd) { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/services/" + rec.data.service + "/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.loading = true; - }, - success: function(response, opts) { - rstore.startUpdate(); - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - }; - - var start_btn = new Ext.Button({ - text: gettext('Start'), - disabled: true, - handler: function(){ - service_cmd("start"); - } - }); - - var stop_btn = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: function(){ - service_cmd("stop"); - } - }); - - var restart_btn = new Ext.Button({ - text: gettext('Restart'), - disabled: true, - handler: function(){ - service_cmd("restart"); - } - }); - - var syslog_btn = new Ext.Button({ - text: gettext('Syslog'), - disabled: true, - handler: view_service_log - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - start_btn.disable(); - stop_btn.disable(); - restart_btn.disable(); - syslog_btn.disable(); - return; - } - var service = rec.data.service; - var state = rec.data.state; - - syslog_btn.enable(); - - if (me.startOnlyServices[service]) { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - } - stop_btn.disable(); - } else { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - stop_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - stop_btn.disable(); - } - } - }; - - me.mon(store, 'refresh', set_button_status); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.apply(me, { - store: store, - stateful: false, - tbar: [ start_btn, stop_btn, restart_btn, syslog_btn ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: gettext('Status'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - dataIndex: 'desc', - flex: 2 - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: view_service_log, - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeTimeEdit'], - - subject: gettext('Time zone'), - - width: 400, - - autoLoad: true, - - fieldDefaults: { - labelWidth: 70 - }, - - items: { - xtype: 'combo', - fieldLabel: gettext('Time zone'), - name: 'timezone', - queryMode: 'local', - store: Ext.create('Proxmox.data.TimezoneStore'), - displayField: 'zone', - forceSelection: true, - editable: false, - allowBlank: false - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.url = "/api2/extjs/nodes/" + me.nodename + "/time"; - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeTimeView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var tzoffset = (new Date()).getTimezoneOffset()*60000; - var renderlocaltime = function(value) { - var servertime = new Date((value * 1000) + tzoffset); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }; - - var run_editor = function() { - var win = Ext.create('Proxmox.node.TimeEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/time", - cwidth1: 150, - interval: 1000, - run_editor: run_editor, - rows: { - timezone: { - header: gettext('Time zone'), - required: true - }, - localtime: { - header: gettext('Server time'), - required: true, - renderer: renderlocaltime - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); diff --git a/serverside/jsmod/5.4-3/pvemanagerlib.js b/serverside/jsmod/5.4-3/pvemanagerlib.js deleted file mode 100644 index e62acde..0000000 --- a/serverside/jsmod/5.4-3/pvemanagerlib.js +++ /dev/null @@ -1,38347 +0,0 @@ -var pveOnlineHelpInfo = { - "ceph_rados_block_devices" : { - "link" : "/pve-docs/chapter-pvesm.html#ceph_rados_block_devices", - "title" : "Ceph RADOS Block Devices (RBD)" - }, - "chapter_ha_manager" : { - "link" : "/pve-docs/chapter-ha-manager.html#chapter_ha_manager", - "title" : "High Availability" - }, - "chapter_lvm" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_lvm", - "title" : "Logical Volume Manager (LVM)" - }, - "chapter_pct" : { - "link" : "/pve-docs/chapter-pct.html#chapter_pct", - "title" : "Proxmox Container Toolkit" - }, - "chapter_pve_firewall" : { - "link" : "/pve-docs/chapter-pve-firewall.html#chapter_pve_firewall", - "title" : "Proxmox VE Firewall" - }, - "chapter_pveceph" : { - "link" : "/pve-docs/chapter-pveceph.html#chapter_pveceph", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "chapter_pvecm" : { - "link" : "/pve-docs/chapter-pvecm.html#chapter_pvecm", - "title" : "Cluster Manager" - }, - "chapter_pvesr" : { - "link" : "/pve-docs/chapter-pvesr.html#chapter_pvesr", - "title" : "Storage Replication" - }, - "chapter_storage" : { - "link" : "/pve-docs/chapter-pvesm.html#chapter_storage", - "title" : "Proxmox VE Storage" - }, - "chapter_system_administration" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_system_administration", - "title" : "Host System Administration" - }, - "chapter_user_management" : { - "link" : "/pve-docs/chapter-pveum.html#chapter_user_management", - "title" : "User Management" - }, - "chapter_virtual_machines" : { - "link" : "/pve-docs/chapter-qm.html#chapter_virtual_machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "chapter_vzdump" : { - "link" : "/pve-docs/chapter-vzdump.html#chapter_vzdump", - "title" : "Backup and Restore" - }, - "chapter_zfs" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_zfs", - "title" : "ZFS on Linux" - }, - "datacenter_configuration_file" : { - "link" : "/pve-docs/pve-admin-guide.html#datacenter_configuration_file", - "title" : "Datacenter Configuration" - }, - "getting_help" : { - "link" : "/pve-docs/pve-admin-guide.html#getting_help", - "title" : "Getting Help" - }, - "gui_my_settings" : { - "link" : "/pve-docs/chapter-pve-gui.html#gui_my_settings", - "subtitle" : "My Settings", - "title" : "Graphical User Interface" - }, - "ha_manager_fencing" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_fencing", - "subtitle" : "Fencing", - "title" : "High Availability" - }, - "ha_manager_groups" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_groups", - "subtitle" : "Groups", - "title" : "High Availability" - }, - "ha_manager_resource_config" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resource_config", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "ha_manager_resources" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resources", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "pct_configuration" : { - "link" : "/pve-docs/chapter-pct.html#pct_configuration", - "subtitle" : "Configuration", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_images" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_images", - "subtitle" : "Container Images", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_network" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_network", - "subtitle" : "Network", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_storage" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_storage", - "subtitle" : "Container Storage", - "title" : "Proxmox Container Toolkit" - }, - "pct_cpu" : { - "link" : "/pve-docs/chapter-pct.html#pct_cpu", - "subtitle" : "CPU", - "title" : "Proxmox Container Toolkit" - }, - "pct_general" : { - "link" : "/pve-docs/chapter-pct.html#pct_general", - "subtitle" : "General Settings", - "title" : "Proxmox Container Toolkit" - }, - "pct_memory" : { - "link" : "/pve-docs/chapter-pct.html#pct_memory", - "subtitle" : "Memory", - "title" : "Proxmox Container Toolkit" - }, - "pct_migration" : { - "link" : "/pve-docs/chapter-pct.html#pct_migration", - "subtitle" : "Migration", - "title" : "Proxmox Container Toolkit" - }, - "pct_options" : { - "link" : "/pve-docs/chapter-pct.html#pct_options", - "subtitle" : "Options", - "title" : "Proxmox Container Toolkit" - }, - "pct_snapshots" : { - "link" : "/pve-docs/chapter-pct.html#pct_snapshots", - "subtitle" : "Snapshots", - "title" : "Proxmox Container Toolkit" - }, - "pct_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-pct.html#pct_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Containers", - "title" : "Proxmox Container Toolkit" - }, - "pve_admin_guide" : { - "link" : "/pve-docs/pve-admin-guide.html", - "title" : "Proxmox VE Administration Guide" - }, - "pve_ceph_install" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_install", - "subtitle" : "Installation of Ceph Packages", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_monitors" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_monitors", - "subtitle" : "Creating Ceph Monitors", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_osds" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_osds", - "subtitle" : "Creating Ceph OSDs", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_pools" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_pools", - "subtitle" : "Creating Ceph Pools", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_documentation_index" : { - "link" : "/pve-docs/index.html", - "title" : "Proxmox VE Documentation Index" - }, - "pve_firewall_cluster_wide_setup" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_cluster_wide_setup", - "subtitle" : "Cluster Wide Setup", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_host_specific_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_host_specific_configuration", - "subtitle" : "Host Specific Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_aliases" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_aliases", - "subtitle" : "IP Aliases", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_sets" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_sets", - "subtitle" : "IP Sets", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_vm_container_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_vm_container_configuration", - "subtitle" : "VM/Container Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_service_daemons" : { - "link" : "/pve-docs/index.html#_service_daemons", - "title" : "Service Daemons" - }, - "pveceph_fs" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs", - "subtitle" : "CephFS", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pveceph_fs_create" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs_create", - "subtitle" : "Create a CephFS", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pveceph_fs_mds" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs_mds", - "subtitle" : "Metadata Server (MDS)", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pvesr_schedule_time_format" : { - "link" : "/pve-docs/chapter-pvesr.html#pvesr_schedule_time_format", - "subtitle" : "Schedule Format", - "title" : "Storage Replication" - }, - "pveum_authentication_realms" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_authentication_realms", - "subtitle" : "Authentication Realms", - "title" : "User Management" - }, - "pveum_groups" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_groups", - "subtitle" : "Groups", - "title" : "User Management" - }, - "pveum_permission_management" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_permission_management", - "subtitle" : "Permission Management", - "title" : "User Management" - }, - "pveum_pools" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_pools", - "subtitle" : "Pools", - "title" : "User Management" - }, - "pveum_roles" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_roles", - "subtitle" : "Roles", - "title" : "User Management" - }, - "pveum_tfa_auth" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_tfa_auth", - "subtitle" : "Two factor authentication", - "title" : "User Management" - }, - "pveum_users" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_users", - "subtitle" : "Users", - "title" : "User Management" - }, - "qm_bios_and_uefi" : { - "link" : "/pve-docs/chapter-qm.html#qm_bios_and_uefi", - "subtitle" : "BIOS and UEFI", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cloud_init" : { - "link" : "/pve-docs/chapter-qm.html#qm_cloud_init", - "title" : "Cloud-Init Support" - }, - "qm_copy_and_clone" : { - "link" : "/pve-docs/chapter-qm.html#qm_copy_and_clone", - "subtitle" : "Copies and Clones", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cpu" : { - "link" : "/pve-docs/chapter-qm.html#qm_cpu", - "subtitle" : "CPU", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_general_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_general_settings", - "subtitle" : "General Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_hard_disk" : { - "link" : "/pve-docs/chapter-qm.html#qm_hard_disk", - "subtitle" : "Hard Disk", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_memory" : { - "link" : "/pve-docs/chapter-qm.html#qm_memory", - "subtitle" : "Memory", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_migration" : { - "link" : "/pve-docs/chapter-qm.html#qm_migration", - "subtitle" : "Migration", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_network_device" : { - "link" : "/pve-docs/chapter-qm.html#qm_network_device", - "subtitle" : "Network Device", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_options" : { - "link" : "/pve-docs/chapter-qm.html#qm_options", - "subtitle" : "Options", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_os_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_os_settings", - "subtitle" : "OS Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_pci_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_pci_passthrough", - "title" : "PCI(e) Passthrough" - }, - "qm_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-qm.html#qm_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Virtual Machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_system_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_system_settings", - "subtitle" : "System Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_usb_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_usb_passthrough", - "subtitle" : "USB Passthrough", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_virtual_machines_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_virtual_machines_settings", - "subtitle" : "Virtual Machines Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "storage_cephfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cephfs", - "title" : "Ceph Filesystem (CephFS)" - }, - "storage_cifs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cifs", - "title" : "CIFS Backend" - }, - "storage_directory" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_directory", - "title" : "Directory Backend" - }, - "storage_glusterfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_glusterfs", - "title" : "GlusterFS Backend" - }, - "storage_lvm" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvm", - "title" : "LVM Backend" - }, - "storage_lvmthin" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvmthin", - "title" : "LVM thin Backend" - }, - "storage_nfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_nfs", - "title" : "NFS Backend" - }, - "storage_open_iscsi" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_open_iscsi", - "title" : "Open-iSCSI initiator" - }, - "storage_zfspool" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_zfspool", - "title" : "Local ZFS Pool Backend" - }, - "sysadmin_certificate_management" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_certificate_management", - "title" : "Certificate Management" - }, - "sysadmin_network_configuration" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_network_configuration", - "title" : "Network Configuration" - } -}; -Ext.ns('PVE'); - -// 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)) { - var console = { - log: function() {} - }; -} -console.log("Starting PVE Manager"); - -Ext.Ajax.defaultHeaders = { - 'Accept': 'application/json' -}; - -/*jslint confusion: true */ -Ext.define('PVE.Utils', { utilities: { - - // this singleton contains miscellaneous utilities - - toolkit: undefined, // (extjs|touch), set inside Toolkit.js - - bus_match: /^(ide|sata|virtio|scsi)\d+$/, - - log_severity_hash: { - 0: "panic", - 1: "alert", - 2: "critical", - 3: "error", - 4: "warning", - 5: "notice", - 6: "info", - 7: "debug" - }, - - support_level_hash: { - 'c': gettext('Community'), - 'b': gettext('Basic'), - 's': gettext('Standard'), - 'p': gettext('Premium') - }, - - noSubKeyHtml: 'You do not have a valid subscription for this server. Please visit www.proxmox.com to get a list of available options.', - - kvm_ostypes: { - 'Linux': [ - { desc: '4.X/3.X/2.6 Kernel', val: 'l26' }, - { desc: '2.4 Kernel', val: 'l24' } - ], - 'Microsoft Windows': [ - { desc: '10/2016', val: 'win10' }, - { desc: '8.x/2012/2012r2', val: 'win8' }, - { desc: '7/2008r2', val: 'win7' }, - { desc: 'Vista/2008', val: 'w2k8' }, - { desc: 'XP/2003', val: 'wxp' }, - { desc: '2000', val: 'w2k' } - ], - 'Solaris Kernel': [ - { desc: '-', val: 'solaris'} - ], - 'Other': [ - { desc: '-', val: 'other'} - ] - }, - - get_health_icon: function(state, circle) { - if (circle === undefined) { - circle = false; - } - - if (state === undefined) { - state = 'uknown'; - } - - var icon = 'faded fa-question'; - switch(state) { - case 'good': - icon = 'good fa-check'; - break; - case 'warning': - icon = 'warning fa-exclamation'; - break; - case 'critical': - icon = 'critical fa-times'; - break; - default: break; - } - - if (circle) { - icon += '-circle'; - } - - return icon; - }, - - map_ceph_health: { - 'HEALTH_OK':'good', - 'HEALTH_WARN':'warning', - 'HEALTH_ERR':'critical' - }, - - render_ceph_health: function(healthObj) { - var state = { - iconCls: PVE.Utils.get_health_icon(), - text: '' - }; - - if (!healthObj || !healthObj.status) { - return state; - } - - var health = PVE.Utils.map_ceph_health[healthObj.status]; - - state.iconCls = PVE.Utils.get_health_icon(health, true); - state.text = healthObj.status; - - return state; - }, - - render_zfs_health: function(value) { - if (typeof value == 'undefined'){ - return ""; - } - var iconCls = 'question-circle'; - switch (value) { - case 'AVAIL': - case 'ONLINE': - iconCls = 'check-circle good'; - break; - case 'REMOVED': - case 'DEGRADED': - iconCls = 'exclamation-circle warning'; - break; - case 'UNAVAIL': - case 'FAULTED': - case 'OFFLINE': - iconCls = 'times-circle critical'; - break; - default: //unknown - } - - return ' ' + value; - - }, - - get_kvm_osinfo: function(value) { - var info = { base: 'Other' }; // default - if (value) { - Ext.each(Object.keys(PVE.Utils.kvm_ostypes), function(k) { - Ext.each(PVE.Utils.kvm_ostypes[k], function(e) { - if (e.val === value) { - info = { desc: e.desc, base: k }; - } - }); - }); - } - return info; - }, - - render_kvm_ostype: function (value) { - var osinfo = PVE.Utils.get_kvm_osinfo(value); - if (osinfo.desc && osinfo.desc !== '-') { - return osinfo.base + ' ' + osinfo.desc; - } else { - return osinfo.base; - } - }, - - render_hotplug_features: function (value) { - var fa = []; - - if (!value || (value === '0')) { - return gettext('Disabled'); - } - - if (value === '1') { - value = 'disk,network,usb'; - } - - Ext.each(value.split(','), function(el) { - if (el === 'disk') { - fa.push(gettext('Disk')); - } else if (el === 'network') { - fa.push(gettext('Network')); - } else if (el === 'usb') { - fa.push('USB'); - } else if (el === 'memory') { - fa.push(gettext('Memory')); - } else if (el === 'cpu') { - fa.push(gettext('CPU')); - } else { - fa.push(el); - } - }); - - return fa.join(', '); - }, - - render_qga_features: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (' + Proxmox.Utils.disabledText + ')'; - } - var props = PVE.Parser.parsePropertyString(value, 'enabled'); - if (!PVE.Parser.parseBoolean(props.enabled)) { - return Proxmox.Utils.disabledText; - } - - delete props.enabled; - var agentstring = Proxmox.Utils.enabledText; - - Ext.Object.each(props, function(key, value) { - var keystring = '' ; - agentstring += ', ' + key + ': '; - - if (PVE.Parser.parseBoolean(value)) { - agentstring += Proxmox.Utils.enabledText; - } else { - agentstring += Proxmox.Utils.disabledText; - } - }); - - return agentstring; - }, - - render_qemu_machine: function(value) { - return value || (Proxmox.Utils.defaultText + ' (i440fx)'); - }, - - render_qemu_bios: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (SeaBIOS)'; - } else if (value === 'seabios') { - return "SeaBIOS"; - } else if (value === 'ovmf') { - return "OVMF (UEFI)"; - } else { - return value; - } - }, - - render_dc_ha_opts: function(value) { - if (!value) { - return Proxmox.Utils.defaultText; - } else { - return PVE.Parser.printPropertyString(value); - } - }, - render_as_property_string: function(value) { - return (!value) ? Proxmox.Utils.defaultText - : PVE.Parser.printPropertyString(value); - }, - - render_scsihw: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (LSI 53C895A)'; - } else if (value === 'lsi') { - return 'LSI 53C895A'; - } else if (value === 'lsi53c810') { - return 'LSI 53C810'; - } else if (value === 'megasas') { - return 'MegaRAID SAS 8708EM2'; - } else if (value === 'virtio-scsi-pci') { - return 'VirtIO SCSI'; - } else if (value === 'virtio-scsi-single') { - return 'VirtIO SCSI single'; - } else if (value === 'pvscsi') { - return 'VMware PVSCSI'; - } else { - return value; - } - }, - - // fixme: auto-generate this - // for now, please keep in sync with PVE::Tools::kvmkeymaps - kvm_keymaps: { - //ar: 'Arabic', - da: 'Danish', - de: 'German', - 'de-ch': 'German (Swiss)', - 'en-gb': 'English (UK)', - 'en-us': 'English (USA)', - es: 'Spanish', - //et: 'Estonia', - fi: 'Finnish', - //fo: 'Faroe Islands', - fr: 'French', - 'fr-be': 'French (Belgium)', - 'fr-ca': 'French (Canada)', - 'fr-ch': 'French (Swiss)', - //hr: 'Croatia', - hu: 'Hungarian', - is: 'Icelandic', - it: 'Italian', - ja: 'Japanese', - lt: 'Lithuanian', - //lv: 'Latvian', - mk: 'Macedonian', - nl: 'Dutch', - //'nl-be': 'Dutch (Belgium)', - no: 'Norwegian', - pl: 'Polish', - pt: 'Portuguese', - 'pt-br': 'Portuguese (Brazil)', - //ru: 'Russian', - sl: 'Slovenian', - sv: 'Swedish', - //th: 'Thai', - tr: 'Turkish' - }, - - kvm_vga_drivers: { - std: gettext('Standard VGA'), - vmware: gettext('VMware compatible'), - qxl: 'SPICE', - qxl2: 'SPICE dual monitor', - qxl3: 'SPICE three monitors', - qxl4: 'SPICE four monitors', - serial0: gettext('Serial terminal') + ' 0', - serial1: gettext('Serial terminal') + ' 1', - serial2: gettext('Serial terminal') + ' 2', - serial3: gettext('Serial terminal') + ' 3', - virtio: 'VirtIO-GPU', - none: Proxmox.Utils.noneText - }, - - render_kvm_language: function (value) { - if (!value || value === '__default__') { - return Proxmox.Utils.defaultText; - } - var text = PVE.Utils.kvm_keymaps[value]; - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_keymap_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_language('')]]; - Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) { - data.push([key, PVE.Utils.render_kvm_language(value)]); - }); - - return data; - }, - - console_map: { - '__default__': Proxmox.Utils.defaultText + ' (HTML5)', - 'vv': 'SPICE (remote-viewer)', - 'html5': 'HTML5 (noVNC)', - 'xtermjs': 'xterm.js' - }, - - render_console_viewer: function(value) { - value = value || '__default__'; - if (PVE.Utils.console_map[value]) { - return PVE.Utils.console_map[value]; - } - return value; - }, - - console_viewer_array: function() { - return Ext.Array.map(Object.keys(PVE.Utils.console_map), function(v) { - return [v, PVE.Utils.render_console_viewer(v)]; - }); - }, - - render_kvm_vga_driver: function (value) { - if (!value) { - return Proxmox.Utils.defaultText; - } - var vga = PVE.Parser.parsePropertyString(value, 'type'); - var text = PVE.Utils.kvm_vga_drivers[vga.type]; - if (!vga.type) { - text = Proxmox.Utils.defaultText; - } - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_vga_driver_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_vga_driver('')]]; - Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) { - data.push([key, PVE.Utils.render_kvm_vga_driver(value)]); - }); - - return data; - }, - - render_kvm_startup: function(value) { - var startup = PVE.Parser.parseStartup(value); - - var res = 'order='; - if (startup.order === undefined) { - res += 'any'; - } else { - res += startup.order; - } - if (startup.up !== undefined) { - res += ',up=' + startup.up; - } - if (startup.down !== undefined) { - res += ',down=' + startup.down; - } - - return res; - }, - - extractFormActionError: function(action) { - var msg; - switch (action.failureType) { - case Ext.form.action.Action.CLIENT_INVALID: - msg = gettext('Form fields may not be submitted with invalid values'); - break; - case Ext.form.action.Action.CONNECT_FAILURE: - msg = gettext('Connection error'); - var resp = action.response; - if (resp.status && resp.statusText) { - msg += " " + resp.status + ": " + resp.statusText; - } - break; - case Ext.form.action.Action.LOAD_FAILURE: - case Ext.form.action.Action.SERVER_INVALID: - msg = Proxmox.Utils.extractRequestError(action.result, true); - break; - } - return msg; - }, - - format_duration_short: function(ut) { - - if (ut < 60) { - return ut.toFixed(1) + 's'; - } - - if (ut < 3600) { - var mins = ut / 60; - return mins.toFixed(1) + 'm'; - } - - if (ut < 86400) { - var hours = ut / 3600; - return hours.toFixed(1) + 'h'; - } - - var days = ut / 86400; - return days.toFixed(1) + 'd'; - }, - - contentTypes: { - 'images': gettext('Disk image'), - 'backup': gettext('VZDump backup file'), - 'vztmpl': gettext('Container template'), - 'iso': gettext('ISO image'), - 'rootdir': gettext('Container'), - 'snippets': gettext('Snippets') - }, - - storageSchema: { - dir: { - name: Proxmox.Utils.directoryText, - ipanel: 'DirInputPanel', - faIcon: 'folder' - }, - lvm: { - name: 'LVM', - ipanel: 'LVMInputPanel', - faIcon: 'folder' - }, - lvmthin: { - name: 'LVM-Thin', - ipanel: 'LvmThinInputPanel', - faIcon: 'folder' - }, - nfs: { - name: 'NFS', - ipanel: 'NFSInputPanel', - faIcon: 'building' - }, - cifs: { - name: 'CIFS', - ipanel: 'CIFSInputPanel', - faIcon: 'building' - }, - glusterfs: { - name: 'GlusterFS', - ipanel: 'GlusterFsInputPanel', - faIcon: 'building' - }, - iscsi: { - name: 'iSCSI', - ipanel: 'IScsiInputPanel', - faIcon: 'building' - }, - sheepdog: { - name: 'Sheepdog', - ipanel: 'SheepdogInputPanel', - hideAdd: true, - faIcon: 'building' - }, - cephfs: { - name: 'CephFS', - ipanel: 'CephFSInputPanel', - faIcon: 'building' - }, - pvecephfs: { - name: 'CephFS (PVE)', - ipanel: 'CephFSInputPanel', - hideAdd: true, - faIcon: 'building' - }, - rbd: { - name: 'RBD', - ipanel: 'RBDInputPanel', - faIcon: 'building' - }, - pveceph: { - name: 'RBD (PVE)', - ipanel: 'RBDInputPanel', - hideAdd: true, - faIcon: 'building' - }, - zfs: { - name: 'ZFS over iSCSI', - ipanel: 'ZFSInputPanel', - faIcon: 'building' - }, - zfspool: { - name: 'ZFS', - ipanel: 'ZFSPoolInputPanel', - faIcon: 'folder' - }, - drbd: { - name: 'DRBD', - hideAdd: true - } - }, - - format_storage_type: function(value, md, record) { - if (value === 'rbd') { - value = (!record || record.get('monhost') ? 'rbd' : 'pveceph'); - } else if (value === 'cephfs') { - value = (!record || record.get('monhost') ? 'cephfs' : 'pvecephfs'); - } - - var schema = PVE.Utils.storageSchema[value]; - if (schema) { - return schema.name; - } - return Proxmox.Utils.unknownText; - }, - - format_ha: function(value) { - var text = Proxmox.Utils.noneText; - - if (value.managed) { - text = value.state || Proxmox.Utils.noneText; - - text += ', ' + Proxmox.Utils.groupText + ': '; - text += value.group || Proxmox.Utils.noneText; - } - - return text; - }, - - format_content_types: function(value) { - return value.split(',').sort().map(function(ct) { - return PVE.Utils.contentTypes[ct] || ct; - }).join(', '); - }, - - render_storage_content: function(value, metaData, record) { - var data = record.data; - if (Ext.isNumber(data.channel) && - Ext.isNumber(data.id) && - Ext.isNumber(data.lun)) { - return "CH " + - Ext.String.leftPad(data.channel,2, '0') + - " ID " + data.id + " LUN " + data.lun; - } - return data.volid.replace(/^.*:(.*\/)?/,''); - }, - - render_serverity: function (value) { - return PVE.Utils.log_severity_hash[value] || value; - }, - - render_cpu: function(value, metaData, record, rowIndex, colIndex, store) { - - if (!(record.data.uptime && Ext.isNumeric(value))) { - return ''; - } - - var maxcpu = record.data.maxcpu || 1; - - if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) { - return ''; - } - - var per = value * 100; - - return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU'); - }, - - render_size: function(value, metaData, record, rowIndex, colIndex, store) { - /*jslint confusion: true */ - - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value); - }, - - render_bandwidth: function(value) { - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value) + '/s'; - }, - - render_timestamp_human_readable: function(value) { - return Ext.Date.format(new Date(value * 1000), 'l d F Y H:i:s'); - }, - - render_duration: function(value) { - if (value === undefined) { - return '-'; - } - return PVE.Utils.format_duration_short(value); - }, - - calculate_mem_usage: function(data) { - if (!Ext.isNumeric(data.mem) || - data.maxmem === 0 || - data.uptime < 1) { - return -1; - } - - return (data.mem / data.maxmem); - }, - - render_mem_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - if (value > 1 ) { - // we got no percentage but bytes - var mem = value; - var maxmem = record.data.maxmem; - if (!record.data.uptime || - maxmem === 0 || - !Ext.isNumeric(mem)) { - return ''; - } - - return ((mem*100)/maxmem).toFixed(1) + " %"; - } - return (value*100).toFixed(1) + " %"; - }, - - render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var mem = value; - var maxmem = record.data.maxmem; - - if (!record.data.uptime) { - return ''; - } - - if (!(Ext.isNumeric(mem) && maxmem)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - calculate_disk_usage: function(data) { - - if (!Ext.isNumeric(data.disk) || - data.type === 'qemu' || - (data.type === 'lxc' && data.uptime === 0) || - data.maxdisk === 0) { - return -1; - } - - return (data.disk / data.maxdisk); - }, - - render_disk_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - - return (value * 100).toFixed(1) + " %"; - }, - - render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var disk = value; - var maxdisk = record.data.maxdisk; - var type = record.data.type; - - if (!Ext.isNumeric(disk) || - type === 'qemu' || - maxdisk === 0 || - (type === 'lxc' && record.data.uptime === 0)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - get_object_icon_class: function(type, record) { - var status = ''; - var objType = type; - - if (type === 'type') { - // for folder view - objType = record.groupbyid; - } else if (record.template) { - // templates - objType = 'template'; - status = type; - } else { - // everything else - status = record.status + ' ha-' + record.hastate; - } - - var defaults = PVE.tree.ResourceTree.typeDefaults[objType]; - if (defaults && defaults.iconCls) { - var retVal = defaults.iconCls + ' ' + status; - return retVal; - } - - return ''; - }, - - render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) { - - var cls = PVE.Utils.get_object_icon_class(value,record.data); - - var fa = ' '; - return fa + value; - }, - - render_support_level: function(value, metaData, record) { - return PVE.Utils.support_level_hash[value] || '-'; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - /* render functions for new status panel */ - - render_usage: function(val) { - return (val*100).toFixed(2) + '%'; - }, - - render_cpu_usage: function(val, max) { - return Ext.String.format(gettext('{0}% of {1}') + - ' ' + gettext('CPU(s)'), (val*100).toFixed(2), max); - }, - - render_size_usage: function(val, max) { - if (max === 0) { - return gettext('N/A'); - } - return (val*100/max).toFixed(2) + '% '+ '(' + - Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(val), PVE.Utils.render_size(max)) + ')'; - }, - - /* this is different for nodes */ - render_node_cpu_usage: function(value, record) { - return PVE.Utils.render_cpu_usage(value, record.cpus); - }, - - /* this is different for nodes */ - render_node_size_usage: function(record) { - return PVE.Utils.render_size_usage(record.used, record.total); - }, - - render_optional_url: function(value) { - var match; - if (value && (match = value.match(/^https?:\/\//)) !== null) { - return '' + value + ''; - } - return value; - }, - - render_san: function(value) { - var names = []; - if (Ext.isArray(value)) { - value.forEach(function(val) { - if (!Ext.isNumber(val)) { - names.push(val); - } - }); - return names.join('
'); - } - return value; - }, - - render_full_name: function(firstname, metaData, record) { - var first = firstname || ''; - var last = record.data.lastname || ''; - return Ext.htmlEncode(first + " " + last); - }, - - render_u2f_error: function(error) { - var ErrorNames = { - '1': gettext('Other Error'), - '2': gettext('Bad Request'), - '3': gettext('Configuration Unsupported'), - '4': gettext('Device Ineligible'), - '5': gettext('Timeout') - }; - return "U2F Error: " + ErrorNames[error] || Proxmox.Utils.unknownText; - }, - - windowHostname: function() { - return window.location.hostname.replace(Proxmox.Utils.IP6_bracket_match, - function(m, addr, offset, original) { return addr; }); - }, - - openDefaultConsoleWindow: function(consoles, vmtype, vmid, nodename, vmname, cmd) { - var dv = PVE.Utils.defaultViewer(consoles); - PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname, cmd); - }, - - openConsoleWindow: function(viewer, vmtype, vmid, nodename, vmname, cmd) { - // kvm, lxc, shell, upgrade - - if (vmid == undefined && (vmtype === 'kvm' || vmtype === 'lxc')) { - throw "missing vmid"; - } - - if (!nodename) { - throw "no nodename specified"; - } - - if (viewer === 'html5') { - PVE.Utils.openVNCViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'xtermjs') { - Proxmox.Utils.openXtermJsViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'vv') { - var url; - var params = { proxy: PVE.Utils.windowHostname() }; - if (vmtype === 'kvm') { - url = '/nodes/' + nodename + '/qemu/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'lxc') { - url = '/nodes/' + nodename + '/lxc/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'shell') { - url = '/nodes/' + nodename + '/spiceshell'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'upgrade') { - url = '/nodes/' + nodename + '/spiceshell'; - params.upgrade = 1; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'cmd') { - url = '/nodes/' + nodename + '/spiceshell'; - params.cmd = cmd; - PVE.Utils.openSpiceViewer(url, params); - } - } else { - throw "unknown viewer type"; - } - }, - - defaultViewer: function(consoles) { - - var allowSpice, allowXtermjs; - - if (consoles === true) { - allowSpice = true; - allowXtermjs = true; - } else if (typeof consoles === 'object') { - allowSpice = consoles.spice; - allowXtermjs = !!consoles.xtermjs; - } - var vncdefault = 'html5'; - var dv = PVE.VersionInfo.console || vncdefault; - if ((dv === 'vv' && !allowSpice) || (dv === 'xtermjs' && !allowXtermjs)) { - dv = vncdefault; - } - - return dv; - }, - - openVNCViewer: function(vmtype, vmid, nodename, vmname, cmd) { - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - novnc: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - resize: 'off', - cmd: cmd - }); - var nw = window.open("?" + url, '_blank', "innerWidth=745,innerheight=427"); - if (nw) { - nw.focus(); - } - }, - - openSpiceViewer: function(url, params){ - - var downloadWithName = function(uri, name) { - var link = Ext.DomHelper.append(document.body, { - tag: 'a', - href: uri, - css : 'display:none;visibility:hidden;height:0px;' - }); - - // Note: we need to tell android the correct file name extension - // but we do not set 'download' tag for other environments, because - // It can have strange side effects (additional user prompt on firefox) - var andriod = navigator.userAgent.match(/Android/i) ? true : false; - if (andriod) { - link.download = name; - } - - if (link.fireEvent) { - link.fireEvent('onclick'); - } else { - var evt = document.createEvent("MouseEvents"); - evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null); - link.dispatchEvent(evt); - } - }; - - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - failure: function(response, opts){ - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts){ - var raw = "[virt-viewer]\n"; - Ext.Object.each(response.result.data, function(k, v) { - raw += k + "=" + v + "\n"; - }); - var url = 'data:application/x-virt-viewer;charset=UTF-8,' + - encodeURIComponent(raw); - - downloadWithName(url, "pve-spice.vv"); - } - }); - }, - - openTreeConsole: function(tree, record, item, index, e) { - e.stopEvent(); - var nodename = record.data.node; - var vmid = record.data.vmid; - var vmname = record.data.name; - if (record.data.type === 'qemu' && !record.data.template) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var allowSpice = !!response.result.data.spice; - PVE.Utils.openDefaultConsoleWindow(allowSpice, 'kvm', vmid, nodename, vmname); - } - }); - } else if (record.data.type === 'lxc' && !record.data.template) { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - }, - - // test automation helper - call_menu_handler: function(menu, text) { - - var list = menu.query('menuitem'); - - Ext.Array.each(list, function(item) { - if (item.text === text) { - if (item.handler) { - item.handler(); - return 1; - } else { - return undefined; - } - } - }); - }, - - createCmdMenu: function(v, record, item, index, event) { - event.stopEvent(); - if (!(v instanceof Ext.tree.View)) { - v.select(record); - } - var menu; - var template = !!record.data.template; - var type = record.data.type; - - if (template) { - if (type === 'qemu' || type == 'lxc') { - menu = Ext.create('PVE.menu.TemplateMenu', { - pveSelNode: record - }); - } - } else if (type === 'qemu' || - type === 'lxc' || - type === 'node') { - menu = Ext.create('PVE.' + type + '.CmdMenu', { - pveSelNode: record, - nodename: record.data.node - }); - } else { - return; - } - - menu.showAt(event.getXY()); - return menu; - }, - - // helper for deleting field which are set to there default values - delete_if_default: function(values, fieldname, default_val, create) { - if (values[fieldname] === '' || values[fieldname] === default_val) { - if (!create) { - if (values['delete']) { - values['delete'] += ',' + fieldname; - } else { - values['delete'] = fieldname; - } - } - - delete values[fieldname]; - } - }, - - loadSSHKeyFromFile: function(file, callback) { - // ssh-keygen produces 740 bytes for an average 4096 bit rsa key, with - // a user@host comment, 1420 for 8192 bits; current max is 16kbit - // assume: 740*8 for max. 32kbit (5920 byte file) - // round upwards to nearest nice number => 8192 bytes, leaves lots of comment space - if (file.size > 8192) { - Ext.Msg.alert(gettext('Error'), gettext("Invalid file size: ") + file.size); - return; - } - /*global - FileReader - */ - var reader = new FileReader(); - reader.onload = function(evt) { - callback(evt.target.result); - }; - reader.readAsText(file); - }, - - bus_counts: { ide: 4, sata: 6, scsi: 16, virtio: 16 }, - - // types is either undefined (all busses), an array of busses, or a single bus - forEachBus: function(types, func) { - var busses = Object.keys(PVE.Utils.bus_counts); - var i, j, count, cont; - - if (Ext.isArray(types)) { - busses = types; - } else if (Ext.isDefined(types)) { - busses = [ types ]; - } - - // check if we only have valid busses - for (i = 0; i < busses.length; i++) { - if (!PVE.Utils.bus_counts[busses[i]]) { - throw "invalid bus: '" + busses[i] + "'"; - } - } - - for (i = 0; i < busses.length; i++) { - count = PVE.Utils.bus_counts[busses[i]]; - for (j = 0; j < count; j++) { - cont = func(busses[i], j); - if (!cont && cont !== undefined) { - return; - } - } - } - }, - - mp_counts: { mps: 256, unused: 256 }, - - forEachMP: function(func, includeUnused) { - var i, cont; - for (i = 0; i < PVE.Utils.mp_counts.mps; i++) { - cont = func('mp', i); - if (!cont && cont !== undefined) { - return; - } - } - - if (!includeUnused) { - return; - } - - for (i = 0; i < PVE.Utils.mp_counts.unused; i++) { - cont = func('unused', i); - if (!cont && cont !== undefined) { - return; - } - } - }, - - cleanEmptyObjectKeys: function (obj) { - var propName; - for (propName in obj) { - if (obj.hasOwnProperty(propName)) { - if (obj[propName] === null || obj[propName] === undefined) { - delete obj[propName]; - } - } - } - }, - - handleStoreErrorOrMask: function(me, store, regex, callback) { - - me.mon(store, 'load', function (proxy, response, success, operation) { - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - var msg; - - if (operation.error.statusText) { - if (operation.error.statusText.match(regex)) { - callback(me, operation.error); - return; - } else { - msg = operation.error.statusText + ' (' + operation.error.status + ')'; - } - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - showCephInstallOrMask: function(container, msg, nodename, callback){ - var regex = new RegExp("not (installed|initialized)", "i"); - if (msg.match(regex)) { - if (Proxmox.UserName === 'root@pam') { - container.el.mask(); - if (!container.down('pveCephInstallWindow')){ - var isInstalled = msg.match(/not initialized/i) ? true : false; - var win = Ext.create('PVE.ceph.Install', { - nodename: nodename - }); - win.getViewModel().set('isInstalled', isInstalled); - container.add(win); - win.show(); - callback(win); - } - } else { - container.mask(Ext.String.format(gettext('{0} not installed.') + - ' ' + gettext('Log in as root to install.'), 'Ceph'), ['pve-static-mask']); - } - return true; - } else { - return false; - } - } -}, - - singleton: true, - constructor: function() { - var me = this; - Ext.apply(me, me.utilities); - } - -}); - -// ExtJS related things - -Proxmox.Utils.toolkit = 'extjs'; - -// custom PVE specific VTypes -Ext.apply(Ext.form.field.VTypes, { - - 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"', - IP64AddressList: function(v) { - var list = v.split(/[\ \,\;]+/); - var i; - for (i = 0; i < list.length; i++) { - if (list[i] == '') { - continue; - } - - if (!Proxmox.Utils.IP64_match.test(list[i])) { - return false; - } - } - - return true; - }, - IP64AddressListText: gettext('Example') + ': 192.168.1.1,192.168.1.2', - IP64AddressListMask: /[A-Fa-f0-9\,\:\.\;\ ]/ -}); - -Ext.define('PVE.form.field.Display', { - override: 'Ext.form.field.Display', - - setSubmitValue: function(value) { - // do nothing, this is only to allow generalized bindings for the: - // `me.isCreate ? 'textfield' : 'displayfield'` cases we have. - } -}); -// Some configuration values are complex strings - -// so we need parsers/generators for them. - -Ext.define('PVE.Parser', { statics: { - - // this class only contains static functions - - parseACME: function(value) { - if (!value) { - return; - } - - var res = {}; - var errors = false; - - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; //continue - } - - var match_res; - if ((match_res = p.match(/^(?:domains=)?((?:[a-zA-Z0-9\-\.]+[;, ]?)+)$/)) !== null) { - res.domains = match_res[1].split(/[;, ]/); - } else { - errors = true; - return false; - } - }); - - if (errors || !res) { - return; - } - - return res; - }, - - parseBoolean: function(value, default_value) { - if (!Ext.isDefined(value)) { - return default_value; - } - value = value.toLowerCase(); - return value === '1' || - value === 'on' || - value === 'yes' || - value === 'true'; - }, - - parsePropertyString: function(value, defaultKey) { - var res = {}, - error; - - Ext.Array.each(value.split(','), function(p) { - var kv = p.split('=', 2); - if (Ext.isDefined(kv[1])) { - res[kv[0]] = kv[1]; - } else if (Ext.isDefined(defaultKey)) { - if (Ext.isDefined(res[defaultKey])) { - error = 'defaultKey may be only defined once in propertyString'; - return false; // break - } - res[defaultKey] = kv[0]; - } else { - error = 'invalid propertyString, not a key=value pair and no defaultKey defined'; - return false; // break - } - }); - - if (error !== undefined) { - console.error(error); - return; - } - - return res; - }, - - printPropertyString: function(data, defaultKey) { - var stringparts = []; - - Ext.Object.each(data, function(key, value) { - if (defaultKey !== undefined && key === defaultKey) { - stringparts.unshift(value); - } else { - stringparts.push(key + '=' + value); - } - }); - - return stringparts.join(','); - }, - - parseQemuNetwork: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(ne2k_pci|e1000|e1000-82540em|e1000-82544gc|e1000-82545em|vmxnet3|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i)) !== null) { - res.model = match_res[1].toLowerCase(); - if (match_res[3]) { - res.macaddr = match_res[3]; - } - } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) { - res.bridge = match_res[1]; - } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) { - res.rate = match_res[1]; - } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) { - res.tag = match_res[1]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - res.firewall = match_res[1]; - } else if ((match_res = p.match(/^link_down=(\d+)$/)) !== null) { - res.disconnect = match_res[1]; - } else if ((match_res = p.match(/^queues=(\d+)$/)) !== null) { - res.queues = match_res[1]; - } else if ((match_res = p.match(/^trunks=(\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*)$/)) !== null) { - res.trunks = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors || !res.model) { - return; - } - - return res; - }, - - printQemuNetwork: function(net) { - - var netstr = net.model; - if (net.macaddr) { - netstr += "=" + net.macaddr; - } - if (net.bridge) { - netstr += ",bridge=" + net.bridge; - if (net.tag) { - netstr += ",tag=" + net.tag; - } - if (net.firewall) { - netstr += ",firewall=" + net.firewall; - } - } - if (net.rate) { - netstr += ",rate=" + net.rate; - } - if (net.queues) { - netstr += ",queues=" + net.queues; - } - if (net.disconnect) { - netstr += ",link_down=" + net.disconnect; - } - if (net.trunks) { - netstr += ",trunks=" + net.trunks; - } - return netstr; - }, - - parseQemuDrive: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var match_res = key.match(/^([a-z]+)(\d+)$/); - if (!match_res) { - return; - } - res['interface'] = match_res[1]; - res.index = match_res[2]; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - if (k === 'cache' && v === 'off') { - v = 'none'; - } - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - return res; - }, - - printQemuDrive: function(drive) { - - var drivestr = drive.file; - - Ext.Object.each(drive, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'index' || key === 'interface') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseIPConfig: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - if ((match_res = p.match(/^ip=(\S+)$/)) !== null) { - res.ip = match_res[1]; - } else if ((match_res = p.match(/^gw=(\S+)$/)) !== null) { - res.gw = match_res[1]; - } else if ((match_res = p.match(/^ip6=(\S+)$/)) !== null) { - res.ip6 = match_res[1]; - } else if ((match_res = p.match(/^gw6=(\S+)$/)) !== null) { - res.gw6 = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printIPConfig: function(cfg) { - var c = ""; - var str = ""; - if (cfg.ip) { - str += "ip=" + cfg.ip; - c = ","; - } - if (cfg.gw) { - str += c + "gw=" + cfg.gw; - c = ","; - } - if (cfg.ip6) { - str += c + "ip6=" + cfg.ip6; - c = ","; - } - if (cfg.gw6) { - str += c + "gw6=" + cfg.gw6; - c = ","; - } - return str; - }, - - parseOpenVZNetIf: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(';'), function(item) { - if (!item || item.match(/^\s*$/)) { - return; // continue - } - - var data = {}; - Ext.Array.each(item.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - if (match_res[1] === 'bridge'){ - var bridgevlanf = match_res[2]; - var bridge_res = bridgevlanf.match(/^(vmbr(\d+))(v(\d+))?(f)?$/); - if (!bridge_res) { - errors = true; - return false; // break - } - data.bridge = bridge_res[1]; - data.tag = bridge_res[4]; - /*jslint confusion: true*/ - data.firewall = bridge_res[5] ? 1 : 0; - /*jslint confusion: false*/ - } else { - data[match_res[1]] = match_res[2]; - } - }); - - if (errors || !data.ifname) { - errors = true; - return false; // break - } - - data.raw = item; - - res[data.ifname] = data; - }); - - return errors ? undefined: res; - }, - - printOpenVZNetIf: function(netif) { - var netarray = []; - - Ext.Object.each(netif, function(iface, data) { - var tmparray = []; - Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname' , 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) { - var value = data[key]; - if (key === 'bridge'){ - if(data.tag){ - value = value + 'v' + data.tag; - } - if (data.firewall){ - value = value + 'f'; - } - } - if (value) { - tmparray.push(key + '=' + value); - } - - }); - netarray.push(tmparray.join(',')); - }); - - return netarray.join(';'); - }, - - parseLxcNetwork: function(value) { - if (!value) { - return; - } - - var data = {}; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|tag|rate)=(\S+)$/); - if (match_res) { - data[match_res[1]] = match_res[2]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - data.firewall = PVE.Parser.parseBoolean(match_res[1]); - } else { - // todo: simply ignore errors ? - return; // continue - } - }); - - return data; - }, - - printLxcNetwork: function(data) { - var tmparray = []; - Ext.Array.each(['bridge', 'hwaddr', 'mtu', 'name', 'ip', - 'gw', 'ip6', 'gw6', 'firewall', 'tag'], function(key) { - var value = data[key]; - if (value) { - tmparray.push(key + '=' + value); - } - }); - - /*jslint confusion: true*/ - if (data.rate > 0) { - tmparray.push('rate=' + data.rate); - } - /*jslint confusion: false*/ - return tmparray.join(','); - }, - - parseLxcMountPoint: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(.+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - var m = res.file.match(/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):/i); - if (m) { - res.storage = m[1]; - res.type = 'volume'; - } else if (res.file.match(/^\/dev\//)) { - res.type = 'device'; - } else { - res.type = 'bind'; - } - - return res; - }, - - printLxcMountPoint: function(mp) { - var drivestr = mp.file; - - Ext.Object.each(mp, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'type' || key === 'storage') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseStartup: function(value) { - if (value === undefined) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) { - res.order = match_res[2]; - } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) { - res.up = match_res[1]; - } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) { - res.down = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printStartup: function(startup) { - var arr = []; - if (startup.order !== undefined && startup.order !== '') { - arr.push('order=' + startup.order); - } - if (startup.up !== undefined && startup.up !== '') { - arr.push('up=' + startup.up); - } - if (startup.down !== undefined && startup.down !== '') { - arr.push('down=' + startup.down); - } - - return arr.join(','); - }, - - parseQemuSmbios1: function(value) { - var res = {}; - - Ext.Array.each(value.split(','), function(p) { - var kva = p.split('=', 2); - res[kva[0]] = kva[1]; - }); - - return res; - }, - - printQemuSmbios1: function(data) { - - var datastr = ''; - - Ext.Object.each(data, function(key, value) { - if (value === '') { return; } - datastr += (datastr !== '' ? ',' : '') + key + '=' + value; - }); - - return datastr; - }, - - parseTfaConfig: function(value) { - var res = {}; - - Ext.Array.each(value.split(','), function(p) { - var kva = p.split('=', 2); - res[kva[0]] = kva[1]; - }); - - return res; - }, - - parseQemuCpu: function(value) { - if (!value) { - return {}; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - if (!p.match(/\=/)) { - if (Ext.isDefined(res.cpu)) { - errors = true; - return false; // break - } - res.cputype = p; - return; // continue - } - - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - - var k = match_res[1]; - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - res[k] = match_res[2]; - }); - - if (errors || !res.cputype) { - return; - } - - return res; - }, - - printQemuCpu: function(cpu) { - var cpustr = cpu.cputype; - var optstr = ''; - - Ext.Object.each(cpu, function(key, value) { - if (!Ext.isDefined(value) || key === 'cputype') { - return; // continue - } - optstr += ',' + key + '=' + value; - }); - - if (!cpustr) { - if (optstr) { - return 'kvm64' + optstr; - } - return; - } - - return cpustr + optstr; - }, - - parseSSHKey: function(key) { - // |--- options can have quotes--| type key comment - var keyre = /^(?:((?:[^\s"]|\"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/; - var typere = /^(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)$/; - - var m = key.match(keyre); - if (!m) { - return null; - } - if (m.length < 3 || !m[2]) { // [2] is always either type or key - return null; - } - if (m[1] && m[1].match(typere)) { - return { - type: m[1], - key: m[2], - comment: m[3] - }; - } - if (m[2].match(typere)) { - return { - options: m[1], - type: m[2], - key: m[3], - comment: m[4] - }; - } - return null; - } -}}); -/* This state provider keeps part of the state inside - * the browser history. - * - * We compress (shorten) url using dictionary based compression - * i.e. use column separated list instead of url encoded hash: - * #v\d* version/format - * := indicates string values - * :\d+ lookup value in dictionary hash - * #v1:=value1:5:=value2:=value3:... -*/ - -Ext.define('PVE.StateProvider', { - extend: 'Ext.state.LocalStorageProvider', - - // private - setHV: function(name, newvalue, fireEvents) { - var me = this; - - var changes = false; - var oldtext = Ext.encode(me.UIState[name]); - var newtext = Ext.encode(newvalue); - if (newtext != oldtext) { - changes = true; - me.UIState[name] = newvalue; - //console.log("changed old " + name + " " + oldtext); - //console.log("changed new " + name + " " + newtext); - if (fireEvents) { - me.fireEvent("statechange", me, name, { value: newvalue }); - } - } - return changes; - }, - - // private - hslist: [ - // order is important for notifications - // [ name, default ] - ['view', 'server'], - ['rid', 'root'], - ['ltab', 'tasks'], - ['nodetab', ''], - ['storagetab', ''], - ['pooltab', ''], - ['kvmtab', ''], - ['lxctab', ''], - ['dctab', ''] - ], - - hprefix: 'v1', - - compDict: { - cloudinit: 52, - replication: 51, - system: 50, - monitor: 49, - 'ha-fencing': 48, - 'ha-groups': 47, - 'ha-resources': 46, - 'ceph-log': 45, - 'ceph-crushmap':44, - 'ceph-pools': 43, - 'ceph-osdtree': 42, - 'ceph-disklist': 41, - 'ceph-monlist': 40, - 'ceph-config': 39, - ceph: 38, - 'firewall-fwlog': 37, - 'firewall-options': 36, - 'firewall-ipset': 35, - 'firewall-aliases': 34, - 'firewall-sg': 33, - firewall: 32, - apt: 31, - members: 30, - snapshot: 29, - ha: 28, - support: 27, - pools: 26, - syslog: 25, - ubc: 24, - initlog: 23, - openvz: 22, - backup: 21, - resources: 20, - content: 19, - root: 18, - domains: 17, - roles: 16, - groups: 15, - users: 14, - time: 13, - dns: 12, - network: 11, - services: 10, - options: 9, - console: 8, - hardware: 7, - permissions: 6, - summary: 5, - tasks: 4, - clog: 3, - storage: 2, - folder: 1, - server: 0 - }, - - decodeHToken: function(token) { - var me = this; - - var state = {}; - if (!token) { - Ext.Array.each(me.hslist, function(rec) { - state[rec[0]] = rec[1]; - }); - return state; - } - - // return Ext.urlDecode(token); - - var items = token.split(':'); - var prefix = items.shift(); - - if (prefix != me.hprefix) { - return me.decodeHToken(); - } - - Ext.Array.each(me.hslist, function(rec) { - var value = items.shift(); - if (value) { - if (value[0] === '=') { - value = decodeURIComponent(value.slice(1)); - } else { - Ext.Object.each(me.compDict, function(key, cv) { - if (value == cv) { - value = key; - return false; - } - }); - } - } - state[rec[0]] = value; - }); - - return state; - }, - - encodeHToken: function(state) { - var me = this; - - // return Ext.urlEncode(state); - - var ctoken = me.hprefix; - Ext.Array.each(me.hslist, function(rec) { - var value = state[rec[0]]; - if (!Ext.isDefined(value)) { - value = rec[1]; - } - value = encodeURIComponent(value); - if (!value) { - ctoken += ':'; - } else { - var comp = me.compDict[value]; - if (Ext.isDefined(comp)) { - ctoken += ":" + comp; - } else { - ctoken += ":=" + value; - } - } - }); - - return ctoken; - }, - - constructor: function(config){ - var me = this; - - me.callParent([config]); - - me.UIState = me.decodeHToken(); // set default - - var history_change_cb = function(token) { - //console.log("HC " + token); - if (!token) { - var res = window.confirm(gettext('Are you sure you want to navigate away from this page?')); - if (res){ - // process text value and close... - Ext.History.back(); - } else { - Ext.History.forward(); - } - return; - } - - var newstate = me.decodeHToken(token); - Ext.Array.each(me.hslist, function(rec) { - if (typeof newstate[rec[0]] == "undefined") { - return; - } - me.setHV(rec[0], newstate[rec[0]], true); - }); - }; - - var start_token = Ext.History.getToken(); - if (start_token) { - history_change_cb(start_token); - } else { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - - Ext.History.on('change', history_change_cb); - }, - - get: function(name, defaultValue){ - /*jslint confusion: true */ - var me = this; - var data; - - if (typeof me.UIState[name] != "undefined") { - data = { value: me.UIState[name] }; - } else { - data = me.callParent(arguments); - if (!data && name === 'GuiCap') { - data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {} }; - } - } - - //console.log("GET " + name + " " + Ext.encode(data)); - return data; - }, - - clear: function(name){ - var me = this; - - if (typeof me.UIState[name] != "undefined") { - me.UIState[name] = null; - } - - me.callParent(arguments); - }, - - set: function(name, value){ - var me = this; - - //console.log("SET " + name + " " + Ext.encode(value)); - if (typeof me.UIState[name] != "undefined") { - var newvalue = value ? value.value : null; - if (me.setHV(name, newvalue, false)) { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - } else { - me.callParent(arguments); - } - } -}); -Ext.define('PVE.menu.Item', { - extend: 'Ext.menu.Item', - alias: 'widget.pveMenuItem', - - // set to wrap the handler callback in a confirm dialog showing this text - confirmMsg: false, - - // set to focus 'No' instead of 'Yes' button and show a warning symbol - dangerous: false, - - initComponent: function() { - var me = this; - - if (me.handler) { - me.setHandler(me.handler, me.scope); - } - - me.callParent(); - }, - - setHandler: function(fn, scope) { - var me = this; - me.scope = scope; - me.handler = function(button, e) { - var rec, msg; - if (me.confirmMsg) { - msg = me.confirmMsg; - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn === 'yes') { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - } - }); - } else { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - }; - } -}); -Ext.define('PVE.menu.TemplateMenu', { - extend: 'Ext.menu.Menu', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var guestType = me.pveSelNode.data.type; - if (guestType !== 'qemu' && guestType != 'lxc') { - throw "invalid guest type"; - } - - var vmname = me.pveSelNode.data.name; - - var template = me.pveSelNode.data.template; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - me.title = (guestType === 'qemu' ? 'VM ' : 'CT ') + vmid; - - me.items = [ - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: guestType, - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - handler: function() { - var win = Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: template - }); - win.show(); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.button.ConsoleButton', { - extend: 'Ext.button.Split', - alias: 'widget.pveConsoleButton', - - consoleType: 'shell', // one of 'shell', 'kvm', 'lxc', 'upgrade', 'cmd' - - cmd: undefined, - - consoleName: undefined, - - iconCls: 'fa fa-terminal', - - enableSpice: true, - enableXtermjs: true, - - nodename: undefined, - - vmid: 0, - - text: gettext('Console'), - - setEnableSpice: function(enable){ - var me = this; - - me.enableSpice = enable; - me.down('#spicemenu').setDisabled(!enable); - }, - - setEnableXtermJS: function(enable){ - var me = this; - - me.enableXtermjs = enable; - me.down('#xtermjs').setDisabled(!enable); - }, - - handler: function() { - var me = this; - var consoles = { - spice: me.enableSpice, - xtermjs: me.enableXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, me.consoleType, me.vmid, - me.nodename, me.consoleName, me.cmd); - }, - - menu: [ - { - xtype:'menuitem', - text: 'noVNC', - iconCls: 'pve-itype-icon-novnc', - type: 'html5', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - xterm: 'menuitem', - itemId: 'spicemenu', - text: 'SPICE', - type: 'vv', - iconCls: 'pve-itype-icon-virt-viewer', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - text: 'xterm.js', - itemId: 'xtermjs', - iconCls: 'pve-itype-icon-xtermjs', - type: 'xtermjs', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.callParent(); - } -}); -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - * - * does this for the button and every menu item - */ -Ext.define('PVE.button.Split', { - extend: 'Ext.button.Split', - alias: 'widget.pveSplitButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - handlerWrapper: function(button, event) { - var me = this; - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - // confirMsg can be boolean or function - /*jslint confusion: true*/ - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - /*jslint confusion: false*/ - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - callback: function(btn) { - if (btn !== 'yes') { - return; - } - me.realHandler(button, event, rec); - } - }); - } else { - me.realHandler(button, event, rec); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - me.realHandler = me.handler; - me.handler = me.handlerWrapper; - } - - if (me.menu && me.menu.items) { - me.menu.items.forEach(function(item) { - if (item.handler) { - item.realHandler = item.handler; - item.handler = me.handlerWrapper; - } - - if (item.selModel) { - me.mon(item.selModel, "selectionchange", function() { - var rec = item.selModel.getSelection()[0]; - if (!rec || (item.enableFn(rec) === false )) { - item.setDisabled(true); - } else { - item.setDisabled(false); - } - }); - } - }); - } - - me.callParent(); - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); -Ext.define('PVE.controller.StorageEdit', { - extend: 'Ext.app.ViewController', - alias: 'controller.storageEdit', - control: { - 'field[name=content]': { - change: function(field, value) { - var hasBackups = Ext.Array.contains(value, 'backup'); - var maxfiles = this.lookupReference('maxfiles'); - if (!maxfiles) { - return; - } - - if (!hasBackups) { - // clear values which will never be submitted - maxfiles.reset(); - } - maxfiles.setDisabled(!hasBackups); - } - } - } -}); -Ext.define('PVE.qemu.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'suspended': - stopped = false; - suspended = true; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = "VM " + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - hidden: running || suspended, - disabled: running || suspended, - handler: function() { - vm_command('start'); - } - }, - { - text: gettext('Pause'), - iconCls: 'fa fa-fw fa-pause', - hidden: stopped || suspended, - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmpause', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend'); - }); - } - }, - { - text: gettext('Hibernate'), - iconCls: 'fa fa-fw fa-download', - hidden: stopped || suspended, - disabled: stopped || suspended, - tooltip: gettext('Suspend to disk'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmsuspend', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend', { todisk: 1 }); - }); - } - }, - { - text: gettext('Resume'), - iconCls: 'fa fa-fw fa-play', - hidden: !suspended, - handler: function() { - vm_command('resume'); - } - }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: (standalone || !caps.vms['VM.Migrate']) && !caps.vms['VM.Allocate'] && !caps.vms['VM.Clone'] - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - hidden: !caps.vms['VM.Allocate'], - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmtemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var allowSpice = response.result.data.spice; - var allowXtermjs = response.result.data.serial; - var consoles = { - spice: allowSpice, - xtermjs: allowXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, 'kvm', vmid, nodename, vmname); - } - }); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.lxc.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no CT ID specified"; - } - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/lxc/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = 'CT ' + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - disabled: running, - handler: function() { - vm_command('start'); - } - }, -// { -// text: gettext('Suspend'), -// iconCls: 'fa fa-fw fa-pause', -// hidde: suspended, -// disabled: stopped || suspended, -// handler: function() { -// var msg = Proxmox.Utils.format_task_description('vzsuspend', vmid); -// Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { -// if (btn !== 'yes') { -// return; -// } -// -// vm_command('suspend'); -// }); -// } -// }, -// { -// text: gettext('Resume'), -// iconCls: 'fa fa-fw fa-play', -// hidden: !suspended, -// handler: function() { -// vm_command('resume'); -// } -// }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: standalone || !caps.vms['VM.Migrate'] - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'lxc'); - } - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - handler: function() { - var msg = Proxmox.Utils.format_task_description('vztemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.node.CmdMenu', { - extend: 'Ext.menu.Menu', - xtype: 'nodeCmdMenu', - - showSeparator: false, - - items: [ - { - text: gettext('Create VM'), - itemId: 'createvm', - iconCls: 'fa fa-desktop', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.qemu.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { - text: gettext('Create CT'), - itemId: 'createct', - iconCls: 'fa fa-cube', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.lxc.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Bulk Start'), - itemId: 'bulkstart', - iconCls: 'fa fa-fw fa-play', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - itemId: 'bulkstop', - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - itemId: 'bulkmigrate', - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Shell'), - itemId: 'shell', - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - var me = this.up('menu'); - PVE.Utils.openDefaultConsoleWindow(true, 'shell', undefined, me.nodename, undefined); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Wake-on-LAN'), - itemId: 'wakeonlan', - iconCls: 'fa fa-fw fa-power-off', - handler: function() { - var me = this.up('menu'); - Proxmox.Utils.API2Request({ - param: {}, - url: '/nodes/' + me.nodename + '/wakeonlan', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - Ext.Msg.show({ - title: 'Success', - icon: Ext.Msg.INFO, - msg: Ext.String.format(gettext("Wake on LAN packet send for '{0}': '{1}'"), me.nodename, response.result.data) - }); - } - }); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no nodename specified'; - } - - me.title = gettext('Node') + " '" + me.nodename + "'"; - me.callParent(); - - var caps = Ext.state.Manager.get('GuiCap'); - // disable not allowed options - if (!caps.vms['VM.Allocate']) { - me.getComponent('createct').setDisabled(true); - me.getComponent('createvm').setDisabled(true); - } - - if (!caps.nodes['Sys.PowerMgmt']) { - me.getComponent('bulkstart').setDisabled(true); - me.getComponent('bulkstop').setDisabled(true); - me.getComponent('bulkmigrate').setDisabled(true); - me.getComponent('wakeonlan').setDisabled(true); - } - - if (!caps.nodes['Sys.Console']) { - me.getComponent('shell').setDisabled(true); - } - - if (me.pveSelNode.data.running) { - me.getComponent('wakeonlan').setDisabled(true); - } - } -}); -Ext.define('PVE.noVncConsole', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNoVncConsole', - - nodename: undefined, - - vmid: undefined, - - cmd: undefined, - - consoleType: undefined, // lxc, kvm, shell, cmd - - layout: 'fit', - - xtermjs: false, - - border: false, - - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.consoleType) { - throw "no console type specified"; - } - - if (!me.vmid && me.consoleType !== 'shell' && me.consoleType !== 'cmd') { - throw "no VM ID specified"; - } - - // always use same iframe, to avoid running several noVnc clients - // at same time (to avoid performance problems) - var box = Ext.create('Ext.ux.IFrame', { itemid : "vncconsole" }); - - var type = me.xtermjs ? 'xtermjs' : 'novnc'; - Ext.apply(me, { - items: box, - listeners: { - activate: function() { - var queryDict = { - console: me.consoleType, // kvm, lxc, upgrade or shell - vmid: me.vmid, - node: me.nodename, - cmd: me.cmd, - resize: 'scale' - }; - queryDict[type] = 1; - PVE.Utils.cleanEmptyObjectKeys(queryDict); - var url = '/?' + Ext.Object.toQueryString(queryDict); - box.load(url); - } - } - }); - - me.callParent(); - - me.on('afterrender', function() { - me.focus(); - }); - } -}); - -Ext.define('PVE.data.PermPathStore', { - extend: 'Ext.data.Store', - alias: 'store.pvePermPath', - fields: [ 'value' ], - autoLoad: false, - data: [ - {'value': '/'}, - {'value': '/access'}, - {'value': '/nodes'}, - {'value': '/pool'}, - {'value': '/storage'}, - {'value': '/vms'} - ], - - constructor: function(config) { - var me = this; - - config = config || {}; - - me.callParent([config]); - - me.suspendEvents(); - PVE.data.ResourceStore.each(function(record) { - switch (record.get('type')) { - case 'node': - me.add({value: '/nodes/' + record.get('text')}); - break; - - case 'qemu': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'lxc': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'storage': - me.add({value: '/storage/' + record.get('storage')}); - break; - case 'pool': - me.add({value: '/pool/' + record.get('pool')}); - break; - } - }); - me.resumeEvents(); - - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - - me.sort({ - property: 'value', - direction: 'ASC' - }); - } -}); -Ext.define('PVE.data.ResourceStore', { - extend: 'Proxmox.data.UpdateStore', - singleton: true, - - findVMID: function(vmid) { - var me = this, i; - - return (me.findExact('vmid', parseInt(vmid, 10)) >= 0); - }, - - // returns the cached data from all nodes - getNodes: function() { - var me = this; - - var nodes = []; - me.each(function(record) { - if (record.get('type') == "node") { - nodes.push( record.getData() ); - } - }); - - return nodes; - }, - - storageIsShared: function(storage_path) { - var me = this; - - var index = me.findExact('id', storage_path); - - return me.getAt(index).data.shared; - }, - - guestNode: function(vmid) { - var me = this; - - var index = me.findExact('vmid', parseInt(vmid, 10)); - - return me.getAt(index).data.node; - }, - - constructor: function(config) { - // fixme: how to avoid those warnings - /*jslint confusion: true */ - - var me = this; - - config = config || {}; - - var field_defaults = { - type: { - header: gettext('Type'), - type: 'string', - renderer: PVE.Utils.render_resource_type, - sortable: true, - hideable: false, - width: 100 - }, - id: { - header: 'ID', - type: 'string', - hidden: true, - sortable: true, - width: 80 - }, - running: { - header: gettext('Online'), - type: 'boolean', - renderer: Proxmox.Utils.format_boolean, - hidden: true, - convert: function(value, record) { - var info = record.data; - return (Ext.isNumeric(info.uptime) && (info.uptime > 0)); - } - }, - text: { - header: gettext('Description'), - type: 'string', - sortable: true, - width: 200, - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - - if (Ext.isNumeric(info.vmid) && info.vmid > 0) { - text = String(info.vmid); - if (info.name) { - text += " (" + info.name + ')'; - } - } else { // node, pool, storage - text = info[info.type] || info.id; - if (info.node && info.type !== 'node') { - text += " (" + info.node + ")"; - } - } - - return text; - } - }, - vmid: { - header: 'VMID', - type: 'integer', - hidden: true, - sortable: true, - width: 80 - }, - name: { - header: gettext('Name'), - hidden: true, - sortable: true, - type: 'string' - }, - disk: { - header: gettext('Disk usage'), - type: 'integer', - renderer: PVE.Utils.render_disk_usage, - sortable: true, - width: 100, - hidden: true - }, - diskuse: { - header: gettext('Disk usage') + " %", - type: 'number', - sortable: true, - renderer: PVE.Utils.render_disk_usage_percent, - width: 100, - calculate: PVE.Utils.calculate_disk_usage, - sortType: 'asFloat' - }, - maxdisk: { - header: gettext('Disk size'), - type: 'integer', - renderer: PVE.Utils.render_size, - sortable: true, - hidden: true, - width: 100 - }, - mem: { - header: gettext('Memory usage'), - type: 'integer', - renderer: PVE.Utils.render_mem_usage, - sortable: true, - hidden: true, - width: 100 - }, - memuse: { - header: gettext('Memory usage') + " %", - type: 'number', - renderer: PVE.Utils.render_mem_usage_percent, - calculate: PVE.Utils.calculate_mem_usage, - sortType: 'asFloat', - sortable: true, - width: 100 - }, - maxmem: { - header: gettext('Memory size'), - type: 'integer', - renderer: PVE.Utils.render_size, - hidden: true, - sortable: true, - width: 100 - }, - cpu: { - header: gettext('CPU usage'), - type: 'float', - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100 - }, - maxcpu: { - header: gettext('maxcpu'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - diskread: { - header: gettext('Total Disk Read'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - diskwrite: { - header: gettext('Total Disk Write'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netin: { - header: gettext('Total NetIn'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netout: { - header: gettext('Total NetOut'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - template: { - header: gettext('Template'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - uptime: { - header: gettext('Uptime'), - type: 'integer', - renderer: Proxmox.Utils.render_uptime, - sortable: true, - width: 110 - }, - node: { - header: gettext('Node'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - storage: { - header: gettext('Storage'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - pool: { - header: gettext('Pool'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - hastate: { - header: gettext('HA State'), - type: 'string', - defaultValue: 'unmanaged', - hidden: true, - sortable: true - }, - status: { - header: gettext('Status'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - } - }; - - var fields = []; - var fieldNames = []; - Ext.Object.each(field_defaults, function(key, value) { - var field = {name: key, type: value.type}; - if (Ext.isDefined(value.convert)) { - field.convert = value.convert; - } - - if (Ext.isDefined(value.calculate)) { - field.calculate = value.calculate; - } - - if (Ext.isDefined(value.defaultValue)) { - field.defaultValue = value.defaultValue; - } - - fields.push(field); - fieldNames.push(key); - }); - - Ext.define('PVEResources', { - extend: "Ext.data.Model", - fields: fields, - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/resources' - } - }); - - Ext.define('PVETree', { - extend: "Ext.data.Model", - fields: fields, - proxy: { type: 'memory' } - }); - - Ext.apply(config, { - storeid: 'PVEResources', - model: 'PVEResources', - defaultColumns: function() { - var res = []; - Ext.Object.each(field_defaults, function(field, info) { - var fi = Ext.apply({ dataIndex: field }, info); - res.push(fi); - }); - return res; - }, - fieldNames: fieldNames - }); - - me.callParent([config]); - } -}); -Ext.define('pve-domains', { - extend: "Ext.data.Model", - fields: [ - 'realm', 'type', 'comment', 'default', 'tfa', - { - name: 'descr', - // Note: We use this in the RealmComboBox.js (see Bug #125) - convert: function(value, record) { - if (value) { - return value; - } - - var info = record.data; - // return realm if there is no comment - var text = info.comment || info.realm; - - if (info.tfa) { - text += " (+ " + info.tfa + ")"; - } - - return Ext.String.htmlEncode(text); - } - } - ], - idProperty: 'realm', - proxy: { - type: 'proxmox', - url: "/api2/json/access/domains" - } -}); -Ext.define('pve-rrd-node', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - { - name:'iowait', - // percentage - convert: function(value) { - return value*100; - } - }, - 'loadavg', - 'maxcpu', - 'memtotal', - 'memused', - 'netin', - 'netout', - 'roottotal', - 'rootused', - 'swaptotal', - 'swapused', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-guest', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - 'maxcpu', - 'netin', - 'netout', - 'mem', - 'maxmem', - 'disk', - 'maxdisk', - 'diskread', - 'diskwrite', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-storage', { - extend: 'Ext.data.Model', - fields: [ - 'used', - 'total', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); -Ext.define('PVE.form.VlanField', { - extend: 'Ext.form.field.Number', - alias: ['widget.pveVlanField'], - - deleteEmpty: false, - - emptyText: 'no VLAN', - - fieldLabel: gettext('VLAN Tag'), - - allowBlank: true, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val) { - data = {}; - data[me.getName()] = val; - } else if (me.deleteEmpty) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - minValue: 1, - maxValue: 4094 - }); - - me.callParent(); - } -}); -// boolean type including 'Default' (delete property from file) -Ext.define('PVE.form.Boolean', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.booleanfield'], - comboItems: [ - ['__default__', gettext('Default')], - [1, gettext('Yes')], - [0, gettext('No')] - ] -}); -Ext.define('PVE.form.CompressionSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveCompressionSelector'], - comboItems: [ - ['0', Proxmox.Utils.noneText], - ['lzo', 'LZO (' + gettext('fast') + ')'], - ['gzip', 'GZIP (' + gettext('good') + ')'] - ] -}); -Ext.define('PVE.form.PoolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pvePoolSelector'], - - allowBlank: false, - valueField: 'poolid', - displayField: 'poolid', - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: 'poolid' - }); - - Ext.apply(me, { - store: store, - autoSelect: false, - listConfig: { - columns: [ - { - header: gettext('Pool'), - sortable: true, - dataIndex: 'poolid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-pools', { - extend: 'Ext.data.Model', - fields: [ 'poolid', 'comment' ], - proxy: { - type: 'proxmox', - url: "/api2/json/pools" - }, - idProperty: 'poolid' - }); - -}); -Ext.define('PVE.form.PrivilegesSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'pvePrivilegesSelector', - - multiSelect: true, - - initComponent: function() { - var me = this; - - // So me.store is available. - me.callParent(); - - Proxmox.Utils.API2Request({ - url: '/access/roles/Administrator', - method: 'GET', - success: function(response, options) { - var data = [], key; - /*jslint forin: true */ - for (key in response.result.data) { - data.push([key, key]); - } - /*jslint forin: false */ - - me.store.setData(data); - - me.store.sort({ - property: 'key', - direction: 'ASC' - }); - }, - - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -Ext.define('pve-groups', { - extend: 'Ext.data.Model', - fields: [ 'groupid', 'comment' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/groups" - }, - idProperty: 'groupid' -}); - -Ext.define('PVE.form.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveGroupSelector', - - allowBlank: false, - autoSelect: false, - valueField: 'groupid', - displayField: 'groupid', - listConfig: { - columns: [ - { - header: gettext('Group'), - sortable: true, - dataIndex: 'groupid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - }, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: [{ - property: 'groupid' - }] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}); -Ext.define('PVE.form.UserSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUserSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'userid', - displayField: 'userid', - - editable: true, - anyMatch: true, - forceSelection: true, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-users', - sorters: [{ - property: 'userid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('User'), - sortable: true, - dataIndex: 'userid', - flex: 1 - }, - { - header: gettext('Name'), - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load({ params: { enabled: 1 }}); - } - -}, function() { - - Ext.define('pve-users', { - extend: 'Ext.data.Model', - fields: [ - 'userid', 'firstname', 'lastname' , 'email', 'comment', - { type: 'boolean', name: 'enable' }, - { type: 'date', dateFormat: 'timestamp', name: 'expire' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/users" - }, - idProperty: 'userid' - }); - -}); - - -Ext.define('PVE.form.RoleSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveRoleSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'roleid', - displayField: 'roleid', - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: [{ - property: 'roleid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Role'), - sortable: true, - dataIndex: 'roleid', - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-roles', { - extend: 'Ext.data.Model', - fields: [ 'roleid', 'privs' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/roles" - }, - idProperty: 'roleid' - }); - -}); -Ext.define('PVE.form.GuestIDSelector', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveGuestIDSelector', - - allowBlank: false, - - minValue: 100, - - maxValue: 999999999, - - validateExists: undefined, - - loadNextFreeID: false, - - guestType: undefined, - - validator: function(value) { - var me = this; - - if (!Ext.isNumeric(value) || - value < me.minValue || - value > me.maxValue) { - // check is done by ExtJS - return true; - } - - if (me.validateExists === true && !me.exists) { - return me.unknownID; - } - - if (me.validateExists === false && me.exists) { - return me.inUseID; - } - - return true; - }, - - initComponent: function() { - var me = this; - var label = '{0} ID'; - var unknownID = gettext('This {0} ID does not exists'); - var inUseID = gettext('This {0} ID is already in use'); - var type = 'CT/VM'; - - if (me.guestType === 'lxc') { - type = 'CT'; - } else if (me.guestType === 'qemu') { - type = 'VM'; - } - - me.label = Ext.String.format(label, type); - me.unknownID = Ext.String.format(unknownID, type); - me.inUseID = Ext.String.format(inUseID, type); - - Ext.apply(me, { - fieldLabel: me.label, - listeners: { - 'change': function(field, newValue, oldValue) { - if (!Ext.isDefined(me.validateExists)) { - return; - } - Proxmox.Utils.API2Request({ - params: { vmid: newValue }, - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.exists = false; - me.validate(); - }, - failure: function(response, opts) { - me.exists = true; - me.validate(); - } - }); - } - } - }); - - me.callParent(); - - if (me.loadNextFreeID) { - Proxmox.Utils.API2Request({ - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.setRawValue(response.result.data); - } - }); - } - } -}); -Ext.define('PVE.form.MemoryField', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveMemoryField', - - allowBlank: false, - - hotplug: false, - - minValue: 32, - - maxValue: 4178944, - - step: 32, - - value: '512', // qm default - - allowDecimals: false, - - allowExponential: false, - - computeUpDown: function(value) { - var me = this; - - if (!me.hotplug) { - return { up: value + me.step, down: value - me.step }; - } - - var dimm_size = 512; - var prev_dimm_size = 0; - var min_size = 1024; - var current_size = min_size; - var value_up = min_size; - var value_down = min_size; - var value_start = min_size; - - var i, j; - for (j = 0; j < 9; j++) { - for (i = 0; i < 32; i++) { - if ((value >= current_size) && (value < (current_size + dimm_size))) { - value_start = current_size; - value_up = current_size + dimm_size; - value_down = current_size - ((i === 0) ? prev_dimm_size : dimm_size); - } - current_size += dimm_size; - } - prev_dimm_size = dimm_size; - dimm_size = dimm_size*2; - } - - return { up: value_up, down: value_down, start: value_start }; - }, - - onSpinUp: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.up, me.minValue, me.maxValue)); - } - }, - - onSpinDown: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.down, me.minValue, me.maxValue)); - } - }, - - initComponent: function() { - var me = this; - - if (me.hotplug) { - me.minValue = 1024; - - me.on('blur', function(field) { - var value = me.getValue(); - var res = me.computeUpDown(value); - if (value === res.start || value === res.up || value === res.down) { - return; - } - field.setValue(res.up); - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.form.NetworkCardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveNetworkCardSelector', - comboItems: [ - ['e1000', 'Intel E1000'], - ['virtio', 'VirtIO (' + gettext('paravirtualized') + ')'], - ['rtl8139', 'Realtek RTL8139'], - ['vmxnet3', 'VMware vmxnet3'] - ] -}); -Ext.define('PVE.form.DiskFormatSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveDiskFormatSelector', - comboItems: [ - ['raw', gettext('Raw disk image') + ' (raw)'], - ['qcow2', gettext('QEMU image format') + ' (qcow2)'], - ['vmdk', gettext('VMware image format') + ' (vmdk)'] - ] -}); -Ext.define('PVE.form.DiskSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveDiskSelector', - - // can be - // undefined: all - // unused: only unused - // journal_disk: all disks with gpt - diskType: undefined, - - valueField: 'devpath', - displayField: 'devpath', - emptyText: gettext('No Disks unused'), - listConfig: { - columns: [ - { - header: gettext('Device'), - width: 80, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Size'), - width: 60, - sortable: false, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Serial'), - flex: 1, - sortable: true, - dataIndex: 'serial' - } - ] - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - filterOnLoad: true, - model: 'pve-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list", - extraParams: { type: me.diskType } - }, - sorters: [ - { - property : 'devpath', - direction: 'ASC' - } - ] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}, function() { - - Ext.define('pve-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial'], - idProperty: 'devpath' - }); -}); -Ext.define('PVE.form.BusTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveBusSelector', - - noVirtIO: false, - - initComponent: function() { - var me = this; - - me.comboItems = [['ide', 'IDE'], ['sata', 'SATA']]; - - if (!me.noVirtIO) { - me.comboItems.push(['virtio', 'VirtIO Block']); - } - - me.comboItems.push(['scsi', 'SCSI']); - - me.callParent(); - } -}); -Ext.define('PVE.form.ControllerSelector', { - extend: 'Ext.form.FieldContainer', - alias: 'widget.pveControllerSelector', - - statics: { - maxIds: { - ide: 3, - sata: 5, - virtio: 15, - scsi: 13 - } - }, - - noVirtIO: false, - - vmconfig: {}, // used to check for existing devices - - sortByPreviousUsage: function(vmconfig, controllerList) { - - var usedControllers = Ext.clone(PVE.form.ControllerSelector.maxIds); - - var type; - for (type in usedControllers) { - if(usedControllers.hasOwnProperty(type)) { - usedControllers[type] = 0; - } - } - - var property; - for (property in vmconfig) { - if (vmconfig.hasOwnProperty(property)) { - if (property.match(PVE.Utils.bus_match) && !vmconfig[property].match(/media=cdrom/)) { - var foundController = property.match(PVE.Utils.bus_match)[1]; - usedControllers[foundController]++; - } - } - } - - var vmDefaults = PVE.qemu.OSDefaults[vmconfig.ostype]; - - var sortPriority = vmDefaults && vmDefaults.busPriority - ? vmDefaults.busPriority : PVE.qemu.OSDefaults.generic; - - var sortedList = Ext.clone(controllerList); - sortedList.sort(function(a,b) { - if (usedControllers[b] == usedControllers[a]) { - return sortPriority[b] - sortPriority[a]; - } - return usedControllers[b] - usedControllers[a]; - }); - - return sortedList; - }, - - setVMConfig: function(vmconfig, autoSelect) { - var me = this; - - me.vmconfig = Ext.apply({}, vmconfig); - - var clist = ['ide', 'virtio', 'scsi', 'sata']; - var bussel = me.down('field[name=controller]'); - var deviceid = me.down('field[name=deviceid]'); - - if (autoSelect === 'cdrom') { - clist = ['ide', 'scsi', 'sata']; - if (!Ext.isDefined(me.vmconfig.ide2)) { - bussel.setValue('ide'); - deviceid.setValue(2); - return; - } - } else { - // in most cases we want to add a disk to the same controller - // we previously used - clist = me.sortByPreviousUsage(me.vmconfig, clist); - } - - Ext.Array.each(clist, function(controller) { - var confid, i; - bussel.setValue(controller); - for (i = 0; i <= PVE.form.ControllerSelector.maxIds[controller]; i++) { - confid = controller + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - deviceid.setValue(i); - return false; // break - } - } - }); - deviceid.validate(); - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - fieldLabel: gettext('Bus/Device'), - layout: 'hbox', - defaults: { - hideLabel: true - }, - items: [ - { - xtype: 'pveBusSelector', - name: 'controller', - value: PVE.qemu.OSDefaults.generic.busType, - noVirtIO: me.noVirtIO, - allowBlank: false, - flex: 2, - listeners: { - change: function(t, value) { - if (!value) { - return; - } - var field = me.down('field[name=deviceid]'); - field.setMaxValue(PVE.form.ControllerSelector.maxIds[value]); - field.validate(); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'deviceid', - minValue: 0, - maxValue: PVE.form.ControllerSelector.maxIds.ide, - value: '0', - flex: 1, - allowBlank: false, - validator: function(value) { - /*jslint confusion: true */ - if (!me.rendered) { - return; - } - var field = me.down('field[name=controller]'); - var controller = field.getValue(); - var confid = controller + value; - if (Ext.isDefined(me.vmconfig[confid])) { - return "This device is already in use."; - } - return true; - } - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.EmailNotificationSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveEmailNotificationSelector'], - comboItems: [ - ['always', gettext('Always')], - ['failure', gettext('On failure only')] - ] -}); -/*global Proxmox*/ -Ext.define('PVE.form.RealmComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveRealmComboBox'], - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store.on('load', this.onLoad, view); - }, - - onLoad: function(store, records, success) { - if (!success) { - return; - } - var me = this; - var val = me.getValue(); - if (!val || !me.store.findRecord('realm', val)) { - var def = 'pam'; - Ext.each(records, function(rec) { - if (rec.data && rec.data['default']) { - def = rec.data.realm; - } - }); - me.setValue(def); - } - } - }, - - fieldLabel: gettext('Realm'), - name: 'realm', - queryMode: 'local', - allowBlank: false, - editable: false, - forceSelection: true, - autoSelect: false, - triggerAction: 'all', - valueField: 'realm', - displayField: 'descr', - getState: function() { - return { value: this.getValue() }; - }, - applyState : function(state) { - if (state && state.value) { - this.setValue(state.value); - } - }, - stateEvents: [ 'select' ], - stateful: true, // last chosen auth realm is saved between page reloads - id: 'pveloginrealm', // We need stable ids when using stateful, not autogenerated - stateID: 'pveloginrealm', - - needOTP: function(realm) { - var me = this; - // use exact match - var rec = me.store.findRecord('realm', realm, 0, false, false, true); - return rec && rec.data && rec.data.tfa ? rec.data.tfa : undefined; - }, - - store: { - model: 'pve-domains', - autoLoad: true - } -}); -/* - * Top left combobox, used to select a view of the underneath RessourceTree - */ -Ext.define('PVE.form.ViewSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveViewSelector'], - - editable: false, - allowBlank: false, - forceSelection: true, - autoSelect: false, - valueField: 'key', - displayField: 'value', - hideLabel: true, - queryMode: 'local', - - initComponent: function() { - var me = this; - - var default_views = { - server: { - text: gettext('Server View'), - groups: ['node'] - }, - folder: { - text: gettext('Folder View'), - groups: ['type'] - }, - storage: { - text: gettext('Storage View'), - groups: ['node'], - filterfn: function(node) { - return node.data.type === 'storage' || node.data.type === 'node'; - } - }, - pool: { - text: gettext('Pool View'), - groups: ['pool'], - // Pool View only lists VMs and Containers - filterfn: function(node) { - return node.data.type === 'qemu' || node.data.type === 'lxc' || node.data.type === 'openvz' || - node.data.type === 'pool'; - } - } - }; - - var groupdef = []; - Ext.Object.each(default_views, function(viewname, value) { - groupdef.push([viewname, value.text]); - }); - - var store = Ext.create('Ext.data.Store', { - model: 'KeyValue', - proxy: { - type: 'memory', - reader: 'array' - }, - data: groupdef, - autoload: true - }); - - Ext.apply(me, { - store: store, - value: groupdef[0][0], - getViewFilter: function() { - var view = me.getValue(); - return Ext.apply({ id: view }, default_views[view] || default_views.server); - }, - - getState: function() { - return { value: me.getValue() }; - }, - - applyState : function(state, doSelect) { - var view = me.getValue(); - if (state && state.value && (view != state.value)) { - var record = store.findRecord('key', state.value); - if (record) { - me.setValue(state.value, true); - if (doSelect) { - me.fireEvent('select', me, [record]); - } - } - } - }, - stateEvents: [ 'select' ], - stateful: true, - stateId: 'pveview', - id: 'view' - }); - - me.callParent(); - - var statechange = function(sp, key, value) { - if (key === me.id) { - me.applyState(value, true); - } - }; - - var sp = Ext.state.Manager.getProvider(); - me.mon(sp, 'statechange', statechange, me); - } -}); -Ext.define('PVE.form.NodeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveNodeSelector'], - - // invalidate nodes which are offline - onlineValidator: false, - - selectCurNode: false, - - // do not allow those nodes (array) - disallowedNodes: undefined, - - // only allow those nodes (array) - allowedNodes: undefined, - // set default value to empty array, else it inits it with - // null and after the store load it is an empty array, - // triggering dirtychange - value: [], - valueField: 'node', - displayField: 'node', - store: { - fields: [ 'node', 'cpu', 'maxcpu', 'mem', 'maxmem', 'uptime' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes' - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - }, - { - property : 'mem', - direction: 'DESC' - } - ] - }, - - listConfig: { - columns: [ - { - header: gettext('Node'), - dataIndex: 'node', - sortable: true, - hideable: false, - flex: 1 - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 100, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100, - dataIndex: 'cpu' - } - ] - }, - - validator: function(value) { - /*jslint confusion: true */ - var me = this; - if (!me.onlineValidator || (me.allowBlank && !value)) { - return true; - } - - var offline = []; - var notAllowed = []; - - Ext.Array.each(value.split(/\s*,\s*/), function(node) { - var rec = me.store.findRecord(me.valueField, node); - if (!(rec && rec.data) || !Ext.isNumeric(rec.data.mem)) { - offline.push(node); - } else if (me.allowedNodes && !Ext.Array.contains(me.allowedNodes, node)) { - notAllowed.push(node); - } - }); - - if (value && notAllowed.length !== 0) { - return "Node " + notAllowed.join(', ') + " is not allowed for this action!"; - } - - if (value && offline.length !== 0) { - return "Node " + offline.join(', ') + " seems to be offline!"; - } - return true; - }, - - initComponent: function() { - var me = this; - - if (me.selectCurNode && PVE.curSelectedNode && PVE.curSelectedNode.data.node) { - me.preferredValue = PVE.curSelectedNode.data.node; - } - - me.callParent(); - me.getStore().load(); - - // filter out disallowed nodes - me.getStore().addFilter(new Ext.util.Filter({ - filterFn: function(item) { - if (Ext.isArray(me.disallowedNodes)) { - return !Ext.Array.contains(me.disallowedNodes, item.data.node); - } else { - return true; - } - } - })); - - me.mon(me.getStore(), 'load', function(){ - me.isValid(); - }); - } -}); -Ext.define('PVE.form.FileSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFileSelector', - - editable: true, - anyMatch: true, - forceSelection: true, - - listeners: { - afterrender: function() { - var me = this; - if (!me.disabled) { - me.setStorage(me.storage, me.nodename); - } - } - }, - - setStorage: function(storage, nodename) { - var me = this; - - var change = false; - if (storage && (me.storage !== storage)) { - me.storage = storage; - change = true; - } - - if (nodename && (me.nodename !== nodename)) { - me.nodename = nodename; - change = true; - } - - if (!(me.storage && me.nodename && change)) { - return; - } - - var url = '/api2/json/nodes/' + me.nodename + '/storage/' + me.storage + '/content'; - if (me.storageContent) { - url += '?content=' + me.storageContent; - } - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - me.store.removeAll(); - me.store.load(); - }, - - setNodename: function(nodename) { - this.setStorage(undefined, nodename); - }, - - store: { - model: 'pve-storage-content' - }, - - allowBlank: false, - autoSelect: false, - valueField: 'volid', - displayField: 'text', - - listConfig: { - width: 600, - columns: [ - { - header: gettext('Name'), - dataIndex: 'text', - hideable: false, - flex: 1 - }, - { - header: gettext('Format'), - width: 60, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - dataIndex: 'size', - renderer: Proxmox.Utils.format_size - } - ] - } -}); -Ext.define('PVE.form.StorageSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveStorageSelector', - - allowBlank: false, - valueField: 'storage', - displayField: 'storage', - listConfig: { - columns: [ - { - header: gettext('Name'), - dataIndex: 'storage', - hideable: false, - flex: 1 - }, - { - header: gettext('Type'), - width: 60, - dataIndex: 'type' - }, - { - header: gettext('Avail'), - width: 80, - dataIndex: 'avail', - renderer: Proxmox.Utils.format_size - }, - { - header: gettext('Capacity'), - width: 80, - dataIndex: 'total', - renderer: Proxmox.Utils.format_size - } - ] - }, - - reloadStorageList: function() { - var me = this; - if (!me.nodename) { - return; - } - - var params = { - format: 1 - }; - var url = '/api2/json/nodes/' + me.nodename + '/storage'; - if (me.storageContent) { - params.content = me.storageContent; - } - if (me.targetNode) { - params.target = me.targetNode; - params.enabled = 1; // skip disabled storages - } - me.store.setProxy({ - type: 'proxmox', - url: url, - extraParams: params - }); - - me.store.load(); - - }, - - setTargetNode: function(targetNode) { - var me = this; - - if (!targetNode || (me.targetNode === targetNode)) { - return; - } - - me.targetNode = targetNode; - - me.reloadStorageList(); - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.reloadStorageList(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - var store = Ext.create('Ext.data.Store', { - model: 'pve-storage-status', - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - if (nodename) { - me.setNodename(nodename); - } - } -}, function() { - - Ext.define('pve-storage-status', { - extend: 'Ext.data.Model', - fields: [ 'storage', 'active', 'type', 'avail', 'total' ], - idProperty: 'storage' - }); - -}); -Ext.define('PVE.form.DiskStorageSelector', { - extend: 'Ext.container.Container', - alias: 'widget.pveDiskStorageSelector', - - layout: 'fit', - defaults: { - margin: '0 0 5 0' - }, - - // the fieldLabel for the storageselector - storageLabel: gettext('Storage'), - - // the content to show (e.g., images or rootdir) - storageContent: undefined, - - // if true, selects the first available storage - autoSelect: false, - - allowBlank: false, - emptyText: '', - - // hides the selection field - // this is always hidden on creation, - // and only shown when the storage needs a selection and - // hideSelection is not true - hideSelection: undefined, - - // hides the size field (e.g, for the efi disk dialog) - hideSize: false, - - // sets the intial size value - // string because else we get a type confusion - defaultSize: '32', - - changeStorage: function(f, value) { - var me = this; - var formatsel = me.getComponent('diskformat'); - var hdfilesel = me.getComponent('hdimage'); - var hdsizesel = me.getComponent('disksize'); - - // initial store load, and reset/deletion of the storage - if (!value) { - hdfilesel.setDisabled(true); - hdfilesel.setVisible(false); - - formatsel.setDisabled(true); - return; - } - - var rec = f.store.getById(value); - // if the storage is not defined, or valid, - // we cannot know what to enable/disable - if (!rec) { - return; - } - - var selectformat = false; - if (rec.data.format) { - var format = rec.data.format[0]; // 0 is the formats, 1 the default in the backend - delete format.subvol; // we never need subvol in the gui - selectformat = (Ext.Object.getSize(format) > 1); - } - - var select = !!rec.data.select_existing && !me.hideSelection; - - formatsel.setDisabled(!selectformat); - formatsel.setValue(selectformat ? 'qcow2' : 'raw'); - - hdfilesel.setDisabled(!select); - hdfilesel.setVisible(select); - if (select) { - hdfilesel.setStorage(value); - } - - hdsizesel.setDisabled(select || me.hideSize); - hdsizesel.setVisible(!select && !me.hideSize); - }, - - setNodename: function(nodename) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - var hdfilesel = me.getComponent('hdimage'); - - hdstorage.setNodename(nodename); - hdfilesel.setNodename(nodename); - }, - - setDisabled: function(value) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - - // reset on disable - if (value) { - hdstorage.setValue(); - } - hdstorage.setDisabled(value); - - // disabling does not always fire this event and we do not need - // the value of the validity - hdstorage.fireEvent('validitychange'); - }, - - initComponent: function() { - var me = this; - - me.items = [ - { - xtype: 'pveStorageSelector', - itemId: 'hdstorage', - name: 'hdstorage', - reference: 'hdstorage', - fieldLabel: me.storageLabel, - nodename: me.nodename, - storageContent: me.storageContent, - disabled: me.disabled, - autoSelect: me.autoSelect, - allowBlank: me.allowBlank, - emptyText: me.emptyText, - listeners: { - change: { - fn: me.changeStorage, - scope: me - } - } - }, - { - xtype: 'pveFileSelector', - name: 'hdimage', - reference: 'hdimage', - itemId: 'hdimage', - fieldLabel: gettext('Disk image'), - nodename: me.nodename, - disabled: true, - hidden: true - }, - { - xtype: 'numberfield', - itemId: 'disksize', - reference: 'disksize', - name: 'disksize', - fieldLabel: gettext('Disk size') + ' (GiB)', - hidden: me.hideSize, - disabled: me.hideSize, - minValue: 0.001, - maxValue: 128*1024, - decimalPrecision: 3, - value: me.defaultSize, - allowBlank: false - }, - { - xtype: 'pveDiskFormatSelector', - itemId: 'diskformat', - reference: 'diskformat', - name: 'diskformat', - fieldLabel: gettext('Format'), - nodename: me.nodename, - disabled: true, - hidden: me.storageContent === 'rootdir', - value: 'qcow2', - allowBlank: false - } - ]; - - // use it to disable the children but not ourself - me.disabled = false; - - me.callParent(); - } -}); -Ext.define('PVE.form.BridgeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.BridgeSelector'], - - bridgeType: 'any_bridge', // bridge, OVSBridge or any_bridge - - store: { - fields: [ 'iface', 'active', 'type' ], - filterOnLoad: true, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }, - valueField: 'iface', - displayField: 'iface', - listConfig: { - columns: [ - { - header: gettext('Bridge'), - dataIndex: 'iface', - hideable: false, - width: 100 - }, - { - header: gettext('Active'), - width: 60, - dataIndex: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/network?type=' + - me.bridgeType - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.PCISelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pvePCISelector', - - store: { - fields: [ 'id','vendor_name', 'device_name', 'vendor', 'device', 'iommugroup', 'mdev' ], - filterOnLoad: true, - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }, - - autoSelect: false, - valueField: 'id', - displayField: 'id', - - // can contain a load callback for the store - // useful to determine the state of the IOMMU - onLoadCallBack: undefined, - - listConfig: { - width: 800, - columns: [ - { - header: 'ID', - dataIndex: 'id', - width: 80 - }, - { - header: gettext('IOMMU Group'), - dataIndex: 'iommugroup', - width: 50 - }, - { - header: gettext('Vendor'), - dataIndex: 'vendor_name', - flex: 2 - }, - { - header: gettext('Device'), - dataIndex: 'device_name', - flex: 6 - }, - { - header: gettext('Mediated Devices'), - dataIndex: 'mdev', - flex: 1, - renderer: function(val) { - return Proxmox.Utils.format_boolean(!!val); - } - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - if (me.onLoadCallBack !== undefined) { - me.mon(me.getStore(), 'load', me.onLoadCallBack); - } - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.MDevSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveMDevSelector', - - store: { - fields: [ 'type','available', 'description' ], - filterOnLoad: true, - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ] - }, - autoSelect: false, - valueField: 'type', - displayField: 'type', - listConfig: { - columns: [ - { - header: gettext('Type'), - dataIndex: 'type', - flex: 1 - }, - { - header: gettext('Available'), - dataIndex: 'available', - width: 80 - }, - { - header: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value) { - if (!value) { - return ''; - } - - return value.split('\n').join('
'); - } - } - ] - }, - - setPciID: function(pciid, force) { - var me = this; - - if (!force && (!pciid || (me.pciid === pciid))) { - return; - } - - me.pciid = pciid; - me.updateProxy(); - }, - - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - me.updateProxy(); - }, - - updateProxy: function() { - var me = this; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci/' + me.pciid + '/mdev' - }); - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no node name specified'; - } - - me.callParent(); - - if (me.pciid) { - me.setPciID(me.pciid, true); - } - } -}); - -Ext.define('PVE.form.SecurityGroupsSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveSecurityGroupsSelector'], - - valueField: 'group', - displayField: 'group', - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'group', 'comment' ], - idProperty: 'group', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/groups" - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Security Group'), - dataIndex: 'group', - hideable: false, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPRefSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPRefSelector'], - - base_url: undefined, - - preferredValue: '', // hack: else Form sets dirty flag? - - ref_type: undefined, // undefined = any [undefined, 'ipset' or 'alias'] - - valueField: 'ref', - displayField: 'ref', - - initComponent: function() { - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - - var url = "/api2/json" + me.base_url; - if (me.ref_type) { - url += "?type=" + me.ref_type; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'type', 'name', 'ref', 'comment' ], - idProperty: 'ref', - proxy: { - type: 'proxmox', - url: url - }, - sorters: { - property: 'ref', - order: 'DESC' - } - }); - - var disable_query_for_ips = function(f, value) { - if (value === null || - value.match(/^\d/)) { // IP address starts with \d - f.queryDelay = 9999999999; // hack: disbale with long delay - } else { - f.queryDelay = 10; - } - }; - - var columns = []; - - if (!me.ref_type) { - columns.push({ - header: gettext('Type'), - dataIndex: 'type', - hideable: false, - width: 60 - }); - } - - columns.push( - { - header: gettext('Name'), - dataIndex: 'ref', - hideable: false, - width: 140 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ); - - Ext.apply(me, { - store: store, - listConfig: { columns: columns } - }); - - me.on('change', disable_query_for_ips); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPProtocolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPProtocolSelector'], - valueField: 'p', - displayField: 'p', - listConfig: { - columns: [ - { - header: gettext('Protocol'), - dataIndex: 'p', - hideable: false, - sortable: false, - width: 100 - }, - { - header: gettext('Number'), - dataIndex: 'n', - hideable: false, - sortable: false, - width: 50 - }, - { - header: gettext('Description'), - dataIndex: 'd', - hideable: false, - sortable: false, - flex: 1 - } - ] - }, - store: { - fields: [ 'p', 'd', 'n'], - data: [ - { p: 'tcp', n: 6, d: 'Transmission Control Protocol' }, - { p: 'udp', n: 17, d: 'User Datagram Protocol' }, - { p: 'icmp', n: 1, d: 'Internet Control Message Protocol' }, - { p: 'igmp', n: 2, d: 'Internet Group Management' }, - { p: 'ggp', n: 3, d: 'gateway-gateway protocol' }, - { p: 'ipencap', n: 4, d: 'IP encapsulated in IP' }, - { p: 'st', n: 5, d: 'ST datagram mode' }, - { p: 'egp', n: 8, d: 'exterior gateway protocol' }, - { p: 'igp', n: 9, d: 'any private interior gateway (Cisco)' }, - { p: 'pup', n: 12, d: 'PARC universal packet protocol' }, - { p: 'hmp', n: 20, d: 'host monitoring protocol' }, - { p: 'xns-idp', n: 22, d: 'Xerox NS IDP' }, - { p: 'rdp', n: 27, d: '"reliable datagram" protocol' }, - { p: 'iso-tp4', n: 29, d: 'ISO Transport Protocol class 4 [RFC905]' }, - { p: 'dccp', n: 33, d: 'Datagram Congestion Control Prot. [RFC4340]' }, - { p: 'xtp', n: 36, d: 'Xpress Transfer Protocol' }, - { p: 'ddp', n: 37, d: 'Datagram Delivery Protocol' }, - { p: 'idpr-cmtp', n: 38, d: 'IDPR Control Message Transport' }, - { p: 'ipv6', n: 41, d: 'Internet Protocol, version 6' }, - { p: 'ipv6-route', n: 43, d: 'Routing Header for IPv6' }, - { p: 'ipv6-frag', n: 44, d: 'Fragment Header for IPv6' }, - { p: 'idrp', n: 45, d: 'Inter-Domain Routing Protocol' }, - { p: 'rsvp', n: 46, d: 'Reservation Protocol' }, - { p: 'gre', n: 47, d: 'General Routing Encapsulation' }, - { p: 'esp', n: 50, d: 'Encap Security Payload [RFC2406]' }, - { p: 'ah', n: 51, d: 'Authentication Header [RFC2402]' }, - { p: 'skip', n: 57, d: 'SKIP' }, - { p: 'ipv6-icmp', n: 58, d: 'ICMP for IPv6' }, - { p: 'ipv6-nonxt', n: 59, d: 'No Next Header for IPv6' }, - { p: 'ipv6-opts', n: 60, d: 'Destination Options for IPv6' }, - { p: 'vmtp', n: 81, d: 'Versatile Message Transport' }, - { p: 'eigrp', n: 88, d: 'Enhanced Interior Routing Protocol (Cisco)' }, - { p: 'ospf', n: 89, d: 'Open Shortest Path First IGP' }, - { p: 'ax.25', n: 93, d: 'AX.25 frames' }, - { p: 'ipip', n: 94, d: 'IP-within-IP Encapsulation Protocol' }, - { p: 'etherip', n: 97, d: 'Ethernet-within-IP Encapsulation [RFC3378]' }, - { p: 'encap', n: 98, d: 'Yet Another IP encapsulation [RFC1241]' }, - { p: 'pim', n: 103, d: 'Protocol Independent Multicast' }, - { p: 'ipcomp', n: 108, d: 'IP Payload Compression Protocol' }, - { p: 'vrrp', n: 112, d: 'Virtual Router Redundancy Protocol [RFC5798]' }, - { p: 'l2tp', n: 115, d: 'Layer Two Tunneling Protocol [RFC2661]' }, - { p: 'isis', n: 124, d: 'IS-IS over IPv4' }, - { p: 'sctp', n: 132, d: 'Stream Control Transmission Protocol' }, - { p: 'fc', n: 133, d: 'Fibre Channel' }, - { p: 'mobility-header', n: 135, d: 'Mobility Support for IPv6 [RFC3775]' }, - { p: 'udplite', n: 136, d: 'UDP-Lite [RFC3828]' }, - { p: 'mpls-in-ip', n: 137, d: 'MPLS-in-IP [RFC4023]' }, - { p: 'hip', n: 139, d: 'Host Identity Protocol' }, - { p: 'shim6', n: 140, d: 'Shim6 Protocol [RFC5533]' }, - { p: 'wesp', n: 141, d: 'Wrapped Encapsulating Security Payload' }, - { p: 'rohc', n: 142, d: 'Robust Header Compression' } - ] - } -}); -Ext.define('PVE.form.CPUModelSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.CPUModelSelector'], - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + ' (kvm64)'], - ['486', '486'], - ['athlon', 'athlon'], - ['core2duo', 'core2duo'], - ['coreduo', 'coreduo'], - ['kvm32', 'kvm32'], - ['kvm64', 'kvm64'], - ['pentium', 'pentium'], - ['pentium2', 'pentium2'], - ['pentium3', 'pentium3'], - ['phenom', 'phenom'], - ['qemu32', 'qemu32'], - ['qemu64', 'qemu64'], - ['Conroe', 'Conroe'], - ['Penryn', 'Penryn'], - ['Nehalem', 'Nehalem'], - ['Westmere', 'Westmere'], - ['SandyBridge', 'SandyBridge'], - ['IvyBridge', 'IvyBridge'], - ['Haswell', 'Haswell'], - ['Haswell-noTSX','Haswell-noTSX'], - ['Broadwell', 'Broadwell'], - ['Broadwell-noTSX','Broadwell-noTSX'], - ['Skylake-Client','Skylake-Client'], - ['Opteron_G1', 'Opteron_G1'], - ['Opteron_G2', 'Opteron_G2'], - ['Opteron_G3', 'Opteron_G3'], - ['Opteron_G4', 'Opteron_G4'], - ['Opteron_G5', 'Opteron_G5'], - ['EPYC', 'EPYC'], - ['host', 'host'] - - ] -}); -Ext.define('PVE.form.VNCKeyboardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.VNCKeyboardSelector'], - comboItems: PVE.Utils.kvm_keymap_array() -}); -Ext.define('PVE.form.CacheTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.CacheTypeSelector'], - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (" + gettext('No cache') + ")"], - ['directsync', 'Direct sync'], - ['writethrough', 'Write through'], - ['writeback', 'Write back'], - ['unsafe', 'Write back (' + gettext('unsafe') + ')'], - ['none', gettext('No cache')] - ] -}); -Ext.define('PVE.form.SnapshotSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.SnapshotSelector'], - - valueField: 'name', - displayField: 'name', - - loadStore: function(nodename, vmid) { - var me = this; - - if (!nodename) { - return; - } - - me.nodename = nodename; - - if (!vmid) { - return; - } - - me.vmid = vmid; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid +'/snapshot' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.guestType) { - throw "no guest type specified"; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'name'], - filterOnLoad: true - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Snapshot'), - dataIndex: 'name', - hideable: false, - flex: 1 - } - ] - } - }); - - me.callParent(); - - me.loadStore(me.nodename, me.vmid); - } -}); -Ext.define('PVE.form.ContentTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveContentTypeSelector'], - - cts: undefined, - - initComponent: function() { - var me = this; - - me.comboItems = []; - - if (me.cts === undefined) { - me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir', 'snippets']; - } - - Ext.Array.each(me.cts, function(ct) { - me.comboItems.push([ct, PVE.Utils.format_content_types(ct)]); - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.HotplugFeatureSelector', { - extend: 'Ext.form.CheckboxGroup', - alias: 'widget.pveHotplugFeatureSelector', - - columns: 1, - vertical: true, - - defaults: { - name: 'hotplug', - submitValue: false - }, - items: [ - { - boxLabel: gettext('Disk'), - inputValue: 'disk', - checked: true - }, - { - boxLabel: gettext('Network'), - inputValue: 'network', - checked: true - }, - { - boxLabel: 'USB', - inputValue: 'usb', - checked: true - }, - { - boxLabel: gettext('Memory'), - inputValue: 'memory' - }, - { - boxLabel: gettext('CPU'), - inputValue: 'cpu' - } - ], - - setValue: function(value) { - var me = this; - var newVal = []; - if (value === '1') { - newVal = ['disk', 'network', 'usb']; - } else if (value !== '0') { - newVal = value.split(','); - } - me.callParent([{ hotplug: newVal }]); - }, - - // overide framework function to - // assemble the hotplug value - getSubmitData: function() { - var me = this, - boxes = me.getBoxes(), - data = []; - Ext.Array.forEach(boxes, function(box){ - if (box.getValue()) { - data.push(box.inputValue); - } - }); - - /* because above is hotplug an array */ - /*jslint confusion: true*/ - if (data.length === 0) { - return { 'hotplug':'0' }; - } else { - return { 'hotplug': data.join(',') }; - } - } - -}); -Ext.define('PVE.form.AgentFeatureSelector', { - extend: 'Proxmox.panel.InputPanel', - alias: ['widget.pveAgentFeatureSelector'], - - initComponent: function() { - var me = this; - me.items= [ - { - xtype: 'proxmoxcheckbox', - boxLabel: gettext('Qemu Agent'), - name: 'enabled', - uncheckedValue: 0, - listeners: { - change: function(f, value, old) { - var gtcb = me.down('proxmoxcheckbox[name=fstrim_cloned_disks]'); - if (value) { - gtcb.setDisabled(false); - } else { - gtcb.setDisabled(true); - } - } - } - }, - { - xtype: 'proxmoxcheckbox', - boxLabel: gettext('Run guest-trim after clone disk'), - name: 'fstrim_cloned_disks', - disabled: true - } - ]; - me.callParent(); - }, - - onGetValues: function(values) { - var agentstr = PVE.Parser.printPropertyString(values, 'enabled'); - return { agent: agentstr }; - }, - - setValues: function(values) { - var agent = values.agent || ''; - var res = PVE.Parser.parsePropertyString(agent, 'enabled'); - this.callParent([res]); - } -}); -Ext.define('PVE.form.iScsiProviderSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveiScsiProviderSelector'], - comboItems: [ - ['comstar', 'Comstar'], - [ 'istgt', 'istgt'], - [ 'iet', 'IET'], - [ 'LIO', 'LIO'] - ] -}); -Ext.define('PVE.form.DayOfWeekSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveDayOfWeekSelector'], - comboItems:[], - initComponent: function(){ - var me = this; - me.comboItems = [ - ['mon', Ext.util.Format.htmlDecode(Ext.Date.dayNames[1])], - ['tue', Ext.util.Format.htmlDecode(Ext.Date.dayNames[2])], - ['wed', Ext.util.Format.htmlDecode(Ext.Date.dayNames[3])], - ['thu', Ext.util.Format.htmlDecode(Ext.Date.dayNames[4])], - ['fri', Ext.util.Format.htmlDecode(Ext.Date.dayNames[5])], - ['sat', Ext.util.Format.htmlDecode(Ext.Date.dayNames[6])], - ['sun', Ext.util.Format.htmlDecode(Ext.Date.dayNames[0])] - ]; - this.callParent(); - } -}); -Ext.define('PVE.form.BackupModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveBackupModeSelector'], - comboItems: [ - ['snapshot', gettext('Snapshot')], - ['suspend', gettext('Suspend')], - ['stop', gettext('Stop')] - ] -}); -Ext.define('PVE.form.ScsiHwSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveScsiHwSelector'], - comboItems: [ - ['__default__', PVE.Utils.render_scsihw('')], - ['lsi', PVE.Utils.render_scsihw('lsi')], - ['lsi53c810', PVE.Utils.render_scsihw('lsi53c810')], - ['megasas', PVE.Utils.render_scsihw('megasas')], - ['virtio-scsi-pci', PVE.Utils.render_scsihw('virtio-scsi-pci')], - ['virtio-scsi-single', PVE.Utils.render_scsihw('virtio-scsi-single')], - ['pvscsi', PVE.Utils.render_scsihw('pvscsi')] - ] -}); -Ext.define('PVE.form.FirewallPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallPolicySelector'], - comboItems: [ - ['ACCEPT', 'ACCEPT'], - ['REJECT', 'REJECT'], - [ 'DROP', 'DROP'] - ] -}); -/* - * This is a global search field - * it loads the /cluster/resources on focus - * and displays the result in a floating grid - * - * it filters and sorts the objects by the algorithm in - * the customFilter function - * - * also it does accept key up/down and enter for input - * and it opens to ctrl+shift+f and ctrl+space - */ -Ext.define('PVE.form.GlobalSearchField', { - extend: 'Ext.form.field.Text', - alias: 'widget.pveGlobalSearchField', - - emptyText: gettext('Search'), - enableKeyEvents: true, - selectOnFocus: true, - padding: '0 5 0 5', - - grid: { - xtype: 'gridpanel', - focusOnToFront: false, - floating: true, - emptyText: Proxmox.Utils.noneText, - width: 600, - height: 400, - scrollable: { - xtype: 'scroller', - y: true, - x:false - }, - store: { - model: 'PVEResources', - proxy:{ - type: 'proxmox', - url: '/api2/extjs/cluster/resources' - } - }, - plugins: { - ptype: 'bufferedrenderer', - trailingBufferZone: 20, - leadingBufferZone: 20 - }, - - hideMe: function() { - var me = this; - if (typeof me.ctxMenu !== 'undefined' && me.ctxMenu.isVisible()) { - return; - } - me.hasFocus = false; - if (!me.textfield.hasFocus) { - me.hide(); - } - }, - - setFocus: function() { - var me = this; - me.hasFocus = true; - }, - - listeners: { - rowclick: function(grid, record) { - var me = this; - me.textfield.selectAndHide(record.id); - }, - itemcontextmenu: function(v, record, item, index, event) { - var me = this; - me.ctxMenu = PVE.Utils.createCmdMenu(v, record, item, index, event); - }, - /* because of lint */ - focusleave: { - fn: 'hideMe' - }, - focusenter: 'setFocus' - }, - - columns: [ - { - text: gettext('Type'), - dataIndex: 'type', - width: 100, - renderer: PVE.Utils.render_resource_type - }, - { - text: gettext('Description'), - flex: 1, - dataIndex: 'text' - }, - { - text: gettext('Node'), - dataIndex: 'node' - }, - { - text: gettext('Pool'), - dataIndex: 'pool' - } - ] - }, - - customFilter: function(item) { - var me = this; - var match = 0; - var fieldArr = []; - var i,j, fields; - - // different types of objects have different fields to search - // for example, a node will never have a pool and vice versa - switch (item.data.type) { - case 'pool': fieldArr = ['type', 'pool', 'text']; break; - case 'node': fieldArr = ['type', 'node', 'text']; break; - case 'storage': fieldArr = ['type', 'pool', 'node', 'storage']; break; - default: fieldArr = ['name', 'type', 'node', 'pool', 'vmid']; - } - if (me.filterVal === '') { - item.data.relevance = 0; - return true; - } - - // all text is case insensitive and each word is - // searched alone - // for every partial match, the row gets - // 1 match point, for every exact match - // it gets 2 points - // - // results gets sorted by points (descending) - fields = me.filterVal.split(/\s+/); - for(i = 0; i < fieldArr.length; i++) { - var v = item.data[fieldArr[i]]; - if (v !== undefined) { - v = v.toString().toLowerCase(); - for(j = 0; j < fields.length; j++) { - if (v.indexOf(fields[j]) !== -1) { - match++; - if(v === fields[j]) { - match++; - } - } - } - } - } - // give the row the 'relevance' value - item.data.relevance = match; - return (match > 0); - }, - - updateFilter: function(field, newValue, oldValue) { - var me = this; - // parse input and filter store, - // show grid - me.grid.store.filterVal = newValue.toLowerCase().trim(); - me.grid.store.clearFilter(true); - me.grid.store.filterBy(me.customFilter); - me.grid.getSelectionModel().select(0); - }, - - selectAndHide: function(id) { - var me = this; - me.tree.selectById(id); - me.grid.hide(); - me.setValue(''); - me.blur(); - }, - - onKey: function(field, e) { - var me = this; - var key = e.getKey(); - - switch(key) { - case Ext.event.Event.ENTER: - // go to first entry if there is one - if (me.grid.store.getCount() > 0) { - me.selectAndHide(me.grid.getSelection()[0].data.id); - } - break; - case Ext.event.Event.UP: - me.grid.getSelectionModel().selectPrevious(); - break; - case Ext.event.Event.DOWN: - me.grid.getSelectionModel().selectNext(); - break; - case Ext.event.Event.ESC: - me.grid.hide(); - me.blur(); - break; - } - }, - - loadValues: function(field) { - var me = this; - var records = []; - - me.hasFocus = true; - me.grid.textfield = me; - me.grid.store.load(); - me.grid.showBy(me, 'tl-bl'); - }, - - hideGrid: function() { - var me = this; - - me.hasFocus = false; - if (!me.grid.hasFocus) { - me.grid.hide(); - } - }, - - listeners: { - change: { - fn: 'updateFilter', - buffer: 250 - }, - specialkey: 'onKey', - focusenter: 'loadValues', - focusleave: { - fn: 'hideGrid', - delay: 100 - } - }, - - toggleFocus: function() { - var me = this; - if (!me.hasFocus) { - me.focus(); - } else { - me.blur(); - } - }, - - initComponent: function() { - var me = this; - - if (!me.tree) { - throw "no tree given"; - } - - me.grid = Ext.create(me.grid); - - me.callParent(); - - /*jslint confusion: true*/ - /*because shift is also a function*/ - // bind ctrl+shift+f and ctrl+space - // to open/close the search - me.keymap = new Ext.KeyMap({ - target: Ext.get(document), - binding: [{ - key:'F', - ctrl: true, - shift: true, - fn: me.toggleFocus, - scope: me - },{ - key:' ', - ctrl: true, - fn: me.toggleFocus, - scope: me - }] - }); - - // always select first item and - // sort by relevance after load - me.mon(me.grid.store, 'load', function() { - me.grid.getSelectionModel().select(0); - me.grid.store.sort({ - property: 'relevance', - direction: 'DESC' - }); - }); - } - -}); -Ext.define('PVE.form.QemuBiosSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveQemuBiosSelector'], - - initComponent: function() { - var me = this; - - me.comboItems = [ - ['__default__', PVE.Utils.render_qemu_bios('')], - ['seabios', PVE.Utils.render_qemu_bios('seabios')], - ['ovmf', PVE.Utils.render_qemu_bios('ovmf')] - ]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -/* filter is a javascript builtin, but extjs calls it also filter */ -Ext.define('PVE.form.VMSelector', { - extend: 'Ext.grid.Panel', - alias: 'widget.vmselector', - - mixins: { - field: 'Ext.form.field.Field' - }, - - allowBlank: true, - selectAll: false, - isFormField: true, - - plugins: 'gridfilters', - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - // due to EXTJS-18711 - // we have to do a static list via a store - // but to avoid creating an object, - // we have to have a pseudo un function - un: function(){} - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - flex: 1, - filter: { - type: 'list' - } - } - ], - - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE' - }, - - checkChangeEvents: [ - 'selectionchange', - 'change' - ], - - listeners: { - selectionchange: function() { - // to trigger validity and error checks - this.checkChange(); - } - }, - - getValue: function() { - var me = this; - var sm = me.getSelectionModel(); - var selection = sm.getSelection(); - var values = []; - var store = me.getStore(); - selection.forEach(function(item) { - // only add if not filtered - if (store.findExact('vmid', item.data.vmid) !== -1) { - values.push(item.data.vmid); - } - }); - return values; - }, - - setValue: function(value) { - console.log(value); - var me = this; - var sm = me.getSelectionModel(); - if (!Ext.isArray(value)) { - value = value.split(','); - } - var selection = []; - var store = me.getStore(); - - value.forEach(function(item) { - var rec = store.findRecord('vmid',item, 0, false, true, true); - console.log(store); - - if (rec) { - console.log(rec); - selection.push(rec); - } - }); - - sm.select(selection); - - return me.mixins.field.setValue.call(me, value); - }, - - getErrors: function(value) { - var me = this; - if (me.allowBlank === false && - me.getSelectionModel().getCount() === 0) { - me.addBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return [gettext('No VM selected')]; - } - - me.removeBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return []; - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.nodename) { - me.store.filters.add({ - property: 'node', - exactMatch: true, - value: me.nodename - }); - } - - // only show the relevant guests by default - if (me.action) { - var statusfilter = ''; - switch (me.action) { - case 'startall': - statusfilter = 'stopped'; - break; - case 'stopall': - statusfilter = 'running'; - break; - } - if (statusfilter !== '') { - me.store.filters.add({ - property: 'template', - value: 0 - },{ - id: 'x-gridfilter-status', - operator: 'in', - property: 'status', - value: [statusfilter] - }); - } - } - - var store = me.getStore(); - var sm = me.getSelectionModel(); - - if (me.selectAll) { - me.mon(store,'load', function(){ - me.getSelectionModel().selectAll(false); - }); - } - } -}); - - -Ext.define('PVE.form.VMComboSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.vmComboSelector', - - valueField: 'vmid', - displayField: 'vmid', - - autoSelect: false, - editable: true, - anyMatch: true, - forceSelection: true, - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - - listConfig: { - width: 600, - plugins: 'gridfilters', - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - hidden: true, - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - un: function(){} // due to EXTJS-18711 - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - hidden: true, - flex: 1, - filter: { - type: 'list' - } - } - ] - } -}); -Ext.define('PVE.form.USBSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUSBSelector'], - allowBlank: false, - autoSelect: false, - displayField: 'usbid', - valueField: 'usbid', - editable: true, - - getUSBValue: function() { - var me = this; - var rec = me.store.findRecord('usbid', me.value); - var val = 'host='+ me.value; - if (rec && rec.data.speed === "5000") { - val = 'host=' + me.value + ",usb3=1"; - } - return val; - }, - - validator: function(value) { - var me = this; - if (me.type === 'device') { - return (/^[a-f0-9]{4}\:[a-f0-9]{4}$/i).test(value); - } else if (me.type === 'port') { - return (/^[0-9]+\-[0-9]+(\.[0-9]+)*$/).test(value); - } - return false; - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - - if (!nodename) { - throw "no nodename specified"; - } - - if (me.type !== 'device' && me.type !== 'port') { - throw "no valid type specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-usb-' + me.type, - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/scan/usb" - }, - filters: [ - function (item) { - return !!item.data.usbpath && !!item.data.prodid && item.data['class'] != 9; - } - ] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: (me.type === 'device')?gettext('Device'):gettext('Port'), - sortable: true, - dataIndex: 'usbid', - width: 80 - }, - { - header: gettext('Manufacturer'), - sortable: true, - dataIndex: 'manufacturer', - width: 100 - }, - { - header: gettext('Product'), - sortable: true, - dataIndex: 'product', - flex: 1 - }, - { - header: gettext('Speed'), - width: 70, - sortable: true, - dataIndex: 'speed', - renderer: function(value) { - if (value === "5000") { - return "USB 3.0"; - } else if (value === "480") { - return "USB 2.0"; - } else { - return "USB 1.x"; - } - } - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-usb-device', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val, data) { - if (val) { - return val; - } - return data.get('vendid') + ':' + data.get('prodid'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' } - ] - }); - - Ext.define('pve-usb-port', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val,data) { - if (val) { - return val; - } - return data.get('busnum') + '-' + data.get('usbpath'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' } - ] - }); -}); -Ext.define('PVE.form.CalendarEvent', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pveCalendarEvent', - - editable: true, - - valueField: 'value', - displayField: 'text', - queryMode: 'local', - - store: { - field: [ 'value', 'text'], - data: [ - { value: '*/30', text: Ext.String.format(gettext("Every {0} minutes"), 30) }, - { value: '*/2:00', text: gettext("Every two hours")}, - { value: '2,22:30', text: gettext("Every day") + " 02:30, 22:30"}, - { value: 'mon..fri', text: gettext("Monday to Friday") + " 00:00"}, - { value: 'mon..fri */1:00', text: gettext("Monday to Friday") + ': ' + gettext("hourly")}, - { value: 'sun 01:00', text: gettext("Sunday") + " 01:00"} - ] - }, - - tpl: [ - '
    ', - '
  • {text}
  • ', - '
' - ], - - displayTpl: [ - '', - '{value}', - '' - ] - -}); -Ext.define('PVE.form.CephPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephPoolSelector', - - allowBlank: false, - valueField: 'pool_name', - displayField: 'pool_name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/pools' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.form.PermPathSelector', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pvePermPathSelector', - - valueField: 'value', - displayField: 'value', - typeAhead: true, - queryMode: 'local', - store: { - type: 'pvePermPath' - } -}); -/* This class defines the "Tasks" tab of the bottom status panel - * Tasks are jobs with a start, end and log output - */ - -Ext.define('PVE.dc.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterTasks'], - - initComponent : function() { - var me = this; - - var taskstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-tasks', - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/tasks' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: taskstore, - sortAfterUpdate: true, - appendAtStart: true, - sorters: [ - { - property : 'pid', - direction: 'DESC' - }, - { - property : 'starttime', - direction: 'DESC' - } - ] - - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type == "vncproxy" || - record.data.type == "vncshell" || - record.data.type == "spiceproxy") { - metaData.tdCls = "x-grid-row-console"; - } else { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type != "vncproxy") { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return Proxmox.Utils.errorText + ': ' + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - show: taskstore.startUpdate, - destroy: taskstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* This class defines the "Cluster log" tab of the bottom status panel - * A log entry is a timestamp associated with an action on a cluster - */ - -Ext.define('PVE.dc.Log', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterLog'], - - initComponent : function() { - var me = this; - - var logstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-log', - model: 'proxmox-cluster-log', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/log' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: logstore, - appendAtStart: true - }); - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, - - getRowClass: function(record, index) { - var pri = record.get('pri'); - - if (pri && pri <= 3) { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Time"), - dataIndex: 'time', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 150 - }, - { - header: gettext("Service"), - dataIndex: 'tag', - width: 100 - }, - { - header: "PID", - dataIndex: 'pid', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Severity"), - dataIndex: 'pri', - renderer: PVE.Utils.render_serverity, - width: 100 - }, - { - header: gettext("Message"), - dataIndex: 'msg', - flex: 1 - } - ], - listeners: { - activate: logstore.startUpdate, - deactivate: logstore.stopUpdate, - destroy: logstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* - * This class describes the bottom panel - */ -Ext.define('PVE.panel.StatusPanel', { - extend: 'Ext.tab.Panel', - alias: 'widget.pveStatusPanel', - - - //title: "Logs", - //tabPosition: 'bottom', - - initComponent: function() { - var me = this; - - var stateid = 'ltab'; - var sp = Ext.state.Manager.getProvider(); - - var state = sp.get(stateid); - if (state && state.value) { - me.activeTab = state.value; - } - - Ext.apply(me, { - listeners: { - tabchange: function() { - var atab = me.getActiveTab().itemId; - var state = { value: atab }; - sp.set(stateid, state); - } - }, - items: [ - { - itemId: 'tasks', - title: gettext('Tasks'), - xtype: 'pveClusterTasks' - }, - { - itemId: 'clog', - title: gettext('Cluster log'), - xtype: 'pveClusterLog' - } - ] - }); - - me.callParent(); - - me.items.get(0).fireEvent('show', me.items.get(0)); - - var statechange = function(sp, key, state) { - if (key === stateid) { - var atab = me.getActiveTab().itemId; - var ntab = state.value; - if (state && ntab && (atab != ntab)) { - me.setActiveTab(ntab); - } - } - }; - - sp.on('statechange', statechange); - me.on('destroy', function() { - sp.un('statechange', statechange); - }); - - } -}); -Ext.define('PVE.panel.StatusView', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStatusView', - - layout: { - type: 'column' - }, - - title: gettext('Status'), - - getRecordValue: function(key, store) { - if (!key) { - throw "no key given"; - } - var me = this; - - if (store === undefined) { - store = me.getStore(); - } - - var rec = store.getById(key); - if (rec) { - return rec.data.value; - } - - return ''; - }, - - fieldRenderer: function(val,max) { - if (max === undefined) { - return val; - } - - if (!Ext.isNumeric(max) || max === 1) { - return PVE.Utils.render_usage(val); - } - return PVE.Utils.render_size_usage(val,max); - }, - - fieldCalculator: function(used, max) { - if (!Ext.isNumeric(max) && Ext.isNumeric(used)) { - return used; - } else if(!Ext.isNumeric(used)) { - /* we come here if the field is from a node - * where the records are not mem and maxmem - * but mem.used and mem.total - */ - if (used.used !== undefined && - used.total !== undefined) { - return used.used/used.total; - } - } - - return used/max; - }, - - updateField: function(field) { - var me = this; - var text = ''; - var renderer = me.fieldRenderer; - if (Ext.isFunction(field.renderer)) { - renderer = field.renderer; - } - if (field.multiField === true) { - field.updateValue(renderer.call(field, me.getStore().getRecord())); - } else if (field.textField !== undefined) { - field.updateValue(renderer.call(field, me.getRecordValue(field.textField))); - } else if(field.valueField !== undefined) { - var used = me.getRecordValue(field.valueField); - /*jslint confusion: true*/ - /* string and int */ - var max = field.maxField !== undefined ? me.getRecordValue(field.maxField) : 1; - - var calculate = me.fieldCalculator; - - if (Ext.isFunction(field.calculate)) { - calculate = field.calculate; - } - field.updateValue(renderer.call(field, used,max), calculate(used,max)); - } - }, - - getStore: function() { - var me = this; - if (!me.rstore) { - throw "there is no rstore"; - } - - return me.rstore; - }, - - updateTitle: function() { - var me = this; - me.setTitle(me.getRecordValue('name')); - }, - - updateValues: function(store, records, success) { - if (!success) { - return; // do not update if store load was not successful - } - var me = this; - var itemsToUpdate = me.query('pveInfoWidget'); - - itemsToUpdate.forEach(me.updateField, me); - - me.updateTitle(store); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - if (!me.title) { - throw "no title given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.callParent(); - - me.mon(me.rstore, 'load', 'updateValues'); - } - -}); -Ext.define('PVE.panel.GuestStatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveGuestStatusView', - mixins: ['Proxmox.Mixin.CBind'], - - height: 300, - - cbindData: function (initialConfig) { - var me = this; - return { - isQemu: me.pveSelNode.data.type === 'qemu', - isLxc: me.pveSelNode.data.type === 'lxc' - }; - }, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'status', - title: gettext('Status'), - iconCls: 'fa fa-info fa-fw', - printBar: false, - multiField: true, - renderer: function(record) { - var me = this; - var text = record.data.status; - var qmpstatus = record.data.qmpstatus; - if (qmpstatus && qmpstatus !== record.data.status) { - text += ' (' + qmpstatus + ')'; - } - return text; - } - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - xtype: 'pveInfoWidget', - itemId: 'node', - iconCls: 'fa fa-building fa-fw', - title: gettext('Node'), - cbind: { - text: '{pveSelNode.data.node}' - }, - printBar: false - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpus', - renderer: PVE.Utils.render_cpu_usage, - // in this specific api call - // we already have the correct value for the usage - calculate: Ext.identityFn - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory usage'), - valueField: 'mem', - maxField: 'maxmem' - }, - { - itemId: 'swap', - xtype: 'pveInfoWidget', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'maxswap', - cbind: { - hidden: '{isQemu}', - disabled: '{isQemu}' - } - }, - { - itemId: 'rootfs', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - valueField: 'disk', - maxField: 'maxdisk', - printBar: false, - renderer: function(used, max) { - var me = this; - me.setPrintBar(used > 0); - if (used === 0) { - return PVE.Utils.render_size(max); - } else { - return PVE.Utils.render_size_usage(used,max); - } - } - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'ips', - xtype: 'pveAgentIPView', - cbind: { - rstore: '{rstore}', - pveSelNode: '{pveSelNode}', - hidden: '{isLxc}', - disabled: '{isLxc}' - } - } - ], - - updateTitle: function() { - var me = this; - var uptime = me.getRecordValue('uptime'); - - var text = ""; - if (Number(uptime) > 0) { - text = " (" + gettext('Uptime') + ': ' + Proxmox.Utils.format_duration_long(uptime) - + ')'; - } - - me.setTitle(me.getRecordValue('name') + text); - } -}); -/* - * This is a running chart widget - * you add time datapoints to it, - * and we only show the last x of it - * used for ceph performance charts - */ -Ext.define('PVE.widget.RunningChart', { - extend: 'Ext.container.Container', - alias: 'widget.pveRunningChart', - - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - width: 80, - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}:

' - }, - { - flex: 1, - xtype: 'cartesian', - height: '100%', - itemId: 'chart', - border: false, - axes: [ - { - type: 'numeric', - position: 'left', - hidden: true, - minimum: 0 - }, - { - type: 'numeric', - position: 'bottom', - hidden: true - } - ], - - store: { - data: {} - }, - - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '0 B/s', - textAlign: 'end', - textBaseline: 'middle', - fontSize: 14 - }], - - series: [{ - type: 'line', - xField: 'time', - yField: 'val', - fill: 'true', - colors: ['#cfcfcf'], - tooltip: { - trackMouse: true, - renderer: function( tooltip, record, ctx) { - var me = this.getChart(); - var date = new Date(record.data.time); - var value = me.up().renderer(record.data.val); - tooltip.setHtml( - me.up().title + ': ' + value + '
' + - Ext.Date.format(date, 'H:i:s') - ); - } - }, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - } - }] - } - ], - - // the renderer for the tooltip and last value, - // default just the value - renderer: Ext.identityFn, - - // show the last x seconds - // default is 5 minutes - timeFrame: 5*60, - - addDataPoint: function(value, time) { - var me = this.chart; - var panel = me.up(); - var now = new Date(); - var begin = new Date(now.getTime() - (1000*panel.timeFrame)); - - me.store.add({ - time: time || now.getTime(), - val: value || 0 - }); - - // delete all old records when we have 20 times more datapoints - // than seconds in our timeframe (so even a subsecond graph does - // not trigger this often) - // - // records in the store do not take much space, but like this, - // we prevent a memory leak when someone has the site open for a long time - // with minimal graphical glitches - if (me.store.count() > panel.timeFrame * 20) { - var oldData = me.store.getData().createFiltered(function(item) { - return item.data.time < begin.getTime(); - }); - - me.store.remove(oldData.getRange()); - } - - me.timeaxis.setMinimum(begin.getTime()); - me.timeaxis.setMaximum(now.getTime()); - me.valuesprite.setText(panel.renderer(value || 0).toString()); - me.valuesprite.setAttributes({ - x: me.getWidth() - 15, - y: me.getHeight()/2 - }, true); - me.redraw(); - }, - - setTitle: function(title) { - this.title = title; - var me = this.getComponent('title'); - me.update({title: title}); - }, - - initComponent: function(){ - var me = this; - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.chart = me.getComponent('chart'); - me.chart.timeaxis = me.chart.getAxes()[1]; - me.chart.valuesprite = me.chart.getSurface('chart').get('valueSprite'); - if (me.color) { - me.chart.series[0].setStyle({ - fill: me.color, - stroke: me.color - }); - } - } -}); -Ext.define('PVE.widget.Info',{ - extend: 'Ext.container.Container', - alias: 'widget.pveInfoWidget', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - value: 0, - maximum: 1, - printBar: true, - items: [ - { - xtype: 'component', - itemId: 'label', - data: { - title: '', - usage: '', - iconCls: undefined - }, - tpl: [ - '
', - '', - ' ', - '', - '{title}
 
{usage}
' - ] - }, - { - height: 2, - border: 0 - }, - { - xtype: 'progressbar', - itemId: 'progress', - height: 5, - value: 0, - animate: true - } - ], - - warningThreshold: 0.6, - criticalThreshold: 0.9, - - setPrintBar: function(enable) { - var me = this; - me.printBar = enable; - me.getComponent('progress').setVisible(enable); - }, - - setIconCls: function(iconCls) { - var me = this; - me.getComponent('label').data.iconCls = iconCls; - }, - - updateValue: function(text, usage) { - var me = this; - var label = me.getComponent('label'); - label.update(Ext.apply(label.data, {title: me.title, usage:text})); - - if (usage !== undefined && - me.printBar && - Ext.isNumeric(usage) && - usage >= 0) { - var progressBar = me.getComponent('progress'); - progressBar.updateProgress(usage, ''); - if (usage > me.criticalThreshold) { - progressBar.removeCls('warning'); - progressBar.addCls('critical'); - } else if (usage > me.warningThreshold) { - progressBar.removeCls('critical'); - progressBar.addCls('warning'); - } else { - progressBar.removeCls('warning'); - progressBar.removeCls('critical'); - } - } - }, - - initComponent: function() { - var me = this; - - if (!me.title) { - throw "no title defined"; - } - - me.callParent(); - - me.getComponent('progress').setVisible(me.printBar); - - me.updateValue(me.text, me.value); - me.setIconCls(me.iconCls); - } - -}); -Ext.define('PVE.panel.TemplateStatusView',{ - extend: 'PVE.panel.StatusView', - alias: 'widget.pveTemplateStatusView', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - printBar: false, - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - itemId: 'node', - iconCls: 'fa fa-fw fa-building', - title: gettext('Node') - }, - { - xtype: 'box', - height: 20 - }, - { - itemId: 'cpus', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('Processors'), - textField: 'cpus' - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory'), - textField: 'maxmem', - renderer: PVE.Utils.render_size - }, - { - itemId: 'swap', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('Swap'), - textField: 'maxswap', - renderer: PVE.Utils.render_size - }, - { - itemId: 'disk', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - textField: 'maxdisk', - renderer: PVE.Utils.render_size - }, - { - xtype: 'box', - height: 20 - } - ], - - initComponent: function() { - var me = this; - - var name = me.pveSelNode.data.name; - if (!name) { - throw "no name specified"; - } - - me.title = name; - - me.callParent(); - if (me.pveSelNode.data.type !== 'lxc') { - me.remove(me.getComponent('swap')); - } - me.getComponent('node').updateValue(me.pveSelNode.data.node); - } -}); -Ext.define('PVE.widget.HealthWidget', { - extend: 'Ext.Component', - alias: 'widget.pveHealthWidget', - - data: { - iconCls: PVE.Utils.get_health_icon(undefined, true), - text: '', - title: '' - }, - - style: { - 'text-align':'center' - }, - - tpl: [ - '

{title}

', - '', - '

', - '{text}' - ], - - updateHealth: function(data) { - var me = this; - me.update(Ext.apply(me.data, data)); - }, - - initComponent: function(){ - var me = this; - - if (me.title) { - me.config.data.title = me.title; - } - - me.callParent(); - } - -}); -/*global u2f*/ -Ext.define('PVE.window.LoginWindow', { - extend: 'Ext.window.Window', - - controller: { - - xclass: 'Ext.app.ViewController', - - onLogon: function() { - var me = this; - - var form = this.lookupReference('loginForm'); - var unField = this.lookupReference('usernameField'); - var saveunField = this.lookupReference('saveunField'); - var view = this.getView(); - - if (!form.isValid()) { - return; - } - - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - - // set or clear username - var sp = Ext.state.Manager.getProvider(); - if (saveunField.getValue() === true) { - sp.set(unField.getStateId(), unField.getValue()); - } else { - sp.clear(unField.getStateId()); - } - sp.set(saveunField.getStateId(), saveunField.getValue()); - - form.submit({ - failure: function(f, resp){ - me.failure(resp); - }, - success: function(f, resp){ - view.el.unmask(); - - var data = resp.result.data; - if (Ext.isDefined(data.NeedTFA)) { - // Store first factor login information first: - data.LoggedOut = true; - Proxmox.Utils.setAuthData(data); - - if (Ext.isDefined(data.U2FChallenge)) { - me.perform_u2f(data); - } else { - me.perform_otp(); - } - } else { - me.success(data); - } - } - }); - - }, - failure: function(resp) { - var me = this; - var view = me.getView(); - view.el.unmask(); - var handler = function() { - var uf = me.lookupReference('usernameField'); - uf.focus(true, true); - }; - - Ext.MessageBox.alert(gettext('Error'), - gettext("Login failed. Please try again"), - handler); - }, - success: function(data) { - var me = this; - var view = me.getView(); - var handler = view.handler || Ext.emptyFn; - handler.call(me, data); - view.close(); - }, - - perform_otp: function() { - var me = this; - var win = Ext.create('PVE.window.TFALoginWindow', { - onLogin: function(value) { - me.finish_tfa(value); - }, - onCancel: function() { - Proxmox.LoggedOut = false; - Proxmox.Utils.authClear(); - me.getView().show(); - } - }); - win.show(); - }, - - perform_u2f: function(data) { - var me = this; - // Show the message: - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Verification'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - var chlg = data.U2FChallenge; - var key = { - version: chlg.version, - keyHandle: chlg.keyHandle - }; - u2f.sign(chlg.appId, chlg.challenge, [key], function(res) { - msg.close(); - if (res.errorCode) { - Proxmox.Utils.authClear(); - Ext.Msg.alert(gettext('Error'), PVE.Utils.render_u2f_error(res.errorCode)); - return; - } - delete res.errorCode; - me.finish_tfa(JSON.stringify(res)); - }); - }, - finish_tfa: function(res) { - var me = this; - var view = me.getView(); - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - var params = { response: res }; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'POST', - timeout: 5000, // it'll delay both success & failure - success: function(resp, opts) { - view.el.unmask(); - // Fill in what we copy over from the 1st factor: - var data = resp.result.data; - data.CSRFPreventionToken = Proxmox.CSRFPreventionToken; - data.username = Proxmox.UserName; - // Finish logging in: - me.success(data); - }, - failure: function(resp, opts) { - Proxmox.Utils.authClear(); - me.failure(resp); - } - }); - }, - - control: { - 'field[name=username]': { - specialkey: function(f, e) { - if (e.getKey() === e.ENTER) { - var pf = this.lookupReference('passwordField'); - if (!pf.getValue()) { - pf.focus(false); - } - } - } - }, - 'field[name=lang]': { - change: function(f, value) { - var dt = Ext.Date.add(new Date(), Ext.Date.YEAR, 10); - Ext.util.Cookies.set('PVELangCookie', value, dt); - this.getView().mask(gettext('Please wait...'), 'x-mask-loading'); - window.location.reload(); - } - }, - 'button[reference=loginButton]': { - click: 'onLogon' - }, - '#': { - show: function() { - var sp = Ext.state.Manager.getProvider(); - var checkboxField = this.lookupReference('saveunField'); - var unField = this.lookupReference('usernameField'); - - var checked = sp.get(checkboxField.getStateId()); - checkboxField.setValue(checked); - - if(checked === true) { - var username = sp.get(unField.getStateId()); - unField.setValue(username); - var pwField = this.lookupReference('passwordField'); - pwField.focus(); - } - } - } - } - }, - - width: 400, - - modal: true, - - border: false, - - draggable: true, - - closable: false, - - resizable: false, - - layout: 'auto', - - title: gettext('Proxmox VE Login'), - - defaultFocus: 'usernameField', - - defaultButton: 'loginButton', - - items: [{ - xtype: 'form', - layout: 'form', - url: '/api2/extjs/access/ticket', - reference: 'loginForm', - - fieldDefaults: { - labelAlign: 'right', - allowBlank: false - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('User name'), - name: 'username', - itemId: 'usernameField', - reference: 'usernameField', - stateId: 'login-username' - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - name: 'password', - reference: 'passwordField' - }, - { - xtype: 'pveRealmComboBox', - name: 'realm' - }, - { - xtype: 'proxmoxLanguageSelector', - fieldLabel: gettext('Language'), - value: Ext.util.Cookies.get('PVELangCookie') || Proxmox.defaultLang || 'en', - name: 'lang', - reference: 'langField', - submitValue: false - } - ], - buttons: [ - { - xtype: 'checkbox', - fieldLabel: gettext('Save User name'), - name: 'saveusername', - reference: 'saveunField', - stateId: 'login-saveusername', - labelWidth: 'auto', - labelAlign: 'right', - submitValue: false - }, - { - text: gettext('Login'), - reference: 'loginButton' - } - ] - }] - }); -Ext.define('PVE.window.TFALoginWindow', { - extend: 'Ext.window.Window', - - modal: true, - resizable: false, - title: 'Two-Factor Authentication', - layout: 'form', - defaultButton: 'loginButton', - defaultFocus: 'otpField', - - controller: { - xclass: 'Ext.app.ViewController', - login: function() { - var me = this; - var view = me.getView(); - view.onLogin(me.lookup('otpField').value); - view.close(); - }, - cancel: function() { - var me = this; - var view = me.getView(); - view.onCancel(); - view.close(); - } - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Please enter your OTP verification code:'), - name: 'otp', - itemId: 'otpField', - reference: 'otpField', - allowBlank: false - } - ], - - buttons: [ - { - text: gettext('Login'), - reference: 'loginButton', - handler: 'login' - }, - { - text: gettext('Cancel'), - handler: 'cancel' - } - ] -}); -Ext.define('PVE.window.Wizard', { - extend: 'Ext.window.Window', - - activeTitle: '', // used for automated testing - - width: 700, - height: 510, - - modal: true, - border: false, - - draggable: true, - closable: true, - resizable: false, - - layout: 'border', - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.down('form').getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - initComponent: function() { - var me = this; - - var tabs = me.items || []; - delete me.items; - - /* - * Items may have the following functions: - * validator(): per tab custom validation - * onSubmit(): submit handler - * onGetValues(): overwrite getValues results - */ - - Ext.Array.each(tabs, function(tab) { - tab.disabled = true; - }); - tabs[0].disabled = false; - - var maxidx = 0; - var curidx = 0; - - var check_card = function(card) { - var valid = true; - var fields = card.query('field, fieldcontainer'); - if (card.isXType('fieldcontainer')) { - fields.unshift(card); - } - Ext.Array.each(fields, function(field) { - // Note: not all fielcontainer have isValid() - if (Ext.isFunction(field.isValid) && !field.isValid()) { - valid = false; - } - }); - - if (Ext.isFunction(card.validator)) { - return card.validator(); - } - - return valid; - }; - - var disable_at = function(card) { - var tp = me.down('#wizcontent'); - var idx = tp.items.indexOf(card); - for(;idx < tp.items.getCount();idx++) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }; - - var tabchange = function(tp, newcard, oldcard) { - if (newcard.onSubmit) { - me.down('#next').setVisible(false); - me.down('#submit').setVisible(true); - } else { - me.down('#next').setVisible(true); - me.down('#submit').setVisible(false); - } - var valid = check_card(newcard); - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - me.down('#back').setDisabled(tp.items.indexOf(newcard) == 0); - - var idx = tp.items.indexOf(newcard); - if (idx > maxidx) { - maxidx = idx; - } - curidx = idx; - - var next = idx + 1; - var ntab = tp.items.getAt(next); - if (valid && ntab && !newcard.onSubmit) { - ntab.enable(); - } - }; - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, true, false); - } - - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - - Ext.apply(me, { - items: [ - { - xtype: 'form', - region: 'center', - layout: 'fit', - border: false, - margins: '5 5 0 5', - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [{ - itemId: 'wizcontent', - xtype: 'tabpanel', - activeItem: 0, - bodyPadding: 10, - listeners: { - afterrender: function(tp) { - var atab = this.getActiveTab(); - tabchange(tp, atab); - }, - tabchange: function(tp, newcard, oldcard) { - tabchange(tp, newcard, oldcard); - } - }, - items: tabs - }] - } - ], - fbar: [ - { - xtype: 'proxmoxHelpButton', - itemId: 'help' - }, - '->', - { - xtype: 'proxmoxcheckbox', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - value: advchecked, - listeners: { - change: function(cb, val) { - var tp = me.down('#wizcontent'); - tp.query('inputpanel').forEach(function(ip) { - ip.setAdvancedVisible(val); - }); - - sp.set('proxmox-advanced-cb', val); - } - } - }, - { - text: gettext('Back'), - disabled: true, - itemId: 'back', - minWidth: 60, - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - var prev = tp.items.indexOf(atab) - 1; - if (prev < 0) { - return; - } - var ntab = tp.items.getAt(prev); - if (ntab) { - tp.setActiveTab(ntab); - } - } - }, - { - text: gettext('Next'), - disabled: true, - itemId: 'next', - minWidth: 60, - handler: function() { - - var form = me.down('form').getForm(); - - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - if (!check_card(atab)) { - return; - } - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - - } - }, - { - text: gettext('Finish'), - minWidth: 60, - hidden: true, - itemId: 'submit', - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - atab.onSubmit(); - } - } - ] - }); - me.callParent(); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setAdvancedVisible(advchecked); - }); - - Ext.Array.each(me.query('field'), function(field) { - var validcheck = function() { - var tp = me.down('#wizcontent'); - - // check tabs from current to the last enabled for validity - // since we might have changed a validity on a later one - var i; - for (i = curidx; i <= maxidx && i < tp.items.getCount(); i++) { - var tab = tp.items.getAt(i); - var valid = check_card(tab); - - // only set the buttons on the current panel - if (i === curidx) { - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - } - - // if a panel is invalid, then disable it and all following, - // else enable it and go to the next - var ntab = tp.items.getAt(i + 1); - if (!valid) { - disable_at(ntab); - return; - } else if (ntab && !tab.onSubmit) { - ntab.enable(); - } - } - }; - field.on('change', validcheck); - field.on('validitychange', validcheck); - }); - } -}); -Ext.define('PVE.window.NotesEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - title: gettext('Notes'), - width: 600, - height: '400px', - resizable: true, - layout: 'fit', - defaultButton: undefined, - items: { - xtype: 'textarea', - name: 'description', - height: '100%', - value: '', - hideLabel: true - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.window.Backup', { - extend: 'Ext.window.Window', - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.vmtype) { - throw "no VM type specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: me.storage, - fieldLabel: gettext('Storage'), - storageContent: 'backup', - allowBlank: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - storagesel, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'pveCompressionSelector', - name: 'compress', - value: 'lzo', - fieldLabel: gettext('Compression') - }, - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto', - emptyText: Proxmox.Utils.noneText - } - ] - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Backup'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - var params = { - storage: storage, - vmid: me.vmid, - mode: values.mode, - remove: 0 - }; - - if ( values.mailto ) { - params.mailto = values.mailto; - } - - if (values.compress) { - params.compress = values.compress; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/vzdump', - params: params, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - }, - success: function(response, options) { - // close later so we reload the grid - // after the task has completed - me.hide(); - - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - close: function() { - me.close(); - } - } - }); - win.show(); - } - }); - } - }); - - var helpBtn = Ext.create('Proxmox.button.Help', { - onlineHelp: 'chapter_vzdump', - listenToGlobalEvent: false, - hidden: false - }); - - var title = gettext('Backup') + " " + - ((me.vmtype === 'lxc') ? "CT" : "VM") + - " " + me.vmid; - - Ext.apply(me, { - title: title, - width: 350, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ helpBtn, '->', submitBtn ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.window.Restore', { - extend: 'Ext.window.Window', // fixme: Proxmox.window.Edit? - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.volid) { - throw "no volume ID specified"; - } - - if (!me.vmtype) { - throw "no vmtype specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: '', - fieldLabel: gettext('Storage'), - storageContent: (me.vmtype === 'lxc') ? 'rootdir' : 'images', - allowBlank: true - }); - - var IDfield; - if (me.vmid) { - IDfield = Ext.create('Ext.form.field.Display', { - name: 'vmid', - value: me.vmid, - fieldLabel: (me.vmtype === 'lxc') ? 'CT' : 'VM' - }); - } else { - IDfield = Ext.create('PVE.form.GuestIDSelector', { - name: 'vmid', - guestType: me.vmtype, - loadNextFreeID: true, - validateExists: false - }); - } - - var items = [ - { - xtype: 'displayfield', - value: me.volidText || me.volid, - fieldLabel: gettext('Source') - }, - storagesel, - IDfield, - { - xtype: 'proxmoxintegerfield', - name: 'bwlimit', - fieldLabel: gettext('Read Limit (MiB/s)'), - minValue: 0, - emptyText: gettext('Defaults to target storage restore limit'), - autoEl: { - tag: 'div', - 'data-qtip': gettext("Use '0' to disable all bandwidth limits.") - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'unique', - fieldLabel: gettext('Unique'), - hidden: !!me.vmid, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses') - }, - checked: false - } - ]; - - /*jslint confusion: true*/ - if (me.vmtype === 'lxc') { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - fieldLabel: gettext('Unprivileged container') - }); - } - /*jslint confusion: false*/ - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var doRestore = function(url, params) { - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - waitMsgTarget: me, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.close(); - } - }); - }; - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Restore'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - - var params = { - storage: storage, - vmid: me.vmid || values.vmid, - force: me.vmid ? 1 : 0 - }; - if (values.unique) { params.unique = 1; } - - if (values.bwlimit !== undefined) { - params.bwlimit = values.bwlimit * 1024; - } - - var url; - var msg; - if (me.vmtype === 'lxc') { - url = '/nodes/' + me.nodename + '/lxc'; - params.ostemplate = me.volid; - params.restore = 1; - if (values.unprivileged) { params.unprivileged = 1; } - msg = Proxmox.Utils.format_task_description('vzrestore', params.vmid); - } else if (me.vmtype === 'qemu') { - url = '/nodes/' + me.nodename + '/qemu'; - params.archive = me.volid; - msg = Proxmox.Utils.format_task_description('qmrestore', params.vmid); - } else { - throw 'unknown VM type'; - } - - if (me.vmid) { - msg += '. ' + gettext('This will permanently erase current VM data.'); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - doRestore(url, params); - }); - } else { - doRestore(url, params); - } - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - var title = gettext('Restore') + ": " + ( - (me.vmtype === 'lxc') ? 'CT' : 'VM'); - - if (me.vmid) { - title += " " + me.vmid; - } - - Ext.apply(me, { - title: title, - width: 500, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); -/* Popup a message window - * where the user has to manually enter the ressource ID - * to enable the destroy button - */ -Ext.define('PVE.window.SafeDestroy', { - extend: 'Ext.window.Window', - alias: 'widget.pveSafeDestroy', - - title: gettext('Confirm'), - modal: true, - buttonAlign: 'center', - bodyPadding: 10, - width: 450, - layout: { type:'hbox' }, - defaultFocus: 'confirmField', - showProgress: false, - - config: { - item: { - id: undefined, - type: undefined - }, - url: undefined, - params: {} - }, - - getParams: function() { - var me = this; - if (Ext.Object.isEmpty(me.params)) { - return ''; - } - return '?' + Ext.Object.toQueryString(me.params); - }, - - controller: { - - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=confirm]': { - change: function(f, value) { - var view = this.getView(); - var removeButton = this.lookupReference('removeButton'); - if (value === view.getItem().id.toString()) { - removeButton.enable(); - } else { - removeButton.disable(); - } - }, - specialkey: function (field, event) { - var removeButton = this.lookupReference('removeButton'); - if (!removeButton.isDisabled() && event.getKey() == event.ENTER) { - removeButton.fireEvent('click', removeButton, event); - } - } - }, - 'button[reference=removeButton]': { - click: function() { - var view = this.getView(); - Proxmox.Utils.API2Request({ - url: view.getUrl() + view.getParams(), - method: 'DELETE', - waitMsgTarget: view, - failure: function(response, opts) { - view.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = view.showProgress && - response.result.data ? true : false; - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - view.hide(); - - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: function () { - view.close(); - } - } - }); - win.show(); - } else { - view.close(); - } - } - }); - } - } - } - }, - - items: [ - { - xtype: 'component', - cls: [ Ext.baseCSSPrefix + 'message-box-icon', - Ext.baseCSSPrefix + 'message-box-warning', - Ext.baseCSSPrefix + 'dlg-icon'] - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'component', - reference: 'messageCmp' - }, - { - itemId: 'confirmField', - reference: 'confirmField', - xtype: 'textfield', - name: 'confirm', - labelWidth: 300, - hideTrigger: true, - allowBlank: false - } - ] - } - ], - buttons: [ - { - reference: 'removeButton', - text: gettext('Remove'), - disabled: true - } - ], - - initComponent : function() { - var me = this; - - me.callParent(); - - var item = me.getItem(); - - if (!Ext.isDefined(item.id)) { - throw "no ID specified"; - } - - if (!Ext.isDefined(item.type)) { - throw "no VM type specified"; - } - - var messageCmp = me.lookupReference('messageCmp'); - var msg; - - if (item.type === 'VM') { - msg = Proxmox.Utils.format_task_description('qmdestroy', item.id); - } else if (item.type === 'CT') { - msg = Proxmox.Utils.format_task_description('vzdestroy', item.id); - } else if (item.type === 'CephPool') { - msg = Proxmox.Utils.format_task_description('cephdestroypool', item.id); - } else if (item.type === 'Image') { - msg = Proxmox.Utils.format_task_description('unknownimgdel', item.id); - } else { - throw "unknown item type specified"; - } - - messageCmp.setHtml(msg); - - var confirmField = me.lookupReference('confirmField'); - msg = gettext('Please enter the ID to confirm') + - ' (' + item.id + ')'; - confirmField.setFieldLabel(msg); - } -}); -Ext.define('PVE.window.BackupConfig', { - extend: 'Ext.window.Window', - title: gettext('Configuration'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: { - xtype: 'component', - itemId: 'configtext', - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }, - - initComponent: function() { - var me = this; - - if (!me.volume) { - throw "no volume specified"; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.callParent(); - - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/vzdump/extractconfig", - method: 'GET', - params: { - volume: me.volume - }, - failure: function(response, opts) { - me.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response,options) { - me.show(); - me.down('#configtext').update(Ext.htmlEncode(response.result.data)); - } - }); - } -}); -Ext.define('PVE.window.Settings', { - extend: 'Ext.window.Window', - - width: '800px', - title: gettext('My Settings'), - iconCls: 'fa fa-gear', - modal: true, - bodyPadding: 10, - resizable: false, - - buttons: [ - { - xtype: 'proxmoxHelpButton', - onlineHelp: 'gui_my_settings', - hidden: false - }, - '->', - { - text: gettext('Close'), - handler: function() { - this.up('window').close(); - } - } - ], - - layout: { - type: 'hbox', - align: 'top' - }, - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - var username = sp.get('login-username') || Proxmox.Utils.noneText; - me.lookupReference('savedUserName').setValue(username); - - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var val = localStorage.getItem('pve-xterm-' + setting); - if (val !== undefined && val !== null) { - var field = me.lookup(setting); - field.setValue(val); - field.resetOriginalValue(); - } - }); - }, - - set_button_status: function() { - var me = this; - - var form = me.lookup('xtermform'); - var valid = form.isValid(); - var dirty = form.isDirty(); - - var hasvalues = false; - var values = form.getValues(); - Ext.Object.eachValue(values, function(value) { - if (value) { - hasvalues = true; - return false; - } - }); - - me.lookup('xtermsave').setDisabled(!dirty || !valid); - me.lookup('xtermreset').setDisabled(!hasvalues); - }, - - control: { - '#xtermjs form': { - dirtychange: 'set_button_status', - validitychange: 'set_button_status' - }, - '#xtermjs button': { - click: function(button) { - var me = this; - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var field = me.lookup(setting); - if (button.reference === 'xtermsave') { - var value = field.getValue(); - if (value) { - localStorage.setItem('pve-xterm-' + setting, value); - } else { - localStorage.removeItem('pve-xterm-' + setting); - } - } else if (button.reference === 'xtermreset') { - field.setValue(undefined); - localStorage.removeItem('pve-xterm-' + setting); - } - field.resetOriginalValue(); - }); - me.set_button_status(); - } - }, - 'button[name=reset]': { - click: function () { - var blacklist = ['GuiCap', 'login-username', 'dash-storages']; - var sp = Ext.state.Manager.getProvider(); - var state; - for (state in sp.state) { - if (sp.state.hasOwnProperty(state)) { - if (blacklist.indexOf(state) !== -1) { - continue; - } - - sp.clear(state); - } - } - - window.location.reload(); - } - }, - 'button[name=clear-username]': { - click: function () { - var me = this; - var usernamefield = me.lookupReference('savedUserName'); - var sp = Ext.state.Manager.getProvider(); - - usernamefield.setValue(Proxmox.Utils.noneText); - sp.clear('login-username'); - } - }, - 'grid[reference=dashboard-storages]': { - selectionchange: function(grid, selected) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - // saves the selected storageids as - // "id1,id2,id3,..." - // or clears the variable - if (selected.length > 0) { - sp.set('dash-storages', - Ext.Array.pluck(selected, 'id').join(',')); - } else { - sp.clear('dash-storages'); - } - }, - afterrender: function(grid) { - var me = grid; - var sp = Ext.state.Manager.getProvider(); - var store = me.getStore(); - var items = []; - me.suspendEvent('selectionchange'); - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - // we have to get the records - // to be able to select them - if (storage !== '') { - var item = store.getById(storage); - if (item) { - items.push(item); - } - } - }); - me.getSelectionModel().select(items); - me.resumeEvent('selectionchange'); - } - } - } - }, - - items: [{ - xtype: 'fieldset', - width: '50%', - title: gettext('Webinterface Settings'), - margin: '5', - layout: { - type: 'vbox', - align: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Dashboard Storages'), - labelAlign: 'left', - labelWidth: '50%' - }, - { - xtype: 'grid', - maxHeight: 150, - reference: 'dashboard-storages', - selModel: { - selType: 'checkboxmodel' - }, - columns: [{ - header: gettext('Name'), - dataIndex: 'storage', - flex: 1 - },{ - header: gettext('Node'), - dataIndex: 'node', - flex: 1 - }], - store: { - type: 'diff', - field: ['type', 'storage', 'id', 'node'], - rstore: PVE.data.ResourceStore, - filters: [{ - property: 'type', - value: 'storage' - }], - sorters: [ 'node','storage'] - } - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Saved User name'), - labelAlign: 'left', - labelWidth: '50%', - stateId: 'login-username', - reference: 'savedUserName', - value: '' - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Clear User name'), - width: 'auto', - name: 'clear-username' - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Layout'), - labelAlign: 'left', - labelWidth: '50%' - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Reset Layout'), - width: 'auto', - name: 'reset' - } - ] - },{ - xtype: 'fieldset', - itemId: 'xtermjs', - width: '50%', - margin: '5', - title: gettext('xterm.js Settings'), - items: [{ - xtype: 'form', - reference: 'xtermform', - border: false, - layout: { - type: 'vbox', - algin: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0' - }, - items: [ - { - xtype: 'textfield', - name: 'fontFamily', - reference: 'fontFamily', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Font-Family') - }, - { - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - name: 'fontSize', - reference: 'fontSize', - minValue: 1, - fieldLabel: gettext('Font-Size') - }, - { - xtype: 'numberfield', - name: 'letterSpacing', - reference: 'letterSpacing', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Letter Spacing') - }, - { - xtype: 'numberfield', - name: 'lineHeight', - minValue: 0.1, - reference: 'lineHeight', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Line Height') - }, - { - xtype: 'container', - layout: { - type: 'hbox', - pack: 'end' - }, - items: [ - { - xtype: 'button', - reference: 'xtermreset', - disabled: true, - text: gettext('Reset') - }, - { - xtype: 'button', - reference: 'xtermsave', - disabled: true, - text: gettext('Save') - } - ] - } - ] - }] - }], - - onShow: function() { - var me = this; - me.callParent(); - } -}); -Ext.define('PVE.panel.StartupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'qm_startup_and_shutdown', - - onGetValues: function(values) { - var me = this; - - var res = PVE.Parser.printStartup(values); - - if (res === undefined || res === '') { - return { 'delete': 'startup' }; - } - - return { startup: res }; - }, - - setStartup: function(value) { - var me = this; - - var startup = PVE.Parser.parseStartup(value); - if (startup) { - me.setValues(startup); - } - }, - - initComponent : function() { - var me = this; - - me.items = [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Shutdown timeout') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.window.StartupEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveWindowStartupEdit', - onlineHelp: undefined, - - initComponent : function() { - - var me = this; - var ipanelConfig = me.onlineHelp ? {onlineHelp: me.onlineHelp} : {}; - var ipanel = Ext.create('PVE.panel.StartupInputPanel', ipanelConfig); - - Ext.applyIf(me, { - subject: gettext('Start/Shutdown order'), - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - ipanel.setStartup(me.vmconfig.startup); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.Install', { - extend: 'Ext.window.Window', - xtype: 'pveCephInstallWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 220, - header: false, - resizable: false, - draggable: false, - modal: true, - nodename: undefined, - shadow: false, - border: false, - bodyBorder: false, - closable: false, - cls: 'install-mask', - bodyCls: 'install-mask', - layout: { - align: 'stretch', - pack: 'center', - type: 'vbox' - }, - viewModel: { - data: { - cephVersion: 'luminous', - isInstalled: false - }, - formulas: { - buttonText: function (get){ - if (get('isInstalled')) { - return gettext('Configure Ceph'); - } else { - return gettext('Install Ceph-') + get('cephVersion'); - } - }, - windowText: function (get) { - if (get('isInstalled')) { - return '

' + - Ext.String.format(gettext('{0} is not initialized.'), 'Ceph') + ' '+ - gettext('You need to create a initial config once.') + '

'; - } else { - return '

' + - Ext.String.format(gettext('{0} is not installed on this node.'), 'Ceph') + '
' + - gettext('Would you like to install it now?') + '

'; - } - } - } - }, - items: [ - { - bind: { - html: '{windowText}' - }, - border: false, - padding: 5, - bodyCls: 'install-mask' - - }, - { - xtype: 'button', - bind: { - text: '{buttonText}' - }, - viewModel: {}, - cbind: { - nodename: '{nodename}' - }, - handler: function() { - var me = this.up('pveCephInstallWindow'); - var win = Ext.create('PVE.ceph.CephInstallWizard',{ - nodename: me.nodename - }); - win.getViewModel().set('isInstalled', this.getViewModel().get('isInstalled')); - win.show(); - me.mon(win,'beforeClose', function(){ - me.fireEvent("cephInstallWindowClosed"); - me.close(); - }); - - } - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallEnableEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveFirewallEnableEdit'], - mixins: ['Proxmox.Mixin.CBind'], - - subject: gettext('Firewall'), - cbindData: { - defaultValue: 0 - }, - width: 350, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - uncheckedValue: 0, - cbind: { - defaultValue: '{defaultValue}', - checked: '{defaultValue}' - }, - deleteDefaultValue: false, - fieldLabel: gettext('Firewall') - }, - { - xtype: 'displayfield', - name: 'warning', - userCls: 'pve-hint', - value: gettext('Warning: Firewall still disabled at datacenter level!'), - hidden: true - } - ], - - beforeShow: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/cluster/firewall/options', - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - if (!response.result.data.enable) { - me.down('displayfield[name=warning]').setVisible(true); - } - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallLograteInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveFirewallLograteInputPanel', - - viewModel: {}, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - reference: 'enable', - fieldLabel: gettext('Enable'), - value: false - }, - { - layout: 'hbox', - border: false, - items: [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Log rate limit'), - minValue: 1, - maxValue: 99, - allowBlank: false, - flex: 2, - value: 1 - }, - { - html: '
/
' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'unit', - comboItems: [['second', 'second'], ['minute', 'minute'], - ['hour', 'hour'], ['day', 'day']], - allowBlank: false, - flex: 1, - value: 'second' - } - ] - }, - { - xtype: 'numberfield', - name: 'burst', - fieldLabel: gettext('Log burst limit'), - minValue: 1, - maxValue: 99, - value: 5 - } - ], - - onGetValues: function(values) { - var me = this; - - var vals = {}; - vals.enable = values.enable !== undefined ? 1 : 0; - vals.rate = values.rate + '/' + values.unit; - vals.burst = values.burst; - var properties = PVE.Parser.printPropertyString(vals, undefined); - if (properties == '') { - return { 'delete': 'log_ratelimit' }; - } - return { log_ratelimit: properties }; - }, - - setValues: function(values) { - var me = this; - - var properties = {}; - if (values.log_ratelimit !== undefined) { - properties = PVE.Parser.parsePropertyString(values.log_ratelimit); - var matches = properties.rate.match(/^(\d+)\/(second|minute|hour|day)$/); - if (matches) { - properties.rate = matches[1]; - properties.unit = matches[2]; - } - } - me.callParent([properties]); - } -}); - -Ext.define('PVE.FirewallLograteEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveFirewallLograteEdit', - - subject: gettext('Log rate limit'), - - items: [{ - xtype: 'pveFirewallLograteInputPanel' - }], - autoLoad: true -}); -Ext.define('PVE.panel.NotesView', { - extend: 'Ext.panel.Panel', - xtype: 'pveNotesView', - - title: gettext("Notes"), - bodyStyle: 'white-space:pre', - bodyPadding: 10, - scrollable: true, - - tbar: { - itemId: 'tbar', - hidden: true, - items: [ - { - text: gettext('Edit'), - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - } - ] - }, - - run_editor: function() { - var me = this; - var win = Ext.create('PVE.window.NotesEdit', { - pveSelNode: me.pveSelNode, - url: me.url - }); - win.show(); - win.on('destroy', me.load, me); - }, - - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data.description || ''; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - listeners: { - render: function(c) { - var me = this; - me.getEl().on('dblclick', me.run_editor, me); - } - }, - - tools: [{ - type: 'gear', - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - }], - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var type = me.pveSelNode.data.type; - if (!Ext.Array.contains(['node', 'qemu', 'lxc'], type)) { - throw 'invalid type specified'; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid && type !== 'node') { - throw "no VM ID specified"; - } - - me.url = '/api2/extjs/nodes/' + nodename + '/'; - - // add the type specific path if qemu/lxc - if (type === 'qemu' || type === 'lxc') { - me.url += type + '/' + vmid + '/'; - } - - me.url += 'config'; - - me.callParent(); - if (type === 'node') { - me.down('#tbar').setVisible(true); - } - me.load(); - } -}); -Ext.define('PVE.grid.ResourceGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveResourceGrid'], - - border: false, - defaultSorter: { - property: 'type', - direction: 'ASC' - }, - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - var coldef = rstore.defaultColumns(); - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: me.defaultSorter, - proxy: { type: 'memory' } - }); - - var textfilter = ''; - - var textfilter_match = function(item) { - var match = false; - Ext.each(['name', 'storage', 'node', 'type', 'text'], function(field) { - var v = item.data[field]; - if (v !== undefined) { - v = v.toLowerCase(); - if (v.indexOf(textfilter) >= 0) { - match = true; - return false; - } - } - }); - return match; - }; - - var updateGrid = function() { - - var filterfn = me.viewFilter ? me.viewFilter.filterfn : null; - - //console.log("START GRID UPDATE " + me.viewFilter); - - store.suspendEvents(); - - var nodeidx = {}; - var gather_child_nodes = function(cn) { - if (!cn) { - return; - } - var cs = cn.childNodes; - if (!cs) { - return; - } - var len = cs.length, i = 0, n, res; - - for (; i < len; i++) { - var child = cs[i]; - var orgnode = rstore.data.get(child.data.id); - if (orgnode) { - if ((!filterfn || filterfn(child)) && - (!textfilter || textfilter_match(child))) { - nodeidx[child.data.id] = orgnode; - } - } - gather_child_nodes(child); - } - }; - gather_child_nodes(me.pveSelNode); - - // remove vanished items - var rmlist = []; - store.each(function(olditem) { - var item = nodeidx[olditem.data.id]; - if (!item) { - //console.log("GRID REM UID: " + olditem.data.id); - rmlist.push(olditem); - } - }); - - if (rmlist.length) { - store.remove(rmlist); - } - - // add new items - var addlist = []; - var key; - for (key in nodeidx) { - if (nodeidx.hasOwnProperty(key)) { - var item = nodeidx[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var olditem = store.getById(item.data.id); - var olditem = store.data.get(item.data.id); - - if (!olditem) { - //console.log("GRID ADD UID: " + item.data.id); - var info = Ext.apply({}, item.data); - var child = Ext.create(store.model, info); - addlist.push(item); - continue; - } - // try to detect changes - var changes = false; - var fieldkeys = PVE.data.ResourceStore.fieldNames; - var fieldcount = fieldkeys.length; - var fieldind; - for (fieldind = 0; fieldind < fieldcount; fieldind++) { - var field = fieldkeys[fieldind]; - if (field != 'id' && item.data[field] != olditem.data[field]) { - changes = true; - //console.log("changed item " + item.id + " " + field + " " + item.data[field] + " != " + olditem.data[field]); - olditem.beginEdit(); - olditem.set(field, item.data[field]); - } - } - if (changes) { - olditem.endEdit(true); - olditem.commit(true); - } - } - } - - if (addlist.length) { - store.add(addlist); - } - - store.sort(); - - store.resumeEvents(); - - store.fireEvent('refresh', store); - - //console.log("END GRID UPDATE"); - }; - - var filter_task = new Ext.util.DelayedTask(function(){ - updateGrid(); - }); - - var load_cb = function() { - updateGrid(); - }; - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-resource', - tbar: [ - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - value: textfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - var v = field.getValue(); - textfilter = v.toLowerCase(); - filter_task.delay(500); - } - } - } - ], - viewConfig: { - stripeRows: true - }, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - destroy: function() { - rstore.un("load", load_cb); - } - }, - columns: coldef - }); - me.callParent(); - updateGrid(); - rstore.on("load", load_cb); - } -}); -Ext.define('PVE.pool.AddVM', { - extend: 'Proxmox.window.Edit', - width: 600, - height: 400, - isAdd: true, - isCreate: true, - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - var vmsField = Ext.create('Ext.form.field.Text', { - name: 'vms', - hidden: true, - allowBlank: false - }); - - var vmStore = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property: 'vmid', - order: 'ASC' - } - ], - filters: [ - function(item) { - return ((item.data.type === 'lxc' || item.data.type === 'qemu') && item.data.pool === ''); - } - ] - }); - - var vmGrid = Ext.create('widget.grid',{ - store: vmStore, - border: true, - height: 300, - scrollable: true, - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected, opts) { - var selectedVms = []; - selected.forEach(function(vm) { - selectedVms.push(vm.data.vmid); - }); - vmsField.setValue(selectedVms); - } - } - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - Ext.apply(me, { - subject: gettext('Virtual Machine'), - items: [ vmsField, vmGrid ] - }); - - me.callParent(); - vmStore.load(); - } -}); - -Ext.define('PVE.pool.AddStorage', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.isCreate = true; - me.isAdd = true; - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - Ext.apply(me, { - subject: gettext('Storage'), - width: 350, - items: [ - { - xtype: 'pveStorageSelector', - name: 'storage', - nodename: 'localhost', - autoSelect: false, - value: '', - fieldLabel: gettext("Storage") - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.grid.PoolMembers', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pvePoolMembers'], - - // fixme: dynamic status update ? - - stateful: true, - stateId: 'grid-pool-members', - - initComponent : function() { - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ], - proxy: { - type: 'proxmox', - root: 'data.members', - url: "/api2/json/pools/" + me.pool - } - }); - - var coldef = PVE.data.ResourceStore.defaultColumns(); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - var params = { 'delete': 1 }; - if (rec.data.type === 'storage') { - params.storage = rec.data.storage; - } else if (rec.data.type === 'qemu' || rec.data.type === 'lxc' || rec.data.type === 'openvz') { - params.vms = rec.data.vmid; - } else { - throw "unknown resource type"; - } - - Proxmox.Utils.API2Request({ - url: '/pools/' + me.pool, - method: 'PUT', - params: params, - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Virtual Machine'), - iconCls: 'pve-itype-icon-qemu', - handler: function() { - var win = Ext.create('PVE.pool.AddVM', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Storage'), - iconCls: 'pve-itype-icon-storage', - handler: function() { - var win = Ext.create('PVE.pool.AddStorage', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - remove_btn - ], - viewConfig: { - stripeRows: true - }, - columns: coldef, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.FWMacroSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFWMacroSelector', - allowBlank: true, - autoSelect: false, - valueField: 'macro', - displayField: 'macro', - listConfig: { - columns: [ - { - header: gettext('Macro'), - dataIndex: 'macro', - hideable: false, - width: 100 - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - flex: 1, - dataIndex: 'descr' - } - ] - }, - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'macro', 'descr' ], - idProperty: 'macro', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/macros" - }, - sorters: { - property: 'macro', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRulePanel', { - extend: 'Proxmox.panel.InputPanel', - - allow_iface: false, - - list_refs_url: undefined, - - onGetValues: function(values) { - var me = this; - - // hack: editable ComboGrid returns nothing when empty, so we need to set '' - // Also, disabled text fields return nothing, so we need to set '' - - Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'log'], function(key) { - if (values[key] === undefined) { - values[key] = ''; - } - }); - - delete values.modified_marker; - - return values; - }, - - initComponent : function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.column1 = [ - { - // hack: we use this field to mark the form 'dirty' when the - // record has errors- so that the user can safe the unmodified - // form again. - xtype: 'hiddenfield', - name: 'modified_marker', - value: '' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'type', - value: 'in', - comboItems: [['in', 'in'], ['out', 'out']], - fieldLabel: gettext('Direction'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - name: 'action', - value: 'ACCEPT', - comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']], - fieldLabel: gettext('Action'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - me.column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } else { - me.column1.push({ - xtype: 'displayfield', - fieldLabel: '', - value: '' - }); - } - - me.column1.push( - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'pveIPRefSelector', - name: 'source', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Source') - - }, - { - xtype: 'pveIPRefSelector', - name: 'dest', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Destination') - } - ); - - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - }, - { - xtype: 'pveFWMacroSelector', - name: 'macro', - fieldLabel: gettext('Macro'), - editable: true, - allowBlank: true, - listeners: { - change: function(f, value) { - if (value === null) { - me.down('field[name=proto]').setDisabled(false); - me.down('field[name=sport]').setDisabled(false); - me.down('field[name=dport]').setDisabled(false); - } else { - me.down('field[name=proto]').setDisabled(true); - me.down('field[name=proto]').setValue(''); - me.down('field[name=sport]').setDisabled(true); - me.down('field[name=sport]').setValue(''); - me.down('field[name=dport]').setDisabled(true); - me.down('field[name=dport]').setValue(''); - } - } - } - }, - { - xtype: 'pveIPProtocolSelector', - name: 'proto', - autoSelect: false, - editable: true, - value: '', - fieldLabel: gettext('Protocol') - }, - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'textfield', - name: 'sport', - value: '', - fieldLabel: gettext('Source port') - }, - { - xtype: 'textfield', - name: 'dport', - value: '', - fieldLabel: gettext('Dest. port') - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'pveFirewallLogLevels' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - list_refs_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.FirewallRulePanel', { - isCreate: me.isCreate, - list_refs_url: me.list_refs_url, - allow_iface: me.allow_iface, - rule_pos: me.rule_pos - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - if (values.errors) { - var field = me.query('[isFormField][name=modified_marker]')[0]; - field.setValue(1); - Ext.Function.defer(function() { - var form = ipanel.up('form').getForm(); - form.markInvalid(values.errors); - }, 100); - } - } - }); - } else if (me.rec) { - ipanel.setValues(me.rec.data); - } - } -}); - -Ext.define('PVE.FirewallGroupRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var column1 = [ - { - xtype: 'hiddenfield', - name: 'type', - value: 'group' - }, - { - xtype: 'pveSecurityGroupsSelector', - name: 'action', - value: '', - fieldLabel: gettext('Security Group'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.FirewallRules', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveFirewallRules', - - onlineHelp: 'chapter_pve_firewall', - - stateful: true, - stateId: 'grid-firewall-rules', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - groupBtn: undefined, - - tbar_prefix: undefined, - - allow_groups: true, - allow_iface: false, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - if (me.groupBtn) { - me.groupBtn.setDisabled(true); - } - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - if (me.groupBtn) { - me.groupBtn.setDisabled(false); - } - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - moveRule: function(from, to) { - var me = this; - - if (!me.base_url) { - return; - } - - Proxmox.Utils.API2Request({ - url: me.base_url + "/" + from, - method: 'PUT', - params: { moveto: to }, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - updateRule: function(rule) { - var me = this; - - if (!me.base_url) { - return; - } - - rule.enable = rule.enable ? 1 : 0; - - var pos = rule.pos; - delete rule.pos; - delete rule.errors; - - Proxmox.Utils.API2Request({ - url: me.base_url + '/' + pos.toString(), - method: 'PUT', - params: rule, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-fw-rule' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type; - - var editor; - if (type === 'in' || type === 'out') { - editor = 'PVE.FirewallRuleEdit'; - } else if (type === 'group') { - editor = 'PVE.FirewallGroupRuleEdit'; - } else { - return; - } - - var win = Ext.create(editor, { - digest: rec.data.digest, - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rule_pos: rec.data.pos - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - var run_copy_editor = function() { - var rec = sm.getSelection()[0]; - - if (!rec) { - return; - } - var type = rec.data.type; - - - if (!(type === 'in' || type === 'out')) { - return; - } - - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rec: rec - }); - - win.show(); - win.on('destroy', reload); - }; - - me.copyBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Copy'), - selModel: sm, - enableFn: function(rec) { - return (rec.data.type === 'in' || rec.data.type === 'out'); - }, - disabled: true, - handler: run_copy_editor - }); - - if (me.allow_groups) { - me.groupBtn = Ext.create('Ext.Button', { - text: gettext('Insert') + ': ' + - gettext('Security Group'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallGroupRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - baseurl: me.base_url + '/', - confirmMsg: false, - getRecordName: function(rec) { - var rule = rec.data; - return rule.pos.toString() + - '?digest=' + encodeURIComponent(rule.digest); - }, - callback: function() { - me.store.load(); - } - }); - - var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : []; - tbar.push(me.addBtn, me.copyBtn); - if (me.groupBtn) { - tbar.push(me.groupBtn); - } - tbar.push(me.removeBtn, me.editBtn); - - var render_errors = function(name, value, metaData, record) { - var errors = record.data.errors; - if (errors && errors[name]) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(errors[name]) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - return value; - }; - - var columns = [ - { - // similar to xtype: 'rownumberer', - dataIndex: 'pos', - resizable: false, - width: 23, - sortable: false, - align: 'right', - hideable: false, - menuDisabled: true, - renderer: function(value, metaData, record, rowIdx, colIdx, store) { - metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special'; - if (value >= 0) { - return value; - } - return ''; - } - }, - { - xtype: 'checkcolumn', - header: gettext('Enable'), - dataIndex: 'enable', - listeners: { - checkchange: function(column, recordIndex, checked) { - var record = me.getStore().getData().items[recordIndex]; - record.commit(); - var data = {}; - Ext.Array.forEach(record.getFields(), function(field) { - data[field.name] = record.get(field.name); - }); - if (!me.allow_iface || !data.iface) { - delete data.iface; - } - me.updateRule(data); - } - }, - width: 50 - }, - { - header: gettext('Type'), - dataIndex: 'type', - renderer: function(value, metaData, record) { - return render_errors('type', value, metaData, record); - }, - width: 50 - }, - { - header: gettext('Action'), - dataIndex: 'action', - renderer: function(value, metaData, record) { - return render_errors('action', value, metaData, record); - }, - width: 80 - }, - { - header: gettext('Macro'), - dataIndex: 'macro', - renderer: function(value, metaData, record) { - return render_errors('macro', value, metaData, record); - }, - width: 80 - } - ]; - - if (me.allow_iface) { - columns.push({ - header: gettext('Interface'), - dataIndex: 'iface', - renderer: function(value, metaData, record) { - return render_errors('iface', value, metaData, record); - }, - width: 80 - }); - } - - columns.push( - { - header: gettext('Source'), - dataIndex: 'source', - renderer: function(value, metaData, record) { - return render_errors('source', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Destination'), - dataIndex: 'dest', - renderer: function(value, metaData, record) { - return render_errors('dest', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Protocol'), - dataIndex: 'proto', - renderer: function(value, metaData, record) { - return render_errors('proto', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Dest. port'), - dataIndex: 'dport', - renderer: function(value, metaData, record) { - return render_errors('dport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Source port'), - dataIndex: 'sport', - renderer: function(value, metaData, record) { - return render_errors('sport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Log level'), - dataIndex: 'log', - renderer: function(value, metaData, record) { - return render_errors('log', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value, metaData, record) { - return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record); - } - } - ); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - plugins: [ - { - ptype: 'gridviewdragdrop', - dragGroup: 'FWRuleDDGroup', - dropGroup: 'FWRuleDDGroup' - } - ], - listeners: { - beforedrop: function(node, data, dropRec, dropPosition) { - if (!dropRec) { - return false; // empty view - } - var moveto = dropRec.get('pos'); - if (dropPosition === 'after') { - moveto++; - } - var pos = data.records[0].get('pos'); - me.moveRule(pos, moveto); - return 0; - }, - itemdblclick: run_editor - } - }, - sortableColumns: false, - columns: columns - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-fw-rule', { - extend: 'Ext.data.Model', - fields: [ { name: 'enable', type: 'boolean' }, - 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface', - 'dport', 'sport', 'comment', 'pos', 'digest', 'errors' ], - idProperty: 'pos' - }); - -}); -Ext.define('PVE.FirewallAliasEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - alias_name: undefined, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.alias_name === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.alias_name; - me.method = 'PUT'; - } - - var items = [ - { - xtype: 'textfield', - name: me.isCreate ? 'name' : 'rename', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'cidr', - fieldLabel: gettext('IP/CIDR'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ]; - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - items: items - }); - - Ext.apply(me, { - subject: gettext('Alias'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - values.rename = values.name; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('pve-fw-aliases', { - extend: 'Ext.data.Model', - - fields: [ 'name', 'cidr', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.FirewallAliases', { - extend: 'Ext.grid.Panel', - alias: ['widget.pveFirewallAliases'], - - onlineHelp: 'pve_firewall_ip_aliases', - - stateful: true, - stateId: 'grid-firewall-aliases', - - base_url: undefined, - - title: gettext('Alias'), - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-aliases', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url, - alias_name: rec.data.name - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - - Ext.apply(me, { - store: store, - tbar: [ me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: gettext('Name'), dataIndex: 'name', width: 100 }, - { header: gettext('IP/CIDR'), dataIndex: 'cidr', width: 100 }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - me.on('activate', reload); - } -}); -Ext.define('PVE.FirewallOptions', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveFirewallOptions'], - - fwtype: undefined, // 'dc', 'node' or 'vm' - - base_url: undefined, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - if (me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm') { - if (me.fwtype === 'node') { - me.cwidth1 = 250; - } - } else { - throw "unknown firewall option type"; - } - - me.rows = {}; - - var add_boolean_row = function(name, text, defaultValue) { - me.add_boolean_row(name, text, { defaultValue: defaultValue }); - }; - var add_integer_row = function(name, text, minValue, labelWidth) { - me.add_integer_row(name, text, { - minValue: minValue, - deleteEmpty: true, - labelWidth: labelWidth, - renderer: function(value) { - if (value === undefined) { - return Proxmox.Utils.defaultText; - } - - return value; - } - }); - }; - - var add_log_row = function(name, labelWidth) { - me.rows[name] = { - header: name, - required: true, - defaultValue: 'nolog', - editor: { - xtype: 'proxmoxWindowEdit', - subject: name, - fieldDefaults: { labelWidth: labelWidth || 100 }, - items: { - xtype: 'pveFirewallLogLevels', - name: name, - fieldLabel: name - } - } - }; - }; - - if (me.fwtype === 'node') { - me.rows.enable = { - required: true, - defaultValue: 1, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 1 - } - }; - add_boolean_row('nosmurfs', gettext('SMURFS filter'), 1); - add_boolean_row('tcpflags', gettext('TCP flags filter'), 0); - add_boolean_row('ndp', 'NDP', 1); - add_integer_row('nf_conntrack_max', 'nf_conntrack_max', 32768, 120); - add_integer_row('nf_conntrack_tcp_timeout_established', - 'nf_conntrack_tcp_timeout_established', 7875, 250); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - add_log_row('tcp_flags_log_level', 120); - add_log_row('smurf_log_level'); - } else if (me.fwtype === 'vm') { - me.rows.enable = { - required: true, - defaultValue: 0, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 0 - } - }; - add_boolean_row('dhcp', 'DHCP', 1); - add_boolean_row('ndp', 'NDP', 1); - add_boolean_row('radv', gettext('Router Advertisement'), 0); - add_boolean_row('macfilter', gettext('MAC filter'), 1); - add_boolean_row('ipfilter', gettext('IP filter'), 0); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - } else if (me.fwtype === 'dc') { - add_boolean_row('enable', gettext('Firewall'), 0); - add_boolean_row('ebtables', 'ebtables', 1); - me.rows.log_ratelimit = { - header: gettext('Log rate limit'), - required: true, - defaultValue: 'enable=0', - editor: { - xtype: 'pveFirewallLograteEdit' - } - }; - } - - if (me.fwtype === 'dc' || me.fwtype === 'vm') { - me.rows.policy_in = { - header: gettext('Input Policy'), - required: true, - defaultValue: 'DROP', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Input Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_in', - value: 'DROP', - fieldLabel: gettext('Input Policy') - } - } - }; - - me.rows.policy_out = { - header: gettext('Output Policy'), - required: true, - defaultValue: 'ACCEPT', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Output Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_out', - value: 'ACCEPT', - fieldLabel: gettext('Output Policy') - } - } - }; - } - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - var rowdef = me.rows[rec.data.key]; - edit_btn.setDisabled(!rowdef.editor); - }; - - Ext.apply(me, { - url: "/api2/json" + me.base_url, - tbar: [ edit_btn ], - editorConfig: { - url: '/api2/extjs/' + me.base_url - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); - - -Ext.define('PVE.FirewallLogLevels', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallLogLevels'], - - name: 'log', - fieldLabel: gettext('Log level'), - value: 'nolog', - comboItems: [['nolog', 'nolog'], ['emerg', 'emerg'], ['alert', 'alert'], - ['crit', 'crit'], ['err', 'err'], ['warning', 'warning'], - ['notice', 'notice'], ['info', 'info'], ['debug', 'debug']] -}); -/* - * Left Treepanel, containing all the ressources we manage in this datacenter: server nodes, server storages, VMs and Containers - */ -Ext.define('PVE.tree.ResourceTree', { - extend: 'Ext.tree.TreePanel', - alias: ['widget.pveResourceTree'], - - statics: { - typeDefaults: { - node: { - iconCls: 'fa fa-building', - text: gettext('Nodes') - }, - pool: { - iconCls: 'fa fa-tags', - text: gettext('Resource Pool') - }, - storage: { - iconCls: 'fa fa-database', - text: gettext('Storage') - }, - qemu: { - iconCls: 'fa fa-desktop', - text: gettext('Virtual Machine') - }, - lxc: { - //iconCls: 'x-tree-node-lxc', - iconCls: 'fa fa-cube', - text: gettext('LXC Container') - }, - template: { - iconCls: 'fa fa-file-o' - } - } - }, - - useArrows: true, - - // private - nodeSortFn: function(node1, node2) { - var n1 = node1.data; - var n2 = node2.data; - - if ((n1.groupbyid && n2.groupbyid) || - !(n1.groupbyid || n2.groupbyid)) { - - var tcmp; - - var v1 = n1.type; - var v2 = n2.type; - - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - - // numeric compare for VM IDs - // sort templates after regular VMs - if (v1 === 'qemu' || v1 === 'lxc') { - if (n1.template && !n2.template) { - return 1; - } else if (n2.template && !n1.template) { - return -1; - } - v1 = n1.vmid; - v2 = n2.vmid; - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - } - - return n1.id > n2.id ? 1 : (n1.id < n2.id ? -1 : 0); - } else if (n1.groupbyid) { - return -1; - } else if (n2.groupbyid) { - return 1; - } - }, - - // private: fast binary search - findInsertIndex: function(node, child, start, end) { - var me = this; - - var diff = end - start; - - var mid = start + (diff>>1); - - if (diff <= 0) { - return start; - } - - var res = me.nodeSortFn(child, node.childNodes[mid]); - if (res <= 0) { - return me.findInsertIndex(node, child, start, mid); - } else { - return me.findInsertIndex(node, child, mid + 1, end); - } - }, - - setIconCls: function(info) { - var me = this; - - var cls = PVE.Utils.get_object_icon_class(info.type, info); - - if (cls !== '') { - info.iconCls = cls; - } - }, - - // add additional elements to text - // at the moment only the usage indicator for storages - setText: function(info) { - var me = this; - - var status = ''; - if (info.type === 'storage') { - var maxdisk = info.maxdisk; - var disk = info.disk; - var usage = disk/maxdisk; - var cls = ''; - if (usage <= 1.0 && usage >= 0.0) { - var height = (usage*100).toFixed(0); - var neg_height = (100-usage*100).toFixed(0); - status = '
'; - status += '
'; - status += '
'; - status += '
'; - } - } - - info.text = status + info.text; - }, - - setToolTip: function(info) { - if (info.type === 'pool' || info.groupbyid !== undefined) { - return; - } - - var qtips = [gettext('Status') + ': ' + (info.qmpstatus || info.status)]; - if (info.hastate != 'unmanaged') { - qtips.push(gettext('HA State') + ": " + info.hastate); - } - - info.qtip = qtips.join(', '); - }, - - // private - addChildSorted: function(node, info) { - var me = this; - - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - - var defaults; - if (info.groupbyid) { - info.text = info.groupbyid; - if (info.type === 'type') { - defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid]; - if (defaults && defaults.text) { - info.text = defaults.text; - } - } - } - var child = Ext.create('PVETree', info); - - var cs = node.childNodes; - var pos; - if (cs) { - pos = cs[me.findInsertIndex(node, child, 0, cs.length)]; - } - - node.insertBefore(child, pos); - - return child; - }, - - // private - groupChild: function(node, info, groups, level) { - var me = this; - - var groupby = groups[level]; - var v = info[groupby]; - - if (v) { - var group = node.findChild('groupbyid', v); - if (!group) { - var groupinfo; - if (info.type === groupby) { - groupinfo = info; - } else { - groupinfo = { - type: groupby, - id : groupby + "/" + v - }; - if (groupby !== 'type') { - groupinfo[groupby] = v; - } - } - groupinfo.leaf = false; - groupinfo.groupbyid = v; - group = me.addChildSorted(node, groupinfo); - } - if (info.type === groupby) { - return group; - } - if (group) { - return me.groupChild(group, info, groups, level + 1); - } - } - - return me.addChildSorted(node, info); - }, - - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - if (!me.viewFilter) { - me.viewFilter = {}; - } - - var pdata = { - dataIndex: {}, - updateCount: 0 - }; - - var store = Ext.create('Ext.data.TreeStore', { - model: 'PVETree', - root: { - expanded: true, - id: 'root', - text: gettext('Datacenter'), - iconCls: 'fa fa-server' - } - }); - - var stateid = 'rid'; - - var updateTree = function() { - var tmp; - - store.suspendEvents(); - - var rootnode = me.store.getRootNode(); - // remember selected node (and all parents) - var sm = me.getSelectionModel(); - - var lastsel = sm.getSelection()[0]; - var reselect = false; - var parents = []; - var p = lastsel; - while (p && !!(p = p.parentNode)) { - parents.push(p); - } - - var index = pdata.dataIndex; - - var groups = me.viewFilter.groups || []; - var filterfn = me.viewFilter.filterfn; - - // remove vanished or moved items - // update in place changed items - var key; - for (key in index) { - if (index.hasOwnProperty(key)) { - var olditem = index[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var item = rstore.getById(olditem.data.id); - var item = rstore.data.get(olditem.data.id); - - var changed = false; - var moved = false; - if (item) { - // test if any grouping attributes changed - // this will also catch migrated nodes - // in server view - var i, len; - for (i = 0, len = groups.length; i < len; i++) { - var attr = groups[i]; - if (item.data[attr] != olditem.data[attr]) { - //console.log("changed " + attr); - moved = true; - break; - } - } - - // explicitely check for node, since - // in some views, node is not a grouping - // attribute - if (!moved && item.data.node !== olditem.data.node) { - moved = true; - } - - // tree item has been updated - if ((item.data.text !== olditem.data.text) || - (item.data.running !== olditem.data.running) || - (item.data.template !== olditem.data.template) || - (item.data.status !== olditem.data.status) || - (item.data.hastate!== olditem.data.hastate)) { - //console.log("changed node/text/running " + olditem.data.id); - changed = true; - } - - // fixme: also test filterfn()? - } - - if (changed) { - olditem.beginEdit(); - //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running); - var info = olditem.data; - Ext.apply(info, item.data); - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - olditem.commit(); - } - if ((!item || moved) && olditem.isLeaf()) { - //console.log("REM UID: " + key + " ITEM " + olditem.data.id); - delete index[key]; - var parentNode = olditem.parentNode; - // when the selected item disappears, - // we have to deselect it here, and reselect it - // later - if (lastsel && olditem.data.id === lastsel.data.id) { - reselect = true; - sm.deselect(olditem); - } - // since the store events are suspended, we - // manually remove the item from the store also - store.remove(olditem); - parentNode.removeChild(olditem, true); - } - } - } - - // add new items - rstore.each(function(item) { - var olditem = index[item.data.id]; - if (olditem) { - return; - } - - if (filterfn && !filterfn(item)) { - return; - } - - //console.log("ADD UID: " + item.data.id); - - var info = Ext.apply({ leaf: true }, item.data); - - var child = me.groupChild(rootnode, info, groups, 0); - if (child) { - index[item.data.id] = child; - } - }); - - store.resumeEvents(); - store.fireEvent('refresh', store); - - // select parent node is selection vanished - if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) { - lastsel = rootnode; - while (!!(p = parents.shift())) { - if (!!(tmp = rootnode.findChild('id', p.data.id, true))) { - lastsel = tmp; - break; - } - } - me.selectById(lastsel.data.id); - } else if (lastsel && reselect) { - me.selectById(lastsel.data.id); - } - - // on first tree load set the selection from the stateful provider - if (!pdata.updateCount) { - rootnode.expand(); - me.applyState(sp.get(stateid)); - } - - pdata.updateCount++; - }; - - var statechange = function(sp, key, value) { - if (key === stateid) { - me.applyState(value); - } - }; - - sp.on('statechange', statechange); - - Ext.apply(me, { - allowSelection: true, - store: store, - viewConfig: { - // note: animate cause problems with applyState - animate: false - }, - //useArrows: true, - //rootVisible: false, - //title: 'Resource Tree', - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - destroy: function() { - rstore.un("load", updateTree); - }, - beforecellmousedown: function (tree, td, cellIndex, record, tr, rowIndex, ev) { - var sm = me.getSelectionModel(); - // disable selection when right clicking - // except the record is already selected - me.allowSelection = (ev.button !== 2) || sm.isSelected(record); - }, - beforeselect: function (tree, record, index, eopts) { - var allow = me.allowSelection; - me.allowSelection = true; - return allow; - }, - itemdblclick: PVE.Utils.openTreeConsole - }, - setViewFilter: function(view) { - me.viewFilter = view; - me.clearTree(); - updateTree(); - }, - setDatacenterText: function(clustername) { - var rootnode = me.store.getRootNode(); - - var rnodeText = gettext('Datacenter'); - if (clustername !== undefined) { - rnodeText += ' (' + clustername + ')'; - } - - rootnode.beginEdit(); - rootnode.data.text = rnodeText; - rootnode.commit(); - }, - clearTree: function() { - pdata.updateCount = 0; - var rootnode = me.store.getRootNode(); - rootnode.collapse(); - rootnode.removeAll(); - pdata.dataIndex = {}; - me.getSelectionModel().deselectAll(); - }, - selectExpand: function(node) { - var sm = me.getSelectionModel(); - if (!sm.isSelected(node)) { - sm.select(node); - var cn = node; - while (!!(cn = cn.parentNode)) { - if (!cn.isExpanded()) { - cn.expand(); - } - } - me.getView().focusRow(node); - } - }, - selectById: function(nodeid) { - var rootnode = me.store.getRootNode(); - var sm = me.getSelectionModel(); - var node; - if (nodeid === 'root') { - node = rootnode; - } else { - node = rootnode.findChild('id', nodeid, true); - } - if (node) { - me.selectExpand(node); - } - return node; - }, - applyState : function(state) { - var sm = me.getSelectionModel(); - if (state && state.value) { - me.selectById(state.value); - } else { - sm.deselectAll(); - } - } - }); - - me.callParent(); - - var sm = me.getSelectionModel(); - sm.on('select', function(sm, n) { - sp.set(stateid, { value: n.data.id}); - }); - - rstore.on("load", updateTree); - rstore.startUpdate(); - //rstore.stopUpdate(); - } - -}); -Ext.define('pve-fw-ipsets', { - extend: 'Ext.data.Model', - fields: [ 'name', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.IPSetList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetList', - - stateful: true, - stateId: 'grid-firewall-ipsetlist', - - ipset_panel: undefined, - - base_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - initComponent: function() { - - var me = this; - - if (me.ipset_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-ipsets', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('Proxmox.window.Edit', { - subject: "IPSet '" + rec.data.name + "'", - url: me.base_url, - method: 'POST', - digest: rec.data.digest, - items: [ - { - xtype: 'hiddenfield', - name: 'rename', - value: rec.data.name - }, - { - xtype: 'textfield', - name: 'name', - value: rec.data.name, - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: rec.data.comment, - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('Proxmox.window.Edit', { - subject: 'IPSet', - url: me.base_url, - method: 'POST', - items: [ - { - xtype: 'textfield', - name: 'name', - value: '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - Ext.apply(me, { - store: store, - tbar: [ 'IPSet:', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: 'IPSet', dataIndex: 'name', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = me.base_url + '/' + rec.data.name; - me.ipset_panel.setBaseUrl(url); - }, - deselect: function() { - me.ipset_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.IPSetCidrEdit', { - extend: 'Proxmox.window.Edit', - - cidr: undefined, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.cidr === undefined); - - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.cidr; - me.method = 'PUT'; - } - - var column1 = []; - - if (me.isCreate) { - if (!me.list_refs_url) { - throw "no alias_base_url specified"; - } - - column1.push({ - xtype: 'pveIPRefSelector', - name: 'cidr', - ref_type: 'alias', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } else { - column1.push({ - xtype: 'displayfield', - name: 'cidr', - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'nomatch', - checked: false, - uncheckedValue: 0, - fieldLabel: 'nomatch' - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('IP/CIDR'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.IPSetGrid', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetGrid', - - stateful: true, - stateId: 'grid-firewall-ipsets', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no1 list_refs_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-ipset' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - cidr: rec.data.cidr - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Add'), - disabled: true, - handler: function() { - if (!me.base_url) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - var render_errors = function(value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors.cidr || errors.nomatch; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - Ext.apply(me, { - tbar: [ 'IP/CIDR:', me.addBtn, me.removeBtn, me.editBtn ], - store: store, - selModel: sm, - listeners: { - itemdblclick: run_editor - }, - columns: [ - { - xtype: 'rownumberer' - }, - { - header: gettext('IP/CIDR'), - dataIndex: 'cidr', - width: 150, - renderer: function(value, metaData, record) { - value = render_errors(value, metaData, record); - if (record.data.nomatch) { - return '! ' + value; - } - return value; - } - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value) { - return Ext.util.Format.htmlEncode(value); - } - } - ] - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-ipset', { - extend: 'Ext.data.Model', - fields: [ { name: 'nomatch', type: 'boolean' }, - 'cidr', 'comment', 'errors' ], - idProperty: 'cidr' - }); - -}); - -Ext.define('PVE.IPSet', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveIPSet', - - title: 'IPSet', - - onlineHelp: 'pve_firewall_ip_sets', - - list_refs_url: undefined, - - initComponent: function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var ipset_panel = Ext.createWidget('pveIPSetGrid', { - region: 'center', - list_refs_url: me.list_refs_url, - border: false - }); - - var ipset_list = Ext.createWidget('pveIPSetList', { - region: 'west', - ipset_panel: ipset_panel, - base_url: me.base_url, - width: '50%', - border: false, - split: true - }); - - Ext.apply(me, { - layout: 'border', - items: [ ipset_list, ipset_panel ], - listeners: { - show: function() { - ipset_list.fireEvent('show', ipset_list); - } - } - }); - - me.callParent(); - } -}); -/* - * Base class for all the multitab config panels - * - * How to use this: - * - * You create a subclass of this, and then define your wanted tabs - * as items like this: - * - * items: [{ - * title: "myTitle", - * xytpe: "somextype", - * iconCls: 'fa fa-icon', - * groups: ['somegroup'], - * expandedOnInit: true, - * itemId: 'someId' - * }] - * - * this has to be in the declarative syntax, else we - * cannot save them for later - * (so no Ext.create or Ext.apply of an item in the subclass) - * - * the groups array expects the itemids of the items - * which are the parents, which have to come before they - * are used - * - * if you want following the tree: - * - * Option1 - * Option2 - * -> SubOption1 - * -> SubSubOption1 - * - * the suboption1 group array has to look like this: - * groups: ['itemid-of-option2'] - * - * and of subsuboption1: - * groups: ['itemid-of-option2', 'itemid-of-suboption1'] - * - * setting the expandedOnInit determines if the item/group is expanded - * initially (false by default) - */ -Ext.define('PVE.panel.Config', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePanelConfig', - - showSearch: true, // add a ressource grid with a search button as first tab - viewFilter: undefined, // a filter to pass to that ressource grid - - tbarSpacing: true, // if true, adds a spacer after the title in tbar - - dockedItems: [{ - // this is needed for the overflow handler - xtype: 'toolbar', - overflowHandler: 'scroller', - dock: 'left', - style: { - backgroundColor: '#f5f5f5', - padding: 0, - margin: 0 - }, - items: { - xtype: 'treelist', - itemId: 'menu', - ui: 'nav', - expanderOnly: true, - expanderFirst: false, - animation: false, - singleExpand: false, - listeners: { - selectionchange: function(treeList, selection) { - var me = this.up('panel'); - me.suspendLayout = true; - me.activateCard(selection.data.id); - me.suspendLayout = false; - me.updateLayout(); - }, - itemclick: function(treelist, info) { - var olditem = treelist.getSelection(); - var newitem = info.node; - - // when clicking on the expand arrow, - // we dont select items, but still want - // the original behaviour - if (info.select === false) { - return; - } - - // if you click on a different item which is open, - // leave it open - // else toggle the clicked item - if (olditem.data.id !== newitem.data.id && - newitem.data.expanded === true) { - info.toggle = false; - } else { - info.toggle = true; - } - } - } - } - }, - { - xtype: 'toolbar', - itemId: 'toolbar', - dock: 'top', - height: 36, - overflowHandler: 'scroller' - }], - - firstItem: '', - layout: 'card', - border: 0, - - // used for automated test - selectById: function(cardid) { - var me = this; - - var root = me.store.getRoot(); - var selection = root.findChild('id', cardid, true); - - if (selection) { - selection.expand(); - var menu = me.down('#menu'); - menu.setSelection(selection); - return cardid; - } - }, - - activateCard: function(cardid) { - var me = this; - if (me.savedItems[cardid]) { - var curcard = me.getLayout().getActiveItem(); - var newcard = me.add(me.savedItems[cardid]); - me.helpButton.setOnlineHelp(newcard.onlineHelp || me.onlineHelp); - if (curcard) { - me.setActiveItem(cardid); - me.remove(curcard, true); - - // trigger state change - - var ncard = cardid; - // Note: '' is alias for first tab. - // First tab can be 'search' or something else - if (cardid === me.firstItem) { - ncard = ''; - } - if (me.hstateid) { - me.sp.set(me.hstateid, { value: ncard }); - } - } - } - }, - - initComponent: function() { - var me = this; - - var stateid = me.hstateid; - - me.sp = Ext.state.Manager.getProvider(); - - var activeTab; // leaving this undefined means items[0] will be the default tab - - if (stateid) { - var state = me.sp.get(stateid); - if (state && state.value) { - // if this tab does not exists, it chooses the first - activeTab = state.value; - } - } - - // get title - var title = me.title || me.pveSelNode.data.text; - me.title = undefined; - - // create toolbar - var tbar = me.tbar || []; - me.tbar = undefined; - - if (!me.onlineHelp) { - switch(me.pveSelNode.data.id) { - case 'type/storage':me.onlineHelp = 'chapter-pvesm.html'; break; - case 'type/qemu':me.onlineHelp = 'chapter-qm.html'; break; - case 'type/lxc':me.onlineHelp = 'chapter-pct.html'; break; - case 'type/pool':me.onlineHelp = 'chapter-pveum.html#_pools'; break; - case 'type/node':me.onlineHelp = 'chapter-sysadmin.html'; break; - } - } - - if (me.tbarSpacing) { - tbar.unshift('->'); - } - tbar.unshift({ - xtype: 'tbtext', - text: title, - baseCls: 'x-panel-header-text' - }); - - me.helpButton = Ext.create('Proxmox.button.Help', { - hidden: false, - listenToGlobalEvent: false, - onlineHelp: me.onlineHelp || undefined - }); - - tbar.push(me.helpButton); - - me.dockedItems[1].items = tbar; - - // include search tab - me.items = me.items || []; - if (me.showSearch) { - me.items.unshift({ - itemId: 'search', - title: gettext('Search'), - iconCls: 'fa fa-search', - xtype: 'pveResourceGrid', - pveSelNode: me.pveSelNode - }); - } - - me.savedItems = {}; - /*jslint confusion:true*/ - if (me.items[0]) { - me.firstItem = me.items[0].itemId; - } - /*jslint confusion:false*/ - - me.store = Ext.create('Ext.data.TreeStore', { - root: { - expanded: true - } - }); - var root = me.store.getRoot(); - me.items.forEach(function(item){ - var treeitem = Ext.create('Ext.data.TreeModel',{ - id: item.itemId, - text: item.title, - iconCls: item.iconCls, - leaf: true, - expanded: item.expandedOnInit - }); - item.header = false; - if (me.savedItems[item.itemId] !== undefined) { - throw "itemId already exists, please use another"; - } - me.savedItems[item.itemId] = item; - - var group; - var curnode = root; - - // get/create the group items - while (Ext.isArray(item.groups) && item.groups.length > 0) { - group = item.groups.shift(); - - var child = curnode.findChild('id', group); - if (child === null) { - // did not find the group item - // so add it where we are - break; - } - curnode = child; - } - - // insert the item - - // lets see if it already exists - var node = curnode.findChild('id', item.itemId); - - if (node === null) { - curnode.appendChild(treeitem); - } else { - // should not happen! - throw "id already exists"; - } - }); - - delete me.items; - me.defaults = me.defaults || {}; - Ext.apply(me.defaults, { - pveSelNode: me.pveSelNode, - viewFilter: me.viewFilter, - workspace: me.workspace, - border: 0 - }); - - me.callParent(); - - var menu = me.down('#menu'); - var selection = root.findChild('id', activeTab, true) || root.firstChild; - var node = selection; - while (node !== root) { - node.expand(); - node = node.parentNode; - } - menu.setStore(me.store); - menu.setSelection(selection); - - // on a state change, - // select the new item - var statechange = function(sp, key, state) { - // it the state change is for this panel - if (stateid && (key === stateid) && state) { - // get active item - var acard = me.getLayout().getActiveItem().itemId; - // get the itemid of the new value - var ncard = state.value || me.firstItem; - if (ncard && (acard != ncard)) { - // select the chosen item - menu.setSelection(root.findChild('id', ncard, true) || root.firstChild); - } - } - }; - - if (stateid) { - me.mon(me.sp, 'statechange', statechange); - } - } -}); -Ext.define('PVE.grid.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveBackupView'], - - onlineHelp: 'chapter_vzdump', - - stateful: true, - stateId: 'grid-guest-backup', - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmtype = me.pveSelNode.data.type; - if (!vmtype) { - throw "no VM type specified"; - } - - var vmtypeFilter; - if (vmtype === 'openvz') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-openvz-'); - }; - } else if (vmtype === 'lxc') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-lxc-'); - }; - } else if (vmtype === 'qemu') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-qemu-'); - }; - } else { - throw "unsupported VM type '" + vmtype + "'"; - } - - var searchFilter = { - property: 'volid', - // on initial store display only our vmid backups - // surround with minus sign to prevent the 2016 VMID bug - value: vmtype + '-' + vmid + '-', - anyMatch: true, - caseSensitive: false - }; - - me.store = Ext.create('Ext.data.Store', { - model: 'pve-storage-content', - sorters: { - property: 'volid', - order: 'DESC' - }, - filters: [ - vmtypeFilter, - searchFilter - ] - }); - - var reload = Ext.Function.createBuffered(function() { - if (me.store) { - me.store.load(); - } - }, 100); - - var setStorage = function(storage) { - var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content'; - url += '?content=backup'; - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - reload(); - }; - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'backup', - allowBlank: false, - listeners: { - change: function(f, value) { - setStorage(value); - } - } - }); - - var storagefilter = Ext.create('Ext.form.field.Text', { - fieldLabel: gettext('Search'), - labelWidth: 50, - labelAlign: 'right', - enableKeyEvents: true, - value: searchFilter.value, - listeners: { - buffer: 500, - keyup: function(field) { - me.store.clearFilter(true); - searchFilter.value = field.getValue(); - me.store.filter([ - vmtypeFilter, - searchFilter - ]); - } - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var backup_btn = Ext.create('Ext.button.Button', { - text: gettext('Backup now'), - handler: function() { - var win = Ext.create('PVE.window.Backup', { - nodename: nodename, - vmid: vmid, - vmtype: vmtype, - storage: storagesel.getValue(), - listeners : { - close: function() { - reload(); - } - } - }); - win.show(); - } - }); - - var restore_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Restore'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var volid = rec.data.volid; - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - vmid: vmid, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }); - - var delete_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - dangerous: true, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.volid + "'"); - msg += " " + gettext('This will permanently erase all data.'); - - return msg; - }, - getUrl: function(rec) { - var storage = storagesel.getValue(); - return '/nodes/' + nodename + '/storage/' + storage + '/content/' + rec.data.volid; - }, - callback: function() { - reload(); - } - }); - - var config_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var storage = storagesel.getValue(); - if (!storage) { - return; - } - - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }); - - Ext.apply(me, { - selModel: sm, - tbar: [ backup_btn, restore_btn, delete_btn,config_btn, '->', storagesel, storagefilter ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'volid' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ] - }); - - me.callParent(); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.CephCreateFS', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreateFS', - - showTaskViewer: true, - onlineHelp: 'pveceph_fs_create', - - subject: 'Ceph FS', - isCreate: true, - method: 'POST', - - setFSName: function(fsName) { - var me = this; - - if (fsName === '' || fsName === undefined) { - fsName = 'cephfs'; - } - - me.url = "/nodes/" + me.nodename + "/ceph/fs/" + fsName; - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - value: 'cephfs', - listeners: { - change: function(f, value) { - this.up('pveCephCreateFS').setFSName(value); - } - }, - submitValue: false, // already encoded in apicall URL - emptyText: 'cephfs' - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'Placement Groups', - name: 'pg_num', - value: 128, - emptyText: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add Storage'), - value: true, - name: 'add-storage' - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.setFSName(); - - me.callParent(); - } -}); - -Ext.define('PVE.CephCreateMDS', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreateMDS', - - showProgress: true, - onlineHelp: 'pveceph_fs_mds', - - subject: 'Ceph MDS', - isCreate: true, - method: 'POST', - - setNode: function(nodename) { - var me = this; - - me.nodename = nodename; - me.url = "/nodes/" + nodename + "/ceph/mds/" + nodename; - }, - - items: [ - { - xtype: 'pveNodeSelector', - fieldLabel: gettext('Node'), - selectCurNode: true, - submitValue: false, - allowBlank: false, - listeners: { - change: function(f, value) { - this.up('pveCephCreateMDS').setNode(value); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.setNode(me.nodename); - - me.callParent(); - } -}); - -Ext.define('PVE.NodeCephFSPanel', { - extend: 'Ext.panel.Panel', - xtype: 'pveNodeCephFSPanel', - mixins: ['Proxmox.Mixin.CBind'], - - title: gettext('CephFS'), - onlineHelp: 'pveceph_fs', - - border: false, - defaults: { - border: false, - cbind: { - nodename: '{nodename}' - } - }, - - viewModel: { - parent: null, - data: { - cephfsConfigured: false, - mdsCount: 0 - }, - formulas: { - canCreateFS: function(get) { - return (!get('cephfsConfigured') && get('mdsCount') > 0); - } - } - }, - - items: [ - { - xtype: 'grid', - emptyText: Ext.String.format(gettext('No {0} configured.'), 'CephFS'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-ceph-fs', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + view.nodename + '/ceph/fs' - }, - model: 'pve-ceph-fs' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'name', - order: 'DESC' - } - })); - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){ - me.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.rstore.startUpdate(); - }); - } - ); - }); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onCreate: function() { - var view = this.getView(); - view.rstore.stopUpdate(); - var win = Ext.create('PVE.CephCreateFS', { - autoShow: true, - nodename: view.nodename, - listeners: { - destroy: function() { - view.rstore.startUpdate(); - } - } - }); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!(success && records && records.length > 0)) { - vm.set('cephfsConfigured', false); - return; - } - vm.set('cephfsConfigured', true); - } - }, - tbar: [ - { - text: gettext('Create CephFS'), - reference: 'createButton', - handler: 'onCreate', - bind: { - // only one CephFS per Ceph cluster makes sense for now - disabled: '{!canCreateFS}' - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - dataIndex: 'name' - }, - { - header: 'Data Pool', - flex: 1, - dataIndex: 'data_pool' - }, - { - header: 'Metadata Pool', - flex: 1, - dataIndex: 'metadata_pool' - } - ], - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'grid', - title: gettext('Metadata Servers'), - emptyText: Ext.String.format(gettext('No {0} configured.'), 'MDS'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 3 * 1000, - autoStart: true, - storeid: 'pve-ceph-mds', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/'+ view.nodename +'/ceph/mds' - }, - model: 'pve-ceph-mds' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'id', - order: 'DESC' - } - })); - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){ - me.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.rstore.startUpdate(); - }); - } - ); - }); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records) { - vm.set('mdsCount', 0); - return; - } - vm.set('mdsCount', records.length); - }, - onCreateMDS: function() { - var view = this.getView(); - view.rstore.stopUpdate(); - var win = Ext.create('PVE.CephCreateMDS', { - autoShow: true, - nodename: view.nodename, - listeners: { - destroy: function() { - view.rstore.startUpdate(); - } - } - }); - } - }, - tbar: [ - { - text: gettext('Create MDS'), - reference: 'createButton', - handler: 'onCreateMDS' - }, - { - text: gettext('Destroy MDS'), - xtype: 'proxmoxStdRemoveButton', - getUrl: function(rec) { - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - return "/nodes/" + rec.data.host + "/ceph/mds/" + rec.data.name; - }, - callback: function(options, success, response) { - if (!success) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - return; - } - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - dataIndex: 'name' - }, - { - header: gettext('Host'), - flex: 1, - dataIndex: 'host' - }, - { - header: gettext('Address'), - flex: 1, - dataIndex: 'addr' - }, - { - header: gettext('State'), - flex: 1, - dataIndex: 'state' - } - ], - cbind: { - nodename: '{nodename}' - } - } - ] -}, function() { - Ext.define('pve-ceph-mds', { - extend: 'Ext.data.Model', - fields: [ 'name', 'host', 'addr', 'state' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/localhost/ceph/mds" - }, - idProperty: 'name' - }); - Ext.define('pve-ceph-fs', { - extend: 'Ext.data.Model', - fields: [ 'name', 'data_pool', 'metadata_pool' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/localhost/ceph/fs" - }, - idProperty: 'name' - }); -}); -Ext.define('PVE.CephCreatePool', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreatePool', - - showProgress: true, - onlineHelp: 'pve_ceph_pools', - - subject: 'Ceph Pool', - isCreate: true, - method: 'POST', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Size'), - name: 'size', - value: 3, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Min. Size'), - name: 'min_size', - value: 2, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'pveCephRuleSelector', - fieldLabel: 'Crush Rule', // do not localize - name: 'crush_rule', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'pg_num', - name: 'pg_num', - value: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add Storage'), - name: 'add_storages' - } - ], - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: "/nodes/" + me.nodename + "/ceph/pools", - defaults: { - nodename: me.nodename - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephPoolList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeCephPoolList', - - onlineHelp: 'chapter_pveceph', - stateful: true, - stateId: 'grid-ceph-pools', - bufferedRenderer: false, - features: [ { ftype: 'summary'} ], - columns: [ - { - header: gettext('Name'), - width: 100, - sortable: true, - dataIndex: 'pool_name' - }, - { - header: gettext('Size') + '/min', - width: 80, - sortable: false, - renderer: function(v, meta, rec) { - return v + '/' + rec.data.min_size; - }, - dataIndex: 'size' - }, - { - header: 'pg_num', - width: 100, - sortable: false, - dataIndex: 'pg_num' - }, - { - header: 'rule', - width: 50, - sortable: false, - dataIndex: 'crush_rule' - }, - { - header: 'rule_name', - width: 50, - sortable: false, - dataIndex: 'crush_rule_name' - }, - { - header: gettext('Used'), - columns: [ - { - header: '%', - width: 80, - sortable: true, - align: 'right', - renderer: Ext.util.Format.numberRenderer('0.00'), - dataIndex: 'percent_used', - summaryType: 'sum', - summaryRenderer: Ext.util.Format.numberRenderer('0.00') - }, - { - header: gettext('Total'), - width: 100, - sortable: true, - renderer: PVE.Utils.render_size, - align: 'right', - dataIndex: 'bytes_used', - summaryType: 'sum', - summaryRenderer: PVE.Utils.render_size - } - ] - } - ], - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'ceph-pool-list' + nodename, - model: 'ceph-pool-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/ceph/pools" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore }); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ - me.store.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.rstore.startUpdate(); - }); - } - ); - }); - - var create_btn = new Ext.Button({ - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.CephCreatePool', { - nodename: nodename - }); - win.show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - var destroy_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Destroy'), - selModel: sm, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - if (!rec.data.pool_name) { - return; - } - var base_url = '/nodes/' + nodename + '/ceph/pools/' + - rec.data.pool_name; - - var win = Ext.create('PVE.window.SafeDestroy', { - showProgress: true, - url: base_url, - params: { - remove_storages: 1 - }, - item: { type: 'CephPool', id: rec.data.pool_name } - }).show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ create_btn, destroy_btn ], - listeners: { - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('ceph-pool-list', { - extend: 'Ext.data.Model', - fields: [ 'pool_name', - { name: 'pool', type: 'integer'}, - { name: 'size', type: 'integer'}, - { name: 'min_size', type: 'integer'}, - { name: 'pg_num', type: 'integer'}, - { name: 'bytes_used', type: 'integer'}, - { name: 'percent_used', type: 'number'}, - { name: 'crush_rule', type: 'integer'}, - { name: 'crush_rule_name', type: 'string'} - ], - idProperty: 'pool_name' - }); -}); - -Ext.define('PVE.form.CephRuleSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephRuleSelector', - - allowBlank: false, - valueField: 'name', - displayField: 'name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/rules' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.CephCreateOsd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephCreateOsd'], - - subject: 'Ceph OSD', - - showProgress: true, - - onlineHelp: 'pve_ceph_osds', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'dev', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'pveDiskSelector', - name: 'journal_dev', - nodename: me.nodename, - diskType: 'journal_disks', - fieldLabel: gettext('Journal/DB Disk'), - value: '', - autoSelect: false, - allowBlank: true, - emptyText: 'use OSD disk' - }, - { - xtype: 'proxmoxcheckbox', - name: 'bluestore', - fieldLabel: 'Bluestore', - uncheckedValue: '0', - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.CephRemoveOsd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephRemoveOsd'], - - isRemove: true, - - showProgress: true, - method: 'DELETE', - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'cleanup', - checked: true, - labelWidth: 130, - fieldLabel: gettext('Remove Partitions') - } - ], - initComponent : function() { - - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - if (me.osdid === undefined || me.osdid < 0) { - throw "no osdid specified"; - } - - me.isCreate = true; - - me.title = gettext('Destroy') + ': Ceph OSD osd.' + me.osdid.toString(); - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd/" + me.osdid.toString() - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephOsdTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveNodeCephOsdTree'], - onlineHelp: 'chapter_pveceph', - stateful: true, - stateId: 'grid-ceph-osd', - columns: [ - { - xtype: 'treecolumn', - text: 'Name', - dataIndex: 'name', - width: 150 - }, - { - text: 'Type', - dataIndex: 'type', - align: 'right', - width: 60 - }, - { - text: gettext("Class"), - dataIndex: 'device_class', - align: 'right', - width: 40 - }, - { - text: "OSD Type", - dataIndex: 'osdtype', - align: 'right', - width: 40 - }, - { - text: "Bluestore Device", - dataIndex: 'blfsdev', - align: 'right', - width: 40, - hidden: true - }, - { - text: "DB Device", - dataIndex: 'dbdev', - align: 'right', - width: 40, - hidden: true - }, - { - text: "WAL Device", - dataIndex: 'waldev', - align: 'right', - renderer: function(value, metaData, rec) { - if (!value && - rec.data.osdtype === 'bluestore' && - rec.data.type === 'osd') { - return 'N/A'; - } - return value; - }, - width: 40, - hidden: true - }, - { - text: 'Status', - dataIndex: 'status', - align: 'right', - renderer: function(value, metaData, rec) { - if (!value) { - return value; - } - var inout = rec.data['in'] ? 'in' : 'out'; - var updownicon = value === 'up' ? 'good fa-arrow-circle-up' : - 'critical fa-arrow-circle-down'; - - var inouticon = rec.data['in'] ? 'good fa-circle' : - 'warning fa-circle-o'; - - var text = value + ' / ' + - inout + ' '; - - return text; - }, - width: 80 - }, - { - text: 'weight', - dataIndex: 'crush_weight', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return value; - }, - width: 80 - }, - { - text: 'reweight', - dataIndex: 'reweight', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return value; - }, - width: 90 - }, - { - header: gettext('Used'), - columns: [ - { - text: '%', - dataIndex: 'percent_used', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return Ext.util.Format.number(value, '0.00'); - }, - width: 80 - }, - { - text: gettext('Total'), - dataIndex: 'total_space', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return PVE.Utils.render_size(value); - }, - width: 100 - } - ] - }, - { - header: gettext('Latency (ms)'), - columns: [ - { - text: 'Apply', - dataIndex: 'apply_latency_ms', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return value; - }, - width: 60 - }, - { - text: 'Commit', - dataIndex: 'commit_latency_ms', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return value; - }, - width: 60 - } - ] - } - ], - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - // we expect noout to be not set by default - var noout = false; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - var set_button_status; // defined later - - var reload = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/ceph/osd", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - reload(); - }); - } - ); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data.root); - me.expandAll(); - // extract noout flag - if (response.result.data.flags && - response.result.data.flags.search(/noout/) !== -1) { - noout = true; - } else { - noout = false; - } - set_button_status(); - } - }); - }; - - var osd_cmd = function(cmd) { - var rec = sm.getSelection()[0]; - if (!(rec && (rec.data.id >= 0) && rec.data.host)) { - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/osd/" + - rec.data.id + '/' + cmd, - waitMsgTarget: me, - method: 'POST', - success: reload, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var service_cmd = function(cmd) { - var rec = sm.getSelection()[0]; - if (!(rec && rec.data.name && rec.data.host)) { - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/" + cmd, - params: { service: rec.data.name }, - waitMsgTarget: me, - method: 'POST', - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.mon(win, 'close', reload, me); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var create_btn = new Proxmox.button.Button({ - text: gettext('Create') + ': OSD', - handler: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.CephCreateOsd', { - nodename: nodename - }); - win.show(); - me.mon(win, 'close', reload, me); - } - }); - - var start_btn = new Ext.Button({ - text: gettext('Start'), - disabled: true, - handler: function(){ service_cmd('start'); } - }); - - var stop_btn = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: function(){ service_cmd('stop'); } - }); - - var restart_btn = new Ext.Button({ - text: gettext('Restart'), - disabled: true, - handler: function(){ service_cmd('restart'); } - }); - - var osd_out_btn = new Ext.Button({ - text: 'Out', - disabled: true, - handler: function(){ osd_cmd('out'); } - }); - - var osd_in_btn = new Ext.Button({ - text: 'In', - disabled: true, - handler: function(){ osd_cmd('in'); } - }); - - var remove_btn = new Ext.Button({ - text: gettext('Destroy'), - disabled: true, - handler: function(){ - var rec = sm.getSelection()[0]; - if (!(rec && (rec.data.id >= 0) && rec.data.host)) { - return; - } - - var win = Ext.create('PVE.CephRemoveOsd', { - nodename: rec.data.host, - osdid: rec.data.id - }); - win.show(); - me.mon(win, 'close', reload, me); - } - }); - - var noout_btn = new Ext.Button({ - text: gettext('Set noout'), - handler: function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/ceph/flags/noout", - waitMsgTarget: me, - method: noout ? 'DELETE' : 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: reload - }); - } - }); - - var osd_label = new Ext.toolbar.TextItem({ - data: { - osd: undefined - }, - tpl: [ - '', - '{osd}:', - '', - gettext('No OSD selected'), - '' - ] - }); - - set_button_status = function() { - var rec = sm.getSelection()[0]; - noout_btn.setText(noout?gettext('Unset noout'):gettext('Set noout')); - - if (!rec) { - start_btn.setDisabled(true); - stop_btn.setDisabled(true); - restart_btn.setDisabled(true); - remove_btn.setDisabled(true); - osd_out_btn.setDisabled(true); - osd_in_btn.setDisabled(true); - return; - } - - var isOsd = (rec.data.host && (rec.data.type === 'osd') && (rec.data.id >= 0)); - - start_btn.setDisabled(!(isOsd && (rec.data.status !== 'up'))); - stop_btn.setDisabled(!(isOsd && (rec.data.status !== 'down'))); - restart_btn.setDisabled(!(isOsd && (rec.data.status !== 'down'))); - remove_btn.setDisabled(!(isOsd && (rec.data.status === 'down'))); - - osd_out_btn.setDisabled(!(isOsd && rec.data['in'])); - osd_in_btn.setDisabled(!(isOsd && !rec.data['in'])); - - osd_label.update(isOsd?{osd:rec.data.name}:undefined); - }; - - sm.on('selectionchange', set_button_status); - - var reload_btn = new Ext.Button({ - text: gettext('Reload'), - handler: reload - }); - - Ext.apply(me, { - tbar: [ create_btn, reload_btn, noout_btn, '->', osd_label, start_btn, stop_btn, restart_btn, osd_out_btn, osd_in_btn, remove_btn ], - rootVisible: false, - useArrows: true, - fields: ['name', 'type', 'status', 'host', 'in', 'id' , - { type: 'number', name: 'reweight' }, - { type: 'number', name: 'percent_used' }, - { type: 'integer', name: 'bytes_used' }, - { type: 'integer', name: 'total_space' }, - { type: 'integer', name: 'apply_latency_ms' }, - { type: 'integer', name: 'commit_latency_ms' }, - { type: 'string', name: 'device_class' }, - { type: 'string', name: 'osdtype' }, - { type: 'string', name: 'blfsdev' }, - { type: 'string', name: 'dbdev' }, - { type: 'string', name: 'waldev' }, - { type: 'string', name: 'iconCls', calculate: function(data) { - var iconCls = 'fa x-fa-tree fa-'; - switch (data.type) { - case 'host': - iconCls += 'building'; - break; - case 'osd': - iconCls += 'hdd-o'; - break; - case 'root': - iconCls += 'server'; - break; - default: - return undefined; - } - return iconCls; - } }, - { type: 'number', name: 'crush_weight' }], - selModel: sm, - - listeners: { - activate: function() { - reload(); - } - } - }); - - me.callParent(); - - reload(); - } -}); -Ext.define('PVE.CephCreateMon', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephCreateMon'], - - subject: 'Ceph Monitor/Manager', - onlineHelp: 'pve_ceph_monitors', - - showProgress: true, - - setNode: function(nodename) { - var me = this; - - me.nodename = nodename; - me.url = "/nodes/" + nodename + "/ceph/mon"; - }, - - initComponent : function() { - - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.setNode(me.nodename); - - me.isCreate = true; - - Ext.applyIf(me, { - method: 'POST', - items: [ - { - xtype: 'pveNodeSelector', - submitValue: false, - fieldLabel: gettext('Host'), - selectCurNode: true, - allowBlank: false, - listeners: { - change: function(f, value) { - me.setNode(value); - } - } - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephMonList', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveNodeCephMonList'], - - onlineHelp: 'chapter_pveceph', - - stateful: true, - stateId: 'grid-ceph-monitor', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'ceph-mon-list' + nodename, - model: 'ceph-mon-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/ceph/mon" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - sorters: [{ property: 'name'}] - }); - - - var service_cmd = function(cmd) { - var rec = sm.getSelection()[0]; - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/" + cmd, - method: 'POST', - params: { service: "mon." + rec.data.name }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var start_btn = new Proxmox.button.Button({ - text: gettext('Start'), - selModel: sm, - disabled: true, - handler: function(){ - service_cmd("start"); - } - }); - - var stop_btn = new Proxmox.button.Button({ - text: gettext('Stop'), - selModel: sm, - disabled: true, - handler: function(){ - service_cmd("stop"); - } - }); - - var restart_btn = new Proxmox.button.Button({ - text: gettext('Restart'), - selModel: sm, - disabled: true, - handler: function(){ - service_cmd("restart"); - } - }); - - var create_btn = new Ext.Button({ - text: gettext('Create'), - handler: function(){ - var win = Ext.create('PVE.CephCreateMon', { - nodename: nodename - }); - win.show(); - } - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - selModel: sm, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/mon/" + - rec.data.name, - method: 'DELETE', - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ start_btn, stop_btn, restart_btn, '-', create_btn, remove_btn ], - columns: [ - { - header: gettext('Name'), - width: 100, - sortable: true, - renderer: function(v) { return "mon." + v; }, - dataIndex: 'name' - }, - { - header: gettext('Host'), - width: 100, - sortable: true, - renderer: function(v) { - return v || 'unknown'; - }, - dataIndex: 'host' - }, - { - header: gettext('Quorum'), - width: 70, - sortable: false, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'quorum' - }, - { - header: gettext('Address'), - flex: 1, - sortable: true, - dataIndex: 'addr' - } - ], - listeners: { - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ - me.store.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.rstore.startUpdate(); - }); - } - ); - }); - - me.callParent(); - } -}, function() { - - Ext.define('ceph-mon-list', { - extend: 'Ext.data.Model', - fields: [ 'addr', 'name', 'rank', 'host', 'quorum' ], - idProperty: 'name' - }); -}); -Ext.define('PVE.node.CephCrushMap', { - extend: 'Ext.panel.Panel', - alias: ['widget.pveNodeCephCrushMap'], - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - stateful: true, - stateId: 'layout-ceph-crush', - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/crush', - - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.node.CephStatus', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephStatus', - - onlineHelp: 'chapter_pveceph', - - scrollable: true, - - bodyPadding: 5, - - layout: { - type: 'column' - }, - - defaults: { - padding: 5 - }, - - items: [ - { - xtype: 'panel', - title: gettext('Health'), - bodyPadding: 10, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - }, - minHeight: 210, - layout: { - type: 'hbox', - align: 'stretch' - }, - items: [ - { - flex: 1, - itemId: 'overallhealth', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - flex: 2, - itemId: 'warnings', - stateful: true, - stateId: 'ceph-status-warnings', - xtype: 'grid', - // since we load the store manually, - // to show the emptytext, we have to - // specify an empty store - store: { data:[] }, - emptyText: gettext('No Warnings/Errors'), - columns: [ - { - dataIndex: 'severity', - header: gettext('Severity'), - align: 'center', - width: 70, - renderer: function(value) { - var health = PVE.Utils.map_ceph_health[value]; - var classes = PVE.Utils.get_health_icon(health); - - return ''; - }, - sorter: { - sorterFn: function(a,b) { - var healthArr = ['HEALTH_ERR', 'HEALTH_WARN', 'HEALTH_OK']; - return healthArr.indexOf(b.data.severity) - healthArr.indexOf(a.data.severity); - } - } - }, - { - dataIndex: 'summary', - header: gettext('Summary'), - flex: 1 - }, - { - xtype: 'actioncolumn', - width: 40, - align: 'center', - tooltip: gettext('Detail'), - items: [ - { - iconCls: 'x-fa fa-info-circle', - handler: function(grid, rowindex, colindex, item, e, record) { - var win = Ext.create('Ext.window.Window', { - title: gettext('Detail'), - resizable: true, - modal: true, - width: 650, - height: 400, - layout: { - type: 'fit' - }, - items: [{ - scrollable: true, - padding: 10, - xtype: 'box', - html: [ - '' + Ext.htmlEncode(record.data.summary) + '', - '
' + Ext.htmlEncode(record.data.detail) + '
' - ] - }] - }); - win.show(); - } - } - ] - } - ] - } - ] - }, - { - xtype: 'pveCephStatusDetail', - itemId: 'statusdetail', - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - }, - title: gettext('Status') - }, - { - xtype: 'panel', - title: gettext('Performance'), - columnWidth: 1, - bodyPadding: 5, - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - flex: 1, - xtype: 'proxmoxGauge', - itemId: 'space', - title: gettext('Usage') - }, - { - flex: 2, - xtype: 'container', - defaults: { - padding: 0, - height: 100 - }, - items: [ - { - itemId: 'reads', - xtype: 'pveRunningChart', - title: gettext('Reads'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'writes', - xtype: 'pveRunningChart', - title: gettext('Writes'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'iops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS', // do not localize - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'readiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Reads'), - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'writeiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Writes'), - renderer: Ext.util.Format.numberRenderer('0,000') - } - ] - } - ] - } - ], - - generateCheckData: function(health) { - var result = []; - var checks = health.checks || {}; - var keys = Ext.Object.getKeys(checks).sort(); - - Ext.Array.forEach(keys, function(key) { - var details = checks[key].detail || []; - result.push({ - id: key, - summary: checks[key].summary.message, - detail: Ext.Array.reduce( - checks[key].detail, - function(first, second) { - return first + '\n' + second.message; - }, - '' - ), - severity: checks[key].severity - }); - }); - - return result; - }, - - updateAll: function(store, records, success) { - if (!success || records.length === 0) { - return; - } - - var me = this; - var rec = records[0]; - - // add health panel - me.down('#overallhealth').updateHealth(PVE.Utils.render_ceph_health(rec.data.health || {})); - // add errors to gridstore - me.down('#warnings').getStore().loadRawData(me.generateCheckData(rec.data.health || {}), false); - - // update detailstatus panel - me.getComponent('statusdetail').updateAll( - rec.data.health || {}, - rec.data.monmap || {}, - rec.data.pgmap || {}, - rec.data.osdmap || {}, - rec.data.quorum_names || []); - - // add performance data - var used = rec.data.pgmap.bytes_used; - var total = rec.data.pgmap.bytes_total; - - var text = Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(used), - PVE.Utils.render_size(total) - ); - - // update the usage widget - me.down('#space').updateValue(used/total, text); - - // TODO: logic for jewel (iops splitted in read/write) - - var iops = rec.data.pgmap.op_per_sec; - var readiops = rec.data.pgmap.read_op_per_sec; - var writeiops = rec.data.pgmap.write_op_per_sec; - var reads = rec.data.pgmap.read_bytes_sec || 0; - var writes = rec.data.pgmap.write_bytes_sec || 0; - - if (iops !== undefined && me.version !== 'hammer') { - me.change_version('hammer'); - } else if((readiops !== undefined || writeiops !== undefined) && me.version !== 'jewel') { - me.change_version('jewel'); - } - // update the graphs - me.reads.addDataPoint(reads); - me.writes.addDataPoint(writes); - me.iops.addDataPoint(iops); - me.readiops.addDataPoint(readiops); - me.writeiops.addDataPoint(writeiops); - }, - - change_version: function(version) { - var me = this; - me.version = version; - me.sp.set('ceph-version', version); - me.iops.setVisible(version === 'hammer'); - me.readiops.setVisible(version === 'jewel'); - me.writeiops.setVisible(version === 'jewel'); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.callParent(); - me.store = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + nodename, - interval: 5000, - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/ceph/status' - } - }); - - // save references for the updatefunction - me.iops = me.down('#iops'); - me.readiops = me.down('#readiops'); - me.writeiops = me.down('#writeiops'); - me.reads = me.down('#reads'); - me.writes = me.down('#writes'); - - // get ceph version - me.sp = Ext.state.Manager.getProvider(); - me.version = me.sp.get('ceph-version'); - me.change_version(me.version); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, me.store, regex, function(me, error){ - me.store.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.startUpdate(); - }); - } - ); - }); - - me.mon(me.store, 'load', me.updateAll, me); - me.on('destroy', me.store.stopUpdate); - me.store.startUpdate(); - } - -}); -Ext.define('PVE.ceph.StatusDetail', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveCephStatusDetail', - - layout: { - type: 'hbox', - align: 'stretch' - }, - - bodyPadding: '0 5 20', - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [{ - flex: 1, - itemId: 'monitors', - xtype: 'container', - items: [ - { - xtype: 'box', - width: '100%', - html: '

' + gettext('Monitors') + '

' - } - ] - },{ - flex: 1, - itemId: 'osds', - data: { - total: 0, - upin: 0, - upout: 0, - downin: 0, - downout: 0 - }, - tpl: [ - '

' + 'OSDs' + '

', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '
', - gettext('In'), - '', - gettext('Out'), - '
', - gettext('Up'), - '{upin}{upout}
', - gettext('Down'), - '{downin}{downout}
', - '
', - gettext('Total'), - ': {total}', - '
' - ] - }, - { - flex: 1.6, - itemId: 'pgs', - padding: '0 10', - data: { - states: [] - }, - tpl: [ - '

' + 'PGs' + '

', - '', - '
{state_name}:
', - '
{count}

', - '
', - '
' - ] - }], - - updateAll: function(health, monmap, pgmap, osdmap, quorum_names) { - var me = this; - me.suspendLayout = true; - - // update pgs sorted - var pgs_by_state = pgmap.pgs_by_state || []; - pgs_by_state.sort(function(a,b){ - return (a.state_name < b.state_name)?-1:(a.state_name === b.state_name)?0:1; - }); - me.getComponent('pgs').update({states: pgs_by_state}); - - var downinregex = /(\d+) osds down/; - var monnameregex = /^mon.(\S+) /; - var downin_osds = 0; - var monmsgs = {}; - - // we collect monitor/osd information from the checks - Ext.Object.each(health.checks, function(key, value, obj) { - var found = null; - if (key === 'OSD_DOWN') { - found = value.summary.message.match(downinregex); - if (found !== null) { - downin_osds = parseInt(found[1],10); - } - } - else if (Ext.String.startsWith(key, 'MON_')) { - if (!value.detail) { - return; - } - found = value.detail[0].message.match(monnameregex); - if (found !== null) { - if (!monmsgs[found[1]]) { - monmsgs[found[1]] = []; - } - monmsgs[found[1]].push({ - text: Ext.Array.reduce(value.detail, function(first, second) { - return first + '\n' + second.message; - }, ''), - severity: value.severity - }); - } - } - }); - - // update osds counts - - var total_osds = osdmap.osdmap.num_osds || 0; - var in_osds = osdmap.osdmap.num_in_osds || 0; - var up_osds = osdmap.osdmap.num_up_osds || 0; - var out_osds = total_osds - in_osds; - var down_osds = total_osds - up_osds; - - var downout_osds = down_osds - downin_osds; - var upin_osds = in_osds - downin_osds; - var upout_osds = up_osds - upin_osds; - var osds = { - total: total_osds, - upin: upin_osds, - upout: upout_osds, - downin: downin_osds, - downout: downout_osds - }; - me.getComponent('osds').update(osds); - - // update the monitors - var mons = monmap.mons.sort(function(a,b) { - return (a.name < b.name)?-1:(a.name > b.name)?1:0; - }); - - var monContainer = me.getComponent('monitors'); - - var i; - for (i = 0; i < mons.length; i++) { - var monitor = monContainer.getComponent('mon.' + mons[i].name); - if (!monitor) { - // since mons are already sorted, and - // we always have a sorted list - // we can add it at the mons+1 position (because of the title) - monitor = monContainer.insert(i+1, { - xtype: 'pveCephMonitorWidget', - itemId: 'mon.' + mons[i].name - }); - } - monitor.updateMonitor(mons[i], monmsgs, quorum_names); - } - me.suspendLayout = false; - me.updateLayout(); - } -}); - -Ext.define('PVE.ceph.MonitorWidget', { - extend: 'Ext.Component', - alias: 'widget.pveCephMonitorWidget', - - userCls: 'monitor inline-block', - data: { - name: '0', - health: 'HEALTH_ERR', - text: '', - iconCls: PVE.Utils.get_health_icon(), - addr: '' - }, - - tpl: [ - '{name}: ', - '' - ], - - // expects 3 variables which are - // timestate: the status from timechecks.mons - // data: the monmap.mons data - // quorum_names: the quorum_names array - updateMonitor: function(data, monmsgs, quorum_names) { - var me = this; - var state = 'HEALTH_ERR'; - var text = ''; - var healthstates = { - 'HEALTH_OK': 3, - 'HEALTH_WARN': 2, - 'HEALTH_ERR': 1 - }; - - if (quorum_names && - quorum_names.indexOf(data.name) !== -1) { - state = 'HEALTH_OK'; - } - - if (monmsgs[data.name]) { - Ext.Array.forEach(monmsgs[data.name], function(msg) { - if (healthstates[msg.severity] < healthstates[state]) { - state = msg.severity; - } - - text += msg.text + "\n"; - }); - } - - me.update(Ext.apply(me.data, { - health: state, - text: text, - addr: data.addr, - name: data.name, - iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[state]) - })); - }, - - listeners: { - mouseenter: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (!me) { - return; - } - if (!me.tooltip) { - me.tooltip = Ext.create('Ext.tip.ToolTip', { - target: me.el, - trackMouse: true, - renderTo: Ext.getBody(), - html: gettext('Monitor') + ': ' + me.data.name + '
' + - gettext('Address') + ': ' + me.data.addr + '
' + - gettext('Health') + ': ' + me.data.health + '
' + - me.data.text - }); - } - me.tooltip.show(); - } - }, - mouseleave: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (me.tooltip) { - me.tooltip.destroy(); - delete me.tooltip; - } - } - } - } -}); -Ext.define('PVE.node.CephConfig', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfig', - - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/config', - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.CephConfigCrush', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfigCrush', - - onlineHelp: 'chapter_pveceph', - - layout: 'border', - items: [{ - title: gettext('Configuration'), - xtype: 'pveNodeCephConfig', - region: 'center' - }, - { - title: 'Crush Map', // do not localize - xtype: 'pveNodeCephCrushMap', - region: 'east', - split: true, - width: '50%' - }], - - initComponent: function() { - var me = this; - me.defaults = { - pveSelNode: me.pveSelNode - }; - me.callParent(); - } -}); -Ext.define('PVE.ceph.Log', { - extend: 'Proxmox.panel.LogView', - xtype: 'cephLogView', - nodename: undefined, - failCallback: function(response) { - var me = this; - var msg = response.htmlStatus; - var windowShow = PVE.Utils.showCephInstallOrMask(me, msg, me.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.loadTask.delay(200); - }); - } - ); - if (!windowShow) { - Proxmox.Utils.setErrorMask(me, msg); - } - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.CephInstallWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveCephInstallWizard', - mixins: ['Proxmox.Mixin.CBind'], - resizable: false, - nodename: undefined, - viewModel: { - data: { - nodename: '', - configuration: true, - isInstalled: false - } - }, - cbindData: { - nodename: undefined - }, - title: gettext('Setup'), - navigateNext: function() { - var tp = this.down('#wizcontent'); - var atab = tp.getActiveTab(); - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - }, - setInitialTab: function (index) { - var tp = this.down('#wizcontent'); - var initialTab = tp.items.getAt(index); - initialTab.enable(); - tp.setActiveTab(initialTab); - }, - onShow: function() { - this.callParent(arguments); - var isInstalled = this.getViewModel().get('isInstalled'); - if (isInstalled) { - this.getViewModel().set('configuration', false); - this.setInitialTab(2); - } - }, - items: [ - { - title: gettext('Info'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'chapter_pveceph', - html: '

Ceph?

'+ - '

"Ceph is a unified, distributed storage system designed for excellent performance, reliability and scalability."

'+ - '

Ceph is currently not installed on this node, click on the next button below to start the installation.'+ - ' This wizard will guide you through the necessary steps, after the initial installation you will be offered to create a initial configuration.'+ - ' The configuration step is only needed once per cluster and will be skipped if a config is already present.

'+ - '

Please take a look at our documentation, by clicking the help button below, before starting the installation, '+ - 'if you want to gain deeper knowledge about Ceph visit ceph.com.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#back').hide(true); - this.up('pveCephInstallWizard').down('#next').setText(gettext('Start installation')); - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#next').setText(gettext('Next')); - } - } - }, - { - title: gettext('Installation'), - xtype: 'panel', - layout: 'fit', - cbind:{ - nodename: '{nodename}' - }, - viewModel: {}, // needed to inherit parent viewModel data - listeners: { - afterrender: function() { - var me = this; - if (this.getViewModel().get('isInstalled')) { - this.mask("Ceph is already installed, click next to create your configuration.",['pve-static-mask']); - } else { - me.down('pveNoVncConsole').fireEvent('activate'); - } - }, - activate: function() { - var me = this; - var nodename = me.nodename; - me.updateStore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + nodename, - interval: 1000, - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/ceph/status' - }, - listeners: { - load: function(rec, response, success, operation) { - - if (success) { - me.updateStore.stopUpdate(); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("not initialized", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',false); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("rados_connect failed", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',true); - me.down('textfield').setValue('success'); - } else if (!operation.error.statusText.match("not installed", "i")) { - Proxmox.Utils.setErrorMask(me, operation.error.statusText); - } - } - } - }); - me.updateStore.startUpdate(); - }, - destroy: function() { - var me = this; - if (me.updateStore) { - me.updateStore.stopUpdate(); - } - } - }, - items: [ - { - itemId: 'jsconsole', - consoleType: 'cmd', - xtermjs: true, - xtype: 'pveNoVncConsole', - cbind:{ - nodename: '{nodename}' - }, - cmd: 'ceph_install' - }, - { - xtype: 'textfield', - name: 'installSuccess', - value: '', - allowBlank: false, - submitValue: false, - hidden: true - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Configuration'), - onlineHelp: 'chapter_pveceph', - cbind: { - nodename: '{nodename}' - }, - viewModel: { - data: { - replicas: undefined, - minreplicas: undefined - } - }, - listeners: { - activate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Next')); - }, - beforeshow: function() { - if (this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - this.mask("Coniguration already initialized",['pve-static-mask']); - } else { - this.unmask(); - } - }, - deactivate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Finish')); - } - }, - column1: [ - { - xtype: 'displayfield', - value: gettext('Ceph cluster configuration') + ':' - }, - { - xtype: 'textfield', - name: 'network', - vtype: 'IPCIDRAddress', - value: '', - fieldLabel: 'Public Network IP/CIDR', - bind: { - allowBlank: '{configuration}' - }, - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - this.validate(); - } - }, - { - xtype: 'textfield', - name: 'cluster-network', - vtype: 'IPCIDRAddress', - fieldLabel: 'Cluster Network IP/CIDR', - allowBlank: true, - emptyText: gettext('Same as Public Network') - } - // FIXME: add hint about cluster network and/or reference user to docs?? - ], - column2: [ - { - xtype: 'displayfield', - value: gettext('First Ceph monitor') + ':' - }, - { - xtype: 'pveNodeSelector', - fieldLabel: gettext('Monitor node'), - name: 'mon-node', - selectCurNode: true, - allowBlank: false - }, - { - xtype: 'displayfield', - value: gettext('Additional monitors are recommended. They can be created at any time in the Monitor tab.'), - userCls: 'pve-hint' - } - ], - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'size', - fieldLabel: 'Number of replicas', - bind: { - value: '{replicas}' - }, - maxValue: 7, - minValue: 2, - emptyText: '3' - }, - { - xtype: 'numberfield', - name: 'min_size', - fieldLabel: 'Minimum replicas', - bind: { - maxValue: '{replicas}', - value: '{minreplicas}' - }, - minValue: 2, - maxValue: 3, - setMaxValue: function(value) { - this.maxValue = Ext.Number.from(value, 2); - // allow enough to avoid split brains with max 'size', but more makes simply no sense - if (this.maxValue > 4) { - this.maxValue = 4; - } - this.toggleSpinners(); - this.validate(); - }, - emptyText: '2' - } - ], - onGetValues: function(values) { - ['cluster-network', 'size', 'min_size'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - return values; - }, - onSubmit: function() { - var me = this; - if (!this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - var wizard = me.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - var monNode = kv['mon-node']; - delete kv['mon-node']; - var nodename = me.nodename; - delete kv.nodename; - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/ceph/init', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + monNode + '/ceph/mon', - waitMsgTarget: wizard, - method: 'POST', - success: function() { - me.up('pveCephInstallWizard').navigateNext(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - - } else { - me.up('pveCephInstallWizard').navigateNext(); - } - } - }, - { - title: gettext('Success'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'pve_ceph_install', - html: '

Installation successful!

'+ - '

The basic installation and configuration is completed, depending on your setup some of the following steps are required to start using Ceph:

'+ - '
  1. Install Ceph on other nodes
  2. '+ - '
  3. Create additional Ceph Monitors
  4. '+ - '
  5. Create Ceph OSDs
  6. '+ - '
  7. Create Ceph Pools
'+ - '

To learn more click on the help button below.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - - var tp = this.up('#wizcontent'); - var idx = tp.items.indexOf(this)-1; - for(;idx >= 0;idx--) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - onSubmit: function() { - var wizard = this.up('pveCephInstallWizard'); - wizard.close(); - } - } - ] - }); -Ext.define('PVE.node.DiskList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeDiskList', - emptyText: gettext('No Disks found'), - stateful: true, - stateId: 'grid-node-disks', - columns: [ - { - header: gettext('Device'), - width: 100, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Type'), - width: 80, - sortable: true, - dataIndex: 'type', - renderer: function(v) { - if (v === 'ssd') { - return 'SSD'; - } else if (v === 'hdd') { - return 'Hard Disk'; - } else if (v === 'usb'){ - return 'USB'; - } else { - return gettext('Unknown'); - } - } - }, - { - header: gettext('Usage'), - width: 80, - sortable: false, - renderer: function(v, metaData, rec) { - if (rec) { - if (rec.data.osdid >= 0) { - var bluestore = ''; - if (rec.data.bluestore === 1) { - bluestore = ' (Bluestore)'; - } - return "Ceph osd." + rec.data.osdid.toString() + bluestore; - } - - var types = []; - if (rec.data.journals > 0) { - types.push('Journal'); - } - - if (rec.data.db > 0) { - types.push('DB'); - } - - if (rec.data.wal > 0) { - types.push('WAL'); - } - - if (types.length > 0) { - return 'Ceph (' + types.join(', ') + ')'; - } - } - - return v || Proxmox.Utils.noText; - }, - dataIndex: 'used' - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: 'GPT', - width: 60, - align: 'right', - renderer: function(value) { - if (value) { - return Proxmox.Utils.yesText; - } else { - return Proxmox.Utils.noText; - } - }, - dataIndex: 'gpt' - }, - { - header: gettext('Vendor'), - width: 100, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'vendor' - }, - { - header: gettext('Model'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'model' - }, - { - header: gettext('Serial'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'serial' - }, - { - header: 'S.M.A.R.T.', - width: 100, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'health' - }, - { - header: 'Wearout', - width: 100, - sortable: true, - dataIndex: 'wearout', - renderer: function(value) { - if (Ext.isNumeric(value)) { - return (100 - value).toString() + '%'; - } - return 'N/A'; - } - } - ], - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var store = Ext.create('Ext.data.Store', { - storeid: 'node-disk-list' + nodename, - model: 'node-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list" - }, - sorters: [ - { - property : 'dev', - direction: 'ASC' - } - ] - }); - - var reloadButton = Ext.create('Proxmox.button.Button', { - text: gettext('Reload'), - handler: function() { - me.store.load(); - } - }); - - var smartButton = Ext.create('Proxmox.button.Button', { - text: gettext('Show S.M.A.R.T. values'), - selModel: sm, - enableFn: function() { - return !!sm.getSelection().length; - }, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - }); - - var initButton = Ext.create('Proxmox.button.Button', { - text: gettext('Initialize Disk with GPT'), - selModel: sm, - enableFn: function() { - var selection = sm.getSelection(); - - if (!selection.length || selection[0].data.used) { - return false; - } else { - return true; - } - }, - disabled: true, - - handler: function() { - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + nodename + '/disks/initgpt', - waitMsgTarget: me, - method: 'POST', - params: { disk: rec.data.devpath}, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - } - }); - - me.loadCount = 1; // avoid duplicate loadmask - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ reloadButton, smartButton, initButton ], - listeners: { - itemdblclick: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - } - }); - - - me.callParent(); - me.store.load(); - } -}, function() { - - Ext.define('node-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial', 'rpm', 'type', 'health', 'wearout' ], - idProperty: 'devpath' - }); -}); - -Ext.define('PVE.DiskSmartWindow', { - extend: 'Ext.window.Window', - alias: 'widget.pveSmartWindow', - - modal: true, - - items: [ - { - xtype: 'gridpanel', - layout: { - type: 'fit' - }, - emptyText: gettext('No S.M.A.R.T. Values'), - scrollable: true, - flex: 1, - itemId: 'smarts', - reserveScrollbar: true, - columns: [ - { text: 'ID', dataIndex: 'id', width: 50 }, - { text: gettext('Attribute'), flex: 1, dataIndex: 'name', renderer: Ext.String.htmlEncode }, - { text: gettext('Value'), dataIndex: 'raw', renderer: Ext.String.htmlEncode }, - { text: gettext('Normalized'), dataIndex: 'value', width: 60}, - { text: gettext('Threshold'), dataIndex: 'threshold', width: 60}, - { text: gettext('Worst'), dataIndex: 'worst', width: 60}, - { text: gettext('Flags'), dataIndex: 'flags'}, - { text: gettext('Failing'), dataIndex: 'fail', renderer: Ext.String.htmlEncode } - ] - }, - { - xtype: 'component', - itemId: 'text', - layout: { - type: 'fit' - }, - hidden: true, - style: { - 'background-color': '#23272a', - 'white-space': 'pre', - 'font-family': 'monospace' - } - } - ], - - buttons: [ - { - text: gettext('Reload'), - name: 'reload', - handler: function() { - var me = this; - me.up('window').store.reload(); - } - }, - { - text: gettext('Close'), - name: 'close', - handler: function() { - var me = this; - me.up('window').close(); - } - } - ], - - layout: { - type: 'vbox', - align: 'stretch' - }, - width: 800, - height: 500, - minWidth: 600, - minHeight: 400, - bodyPadding: 5, - title: gettext('S.M.A.R.T. Values'), - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var dev = me.dev; - if (!dev) { - throw "no device specified"; - } - - me.store = Ext.create('Ext.data.Store', { - model: 'disk-smart', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/smart?disk=" + dev - } - }); - - me.callParent(); - var grid = me.down('#smarts'); - var text = me.down('#text'); - - Proxmox.Utils.monStoreErrors(grid, me.store); - me.mon(me.store, 'load', function(s, records, success) { - if (success && records.length > 0) { - var rec = records[0]; - switch (rec.data.type) { - case 'text': - grid.setVisible(false); - text.setVisible(true); - text.setHtml(Ext.String.htmlEncode(rec.data.text)); - break; - default: - // includes 'ata' - // cannot use empty case because - // of jslint - grid.setVisible(true); - text.setVisible(false); - grid.setStore(rec.attributes()); - break; - } - } - }); - - me.store.load(); - } -}, function() { - - Ext.define('disk-smart', { - extend: 'Ext.data.Model', - fields: [ - { name:'health'}, - { name:'type'}, - { name:'text'} - ], - hasMany: {model: 'smart-attribute', name: 'attributes'} - }); - Ext.define('smart-attribute', { - extend: 'Ext.data.Model', - fields: [ - { name:'id', type:'number' }, 'name', 'value', 'worst', 'threshold', 'flags', 'fail', 'raw' - ] - }); -}); -Ext.define('PVE.node.CreateLVM', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVM', - - subject: 'LVM Volume Group', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvm", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMList', { - extend: 'Ext.tree.Panel', - xtype: 'pveLVMList', - emptyText: gettext('No Volume Groups found'), - stateful: true, - stateId: 'grid-node-lvm', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Number of LVs'), - dataIndex: 'lvcount', - width: 150, - align: 'right' - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Volume Group', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVM', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/lvm", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'size', 'free', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - txt += (data.leaf) ? 'hdd-o' : 'object-group'; - return txt; - } - }, - { - type: 'number', - name: 'usage', - calculate: function(data) { - return ((data.size-data.free)/data.size); - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.CreateLVMThin', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVMThin', - - subject: 'LVM Thinpool', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvmthin", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMThinList', { - extend: 'Ext.grid.Panel', - xtype: 'pveLVMThinList', - - emptyText: gettext('No thinpools found'), - stateful: true, - stateId: 'grid-node-lvmthin', - columns: [ - { - text: gettext('Name'), - dataIndex: 'lv', - flex: 1 - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'lv_size' - }, - { - header: gettext('Used'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'used' - }, - { - header: gettext('Metadata Usage'), - width: 120, - dataIndex: 'metadata_usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Metadata Size'), - width: 120, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_size' - }, - { - header: gettext('Metadata Used'), - width: 125, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_used' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Thinpool', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVMThin', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['lv', 'lv_size', 'used', 'metadata_size', 'metadata_used', - { - type: 'number', - name: 'usage', - calculate: function(data) { - return data.used/data.lv_size; - } - }, - { - type: 'number', - name: 'metadata_usage', - calculate: function(data) { - return data.metadata_used/data.metadata_size; - } - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/lvmthin' - }, - sorters: 'lv' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.CreateDirectory', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateDirectory', - - subject: Proxmox.Utils.directoryText, - - showProgress: true, - - onlineHelp: 'chapter_storage', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/directory", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['ext4', 'ext4'], - ['xfs', 'xfs'] - ], - fieldLabel: gettext('Filesystem'), - name: 'filesystem', - value: '', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.Directorylist', { - extend: 'Ext.grid.Panel', - xtype: 'pveDirectoryList', - - stateful: true, - stateId: 'grid-node-directory', - columns: [ - { - text: gettext('Path'), - dataIndex: 'path', - flex: 1 - }, - { - header: gettext('Device'), - flex: 1, - dataIndex: 'device' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'type' - }, - { - header: gettext('Options'), - width: 100, - dataIndex: 'options' - }, - { - header: gettext('Unit File'), - hidden: true, - dataIndex: 'unitfile' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Directory', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateDirectory', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['path', 'device', 'type', 'options', 'unitfile' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/directory' - }, - sorters: 'path' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -/*jslint confusion: true*/ -Ext.define('PVE.node.CreateZFS', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateZFS', - - subject: 'ZFS', - - showProgress: true, - - onlineHelp: 'chapter_zfs', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - var update_disklist = function() { - var grid = me.down('#disklist'); - var disks = grid.getSelection(); - - var val = []; - disks.sort(function(a,b) { - var aorder = a.get('order') || 0; - var border = b.get('order') || 0; - return (aorder - border); - }); - - disks.forEach(function(disk) { - val.push(disk.get('devpath')); - }); - - me.down('field[name=devices]').setValue(val.join(',')); - }; - - Ext.apply(me, { - url: '/nodes/' + me.nodename + '/disks/zfs', - method: 'POST', - items: [ - { - xtype: 'inputpanel', - onGetValues: function(values) { - return values; - }, - column1: [ - { - xtype: 'textfield', - hidden: true, - name: 'devices', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ], - column2: [ - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('RAID Level'), - name: 'raidlevel', - value: 'single', - comboItems: [ - ['single', gettext('Single Disk')], - ['mirror', 'Mirror'], - ['raid10', 'RAID10'], - ['raidz', 'RAIDZ'], - ['raidz2', 'RAIDZ2'], - ['raidz3', 'RAIDZ3'] - ] - }, - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Compression'), - name: 'compression', - value: 'on', - comboItems: [ - ['on', 'on'], - ['off', 'off'], - ['gzip', 'gzip'], - ['lz4', 'lz4'], - ['lzjb', 'lzjb'], - ['zle', 'zle'] - ] - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('ashift'), - minValue: 9, - maxValue: 16, - value: '12', - name: 'ashift' - } - ], - columnB: [ - { - xtype: 'grid', - height: 200, - emptyText: gettext('No Disks unused'), - itemId: 'disklist', - selModel: 'checkboxmodel', - listeners: { - selectionchange: update_disklist - }, - store: { - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/disks/list?type=unused' - } - }, - columns: [ - { - text: gettext('Device'), - dataIndex: 'devpath', - flex: 1 - }, - { - text: gettext('Serial'), - dataIndex: 'serial' - }, - { - text: gettext('Size'), - dataIndex: 'size', - renderer: PVE.Utils.render_size - }, - { - header: gettext('Order'), - xtype: 'widgetcolumn', - dataIndex: 'order', - sortable: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 1, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('order', value); - update_disklist(record); - } - } - } - } - ] - } - ] - } - ] - }); - - me.callParent(); - me.down('#disklist').getStore().load(); - } -}); - -Ext.define('PVE.node.ZFSDevices', { - extend: 'Ext.tree.Panel', - xtype: 'pveZFSDevices', - stateful: true, - stateId: 'grid-node-zfsstatus', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'state' - }, - { - text: 'READ', - dataIndex: 'read' - }, - { - text: 'WRITE', - dataIndex: 'write' - }, - { - text: 'CKSUM', - dataIndex: 'cksum' - }, - { - text: gettext('Message'), - dataIndex: 'msg' - } - ], - - rootVisible: true, - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/zfs/" + me.zpool, - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'status', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - if (data.leaf) { - return txt + 'hdd-o'; - } - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSStatus', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveZFSStatus', - layout: 'fit', - border: false, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - me.url = "/api2/extjs/nodes/" + me.nodename + "/disks/zfs/" + me.zpool; - - me.rows = { - scan: { - header: gettext('Scan') - }, - status: { - header: gettext('Status') - }, - action: { - header: gettext('Action') - }, - errors: { - header: gettext('Errors') - } - }; - - me.callParent(); - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSList', { - extend: 'Ext.grid.Panel', - xtype: 'pveZFSList', - - stateful: true, - stateId: 'grid-node-zfs', - columns: [ - { - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Size'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - }, - { - header: gettext('Allocated'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'alloc' - }, - { - header: gettext('Fragmentation'), - renderer: function(value) { - return value.toString() + '%'; - }, - dataIndex: 'frag' - }, - { - header: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'health' - }, - { - header: gettext('Deduplication'), - hidden: true, - renderer: function(value) { - return value.toFixed(2).toString() + 'x'; - }, - dataIndex: 'dedup' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': ZFS', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateZFS', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - }, - { - text: gettext('Detail'), - itemId: 'detailbtn', - disabled: true, - handler: function() { - var me = this.up('panel'); - var selection = me.getSelection(); - if (selection.length < 1) { - return; - } - me.show_detail(selection[0].get('name')); - } - } - ], - - show_detail: function(zpool) { - var me = this; - - var detailsgrid = Ext.create('PVE.node.ZFSStatus', { - layout: 'fit', - nodename: me.nodename, - flex: 0, - zpool: zpool - }); - - var devicetree = Ext.create('PVE.node.ZFSDevices', { - title: gettext('Devices'), - nodename: me.nodename, - flex: 1, - zpool: zpool - }); - - - var win = Ext.create('Ext.window.Window', { - modal: true, - width: 800, - height: 400, - resizable: true, - layout: 'fit', - title: gettext('Status') + ': ' + zpool, - items:[{ - xtype: 'panel', - region: 'center', - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [detailsgrid, devicetree], - tbar: [{ - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - - devicetree.reload(); - detailsgrid.reload(); - } - }] - }] - }).show(); - }, - - set_button_status: function() { - var me = this; - var selection = me.getSelection(); - me.down('#detailbtn').setDisabled(selection.length === 0); - }, - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - }, - selectionchange: function() { - this.set_button_status(); - }, - itemdblclick: function(grid, record) { - var me = this; - me.show_detail(record.get('name')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['name', 'size', 'free', 'alloc', 'dedup', 'frag', 'health'], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/zfs' - }, - sorters: 'name' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveNodeStatus', - - height: 300, - bodyPadding: '20 15 20 15', - - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 15 5 15' - }, - - items: [ - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpuinfo', - renderer: PVE.Utils.render_node_cpu_usage - }, - { - itemId: 'wait', - iconCls: 'fa fa-fw fa-clock-o', - title: gettext('IO delay'), - valueField: 'wait', - rowspan: 2 - }, - { - itemId: 'load', - iconCls: 'fa fa-fw fa-tasks', - title: gettext('Load average'), - printBar: false, - textField: 'loadavg' - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - itemId: 'memory', - title: gettext('RAM usage'), - valueField: 'memory', - maxField: 'memory', - renderer: PVE.Utils.render_node_size_usage - }, - { - itemId: 'ksm', - printBar: false, - title: gettext('KSM sharing'), - textField: 'ksm', - renderer: function(record) { - return PVE.Utils.render_size(record.shared); - }, - padding: '0 15 10 15' - }, - { - iconCls: 'fa fa-fw fa-hdd-o', - itemId: 'rootfs', - title: gettext('HD space') + '(root)', - valueField: 'rootfs', - maxField: 'rootfs', - renderer: PVE.Utils.render_node_size_usage - }, - { - iconCls: 'fa fa-fw fa-refresh', - itemId: 'swap', - printSize: true, - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'swap', - renderer: PVE.Utils.render_node_size_usage - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - itemId: 'cpus', - colspan: 2, - printBar: false, - title: gettext('CPU(s)'), - textField: 'cpuinfo', - renderer: function(cpuinfo) { - return cpuinfo.cpus + " x " + cpuinfo.model + " (" + - cpuinfo.sockets.toString() + " " + - (cpuinfo.sockets > 1 ? - gettext('Sockets') : - gettext('Socket') - ) + ")"; - }, - value: '' - }, - { - itemId: 'kversion', - colspan: 2, - title: gettext('Kernel Version'), - printBar: false, - textField: 'kversion', - value: '' - }, - { - itemId: 'version', - colspan: 2, - printBar: false, - title: gettext('PVE Manager Version'), - textField: 'pveversion', - value: '' - } - ], - - updateTitle: function() { - var me = this; - var uptime = Proxmox.Utils.render_uptime(me.getRecordValue('uptime')); - me.setTitle(me.pveSelNode.data.node + ' (' + gettext('Uptime') + ': ' + uptime + ')'); - } - -}); -Ext.define('PVE.node.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeSummary', - - scrollable: true, - bodyPadding: 5, - - showVersions: function() { - var me = this; - - // Note: we use simply text/html here, because ExtJS grid has problems - // with cut&paste - - var nodename = me.pveSelNode.data.node; - - var view = Ext.createWidget('component', { - autoScroll: true, - padding: 5, - style: { - 'background-color': '#23272a', - 'white-space': 'pre', - 'font-family': 'monospace' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Package versions'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + nodename + "/apt/versions", - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - var text = ''; - - Ext.Array.each(response.result.data, function(rec) { - var version = "not correctly installed"; - var pkg = rec.Package; - if (rec.OldVersion && rec.CurrentState === 'Installed') { - version = rec.OldVersion; - } - if (rec.RunningKernel) { - text += pkg + ': ' + version + ' (running kernel: ' + - rec.RunningKernel + ')\n'; - } else if (rec.ManagerVersion) { - text += pkg + ': ' + version + ' (running version: ' + - rec.ManagerVersion + ')\n'; - } else { - text += pkg + ': ' + version + '\n'; - } - }); - - view.update(Ext.htmlEncode(text)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var rstore = me.statusStore; - - var version_btn = new Ext.Button({ - text: gettext('Package versions'), - handler: function(){ - Proxmox.Utils.checked_command(function() { me.showVersions(); }); - } - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/rrddata", - model: 'pve-rrd-node' - }); - - Ext.apply(me, { - tbar: [version_btn, '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: 'column', - defaults: { - minHeight: 320, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: [ - { - xtype: 'pveNodeStatus', - rstore: rstore, - width: 770, - pveSelNode: me.pveSelNode - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - fields: ['cpu','iowait'], - fieldTitles: [gettext('CPU usage'), gettext('IO delay')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Server load'), - fields: ['loadavg'], - fieldTitles: [gettext('Load average')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - fields: ['memtotal','memused'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - fields: ['netin','netout'], - store: rrdstore - } - ] - } - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - } -}); -/*global Blob*/ -Ext.define('PVE.node.SubscriptionKeyEdit', { - extend: 'Proxmox.window.Edit', - title: gettext('Upload Subscription Key'), - width: 300, - items: { - xtype: 'textfield', - name: 'key', - value: '', - fieldLabel: gettext('Subscription Key') - }, - initComponent : function() { - var me = this; - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.Subscription', { - extend: 'Proxmox.grid.ObjectGrid', - - alias: ['widget.pveNodeSubscription'], - - onlineHelp: 'getting_help', - - viewConfig: { - enableTextSelection: true - }, - - showReport: function() { - var me = this; - var nodename = me.pveSelNode.data.node; - - var getReportFileName = function() { - var now = Ext.Date.format(new Date(), 'D-d-F-Y-G-i'); - return me.nodename + '-report-' + now + '.txt'; - }; - - var view = Ext.createWidget('component', { - itemId: 'system-report-view', - scrollable: true, - style: { - 'background-color': '#23272a', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var reportWindow = Ext.create('Ext.window.Window', { - title: gettext('System Report'), - width: 1024, - height: 600, - layout: 'fit', - modal: true, - buttons: [ - '->', - { - text: gettext('Download'), - handler: function() { - var fileContent = reportWindow.getComponent('system-report-view').html; - var fileName = getReportFileName(); - - // Internet Explorer - if (window.navigator.msSaveOrOpenBlob) { - navigator.msSaveOrOpenBlob(new Blob([fileContent]), fileName); - } else { - var element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset=utf-8,' - + encodeURIComponent(fileContent)); - element.setAttribute('download', fileName); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - } - } - } - ], - items: view - }); - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + me.nodename + '/report', - method: 'GET', - waitMsgTarget: me, - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var report = Ext.htmlEncode(response.result.data); - reportWindow.show(); - view.update(report); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var reload = function() { - me.rstore.load(); - }; - - var baseurl = '/nodes/' + me.nodename + '/subscription'; - - var render_status = function(value) { - - var message = me.getObjectValue('message'); - - if (message) { - return value + ": " + message; - } - return value; - }; - - var rows = { - productname: { - header: gettext('Type') - }, - key: { - header: gettext('Subscription Key') - }, - status: { - header: gettext('Status'), - renderer: render_status - }, - message: { - visible: false - }, - serverid: { - header: gettext('Server ID') - }, - sockets: { - header: gettext('Sockets') - }, - checktime: { - header: gettext('Last checked'), - renderer: Proxmox.Utils.render_timestamp - }, - nextduedate: { - header: gettext('Next due date') - } - }; - - Ext.apply(me, { - url: '/api2/json' + baseurl, - cwidth1: 170, - tbar: [ - { - text: gettext('Upload Subscription Key'), - handler: function() { - var win = Ext.create('PVE.node.SubscriptionKeyEdit', { - url: '/api2/extjs/' + baseurl - }); - win.show(); - win.on('destroy', reload); - } - }, - { - text: gettext('Check'), - handler: function() { - Proxmox.Utils.API2Request({ - params: { force: 1 }, - url: baseurl, - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: reload - }); - } - }, - { - text: gettext('System Report'), - handler: function() { - Proxmox.Utils.checked_command(function (){ me.showReport(); }); - } - } - ], - rows: rows, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.node.CertificateView', { - extend: 'Ext.container.Container', - xtype: 'pveCertificatesView', - - onlineHelp: 'sysadmin_certificate_management', - - mixins: ['Proxmox.Mixin.CBind' ], - - items: [ - { - xtype: 'pveCertView', - border: 0, - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'pveACMEView', - border: 0, - cbind: { - nodename: '{nodename}' - } - } - ] - -}); - -Ext.define('PVE.node.CertificateViewer', { - extend: 'Proxmox.window.Edit', - - title: gettext('Certificate'), - - fieldDefaults: { - labelWidth: 120 - }, - width: 800, - resizable: true, - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Name'), - name: 'filename' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Fingerprint'), - name: 'fingerprint' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Issuer'), - name: 'issuer' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject'), - name: 'subject' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Valid Since'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notbefore' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Expires'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notafter' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject Alternative Names'), - name: 'san', - renderer: PVE.Utils.render_san - }, - { - xtype: 'textarea', - editable: false, - grow: true, - growMax: 200, - fieldLabel: gettext('Certificate'), - name: 'pem' - } - ], - - initComponent: function() { - var me = this; - - if (!me.cert) { - throw "no cert given"; - } - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/info'; - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - if (Ext.isArray(response.result.data)) { - Ext.Array.each(response.result.data, function(item) { - if (item.filename === me.cert) { - me.setValues(item); - return false; - } - }); - } - } - }); - } -}); - -Ext.define('PVE.node.CertUpload', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCertUpload', - - title: gettext('Upload Custom Certificate'), - resizable: false, - isCreate: true, - submitText: gettext('Upload'), - method: 'POST', - width: 600, - - apiCallDone: function(success, response, options) { - if (!success) { - return; - } - - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - items: [ - { - fieldLabel: gettext('Private Key (Optional)'), - labelAlign: 'top', - emptyText: gettext('No change'), - name: 'key', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=key]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'box', - autoEl: 'hr' - }, - { - fieldLabel: gettext('Certificate Chain'), - labelAlign: 'top', - allowBlank: false, - name: 'certificates', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=certificates]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'hidden', - name: 'restart', - value: '1' - }, - { - xtype: 'hidden', - name: 'force', - value: '1' - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/custom'; - - me.callParent(); - } -}); - -Ext.define('pve-certificate', { - extend: 'Ext.data.Model', - - fields: [ 'filename', 'fingerprint', 'issuer', 'notafter', 'notbefore', 'subject', 'san' ], - idProperty: 'filename' -}); - -Ext.define('PVE.node.Certificates', { - extend: 'Ext.grid.Panel', - xtype: 'pveCertView', - - tbar: [ - { - xtype: 'button', - text: gettext('Upload Custom Certificate'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.CertUpload', { - nodename: me.nodename - }); - win.show(); - win.on('destroy', me.reload, me); - } - }, - { - xtype: 'button', - itemId: 'deletebtn', - text: gettext('Delete Custom Certificate'), - handler: function() { - var me = this.up('grid'); - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/certificates/custom?restart=1', - method: 'DELETE', - success: function(response, opt) { - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - '-', - { - xtype: 'proxmoxButton', - itemId: 'viewbtn', - disabled: true, - text: gettext('View Certificate'), - handler: function() { - var me = this.up('grid'); - me.view_certificate(); - } - } - ], - - columns: [ - { - header: gettext('File'), - width: 150, - dataIndex: 'filename' - }, - { - header: gettext('Issuer'), - flex: 1, - dataIndex: 'issuer' - }, - { - header: gettext('Subject'), - flex: 1, - dataIndex: 'subject' - }, - { - header: gettext('Valid Since'), - width: 150, - dataIndex: 'notbefore', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Expires'), - width: 150, - dataIndex: 'notafter', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Subject Alternative Names'), - flex: 1, - dataIndex: 'san', - renderer: PVE.Utils.render_san - }, - { - header: gettext('Fingerprint'), - dataIndex: 'fingerprint', - hidden: true - }, - { - header: gettext('PEM'), - dataIndex: 'pem', - hidden: true - } - ], - - reload: function() { - var me = this; - me.rstore.load(); - }, - - set_button_status: function() { - var me = this; - var rec = me.rstore.getById('pveproxy-ssl.pem'); - - me.down('#deletebtn').setDisabled(!rec); - }, - - view_certificate: function() { - var me = this; - var selection = me.getSelection(); - if (!selection || selection.length < 1) { - return; - } - var win = Ext.create('PVE.node.CertificateViewer', { - cert: selection[0].data.filename, - nodename : me.nodename - }); - win.show(); - }, - - listeners: { - itemdblclick: 'view_certificate' - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'certs-' + me.nodename, - model: 'pve-certificate', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/certificates/info' - } - }); - - me.store = { - type: 'diff', - rstore: me.rstore - }; - - me.callParent(); - - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - } -}); -Ext.define('PVE.node.ACMEEditor', { - extend: 'Proxmox.window.Edit', - xtype: 'pveACMEEditor', - - subject: gettext('Domains'), - items: [ - { - xtype: 'inputpanel', - items: [ - { - xtype: 'textarea', - fieldLabel: gettext('Domains'), - emptyText: "domain1.example.com\ndomain2.example.com", - name: 'domains' - } - ], - onGetValues: function(values) { - if (!values.domains) { - return { - 'delete': 'acme' - }; - } - var domains = values.domains.split(/\n/).join(';'); - return { - 'acme': 'domains=' + domains - }; - } - } - ], - - initComponent: function() { - var me = this; - me.callParent(); - - me.load({ - success: function(response, opts) { - var res = PVE.Parser.parseACME(response.result.data.acme); - if (res) { - res.domains = res.domains.join(' '); - me.setValues(res); - } - } - }); - } -}); - -Ext.define('PVE.node.ACMEAccountCreate', { - extend: 'Proxmox.window.Edit', - - width: 400, - title: gettext('Register Account'), - isCreate: true, - method: 'POST', - submitText: gettext('Register'), - url: '/cluster/acme/account', - showTaskViewer: true, - - items: [ - { - xtype: 'proxmoxComboGrid', - name: 'directory', - allowBlank: false, - valueField: 'url', - displayField: 'name', - fieldLabel: gettext('ACME Directory'), - store: { - autoLoad: true, - fields: ['name', 'url'], - idProperty: ['name'], - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/acme/directories' - }, - sorters: { - property: 'name', - order: 'ASC' - } - }, - listConfig: { - columns: [ - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('URL'), - dataIndex: 'url', - flex: 1 - } - ] - }, - listeners: { - change: function(combogrid, value) { - var me = this; - if (!value) { - return; - } - - var disp = me.up('window').down('#tos_url_display'); - var field = me.up('window').down('#tos_url'); - var checkbox = me.up('window').down('#tos_checkbox'); - - disp.setValue(gettext('Loading')); - field.setValue(undefined); - checkbox.setValue(undefined); - - Proxmox.Utils.API2Request({ - url: '/cluster/acme/tos', - method: 'GET', - params: { - directory: value - }, - success: function(response, opt) { - me.up('window').down('#tos_url').setValue(response.result.data); - me.up('window').down('#tos_url_display').setValue(response.result.data); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - }, - { - xtype: 'displayfield', - itemId: 'tos_url_display', - fieldLabel: gettext('Terms of Service'), - renderer: PVE.Utils.render_optional_url, - name: 'tos_url_display' - }, - { - xtype: 'hidden', - itemId: 'tos_url', - name: 'tos_url' - }, - { - xtype: 'proxmoxcheckbox', - itemId: 'tos_checkbox', - fieldLabel: gettext('Accept TOS'), - submitValue: false, - validateValue: function(value) { - if (value && this.checked) { - return true; - } - return false; - } - }, - { - xtype: 'textfield', - name: 'contact', - vtype: 'email', - allowBlank: false, - fieldLabel: gettext('E-Mail') - } - ] - -}); - -Ext.define('PVE.node.ACMEAccountView', { - extend: 'Proxmox.window.Edit', - - width: 600, - fieldDefaults: { - labelWidth: 140 - }, - - title: gettext('Account'), - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('E-Mail'), - name: 'email' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Created'), - name: 'createdAt' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Status'), - name: 'status' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Directory'), - renderer: PVE.Utils.render_optional_url, - name: 'directory' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Terms of Services'), - renderer: PVE.Utils.render_optional_url, - name: 'tos' - } - ], - - initComponent: function() { - var me = this; - - if (!me.accountname) { - throw "no account name defined"; - } - - me.url = '/cluster/acme/account/' + me.accountname; - - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - var data = response.result.data; - data.email = data.account.contact[0]; - data.createdAt = data.account.createdAt; - data.status = data.account.status; - me.setValues(data); - } - }); - } -}); - -Ext.define('PVE.node.ACME', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveACMEView', - - margin: '10 0 0 0', - title: 'ACME', - - tbar: [ - { - xtype: 'button', - itemId: 'edit', - text: gettext('Edit Domains'), - handler: function() { - this.up('grid').run_editor(); - } - }, - { - xtype: 'button', - itemId: 'createaccount', - text: gettext('Register Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountCreate', { - taskDone: function() { - me.load_account(); - me.reload(); - } - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'viewaccount', - text: gettext('View Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountView', { - accountname: 'default' - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'order', - text: gettext('Order Certificate'), - handler: function() { - var me = this.up('grid'); - - Proxmox.Utils.API2Request({ - method: 'POST', - params: { - force: 1 - }, - url: '/nodes/' + me.nodename + '/certificates/acme/certificate', - success: function(response, opt) { - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: response.result.data, - taskDone: function(success) { - me.certificate_order_finished(success); - } - }); - win.show(); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ], - - certificate_order_finished: function(success) { - if (!success) { - return; - } - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - set_button_status: function() { - var me = this; - - var account = !!me.account; - var acmeObj = PVE.Parser.parseACME(me.getObjectValue('acme')); - var domains = acmeObj ? acmeObj.domains.length : 0; - - var order = me.down('#order'); - order.setVisible(account); - order.setDisabled(!account || !domains); - - me.down('#createaccount').setVisible(!account); - me.down('#viewaccount').setVisible(account); - }, - - load_account: function() { - var me = this; - - // for now we only use the 'default' account - Proxmox.Utils.API2Request({ - url: '/cluster/acme/account/default', - success: function(response, opt) { - me.account = response.result.data; - me.set_button_status(); - }, - failure: function(response, opt) { - me.account = undefined; - me.set_button_status(); - } - }); - }, - - run_editor: function() { - var me = this; - var win = Ext.create(me.rows.acme.editor, me.editorConfig); - win.show(); - win.on('destroy', me.reload, me); - }, - - listeners: { - itemdblclick: 'run_editor' - }, - - // account data gets loaded here - account: undefined, - - disableSelection: true, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/api2/json/nodes/' + me.nodename + '/config'; - - me.editorConfig = { - url: '/api2/extjs/nodes/' + me.nodename + '/config' - }; - /*jslint confusion: true*/ - /*acme is a string above*/ - me.rows = { - acme: { - defaultValue: '', - header: gettext('Domains'), - editor: 'PVE.node.ACMEEditor', - renderer: function(value) { - var acmeObj = PVE.Parser.parseACME(value); - if (acmeObj) { - return acmeObj.domains.join('
'); - } - return Proxmox.Utils.noneText; - } - } - }; - /*jslint confusion: false*/ - - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - me.load_account(); - } -}); -Ext.define('PVE.node.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.node.Config', - - onlineHelp: 'chapter_system_administration', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/status", - interval: 1000 - }); - - var node_command = function(cmd) { - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/status', - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var actionBtn = Ext.create('Ext.Button', { - text: gettext('Bulk Actions'), - iconCls: 'fa fa-fw fa-ellipsis-v', - disabled: !caps.nodes['Sys.PowerMgmt'], - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Bulk Start'), - iconCls: 'fa fa-fw fa-play', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - } - ] - }) - }); - - var restartBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Reboot'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Reboot node '{0}'?"), nodename), - handler: function() { - node_command('reboot'); - }, - iconCls: 'fa fa-undo' - }); - - var shutdownBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Shutdown'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Shutdown node '{0}'?"), nodename), - handler: function() { - node_command('shutdown'); - }, - iconCls: 'fa fa-power-off' - }); - - var shellBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.nodes['Sys.Console'], - text: gettext('Shell'), - consoleType: 'shell', - nodename: nodename - }); - - me.items = []; - - Ext.apply(me, { - title: gettext('Node') + " '" + nodename + "'", - hstateid: 'nodetab', - defaults: { statusStore: me.statusStore }, - tbar: [ restartBtn, shutdownBtn, shellBtn, actionBtn] - }); - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - itemId: 'summary', - xtype: 'pveNodeSummary' - }, - { - title: gettext('Notes'), - iconCls: 'fa fa-sticky-note-o', - itemId: 'notes', - xtype: 'pveNotesView' - } - ); - } - - if (caps.nodes['Sys.Console']) { - me.items.push( - { - title: gettext('Shell'), - iconCls: 'fa fa-terminal', - itemId: 'jsconsole', - xtype: 'pveNoVncConsole', - consoleType: 'shell', - xtermjs: true, - nodename: nodename - } - ); - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('System'), - iconCls: 'fa fa-cogs', - itemId: 'services', - expandedOnInit: true, - startOnlyServices: { - 'pveproxy': true, - 'pvedaemon': true, - 'pve-cluster': true - }, - nodename: nodename, - onlineHelp: 'pve_service_daemons', - xtype: 'proxmoxNodeServiceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - groups: ['services'], - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeNetworkView' - }, - { - title: gettext('Certificates'), - iconCls: 'fa fa-certificate', - itemId: 'certificates', - groups: ['services'], - nodename: nodename, - xtype: 'pveCertificatesView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'dns', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeDNSView' - }, - { - title: gettext('Hosts'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'hosts', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeHostsView' - }, - { - title: gettext('Time'), - itemId: 'time', - groups: ['services'], - nodename: nodename, - xtype: 'proxmoxNodeTimeView', - iconCls: 'fa fa-clock-o' - }); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push({ - title: 'Syslog', - iconCls: 'fa fa-list', - groups: ['services'], - disabled: !caps.nodes['Sys.Syslog'], - itemId: 'syslog', - xtype: 'proxmoxLogView', - url: "/api2/extjs/nodes/" + nodename + "/syslog", - log_select_timespan: 1 - }); - - if (caps.nodes['Sys.Modify']) { - me.items.push({ - title: gettext('Updates'), - iconCls: 'fa fa-refresh', - disabled: !caps.nodes['Sys.Console'], - // do we want to link to system updates instead? - itemId: 'apt', - xtype: 'proxmoxNodeAPT', - upgradeBtn: { - xtype: 'pveConsoleButton', - disabled: Proxmox.UserName !== 'root@pam', - text: gettext('Upgrade'), - consoleType: 'upgrade', - nodename: nodename - }, - nodename: nodename - }); - } - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - xtype: 'pveFirewallRules', - iconCls: 'fa fa-shield', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/nodes/' + nodename + '/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_host_specific_configuration', - groups: ['firewall'], - base_url: '/nodes/' + nodename + '/firewall/options', - fwtype: 'node', - itemId: 'firewall-options' - }); - } - - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Disks'), - itemId: 'storage', - expandedOnInit: true, - iconCls: 'fa fa-hdd-o', - xtype: 'pveNodeDiskList' - }, - { - title: 'LVM', - itemId: 'lvm', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square', - groups: ['storage'], - xtype: 'pveLVMList' - }, - { - title: 'LVM-Thin', - itemId: 'lvmthin', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square-o', - groups: ['storage'], - xtype: 'pveLVMThinList' - }, - { - title: Proxmox.Utils.directoryText, - itemId: 'directory', - onlineHelp: 'chapter_storage', - iconCls: 'fa fa-folder', - groups: ['storage'], - xtype: 'pveDirectoryList' - }, - { - title: 'ZFS', - itemId: 'zfs', - onlineHelp: 'chapter_zfs', - iconCls: 'fa fa-th-large', - groups: ['storage'], - xtype: 'pveZFSList' - }, - { - title: 'Ceph', - itemId: 'ceph', - iconCls: 'fa fa-ceph', - xtype: 'pveNodeCephStatus' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveNodeCephConfigCrush', - title: gettext('Configuration'), - iconCls: 'fa fa-gear', - groups: ['ceph'], - itemId: 'ceph-config' - }, - { - xtype: 'pveNodeCephMonList', - title: gettext('Monitor'), - iconCls: 'fa fa-tv', - groups: ['ceph'], - itemId: 'ceph-monlist' - }, - { - xtype: 'pveNodeCephOsdTree', - title: 'OSD', - iconCls: 'fa fa-hdd-o', - groups: ['ceph'], - itemId: 'ceph-osdtree' - }, - { - xtype: 'pveNodeCephFSPanel', - title: 'CephFS', - iconCls: 'fa fa-folder', - groups: ['ceph'], - nodename: nodename, - itemId: 'ceph-cephfspanel' - }, - { - xtype: 'pveNodeCephPoolList', - title: 'Pools', - iconCls: 'fa fa-sitemap', - groups: ['ceph'], - itemId: 'ceph-pools' - } - ); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push( - { - xtype: 'proxmoxLogView', - title: gettext('Log'), - iconCls: 'fa fa-list', - groups: ['firewall'], - onlineHelp: 'chapter_pve_firewall', - url: '/api2/extjs/nodes/' + nodename + '/firewall/log', - itemId: 'firewall-fwlog' - }, - { - title: gettext('Log'), - itemId: 'ceph-log', - iconCls: 'fa fa-list', - groups: ['ceph'], - onlineHelp: 'chapter_pveceph', - xtype: 'cephLogView', - url: "/api2/extjs/nodes/" + nodename + "/ceph/log", - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Task History'), - iconCls: 'fa fa-list', - itemId: 'tasks', - nodename: nodename, - xtype: 'proxmoxNodeTasks' - }, - { - title: gettext('Subscription'), - iconCls: 'fa fa-support', - itemId: 'support', - xtype: 'pveNodeSubscription', - nodename: nodename - } - ); - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var uptimerec = s.data.get('uptime'); - var powermgmt = uptimerec ? uptimerec.data.value : false; - if (!caps.nodes['Sys.PowerMgmt']) { - powermgmt = false; - } - restartBtn.setDisabled(!powermgmt); - shutdownBtn.setDisabled(!powermgmt); - shellBtn.setDisabled(!powermgmt); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -Ext.define('PVE.window.Migrate', { - extend: 'Ext.window.Window', - - config: { - vmtype: undefined, - nodename: undefined, - vmid: undefined - }, - // private, used to store the migration mode after checking if the guest runs - liveMode: undefined, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=formPanel]': { - validityChange: function(panel, isValid) { - this.lookup('submitButton').setDisabled(!isValid); - } - }, - 'button[reference=submitButton]': { - click: function() { - var me = this; - var view = me.getView(); - - var values = me.lookup('formPanel').getValues(); - var params = { - target: values.target - }; - - if (view.liveMode) { - params[view.liveMode] = 1; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + view.nodename + '/' + view.vmtype + '/' + view.vmid + '/migrate', - waitMsgTarget: view, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var extraTitle = Ext.String.format(' ({0} ---> {1})', view.nodename, params.target); - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - extraTitle: extraTitle - }).show(); - - view.close(); - } - }); - } - } - } - }, - - width: 350, - modal: true, - layout: 'auto', - border: false, - resizable: false, - items: [ - { - xtype: 'form', - reference: 'formPanel', - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - xtype: 'pveNodeSelector', - reference: 'pveNodeSelector', - name: 'target', - fieldLabel: gettext('Target node'), - allowBlank: false, - disallowedNodes: undefined, - onlineValidator: true - }, - { - xtype: 'displayfield', - reference: 'migrationMode', - fieldLabel: gettext('Mode'), - value: gettext('Offline') - } - ] - } - ], - buttons: [ - { - xtype: 'proxmoxHelpButton', - reference: 'proxmoxHelpButton', - onlineHelp: 'pct_migration', - listenToGlobalEvent: false, - hidden: false - }, - '->', - { - xtype: 'button', - reference: 'submitButton', - text: gettext('Migrate') - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.vmtype) { - throw "no VM type specified"; - } - - me.callParent(); - - var title = gettext('Migrate') + (' CT ') + me.vmid; - me.liveMode = 'restart'; - - if (me.vmtype === 'qemu') { - me.lookup('proxmoxHelpButton').setHelpConfig({ - onlineHelp: 'qm_migration' - }); - title = gettext('Migrate') + (' VM ') + me.vmid; - me.liveMode = 'online'; - } - - var running = false; - var vmrec = PVE.data.ResourceStore.findRecord('vmid', me.vmid, - 0, false, false, true); - if (vmrec && vmrec.data && vmrec.data.running) { - running = true; - } - - if (running) { - var displayField = me.lookup('migrationMode'); - if (me.vmtype === 'qemu') { - displayField.setValue(gettext('Online')); - me.liveMode = 'online'; - } else { - displayField.setValue(gettext('Restart Mode')); - me.liveMode = 'restart'; - } - } - - me.setTitle(title); - me.lookup('pveNodeSelector').disallowedNodes = [me.nodename]; - me.lookup('formPanel').isValid(); - } -});Ext.define('PVE.window.BulkAction', { - extend: 'Ext.window.Window', - - resizable: true, - width: 800, - modal: true, - layout: { - type: 'fit' - }, - border: false, - - // the action to be set - // currently there are - // startall - // migrateall - // stopall - action: undefined, - - submit: function(params) { - var me = this; - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.action, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.hide(); - win.on('destroy', function() { - me.close(); - }); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.action) { - throw "no action specified"; - } - - if (!me.btnText) { - throw "no button text specified"; - } - - if (!me.title) { - throw "no title specified"; - } - - var items = []; - - if (me.action === 'migrateall') { - /*jslint confusion: true*/ - /*value is string and number*/ - items.push( - { - xtype: 'pveNodeSelector', - name: 'target', - disallowedNodes: [me.nodename], - fieldLabel: gettext('Target node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'maxworkers', - minValue: 1, - maxValue: 100, - value: 1, - fieldLabel: gettext('Parallel jobs'), - allowBlank: false - }, - { - itemId: 'lxcwarning', - xtype: 'displayfield', - userCls: 'pve-hint', - value: 'Warning: Running CTs will be migrated in Restart Mode.', - hidden: true // only visible if running container chosen - } - ); - /*jslint confusion: false*/ - } else if (me.action === 'startall') { - items.push({ - xtype: 'hiddenfield', - name: 'force', - value: 1 - }); - } - - items.push({ - xtype: 'vmselector', - itemId: 'vms', - name: 'vms', - flex: 1, - height: 300, - selectAll: true, - allowBlank: false, - nodename: me.nodename, - action: me.action, - listeners: { - selectionchange: function(vmselector, records) { - if (me.action == 'migrateall') { - var showWarning = records.some(function(item) { - return (item.data.type == 'lxc' && - item.data.status == 'running'); - }); - me.down('#lxcwarning').setVisible(showWarning); - } - } - } - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - layout: { - type: 'vbox', - align: 'stretch' - }, - fieldDefaults: { - labelWidth: 300, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: me.btnText, - handler: function() { - form.isValid(); - me.submit(form.getValues()); - } - }); - - Ext.apply(me, { - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - - form.on('validitychange', function() { - var valid = form.isValid(); - submitBtn.setDisabled(!valid); - }); - form.isValid(); - } -}); -Ext.define('PVE.window.Clone', { - extend: 'Ext.window.Window', - - resizable: false, - - isTemplate: false, - - onlineHelp: 'qm_copy_and_clone', - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=cloneform]': { - validitychange: 'disableSubmit' - } - }, - disableSubmit: function(form) { - this.lookupReference('submitBtn').setDisabled(!form.isValid()); - } - }, - - statics: { - // display a snapshot selector only if needed - wrap: function(nodename, vmid, isTemplate, guestType) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid +'/snapshot', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var snapshotList = response.result.data; - var hasSnapshots = snapshotList.length === 1 && - snapshotList[0].name === 'current' ? false : true; - - Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: isTemplate, - hasSnapshots: hasSnapshots - }).show(); - } - }); - } - }, - - create_clone: function(values) { - var me = this; - - var params = { newid: values.newvmid }; - - if (values.snapname && values.snapname !== 'current') { - params.snapname = values.snapname; - } - - if (values.pool) { - params.pool = values.pool; - } - - if (values.name) { - if (me.guestType === 'lxc') { - params.hostname = values.name; - } else { - params.name = values.name; - } - } - - if (values.target) { - params.target = values.target; - } - - if (values.clonemode === 'copy') { - params.full = 1; - if (values.hdstorage) { - params.storage = values.hdstorage; - if (values.diskformat && me.guestType !== 'lxc') { - params.format = values.diskformat; - } - } - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/clone', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - - }, - - // disable the Storage selector when clone mode is linked clone - updateVisibility: function() { - var me = this; - var clonemode = me.lookupReference('clonemodesel').getValue(); - var disksel = me.lookup('diskselector'); - disksel.setDisabled(clonemode === 'clone'); - }, - - // add to the list of valid nodes each node where - // all the VM disks are available - verifyFeature: function() { - var me = this; - - var snapname = me.lookupReference('snapshotsel').getValue(); - var clonemode = me.lookupReference('clonemodesel').getValue(); - - var params = { feature: clonemode }; - if (snapname !== 'current') { - params.snapname = snapname; - } - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/feature', - params: params, - method: 'GET', - failure: function(response, opts) { - me.lookupReference('submitBtn').setDisabled(true); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var res = response.result.data; - - me.lookupReference('targetsel').allowedNodes = res.nodes; - me.lookupReference('targetsel').validate(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.snapname) { - me.snapname = 'current'; - } - - if (!me.guestType) { - throw "no Guest Type specified"; - } - - var titletext = me.guestType === 'lxc' ? 'CT' : 'VM'; - if (me.isTemplate) { - titletext += ' Template'; - } - me.title = "Clone " + titletext + " " + me.vmid; - - var col1 = []; - var col2 = []; - - col1.push({ - xtype: 'pveNodeSelector', - name: 'target', - reference: 'targetsel', - fieldLabel: gettext('Target node'), - selectCurNode: true, - allowBlank: false, - onlineValidator: true, - listeners: { - change: function(f, value) { - me.lookupReference('hdstorage').setTargetNode(value); - } - } - }); - - var modelist = [['copy', gettext('Full Clone')]]; - if (me.isTemplate) { - modelist.push(['clone', gettext('Linked Clone')]); - } - - col1.push({ - xtype: 'pveGuestIDSelector', - name: 'newvmid', - guestType: me.guestType, - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - allowBlank: true, - fieldLabel: me.guestType === 'lxc' ? gettext('Hostname') : gettext('Name') - }, - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ); - - col2.push({ - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Mode'), - name: 'clonemode', - reference: 'clonemodesel', - allowBlank: false, - hidden: !me.isTemplate, - value: me.isTemplate ? 'clone' : 'copy', - comboItems: modelist, - listeners: { - change: function(t, value) { - me.updateVisibility(); - me.verifyFeature(); - } - } - }, - { - xtype: 'PVE.form.SnapshotSelector', - name: 'snapname', - reference: 'snapshotsel', - fieldLabel: gettext('Snapshot'), - nodename: me.nodename, - guestType: me.guestType, - vmid: me.vmid, - hidden: me.isTemplate || !me.hasSnapshots ? true : false, - disabled: false, - allowBlank: false, - value : me.snapname, - listeners: { - change: function(f, value) { - me.verifyFeature(); - } - } - }, - { - xtype: 'pveDiskStorageSelector', - reference: 'diskselector', - nodename: me.nodename, - autoSelect: false, - hideSize: true, - hideSelection: true, - storageLabel: gettext('Target Storage'), - allowBlank: true, - storageContent: me.guestType === 'qemu' ? 'images' : 'rootdir', - emptyText: gettext('Same as source'), - disabled: me.isTemplate ? true : false // because default mode is clone for templates - }); - - var formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - reference: 'cloneform', - border: false, - layout: 'column', - defaultType: 'container', - columns: 2, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: col1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: col2 - } - ] - }); - - Ext.apply(me, { - modal: true, - width: 600, - height: 250, - border: false, - layout: 'fit', - buttons: [ { - xtype: 'proxmoxHelpButton', - listenToGlobalEvent: false, - hidden: false, - onlineHelp: me.onlineHelp - }, - '->', - { - reference: 'submitBtn', - text: gettext('Clone'), - disabled: true, - handler: function() { - var cloneForm = me.lookupReference('cloneform'); - if (cloneForm.isValid()) { - me.create_clone(cloneForm.getValues()); - } - } - } ], - items: [ formPanel ] - }); - - me.callParent(); - - me.verifyFeature(); - } -}); -Ext.define('PVE.qemu.Monitor', { - extend: 'Ext.panel.Panel', - - alias: 'widget.pveQemuMonitor', - - maxLines: 500, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var history = []; - var histNum = -1; - var lines = []; - - var textbox = Ext.createWidget('panel', { - region: 'center', - xtype: 'panel', - autoScroll: true, - border: true, - margins: '5 5 5 5', - bodyStyle: 'font-family: monospace;' - }); - - var scrollToEnd = function() { - var el = textbox.getTargetEl(); - var dom = Ext.getDom(el); - - var clientHeight = dom.clientHeight; - // BrowserBug: clientHeight reports 0 in IE9 StrictMode - // Instead we are using offsetHeight and hardcoding borders - if (Ext.isIE9 && Ext.isStrict) { - clientHeight = dom.offsetHeight + 2; - } - dom.scrollTop = dom.scrollHeight - clientHeight; - }; - - var refresh = function() { - textbox.update('
' + lines.join('\n') + '
'); - scrollToEnd(); - }; - - var addLine = function(line) { - lines.push(line); - if (lines.length > me.maxLines) { - lines.shift(); - } - }; - - var executeCmd = function(cmd) { - addLine("# " + Ext.htmlEncode(cmd)); - if (cmd) { - history.unshift(cmd); - if (history.length > 20) { - history.splice(20); - } - } - histNum = -1; - - refresh(); - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/monitor", - method: 'POST', - waitMsgTarget: me, - success: function(response, opts) { - var res = response.result.data; - Ext.Array.each(res.split('\n'), function(line) { - addLine(Ext.htmlEncode(line)); - }); - refresh(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - textbox, - { - region: 'south', - margins:'0 5 5 5', - border: false, - xtype: 'textfield', - name: 'cmd', - value: '', - fieldStyle: 'font-family: monospace;', - allowBlank: true, - listeners: { - afterrender: function(f) { - f.focus(false); - addLine("Type 'help' for help."); - refresh(); - }, - specialkey: function(f, e) { - var key = e.getKey(); - switch (key) { - case e.ENTER: - var cmd = f.getValue(); - f.setValue(''); - executeCmd(cmd); - break; - case e.PAGE_UP: - textbox.scrollBy(0, -0.9*textbox.getHeight(), false); - break; - case e.PAGE_DOWN: - textbox.scrollBy(0, 0.9*textbox.getHeight(), false); - break; - case e.UP: - if (histNum + 1 < history.length) { - f.setValue(history[++histNum]); - } - e.preventDefault(); - break; - case e.DOWN: - if (histNum > 0) { - f.setValue(history[--histNum]); - } - e.preventDefault(); - break; - default: - break; - } - } - } - } - ], - listeners: { - show: function() { - var field = me.query('textfield[name="cmd"]')[0]; - field.focus(false, true); - } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.qemu.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveQemuSummary', - - scrollable: true, - bodyPadding: 5, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - if (!me.workspace) { - throw "no workspace specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var template = !!me.pveSelNode.data.template; - var rstore = me.statusStore; - - var width = template ? 1 : 0.5; - var items = [ - { - xtype: template ? 'pveTemplateStatusView' : 'pveGuestStatusView', - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - }, - itemId: 'gueststatus', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'pveNotesView', - maxHeight: 330, - itemId: 'notesview', - pveSelNode: me.pveSelNode, - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - } - } - ]; - - var rrdstore; - if (!template) { - - rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/qemu/" + vmid + "/rrddata", - model: 'pve-rrd-guest' - }); - - items.push( - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - pveSelNode: me.pveSelNode, - fields: ['cpu'], - fieldTitles: [gettext('CPU usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - pveSelNode: me.pveSelNode, - fields: ['maxmem', 'mem'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - pveSelNode: me.pveSelNode, - fields: ['netin','netout'], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Disk IO'), - pveSelNode: me.pveSelNode, - fields: ['diskread','diskwrite'], - store: rrdstore - } - ); - - } - - Ext.apply(me, { - tbar: [ '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: { - type: 'column' - }, - defaults: { - minHeight: 330, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: items - } - ] - }); - - me.callParent(); - if (!template) { - rrdstore.startUpdate(); - me.on('destroy', rrdstore.stopUpdate); - } - } -}); -Ext.define('PVE.qemu.OSTypeInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuOSTypePanel', - onlineHelp: 'qm_os_settings', - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'combobox[name=osbase]': { - change: 'onOSBaseChange' - }, - 'combobox[name=ostype]': { - afterrender: 'onOSTypeChange', - change: 'onOSTypeChange' - } - }, - onOSBaseChange: function(field, value) { - this.lookup('ostype').getStore().setData(PVE.Utils.kvm_ostypes[value]); - }, - onOSTypeChange: function(field) { - var me = this, ostype = field.getValue(); - if (!me.getView().insideWizard) { - return; - } - var targetValues = PVE.qemu.OSDefaults.getDefaults(ostype); - - me.setWidget('pveBusSelector', targetValues.busType); - me.setWidget('pveNetworkCardSelector', targetValues.networkCard); - var scsihw = targetValues.scsihw || '__default__'; - this.getViewModel().set('current.scsihw', scsihw); - }, - setWidget: function(widget, newValue) { - // changing a widget is safe only if ComponentQuery.query returns us - // a single value array - var widgets = Ext.ComponentQuery.query('pveQemuCreateWizard ' + widget); - if (widgets.length === 1) { - widgets[0].setValue(newValue); - } else { - throw 'non unique widget :' + widget + ' in Wizard'; - } - } - }, - - initComponent : function() { - var me = this; - - /*jslint confusion: true */ - me.items = [ - { - xtype: 'displayfield', - value: gettext('Guest OS') + ':', - hidden: !me.insideWizard - }, - { - xtype: 'combobox', - submitValue: false, - name: 'osbase', - fieldLabel: gettext('Type'), - editable: false, - queryMode: 'local', - value: 'Linux', - store: Object.keys(PVE.Utils.kvm_ostypes) - }, - { - xtype: 'combobox', - name: 'ostype', - reference: 'ostype', - fieldLabel: gettext('Version'), - value: 'l26', - allowBlank : false, - editable: false, - queryMode: 'local', - valueField: 'val', - displayField: 'desc', - store: { - fields: ['desc', 'val'], - data: PVE.Utils.kvm_ostypes.Linux, - listeners: { - datachanged: function (store) { - var ostype = me.lookup('ostype'); - var old_val = ostype.getValue(); - if (!me.insideWizard && old_val && store.find('val', old_val) != -1) { - ostype.setValue(old_val); - } else { - ostype.setValue(store.getAt(0)); - } - } - } - } - } - ]; - /*jslint confusion: false */ - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.OSTypeEdit', { - extend: 'Proxmox.window.Edit', - - subject: 'OS Type', - - items: [{ xtype: 'pveQemuOSTypePanel' }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response, options) { - var value = response.result.data.ostype || 'other'; - var osinfo = PVE.Utils.get_kvm_osinfo(value); - me.setValues({ ostype: value, osbase: osinfo.base }); - } - }); - } -}); -/* - * This class holds performance *recommended* settings for the PVE Qemu wizards - * the *mandatory* settings are set in the PVE::QemuServer - * config_to_command sub - * We store this here until we get the data from the API server -*/ - -// this is how you would add an hypothetic FreeBSD > 10 entry -// -//virtio-blk is stable but virtIO net still -// problematic as of 10.3 -// see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059 -// addOS({ -// parent: 'generic', // inherits defaults -// pveOS: 'freebsd10', // must match a radiofield in OSTypeEdit.js -// busType: 'virtio' // must match a pveBusController value -// // networkCard muss match a pveNetworkCardSelector - - -Ext.define('PVE.qemu.OSDefaults', { - singleton: true, // will also force creation when loaded - - constructor: function() { - var me = this; - - var addOS = function(settings) { - if (me.hasOwnProperty(settings.parent)) { - var child = Ext.clone(me[settings.parent]); - me[settings.pveOS] = Ext.apply(child, settings); - - } else { - throw("Could not find your genitor"); - } - }; - - // default values - me.generic = { - busType: 'ide', - networkCard: 'e1000', - busPriority: { - ide: 4, - sata: 3, - scsi: 2, - virtio: 1 - }, - scsihw: 'virtio-scsi-pci' - }; - - // virtio-net is in kernel since 2.6.25 - // virtio-scsi since 3.2 but backported in RHEL with 2.6 kernel - addOS({ - pveOS: 'l26', - parent : 'generic', - busType: 'scsi', - busPriority: { - scsi: 4, - virtio: 3, - sata: 2, - ide: 1 - }, - networkCard: 'virtio' - }); - - // recommandation from http://wiki.qemu.org/Windows2000 - addOS({ - pveOS: 'w2k', - parent : 'generic', - networkCard: 'rtl8139', - scsihw: '' - }); - // https://pve.proxmox.com/wiki/Windows_XP_Guest_Notes - addOS({ - pveOS: 'wxp', - parent : 'w2k' - }); - - me.getDefaults = function(ostype) { - if (PVE.qemu.OSDefaults[ostype]) { - return PVE.qemu.OSDefaults[ostype]; - } else { - return PVE.qemu.OSDefaults.generic; - } - }; - } -}); -Ext.define('PVE.qemu.ProcessorInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuProcessorPanel', - onlineHelp: 'qm_cpu', - - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - - updateCores: function() { - var me = this.getView(); - var sockets = me.down('field[name=sockets]').getValue(); - var cores = me.down('field[name=cores]').getValue(); - me.down('field[name=totalcores]').setValue(sockets*cores); - var vcpus = me.down('field[name=vcpus]'); - vcpus.setMaxValue(sockets*cores); - vcpus.setEmptyText(sockets*cores); - vcpus.validate(); - }, - - control: { - 'field[name=sockets]': { - change: 'updateCores' - }, - 'field[name=cores]': { - change: 'updateCores' - } - } - }, - - onGetValues: function(values) { - var me = this; - - if (Array.isArray(values['delete'])) { - values['delete'] = values['delete'].join(','); - } - - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - // build the cpu options: - me.cpu.cputype = values.cputype; - - var flags = []; - - ['pcid', 'spec-ctrl'].forEach(function(flag) { - if (values[flag]) { - flags.push('+' + flag.toString()); - } - delete values[flag]; - }); - - me.cpu.flags = flags.length ? flags.join(';') : undefined; - - delete values.cputype; - delete values.flags; - var cpustring = PVE.Parser.printQemuCpu(me.cpu); - - // remove cputype delete request: - var del = values['delete']; - delete values['delete']; - if (del) { - del = del.split(','); - Ext.Array.remove(del, 'cputype'); - } else { - del = []; - } - - if (cpustring) { - values.cpu = cpustring; - } else { - del.push('cpu'); - } - - var delarr = del.join(','); - if (delarr) { - values['delete'] = delarr; - } - - return values; - }, - - cpu: {}, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'sockets', - minValue: 1, - maxValue: 4, - value: '1', - fieldLabel: gettext('Sockets'), - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: '1', - fieldLabel: gettext('Cores'), - allowBlank: false - } - ], - - column2: [ - { - xtype: 'CPUModelSelector', - name: 'cputype', - value: '__default__', - fieldLabel: gettext('Type') - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Total cores'), - name: 'totalcores', - value: '1' - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxintegerfield', - name: 'vcpus', - minValue: 1, - maxValue: 1, - value: '', - fieldLabel: gettext('VCPUs'), - deleteEmpty: true, - allowBlank: true, - emptyText: '1' - }, - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - maxValue: 128, // api maximum - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - minValue: 8, - maxValue: 500000, - value: '1024', - deleteEmpty: true, - allowBlank: true - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable NUMA'), - name: 'numa', - uncheckedValue: 0 - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'PCID', - name: 'pcid', - uncheckedValue: 0 - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'SPEC-CTRL', - name: 'spec-ctrl', - uncheckedValue: 0 - } - ] -}); - -Ext.define('PVE.qemu.ProcessorEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.ProcessorInputPanel'); - - Ext.apply(me, { - subject: gettext('Processors'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - var value = data.cpu; - if (value) { - var cpu = PVE.Parser.parseQemuCpu(value); - ipanel.cpu = cpu; - data.cputype = cpu.cputype; - if (cpu.flags) { - var flags = cpu.flags.split(';'); - flags.forEach(function(flag) { - var sign = flag.substr(0,1); - flag = flag.substr(1); - data[flag] = (sign === '+'); - }); - } - } - me.setValues(data); - } - }); - } -}); -Ext.define('PVE.qemu.BootOrderPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuBootOrderPanel', - vmconfig: {}, // store loaded vm config - - bootdisk: undefined, - selection: [], - list: [], - comboboxes: [], - - isBootDisk: function(value) { - return PVE.Utils.bus_match.test(value); - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - var order = me.vmconfig.boot || 'cdn'; - me.bootdisk = me.vmconfig.bootdisk || undefined; - - // get the first 3 characters - // ignore the rest (there should never be more than 3) - me.selection = order.split('').slice(0,3); - - // build bootdev list - me.list = []; - Ext.Object.each(me.vmconfig, function(key, value) { - if (me.isBootDisk(key) && - !(/media=cdrom/).test(value)) { - me.list.push([key, "Disk '" + key + "'"]); - } - }); - - me.list.push(['d', 'CD-ROM']); - me.list.push(['n', gettext('Network')]); - me.list.push(['__none__', Proxmox.Utils.noneText]); - - me.recomputeList(); - - me.comboboxes.forEach(function(box) { - box.resetOriginalValue(); - }); - }, - - onGetValues: function(values) { - var me = this; - var order = me.selection.join(''); - var res = { boot: order }; - - if (me.bootdisk && order.indexOf('c') !== -1) { - res.bootdisk = me.bootdisk; - } else { - res['delete'] = 'bootdisk'; - } - - return res; - }, - - recomputeSelection: function(combobox, newVal, oldVal) { - var me = this.up('#inputpanel'); - me.selection = []; - me.comboboxes.forEach(function(item) { - var val = item.getValue(); - - // when selecting an already selected item, - // switch it around - if ((val === newVal || (me.isBootDisk(val) && me.isBootDisk(newVal))) && - item.name !== combobox.name && - newVal !== '__none__') { - // swap items - val = oldVal; - } - - // push 'c','d' or 'n' in the array - if (me.isBootDisk(val)) { - me.selection.push('c'); - me.bootdisk = val; - } else if (val === 'd' || - val === 'n') { - me.selection.push(val); - } - }); - - me.recomputeList(); - }, - - recomputeList: function(){ - var me = this; - // set the correct values in the kvcomboboxes - var cnt = 0; - me.comboboxes.forEach(function(item) { - if (cnt === 0) { - // never show 'none' on first combobox - item.store.loadData(me.list.slice(0, me.list.length-1)); - } else { - item.store.loadData(me.list); - } - item.suspendEvent('change'); - if (cnt < me.selection.length) { - item.setValue((me.selection[cnt] !== 'c')?me.selection[cnt]:me.bootdisk); - } else if (cnt === 0){ - item.setValue(''); - } else { - item.setValue('__none__'); - } - cnt++; - item.resumeEvent('change'); - item.validate(); - }); - }, - - initComponent : function() { - var me = this; - - // this has to be done here, because of - // the way our inputPanel class handles items - me.comboboxes = [ - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 1", - labelWidth: 120, - name: 'bd1', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 2", - labelWidth: 120, - name: 'bd2', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 3", - labelWidth: 120, - name: 'bd3', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }) - ]; - Ext.apply(me, { items: me.comboboxes }); - me.callParent(); - } -}); - -Ext.define('PVE.qemu.BootOrderEdit', { - extend: 'Proxmox.window.Edit', - - items: [{ - xtype: 'pveQemuBootOrderPanel', - itemId: 'inputpanel' - }], - - subject: gettext('Boot Order'), - - initComponent : function() { - var me = this; - me.callParent(); - me.load({ - success: function(response, options) { - me.down('#inputpanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuMemoryPanel', - onlineHelp: 'qm_memory', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var res = {}; - - res.memory = values.memory; - res.balloon = values.balloon; - - if (!values.ballooning) { - res.balloon = 0; - res['delete'] = 'shares'; - } else if (values.memory === values.balloon) { - delete res.balloon; - res['delete'] = 'balloon,shares'; - } else if (Ext.isDefined(values.shares) && (values.shares !== "")) { - res.shares = values.shares; - } else { - res['delete'] = "shares"; - } - - return res; - }, - - initComponent: function() { - var me = this; - var labelWidth = 160; - - me.items= [ - { - xtype: 'pveMemoryField', - labelWidth: labelWidth, - fieldLabel: gettext('Memory') + ' (MiB)', - name: 'memory', - minValue: 1, - step: 32, - hotplug: me.hotplug, - listeners: { - change: function(f, value, old) { - var bf = me.down('field[name=balloon]'); - var balloon = bf.getValue(); - bf.setMaxValue(value); - if (balloon === old) { - bf.setValue(value); - } - bf.validate(); - } - } - } - ]; - - me.advancedItems= [ - { - xtype: 'pveMemoryField', - name: 'balloon', - minValue: 1, - step: 32, - fieldLabel: gettext('Minimum memory') + ' (MiB)', - hotplug: me.hotplug, - labelWidth: labelWidth, - allowBlank: false, - listeners: { - change: function(f, value) { - var memory = me.down('field[name=memory]').getValue(); - var shares = me.down('field[name=shares]'); - shares.setDisabled(value === memory); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'shares', - disabled: true, - minValue: 0, - maxValue: 50000, - value: '', - step: 10, - fieldLabel: gettext('Shares'), - labelWidth: labelWidth, - allowBlank: true, - emptyText: Proxmox.Utils.defaultText + ' (1000)', - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - labelWidth: labelWidth, - value: '1', - name: 'ballooning', - fieldLabel: gettext('Ballooning Device'), - listeners: { - change: function(f, value) { - var bf = me.down('field[name=balloon]'); - var shares = me.down('field[name=shares]'); - var memory = me.down('field[name=memory]'); - bf.setDisabled(!value); - shares.setDisabled(!value || (bf.getValue() === memory.getValue())); - } - } - } - ]; - - if (me.insideWizard) { - me.column1 = me.items; - me.items = undefined; - me.advancedColumn1 = me.advancedItems; - me.advancedItems = undefined; - } - me.callParent(); - } - -}); - -Ext.define('PVE.qemu.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent: function() { - var me = this; - - var memoryhotplug; - if(me.hotplug) { - Ext.each(me.hotplug.split(','), function(el) { - if (el === 'memory') { - memoryhotplug = 1; - } - }); - } - - var ipanel = Ext.create('PVE.qemu.MemoryInputPanel', { - hotplug: memoryhotplug - }); - - Ext.apply(me, { - subject: gettext('Memory'), - items: [ ipanel ], - // uncomment the following to use the async configiguration API - // backgroundDelay: 5, - width: 400 - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - - var values = { - ballooning: data.balloon === 0 ? '0' : '1', - shares: data.shares, - memory: data.memory || '512', - balloon: data.balloon > 0 ? data.balloon : (data.memory || '512') - }; - - ipanel.setValues(values); - } - }); - } -}); -Ext.define('PVE.qemu.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuNetworkInputPanel', - onlineHelp: 'qm_network_device', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - me.network.model = values.model; - if (values.nonetwork) { - return {}; - } else { - me.network.bridge = values.bridge; - me.network.tag = values.tag; - me.network.firewall = values.firewall; - } - me.network.macaddr = values.macaddr; - me.network.disconnect = values.disconnect; - me.network.queues = values.queues; - - if (values.rate) { - me.network.rate = values.rate; - } else { - delete me.network.rate; - } - - var params = {}; - - params[me.confid] = PVE.Parser.printQemuNetwork(me.network); - - return params; - }, - - setNetwork: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data) { - data.networkmode = data.bridge ? 'bridge' : 'nat'; - } else { - data = {}; - data.networkmode = 'bridge'; - } - me.network = data; - - me.setValues(me.network); - }, - - setNodename: function(nodename) { - var me = this; - - me.bridgesel.setNodename(nodename); - }, - - initComponent : function() { - var me = this; - - me.network = {}; - me.confid = 'net0'; - - me.column1 = []; - me.column2 = []; - - me.bridgesel = Ext.create('PVE.form.BridgeSelector', { - name: 'bridge', - fieldLabel: gettext('Bridge'), - nodename: me.nodename, - autoSelect: true, - allowBlank: false - }); - - me.column1 = [ - me.bridgesel, - { - xtype: 'pveVlanField', - name: 'tag', - value: '' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - checked: (me.insideWizard || me.isCreate) - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Disconnect'), - name: 'disconnect' - } - ]; - - if (me.insideWizard) { - me.column1.unshift({ - xtype: 'checkbox', - name: 'nonetwork', - inputValue: 'none', - boxLabel: gettext('No network device'), - listeners: { - change: function(cb, value) { - var fields = [ - 'disconnect', - 'bridge', - 'tag', - 'firewall', - 'model', - 'macaddr', - 'rate', - 'queues' - ]; - fields.forEach(function(fieldname) { - me.down('field[name='+fieldname+']').setDisabled(value); - }); - me.down('field[name=bridge]').validate(); - } - } - }); - me.column2.unshift({ - xtype: 'displayfield' - }); - } - - me.column2.push( - { - xtype: 'pveNetworkCardSelector', - name: 'model', - fieldLabel: gettext('Model'), - value: PVE.qemu.OSDefaults.generic.networkCard, - allowBlank: false - }, - { - xtype: 'textfield', - name: 'macaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - allowBlank: true, - emptyText: 'auto' - }); - me.advancedColumn2 = [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: '', - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'queues', - fieldLabel: 'Multiqueue', - minValue: 1, - maxValue: 8, - value: '', - allowBlank: true - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.NetworkInputPanel', { - confid: me.confid, - nodename: nodename, - isCreate: me.isCreate - }); - - Ext.applyIf(me, { - subject: gettext('Network Device'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - if (!me.isCreate) { - var value = me.vmconfig[me.confid]; - var network = PVE.Parser.parseQemuNetwork(me.confid, value); - if (!network) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse network options'); - me.close(); - return; - } - ipanel.setNetwork(me.confid, network); - } else { - for (i = 0; i < 100; i++) { - confid = 'net' + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - me.confid = confid; - break; - } - } - ipanel.setNetwork(me.confid); - } - } - }); - } -}); -Ext.define('PVE.qemu.Smbios1InputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.PVE.qemu.Smbios1InputPanel', - - insideWizard: false, - - smbios1: {}, - - onGetValues: function(values) { - var me = this; - - var params = { - smbios1: PVE.Parser.printQemuSmbios1(values) - }; - - return params; - }, - - setSmbios1: function(data) { - var me = this; - - me.smbios1 = data; - - me.setValues(me.smbios1); - }, - - initComponent : function() { - var me = this; - - - me.items = [ - { - xtype: 'textfield', - fieldLabel: 'UUID', - regex: /^[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$/, - name: 'uuid' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Manufacturer'), - regex: /^\S+$/, - name: 'manufacturer' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Product'), - regex: /^\S+$/, - name: 'product' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Version'), - regex: /^\S+$/, - name: 'version' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Serial'), - regex: /^\S+$/, - name: 'serial' - }, - { - xtype: 'textfield', - fieldLabel: 'SKU', - regex: /^\S+$/, - name: 'sku' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Family'), - regex: /^\S+$/, - name: 'family' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.Smbios1Edit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var ipanel = Ext.create('PVE.qemu.Smbios1InputPanel', {}); - - Ext.applyIf(me, { - subject: gettext('SMBIOS settings (type1)'), - width: 450, - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - var value = me.vmconfig.smbios1; - if (value) { - var data = PVE.Parser.parseQemuSmbios1(value); - if (!data) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse smbios options'); - me.close(); - return; - } - ipanel.setSmbios1(data); - } - } - }); - } -}); -Ext.define('PVE.qemu.CDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuCDInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || (values.controller + values.deviceid); - - me.drive.media = 'cdrom'; - if (values.mediaType === 'iso') { - me.drive.file = values.cdimage; - } else if (values.mediaType === 'cdrom') { - me.drive.file = 'cdrom'; - } else { - me.drive.file = 'none'; - } - - var params = {}; - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig, 'cdrom'); - } - }, - - setDrive: function(drive) { - var me = this; - - var values = {}; - if (drive.file === 'cdrom') { - values.mediaType = 'cdrom'; - } else if (drive.file === 'none') { - values.mediaType = 'none'; - } else { - values.mediaType = 'iso'; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.cdstorage = match[1]; - values.cdimage = drive.file; - } - } - - me.drive = drive; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - - me.cdstoragesel.setNodename(nodename); - me.cdfilesel.setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - var items = []; - - if (!me.confid) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - noVirtIO: true - }); - items.push(me.bussel); - } - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'iso', - boxLabel: gettext('Use CD/DVD disc image file (iso)'), - checked: true, - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=cdstorage]').setDisabled(!value); - me.down('field[name=cdimage]').setDisabled(!value); - me.down('field[name=cdimage]').validate(); - } - } - }); - - me.cdfilesel = Ext.create('PVE.form.FileSelector', { - name: 'cdimage', - nodename: me.nodename, - storageContent: 'iso', - fieldLabel: gettext('ISO image'), - labelAlign: 'right', - allowBlank: false - }); - - me.cdstoragesel = Ext.create('PVE.form.StorageSelector', { - name: 'cdstorage', - nodename: me.nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'iso', - allowBlank: false, - autoSelect: me.insideWizard, - listeners: { - change: function(f, value) { - me.cdfilesel.setStorage(value); - } - } - }); - - items.push(me.cdstoragesel); - items.push(me.cdfilesel); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'cdrom', - boxLabel: gettext('Use physical CD/DVD Drive') - }); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'none', - boxLabel: gettext('Do not use any media') - }); - - me.items = items; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CDEdit', { - extend: 'Proxmox.window.Edit', - - width: 400, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.CDInputPanel', { - confid: me.confid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: 'CD/DVD Drive', - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert('Error', 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - } - } - }); - } -}); -/*jslint confusion: true */ -/* 'change' property is assigned a string and then a function */ -Ext.define('PVE.qemu.HDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuHDInputPanel', - onlineHelp: 'qm_hard_disk', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - controller: { - - xclass: 'Ext.app.ViewController', - - onControllerChange: function(field) { - var value = field.getValue(); - - var allowIOthread = value.match(/^(virtio|scsi)/); - this.lookup('iothread').setDisabled(!allowIOthread); - if (!allowIOthread) { - this.lookup('iothread').setValue(false); - } - - var virtio = value.match(/^virtio/); - this.lookup('discard').setDisabled(virtio); - this.lookup('ssd').setDisabled(virtio); - if (virtio) { - this.lookup('discard').setValue(false); - this.lookup('ssd').setValue(false); - } - - this.lookup('scsiController').setVisible(value.match(/^scsi/)); - }, - - control: { - 'field[name=controller]': { - change: 'onControllerChange', - afterrender: 'onControllerChange' - }, - 'field[name=iothread]' : { - change: function(f, value) { - if (!this.getView().insideWizard) { - return; - } - var vmScsiType = value ? 'virtio-scsi-single': 'virtio-scsi-pci'; - this.lookupReference('scsiController').setValue(vmScsiType); - } - } - } - }, - - onGetValues: function(values) { - var me = this; - - var params = {}; - var confid = me.confid || (values.controller + values.deviceid); - - if (me.unused) { - me.drive.file = me.vmconfig[values.unusedId]; - confid = values.controller + values.deviceid; - } else if (me.isCreate) { - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - me.drive.file = values.hdstorage + ":" + values.disksize; - } - me.drive.format = values.diskformat; - } - - if (values.nobackup) { - me.drive.backup = 'no'; - } else { - delete me.drive.backup; - } - - if (values.noreplicate) { - me.drive.replicate = 'no'; - } else { - delete me.drive.replicate; - } - - if (values.discard) { - me.drive.discard = 'on'; - } else { - delete me.drive.discard; - } - - if (values.ssd) { - me.drive.ssd = 'on'; - } else { - delete me.drive.ssd; - } - - if (values.iothread) { - me.drive.iothread = 'on'; - } else { - delete me.drive.iothread; - } - - if (values.cache) { - me.drive.cache = values.cache; - } else { - delete me.drive.cache; - } - - var names = ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr']; - Ext.Array.each(names, function(name) { - if (values[name]) { - me.drive[name] = values[name]; - } else { - delete me.drive[name]; - } - var burst_name = name + '_max'; - if (values[burst_name] && values[name]) { - me.drive[burst_name] = values[burst_name]; - } else { - delete me.drive[burst_name]; - } - }); - - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - me.vmconfig = vmconfig; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig); - me.scsiController.setValue(vmconfig.scsihw); - } - if (me.unusedDisks) { - var disklist = []; - Ext.Object.each(vmconfig, function(key, value) { - if (key.match(/^unused\d+$/)) { - disklist.push([key, value]); - } - }); - me.unusedDisks.store.loadData(disklist); - me.unusedDisks.setValue(me.confid); - } - }, - - setDrive: function(drive) { - var me = this; - - me.drive = drive; - - var values = {}; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.hdstorage = match[1]; - } - - values.hdimage = drive.file; - values.nobackup = !PVE.Parser.parseBoolean(drive.backup, 1); - values.noreplicate = !PVE.Parser.parseBoolean(drive.replicate, 1); - values.diskformat = drive.format || 'raw'; - values.cache = drive.cache || '__default__'; - values.discard = (drive.discard === 'on'); - values.ssd = PVE.Parser.parseBoolean(drive.ssd); - values.iothread = PVE.Parser.parseBoolean(drive.iothread); - - values.mbps_rd = drive.mbps_rd; - values.mbps_wr = drive.mbps_wr; - values.iops_rd = drive.iops_rd; - values.iops_wr = drive.iops_wr; - values.mbps_rd_max = drive.mbps_rd_max; - values.mbps_wr_max = drive.mbps_wr_max; - values.iops_rd_max = drive.iops_rd_max; - values.iops_wr_max = drive.iops_wr_max; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - var labelWidth = 140; - - me.drive = {}; - - me.column1 = []; - me.column2 = []; - - me.advancedColumn1 = []; - me.advancedColumn2 = []; - - if (!me.confid || me.unused) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - vmconfig: me.insideWizard ? {ide2: 'cdrom'} : {} - }); - me.column1.push(me.bussel); - - me.scsiController = Ext.create('Ext.form.field.Display', { - fieldLabel: gettext('SCSI Controller'), - reference: 'scsiController', - bind: me.insideWizard ? { - value: '{current.scsihw}' - } : undefined, - renderer: PVE.Utils.render_scsihw, - submitValue: false, - hidden: true - }); - me.column1.push(me.scsiController); - } - - if (me.unused) { - me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', { - name: 'unusedId', - fieldLabel: gettext('Disk image'), - matchFieldWidth: false, - listConfig: { - width: 350 - }, - data: [], - allowBlank: false - }); - me.column1.push(me.unusedDisks); - } else if (me.isCreate) { - me.column1.push({ - xtype: 'pveDiskStorageSelector', - storageContent: 'images', - name: 'disk', - nodename: me.nodename, - autoSelect: me.insideWizard - }); - } else { - me.column1.push({ - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'hdimage' - }); - } - - me.column2.push( - { - xtype: 'CacheTypeSelector', - name: 'cache', - value: '__default__', - fieldLabel: gettext('Cache') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Discard'), - disabled: me.confid && me.confid.match(/^virtio/), - reference: 'discard', - name: 'discard' - } - ); - - me.advancedColumn1.push( - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && me.confid.match(/^virtio/), - fieldLabel: gettext('SSD emulation'), - labelWidth: labelWidth, - name: 'ssd', - reference: 'ssd' - }, - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && !me.confid.match(/^(virtio|scsi)/), - fieldLabel: 'IO thread', - labelWidth: labelWidth, - reference: 'iothread', - name: 'iothread' - }, - { - xtype: 'numberfield', - name: 'mbps_rd', - minValue: 1, - step: 1, - fieldLabel: gettext('Read limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'numberfield', - name: 'mbps_wr', - minValue: 1, - step: 1, - fieldLabel: gettext('Write limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd', - minValue: 10, - step: 10, - fieldLabel: gettext('Read limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr', - minValue: 10, - step: 10, - fieldLabel: gettext('Write limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - } - ); - - me.advancedColumn2.push( - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('No backup'), - labelWidth: labelWidth, - name: 'nobackup' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Skip replication'), - labelWidth: labelWidth, - name: 'noreplicate' - }, - { - xtype: 'numberfield', - name: 'mbps_rd_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Read max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'numberfield', - name: 'mbps_wr_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Write max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Read max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Write max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - } - ); - - me.callParent(); - } -}); -/*jslint confusion: false */ - -Ext.define('PVE.qemu.HDEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - backgroundDelay: 5, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.qemu.HDInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - me.subject = gettext('Unused Disk'); - } else if (me.isCreate) { - me.subject = gettext('Hard Disk'); - } else { - me.subject = gettext('Hard Disk') + ' (' + me.confid + ')'; - } - - me.items = [ ipanel ]; - - me.callParent(); - /*jslint confusion: true*/ - /* 'data' is assigned an empty array in same file, and here we - * use it like an object - */ - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - me.isValid(); // trigger validation - } - } - }); - /*jslint confusion: false*/ - } -}); -Ext.define('PVE.window.HDResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 140, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 250, - height: 150, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -Ext.define('PVE.window.HDMove', { - extend: 'Ext.window.Window', - - resizable: false, - - - move_disk: function(disk, storage, format, delete_disk) { - var me = this; - var qemu = (me.type === 'qemu'); - var params = {}; - params.storage = storage; - params[qemu ? 'disk':'volume'] = disk; - - if (format && qemu) { - params.format = format; - } - - if (delete_disk) { - params['delete'] = 1; - } - - var url = '/nodes/' + me.nodename + '/' + me.type + '/' + me.vmid + '/'; - url += qemu ? 'move_disk' : 'move_volume'; - - Proxmox.Utils.API2Request({ - params: params, - url: url, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - win.on('destroy', function() { me.close(); }); - } - }); - - }, - - initComponent : function() { - var me = this; - - var diskarray = []; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.type) { - me.type = 'qemu'; - } - - var qemu = (me.type === 'qemu'); - - var items = [ - { - xtype: 'displayfield', - name: qemu ? 'disk' : 'volume', - value: me.disk, - fieldLabel: qemu ? gettext('Disk') : gettext('Mount Point'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - items.push({ - xtype: 'pveDiskStorageSelector', - storageLabel: gettext('Target Storage'), - nodename: me.nodename, - storageContent: qemu ? 'images' : 'rootdir', - hideSize: true - }); - - items.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Delete source'), - name: 'deleteDisk', - uncheckedValue: 0, - checked: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = qemu ? gettext("Move disk") : gettext('Move Volume'); - submitBtn = Ext.create('Ext.Button', { - text: me.title, - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.move_disk(me.disk, values.hdstorage, values.diskformat, - values.deleteDisk); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 350, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - me.mon(me.formPanel, 'validitychange', function(fp, isValid) { - submitBtn.setDisabled(!isValid); - }); - - me.formPanel.isValid(); - } -}); -Ext.define('PVE.qemu.EFIDiskInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveEFIDiskInputPanel', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var confid = 'efidisk0'; - - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - // we use 1 here, because for efi the size gets overridden from the backend - me.drive.file = values.hdstorage + ":1"; - } - - me.drive.format = values.diskformat; - var params = {}; - params[confid] = PVE.Parser.printQemuDrive(me.drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items= []; - - me.items.push({ - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - }); - me.callParent(); - } -}); - -Ext.define('PVE.qemu.EFIDiskEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - subject: gettext('EFI Disk'), - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveEFIDiskInputPanel', - onlineHelp: 'qm_bios_and_uefi', - confid: me.confid, - nodename: nodename, - isCreate: true - }]; - - me.callParent(); - } -}); -Ext.define('PVE.qemu.DisplayInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveDisplayInputPanel', - - onGetValues: function(values) { - var ret = PVE.Parser.printPropertyString(values, 'type'); - if (ret === '') { - return { - 'delete': 'vga' - }; - } - return { - vga: ret - }; - }, - - items: [{ - name: 'type', - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - comboItems: PVE.Utils.kvm_vga_driver_array(), - validator: function() { - var v = this.getValue(); - var cfg = this.up('proxmoxWindowEdit').vmconfig || {}; - - if (v.match(/^serial\d+$/) && (!cfg[v] || cfg[v] !== 'socket')) { - var fmt = gettext("Serial interface '{0}' is not correctly configured."); - return Ext.String.format(fmt, v); - } - return true; - }, - listeners: { - change: function(cb, val) { - var me = this.up('panel'); - if (!val) { - return; - } - var disable = false; - var emptyText = Proxmox.Utils.defaultText; - switch (val) { - case "cirrus": - emptyText = "4"; - break; - case "std": - emptyText = "16"; - break; - case "qxl": - case "qxl2": - case "qxl3": - case "qxl4": - emptyText = "16"; - break; - case "vmware": - emptyText = "16"; - break; - case "none": - case "serial0": - case "serial1": - case "serial2": - case "serial3": - emptyText = 'N/A'; - disable = true; - break; - case "virtio": - emptyText = "256"; - break; - default: - break; - } - var memoryfield = me.down('field[name=memory]'); - memoryfield.setEmptyText(emptyText); - memoryfield.setDisabled(disable); - } - } - },{ - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Memory') + ' (MiB)', - minValue: 4, - maxValue: 512, - step: 4, - name: 'memory' - }] -}); - -Ext.define('PVE.qemu.DisplayEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - subject: gettext('Display'), - width: 350, - - items: [{ - xtype: 'pveDisplayInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response) { - me.vmconfig = response.result.data; - var vga = me.vmconfig.vga || '__default__'; - me.setValues(PVE.Parser.parsePropertyString(vga, 'type')); - } - }); - } -}); -Ext.define('PVE.qemu.KeyboardEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('Keyboard Layout'), - items: { - xtype: 'VNCKeyboardSelector', - name: 'keyboard', - value: '__default__', - fieldLabel: gettext('Keyboard Layout') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.HardwareView', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.HardwareView'], - - onlineHelp: 'qm_virtual_machines_settings', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - var iconCls = rowdef.iconCls; - var icon = ''; - var txt = (rowdef.header || key); - - metaData.tdAttr = "valign=middle"; - - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - if (rowdef.tdCls == 'pve-itype-icon-storage') { - var value = me.getObjectValue(key, '', false); - if (value === '') { - value = me.getObjectValue(key, '', true); - } - if (value.match(/vm-.*-cloudinit/)) { - metaData.tdCls = 'pve-itype-icon-cloud'; - return rowdef.cloudheader; - } else if (value.match(/media=cdrom/)) { - metaData.tdCls = 'pve-itype-icon-cdrom'; - return rowdef.cdheader; - } - } - } else if (iconCls) { - icon = ""; - metaData.tdCls += " pve-itype-fa"; - } - return icon + txt; - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - /*jslint confusion: true */ - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined, - never_delete: true, - defaultValue: '512', - tdCls: 'pve-itype-icon-memory', - group: 2, - multiKey: ['memory', 'balloon', 'shares'], - renderer: function(value, metaData, record, ri, ci, store, pending) { - var res = ''; - - var max = me.getObjectValue('memory', 512, pending); - var balloon = me.getObjectValue('balloon', undefined, pending); - var shares = me.getObjectValue('shares', undefined, pending); - - res = Proxmox.Utils.format_size(max*1024*1024); - - if (balloon !== undefined && balloon > 0) { - res = Proxmox.Utils.format_size(balloon*1024*1024) + "/" + res; - - if (shares) { - res += ' [shares=' + shares +']'; - } - } else if (balloon === 0) { - res += ' [balloon=0]'; - } - return res; - } - }, - sockets: { - header: gettext('Processors'), - never_delete: true, - editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ? - 'PVE.qemu.ProcessorEdit' : undefined, - tdCls: 'pve-itype-icon-processor', - group: 3, - defaultValue: '1', - multiKey: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'], - renderer: function(value, metaData, record, rowIndex, colIndex, store, pending) { - - var sockets = me.getObjectValue('sockets', 1, pending); - var model = me.getObjectValue('cpu', undefined, pending); - var cores = me.getObjectValue('cores', 1, pending); - var numa = me.getObjectValue('numa', undefined, pending); - var vcpus = me.getObjectValue('vcpus', undefined, pending); - var cpulimit = me.getObjectValue('cpulimit', undefined, pending); - var cpuunits = me.getObjectValue('cpuunits', undefined, pending); - - var res = Ext.String.format('{0} ({1} sockets, {2} cores)', - sockets*cores, sockets, cores); - - if (model) { - res += ' [' + model + ']'; - } - - if (numa) { - res += ' [numa=' + numa +']'; - } - - if (vcpus) { - res += ' [vcpus=' + vcpus +']'; - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit +']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits +']'; - } - - return res; - } - }, - bios: { - header: 'BIOS', - group: 4, - never_delete: true, - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.BiosEdit' : undefined, - defaultValue: '', - iconCls: 'microchip', - renderer: PVE.Utils.render_qemu_bios - }, - vga: { - header: gettext('Display'), - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined, - never_delete: true, - tdCls: 'pve-itype-icon-display', - group:5, - defaultValue: '', - renderer: PVE.Utils.render_kvm_vga_driver - }, - machine: { - header: gettext('Machine'), - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Machine'), - width: 350, - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - }]} : undefined, - iconCls: 'cogs', - never_delete: true, - group: 6, - defaultValue: '', - renderer: PVE.Utils.render_qemu_machine - }, - scsihw: { - header: gettext('SCSI Controller'), - iconCls: 'database', - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.ScsiHwEdit' : undefined, - renderer: PVE.Utils.render_scsihw, - group: 7, - never_delete: true, - defaultValue: '' - }, - cores: { - visible: false - }, - cpu: { - visible: false - }, - numa: { - visible: false - }, - balloon: { - visible: false - }, - hotplug: { - visible: false - }, - vcpus: { - visible: false - }, - cpuunits: { - visible: false - }, - cpulimit: { - visible: false - }, - shares: { - visible: false - } - }; - /*jslint confusion: false */ - - PVE.Utils.forEachBus(undefined, function(type, id) { - var confid = type + id; - rows[confid] = { - group: 10, - tdCls: 'pve-itype-icon-storage', - editor: 'PVE.qemu.HDEdit', - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - header: gettext('Hard Disk') + ' (' + confid +')', - cdheader: gettext('CD/DVD Drive') + ' (' + confid +')', - cloudheader: gettext('CloudInit Drive') + ' (' + confid + ')' - }; - }); - for (i = 0; i < 32; i++) { - confid = "net" + i.toString(); - rows[confid] = { - group: 15, - order: i, - tdCls: 'pve-itype-icon-network', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined, - never_delete: caps.vms['VM.Config.Network'] ? false : true, - header: gettext('Network Device') + ' (' + confid +')' - }; - } - rows.efidisk0 = { - group: 20, - tdCls: 'pve-itype-icon-storage', - editor: null, - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - header: gettext('EFI Disk') - }; - for (i = 0; i < 5; i++) { - confid = "usb" + i.toString(); - rows[confid] = { - group: 25, - order: i, - tdCls: 'pve-itype-icon-usb', - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined, - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('USB Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < 4; i++) { - confid = "hostpci" + i.toString(); - rows[confid] = { - group: 30, - order: i, - tdCls: 'pve-itype-icon-pci', - never_delete: caps.nodes['Sys.Console'] ? false : true, - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.PCIEdit' : undefined, - header: gettext('PCI Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < 4; i++) { - confid = "serial" + i.toString(); - rows[confid] = { - group: 35, - order: i, - tdCls: 'pve-itype-icon-serial', - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('Serial Port') + ' (' + confid + ')' - }; - } - for (i = 0; i < 256; i++) { - rows["unused" + i.toString()] = { - group: 99, - order: i, - tdCls: 'pve-itype-icon-storage', - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined, - header: gettext('Unused Disk') + ' ' + i.toString() - }; - } - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - var reload = function() { - me.rstore.load(); - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var editor = rowdef.editor; - if (rowdef.tdCls == 'pve-itype-icon-storage') { - if (!diskCap) { - return; - } - var value = me.getObjectValue(rec.data.key, '', true); - if (value.match(/vm-.*-cloudinit/)) { - return; - } else if (value.match(/media=cdrom/)) { - editor = 'PVE.qemu.CDEdit'; - } - } - - var win; - - if (Ext.isString(editor)) { - win = Ext.create(editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', reload); - }; - - var run_resize = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', reload); - }; - - var run_move = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', reload); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - handler: run_editor - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: sm, - disabled: true, - handler: run_resize - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move disk'), - selModel: sm, - disabled: true, - handler: run_move - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - defaultText: gettext('Remove'), - altText: gettext('Detach'), - selModel: sm, - disabled: true, - dangerous: true, - RESTMethod: 'PUT', - confirmMsg: function(rec) { - var warn = gettext('Are you sure you want to remove entry {0}'); - if (this.text === this.altText) { - warn = gettext('Are you sure you want to detach entry {0}'); - } - - var entry = rec.data.key; - var msg = Ext.String.format(warn, "'" - + me.renderKey(entry, {}, rec) + "'"); - - if (entry.match(/^unused\d+$/)) { - msg += " " + gettext('This will permanently erase all data.'); - } - - return msg; - }, - handler: function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: b.RESTMethod, - params: { - 'delete': rec.data.key - }, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - if (b.RESTMethod === 'POST') { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: function () { - me.reload(); - } - } - }); - win.show(); - } - } - }); - }, - listeners: { - render: function(btn) { - // hack: calculate an optimal button width on first display - // to prevent the whole toolbar to move when we switch - // between the "Remove" and "Detach" labels - var def = btn.getSize().width; - - btn.setText(btn.altText); - var alt = btn.getSize().width; - - btn.setText(btn.defaultText); - - var optimal = alt > def ? alt : def; - btn.setSize({ width: optimal }); - } - } - }); - - var revert_btn = new Proxmox.button.Button({ - text: gettext('Revert'), - selModel: sm, - disabled: true, - handler: function(b, e, rec) { - var rowdef = me.rows[rec.data.key] || {}; - var keys = rowdef.multiKey || [ rec.data.key ]; - var revert = keys.join(','); - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'revert': revert - }, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - } - }); - } - }); - - var efidisk_menuitem = Ext.create('Ext.menu.Item',{ - text: gettext('EFI Disk'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - - var rstoredata = me.rstore.getData().map; - // check if ovmf is configured - if (rstoredata.bios && rstoredata.bios.data.value === 'ovmf') { - var win = Ext.create('PVE.qemu.EFIDiskEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } else { - Ext.Msg.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.')); - } - - } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - // disable button when we have an efidisk already - // disable is ok in this case, because you can instantly - // see that there is already one - efidisk_menuitem.setDisabled(me.rstore.getData().map.efidisk0 !== undefined); - // en/disable usb add button - var usbcount = 0; - var pcicount = 0; - var hasCloudInit = false; - me.rstore.getData().items.forEach(function(item){ - if (/^usb\d+/.test(item.id)) { - usbcount++; - } else if (/^hostpci\d+/.test(item.id)) { - pcicount++; - } - if (!hasCloudInit && /vm-.*-cloudinit/.test(item.data.value)) { - hasCloudInit = true; - } - }); - - // heuristic only for disabling some stuff, the backend has the final word. - var noSysConsolePerm = !caps.nodes['Sys.Console']; - - me.down('#addusb').setDisabled(noSysConsolePerm || (usbcount >= 5)); - me.down('#addpci').setDisabled(noSysConsolePerm || (pcicount >= 4)); - me.down('#addci').setDisabled(noSysConsolePerm || hasCloudInit); - - if (!rec) { - remove_btn.disable(); - edit_btn.disable(); - resize_btn.disable(); - move_btn.disable(); - revert_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var isUnusedDisk = key.match(/^unused\d+/); - var isUsedDisk = !isUnusedDisk && - rowdef.tdCls == 'pve-itype-icon-storage' && - (value && !value.match(/media=cdrom/)); - - var isCloudInit = (value && value.toString().match(/vm-.*-cloudinit/)); - - var isEfi = (key === 'efidisk0'); - - remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete === true) || (isUnusedDisk && !diskCap)); - remove_btn.setText((isUsedDisk && !isCloudInit) ? remove_btn.altText : remove_btn.defaultText); - remove_btn.RESTMethod = isUnusedDisk ? 'POST':'PUT'; - - edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor || isCloudInit || !diskCap); - - resize_btn.setDisabled(pending || !isUsedDisk || !diskCap); - - move_btn.setDisabled(pending || !isUsedDisk || !diskCap); - - revert_btn.setDisabled(!pending); - - }; - - Ext.apply(me, { - url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + '/pending', - interval: 5000, - selModel: sm, - run_editor: run_editor, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Hard Disk'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.HDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('CD/DVD Drive'), - iconCls: 'pve-itype-icon-cdrom', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.CDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Network Device'), - iconCls: 'pve-itype-icon-network', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.qemu.NetworkEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode, - isCreate: true - }); - win.on('destroy', reload); - win.show(); - } - }, - efidisk_menuitem, - { - text: gettext('USB Device'), - itemId: 'addusb', - iconCls: 'pve-itype-icon-usb', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.USBEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('PCI Device'), - itemId: 'addpci', - iconCls: 'pve-itype-icon-pci', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.PCIEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Serial Port'), - itemId: 'addserial', - iconCls: 'pve-itype-icon-serial', - disabled: !caps.vms['VM.Config.Options'], - handler: function() { - var win = Ext.create('PVE.qemu.SerialEdit', { - url: '/api2/extjs/' + baseurl - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('CloudInit Drive'), - itemId: 'addci', - iconCls: 'pve-itype-icon-cloud', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.CIDriveEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - remove_btn, - edit_btn, - resize_btn, - move_btn, - revert_btn - ], - rows: rows, - sorterFn: sorterFn, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - me.mon(me.rstore, 'refresh', function() { - set_button_status(); - }); - } -}); -Ext.define('PVE.qemu.ScsiHwEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('SCSI Controller Type'), - items: { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - fieldLabel: gettext('Type') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.BiosEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveQemuBiosEdit', - - initComponent : function() { - var me = this; - - var EFIHint = Ext.createWidget({ - xtype: 'displayfield', //submitValue is false, so we don't get submitted - userCls: 'pve-hint', - value: 'You need to add an EFI disk for storing the ' + - 'EFI settings. See the online help for details.', - hidden: true - }); - - Ext.applyIf(me, { - subject: 'BIOS', - items: [ { - xtype: 'pveQemuBiosSelector', - onlineHelp: 'qm_bios_and_uefi', - name: 'bios', - value: '__default__', - fieldLabel: 'BIOS', - listeners: { - 'change' : function(field, newValue) { - if (newValue == 'ovmf') { - Proxmox.Utils.API2Request({ - url : me.url, - method : 'GET', - failure : function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success : function(response, opts) { - var vmConfig = response.result.data; - // there can be only one - if (!vmConfig.efidisk0) { - EFIHint.setVisible(true); - } - } - }); - } else { - if (EFIHint.isVisible()) { - EFIHint.setVisible(false); - } - } - } - } - }, - EFIHint - ] }); - - me.callParent(); - - me.load(); - - } -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.Options', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.Options'], - - onlineHelp: 'qm_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - name: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Name'), - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Name'), - items: { - xtype: 'inputpanel', - items:{ - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - }, - onGetValues: function(values) { - var params = values; - if (values.name === undefined || - values.name === null || - values.name === '') { - params = { 'delete':'name'}; - } - return params; - } - } - } : undefined - }, - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'qm_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.OSTypeEdit' : undefined, - renderer: PVE.Utils.render_kvm_ostype, - defaultValue: 'other' - }, - bootdisk: { - visible: false - }, - boot: { - header: gettext('Boot Order'), - defaultValue: 'cdn', - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.BootOrderEdit' : undefined, - multiKey: ['boot', 'bootdisk'], - renderer: function(order, metaData, record, rowIndex, colIndex, store, pending) { - var i; - var text = ''; - var bootdisk = me.getObjectValue('bootdisk', undefined, pending); - order = order || 'cdn'; - for (i = 0; i < order.length; i++) { - var sel = order.substring(i, i + 1); - if (text) { - text += ', '; - } - if (sel === 'c') { - if (bootdisk) { - text += "Disk '" + bootdisk + "'"; - } else { - text += "Disk"; - } - } else if (sel === 'n') { - text += 'Network'; - } else if (sel === 'a') { - text += 'Floppy'; - } else if (sel === 'd') { - text += 'CD-ROM'; - } else { - text += sel; - } - } - return text; - } - }, - tablet: { - header: gettext('Use tablet for pointer'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use tablet for pointer'), - items: { - xtype: 'proxmoxcheckbox', - name: 'tablet', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - hotplug: { - header: gettext('Hotplug'), - defaultValue: 'disk,network,usb', - renderer: PVE.Utils.render_hotplug_features, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hotplug'), - items: { - xtype: 'pveHotplugFeatureSelector', - name: 'hotplug', - value: '', - multiSelect: true, - fieldLabel: gettext('Hotplug'), - allowBlank: true - } - } : undefined - }, - acpi: { - header: gettext('ACPI support'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('ACPI support'), - items: { - xtype: 'proxmoxcheckbox', - name: 'acpi', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - kvm: { - header: gettext('KVM hardware virtualization'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('KVM hardware virtualization'), - items: { - xtype: 'proxmoxcheckbox', - name: 'kvm', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - freeze: { - header: gettext('Freeze CPU at startup'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.PowerMgmt'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Freeze CPU at startup'), - items: { - xtype: 'proxmoxcheckbox', - name: 'freeze', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Freeze CPU at startup') - } - } : undefined - }, - localtime: { - header: gettext('Use local time for RTC'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use local time for RTC'), - items: { - xtype: 'proxmoxcheckbox', - name: 'localtime', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Use local time for RTC') - } - } : undefined - }, - startdate: { - header: gettext('RTC start date'), - defaultValue: 'now', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('RTC start date'), - items: { - xtype: 'proxmoxtextfield', - name: 'startdate', - deleteEmpty: true, - value: 'now', - fieldLabel: gettext('RTC start date'), - vtype: 'QemuStartDate', - allowBlank: true - } - } : undefined - }, - smbios1: { - header: gettext('SMBIOS settings (type1)'), - defaultValue: '', - renderer: Ext.String.htmlEncode, - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.Smbios1Edit' : undefined - }, - agent: { - header: gettext('Qemu Agent'), - defaultValue: false, - renderer: PVE.Utils.render_qga_features, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Qemu Agent'), - items: { - xtype: 'pveAgentFeatureSelector', - name: 'agent' - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var revert_btn = new Proxmox.button.Button({ - text: gettext('Revert'), - disabled: true, - handler: function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = me.rows[rec.data.key] || {}; - var keys = rowdef.multiKey || [ rec.data.key ]; - var revert = keys.join(','); - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'revert': revert - }, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - } - }); - } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - - var key = rec.data.key; - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var rowdef = rows[key]; - - edit_btn.setDisabled(!rowdef.editor); - revert_btn.setDisabled(!pending); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/qemu/" + vmid + "/pending", - interval: 5000, - cwidth1: 250, - tbar: [ edit_btn, revert_btn ], - rows: rows, - editorConfig: { - url: "/api2/extjs/" + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - me.rstore.on('datachanged', function() { - set_button_status(); - }); - } -}); - -Ext.define('PVE.window.Snapshot', { - extend: 'Ext.window.Window', - - resizable: false, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - defaultFocus: 'field', - - take_snapshot: function(snapname, descr, vmstate) { - var me = this; - var params = { snapname: snapname, vmstate: vmstate ? 1 : 0 }; - if (descr) { - params.description = descr; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot", - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - update_snapshot: function(snapname, descr) { - var me = this; - Proxmox.Utils.API2Request({ - params: { description: descr }, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot/" + - snapname + '/config', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var summarystore = Ext.create('Ext.data.Store', { - model: 'KeyValue', - sorters: [ - { - property : 'key', - direction: 'ASC' - } - ] - }); - - var items = [ - { - xtype: me.snapname ? 'displayfield' : 'textfield', - name: 'snapname', - value: me.snapname, - fieldLabel: gettext('Name'), - vtype: 'ConfigId', - allowBlank: false - } - ]; - - if (me.snapname) { - items.push({ - xtype: 'displayfield', - name: 'snaptime', - renderer: PVE.Utils.render_timestamp_human_readable, - fieldLabel: gettext('Timestamp') - }); - } else { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'vmstate', - uncheckedValue: 0, - defaultValue: 0, - checked: 1, - fieldLabel: gettext('Include RAM') - }); - } - - items.push({ - xtype: 'textareafield', - grow: true, - name: 'description', - fieldLabel: gettext('Description') - }); - - if (me.snapname) { - items.push({ - title: gettext('Settings'), - xtype: 'grid', - height: 200, - store: summarystore, - columns: [ - {header: gettext('Key'), width: 150, dataIndex: 'key'}, - {header: gettext('Value'), flex: 1, dataIndex: 'value'} - ] - }); - } - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - if (me.snapname) { - me.title = gettext('Edit') + ': ' + gettext('Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Update'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.update_snapshot(me.snapname, values.description); - } - } - }); - } else { - me.title ="VM " + me.vmid + ': ' + gettext('Take Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Take Snapshot'), - reference: 'submitbutton', - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.take_snapshot(values.snapname, values.description, values.vmstate); - } - } - }); - } - - Ext.apply(me, { - modal: true, - width: 450, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - if (me.snapname) { - Ext.apply(me, { - width: 620, - height: 420 - }); - } - - me.callParent(); - - if (!me.snapname) { - return; - } - - // else load data - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot/" + - me.snapname + '/config', - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.close(); - }, - success: function(response, options) { - var data = response.result.data; - var kvarray = []; - Ext.Object.each(data, function(key, value) { - if (key === 'description' || key === 'snaptime') { - return; - } - kvarray.push({ key: key, value: value }); - }); - - summarystore.suspendEvents(); - summarystore.add(kvarray); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh', summarystore); - - form.findField('snaptime').setValue(data.snaptime); - form.findField('description').setValue(data.description); - } - }); - } -}); -Ext.define('PVE.qemu.SnapshotTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveQemuSnapshotTree'], - - load_delay: 3000, - - old_digest: 'invalid', - - stateful: true, - stateId: 'grid-qemu-snapshots', - - sorterFn: function(rec1, rec2) { - var v1 = rec1.data.snaptime; - var v2 = rec2.data.snaptime; - - if (rec1.data.name === 'current') { - return 1; - } - if (rec2.data.name === 'current') { - return -1; - } - - return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); - }, - - reload: function(repeat) { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot', - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - me.load_task.delay(me.load_delay); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var digest = 'invalid'; - var idhash = {}; - var root = { name: '__root', expanded: true, children: [] }; - Ext.Array.each(response.result.data, function(item) { - item.leaf = true; - item.children = []; - if (item.name === 'current') { - digest = item.digest + item.running; - if (item.running) { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running'; - } else { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree'; - } - } else { - item.iconCls = 'fa fa-fw fa-history x-fa-tree'; - } - idhash[item.name] = item; - }); - - if (digest !== me.old_digest) { - me.old_digest = digest; - - Ext.Array.each(response.result.data, function(item) { - if (item.parent && idhash[item.parent]) { - var parent_item = idhash[item.parent]; - parent_item.children.push(item); - parent_item.leaf = false; - parent_item.expanded = true; - parent_item.expandable = false; - } else { - root.children.push(item); - } - }); - - me.setRootNode(root); - } - - me.load_task.delay(me.load_delay); - } - }); - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/feature', - params: { feature: 'snapshot' }, - method: 'GET', - success: function(response, options) { - var res = response.result.data; - if (res.hasFeature) { - var snpBtns = Ext.ComponentQuery.query('#snapshotBtn'); - snpBtns.forEach(function(item){ - item.enable(); - }); - } - } - }); - - - }, - - listeners: { - beforestatesave: function(grid, state, eopts) { - // extjs cannot serialize functions, - // so a the sorter with only the sorterFn will - // not be a valid sorter when restoring the state - delete state.storeState.sorters; - } - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.vmid = me.pveSelNode.data.vmid; - if (!me.vmid) { - throw "no VM ID specified"; - } - - me.load_task = new Ext.util.DelayedTask(me.reload, me); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var valid_snapshot = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current'; - }; - - var valid_snapshot_rollback = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current' && !record.data.snapstate; - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (valid_snapshot(rec)) { - var win = Ext.create('PVE.window.Snapshot', { - snapname: rec.data.name, - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - me.mon(win, 'close', me.reload, me); - } - }; - - var editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot, - handler: run_editor - }); - - var rollbackBtn = new Proxmox.button.Button({ - text: gettext('Rollback'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot_rollback, - confirmMsg: function(rec) { - return Proxmox.Utils.format_task_description('qmrollback', me.vmid) + - " '" + rec.data.name + "'"; - }, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname + '/rollback', - method: 'POST', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var removeBtn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.name + "'"); - return msg; - }, - enableFn: valid_snapshot, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var snapshotBtn = Ext.create('Ext.Button', { - itemId: 'snapshotBtn', - text: gettext('Take Snapshot'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.window.Snapshot', { - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - } - }); - - Ext.apply(me, { - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - selModel: sm, - tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ], - fields: [ - 'name', 'description', 'snapstate', 'vmstate', 'running', - { name: 'snaptime', type: 'date', dateFormat: 'timestamp' } - ], - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - width: 200, - renderer: function(value, metaData, record) { - if (value === 'current') { - return "NOW"; - } else { - return value; - } - } - }, - { - text: gettext('RAM'), - align: 'center', - resizable: false, - dataIndex: 'vmstate', - width: 50, - renderer: function(value, metaData, record) { - if (record.data.name !== 'current') { - return Proxmox.Utils.format_boolean(value); - } - } - }, - { - text: gettext('Date') + "/" + gettext("Status"), - dataIndex: 'snaptime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.snapstate) { - return record.data.snapstate; - } - if (value) { - return Ext.Date.format(value,'Y-m-d H:i:s'); - } - } - }, - { - text: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value, metaData, record) { - if (record.data.name === 'current') { - return gettext("You are here!"); - } else { - return Ext.String.htmlEncode(value); - } - } - } - ], - columnLines: true, // will work in 4.1? - listeners: { - activate: me.reload, - destroy: me.load_task.cancel, - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.store.sorters.add(new Ext.util.Sorter({ - sorterFn: me.sorterFn - })); - } -}); - -Ext.define('PVE.qemu.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.qemu.Config', - - onlineHelp: 'chapter_virtual_machines', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!me.pveSelNode.data.template; - - var running = !!me.pveSelNode.data.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + "/qemu/" + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + '/status/' + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var resumeBtn = Ext.create('Ext.Button', { - text: gettext('Resume'), - disabled: !caps.vms['VM.PowerMgmt'], - hidden: true, - handler: function() { - vm_command('resume'); - }, - iconCls: 'fa fa-play' - }); - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('qmtemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = me.pveSelNode.data.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - itemId: 'removeBtn', - disabled: !caps.vms['VM.Allocate'], - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'VM', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('qmshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items: [{ - text: gettext('Pause'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmpause', vmid), - handler: function() { - vm_command("suspend"); - }, - iconCls: 'fa fa-pause' - },{ - text: gettext('Hibernate'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmsuspend', vmid), - tooltip: gettext('Suspend to disk'), - handler: function() { - vm_command("suspend", { todisk: 1 }); - }, - iconCls: 'fa fa-download' - },{ - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - dangerous: true, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - confirmMsg: Proxmox.Utils.format_task_description('qmstop', vmid), - handler: function() { - vm_command("stop", { timeout: 30 }); - }, - iconCls: 'fa fa-stop' - },{ - text: gettext('Reset'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmreset', vmid), - handler: function() { - vm_command("reset"); - }, - iconCls: 'fa fa-bolt' - }] - }, - iconCls: 'fa fa-power-off' - }); - - var vm = me.pveSelNode.data; - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - hidden: template, - consoleType: 'kvm', - consoleName: vm.name, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - Ext.apply(me, { - title: Ext.String.format(gettext("Virtual Machine {0} on node '{1}'"), vm.text, nodename), - hstateid: 'kvmtab', - tbarSpacing: false, - tbar: [ statusTxt, '->', resumeBtn, startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveQemuSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push({ - title: gettext('Console'), - itemId: 'console', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'kvm', - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Hardware'), - itemId: 'hardware', - iconCls: 'fa fa-desktop', - xtype: 'PVE.qemu.HardwareView' - }, - { - title: 'Cloud-Init', - itemId: 'cloudinit', - iconCls: 'fa fa-cloud', - xtype: 'pveCiPanel' - }, - { - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options', - xtype: 'PVE.qemu.Options' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - xtype: 'proxmoxNodeTasks', - iconCls: 'fa fa-list', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Monitor'] && !template) { - me.items.push({ - title: gettext('Monitor'), - iconCls: 'fa fa-eye', - itemId: 'monitor', - xtype: 'pveQemuMonitor' - }); - } - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - xtype: 'pveQemuSnapshotTree', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var qmpstatus; - var spice = false; - var xtermjs = false; - var lock; - - if (!success) { - status = qmpstatus = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('qmpstatus'); - qmpstatus = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - - spice = s.data.get('spice') ? true : false; - xtermjs = s.data.get('serial') ? true : false; - - } - - if (template) { - return; - } - - var resume = (['prelaunch', 'paused', 'suspended'].indexOf(qmpstatus) !== -1); - - if (resume || lock === 'suspended') { - startBtn.setVisible(false); - resumeBtn.setVisible(true); - } else { - startBtn.setVisible(true); - resumeBtn.setVisible(false); - } - - consoleBtn.setEnableSpice(spice); - consoleBtn.setEnableXtermJS(xtermjs); - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.CreateWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveQemuCreateWizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - current: { - scsihw: '' - } - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('Virtual Machine'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'qm_general_settings', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', - guestType: 'qemu', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ], - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - ], - advancedColumn2: [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - labelWidth: 120, - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Shutdown timeout') - } - ], - onGetValues: function(values) { - - ['name', 'pool', 'onboot', 'agent'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - - var res = PVE.Parser.printStartup({ - order: values.order, - up: values.up, - down: values.down - }); - - if (res) { - values.startup = res; - } - - delete values.order; - delete values.up; - delete values.down; - - return values; - } - }, - { - xtype: 'container', - layout: 'hbox', - defaults: { - flex: 1, - padding: '0 10' - }, - title: gettext('OS'), - items: [ - { - xtype: 'pveQemuCDInputPanel', - bind: { - nodename: '{nodename}' - }, - confid: 'ide2', - insideWizard: true - }, - { - xtype: 'pveQemuOSTypePanel', - insideWizard: true - } - ] - }, - { - xtype: 'pveQemuSystemPanel', - title: gettext('System'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuHDInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Hard Disk'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuProcessorPanel', - insideWizard: true, - title: gettext('CPU') - }, - { - xtype: 'pveQemuMemoryPanel', - insideWizard: true, - title: gettext('Memory') - }, - { - xtype: 'pveQemuNetworkInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Network'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var kv = this.up('window').getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete') { // ignore - return; - } - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response){ - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - - -Ext.define('PVE.qemu.USBInputPanel', { - extend: 'Proxmox.panel.InputPanel', - mixins: ['Proxmox.Mixin.CBind' ], - - autoComplete: false, - onlineHelp: 'qm_usb_passthrough', - - controller: { - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=usb]': { - change: function(field, newValue, oldValue) { - var hwidfield = this.lookupReference('hwid'); - var portfield = this.lookupReference('port'); - var usb3field = this.lookupReference('usb3'); - if (field.inputValue === 'hostdevice') { - hwidfield.setDisabled(!newValue); - } else if(field.inputValue === 'port') { - portfield.setDisabled(!newValue); - } else if(field.inputValue === 'spice') { - usb3field.setDisabled(newValue); - } - } - }, - 'pveUSBSelector': { - change: function(field, newValue, oldValue) { - var usbval = field.getUSBValue(); - var usb3field = this.lookupReference('usb3'); - var usb3 = /usb3/.test(usbval); - if(usb3 && !usb3field.isDisabled()) { - usb3field.savedVal = usb3field.getValue(); - usb3field.setValue(true); - usb3field.setDisabled(true); - } else if(!usb3 && usb3field.isDisabled()){ - var val = (usb3field.savedVal === undefined)?usb3field.originalValue:usb3field.savedVal; - usb3field.setValue(val); - usb3field.setDisabled(false); - } - } - } - } - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - }, - - onGetValues: function(values) { - var me = this; - if(!me.confid) { - var i; - for (i = 0; i < 6; i++) { - if (!me.vmconfig['usb' + i.toString()]) { - me.confid = 'usb' + i.toString(); - break; - } - } - } - var val = ""; - var type = me.down('radiofield').getGroupValue(); - switch (type) { - case 'spice': - val = 'spice'; break; - case 'hostdevice': - case 'port': - val = me.down('pveUSBSelector[name=' + type + ']').getUSBValue(); - if (!/usb3/.test(val) && me.down('field[name=usb3]').getValue() === true) { - val += ',usb3=1'; - } - break; - default: - throw "invalid type selected"; - } - - values[me.confid] = val; - return values; - }, - - items: [ - { - xtype: 'fieldcontainer', - defaultType: 'radiofield', - items:[ - { - name: 'usb', - inputValue: 'spice', - boxLabel: gettext('Spice Port'), - submitValue: false, - checked: true - }, - { - name: 'usb', - inputValue: 'hostdevice', - boxLabel: gettext('Use USB Vendor/Device ID'), - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - type: 'device', - name: 'hostdevice', - cbind: { pveSelNode: '{pveSelNode}' }, - editable: true, - reference: 'hwid', - allowBlank: false, - fieldLabel: 'Choose Device', - labelAlign: 'right', - submitValue: false - }, - { - name: 'usb', - inputValue: 'port', - boxLabel: gettext('Use USB Port'), - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - name: 'port', - cbind: { pveSelNode: '{pveSelNode}' }, - editable: true, - type: 'port', - reference: 'port', - allowBlank: false, - fieldLabel: gettext('Choose Port'), - labelAlign: 'right', - submitValue: false - }, - { - xtype: 'checkbox', - name: 'usb3', - submitValue: false, - reference: 'usb3', - fieldLabel: gettext('Use USB3') - } - ] - } - ] -}); - -Ext.define('PVE.qemu.USBEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('USB Device'), - - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.USBInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var data = response.result.data[me.confid].split(','); - var port, hostdevice, usb3 = false; - var type = 'spice'; - var i; - for (i = 0; i < data.length; i++) { - if (/^(host=)?(0x)?[a-zA-Z0-9]{4}\:(0x)?[a-zA-Z0-9]{4}$/.test(data[i])) { - hostdevice = data[i]; - hostdevice = hostdevice.replace('host=', '').replace('0x',''); - type = 'hostdevice'; - } else if (/^(host=)?(\d+)\-(\d+(\.\d+)*)$/.test(data[i])) { - port = data[i]; - port = port.replace('host=',''); - type = 'port'; - } - - if (/^usb3=(1|on|true)$/.test(data[i])) { - usb3 = true; - } - } - var values = { - usb : type, - hostdevice: hostdevice, - port: port, - usb3: usb3 - }; - - ipanel.setValues(values); - } - } - }); - } -}); -Ext.define('PVE.qemu.PCIInputPanel', { - extend: 'Proxmox.panel.InputPanel', - - onlineHelp: 'qm_pci_passthrough', - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - - var hostpci = me.vmconfig[me.confid] || ''; - - var values = PVE.Parser.parsePropertyString(hostpci, 'host'); - if (values.host && values.host.length < 6) { // 00:00 format not 00:00.0 - values.host += ".0"; - values.multifunction = true; - } - values['x-vga'] = PVE.Parser.parseBoolean(values['x-vga'], 0); - values.pcie = PVE.Parser.parseBoolean(values.pcie, 0); - values.rombar = PVE.Parser.parseBoolean(values.rombar, 1); - - me.setValues(values); - if (!me.vmconfig.machine || me.vmconfig.machine.indexOf('q35') === -1) { - // machine is not set to some variant of q35, so we disable pcie - var pcie = me.down('field[name=pcie]'); - pcie.setDisabled(true); - pcie.setBoxLabel(gettext('Q35 only')); - } - - if (values.romfile) { - me.down('field[name=romfile]').setVisible(true); - } - }, - - onGetValues: function(values) { - var me = this; - var ret = {}; - if(!me.confid) { - var i; - for (i = 0; i < 5; i++) { - if (!me.vmconfig['hostpci' + i.toString()]) { - me.confid = 'hostpci' + i.toString(); - break; - } - } - } - if (values.multifunction) { - // modify host to skip the '.X' - values.host = values.host.substring(0,5); - delete values.multifunction; - } - - if (values.rombar) { - delete values.rombar; - } else { - values.rombar = 0; - } - - if (!values.romfile) { - delete values.romfile; - } - - ret[me.confid] = PVE.Parser.printPropertyString(values, 'host'); - return ret; - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.column1 = [ - { - xtype: 'pvePCISelector', - fieldLabel: gettext('Device'), - name: 'host', - nodename: me.nodename, - allowBlank: false, - onLoadCallBack: function(store, records, success) { - if (!success || !records.length) { - return; - } - - var first = records[0]; - if (first.data.iommugroup === -1) { - // no iommu groups - var warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - value: 'No IOMMU detected, please activate it.' + - 'See Documentation for further information.', - userCls: 'pve-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } - }, - listeners: { - change: function(pcisel, value) { - if (!value) { - return; - } - var pcidev = pcisel.getStore().getById(value); - var mdevfield = me.down('field[name=mdev]'); - mdevfield.setDisabled(!pcidev || !pcidev.data.mdev); - if (!pcidev) { - return; - } - var id = pcidev.data.id.substring(0,5); // 00:00 - var iommu = pcidev.data.iommugroup; - // try to find out if there are more devices - // in that iommu group - if (iommu !== -1) { - var count = 0; - pcisel.getStore().each(function(record) { - if (record.data.iommugroup === iommu && - record.data.id.substring(0,5) !== id) - { - count++; - return false; - } - }); - var warning = me.down('#iommuwarning'); - if (count && !warning) { - warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - itemId: 'iommuwarning', - value: 'The selected Device is not in a seperate' + - 'IOMMU group, make sure this is intended.', - userCls: 'pve-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } else if (!count && warning) { - me.remove(warning); - } - } - if (pcidev.data.mdev) { - mdevfield.setPciID(value); - } - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('All Functions'), - name: 'multifunction' - } - ]; - - me.column2 = [ - { - xtype: 'pveMDevSelector', - name: 'mdev', - disabled: true, - fieldLabel: gettext('MDev Type'), - nodename: me.nodename, - listeners: { - change: function(field, value) { - var mf = me.down('field[name=multifunction]'); - if (!!value) { - mf.setValue(false); - } - mf.setDisabled(!!value); - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Primary GPU'), - name: 'x-vga' - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'ROM-Bar', - name: 'rombar' - }, - { - xtype: 'displayfield', - submitValue: true, - hidden: true, - fieldLabel: 'ROM-File', - name: 'romfile' - } - ]; - - me.advancedColumn2 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'PCI-Express', - name: 'pcie' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.PCIEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('PCI Device'), - - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.PCIInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.SerialnputPanel', { - extend: 'Proxmox.panel.InputPanel', - - autoComplete: false, - - setVMConfig: function(vmconfig) { - var me = this, i; - me.vmconfig = vmconfig; - - for (i = 0; i < 4; i++) { - var port = 'serial' + i.toString(); - if (!me.vmconfig[port]) { - me.down('field[name=serialid]').setValue(i); - break; - } - } - - }, - - onGetValues: function(values) { - var me = this; - - var id = 'serial' + values.serialid; - delete values.serialid; - values[id] = 'socket'; - return values; - }, - - items: [ - { - xtype: 'proxmoxintegerfield', - name: 'serialid', - fieldLabel: gettext('Serial Port'), - minValue: 0, - maxValue: 3, - allowBlank: false, - validator: function(id) { - if (!this.rendered) { - return true; - } - var me = this.up('panel'); - if (me.vmconfig !== undefined && Ext.isDefined(me.vmconfig['serial' + id])) { - return "This device is already in use."; - } - return true; - } - } - ] -}); - -Ext.define('PVE.qemu.SerialEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('Serial Port'), - - initComponent : function() { - var me = this; - - // for now create of (socket) serial port only - me.isCreate = true; - - var ipanel = Ext.create('PVE.qemu.SerialnputPanel', {}); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.window.IPInfo', { - extend: 'Ext.window.Window', - width: 600, - title: gettext('Guest Agent Network Information'), - height: 300, - layout: { - type: 'fit' - }, - modal: true, - items: [ - { - xtype: 'grid', - emptyText: gettext('No network information'), - columns: [ - { - dataIndex: 'name', - text: gettext('Name'), - flex: 3 - }, - { - dataIndex: 'hardware-address', - text: gettext('MAC address'), - width: 140 - }, - { - dataIndex: 'ip-addresses', - text: gettext('IP address'), - align: 'right', - flex: 4, - renderer: function(val) { - if (!Ext.isArray(val)) { - return ''; - } - var ips = []; - val.forEach(function(ip) { - var addr = ip['ip-address']; - var pref = ip.prefix; - if (addr && pref) { - ips.push(addr + '/' + pref); - } - }); - return ips.join('
'); - } - } - ] - } - ] -}); - -Ext.define('PVE.qemu.AgentIPView', { - extend: 'Ext.container.Container', - xtype: 'pveAgentIPView', - - layout: { - type: 'hbox', - align: 'top' - }, - - nics: [], - - items: [ - { - xtype: 'box', - html: ' IPs' - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'right', - pack: 'end' - }, - items: [ - { - xtype: 'label', - flex: 1, - itemId: 'ipBox', - style: { - 'text-align': 'right' - } - }, - { - xtype: 'button', - itemId: 'moreBtn', - hidden: true, - ui: 'default-toolbar', - handler: function(btn) { - var me = this.up('pveAgentIPView'); - - var win = Ext.create('PVE.window.IPInfo'); - win.down('grid').getStore().setData(me.nics); - win.show(); - }, - text: gettext('More') - } - ] - } - ], - - getDefaultIps: function(nics) { - var me = this; - var ips = []; - nics.forEach(function(nic) { - if (nic['hardware-address'] && - nic['hardware-address'] != '00:00:00:00:00:00') { - - var nic_ips = nic['ip-addresses'] || []; - nic_ips.forEach(function(ip) { - var p = ip['ip-address']; - // show 2 ips at maximum - if (ips.length < 2) { - ips.push(p); - } - }); - } - }); - - return ips; - }, - - startIPStore: function(store, records, success) { - var me = this; - var agentRec = store.getById('agent'); - /*jslint confusion: true*/ - /* value is number and string */ - me.agent = (agentRec && agentRec.data.value === 1); - me.running = (store.getById('status').data.value === 'running'); - /*jslint confusion: false*/ - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!caps.vms['VM.Monitor']) { - var errorText = gettext("Requires '{0}' Privileges"); - me.updateStatus(false, Ext.String.format(errorText, 'VM.Monitor')); - return; - } - - if (me.agent && me.running && me.ipStore.isStopped) { - me.ipStore.startUpdate(); - } else if (me.ipStore.isStopped) { - me.updateStatus(); - } - }, - - updateStatus: function(unsuccessful, defaulttext) { - var me = this; - var text = defaulttext || gettext('No network information'); - var more = false; - if (unsuccessful) { - text = gettext('Guest Agent not running'); - } else if (me.agent && me.running) { - if (Ext.isArray(me.nics) && me.nics.length) { - more = true; - var ips = me.getDefaultIps(me.nics); - if (ips.length !== 0) { - text = ips.join('
'); - } - } else if (me.nics && me.nics.error) { - var msg = gettext('Cannot get info from Guest Agent
Error: {0}'); - text = Ext.String.format(text, me.nics.error.desc); - } - } else if (me.agent) { - text = gettext('Guest Agent not running'); - } else { - text = gettext('No Guest Agent configured'); - } - - var ipBox = me.down('#ipBox'); - ipBox.update(text); - - var moreBtn = me.down('#moreBtn'); - moreBtn.setVisible(more); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw 'rstore not given'; - } - - if (!me.pveSelNode) { - throw 'pveSelNode not given'; - } - - var nodename = me.pveSelNode.data.node; - var vmid = me.pveSelNode.data.vmid; - - me.ipStore = Ext.create('Proxmox.data.UpdateStore', { - interval: 10000, - storeid: 'pve-qemu-agent-' + vmid, - method: 'POST', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/qemu/' + vmid + '/agent/network-get-interfaces' - } - }); - - me.callParent(); - - me.mon(me.ipStore, 'load', function(store, records, success) { - if (records && records.length) { - me.nics = records[0].data.result; - } else { - me.nics = undefined; - } - me.updateStatus(!success); - }); - - me.on('destroy', me.ipStore.stopUpdate); - - // if we already have info about the vm, use it immediately - if (me.rstore.getCount()) { - me.startIPStore(me.rstore, me.rstore.getData(), false); - } - - // check if the guest agent is there on every statusstore load - me.mon(me.rstore, 'load', me.startIPStore, me); - } -}); -Ext.define('PVE.qemu.CloudInit', { - extend: 'Proxmox.grid.PendingObjectGrid', - xtype: 'pveCiPanel', - - onlineHelp: 'qm_cloud_init', - - tbar: [ - { - xtype: 'proxmoxButton', - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var me = this.up('grid'); - var warn = gettext('Are you sure you want to remove entry {0}'); - - var entry = rec.data.key; - var msg = Ext.String.format(warn, "'" - + me.renderKey(entry, {}, rec) + "'"); - - return msg; - }, - enableFn: function(record) { - var me = this.up('grid'); - var caps = Ext.state.Manager.get('GuiCap'); - if (me.rows[record.data.key].never_delete || - !caps.vms['VM.Config.Network']) { - return false; - } - - if (record.data.key === 'cipassword' && !record.data.value) { - return false; - } - return true; - }, - handler: function() { - var me = this.up('grid'); - var records = me.getSelection(); - if (!records || !records.length) { - return; - } - - var id = records[0].data.key; - var match = id.match(/^net(\d+)$/); - if (match) { - id = 'ipconfig' + match[1]; - } - - var params = {}; - params['delete'] = id; - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: params, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - callback: function() { - me.reload(); - } - }); - }, - text: gettext('Remove') - }, - { - xtype: 'proxmoxButton', - disabled: true, - handler: function() { - var me = this.up('grid'); - me.run_editor(); - }, - text: gettext('Edit') - }, - '-', - { - xtype: 'button', - itemId: 'savebtn', - text: gettext('Regenerate Image'), - handler: function() { - var me = this.up('grid'); - var eject_params = {}; - var insert_params = {}; - var disk = PVE.Parser.parseQemuDrive(me.ciDriveId, me.ciDrive); - var storage = ''; - var stormatch = disk.file.match(/^([^\:]+)\:/); - if (stormatch) { - storage = stormatch[1]; - } - eject_params[me.ciDriveId] = 'none,media=cdrom'; - insert_params[me.ciDriveId] = storage + ':cloudinit'; - - var failure = function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }; - - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: eject_params, - failure: failure, - callback: function() { - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: insert_params, - failure: failure, - callback: function() { - me.reload(); - } - }); - } - }); - } - } - ], - - border: false, - - set_button_status: function(rstore, records, success) { - if (!success || records.length < 1) { - return; - } - var me = this; - var found; - records.forEach(function(record) { - if (found) { - return; - } - var id = record.data.key; - var value = record.data.value; - var ciregex = new RegExp("vm-" + me.pveSelNode.data.vmid + "-cloudinit"); - if (id.match(/^(ide|scsi|sata)\d+$/) && ciregex.test(value)) { - found = id; - me.ciDriveId = found; - me.ciDrive = value; - } - }); - - me.down('#savebtn').setDisabled(!found); - me.setDisabled(!found); - if (!found) { - me.getView().mask(gettext('No CloudInit Drive found'), ['pve-static-mask']); - } else { - me.getView().unmask(); - } - }, - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - - var icon = ""; - if (rowdef.iconCls) { - icon = ' '; - } - return icon + (rowdef.header || key); - }, - - listeners: { - activate: function () { - var me = this; - me.rstore.startUpdate(); - }, - itemdblclick: function() { - var me = this; - me.run_editor(); - } - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - var caps = Ext.state.Manager.get('GuiCap'); - me.baseurl = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid; - me.url = me.baseurl + '/pending'; - me.editorConfig.url = me.baseurl + '/config'; - me.editorConfig.pveSelNode = me.pveSelNode; - - /*jslint confusion: true*/ - /* editor is string and object */ - me.rows = { - ciuser: { - header: gettext('User'), - iconCls: 'fa fa-user', - never_delete: true, - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('User'), - items: [ - { - xtype: 'proxmoxtextfield', - deleteEmpty: true, - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('User'), - name: 'ciuser' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.defaultText; - } - }, - cipassword: { - header: gettext('Password'), - iconCls: 'fa fa-unlock', - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Password'), - items: [ - { - xtype: 'proxmoxtextfield', - inputType: 'password', - deleteEmpty: true, - emptyText: Proxmox.Utils.noneText, - fieldLabel: gettext('Password'), - name: 'cipassword' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.noneText; - } - }, - searchdomain: { - header: gettext('DNS domain'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - nameserver: { - header: gettext('DNS servers'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - sshkeys: { - header: gettext('SSH public key'), - iconCls: 'fa fa-key', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.SSHKeyEdit' : undefined, - never_delete: true, - renderer: function(value) { - value = decodeURIComponent(value); - var keys = value.split('\n'); - var text = []; - keys.forEach(function(key) { - if (key.length) { - // First erase all quoted strings (eg. command="foo" - var v = key.replace(/"(?:\\.|[^"\\])*"/g, ''); - // Now try to detect the comment: - var res = v.match(/^\s*(\S+\s+)?(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)\s+\S+\s+(.*?)\s*$/, ''); - if (res) { - key = Ext.String.htmlEncode(res[2]); - if (res[1]) { - key += ' (' + gettext('with options') + ')'; - } - text.push(key); - return; - } - // Most likely invalid at this point, so just stick to - // the old value. - text.push(Ext.String.htmlEncode(key)); - } - }); - if (text.length) { - return text.join('
'); - } else { - return Proxmox.Utils.noneText; - } - }, - defaultValue: '' - } - }; - var i; - var ipconfig_renderer = function(value, md, record, ri, ci, store, pending) { - var id = record.data.key; - var match = id.match(/^net(\d+)$/); - var val = ''; - if (match) { - val = me.getObjectValue('ipconfig'+match[1], '', pending); - } - return val; - }; - for (i = 0; i < 32; i++) { - // we want to show an entry for every network device - // even if it is empty - me.rows['net' + i.toString()] = { - multiKey: ['ipconfig' + i.toString(), 'net' + i.toString()], - header: gettext('IP Config') + ' (net' + i.toString() +')', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.IPConfigEdit' : undefined, - iconCls: 'fa fa-exchange', - renderer: ipconfig_renderer - }; - me.rows['ipconfig' + i.toString()] = { - visible: false - }; - } - /*jslint confusion: false*/ - - PVE.Utils.forEachBus(['ide', 'scsi', 'sata'], function(type, id) { - me.rows[type+id] = { - visible: false - }; - }); - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - } -}); -Ext.define('PVE.qemu.CIDriveInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveCIDriveInputPanel', - - insideWizard: false, - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var drive = {}; - var params = {}; - drive.file = values.hdstorage + ":cloudinit"; - drive.format = values.diskformat; - params[values.controller + values.deviceid] = PVE.Parser.printQemuDrive(drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - setVMConfig: function(config) { - var me = this; - me.down('#drive').setVMConfig(config, 'cdrom'); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items = [ - { - xtype: 'pveControllerSelector', - noVirtIO: true, - itemId: 'drive', - fieldLabel: gettext('CloudInit Drive'), - name: 'drive' - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'storselector', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - } - ]; - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CIDriveEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCIDriveEdit', - - isCreate: true, - subject: gettext('CloudInit Drive'), - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveCIDriveInputPanel', - itemId: 'cipanel', - nodename: nodename - }]; - - me.callParent(); - - me.load({ - success: function(response, opts) { - me.down('#cipanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.SSHKeyInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSSHKeyInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - if (values.sshkeys) { - values.sshkeys.trim(); - } - if (!values.sshkeys.length) { - values = {}; - values['delete'] = 'sshkeys'; - return values; - } else { - values.sshkeys = encodeURIComponent(values.sshkeys); - } - return values; - }, - - items: [ - { - xtype: 'textarea', - itemId: 'sshkeys', - name: 'sshkeys', - height: 250 - }, - { - xtype: 'filebutton', - itemId: 'filebutton', - name: 'file', - text: gettext('Load SSH Key File'), - fieldLabel: 'test', - listeners: { - change: function(btn, e, value) { - var me = this.up('inputpanel'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - var keysField = me.down('#sshkeys'); - var old = keysField.getValue(); - keysField.setValue(old + res); - }); - }); - btn.reset(); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - if (!window.FileReader) { - me.down('#filebutton').setVisible(false); - } - - } -}); - -Ext.define('PVE.qemu.SSHKeyEdit', { - extend: 'Proxmox.window.Edit', - - width: 800, - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.SSHKeyInputPanel'); - - Ext.apply(me, { - subject: gettext('SSH Keys'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.create) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.sshkeys) { - data.sshkeys = decodeURIComponent(data.sshkeys); - ipanel.setValues(data); - } - } - }); - } - } -}); -Ext.define('PVE.qemu.IPConfigPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveIPConfigPanel', - - insideWizard: false, - - vmconfig: {}, - - onGetValues: function(values) { - var me = this; - - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - - var params = {}; - - var cfg = PVE.Parser.printIPConfig(values); - if (cfg === '') { - params['delete'] = [me.confid]; - } else { - params[me.confid] = cfg; - } - return params; - }, - - setVMConfig: function(config) { - var me = this; - me.vmconfig = config; - }, - - setIPConfig: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data.ip === 'dhcp') { - data.ipv4mode = data.ip; - data.ip = ''; - } else { - data.ipv4mode = 'static'; - } - if (data.ip6 === 'dhcp' || data.ip6 === 'auto') { - data.ipv6mode = data.ip6; - data.ip6 = ''; - } else { - data.ipv6mode = 'static'; - } - - me.ipconfig = data; - me.setValues(me.ipconfig); - }, - - initComponent : function() { - var me = this; - - me.ipconfig = {}; - - me.column1 = [ - { - xtype: 'displayfield', - fieldLabel: gettext('Network Device'), - value: me.netid - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv4') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv4mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: '', - disabled: true, - fieldLabel: gettext('IPv4/CIDR') - }, - { - xtype: 'textfield', - name: 'gw', - value: '', - vtype: 'IPAddress', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv4') +')' - } - ]; - - me.column2 = [ - { - xtype: 'displayfield' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv6') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv6mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: '', - vtype: 'IP6CIDRAddress', - disabled: true, - fieldLabel: gettext('IPv6/CIDR') - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: '', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv6') +')' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.IPConfigEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - // convert confid from netX to ipconfigX - var match = me.confid.match(/^net(\d+)$/); - if (match) { - me.netid = me.confid; - me.confid = 'ipconfig' + match[1]; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.IPConfigPanel', { - confid: me.confid, - netid: me.netid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: gettext('Network Config'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - me.vmconfig = response.result.data; - var ipconfig = {}; - var value = me.vmconfig[me.confid]; - if (value) { - ipconfig = PVE.Parser.parseIPConfig(me.confid, value); - if (!ipconfig) { - Ext.Msg.alert(gettext('Error'), gettext('Unable to parse network configuration')); - me.close(); - return; - } - } - ipanel.setIPConfig(me.confid, ipconfig); - ipanel.setVMConfig(me.vmconfig); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.SystemInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSystemPanel', - - onlineHelp: 'qm_system_settings', - - viewModel: { - data: { - efi: false, - addefi: true - }, - - formulas: { - efidisk: function(get) { - return get('efi') && get('addefi'); - } - } - }, - - onGetValues: function(values) { - if (values.vga && values.vga.substr(0,6) === 'serial') { - values['serial' + values.vga.substr(6,1)] = 'socket'; - } - - var efidrive = {}; - if (values.hdimage) { - efidrive.file = values.hdimage; - } else if (values.hdstorage) { - efidrive.file = values.hdstorage + ":1"; - } - - if (values.diskformat) { - efidrive.format = values.diskformat; - } - - delete values.hdimage; - delete values.hdstorage; - delete values.diskformat; - - if (efidrive.file) { - values.efidisk0 = PVE.Parser.printQemuDrive(efidrive); - } - - return values; - }, - - controller: { - xclass: 'Ext.app.ViewController', - - scsihwChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('current.scsihw', value); - } - }, - - biosChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('efi', value === 'ovmf'); - } - }, - - control: { - 'pveScsiHwSelector': { - change: 'scsihwChange' - }, - 'pveQemuBiosSelector': { - change: 'biosChange' - } - } - }, - - column1: [ - { - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - name: 'vga', - comboItems: PVE.Utils.kvm_vga_driver_array() - }, - { - xtype: 'proxmoxcheckbox', - name: 'agent', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Qemu Agent') - } - ], - - column2: [ - { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - bind: { - value: '{current.scsihw}' - }, - fieldLabel: gettext('SCSI Controller') - } - ], - - advancedColumn1: [ - { - xtype: 'pveQemuBiosSelector', - name: 'bios', - value: '__default__', - fieldLabel: 'BIOS' - }, - { - xtype: 'proxmoxcheckbox', - bind: { - value: '{addefi}', - hidden: '{!efi}', - disabled: '{!efi}' - }, - hidden: true, - submitValue: false, - disabled: true, - fieldLabel: gettext('Add EFI Disk') - }, - { - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - bind: { - nodename: '{nodename}', - hidden: '{!efi}', - disabled: '{!efidisk}' - }, - autoSelect: false, - disabled: true, - hidden: true, - hideSize: true - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - } - ] - -}); -Ext.define('PVE.lxc.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveLxcSummary', - - scrollable: true, - bodyPadding: 5, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - if (!me.workspace) { - throw "no workspace specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var template = !!me.pveSelNode.data.template; - var rstore = me.statusStore; - - var width = template ? 1 : 0.5; - var items = [ - { - xtype: template ? 'pveTemplateStatusView' : 'pveGuestStatusView', - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - }, - itemId: 'gueststatus', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'pveNotesView', - maxHeight: 320, - itemId: 'notesview', - pveSelNode: me.pveSelNode, - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - } - } - ]; - - var rrdstore; - if (!template) { - - rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/rrddata", - model: 'pve-rrd-guest' - }); - - items.push( - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - pveSelNode: me.pveSelNode, - fields: ['cpu'], - fieldTitles: [gettext('CPU usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - pveSelNode: me.pveSelNode, - fields: ['maxmem', 'mem'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - pveSelNode: me.pveSelNode, - fields: ['netin','netout'], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Disk IO'), - pveSelNode: me.pveSelNode, - fields: ['diskread','diskwrite'], - store: rrdstore - } - ); - - } - - Ext.apply(me, { - tbar: [ '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: { - type: 'column' - }, - defaults: { - minHeight: 320, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: items - } - ] - }); - - me.callParent(); - if (!template) { - rrdstore.startUpdate(); - me.on('destroy', rrdstore.stopUpdate); - } - } -}); -Ext.define('PVE.lxc.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcNetworkInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_network', - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - var bridgesel = me.query("[isFormField][name=bridge]")[0]; - bridgesel.setNodename(nodename); - }, - - onGetValues: function(values) { - var me = this; - - var id; - if (me.isCreate) { - id = values.id; - delete values.id; - } else { - id = me.ifname; - } - - if (!id) { - return {}; - } - - var newdata = {}; - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - newdata[id] = PVE.Parser.printLxcNetwork(values); - return newdata; - }, - - initComponent : function() { - var me = this; - - var cdata = {}; - - if (me.insideWizard) { - me.ifname = 'net0'; - cdata.name = 'eth0'; - me.dataCache = {}; - } - cdata.firewall = (me.insideWizard || me.isCreate); - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.isCreate) { - if (!me.ifname) { - throw "no interface name specified"; - } - if (!me.dataCache[me.ifname]) { - throw "no such interface '" + me.ifname + "'"; - } - - cdata = PVE.Parser.parseLxcNetwork(me.dataCache[me.ifname]); - } - - var i; - for (i = 0; i < 10; i++) { - if (me.isCreate && !me.dataCache['net'+i.toString()]) { - me.ifname = 'net' + i.toString(); - break; - } - } - - var idselector = { - xtype: 'hidden', - name: 'id', - value: me.ifname - }; - - me.column1 = [ - idselector, - { - xtype: 'textfield', - name: 'name', - fieldLabel: gettext('Name'), - emptyText: '(e.g., eth0)', - allowBlank: false, - value: cdata.name, - validator: function(value) { - var result = ''; - Ext.Object.each(me.dataCache, function(key, netstr) { - if (!key.match(/^net\d+/) || key === me.ifname) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(netstr); - if (net.name === value) { - result = "interface name already in use"; - return false; - } - }); - if (result !== '') { - return result; - } - // validator can return bool/string - /*jslint confusion:true*/ - return true; - } - }, - { - xtype: 'textfield', - name: 'hwaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - value: cdata.hwaddr, - allowBlank: true, - emptyText: 'auto' - }, - { - xtype: 'PVE.form.BridgeSelector', - name: 'bridge', - nodename: me.nodename, - fieldLabel: gettext('Bridge'), - value: cdata.bridge, - allowBlank: false - }, - { - xtype: 'pveVlanField', - name: 'tag', - value: cdata.tag - }, - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: cdata.rate, - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - value: cdata.firewall - } - ]; - - var dhcp4 = (cdata.ip === 'dhcp'); - if (dhcp4) { - cdata.ip = ''; - cdata.gw = ''; - } - - var auto6 = (cdata.ip6 === 'auto'); - var dhcp6 = (cdata.ip6 === 'dhcp'); - if (auto6 || dhcp6) { - cdata.ip6 = ''; - cdata.gw6 = ''; - } - - me.column2 = [ - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv4:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: !dhcp4, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv4mode', - inputValue: 'dhcp', - checked: dhcp4, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: cdata.ip, - disabled: dhcp4, - fieldLabel: 'IPv4/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw', - value: cdata.gw, - vtype: 'IPAddress', - disabled: dhcp4, - fieldLabel: gettext('Gateway') + ' (IPv4)', - margin: '0 0 3 0' // override bottom margin to account for the menuseparator - }, - { - xtype: 'menuseparator', - height: '3', - margin: '0' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv6:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: !(auto6 || dhcp6), - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv6mode', - inputValue: 'dhcp', - checked: dhcp6, - margin: '0 0 0 10' - }, - { - xtype: 'radiofield', - boxLabel: 'SLAAC', // do not localize - name: 'ipv6mode', - inputValue: 'auto', - checked: auto6, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: cdata.ip6, - vtype: 'IP6CIDRAddress', - disabled: (dhcp6 || auto6), - fieldLabel: 'IPv6/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: cdata.gw6, - disabled: (dhcp6 || auto6), - fieldLabel: gettext('Gateway') + ' (IPv6)' - } - ]; - - me.callParent(); - } -}); - - -Ext.define('PVE.lxc.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - var me = this; - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.nodename) { - throw "no node name specified"; - } - - var ipanel = Ext.create('PVE.lxc.NetworkInputPanel', { - ifname: me.ifname, - nodename: me.nodename, - dataCache: me.dataCache, - isCreate: me.isCreate - }); - - Ext.apply(me, { - subject: gettext('Network Device') + ' (veth)', - digest: me.dataCache.digest, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.NetworkView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveLxcNetworkView', - - onlineHelp: 'pct_container_network', - - dataCache: {}, // used to store result of last load - - stateful: true, - stateId: 'grid-lxc-network', - - load: function() { - var me = this; - - Proxmox.Utils.setErrorMask(me, true); - - Proxmox.Utils.API2Request({ - url: me.url, - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, gettext('Error') + ': ' + response.htmlStatus); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var result = Ext.decode(response.responseText); - var data = result.data || {}; - me.dataCache = data; - var records = []; - Ext.Object.each(data, function(key, value) { - if (!key.match(/^net\d+/)) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(value); - net.id = key; - records.push(net); - }); - me.store.loadData(records); - me.down('button[name=addButton]').setDisabled((records.length >= 10)); - } - }); - }, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.url = '/nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var store = new Ext.data.Store({ - model: 'pve-lxc-network', - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!caps.vms['VM.Config.Network']; - }, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - method: 'PUT', - params: { 'delete': rec.data.id, digest: me.dataCache.digest }, - callback: function() { - me.load(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (!caps.vms['VM.Config.Network']) { - return false; - } - - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - dataCache: me.dataCache, - ifname: rec.data.id - }); - win.on('destroy', me.load, me); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!caps.vms['VM.Config.Network']) { - return false; - } - return true; - }, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - name: 'addButton', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - isCreate: true, - dataCache: me.dataCache - }); - win.on('destroy', me.load, me); - win.show(); - } - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - width: 50, - dataIndex: 'id' - }, - { - header: gettext('Name'), - width: 80, - dataIndex: 'name' - }, - { - header: gettext('Bridge'), - width: 80, - dataIndex: 'bridge' - }, - { - header: gettext('Firewall'), - width: 80, - dataIndex: 'firewall', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('VLAN Tag'), - width: 80, - dataIndex: 'tag' - }, - { - header: gettext('MAC address'), - width: 110, - dataIndex: 'hwaddr' - }, - { - header: gettext('IP address'), - width: 150, - dataIndex: 'ip', - renderer: function(value, metaData, rec) { - if (rec.data.ip && rec.data.ip6) { - return rec.data.ip + "
" + rec.data.ip6; - } else if (rec.data.ip6) { - return rec.data.ip6; - } else { - return rec.data.ip; - } - } - }, - { - header: gettext('Gateway'), - width: 150, - dataIndex: 'gw', - renderer: function(value, metaData, rec) { - if (rec.data.gw && rec.data.gw6) { - return rec.data.gw + "
" + rec.data.gw6; - } else if (rec.data.gw6) { - return rec.data.gw6; - } else { - return rec.data.gw; - } - } - } - ], - listeners: { - activate: me.load, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-lxc-network', { - extend: "Ext.data.Model", - proxy: { type: 'memory' }, - fields: [ 'id', 'name', 'hwaddr', 'bridge', - 'ip', 'gw', 'ip6', 'gw6', 'tag', 'firewall' ] - }); - -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.RessourceView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcRessourceView'], - - onlineHelp: 'pct_configuration', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rowdef = me.rows[key] || {}; - - metaData.tdAttr = "valign=middle"; - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - } - return rowdef.header || key; - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - var mpeditor = caps.vms['VM.Config.Disk'] ? 'PVE.lxc.MountPointEdit' : undefined; - - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-memory', - group: 1, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - swap: { - header: gettext('Swap'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-swap', - group: 2, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - cores: { - header: gettext('Cores'), - editor: caps.vms['VM.Config.CPU'] ? 'PVE.lxc.CPUEdit' : undefined, - defaultValue: '', - tdCls: 'pve-itype-icon-processor', - group: 3, - renderer: function(value) { - var cpulimit = me.getObjectValue('cpulimit'); - var cpuunits = me.getObjectValue('cpuunits'); - var res; - if (value) { - res = value; - } else { - res = gettext('unlimited'); - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit + ']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits + ']'; - } - return res; - } - }, - rootfs: { - header: gettext('Root Disk'), - defaultValue: Proxmox.Utils.noneText, - editor: mpeditor, - tdCls: 'pve-itype-icon-storage', - group: 4 - }, - cpulimit: { - visible: false - }, - cpuunits: { - visible: false - }, - unprivileged: { - visible: false - } - }; - - PVE.Utils.forEachMP(function(bus, i) { - confid = bus + i; - var group = 5; - var header; - if (bus === 'mp') { - header = gettext('Mount Point') + ' (' + confid + ')'; - } else { - header = gettext('Unused Disk') + ' ' + i; - group += 1; - } - rows[confid] = { - group: group, - order: i, - tdCls: 'pve-itype-icon-storage', - editor: mpeditor, - header: header - }; - }, true); - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - var run_resize = function() { - var rec = me.selModel.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.MPResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - }; - - var run_remove = function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'delete': rec.data.key - }, - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var run_move = function(b, e, rec) { - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid, - type: 'lxc' - }); - - win.show(); - - win.on('destroy', me.reload, me); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: me.selModel, - disabled: true, - enableFn: function(rec) { - if (!rec) { - return false; - } - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: me.selModel, - disabled: true, - handler: run_resize - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - selModel: me.selModel, - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + me.renderKey(rec.data.key, {}, rec) + "'"); - if (rec.data.key.match(/^unused\d+$/)) { - msg += " " + gettext('This will permanently erase all data.'); - } - - return msg; - }, - handler: run_remove - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move Volume'), - selModel: me.selModel, - disabled: true, - dangerous: true, - handler: run_move - }); - - var set_button_status = function() { - var rec = me.selModel.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - remove_btn.disable(); - resize_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var isDisk = (rowdef.tdCls == 'pve-itype-icon-storage'); - - var noedit = rec.data['delete'] || !rowdef.editor; - if (!noedit && Proxmox.UserName !== 'root@pam' && key.match(/^mp\d+$/)) { - var mp = PVE.Parser.parseLxcMountPoint(value); - if (mp.type !== 'volume') { - noedit = true; - } - } - edit_btn.setDisabled(noedit); - - remove_btn.setDisabled(!isDisk || rec.data.key === 'rootfs' || !diskCap); - resize_btn.setDisabled(!isDisk || !diskCap); - move_btn.setDisabled(!isDisk || !diskCap); - - }; - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - Ext.apply(me, { - url: '/api2/json/' + baseurl, - selModel: me.selModel, - interval: 2000, - cwidth1: 170, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Mount Point'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.lxc.MountPointEdit', { - url: '/api2/extjs/' + baseurl, - unprivileged: me.getObjectValue('unprivileged'), - pveSelNode: me.pveSelNode - }); - win.show(); - } - } - ] - }) - }, - edit_btn, - remove_btn, - resize_btn, - move_btn - ], - rows: rows, - sorterFn: sorterFn, - editorConfig: { - pveSelNode: me.pveSelNode, - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - Ext.apply(me.editorConfig, { unprivileged: me.getObjectValue('unprivileged') }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.FeaturesInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcFeaturesInputPanel', - - // used to save the mounts fstypes until sending - mounts: [], - - fstypes: ['nfs', 'cifs'], - - viewModel: { - parent: null, - data: { - unprivileged: false - }, - formulas: { - privilegedOnly: function(get) { - return (get('unprivileged') ? gettext('privileged only') : ''); - }, - unprivilegedOnly: function(get) { - return (!get('unprivileged') ? gettext('unprivileged only') : ''); - } - } - }, - - items: [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('keyctl'), - name: 'keyctl', - bind: { - disabled: '{!unprivileged}', - boxLabel: '{unprivilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Nesting'), - name: 'nesting' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nfs', - fieldLabel: 'NFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'cifs', - fieldLabel: 'CIFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'fuse', - fieldLabel: 'FUSE' - } - ], - - onGetValues: function(values) { - var me = this; - var mounts = me.mounts; - me.fstypes.forEach(function(fs) { - if (values[fs]) { - mounts.push(fs); - } - delete values[fs]; - }); - - if (mounts.length) { - values.mount = mounts.join(';'); - } - - var featuresstring = PVE.Parser.printPropertyString(values, undefined); - if (featuresstring == '') { - return { 'delete': 'features' }; - } - return { features: featuresstring }; - }, - - setValues: function(values) { - var me = this; - - me.viewModel.set({ unprivileged: values.unprivileged }); - - if (values.features) { - var res = PVE.Parser.parsePropertyString(values.features); - me.mounts = []; - if (res.mount) { - res.mount.split(/[; ]/).forEach(function(item) { - if (me.fstypes.indexOf(item) === -1) { - me.mounts.push(item); - } else { - res[item] = 1; - } - }); - } - this.callParent([res]); - } - } -}); - -Ext.define('PVE.lxc.FeaturesEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveLxcFeaturesEdit', - - subject: gettext('Features'), - - items: [{ - xtype: 'pveLxcFeaturesInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load(); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.lxc.Options', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcOptions'], - - onlineHelp: 'pct_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'pct_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - defaultValue: Proxmox.Utils.unknownText - }, - arch: { - header: gettext('Architecture'), - defaultValue: Proxmox.Utils.unknownText - }, - console: { - header: '/dev/console', - defaultValue: 1, - renderer: Proxmox.Utils.format_enabled_toggle, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: '/dev/console', - items: { - xtype: 'proxmoxcheckbox', - name: 'console', - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - checked: true, - fieldLabel: '/dev/console' - } - } : undefined - }, - tty: { - header: gettext('TTY count'), - defaultValue: 2, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('TTY count'), - items: { - xtype: 'proxmoxintegerfield', - name: 'tty', - minValue: 0, - maxValue: 6, - value: 2, - fieldLabel: gettext('TTY count'), - emptyText: gettext('Default'), - deleteEmpty: true - } - } : undefined - }, - cmode: { - header: gettext('Console mode'), - defaultValue: 'tty', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Console mode'), - items: { - xtype: 'proxmoxKVComboBox', - name: 'cmode', - deleteEmpty: true, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (tty)"], - ['tty', "/dev/tty[X]"], - ['console', "/dev/console"], - ['shell', "shell"] - ], - fieldLabel: gettext('Console mode') - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - unprivileged: { - header: gettext('Unprivileged container'), - renderer: Proxmox.Utils.format_boolean, - defaultValue: 0 - }, - features: { - header: gettext('Features'), - defaultValue: Proxmox.Utils.noneText, - editor: Proxmox.UserName === 'root@pam' ? - 'PVE.lxc.FeaturesEdit' : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - Ext.apply(me, { - url: "/api2/json/" + baseurl, - selModel: sm, - interval: 5000, - tbar: [ edit_btn ], - rows: rows, - editorConfig: { - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - } -}); - -Ext.define('PVE.lxc.DNSInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcDNSInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var deletes = []; - if (!values.searchdomain && !me.insideWizard) { - deletes.push('searchdomain'); - } - - if (values.nameserver) { - var list = values.nameserver.split(/[\ \,\;]+/); - values.nameserver = list.join(' '); - } else if(!me.insideWizard) { - deletes.push('nameserver'); - } - - if (deletes.length) { - values['delete'] = deletes.join(','); - } - - return values; - }, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxtextfield', - name: 'searchdomain', - skipEmptyText: true, - fieldLabel: gettext('DNS domain'), - emptyText: gettext('use host settings'), - allowBlank: true - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS servers'), - vtype: 'IP64AddressList', - allowBlank: true, - emptyText: gettext('use host settings'), - name: 'nameserver', - itemId: 'nameserver' - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.DNSEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.lxc.DNSInputPanel'); - - Ext.apply(me, { - subject: gettext('Resources'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - if (values.nameserver) { - values.nameserver.replace(/[,;]/, ' '); - values.nameserver.replace(/^\s+/, ''); - } - - ipanel.setValues(values); - } - }); - } - } -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.DNS', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcDNS'], - - onlineHelp: 'pct_container_network', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - hostname: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Hostname'), - editor: caps.vms['VM.Config.Network'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hostname'), - items: { - xtype: 'inputpanel', - items:{ - fieldLabel: gettext('Hostname'), - xtype: 'textfield', - name: 'hostname', - vtype: 'DnsName', - allowBlank: true, - emptyText: 'CT' + vmid.toString() - }, - onGetValues: function(values) { - var params = values; - if (values.hostname === undefined || - values.hostname === null || - values.hostname === '') { - params = { hostname: 'CT'+vmid.toString()}; - } - return params; - } - } - } : undefined - }, - searchdomain: { - header: gettext('DNS domain'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - }, - nameserver: { - header: gettext('DNS server'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var reload = function() { - me.rstore.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - if (Ext.isString(rowdef.editor)) { - win = Ext.create(rowdef.editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - //win.load(); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: run_editor - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - var rowdef = rows[rec.data.key]; - edit_btn.setDisabled(!rowdef.editor); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/config", - selModel: sm, - cwidth1: 150, - run_editor: run_editor, - tbar: [ edit_btn ], - rows: rows, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status, - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.lxc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.lxc.Config', - - onlineHelp: 'chapter_pct', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!me.pveSelNode.data.template; - - var running = !!me.pveSelNode.data.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + '/lxc/' + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + "/status/" + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var stopBtn = Ext.create('Ext.menu.Item',{ - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('vzstop', vmid), - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - dangerous: true, - handler: function() { - vm_command("stop"); - }, - iconCls: 'fa fa-stop' - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('vzshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items:[stopBtn] - }, - iconCls: 'fa fa-power-off' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'lxc'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('vztemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = me.pveSelNode.data.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - guestType: 'ct', - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - disabled: !caps.vms['VM.Allocate'], - itemId: 'removeBtn', - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'CT', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var vm = me.pveSelNode.data; - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - consoleType: 'lxc', - consoleName: vm.name, - hidden: template, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - - Ext.apply(me, { - title: Ext.String.format(gettext("Container {0} on node '{1}'"), vm.text, nodename), - hstateid: 'lxctab', - tbarSpacing: false, - tbar: [ statusTxt, '->', startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveLxcSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push( - { - title: gettext('Console'), - itemId: 'consolejs', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'lxc', - xtermjs: true, - nodename: nodename - } - ); - } - - me.items.push( - { - title: gettext('Resources'), - itemId: 'resources', - expandedOnInit: true, - iconCls: 'fa fa-cube', - xtype: 'pveLxcRessourceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - xtype: 'pveLxcNetworkView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - itemId: 'dns', - xtype: 'pveLxcDNS' - }, - { - title: gettext('Options'), - itemId: 'options', - iconCls: 'fa fa-gear', - xtype: 'pveLxcOptions' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - iconCls: 'fa fa-list', - xtype: 'proxmoxNodeTasks', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - xtype: 'pveLxcSnapshotTree', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - itemId: 'permissions', - iconCls: 'fa fa-unlock', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var lock; - if (!success) { - status = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - } - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - stopBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'stopped'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.CreateWizard', { - extend: 'PVE.window.Wizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - storage: '', - unprivileged: true - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('LXC Container'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'pct_general', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', // backend only knows vmid - guestType: 'lxc', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'proxmoxtextfield', - name: 'hostname', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Hostname'), - skipEmptyText: true, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - bind: { - value: '{unprivileged}' - }, - fieldLabel: gettext('Unprivileged container') - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'password', - value: '', - fieldLabel: gettext('Password'), - allowBlank: false, - minLength: 5, - change: function(f, value) { - if (f.rendered) { - f.up().down('field[name=confirmpw]').validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'confirmpw', - value: '', - fieldLabel: gettext('Confirm password'), - allowBlank: true, - submitValue: false, - validator: function(value) { - var pw = this.up().down('field[name=password]').getValue(); - if (pw !== value) { - return "Passwords do not match!"; - } - return true; - } - }, - { - xtype: 'proxmoxtextfield', - name: 'ssh-public-keys', - value: '', - fieldLabel: gettext('SSH public key'), - allowBlank: true, - validator: function(value) { - var pwfield = this.up().down('field[name=password]'); - if (value.length) { - var key = PVE.Parser.parseSSHKey(value); - if (!key) { - return "Failed to recognize ssh key"; - } - pwfield.allowBlank = true; - } else { - pwfield.allowBlank = false; - } - pwfield.validate(); - return true; - }, - afterRender: function() { - if (!window.FileReader) { - // No FileReader support in this browser - return; - } - var cancel = function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - }; - var field = this; - field.inputEl.on('dragover', cancel); - field.inputEl.on('dragenter', cancel); - field.inputEl.on('drop', function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - var files = ev.dataTransfer.files; - PVE.Utils.loadSSHKeyFromFile(files[0], function(v) { - field.setValue(v); - }); - }); - } - }, - { - xtype: 'filebutton', - name: 'file', - hidden: !window.FileReader, - text: gettext('Load SSH Key File'), - listeners: { - change: function(btn, e, value) { - e = e.event; - var field = this.up().down('proxmoxtextfield[name=ssh-public-keys]'); - PVE.Utils.loadSSHKeyFromFile(e.target.files[0], function(v) { - field.setValue(v); - }); - btn.reset(); - } - } - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Template'), - onlineHelp: 'pct_container_images', - column1: [ - { - xtype: 'pveStorageSelector', - name: 'tmplstorage', - fieldLabel: gettext('Storage'), - storageContent: 'vztmpl', - autoSelect: true, - allowBlank: false, - bind: { - value: '{storage}', - nodename: '{nodename}' - } - }, - { - xtype: 'pveFileSelector', - name: 'ostemplate', - storageContent: 'vztmpl', - fieldLabel: gettext('Template'), - bind: { - storage: '{storage}', - nodename: '{nodename}' - }, - allowBlank: false - } - ] - }, - { - xtype: 'pveLxcMountPointInputPanel', - title: gettext('Root Disk'), - insideWizard: true, - isCreate: true, - unused: false, - bind: { - nodename: '{nodename}', - unprivileged: '{unprivileged}' - }, - confid: 'rootfs' - }, - { - xtype: 'pveLxcCPUInputPanel', - title: gettext('CPU'), - insideWizard: true - }, - { - xtype: 'pveLxcMemoryInputPanel', - title: gettext('Memory'), - insideWizard: true - }, - { - xtype: 'pveLxcNetworkInputPanel', - title: gettext('Network'), - insideWizard: true, - bind: { - nodename: '{nodename}' - }, - isCreate: true - }, - { - xtype: 'pveLxcDNSInputPanel', - title: gettext('DNS'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var wizard = this.up('window'); - var kv = wizard.getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete' || key === 'tmplstorage') { // ignore - return; - } - if (key === 'password') { // don't show pw - return; - } - var html = Ext.htmlEncode(Ext.JSON.encode(value)); - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - delete kv.tmplstorage; - - if (!kv.pool.length) { - delete kv.pool; - } - - if (!kv.password.length && kv['ssh-public-keys']) { - delete kv.password; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response, opts){ - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - -Ext.define('PVE.lxc.SnapshotTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveLxcSnapshotTree'], - - onlineHelp: 'pct_snapshots', - - load_delay: 3000, - - old_digest: 'invalid', - - stateful: true, - stateId: 'grid-lxc-snapshots', - - sorterFn: function(rec1, rec2) { - var v1 = rec1.data.snaptime; - var v2 = rec2.data.snaptime; - - if (rec1.data.name === 'current') { - return 1; - } - if (rec2.data.name === 'current') { - return -1; - } - - return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); - }, - - reload: function(repeat) { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot', - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - me.load_task.delay(me.load_delay); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var digest = 'invalid'; - var idhash = {}; - var root = { name: '__root', expanded: true, children: [] }; - Ext.Array.each(response.result.data, function(item) { - item.leaf = true; - item.children = []; - if (item.name === 'current') { - digest = item.digest + item.running; - if (item.running) { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running'; - } else { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree'; - } - } else { - item.iconCls = 'fa fa-fw fa-history x-fa-tree'; - } - idhash[item.name] = item; - }); - - if (digest !== me.old_digest) { - me.old_digest = digest; - - Ext.Array.each(response.result.data, function(item) { - if (item.parent && idhash[item.parent]) { - var parent_item = idhash[item.parent]; - parent_item.children.push(item); - parent_item.leaf = false; - parent_item.expanded = true; - parent_item.expandable = false; - } else { - root.children.push(item); - } - }); - - me.setRootNode(root); - } - - me.load_task.delay(me.load_delay); - } - }); - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/feature', - params: { feature: 'snapshot' }, - method: 'GET', - success: function(response, options) { - var res = response.result.data; - if (res.hasFeature) { - var snpBtns = Ext.ComponentQuery.query('#snapshotBtn'); - snpBtns.forEach(function(item){ - item.enable(); - }); - } - } - }); - - - }, - - listeners: { - beforestatesave: function(grid, state, eopts) { - // extjs cannot serialize functions, - // so a the sorter with only the sorterFn will - // not be a valid sorter when restoring the state - delete state.storeState.sorters; - } - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.vmid = me.pveSelNode.data.vmid; - if (!me.vmid) { - throw "no VM ID specified"; - } - - me.load_task = new Ext.util.DelayedTask(me.reload, me); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var valid_snapshot = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current'; - }; - - var valid_snapshot_rollback = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current' && !record.data.snapstate; - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (valid_snapshot(rec)) { - var win = Ext.create('PVE.window.LxcSnapshot', { - snapname: rec.data.name, - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - me.mon(win, 'close', me.reload, me); - } - }; - - var editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot, - handler: run_editor - }); - - var rollbackBtn = new Proxmox.button.Button({ - text: gettext('Rollback'), - disabled: true, - dangerous: true, - selModel: sm, - enableFn: valid_snapshot_rollback, - confirmMsg: function(rec) { - var taskdescription = Proxmox.Utils.format_task_description('vzrollback', me.vmid); - var snaptime = Ext.Date.format(rec.data.snaptime,'Y-m-d H:i:s'); - var snapname = rec.data.name; - - var msg = Ext.String.format(gettext('{0} to {1} ({2})'), - taskdescription, snapname, snaptime); - msg += '

' + gettext('Note: Rollback stops CT') + '

'; - - return msg; - }, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname + '/rollback', - method: 'POST', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var removeBtn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.name + "'"); - return msg; - }, - enableFn: valid_snapshot, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var snapshotBtn = Ext.create('Ext.Button', { - itemId: 'snapshotBtn', - text: gettext('Take Snapshot'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.window.LxcSnapshot', { - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - } - }); - - Ext.apply(me, { - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - selModel: sm, - tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ], - fields: [ - 'name', 'description', 'snapstate', 'vmstate', 'running', - { name: 'snaptime', type: 'date', dateFormat: 'timestamp' } - ], - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - width: 200, - renderer: function(value, metaData, record) { - if (value === 'current') { - return "NOW"; - } else { - return value; - } - } - }, -// { -// text: gettext('RAM'), -// align: 'center', -// resizable: false, -// dataIndex: 'vmstate', -// width: 50, -// renderer: function(value, metaData, record) { -// if (record.data.name !== 'current') { -// return Proxmox.Utils.format_boolean(value); -// } -// } -// }, - { - text: gettext('Date') + "/" + gettext("Status"), - dataIndex: 'snaptime', - resizable: false, - width: 150, - renderer: function(value, metaData, record) { - if (record.data.snapstate) { - return record.data.snapstate; - } - if (value) { - return Ext.Date.format(value,'Y-m-d H:i:s'); - } - } - }, - { - text: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value, metaData, record) { - if (record.data.name === 'current') { - return gettext("You are here!"); - } else { - return Ext.String.htmlEncode(value); - } - } - } - ], - columnLines: true, - listeners: { - activate: me.reload, - destroy: me.load_task.cancel, - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.store.sorters.add(new Ext.util.Sorter({ - sorterFn: me.sorterFn - })); - } -}); -Ext.define('PVE.window.LxcSnapshot', { - extend: 'Ext.window.Window', - - resizable: false, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - defaultFocus: 'field', - - take_snapshot: function(snapname, descr, vmstate) { - var me = this; - var params = { snapname: snapname }; - if (descr) { - params.description = descr; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot", - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - update_snapshot: function(snapname, descr) { - var me = this; - Proxmox.Utils.API2Request({ - params: { description: descr }, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot/" + - snapname + '/config', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var summarystore = Ext.create('Ext.data.Store', { - model: 'KeyValue', - sorters: [ - { - property : 'key', - direction: 'ASC' - } - ] - }); - - var items = [ - { - xtype: me.snapname ? 'displayfield' : 'textfield', - name: 'snapname', - value: me.snapname, - fieldLabel: gettext('Name'), - vtype: 'ConfigId', - allowBlank: false - } - ]; - - if (me.snapname) { - items.push({ - xtype: 'displayfield', - name: 'snaptime', - renderer: PVE.Utils.render_timestamp_human_readable, - fieldLabel: gettext('Timestamp') - }); - } - - items.push({ - xtype: 'textareafield', - grow: true, - name: 'description', - fieldLabel: gettext('Description') - }); - - if (me.snapname) { - items.push({ - title: gettext('Settings'), - xtype: 'grid', - height: 200, - store: summarystore, - columns: [ - {header: gettext('Key'), width: 150, dataIndex: 'key'}, - {header: gettext('Value'), flex: 1, dataIndex: 'value'} - ] - }); - } - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - if (me.snapname) { - me.title = gettext('Edit') + ': ' + gettext('Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Update'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.update_snapshot(me.snapname, values.description); - } - } - }); - } else { - me.title ="VM " + me.vmid + ': ' + gettext('Take Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Take Snapshot'), - reference: 'submitbutton', - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.take_snapshot(values.snapname, values.description); - } - } - }); - } - - Ext.apply(me, { - modal: true, - width: 450, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - if (me.snapname) { - Ext.apply(me, { - width: 620, - height: 420 - }); - } - - me.callParent(); - - if (!me.snapname) { - return; - } - - // else load data - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot/" + - me.snapname + '/config', - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.close(); - }, - success: function(response, options) { - var data = response.result.data; - var kvarray = []; - Ext.Object.each(data, function(key, value) { - if (key === 'description' || key === 'snaptime') { - return; - } - kvarray.push({ key: key, value: value }); - }); - - summarystore.suspendEvents(); - summarystore.add(kvarray); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh', summarystore); - - form.findField('snaptime').setValue(data.snaptime); - form.findField('description').setValue(data.description); - } - }); - } -}); -/*jslint confusion: true */ -var labelWidth = 120; - -Ext.define('PVE.lxc.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('Memory'), - items: Ext.create('PVE.lxc.MemoryInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - - -Ext.define('PVE.lxc.CPUEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('CPU'), - items: Ext.create('PVE.lxc.CPUInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.lxc.CPUInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcCPUInputPanel', - - onlineHelp: 'pct_cpu', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - PVE.Utils.delete_if_default(values, 'cores', '', me.insideWizard); - // cpu{limit,unit} aren't in the wizard so create is always false - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - return values; - }, - - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - value: 1024, - minValue: 8, - maxValue: 500000, - labelWidth: labelWidth, - allowBlank: false - } - ], - - initComponent: function() { - var me = this; - - me.column1 = [ - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: me.insideWizard ? 1 : '', - fieldLabel: gettext('Cores'), - allowBlank: true, - deleteEmpty: true, - emptyText: gettext('unlimited') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcMemoryInputPanel', - - onlineHelp: 'pct_memory', - - insideWizard: false, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxintegerfield', - name: 'memory', - minValue: 16, - value: '512', - step: 32, - fieldLabel: gettext('Memory') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'swap', - minValue: 0, - value: '512', - step: 32, - fieldLabel: gettext('Swap') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); -Ext.define('PVE.window.MPResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 120, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -/*jslint confusion: true*/ -/* hidden: boolean and string - * bind: function and object - * disabled: boolean and string - */ -Ext.define('PVE.lxc.MountPointInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcMountPointInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_storage', - - unused: false, // add unused disk imaged - - unprivileged: false, - - vmconfig: {}, // used to select unused disks - - setUnprivileged: function(unprivileged) { - var me = this; - var vm = me.getViewModel(); - me.unprivileged = unprivileged; - vm.set('unpriv', unprivileged); - }, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || "mp"+values.mpid; - values.file = me.down('field[name=file]').getValue(); - - if (me.unused) { - confid = "mp"+values.mpid; - } else if (me.isCreate) { - values.file = values.hdstorage + ':' + values.disksize; - } - - // delete unnecessary fields - delete values.mpid; - delete values.hdstorage; - delete values.disksize; - delete values.diskformat; - - var res = {}; - res[confid] = PVE.Parser.printLxcMountPoint(values); - return res; - }, - - - setMountPoint: function(mp) { - var me = this; - var vm = this.getViewModel(); - vm.set('mptype', mp.type); - me.setValues(mp); - }, - - setVMConfig: function(vmconfig) { - var me = this; - var vm = me.getViewModel(); - me.vmconfig = vmconfig; - vm.set('unpriv', vmconfig.unprivileged); - vm.notify(); - - PVE.Utils.forEachMP(function(bus, i) { - var name = "mp" + i.toString(); - if (!Ext.isDefined(vmconfig[name])) { - me.down('field[name=mpid]').setValue(i); - return false; - } - }); - }, - - setNodename: function(nodename) { - var me = this; - var vm = me.getViewModel(); - vm.set('node', nodename); - vm.notify(); - me.down('#diskstorage').setNodename(nodename); - }, - - controller: { - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=mpid]': { - change: function(field, value) { - field.validate(); - } - }, - '#hdstorage': { - change: function(field, newValue) { - var me = this; - if (!newValue) { - return; - } - - var rec = field.store.getById(newValue); - if (!rec) { - return; - } - - var vm = me.getViewModel(); - vm.set('type', rec.data.type); - vm.notify(); - } - } - }, - - init: function(view) { - var me = this; - var vm = this.getViewModel(); - vm.set('confid', view.confid); - vm.set('unused', view.unused); - vm.set('node', view.nodename); - vm.set('unpriv', view.unprivileged); - vm.set('hideStorSelector', view.unused || !view.isCreate); - vm.notify(); - } - }, - - viewModel: { - data: { - unpriv: false, - unused: false, - showStorageSelector: false, - mptype: '', - type: '', - confid: '', - node: '' - }, - - formulas: { - quota: function(get) { - return !(get('type') === 'zfs' || - get('type') === 'zfspool' || - get('unpriv') || - get('isBind')); - }, - hasMP: function(get) { - return !!get('confid') && !get('unused'); - }, - isRoot: function(get) { - return get('confid') === 'rootfs'; - }, - isBind: function(get) { - return get('mptype') === 'bind'; - }, - isBindOrRoot: function(get) { - return get('isBind') || get('isRoot'); - } - } - }, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'mpid', - fieldLabel: gettext('Mount Point ID'), - minValue: 0, - maxValue: PVE.Utils.mp_counts.mps - 1, - hidden: true, - allowBlank: false, - disabled: true, - bind: { - hidden: '{hasMP}', - disabled: '{hasMP}' - }, - validator: function(value) { - var me = this.up('inputpanel'); - if (!me.rendered) { - return; - } - if (Ext.isDefined(me.vmconfig["mp"+value])) { - return "Mount point is already in use."; - } - /*jslint confusion: true*/ - /* returns a string above */ - return true; - } - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'diskstorage', - storageContent: 'rootdir', - hidden: true, - autoSelect: true, - selectformat: false, - defaultSize: 8, - bind: { - hidden: '{hideStorSelector}', - disabled: '{hideStorSelector}', - nodename: '{node}' - } - }, - { - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'file', - bind: { - hidden: '{!hideStorSelector}' - } - } - ], - - column2: [ - { - xtype: 'textfield', - name: 'mp', - value: '', - emptyText: gettext('/some/path'), - allowBlank: false, - disabled: true, - fieldLabel: gettext('Path'), - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'backup', - fieldLabel: gettext('Backup'), - bind: { - hidden: '{isRoot}', - disabled: '{isBindOrRoot}' - } - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'quota', - defaultValue: 0, - bind: { - disabled: '{!quota}' - }, - fieldLabel: gettext('Enable quota'), - listeners: { - disable: function() { - this.reset(); - } - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'ro', - defaultValue: 0, - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - }, - fieldLabel: gettext('Read-only') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'acl', - fieldLabel: 'ACLs', - deleteEmpty: false, - comboItems: [ - ['__default__', Proxmox.Utils.defaultText], - ['1', Proxmox.Utils.enabledText], - ['0', Proxmox.Utils.disabledText] - ], - value: '__default__', - bind: { - disabled: '{isBind}' - }, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - inputValue: '0', // reverses the logic - name: 'replicate', - fieldLabel: gettext('Skip replication') - } - ] -}); - -Ext.define('PVE.lxc.MountPointEdit', { - extend: 'Proxmox.window.Edit', - - unprivileged: false, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.lxc.MountPointInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - unprivileged: me.unprivileged, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - subject = gettext('Unused Disk'); - } else if (me.isCreate) { - subject = gettext('Mount Point'); - } else { - subject = gettext('Mount Point') + ' (' + me.confid + ')'; - } - - Ext.apply(me, { - subject: subject, - defaultFocus: me.confid !== 'rootfs' ? 'textfield[name=mp]' : 'tool', - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - /*jslint confusion: true*/ - /*data is defined as array above*/ - var value = response.result.data[me.confid]; - /*jslint confusion: false*/ - var mp = PVE.Parser.parseLxcMountPoint(value); - - if (!mp) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse mount point options'); - me.close(); - return; - } - - ipanel.setMountPoint(mp); - me.isValid(); // trigger validation - } - } - }); - } -}); -Ext.define('PVE.pool.StatusView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pvePoolStatusView'], - disabled: true, - - title: gettext('Status'), - cwidth1: 150, - interval: 30000, - //height: 195, - initComponent : function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var rows = { - comment: { - header: gettext('Comment'), - renderer: Ext.String.htmlEncode, - required: true - } - }; - - Ext.apply(me, { - url: "/api2/json/pools/" + pool, - rows: rows - }); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePoolSummary', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var statusview = Ext.create('PVE.pool.StatusView', { - pveSelNode: me.pveSelNode, - style: 'padding-top:0px' - }); - - var rstore = statusview.rstore; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - defaults: { - style: 'padding-top:10px', - width: 800 - }, - items: [ statusview ] - }); - - me.on('activate', rstore.startUpdate); - me.on('destroy', rstore.stopUpdate); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.pvePoolConfig', - - onlineHelp: 'pveum_pools', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - Ext.apply(me, { - title: Ext.String.format(gettext("Resource Pool") + ': ' + pool), - hstateid: 'pooltab', - items: [ - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - xtype: 'pvePoolSummary', - itemId: 'summary' - }, - { - title: gettext('Members'), - xtype: 'pvePoolMembers', - iconCls: 'fa fa-th', - pool: pool, - itemId: 'members' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/pool/' + pool - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.panel.StorageBase', { - extend: 'Proxmox.panel.InputPanel', - controller: 'storageEdit', - - type: '', - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = me.type; - } else { - delete values.storage; - } - - values.disable = values.enable ? 0 : 1; - delete values.enable; - - return values; - }, - - initComponent : function() { - var me = this; - - me.column1.unshift({ - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'storage', - value: me.storageId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }); - - me.column2.unshift( - { - xtype: 'pveNodeSelector', - name: 'nodes', - disabled: me.storageId === 'local', - fieldLabel: gettext('Nodes'), - emptyText: gettext('All') + ' (' + gettext('No restrictions') +')', - multiSelect: true, - autoSelect: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - me.isCreate = !me.storageId; - - if (me.isCreate) { - me.url = '/api2/extjs/storage'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/storage/' + me.storageId; - me.method = 'PUT'; - } - - var ipanel = Ext.create(me.paneltype, { - type: me.type, - isCreate: me.isCreate, - storageId: me.storageId - }); - - Ext.apply(me, { - subject: PVE.Utils.format_storage_type(me.type), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - var ctypes = values.content || ''; - - values.content = ctypes.split(','); - - if (values.nodes) { - values.nodes = values.nodes.split(','); - } - values.enable = values.disable ? 0 : 1; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.grid.TemplateSelector', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveTemplateSelector', - - stateful: true, - stateId: 'grid-template-selector', - viewConfig: { - trackOver: false - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/aplinfo"; - var store = new Ext.data.Store({ - model: 'pve-aplinfo', - groupField: 'section', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{[ "Section: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - '->', - gettext('Search'), - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - var value = field.getValue().toLowerCase(); - store.clearFilter(true); - store.filterBy(function(rec) { - return (rec.data['package'].toLowerCase().indexOf(value) !== -1) - || (rec.data.headline.toLowerCase().indexOf(value) !== -1); - }); - } - } - } - ], - features: [ groupingFeature ], - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Package'), - flex: 1, - dataIndex: 'package' - }, - { - header: gettext('Version'), - width: 80, - dataIndex: 'version' - }, - { - header: gettext('Description'), - flex: 1.5, - renderer: Ext.String.htmlEncode, - dataIndex: 'headline' - } - ], - listeners: { - afterRender: reload - } - }); - - me.callParent(); - } - -}, function() { - - Ext.define('pve-aplinfo', { - extend: 'Ext.data.Model', - fields: [ - 'template', 'type', 'package', 'version', 'headline', 'infopage', - 'description', 'os', 'section' - ], - idProperty: 'template' - }); - -}); - -Ext.define('PVE.storage.TemplateDownload', { - extend: 'Ext.window.Window', - alias: 'widget.pveTemplateDownload', - - modal: true, - title: gettext('Templates'), - layout: 'fit', - width: 900, - height: 600, - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var grid = Ext.create('PVE.grid.TemplateSelector', { - border: false, - scrollable: true, - nodename: me.nodename - }); - - var sm = grid.getSelectionModel(); - - var submitBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Download'), - disabled: true, - selModel: sm, - handler: function(button, event, rec) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/aplinfo', - params: { - storage: me.storage, - template: rec.data.template - }, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - destroy: me.reloadGrid - } - }).show(); - - me.close(); - } - }); - } - }); - - Ext.apply(me, { - items: grid, - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.Upload', { - extend: 'Ext.window.Window', - alias: 'widget.pveStorageUpload', - - resizable: false, - - modal: true, - - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var xhr; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/storage/" + me.storage + "/upload"; - - var pbar = Ext.create('Ext.ProgressBar', { - text: 'Ready', - hidden: true - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - method: 'POST', - waitMsgTarget: true, - bodyPadding: 10, - border: false, - width: 300, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - xtype: 'pveContentTypeSelector', - cts: me.contents, - fieldLabel: gettext('Content'), - name: 'content', - value: me.contents[0] || '', - allowBlank: false - }, - { - xtype: 'filefield', - name: 'filename', - buttonText: gettext('Select File...'), - allowBlank: false - }, - pbar - ] - }); - - var form = me.formPanel.getForm(); - - var doStandardSubmit = function() { - form.submit({ - url: "/api2/htmljs" + baseurl, - waitMsg: gettext('Uploading file...'), - success: function(f, action) { - me.close(); - }, - failure: function(f, action) { - var msg = PVE.Utils.extractFormActionError(action); - Ext.Msg.alert(gettext('Error'), msg); - } - }); - }; - - var updateProgress = function(per, bytes) { - var text = (per * 100).toFixed(2) + '%'; - if (bytes) { - text += " (" + Proxmox.Utils.format_size(bytes) + ')'; - } - pbar.updateProgress(per, text); - }; - - var abortBtn = Ext.create('Ext.Button', { - text: gettext('Abort'), - disabled: true, - handler: function() { - me.close(); - } - }); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Upload'), - disabled: true, - handler: function(button) { - var fd; - try { - fd = new FormData(); - } catch (err) { - doStandardSubmit(); - return; - } - - button.setDisabled(true); - abortBtn.setDisabled(false); - - var field = form.findField('content'); - fd.append("content", field.getValue()); - field.setDisabled(true); - - field = form.findField('filename'); - var file = field.fileInputEl.dom; - fd.append("filename", file.files[0]); - field.setDisabled(true); - - pbar.setVisible(true); - updateProgress(0); - - xhr = new XMLHttpRequest(); - - xhr.addEventListener("load", function(e) { - if (xhr.status == 200) { - me.close(); - } else { - var msg = gettext('Error') + " " + xhr.status.toString() + ": " + Ext.htmlEncode(xhr.statusText); - var result = Ext.decode(xhr.responseText); - result.message = msg; - var htmlStatus = Proxmox.Utils.extractRequestError(result, true); - Ext.Msg.alert(gettext('Error'), htmlStatus, function(btn) { - me.close(); - }); - - } - }, false); - - xhr.addEventListener("error", function(e) { - var msg = "Error " + e.target.status.toString() + " occurred while receiving the document."; - Ext.Msg.alert(gettext('Error'), msg, function(btn) { - me.close(); - }); - }); - - xhr.upload.addEventListener("progress", function(evt) { - if (evt.lengthComputable) { - var percentComplete = evt.loaded / evt.total; - updateProgress(percentComplete, evt.loaded); - } - }, false); - - xhr.open("POST", "/api2/json" + baseurl, true); - xhr.send(fd); - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - Ext.apply(me, { - title: gettext('Upload'), - items: me.formPanel, - buttons: [ abortBtn, submitBtn ], - listeners: { - close: function() { - if (xhr) { - xhr.abort(); - } - } - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ContentView', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveStorageContentView', - - stateful: true, - stateId: 'grid-storage-content', - viewConfig: { - trackOver: false, - loadMask: false - }, - features: [ - { - ftype: 'grouping', - groupHeaderTpl: '{name} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - } - ], - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + nodename + "/storage/" + storage + "/content"; - var store = Ext.create('Ext.data.Store',{ - model: 'pve-storage-content', - groupField: 'content', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - }, - sorters: { - property: 'volid', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - me.statusStore.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var templateButton = Ext.create('Proxmox.button.Button',{ - itemId: 'tmpl-btn', - text: gettext('Templates'), - handler: function() { - var win = Ext.create('PVE.storage.TemplateDownload', { - nodename: nodename, - storage: storage, - reloadGrid: reload - }); - win.show(); - } - }); - - var uploadButton = Ext.create('Proxmox.button.Button', { - contents : ['iso','vztmpl'], - text: gettext('Upload'), - handler: function() { - var me = this; - var win = Ext.create('PVE.storage.Upload', { - nodename: nodename, - storage: storage, - contents: me.contents - }); - win.show(); - win.on('destroy', reload); - } - }); - - var imageRemoveButton; - var removeButton = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - enableFn: function(rec) { - if (rec && rec.data.content !== 'images') { - imageRemoveButton.setVisible(false); - removeButton.setVisible(true); - return true; - } - return false; - }, - callback: function() { - reload(); - }, - baseurl: baseurl + '/' - }); - - imageRemoveButton = Ext.create('Proxmox.button.Button',{ - selModel: sm, - hidden: true, - text: gettext('Remove'), - enableFn: function(rec) { - if (rec && rec.data.content === 'images') { - removeButton.setVisible(false); - imageRemoveButton.setVisible(true); - return true; - } - return false; - }, - handler: function(btn, event, rec) { - me = this; - - var url = baseurl + '/' + rec.data.volid; - var vmid = rec.data.vmid; - - var store = PVE.data.ResourceStore; - - if (vmid && store.findVMID(vmid)) { - var guest_node = store.guestNode(vmid); - var storage_path = 'storage/' + nodename + '/' + storage; - - // allow to delete local backed images if a VMID exists on another node. - if (store.storageIsShared(storage_path) || guest_node == nodename) { - var msg = Ext.String.format( - gettext("Cannot remove image, a guest with VMID '{0}' exists!"), vmid); - msg += '
' + gettext("You can delete the image from the guest's hardware pane"); - - Ext.Msg.show({ - title: gettext('Cannot remove disk image.'), - icon: Ext.Msg.ERROR, - msg: msg - }); - return; - } - } - var win = Ext.create('PVE.window.SafeDestroy', { - title: Ext.String.format(gettext("Destroy '{0}'"), rec.data.volid), - showProgress: true, - url: url, - item: { type: 'Image', id: vmid } - }).show(); - win.on('destroy', function() { - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - reload(); - - }); - } - }); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - xtype: 'proxmoxButton', - text: gettext('Restore'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b, e, rec) { - var vmtype; - if (rec.data.volid.match(/vzdump-qemu-/)) { - vmtype = 'qemu'; - } else if (rec.data.volid.match(/vzdump-openvz-/) || rec.data.volid.match(/vzdump-lxc-/)) { - vmtype = 'lxc'; - } else { - return; - } - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }, - removeButton, - imageRemoveButton, - templateButton, - uploadButton, - { - xtype: 'proxmoxButton', - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b,e,rec) { - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }, - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - store.clearFilter(true); - store.filter([ - { - property: 'text', - value: field.getValue(), - anyMatch: true, - caseSensitive: false - } - ]); - } - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'text' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ], - listeners: { - activate: reload - } - }); - - me.callParent(); - - // disable the buttons/restrict the upload window - // if templates or uploads are not allowed - me.mon(me.statusStore, 'load', function(s,records,succes) { - var availcontent = []; - Ext.Array.each(records, function(item){ - if (item.id === 'content') { - availcontent = item.data.value.split(','); - } - }); - var templ = false; - var upload = false; - var cts = []; - - Ext.Array.each(availcontent, function(content) { - if (content === 'vztmpl') { - templ = true; - cts.push('vztmpl'); - } else if (content === 'iso') { - upload = true; - cts.push('iso'); - } - }); - - if (templ !== upload) { - uploadButton.contents = cts; - } - - templateButton.setDisabled(!templ); - uploadButton.setDisabled(!upload && !templ); - }); - } -}, function() { - - Ext.define('pve-storage-content', { - extend: 'Ext.data.Model', - fields: [ - 'volid', 'content', 'format', 'size', 'used', 'vmid', - 'channel', 'id', 'lun', - { - name: 'text', - convert: function(value, record) { - // check for volid, because if you click on a grouping header, - // it calls convert (but with an empty volid) - if (value || record.data.volid === null) { - return value; - } - return PVE.Utils.render_storage_content(value, {}, record); - } - } - ], - idProperty: 'volid' - }); - -}); -Ext.define('PVE.storage.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveStorageStatusView', - - height: 230, - title: gettext('Status'), - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 30 5 30' - }, - items: [ - { - xtype: 'box', - height: 30 - }, - { - itemId: 'enabled', - title: gettext('Enabled'), - printBar: false, - textField: 'disabled', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - itemId: 'active', - title: gettext('Active'), - printBar: false, - textField: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - itemId: 'content', - title: gettext('Content'), - printBar: false, - textField: 'content', - renderer: PVE.Utils.format_content_types - }, - { - itemId: 'type', - title: gettext('Type'), - printBar: false, - textField: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - xtype: 'box', - height: 10 - }, - { - itemId: 'usage', - title: gettext('Usage'), - valueField: 'used', - maxField: 'total' - } - ], - - updateTitle: function() { - return; - } -}); -Ext.define('PVE.storage.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStorageSummary', - scrollable: true, - bodyPadding: 5, - tbar: [ - '->', - { - xtype: 'proxmoxRRDTypeSelector' - } - ], - layout: { - type: 'column' - }, - defaults: { - padding: 5, - columnWidth: 1 - }, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var rstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/status", - interval: 1000 - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/rrddata", - model: 'pve-rrd-storage' - }); - - Ext.apply(me, { - items: [ - { - xtype: 'pveStorageStatusView', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Usage'), - fields: ['total','used'], - fieldTitles: ['Total Size', 'Used Size'], - store: rrdstore - } - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.storage.Browser', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.storage.Browser', - - onlineHelp: 'chapter_storage', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storeid = me.pveSelNode.data.storage; - if (!storeid) { - throw "no storage ID specified"; - } - - - me.items = [ - { - title: gettext('Summary'), - xtype: 'pveStorageSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ]; - - var caps = Ext.state.Manager.get('GuiCap'); - - Ext.apply(me, { - title: Ext.String.format(gettext("Storage {0} on node {1}"), - "'" + storeid + "'", "'" + nodename + "'"), - hstateid: 'storagetab' - }); - - if (caps.storage['Datastore.Allocate'] || - caps.storage['Datastore.AllocateSpace'] || - caps.storage['Datastore.Audit']) { - me.items.push({ - xtype: 'pveStorageContentView', - title: gettext('Content'), - iconCls: 'fa fa-th', - itemId: 'content' - }); - } - - if (caps.storage['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/storage/' + storeid - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.storage.DirInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_directory', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'path', - value: '', - fieldLabel: gettext('Directory'), - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.NFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveNFSScan', - - queryParam: 'server', - - valueField: 'path', - displayField: 'path', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.nfsServer) { - me.store.removeAll(); - } - - me.allQuery = me.nfsServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.nfsServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'path', 'options' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/nfs' - } - }); - - store.sort('path', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.NFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_nfs', - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - // hack: for now we always create nvf v3 - // fixme: make this configurable - values.options = 'vers=3'; - } - - return me.callParent([values]); - }, - - initComponent : function() { - var me = this; - - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=export]'); - exportField.setServer(value); - exportField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'pveNFSScan' : 'displayfield', - name: 'export', - value: '', - fieldLabel: 'Export', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.CIFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCIFSScan', - - queryParam: 'server', - - valueField: 'share', - displayField: 'share', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.cifsServer) { - me.store.removeAll(); - } - - var params = {}; - if (me.cifsUsername && me.cifsPassword) { - params.username = me.cifsUsername; - params.password = me.cifsPassword; - } - - if (me.cifsDomain) { - params.domain = me.cifsDomain; - } - - me.store.getProxy().setExtraParams(params); - me.allQuery = me.cifsServer; - - me.callParent(); - }, - - setServer: function(server) { - this.cifsServer = server; - }, - - setUsername: function(username) { - this.cifsUsername = username; - }, - - setPassword: function(password) { - this.cifsPassword = password; - }, - - setDomain: function(domain) { - this.cifsDomain = domain; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['description', 'share'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/cifs' - } - }); - store.sort('share', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.CIFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_cifs', - - initComponent : function() { - var me = this; - - var passwordfield = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - inputType: 'password', - name: 'password', - value: me.isCreate ? '' : '********', - fieldLabel: gettext('Password'), - allowBlank: false, - disabled: me.isCreate, - minLength: 1, - listeners: { - change: function(f, value) { - - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setPassword(value); - } - } - } - }); - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setServer(value); - } - } - } - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: '', - fieldLabel: gettext('Username'), - emptyText: gettext('Guest user'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (!me.isCreate) { - return; - } - var exportField = me.down('field[name=share]'); - exportField.setUsername(value); - - if (value == "") { - passwordfield.disable(); - } else { - passwordfield.enable(); - } - passwordfield.validate(); - } - } - }, - passwordfield, - { - xtype: me.isCreate ? 'pveCIFSScan' : 'displayfield', - name: 'share', - value: '', - fieldLabel: 'Share', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'domain', - value: me.isCreate ? '' : undefined, - fieldLabel: gettext('Domain'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (me.isCreate) { - - var exportField = me.down('field[name=share]'); - exportField.setDomain(value); - } - } - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.GlusterFsScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveGlusterFsScan', - - queryParam: 'server', - - valueField: 'volname', - displayField: 'volname', - matchFieldWidth: false, - listConfig: { - loadingText: 'Scanning...', - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.glusterServer) { - me.store.removeAll(); - } - - me.allQuery = me.glusterServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.glusterServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'volname' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/glusterfs' - } - }); - - store.sort('volname', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.GlusterFsInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_glusterfs', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var volumeField = me.down('field[name=volume]'); - volumeField.setServer(value); - volumeField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'server2', - value: '', - fieldLabel: gettext('Second Server'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'pveGlusterFsScan' : 'displayfield', - name: 'volume', - value: '', - fieldLabel: 'Volume name', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'iso', 'backup', 'vztmpl', 'snippets'], - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.IScsiScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveIScsiScan', - - queryParam: 'portal', - valueField: 'target', - displayField: 'target', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.portal) { - me.store.removeAll(); - } - - me.allQuery = me.portal; - - me.callParent(); - }, - - setPortal: function(portal) { - var me = this; - - me.portal = portal; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'target', 'portal' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/iscsi' - } - }); - - store.sort('target', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.IScsiInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_open_iscsi', - - onGetValues: function(values) { - var me = this; - - values.content = values.luns ? 'images' : 'none'; - delete values.luns; - - return me.callParent([values]); - }, - - setValues: function(values) { - values.luns = (values.content.indexOf('images') !== -1) ? true : false; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: 'Portal', - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=target]'); - exportField.setPortal(value); - exportField.setValue(''); - } - } - } - }, - { - readOnly: !me.isCreate, - xtype: me.isCreate ? 'pveIScsiScan' : 'displayfield', - name: 'target', - value: '', - fieldLabel: 'Target', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'checkbox', - name: 'luns', - checked: true, - fieldLabel: gettext('Use LUNs directly') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.VgSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveVgSelector', - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'vg', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - store.sort('vg', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseStorageSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseStorageSelector', - - existingGroupsText: gettext("Existing volume groups"), - queryMode: 'local', - editable: false, - value: '', - valueField: 'storage', - displayField: 'text', - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: { - addRecords: true, - params: { - type: 'iscsi' - } - }, - fields: [ 'storage', 'type', 'content', - { - name: 'text', - convert: function(value, record) { - if (record.data.storage) { - return record.data.storage + " (iSCSI)"; - } else { - return me.existingGroupsText; - } - } - }], - proxy: { - type: 'proxmox', - url: '/api2/json/storage/' - } - }); - - store.loadData([{ storage: '' }], true); - - store.sort('storage', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LVMInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvm', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.VgSelector', { - name: 'vgname', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var baseField = Ext.createWidget('pveFileSelector', { - name: 'base', - hidden: true, - disabled: true, - nodename: 'localhost', - storageContent: 'images', - fieldLabel: gettext('Base volume'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseStorageSelector', - name: 'basesel', - fieldLabel: gettext('Base storage'), - submitValue: false, - listeners: { - change: function(f, value) { - if (value) { - vgnameField.setVisible(true); - vgnameField.setDisabled(false); - vgField.setVisible(false); - vgField.setDisabled(true); - baseField.setVisible(true); - baseField.setDisabled(false); - } else { - vgnameField.setVisible(false); - vgnameField.setDisabled(true); - vgField.setVisible(true); - vgField.setDisabled(false); - baseField.setVisible(false); - baseField.setDisabled(true); - } - baseField.setStorage(value); - } - } - }); - - me.column1.push(baseField); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.TPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveTPSelector', - - queryParam: 'vg', - valueField: 'lv', - displayField: 'lv', - editable: false, - - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.vg) { - me.store.removeAll(); - } - - me.allQuery = me.vg; - - me.callParent(); - }, - - setVG: function(myvg) { - var me = this; - - me.vg = myvg; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'lv' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvmthin' - } - }); - - store.sort('lv', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseVGSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseVGSelector', - - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, - fields: [ 'vg', 'size', 'free'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LvmThinInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvmthin', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var thinpoolField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'thinpool', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.TPoolSelector', { - name: 'thinpool', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseVGSelector', - name: 'vgname', - fieldLabel: gettext('Volume group'), - listeners: { - change: function(f, value) { - if (me.isCreate) { - vgField.setVG(value); - vgField.setValue(''); - } - } - } - }); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - me.column1.push(thinpoolField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = []; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.CephFSInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'storage_cephfs', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'cephfs'; - - me.column1 = []; - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - value: '', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: 'admin', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['backup', 'iso', 'vztmpl', 'snippets'], - fieldLabel: gettext('Content'), - name: 'content', - value: 'backup', - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged cephFS') - }]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.Ceph.Model', { - extend: 'Ext.app.ViewModel', - alias: 'viewmodel.cephstorage', - - data: { - pveceph: true, - pvecephPossible: true - } -}); - -Ext.define('PVE.storage.Ceph.Controller', { - extend: 'PVE.controller.StorageEdit', - alias: 'controller.cephstorage', - - control: { - '#': { - afterrender: 'queryMonitors' - }, - 'textfield[name=username]': { - disable: 'resetField' - }, - 'displayfield[name=monhost]': { - enable: 'queryMonitors' - }, - 'textfield[name=monhost]': { - disable: 'resetField', - enable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - queryMonitors: function(field, newVal, oldVal) { - // we get called with two signatures, the above one for a field - // change event and the afterrender from the view, this check only - // can be true for the field change one and omit the API request if - // pveceph got unchecked - as it's not needed there. - if (field && !newVal && oldVal) { - return; - } - var view = this.getView(); - var vm = this.getViewModel(); - if (!(view.isCreate || vm.get('pveceph'))) { - return; // only query on create or if editing a pveceph store - } - - var monhostField = this.lookupReference('monhost'); - - Proxmox.Utils.API2Request({ - url: '/api2/json/nodes/localhost/ceph/mon', - method: 'GET', - scope: this, - callback: function(options, success, response) { - var data = response.result.data; - if (response.status === 200) { - if (data.length > 0) { - var monhost = Ext.Array.pluck(data, 'name').sort().join(','); - monhostField.setValue(monhost); - monhostField.resetOriginalValue(); - if (view.isCreate) { - vm.set('pvecephPossible', true); - } - } else { - vm.set('pveceph', false); - } - } else { - vm.set('pveceph', false); - vm.set('pvecephPossible', false); - } - } - }); - } -}); - -Ext.define('PVE.storage.RBDInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'ceph_rados_block_devices', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'rbd'; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push({ - xtype: 'pveCephPoolSelector', - nodename: me.nodename, - name: 'pool', - bind: { - disabled: '{!pveceph}', - submitValue: '{pveceph}', - hidden: '{!pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - },{ - xtype: 'textfield', - name: 'pool', - value: 'rbd', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } else { - me.column1.push({ - xtype: 'displayfield', - nodename: me.nodename, - name: 'pool', - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - value: 'admin', - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images'], - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'krbd', - uncheckedValue: 0, - fieldLabel: 'KRBD' - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged ceph pool') - }]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.SheepdogInputPanel', { - extend: 'PVE.panel.StorageBase', - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.content = 'images'; - } - - return me.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '127.0.0.1:7000', - fieldLabel: gettext('Gateway'), - allowBlank: false - } - ]; - me.column2 = []; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.ZFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - viewModel: { - parent: null, - data: { - isLIO: false, - isComstar: true, - hasWriteCacheOption: true - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[name=iscsiprovider]': { - change: 'changeISCSIProvider' - } - }, - changeISCSIProvider: function(f, newVal, oldVal) { - var vm = this.getViewModel(); - vm.set('isLIO', newVal === 'LIO'); - vm.set('isComstar', newVal === 'comstar'); - vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt'); - } - }, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.content = 'images'; - } - - values.nowritecache = values.writecache ? 0 : 1; - delete values.writecache; - - return me.callParent([values]); - }, - - setValues: function diff(values) { - values.writecache = values.nowritecache ? 0 : 1; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: gettext('Portal'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'pool', - value: '', - fieldLabel: gettext('Pool'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'blocksize', - value: '4k', - fieldLabel: gettext('Block Size'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'target', - value: '', - fieldLabel: gettext('Target'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_tg', - value: '', - fieldLabel: gettext('Target group'), - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - allowBlank: true - } - ]; - - me.column2 = [ - { - xtype: me.isCreate ? 'pveiScsiProviderSelector' : 'displayfield', - name: 'iscsiprovider', - value: 'comstar', - fieldLabel: gettext('iSCSI Provider'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'proxmoxcheckbox', - name: 'writecache', - checked: true, - bind: me.isCreate ? { disabled: '{!hasWriteCacheOption}' } : { hidden: '{!hasWriteCacheOption}' }, - uncheckedValue: 0, - fieldLabel: gettext('Write cache') - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_hg', - value: '', - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - fieldLabel: gettext('Host group'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'lio_tpg', - value: '', - bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' }, - allowBlank: false, - fieldLabel: gettext('Target portal group') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.ZFSPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveZFSPoolSelector', - valueField: 'pool', - displayField: 'pool', - queryMode: 'local', - editable: false, - listConfig: { - loadingText: gettext('Scanning...') - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'pool', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/zfs' - } - }); - - store.sort('pool', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ZFSPoolInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_zfspool', - - initComponent : function() { - var me = this; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push(Ext.create('PVE.storage.ZFSPoolSelector', { - name: 'pool', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } else { - me.column1.push(Ext.createWidget('displayfield', { - name: 'pool', - value: '', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } - - // value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push( - {xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'textfield', - name: 'blocksize', - emptyText: '8k', - fieldLabel: gettext('Block Size'), - allowBlank: true - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.ha.StatusView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAStatusView'], - - onlineHelp: 'chapter_ha_manager', - - sortPriority: { - quorum: 1, - master: 2, - lrm: 3, - service: 4 - }, - - initComponent : function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sortAfterUpdate: true, - sorters: [{ - sorterFn: function(rec1, rec2) { - var p1 = me.sortPriority[rec1.data.type]; - var p2 = me.sortPriority[rec2.data.type]; - return (p1 !== p2) ? ((p1 > p2) ? 1 : -1) : 0; - } - }], - filters: { - property: 'type', - value: 'service', - operator: '!=' - } - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Status'), - width: 80, - flex: 1, - dataIndex: 'status' - } - ] - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - } -}, function() { - - Ext.define('pve-ha-status', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'type', 'node', 'status', 'sid', - 'state', 'group', 'comment', - 'max_restart', 'max_relocate', 'type', - 'crm_state', 'request_state' - ], - idProperty: 'id' - }); - -}); -Ext.define('PVE.ha.Status', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveHAStatus', - - onlineHelp: 'chapter_ha_manager', - layout: { - type: 'vbox', - align: 'stretch' - }, - - initComponent: function() { - var me = this; - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - interval: me.interval, - model: 'pve-ha-status', - storeid: 'pve-store-' + (++Ext.idSeed), - groupField: 'type', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/ha/status/current' - } - }); - - me.items = [{ - xtype: 'pveHAStatusView', - title: gettext('Status'), - rstore: me.rstore, - border: 0, - collapsible: true, - padding: '0 0 20 0' - },{ - xtype: 'pveHAResourcesView', - flex: 1, - collapsible: true, - title: gettext('Resources'), - border: 0, - rstore: me.rstore - }]; - - me.callParent(); - me.on('activate', me.rstore.startUpdate); - } -}); -Ext.define('PVE.ha.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveHAGroupSelector'], - - value: [], - autoSelect: false, - valueField: 'group', - displayField: 'group', - listConfig: { - columns: [ - { - header: gettext('Group'), - width: 100, - sortable: true, - dataIndex: 'group' - }, - { - header: gettext('Nodes'), - width: 100, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode - } - ] - }, - store: { - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }, - - initComponent: function() { - var me = this; - me.callParent(); - me.getStore().load(); - } - -}, function() { - - Ext.define('pve-ha-groups', { - extend: 'Ext.data.Model', - fields: [ - 'group', 'type', 'digest', 'nodes', 'comment', - { - name : 'restricted', - type: 'boolean' - }, - { - name : 'nofailback', - type: 'boolean' - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/ha/groups" - }, - idProperty: 'group' - }); -}); -Ext.define('PVE.ha.VMResourceInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_resource_config', - vmid: undefined, - - onGetValues: function(values) { - var me = this; - - if (values.vmid) { - values.sid = values.vmid; - } - delete values.vmid; - - PVE.Utils.delete_if_default(values, 'group', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_restart', '1', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_relocate', '1', me.isCreate); - - return values; - }, - - initComponent : function() { - var me = this; - var MIN_QUORUM_VOTES = 3; - - var disabledHint = Ext.createWidget({ - xtype: 'displayfield', // won't get submitted by default - userCls: 'pve-hint', - value: 'Disabling the resource will stop the guest system. ' + - 'See the online help for details.', - hidden: true - }); - - var fewVotesHint = Ext.createWidget({ - itemId: 'fewVotesHint', - xtype: 'displayfield', - userCls: 'pve-hint', - value: 'At least three quorum votes are recommended for reliable HA.', - hidden: true - }); - - Proxmox.Utils.API2Request({ - url: '/cluster/config/nodes', - method: 'GET', - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var nodes = response.result.data; - var votes = 0; - Ext.Array.forEach(nodes, function(node) { - var vote = parseInt(node.quorum_votes, 10); // parse as base 10 - votes += vote || 0; // parseInt might return NaN, which is false - }); - - if (votes < MIN_QUORUM_VOTES) { - fewVotesHint.setVisible(true); - } - } - }); - - /*jslint confusion: true */ - var vmidStore = (me.vmid) ? {} : { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [ - { - property: 'type', - value: /lxc|qemu/ - }, - { - property: 'hastate', - value: /unmanaged/ - } - ] - }; - - // value is a string above, but a number below - me.column1 = [ - { - xtype: me.vmid ? 'displayfield' : 'vmComboSelector', - submitValue: me.isCreate, - name: 'vmid', - fieldLabel: (me.vmid && me.guestType === 'ct') ? 'CT' : 'VM', - value: me.vmid, - store: vmidStore, - validateExists: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_restart', - fieldLabel: gettext('Max. Restart'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_relocate', - fieldLabel: gettext('Max. Relocate'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - } - ]; - /*jslint confusion: false */ - - me.column2 = [ - { - xtype: 'pveHAGroupSelector', - name: 'group', - fieldLabel: gettext('Group') - }, - { - xtype: 'proxmoxKVComboBox', - name: 'state', - value: 'started', - fieldLabel: gettext('Request State'), - comboItems: [ - ['started', 'started'], - ['stopped', 'stopped'], - ['ignored', 'ignored'], - ['disabled', 'disabled'] - ], - listeners: { - 'change': function(field, newValue) { - if (newValue === 'disabled') { - disabledHint.setVisible(true); - } - else { - if (disabledHint.isVisible()) { - disabledHint.setVisible(false); - } - } - } - } - }, - disabledHint - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - fewVotesHint - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.VMResourceEdit', { - extend: 'Proxmox.window.Edit', - - vmid: undefined, - guestType: undefined, - isCreate: undefined, - - initComponent : function() { - var me = this; - - if (me.isCreate === undefined) { - me.isCreate = !me.vmid; - } - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/resources'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/resources/' + me.vmid; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.VMResourceInputPanel', { - isCreate: me.isCreate, - vmid: me.vmid, - guestType: me.guestType - }); - - Ext.apply(me, { - subject: gettext('Resource') + ': ' + gettext('Container') + - '/' + gettext('Virtual Machine'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(values.sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - throw "got unexpected resource type"; - } - - values.vmid = res[2]; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.ResourcesView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAResourcesView'], - - onlineHelp: 'ha_manager_resources', - - stateful: true, - stateId: 'grid-ha-resources', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!me.rstore) { - throw "no store given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - filters: { - property: 'type', - value: 'service' - } - }); - - var reload = function() { - me.rstore.load(); - }; - - var render_error = function(dataIndex, value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors[dataIndex]; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - var sid = rec.data.sid; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - return; - } - var guestType = res[1]; - var vmid = res[2]; - - var win = Ext.create('PVE.ha.VMResourceEdit',{ - guestType: guestType, - vmid: vmid - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/resources/', - getUrl: function(rec) { - var me = this; - return me.baseurl + '/' + rec.get('sid'); - }, - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.VMResourceEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - - columns: [ - { - header: 'ID', - width: 100, - sortable: true, - dataIndex: 'sid' - }, - { - header: gettext('State'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Request State'), - width: 100, - hidden: true, - sortable: true, - renderer: function(v) { - return v || 'started'; - }, - dataIndex: 'request_state' - }, - { - header: gettext('CRM State'), - width: 100, - hidden: true, - sortable: true, - dataIndex: 'crm_state' - }, - { - header: gettext('Max. Restart'), - width: 100, - sortable: true, - renderer: function(v) { - return v || '1'; - }, - dataIndex: 'max_restart' - }, - { - header: gettext('Max. Relocate'), - width: 100, - sortable: true, - renderer: function(v) { - return v || '1'; - }, - dataIndex: 'max_relocate' - }, - { - header: gettext('Group'), - width: 200, - sortable: true, - renderer: function(value, metaData, record) { - return render_error('group', value, metaData, record); - }, - dataIndex: 'group' - }, - { - header: gettext('Description'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-ha-resources', { - extend: 'Ext.data.Model', - fields: [ - 'sid', 'state', 'digest', 'errors', 'group', 'comment', - 'max_restart', 'max_relocate', 'type', 'status', 'node', - 'crm_state', 'request_state' - ], - idProperty: 'sid' - }); - -}); -Ext.define('PVE.ha.GroupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_groups', - - groupId: undefined, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = 'group'; - } - - return values; - }, - - initComponent : function() { - var me = this; - - var update_nodefield, update_node_selection; - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - update_nodefield(selected); - } - } - }); - - // use already cached data to avoid an API call - var data = PVE.data.ResourceStore.getNodes(); - - var store = Ext.create('Ext.data.Store', { - fields: [ 'node', 'mem', 'cpu', 'priority' ], - data: data, - proxy: { - type: 'memory', - reader: {type: 'json'} - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - } - ] - }); - - var nodegrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - columns: [ - { - header: gettext('Node'), - flex: 1, - dataIndex: 'node' - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 150, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 150, - dataIndex: 'cpu' - }, - { - header: 'Priority', - xtype: 'widgetcolumn', - dataIndex: 'priority', - sortable: true, - stopSelection: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 0, - maxValue: 1000, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('priority', value); - update_nodefield(sm.getSelection()); - } - } - } - } - ] - }); - - var nodefield = Ext.create('Ext.form.field.Hidden', { - name: 'nodes', - value: '', - listeners: { - change: function (nodefield, value) { - update_node_selection(value); - } - }, - isValid: function () { - var value = nodefield.getValue(); - return (value && 0 !== value.length); - } - }); - - update_node_selection = function(string) { - sm.deselectAll(true); - - string.split(',').forEach(function (e, idx, array) { - var res = e.split(':'); - - store.each(function(record) { - var node = record.get('node'); - - if (node == res[0]) { - sm.select(record, true); - record.set('priority', res[1]); - record.commit(); - } - }); - }); - nodegrid.reconfigure(store); - - }; - - update_nodefield = function(selected) { - var nodes = ''; - var first_iteration = true; - Ext.Array.each(selected, function(record) { - if (!first_iteration) { - nodes += ','; - } - first_iteration = false; - - nodes += record.data.node; - if (record.data.priority) { - nodes += ':' + record.data.priority; - } - }); - - // nodefield change listener calls us again, which results in a - // endless recursion, suspend the event temporary to avoid this - nodefield.suspendEvent('change'); - nodefield.setValue(nodes); - nodefield.resumeEvent('change'); - }; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'group', - value: me.groupId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }, - nodefield - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'restricted', - uncheckedValue: 0, - fieldLabel: 'restricted' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nofailback', - uncheckedValue: 0, - fieldLabel: 'nofailback' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - nodegrid - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.GroupEdit', { - extend: 'Proxmox.window.Edit', - - groupId: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupId; - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/groups'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/groups/' + me.groupId; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.GroupInputPanel', { - isCreate: me.isCreate, - groupId: me.groupId - }); - - Ext.apply(me, { - subject: gettext('HA Group'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.GroupsView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAGroupsView'], - - onlineHelp: 'ha_manager_groups', - - stateful: true, - stateId: 'grid-ha-groups', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.ha.GroupEdit',{ - groupId: rec.data.group - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/groups/', - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Create'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.GroupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - columns: [ - { - header: gettext('Group'), - width: 150, - sortable: true, - dataIndex: 'group' - }, - { - header: 'restricted', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'restricted' - }, - { - header: 'nofailback', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'nofailback' - }, - { - header: gettext('Nodes'), - flex: 1, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - activate: reload, - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.ha.FencingView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveFencingView'], - - onlineHelp: 'ha_manager_fencing', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-ha-fencing', - data: [] - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false, - deferEmptyText: false, - emptyText: 'Use watchdog based fencing.' - }, - columns: [ - { - header: 'Node', - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Command'), - flex: 1, - dataIndex: 'command' - } - ] - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-ha-fencing', { - extend: 'Ext.data.Model', - fields: [ - 'node', 'command', 'digest' - ] - }); - -}); -Ext.define('PVE.dc.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSummary', - - scrollable: true, - - bodyPadding: 5, - - layout: 'column', - - defaults: { - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - - items: [ - { - itemId: 'dcHealth', - xtype: 'pveDcHealth' - }, - { - itemId: 'dcGuests', - xtype: 'pveDcGuests' - }, - { - title: gettext('Resources'), - xtype: 'panel', - minHeight: 250, - bodyPadding: 5, - layout: 'hbox', - defaults: { - xtype: 'proxmoxGauge', - flex: 1 - }, - items:[ - { - title: gettext('CPU'), - itemId: 'cpu' - }, - { - title: gettext('Memory'), - itemId: 'memory' - }, - { - title: gettext('Storage'), - itemId: 'storage' - } - ] - }, - { - itemId: 'nodeview', - xtype: 'pveDcNodeView', - height: 250 - }, - { - title: gettext('Subscriptions'), - height: 220, - items: [ - { - itemId: 'subscriptions', - xtype: 'pveHealthWidget', - userCls: 'pointer', - listeners: { - element: 'el', - click: function() { - if (this.component.userCls === 'pointer') { - window.open('https://www.proxmox.com/en/proxmox-ve/pricing', '_blank'); - } - } - } - } - ] - } - ], - - initComponent: function() { - var me = this; - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-status', - model: 'pve-dc-nodes', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/status" - } - }); - - var gridstore = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - filters: { - property: 'type', - value: 'node' - }, - sorters: { - property: 'id', - direction: 'ASC' - } - }); - - me.callParent(); - - me.getComponent('nodeview').setStore(gridstore); - - var gueststatus = me.getComponent('dcGuests'); - - var cpustat = me.down('#cpu'); - var memorystat = me.down('#memory'); - var storagestat = me.down('#storage'); - var sp = Ext.state.Manager.getProvider(); - - me.mon(PVE.data.ResourceStore, 'load', function(curstore, results) { - me.suspendLayout = true; - - var cpu = 0; - var maxcpu = 0; - - var nodes = 0; - - var memory = 0; - var maxmem = 0; - - var countedStorages = {}; - var used = 0; - var total = 0; - var usableStorages = {}; - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - if (storage !== '') { - usableStorages[storage] = true; - } - }); - - var qemu = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var lxc = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var error = 0; - - var i; - - for (i = 0; i < results.length; i++) { - var item = results[i]; - switch(item.data.type) { - case 'node': - cpu += (item.data.cpu * item.data.maxcpu); - maxcpu += item.data.maxcpu || 0; - memory += item.data.mem || 0; - maxmem += item.data.maxmem || 0; - nodes++; - - // update grid also - var griditem = gridstore.getById(item.data.id); - if (griditem) { - griditem.set('cpuusage', item.data.cpu); - var max = item.data.maxmem || 1; - var val = item.data.mem || 0; - griditem.set('memoryusage', val/max); - griditem.set('uptime', item.data.uptime); - griditem.commit(); //else it marks the fields as dirty - } - break; - case 'storage': - if (!Ext.Object.isEmpty(usableStorages)) { - if (usableStorages[item.data.id] === true) { - used += item.data.disk; - total += item.data.maxdisk; - } - break; - } - if (!countedStorages[item.data.storage] || - (item.data.storage === 'local' && - !countedStorages[item.data.id])) { - used += item.data.disk; - total += item.data.maxdisk; - - countedStorages[item.data.storage === 'local'?item.data.id:item.data.storage] = true; - } - break; - case 'qemu': - qemu[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - case 'lxc': - lxc[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - default: break; - } - } - - var text = Ext.String.format(gettext('of {0} CPU(s)'), maxcpu); - cpustat.updateValue((cpu/maxcpu), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(memory), PVE.Utils.render_size(maxmem)); - memorystat.updateValue((memory/maxmem), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(used), PVE.Utils.render_size(total)); - storagestat.updateValue((used/total), text); - - gueststatus.updateValues(qemu,lxc,error); - - me.suspendLayout = false; - me.updateLayout(true); - }); - - var dcHealth = me.getComponent('dcHealth'); - me.mon(rstore, 'load', dcHealth.updateStatus, dcHealth); - - var subs = me.down('#subscriptions'); - me.mon(rstore, 'load', function(store, records, success) { - var i; - var level; - var curlevel; - for (i = 0; i < records.length; i++) { - if (records[i].get('type') !== 'node') { - continue; - } - - curlevel = records[i].get('level'); - if (level === undefined || !curlevel) { - level = curlevel; - continue; - } - - if (level !== curlevel) { - break; - } - } - - if (level === '') { - subs.setData({ - title: gettext('No Subscription'), - iconCls: PVE.Utils.get_health_icon('critical', true), - text: gettext('You have at least one node without subscription.') - }); - subs.setUserCls('pointer'); - } else if (level !== curlevel) { - subs.setData({ - title: gettext('Mixed Subscriptions'), - iconCls: PVE.Utils.get_health_icon('warning', true), - text: gettext('Warning: Your subscription levels are not the same.') - }); - subs.setUserCls('pointer'); - } else { - subs.setData({ - title: PVE.Utils.render_support_level(level), - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext('Your subscription status is valid.') - }); - subs.setUserCls(''); - } - }); - - me.on('destroy', function(){ - rstore.stopUpdate(); - }); - - rstore.startUpdate(); - } - -}); -Ext.define('PVE.window.ReplicaEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveReplicaEdit', - - subject: gettext('Replication Job'), - - - url: '/cluster/replication', - method: 'POST', - - initComponent: function() { - var me = this; - - var vmid = me.pveSelNode.data.vmid; - var nodename = me.pveSelNode.data.node; - - var items = []; - - items.push({ - xtype: (me.isCreate && !vmid)?'pveGuestIDSelector':'displayfield', - name: 'guest', - fieldLabel: 'CT/VM ID', - value: vmid || '' - }); - - items.push( - { - xtype: me.isCreate ? 'pveNodeSelector':'displayfield', - name: 'target', - disallowedNodes: [nodename], - allowBlank: false, - onlineValidator: true, - fieldLabel: gettext("Target") - }, - { - xtype: 'pveCalendarEvent', - fieldLabel: gettext('Schedule'), - emptyText: '*/15 - ' + Ext.String.format(gettext('Every {0} minutes'), 15), - name: 'schedule' - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - step: 1, - minValue: 1, - emptyText: gettext('unlimited'), - name: 'rate' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment' - }, - { - xtype: 'proxmoxcheckbox', - name: 'enabled', - defaultValue: 'on', - checked: true, - fieldLabel: gettext('Enabled') - } - ); - - me.items = [ - { - xtype: 'inputpanel', - itemId: 'ipanel', - onlineHelp: 'pvesr_schedule_time_format', - - onGetValues: function(values) { - var me = this.up('window'); - - values.disable = values.enabled ? 0 : 1; - delete values.enabled; - - PVE.Utils.delete_if_default(values, 'rate', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'disable', 0, me.isCreate); - PVE.Utils.delete_if_default(values, 'schedule', '*/15', me.isCreate); - PVE.Utils.delete_if_default(values, 'comment', '', me.isCreate); - - if (me.isCreate) { - values.type = 'local'; - var vm = vmid || values.guest; - var id = -1; - if (me.highestids[vm] !== undefined) { - id = me.highestids[vm]; - } - id++; - values.id = vm + '-' + id.toString(); - delete values.guest; - } - return values; - }, - items: items - } - ]; - - me.callParent(); - - if (me.isCreate) { - me.load({ - success: function(response) { - var jobs = response.result.data; - var highestids = {}; - Ext.Array.forEach(jobs, function(job) { - var match = /^([0-9]+)\-([0-9]+)$/.exec(job.id); - if (match) { - var vmid = parseInt(match[1],10); - var id = parseInt(match[2],10); - if (highestids[vmid] < id || - highestids[vmid] === undefined) { - highestids[vmid] = id; - } - } - }); - - me.highestids = highestids; - } - }); - - } else { - me.load({ - success: function(response, options) { - response.result.data.enabled = !response.result.data.disable; - me.setValues(response.result.data); - me.digest = response.result.data.digest; - } - }); - } - } -}); - -/*jslint confusion: true */ -/* callback is a function and string */ -Ext.define('PVE.grid.ReplicaView', { - extend: 'Ext.grid.Panel', - xtype: 'pveReplicaView', - - onlineHelp: 'chapter_pvesr', - - stateful: true, - stateId: 'grid-pve-replication-status', - - controller: { - xclass: 'Ext.app.ViewController', - - addJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var win = Ext.create('PVE.window.ReplicaEdit', { - isCreate: true, - method: 'POST', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - editJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var data = rec.data; - var win = Ext.create('PVE.window.ReplicaEdit', { - url: '/cluster/replication/' + data.id, - method: 'PUT', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - scheduleJobNow: function(button,event,rec) { - var me = this.getView(); - var controller = this; - - Proxmox.Utils.API2Request({ - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/schedule_now", - method: 'POST', - waitMsgTarget: me, - callback: function() { controller.reload(); }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - showLog: function(button, event, rec) { - var me = this.getView(); - var controller = this; - var logView = Ext.create('Proxmox.panel.LogView', { - border: false, - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/log" - }); - var win = Ext.create('Ext.window.Window', { - items: [ logView ], - layout: 'fit', - width: 800, - height: 400, - modal: true, - title: gettext("Replication Log") - }); - var task = { - run: function() { - logView.requestUpdate(); - }, - interval: 1000 - }; - Ext.TaskManager.start(task); - win.on('destroy', function() { - Ext.TaskManager.stop(task); - controller.reload(); - }); - win.show(); - }, - - reload: function() { - var me = this.getView(); - me.rstore.load(); - }, - - dblClick: function(grid, record, item) { - var me = this; - me.editJob(undefined, undefined, record); - }, - - // check for cluster - // currently replication is for cluster only, so we disable the whole - // component - checkPrerequisites: function() { - var me = this.getView(); - if (PVE.data.ResourceStore.getNodes().length < 2) { - me.mask(gettext("Replication needs at least two nodes"), ['pve-static-mask']); - } - }, - - control: { - '#': { - itemdblclick: 'dblClick', - afterlayout: 'checkPrerequisites' - } - } - }, - - tbar: [ - { - text: gettext('Add'), - itemId: 'addButton', - handler: 'addJob' - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - itemId: 'editButton', - handler: 'editJob', - disabled: true - }, - { - xtype: 'proxmoxStdRemoveButton', - itemId: 'removeButton', - baseurl: '/api2/extjs/cluster/replication/', - dangerous: true, - callback: 'reload' - }, - { - xtype: 'proxmoxButton', - text: gettext('Log'), - itemId: 'logButton', - handler: 'showLog', - disabled: true - }, - { - xtype: 'proxmoxButton', - text: gettext('Schedule now'), - itemId: 'scheduleNowButton', - handler: 'scheduleJobNow', - disabled: true - } - ], - - initComponent: function() { - var me = this; - var mode = ''; - var url = '/cluster/replication'; - - me.nodename = me.pveSelNode.data.node; - me.vmid = me.pveSelNode.data.vmid; - - me.columns = [ - { - text: gettext('Enabled'), - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true - }, - { - text: 'ID', - dataIndex: 'id', - width: 60, - hidden: true - }, - { - text: gettext('Guest'), - dataIndex: 'guest', - width: 75 - }, - { - text: gettext('Job'), - dataIndex: 'jobnum', - width: 60 - }, - { - text: gettext('Target'), - dataIndex: 'target' - } - ]; - - if (!me.nodename) { - mode = 'dc'; - me.stateId = 'grid-pve-replication-dc'; - } else if (!me.vmid) { - mode = 'node'; - url = '/nodes/' + me.nodename + '/replication'; - } else { - mode = 'vm'; - url = '/nodes/' + me.nodename + '/replication' + '?guest=' + me.vmid; - } - - if (mode !== 'dc') { - me.columns.push( - { - text: gettext('Status'), - dataIndex: 'state', - minWidth: 160, - flex: 1, - renderer: function(value, metadata, record) { - - if (record.data.pid) { - metadata.tdCls = 'x-grid-row-loading'; - return ''; - } - - var icons = []; - var states = []; - - if (record.data.remove_job) { - icons.push(''); - states.push(gettext("Removal Scheduled")); - } - - if (record.data.error) { - icons.push(''); - states.push(record.data.error); - } - - if (icons.length == 0) { - icons.push(''); - states.push(gettext('OK')); - } - - return icons.join(',') + ' ' + states.join(','); - } - }, - { - text: gettext('Last Sync'), - dataIndex: 'last_sync', - width: 150, - renderer: function(value, metadata, record) { - if (!value) { - return '-'; - } - - if (record.data.pid) { - return gettext('syncing'); - } - - return Proxmox.Utils.render_timestamp(value); - } - }, - { - text: gettext('Duration'), - dataIndex: 'duration', - width: 60, - renderer: PVE.Utils.render_duration - }, - { - text: gettext('Next Sync'), - dataIndex: 'next_sync', - width: 150, - renderer: function(value) { - if (!value) { - return '-'; - } - - var now = new Date(); - var next = new Date(value*1000); - - if (next < now) { - return gettext('pending'); - } - - return Proxmox.Utils.render_timestamp(value); - } - } - ); - } - - me.columns.push( - { - text: gettext('Schedule'), - width: 75, - dataIndex: 'schedule' - }, - { - text: gettext('Rate limit'), - dataIndex: 'rate', - renderer: function(value) { - if (!value) { - return gettext('unlimited'); - } - - return value.toString() + ' MB/s'; - }, - hidden: true - }, - { - text: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.htmlEncode - } - ); - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-replica-' + me.nodename + me.vmid, - model: (mode === 'dc')? 'pve-replication' : 'pve-replication-state', - interval: 3000, - proxy: { - type: 'proxmox', - url: "/api2/json" + url - } - }); - - me.store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sorters: [ - { - property: 'guest' - }, - { - property: 'jobnum' - } - ] - }); - - me.callParent(); - - // we cannot access the log and scheduleNow button - // in the datacenter, because - // we do not know where/if the jobs runs - if (mode === 'dc') { - me.down('#logButton').setHidden(true); - me.down('#scheduleNowButton').setHidden(true); - } - - // if we set the warning mask, we do not want to load - // or set the mask on store errors - if (PVE.data.ResourceStore.getNodes().length < 2) { - return; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.on('destroy', me.rstore.stopUpdate); - me.rstore.startUpdate(); - } -}, function() { - - Ext.define('pve-replication', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'target', 'comment', 'rate', 'type', - { name: 'guest', type: 'integer' }, - { name: 'jobnum', type: 'integer' }, - { name: 'schedule', defaultValue: '*/15' }, - { name: 'disable', defaultValue: '' }, - { name: 'enabled', calculate: function(data) { return !data.disable; } } - ] - }); - - Ext.define('pve-replication-state', { - extend: 'pve-replication', - fields: [ - 'last_sync', 'next_sync', 'error', 'duration', 'state', - 'fail_count', 'remove_job', 'pid' - ] - }); - -}); -Ext.define('PVE.dc.Health', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcHealth', - - title: gettext('Health'), - - bodyPadding: 10, - height: 220, - layout: { - type: 'hbox', - align: 'stretch' - }, - - defaults: { - flex: 1, - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - nodeList: [], - nodeIndex: 0, - - updateStatus: function(store, records, success) { - var me = this; - if (!success) { - return; - } - - var cluster = { - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext("Standalone node - no cluster defined") - }; - - var nodes = { - online: 0, - offline: 0 - }; - - // by default we have one node - var numNodes = 1; - var i; - - for (i = 0; i < records.length; i++) { - var item = records[i]; - if (item.data.type === 'node') { - nodes[item.data.online === 1 ? 'online':'offline']++; - } else if(item.data.type === 'cluster') { - cluster.text = gettext("Cluster") + ": "; - cluster.text += item.data.name + ", "; - cluster.text += gettext("Quorate") + ": "; - cluster.text += Proxmox.Utils.format_boolean(item.data.quorate); - if (item.data.quorate != 1) { - cluster.iconCls = PVE.Utils.get_health_icon('critical', true); - } - - numNodes = item.data.nodes; - } - } - - if (numNodes !== (nodes.online + nodes.offline)) { - nodes.offline = numNodes - nodes.online; - } - - me.getComponent('clusterstatus').updateHealth(cluster); - me.getComponent('nodestatus').update(nodes); - }, - - updateCeph: function(store, records, success) { - var me = this; - var cephstatus = me.getComponent('ceph'); - if (!success || records.length < 1) { - - // if ceph status is already visible - // dont stop to update - if (cephstatus.isVisible()) { - return; - } - - // try all nodes until we either get a successfull api call, - // or we tried all nodes - if (++me.nodeIndex >= me.nodeList.length) { - me.cephstore.stopUpdate(); - } else { - store.getProxy().setUrl('/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status'); - } - - return; - } - - var state = PVE.Utils.render_ceph_health(records[0].data.health || {}); - cephstatus.updateHealth(state); - cephstatus.setVisible(true); - }, - - listeners: { - destroy: function() { - var me = this; - me.cephstore.stopUpdate(); - } - }, - - items: [ - { - itemId: 'clusterstatus', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - itemId: 'nodestatus', - data: { - online: 0, - offline: 0 - }, - tpl: [ - '

' + gettext('Nodes') + '


', - '
', - '
', - ' ', - gettext('Online'), - '
', - '
{online}
', - '

', - '
', - ' ', - gettext('Offline'), - '
', - '
{offline}
', - '
' - ] - }, - { - itemId: 'ceph', - width: 250, - columnWidth: undefined, - userCls: 'pointer', - title: 'Ceph', - xtype: 'pveHealthWidget', - hidden: true, - listeners: { - element: 'el', - click: function() { - var me = this.component.up('pveDcHealth'); - var sp = Ext.state.Manager.getProvider(); - - // preselect the ceph tab - sp.set('nodetab', {value:'ceph'}); - - // select the node that had the successfull api call - var id = me.nodeList[me.nodeIndex].id; - Ext.ComponentQuery.query('pveResourceTree')[0].selectById(id); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.nodeList = PVE.data.ResourceStore.getNodes(); - me.nodeIndex = 0; - me.cephstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-ceph', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status' - } - }); - me.callParent(); - me.mon(me.cephstore, 'load', me.updateCeph, me); - me.cephstore.startUpdate(); - } -}); -Ext.define('PVE.dc.Guests', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcGuests', - - - title: gettext('Guests'), - height: 220, - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - bodyPadding: '0 20 20 20', - - defaults: { - xtype: 'box', - padding: '0 50 0 50', - style: { - 'text-align':'center', - 'line-height':'1.2' - } - }, - items: [{ - itemId: 'qemu', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("Virtual Machines") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'lxc', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("LXC Container") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'error', - colspan: 2, - data: { - num: 0 - }, - columnWidth: 1, - padding: '10 250 0 250', - tpl: [ - '', - '
', - ' ', - gettext('Error'), - '
', - '
{num}
', - '
' - ] - }], - - updateValues: function(qemu, lxc, error) { - var me = this; - me.getComponent('qemu').update(qemu); - me.getComponent('lxc').update(lxc); - me.getComponent('error').update({num: error}); - } -}); - /*jslint confusion: true*/ -Ext.define('PVE.dc.OptionView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveDcOptionView'], - - onlineHelp: 'datacenter_configuration_file', - - monStoreErrors: true, - - add_inputpanel_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - var canEdit = (opts.caps === undefined || opts.caps); - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: canEdit ? { - xtype: 'proxmoxWindowEdit', - width: 350, - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - setValues: function(values) { - // FIXME: run through parsePropertyString if not an object? - var edit_value = values[name]; - Ext.Array.each(this.query('inputpanel'), function(panel) { - panel.setValues(edit_value); - }); - }, - url: opts.url, - items: [{ - xtype: 'inputpanel', - onGetValues: function(values) { - if (values === undefined || Object.keys(values).length === 0) { - return { 'delete': name }; - } - var ret_val = {}; - ret_val[name] = PVE.Parser.printPropertyString(values); - return ret_val; - }, - items: opts.items - }] - } : undefined - }; - }, - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.add_combobox_row('keyboard', gettext('Keyboard Layout'), { - renderer: PVE.Utils.render_kvm_language, - comboItems: PVE.Utils.kvm_keymap_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('http_proxy', gettext('HTTP proxy'), { - defaultValue: Proxmox.Utils.noneText, - vtype: 'HttpProxy', - deleteEmpty: true - }); - me.add_combobox_row('console', gettext('Console Viewer'), { - renderer: PVE.Utils.render_console_viewer, - comboItems: PVE.Utils.console_viewer_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('email_from', gettext('Email from address'), { - deleteEmpty: true, - vtype: 'proxmoxMail', - defaultValue: 'root@$hostname' - }); - me.add_text_row('mac_prefix', gettext('MAC address prefix'), { - deleteEmpty: true, - vtype: 'MacPrefix', - defaultValue: Proxmox.Utils.noneText - }); - me.add_inputpanel_row('migration', gettext('Migration Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.vms['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - defaultKey: 'type', - items: [{ - xtype: 'displayfield', - name: 'type', - fieldLabel: gettext('Type'), - value: 'secure', - submitValue: true, - vtype: 'IPCIDRAddress' - }, { - xtype: 'textfield', - name: 'network', - fieldLabel: gettext('Network'), - vtype: 'IPCIDRAddress', - emptyText: Proxmox.Utils.defaultText, - value: '' - }] - }); - me.add_inputpanel_row('ha', gettext('HA Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.vms['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'shutdown_policy', - fieldLabel: gettext('Shutdown Policy'), - deleteEmpty: false, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + ' (conditional)' ], - ['freeze', 'freeze'], - ['failover', 'failover'], - ['conditional', 'conditional'] - ], - defaultValue: '__default__' - }] - }); - - // TODO: bwlimits, migration net, u2f? - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - Ext.apply(me, { - tbar: [{ - text: gettext('Edit'), - xtype: 'proxmoxButton', - disabled: true, - handler: function() { me.run_editor(); }, - selModel: me.selModel - }], - url: "/api2/json/cluster/options", - editorConfig: { - url: "/api2/extjs/cluster/options" - }, - interval: 5000, - cwidth1: 200, - listeners: { - itemdblclick: me.run_editor - } - }); - - me.callParent(); - - // set the new value for the default console - me.mon(me.rstore, 'load', function(store, records, success) { - if (!success) { - return; - } - - var rec = store.getById('console'); - PVE.VersionInfo.console = rec.data.value; - if (rec.data.value === '__default__') { - delete PVE.VersionInfo.console; - } - }); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); -Ext.define('PVE.dc.StorageView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveStorageView'], - - onlineHelp: 'chapter_storage', - - stateful: true, - stateId: 'grid-dc-storage', - - createStorageEditWindow: function(type, sid) { - var schema = PVE.Utils.storageSchema[type]; - if (!schema || !schema.ipanel) { - throw "no editor registered for storage type: " + type; - } - - Ext.create('PVE.storage.BaseEdit', { - paneltype: 'PVE.storage.' + schema.ipanel, - type: type, - storageId: sid, - autoShow: true, - listeners: { - destroy: this.reloadStore - } - }); - }, - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-storage', - proxy: { - type: 'proxmox', - url: "/api2/json/storage" - }, - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type, - sid = rec.data.storage; - - me.createStorageEditWindow(type, sid); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/storage/', - callback: reload - }); - - // else we cannot dynamically generate the add menu handlers - var addHandleGenerator = function(type) { - return function() { me.createStorageEditWindow(type); }; - }; - var addMenuItems = [], type; - /*jslint forin: true */ - for (type in PVE.Utils.storageSchema) { - var storage = PVE.Utils.storageSchema[type]; - if (storage.hideAdd) { - continue; - } - addMenuItems.push({ - text: PVE.Utils.format_storage_type(type), - iconCls: 'fa fa-fw fa-' + storage.faIcon, - handler: addHandleGenerator(type) - }); - } - - Ext.apply(me, { - store: store, - reloadStore: reload, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: addMenuItems - }) - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - flex: 2, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Type'), - flex: 1, - sortable: true, - dataIndex: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - header: gettext('Content'), - flex: 3, - sortable: true, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Path') + '/' + gettext('Target'), - flex: 2, - sortable: true, - dataIndex: 'path', - renderer: function(value, metaData, record) { - if (record.data.target) { - return record.data.target; - } - return value; - } - }, - { - header: gettext('Shared'), - flex: 1, - sortable: true, - dataIndex: 'shared', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Enabled'), - flex: 1, - sortable: true, - dataIndex: 'disable', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - header: gettext('Bandwidth Limit'), - flex: 2, - sortable: true, - dataIndex: 'bwlimit' - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-storage', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'content', 'server', 'portal', 'target', 'export', 'storage', - { name: 'shared', type: 'boolean'}, - { name: 'disable', type: 'boolean'} - ], - idProperty: 'storage' - }); - -}); -/*global u2f,QRCode,Uint8Array*/ -/*jslint confusion: true*/ -Ext.define('PVE.window.TFAEdit', { - extend: 'Ext.window.Window', - mixins: ['Proxmox.Mixin.CBind'], - - onlineHelp: 'pveum_tfa_auth', // fake to ensure this gets a link target - - modal: true, - resizable: false, - title: gettext('Two Factor Authentication'), - subject: 'TFA', - url: '/api2/extjs/access/tfa', - width: 512, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - updateQrCode: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var algorithm = values.algorithm; - if (!algorithm) { - algorithm = 'SHA1'; - } - - me.qrcode.makeCode( - 'otpauth://totp/' + encodeURIComponent(me.userid) + - '?secret=' + values.secret + - '&period=' + values.step + - '&digits=' + values.digits + - '&algorithm=' + algorithm + - '&issuer=' + encodeURIComponent(values.issuer) - ); - - me.lookup('challenge').setVisible(true); - me.down('#qrbox').setVisible(true); - }, - - showError: function(error) { - Ext.Msg.alert( - gettext('Error'), - PVE.Utils.render_u2f_error(error) - ); - }, - - doU2FChallenge: function(response) { - var me = this; - - var data = response.result.data; - me.lookup('password').setDisabled(true); - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Setup'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - Ext.Function.defer(function() { - u2f.register(data.appId, [data], [], function(data) { - msg.close(); - if (data.errorCode) { - me.showError(data.errorCode); - } else { - me.respondToU2FChallenge(data); - } - }); - }, 500, me); - }, - - respondToU2FChallenge: function(data) { - var me = this; - var params = { - userid: me.userid, - action: 'confirm', - response: JSON.stringify(data) - }; - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - success: function() { - me.close(); - Ext.Msg.show({ - title: gettext('Success'), - message: gettext('U2F Device successfully connected.'), - buttons: Ext.Msg.OK - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - viewModel: { - data: { - in_totp_tab: true, - tfa_required: false, - has_tfa: false, - valid: false, - u2f_available: true - }, - formulas: { - canDeleteTFA: function(get) { - return (get('has_tfa') && !get('tfa_required')); - } - } - }, - - afterLoadingRealm: function(realm_tfa_type) { - var me = this; - var viewmodel = me.getViewModel(); - if (!realm_tfa_type) { - // There's no TFA enforced by the realm, everything works. - viewmodel.set('u2f_available', true); - viewmodel.set('tfa_required', false); - } else if (realm_tfa_type === 'oath') { - // The realm explicitly requires TOTP - viewmodel.set('tfa_required', true); - viewmodel.set('u2f_available', false); - } else { - // The realm enforces some other TFA type (yubico) - me.close(); - Ext.Msg.alert( - gettext('Error'), - Ext.String.format( - gettext("Custom 2nd factor configuration is not supported on realms with '{0}' TFA."), - realm_tfa_type - ) - ); - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[qrupdate=true]': { - change: function() { - var me = this.getView(); - me.updateQrCode(); - } - }, - 'field': { - validitychange: function(field, valid) { - var me = this; - var viewModel = me.getViewModel(); - var form = me.lookup('totp_form'); - var challenge = me.lookup('challenge'); - var password = me.lookup('password'); - viewModel.set('valid', form.isValid() && challenge.isValid() && password.isValid()); - } - }, - '#': { - show: function() { - var me = this.getView(); - var viewmodel = this.getViewModel(); - - me.qrdiv = document.createElement('center'); - me.qrcode = new QRCode(me.qrdiv, { - width: 256, - height: 256, - correctLevel: QRCode.CorrectLevel.M - }); - me.down('#qrbox').getEl().appendChild(me.qrdiv); - - viewmodel.set('has_tfa', me.hasTFA); - if (!me.hasTFA) { - this.randomizeSecret(); - } else { - me.down('#qrbox').setVisible(false); - me.lookup('challenge').setVisible(false); - } - - if (Proxmox.UserName === 'root@pam') { - me.lookup('password').setVisible(false); - me.lookup('password').setDisabled(true); - } - } - }, - '#tfatabs': { - tabchange: function(panel, newcard) { - var viewmodel = this.getViewModel(); - viewmodel.set('in_totp_tab', newcard.itemId === 'totp-panel'); - } - } - }, - - applySettings: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'new', - key: values.secret, - config: PVE.Parser.printPropertyString({ - type: 'oath', - digits: values.digits, - step: values.step - }), - // this is used to verify that the client generates the correct codes: - response: me.lookup('challenge').value - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - deleteTFA: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'delete' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - randomizeSecret: function() { - var me = this; - var rnd = new Uint8Array(16); - window.crypto.getRandomValues(rnd); - var data = ''; - rnd.forEach(function(b) { - // just use the first 5 bit - b = b & 0x1f; - if (b < 26) { - // A..Z - data += String.fromCharCode(b + 0x41); - } else { - // 2..7 - data += String.fromCharCode(b-26 + 0x32); - } - }); - me.lookup('tfa_secret').setValue(data); - }, - - startU2FRegistration: function() { - var me = this; - - var params = { - userid: me.getView().userid, - action: 'new' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response) { - me.getView().doU2FChallenge(response); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - - items: [ - { - xtype: 'tabpanel', - itemId: 'tfatabs', - border: false, - items: [ - { - xtype: 'panel', - title: 'TOTP', - itemId: 'totp-panel', - border: false, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'form', - layout: 'anchor', - border: false, - reference: 'totp_form', - fieldDefaults: { - anchor: '100%', - padding: '0 5' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('User name'), - cbind: { - value: '{userid}' - } - }, - { - layout: 'hbox', - border: false, - padding: '0 0 5 0', - items: [{ - xtype: 'textfield', - fieldLabel: gettext('Secret'), - emptyText: gettext('Unchanged'), - name: 'secret', - reference: 'tfa_secret', - regex: /^[A-Z2-7=]+$/, - regexText: 'Must be base32 [A-Z2-7=]', - maskRe: /[A-Z2-7=]/, - qrupdate: true, - flex: 4 - }, - { - xtype: 'button', - text: gettext('Randomize'), - reference: 'randomize_button', - handler: 'randomizeSecret', - flex: 1 - }] - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Time period'), - name: 'step', - // Google Authenticator ignores this and generates bogus data - hidden: true, - value: 30, - minValue: 10, - qrupdate: true - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Digits'), - name: 'digits', - value: 6, - // Google Authenticator ignores this and generates bogus data - hidden: true, - minValue: 6, - maxValue: 8, - qrupdate: true - }, - { - xtype: 'textfield', - fieldLabel: gettext('Issuer Name'), - name: 'issuer', - value: 'Proxmox Web UI', - qrupdate: true - } - ] - }, - { - xtype: 'box', - itemId: 'qrbox', - visible: false, // will be enabled when generating a qr code - style: { - 'background-color': '#23272a', - padding: '5px', - width: '266px', - height: '266px' - } - }, - { - xtype: 'textfield', - fieldLabel: gettext('Verification Code'), - allowBlank: false, - reference: 'challenge', - padding: '0 5', - emptyText: gettext('Scan QR code and enter TOTP auth. code to verify') - } - ] - }, - { - title: 'U2F', - itemId: 'u2f-panel', - reference: 'u2f_panel', - border: false, - padding: '5 5', - layout: { - type: 'vbox', - align: 'middle' - }, - bind: { - disabled: '{!u2f_available}' - }, - items: [ - { - xtype: 'label', - width: 500, - text: gettext('To register a U2F device, connect the device, then click the button and follow the instructions.') - } - ] - } - ] - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - reference: 'password', - allowBlank: false, - validateBlank: true, - padding: '0 0 5 5', - emptyText: gettext('verify current password') - } - ], - - buttons: [ - { - xtype: 'proxmoxHelpButton' - }, - '->', - { - text: gettext('Apply'), - handler: 'applySettings', - bind: { - hidden: '{!in_totp_tab}', - disabled: '{!valid}' - } - }, - { - xtype: 'button', - text: gettext('Register U2F Device'), - handler: 'startU2FRegistration', - bind: { - hidden: '{in_totp_tab}' - } - }, - { - text: gettext('Delete'), - reference: 'delete_button', - handler: 'deleteTFA', - bind: { - disabled: '{!canDeleteTFA}' - } - } - ], - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-domains', - autoLoad: true - }); - - store.on('load', function() { - var user_realm = me.userid.split('@')[1]; - var realm = me.store.findRecord('realm', user_realm); - me.afterLoadingRealm(realm && realm.data && realm.data.tfa); - }, me); - - Ext.apply(me, { store: store }); - - me.callParent(); - - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', 'pveum_tfa_auth'); - } -}); -Ext.define('PVE.dc.UserEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcUserEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.userid; - - var url; - var method; - var realm; - - if (me.isCreate) { - url = '/api2/extjs/access/users'; - method = 'POST'; - } else { - url = '/api2/extjs/access/users/' + me.userid; - method = 'PUT'; - } - - var verifypw; - var pwfield; - - var validate_pw = function() { - if (verifypw.getValue() !== pwfield.getValue()) { - return gettext("Passwords do not match"); - } - return true; - }; - - verifypw = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - submitValue: false, - disabled: true, - hidden: true, - validator: validate_pw - }); - - pwfield = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - name: 'password', - disabled: true, - hidden: true, - validator: validate_pw - }); - - var update_passwd_field = function(realm) { - if (realm === 'pve') { - pwfield.setVisible(true); - pwfield.setDisabled(false); - verifypw.setVisible(true); - verifypw.setDisabled(false); - } else { - pwfield.setVisible(false); - pwfield.setDisabled(true); - verifypw.setVisible(false); - verifypw.setDisabled(true); - } - - }; - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'userid', - fieldLabel: gettext('User name'), - value: me.userid, - allowBlank: false, - submitValue: me.isCreate ? true : false - }, - pwfield, verifypw, - { - xtype: 'pveGroupSelector', - name: 'groups', - multiSelect: true, - allowBlank: true, - fieldLabel: gettext('Group') - }, - { - xtype: 'datefield', - name: 'expire', - emptyText: 'never', - format: 'Y-m-d', - submitFormat: 'U', - fieldLabel: gettext('Expire') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enabled'), - name: 'enable', - uncheckedValue: 0, - defaultValue: 1, - checked: true - } - ]; - - var column2 = [ - { - xtype: 'textfield', - name: 'firstname', - fieldLabel: gettext('First Name') - }, - { - xtype: 'textfield', - name: 'lastname', - fieldLabel: gettext('Last Name') - }, - { - xtype: 'textfield', - name: 'email', - fieldLabel: gettext('E-Mail'), - vtype: 'proxmoxMail' - } - ]; - - if (me.isCreate) { - column1.splice(1,0,{ - xtype: 'pveRealmComboBox', - name: 'realm', - fieldLabel: gettext('Realm'), - allowBlank: false, - matchFieldWidth: false, - listConfig: { width: 300 }, - listeners: { - change: function(combo, newValue){ - realm = newValue; - update_passwd_field(realm); - } - }, - submitValue: false - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ], - advancedItems: [ - { - xtype: 'textfield', - name: 'keys', - fieldLabel: gettext('Key IDs') - } - ], - onGetValues: function(values) { - // hack: ExtJS datefield does not submit 0, so we need to set that - if (!values.expire) { - values.expire = 0; - } - - if (realm) { - values.userid = values.userid + '@' + realm; - } - - if (!values.password) { - delete values.password; - } - - return values; - } - }); - - Ext.applyIf(me, { - subject: gettext('User'), - url: url, - method: method, - fieldDefaults: { - labelWidth: 110 // for spanish translation - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (Ext.isDefined(data.expire)) { - if (data.expire) { - data.expire = new Date(data.expire * 1000); - } else { - // display 'never' instead of '1970-01-01' - data.expire = null; - } - } - me.setValues(data); - } - }); - } - } -}); -/*jslint confusion: true */ -Ext.define('PVE.dc.UserView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveUserView'], - - onlineHelp: 'pveum_users', - - stateful: true, - stateId: 'grid-users', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - id: "users", - model: 'pve-users', - sorters: { - property: 'userid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/access/users/', - enableFn: function(rec) { - if (!caps.access['User.Modify']) { - return false; - } - return rec.data.userid !== 'root@pam'; - }, - callback: function() { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec || !caps.access['User.Modify']) { - return; - } - - var win = Ext.create('PVE.dc.UserEdit',{ - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - enableFn: function(rec) { - return !!caps.access['User.Modify']; - }, - selModel: sm, - handler: run_editor - }); - - var pwchange_btn = new Proxmox.button.Button({ - text: gettext('Password'), - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var win = Ext.create('Proxmox.window.PasswordEdit', { - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var tfachange_btn = new Proxmox.button.Button({ - text: 'TFA', - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var d = rec.data; - var win = Ext.create('PVE.window.TFAEdit',{ - hasTFA: d.keys != undefined && d.keys.length, - userid: d.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - disabled: !caps.access['User.Modify'], - handler: function() { - var win = Ext.create('PVE.dc.UserEdit',{ - }); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn, pwchange_btn, tfachange_btn - ]; - - var render_username = function(userid) { - return userid.match(/^(.+)(@[^@]+)$/)[1]; - }; - - var render_realm = function(userid) { - return userid.match(/@([^@]+)$/)[1]; - }; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('User name'), - width: 200, - sortable: true, - renderer: render_username, - dataIndex: 'userid' - }, - { - header: gettext('Realm'), - width: 100, - sortable: true, - renderer: render_realm, - dataIndex: 'userid' - }, - { - header: gettext('Enabled'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'enable' - }, - { - header: gettext('Expire'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_expire, - dataIndex: 'expire' - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname' - }, - { - header: 'TFA', - width: 50, - sortable: true, - renderer: function(v) { - return Proxmox.Utils.format_boolean(v !== undefined && v.length); - }, - dataIndex: 'keys' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.PoolView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pvePoolView'], - - onlineHelp: 'pveum_pools', - - stateful: true, - stateId: 'grid-pools', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: { - property: 'poolid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/pools/', - callback: function () { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.PoolEdit',{ - poolid: rec.data.poolid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.PoolEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'poolid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.PoolEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcPoolEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.poolid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/pools'; - method = 'POST'; - } else { - url = '/api2/extjs/pools/' + me.poolid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Pool'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'poolid', - value: me.poolid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.GroupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveGroupView'], - - onlineHelp: 'pveum_groups', - - stateful: true, - stateId: 'grid-groups', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: { - property: 'groupid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/groups/' - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.GroupEdit',{ - groupid: rec.data.groupid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.GroupEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'groupid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.GroupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcGroupEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/groups'; - method = 'POST'; - } else { - url = '/api2/extjs/access/groups/' + me.groupid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Group'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'groupid', - value: me.groupid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.RoleView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveRoleView'], - - onlineHelp: 'pveum_roles', - - stateful: true, - stateId: 'grid-roles', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: { - property: 'roleid', - order: 'DESC' - } - }); - - var render_privs = function(value, metaData) { - - if (!value) { - return '-'; - } - - // allow word wrap - metaData.style = 'white-space:normal;'; - - return value.replace(/\,/g, ' '); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (rec.data.special === "1") { - return; - } - - var win = Ext.create('PVE.dc.RoleEdit',{ - roleid: rec.data.roleid, - privs: rec.data.privs - }); - win.on('destroy', reload); - win.show(); - }; - - Ext.apply(me, { - store: store, - selModel: sm, - - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Built-In'), - width: 65, - sortable: true, - dataIndex: 'special', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - dataIndex: 'roleid' - }, - { - itemid: 'privs', - header: gettext('Privileges'), - sortable: false, - renderer: render_privs, - dataIndex: 'privs', - flex: 1 - } - ], - listeners: { - activate: function() { - store.load(); - }, - itemdblclick: run_editor - }, - tbar: [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.RoleEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor, - enableFn: function(record) { - return record.data.special !== '1'; - } - }, - { - xtype: 'proxmoxStdRemoveButton', - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/roles/', - enableFn: function(record) { - return record.data.special !== '1'; - } - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.RoleEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveDcRoleEdit', - - width: 400, - - initComponent : function() { - var me = this; - - me.isCreate = !me.roleid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/roles'; - method = 'POST'; - } else { - url = '/api2/extjs/access/roles/' + me.roleid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Role'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'roleid', - value: me.roleid, - allowBlank: false, - fieldLabel: gettext('Name') - }, - { - xtype: 'pvePrivilegesSelector', - name: 'privs', - value: me.privs, - allowBlank: false, - fieldLabel: gettext('Privileges') - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response) { - var data = response.result.data; - var keys = Ext.Object.getKeys(data); - - me.setValues({ - privs: keys, - roleid: me.roleid - }); - } - }); - } - } -}); -Ext.define('PVE.dc.ACLAdd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveACLAdd'], - url: '/access/acl', - method: 'PUT', - isAdd: true, - initComponent : function() { - - var me = this; - - me.isCreate = true; - - var items = [ - { - xtype: me.path ? 'hiddenfield' : 'pvePermPathSelector', - name: 'path', - value: me.path, - allowBlank: false, - fieldLabel: gettext('Path') - } - ]; - - if (me.aclType === 'group') { - me.subject = gettext("Group Permission"); - items.push({ - xtype: 'pveGroupSelector', - name: 'groups', - fieldLabel: gettext('Group') - }); - } else if (me.aclType === 'user') { - me.subject = gettext("User Permission"); - items.push({ - xtype: 'pveUserSelector', - name: 'users', - fieldLabel: gettext('User') - }); - } else { - throw "unknown ACL type"; - } - - items.push({ - xtype: 'pveRoleSelector', - name: 'roles', - value: 'NoAccess', - fieldLabel: gettext('Role') - }); - - if (!me.path) { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'propagate', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Propagate') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - items: items, - onlineHelp: 'pveum_permission_management' - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.dc.ACLView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveACLView'], - - onlineHelp: 'chapter_user_management', - - stateful: true, - stateId: 'grid-acls', - - // use fixed path - path: undefined, - - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-acl', - proxy: { - type: 'proxmox', - url: "/api2/json/access/acl" - }, - sorters: { - property: 'path', - order: 'DESC' - } - }); - - if (me.path) { - store.addFilter(Ext.create('Ext.util.Filter',{ - filterFn: function(item) { - if (item.data.path === me.path) { - return true; - } - } - })); - } - - var render_ugid = function(ugid, metaData, record) { - if (record.data.type == 'group') { - return '@' + ugid; - } - - return ugid; - }; - - var columns = [ - { - header: gettext('User') + '/' + gettext('Group'), - flex: 1, - sortable: true, - renderer: render_ugid, - dataIndex: 'ugid' - }, - { - header: gettext('Role'), - flex: 1, - sortable: true, - dataIndex: 'roleid' - } - ]; - - if (!me.path) { - columns.unshift({ - header: gettext('Path'), - flex: 1, - sortable: true, - dataIndex: 'path' - }); - columns.push({ - header: gettext('Propagate'), - width: 80, - sortable: true, - dataIndex: 'propagate' - }); - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: gettext('Are you sure you want to remove this entry'), - handler: function(btn, event, rec) { - var params = { - 'delete': 1, - path: rec.data.path, - roles: rec.data.roleid - }; - if (rec.data.type === 'group') { - params.groups = rec.data.ugid; - } else if (rec.data.type === 'user') { - params.users = rec.data.ugid; - } else { - throw 'unknown data type'; - } - - Proxmox.Utils.API2Request({ - url: '/access/acl', - params: params, - method: 'PUT', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: { - xtype: 'menu', - items: [ - { - text: gettext('Group Permission'), - iconCls: 'fa fa-fw fa-group', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'group', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('User Permission'), - iconCls: 'fa fa-fw fa-user', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'user', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - } - ] - } - }, - remove_btn - ], - viewConfig: { - trackOver: false - }, - columns: columns, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-acl', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'ugid', 'roleid', - { - name: 'propagate', - type: 'boolean' - } - ] - }); - -}); -Ext.define('PVE.dc.AuthView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveAuthView'], - - onlineHelp: 'pveum_authentication_realms', - - stateful: true, - stateId: 'grid-authrealms', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-domains', - sorters: { - property: 'realm', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.AuthEdit',{ - realm: rec.data.realm, - authType: rec.data.type - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - baseurl: '/access/domains/', - selModel: sm, - enableFn: function(rec) { - return !(rec.data.type === 'pve' || rec.data.type === 'pam'); - }, - callback: function() { - reload(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Active Directory Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit', { - authType: 'ad' - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('LDAP Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit',{ - authType: 'ldap' - }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - edit_btn, remove_btn - ]; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Realm'), - width: 100, - sortable: true, - dataIndex: 'realm' - }, - { - header: gettext('Type'), - width: 100, - sortable: true, - dataIndex: 'type' - }, - { - header: gettext('TFA'), - width: 100, - sortable: true, - dataIndex: 'tfa' - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.AuthEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcAuthEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.realm; - - var url; - var method; - var serverlist; - - if (me.isCreate) { - url = '/api2/extjs/access/domains'; - method = 'POST'; - } else { - url = '/api2/extjs/access/domains/' + me.realm; - method = 'PUT'; - } - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'realm', - fieldLabel: gettext('Realm'), - value: me.realm, - allowBlank: false - } - ]; - - if (me.authType === 'ad') { - - me.subject = gettext('Active Directory Server'); - - column1.push({ - xtype: 'textfield', - name: 'domain', - fieldLabel: gettext('Domain'), - emptyText: 'company.net', - allowBlank: false - }); - - } else if (me.authType === 'ldap') { - - me.subject = gettext('LDAP Server'); - - column1.push({ - xtype: 'textfield', - name: 'base_dn', - fieldLabel: gettext('Base Domain Name'), - emptyText: 'CN=Users,DC=Company,DC=net', - allowBlank: false - }); - - column1.push({ - xtype: 'textfield', - name: 'user_attr', - emptyText: 'uid / sAMAccountName', - fieldLabel: gettext('User Attribute Name'), - allowBlank: false - }); - } else if (me.authType === 'pve') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'Proxmox VE authentication server'; - - } else if (me.authType === 'pam') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'linux PAM'; - - } else { - throw 'unknown auth type '; - } - - column1.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Default'), - name: 'default', - uncheckedValue: 0 - }); - - var column2 = []; - - if (me.authType === 'ldap' || me.authType === 'ad') { - column2.push( - { - xtype: 'textfield', - fieldLabel: gettext('Server'), - name: 'server1', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Fallback Server'), - deleteEmpty: !me.isCreate, - name: 'server2' - }, - { - xtype: 'proxmoxintegerfield', - name: 'port', - fieldLabel: gettext('Port'), - minValue: 1, - maxValue: 65535, - emptyText: gettext('Default'), - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'SSL', - name: 'secure', - uncheckedValue: 0 - } - ); - } - - // Two Factor Auth settings - - column2.push({ - xtype: 'proxmoxKVComboBox', - name: 'tfa', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('TFA'), - comboItems: [ ['__default__', Proxmox.Utils.noneText], ['oath', 'OATH'], ['yubico', 'Yubico']], - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=oath_step]').setVisible(value === 'oath'); - me.down('field[name=oath_digits]').setVisible(value === 'oath'); - me.down('field[name=yubico_api_id]').setVisible(value === 'yubico'); - me.down('field[name=yubico_api_key]').setVisible(value === 'yubico'); - me.down('field[name=yubico_url]').setVisible(value === 'yubico'); - } - } - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_step', - value: '', - minValue: 10, - emptyText: Proxmox.Utils.defaultText + ' (30)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH time step' - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_digits', - value: '', - minValue: 6, - maxValue: 8, - emptyText: Proxmox.Utils.defaultText + ' (6)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH password length' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_id', - hidden: true, - fieldLabel: 'Yubico API Id' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_key', - hidden: true, - fieldLabel: 'Yubico API Key' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_url', - hidden: true, - fieldLabel: 'Yubico URL' - }); - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [{ - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }], - onGetValues: function(values) { - if (!values.port) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'port' }); - } - delete values.port; - } - - if (me.isCreate) { - values.type = me.authType; - } - - if (values.tfa === 'oath') { - values.tfa = "type=oath"; - if (values.oath_step) { - values.tfa += ",step=" + values.oath_step; - } - if (values.oath_digits) { - values.tfa += ",digits=" + values.oath_digits; - } - } else if (values.tfa === 'yubico') { - values.tfa = "type=yubico"; - values.tfa += ",id=" + values.yubico_api_id; - values.tfa += ",key=" + values.yubico_api_key; - if (values.yubico_url) { - values.tfa += ",url=" + values.yubico_url; - } - } else { - delete values.tfa; - } - - delete values.oath_step; - delete values.oath_digits; - delete values.yubico_api_id; - delete values.yubico_api_key; - delete values.yubico_url; - - return values; - } - }); - - Ext.applyIf(me, { - url: url, - method: method, - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data || {}; - // just to be sure (should not happen) - if (data.type !== me.authType) { - me.close(); - throw "got wrong auth type"; - } - - if (data.tfa) { - var tfacfg = PVE.Parser.parseTfaConfig(data.tfa); - data.tfa = tfacfg.type; - if (tfacfg.type === 'yubico') { - data.yubico_api_key = tfacfg.key; - data.yubico_api_id = tfacfg.id; - data.yubico_url = tfacfg.url; - } else if (tfacfg.type === 'oath') { - // step is a number before - /*jslint confusion: true*/ - data.oath_step = tfacfg.step; - data.oath_digits = tfacfg.digits; - /*jslint confusion: false*/ - } - } - - me.setValues(data); - } - }); - } - } -}); -Ext.define('PVE.dc.BackupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcBackupEdit'], - - defaultFocus: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.jobid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/cluster/backup'; - method = 'POST'; - } else { - url = '/api2/extjs/cluster/backup/' + me.jobid; - method = 'PUT'; - } - - var vmidField = Ext.create('Ext.form.field.Hidden', { - name: 'vmid' - }); - - /*jslint confusion: true*/ - // 'value' can be assigned a string or an array - var selModeField = Ext.create('Proxmox.form.KVComboBox', { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['include', gettext('Include selected VMs')], - ['all', gettext('All')], - ['exclude', gettext('Exclude selected VMs')] - ], - fieldLabel: gettext('Selection mode'), - name: 'selMode', - value: '' - }); - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - var sel = []; - Ext.Array.each(selected, function(record) { - sel.push(record.data.vmid); - }); - - // to avoid endless recursion suspend the vmidField change - // event temporary as it calls us again - vmidField.suspendEvent('change'); - vmidField.setValue(sel); - vmidField.resumeEvent('change'); - } - } - }); - - var storagesel = Ext.create('PVE.form.StorageSelector', { - fieldLabel: gettext('Storage'), - nodename: 'localhost', - storageContent: 'backup', - allowBlank: false, - name: 'storage' - }); - - var store = new Ext.data.Store({ - model: 'PVEResources', - sorters: { - property: 'vmid', - order: 'ASC' - } - }); - - var vmgrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - disabled: true, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - - var nodesel = Ext.create('PVE.form.NodeSelector', { - name: 'node', - fieldLabel: gettext('Node'), - allowBlank: true, - editable: true, - autoSelect: false, - emptyText: '-- ' + gettext('All') + ' --', - listeners: { - change: function(f, value) { - storagesel.setNodename(value || 'localhost'); - var mode = selModeField.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!value || rec.get('node') === value); - }); - if (mode === 'all') { - sm.selectAll(true); - } - } - } - }); - - var column1 = [ - nodesel, - storagesel, - { - xtype: 'pveDayOfWeekSelector', - name: 'dow', - fieldLabel: gettext('Day of week'), - multiSelect: true, - value: ['sat'], - allowBlank: false - }, - { - xtype: 'timefield', - fieldLabel: gettext('Start Time'), - name: 'starttime', - format: 'H:i', - formatText: 'HH:MM', - value: '00:00', - allowBlank: false - }, - selModeField - ]; - - var column2 = [ - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto' - }, - { - xtype: 'pveEmailNotificationSelector', - fieldLabel: gettext('Email notification'), - name: 'mailnotification', - deleteEmpty: me.isCreate ? false : true, - value: me.isCreate ? 'always' : '' - }, - { - xtype: 'pveCompressionSelector', - fieldLabel: gettext('Compression'), - name: 'compress', - deleteEmpty: me.isCreate ? false : true, - value: 'lzo' - }, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable'), - name: 'enabled', - uncheckedValue: 0, - defaultValue: 1, - checked: true - }, - vmidField - ]; - /*jslint confusion: false*/ - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - onlineHelp: 'chapter_vzdump', - column1: column1, - column2: column2, - onGetValues: function(values) { - if (!values.node) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'node' }); - } - delete values.node; - } - - var selMode = values.selMode; - delete values.selMode; - - if (selMode === 'all') { - values.all = 1; - values.exclude = ''; - delete values.vmid; - } else if (selMode === 'exclude') { - values.all = 1; - values.exclude = values.vmid; - delete values.vmid; - } - return values; - } - }); - - var update_vmid_selection = function(list, mode) { - if (mode !== 'all') { - sm.deselectAll(true); - if (list) { - Ext.Array.each(list.split(','), function(vmid) { - var rec = store.findRecord('vmid', vmid); - if (rec) { - sm.select(rec, true); - } - }); - } - } - }; - - vmidField.on('change', function(f, value) { - var mode = selModeField.getValue(); - update_vmid_selection(value, mode); - }); - - selModeField.on('change', function(f, value, oldValue) { - if (value === 'all') { - sm.selectAll(true); - vmgrid.setDisabled(true); - } else { - vmgrid.setDisabled(false); - } - if (oldValue === 'all') { - sm.deselectAll(true); - vmidField.setValue(''); - } - var list = vmidField.getValue(); - update_vmid_selection(list, value); - }); - - var reload = function() { - store.load({ - params: { type: 'vm' }, - callback: function() { - var node = nodesel.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!node || node.length === 0 || rec.get('node') === node); - }); - var list = vmidField.getValue(); - var mode = selModeField.getValue(); - if (mode === 'all') { - sm.selectAll(true); - } else { - update_vmid_selection(list, mode); - } - } - }); - }; - - Ext.applyIf(me, { - subject: gettext("Backup Job"), - url: url, - method: method, - items: [ ipanel, vmgrid ] - }); - - me.callParent(); - - if (me.isCreate) { - selModeField.setValue('include'); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - - data.dow = data.dow.split(','); - - if (data.all || data.exclude) { - if (data.exclude) { - data.vmid = data.exclude; - data.selMode = 'exclude'; - } else { - data.vmid = ''; - data.selMode = 'all'; - } - } else { - data.selMode = 'include'; - } - - me.setValues(data); - } - }); - } - - reload(); - } -}); - - -Ext.define('PVE.dc.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveDcBackupView'], - - onlineHelp: 'chapter_vzdump', - - allText: '-- ' + gettext('All') + ' --', - allExceptText: gettext('All except {0}'), - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-cluster-backup', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/backup" - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.BackupEdit',{ - jobid: rec.data.id - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/backup', - callback: function() { - reload(); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - stateful: true, - stateId: 'grid-dc-backup', - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.dc.BackupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: gettext('Enabled'), - width: 80, - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true, - disabledCls: 'x-item-enabled', - stopSelection: false - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node', - renderer: function(value) { - if (value) { - return value; - } - return me.allText; - } - }, - { - header: gettext('Day of week'), - width: 200, - sortable: false, - dataIndex: 'dow', - renderer: function(val) { - var dows = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; - var selected = []; - var cur = -1; - val.split(',').forEach(function(day){ - cur++; - var dow = (dows.indexOf(day)+6)%7; - if (cur === dow) { - if (selected.length === 0 || selected[selected.length-1] === 0) { - selected.push(1); - } else { - selected[selected.length-1]++; - } - } else { - while (cur < dow) { - cur++; - selected.push(0); - } - selected.push(1); - } - }); - - cur = -1; - var days = []; - selected.forEach(function(item) { - cur++; - if (item > 2) { - days.push(Ext.Date.dayNames[(cur+1)] + '-' + Ext.Date.dayNames[(cur+item)%7]); - cur += item-1; - } else if (item == 2) { - days.push(Ext.Date.dayNames[cur+1]); - days.push(Ext.Date.dayNames[(cur+2)%7]); - cur++; - } else if (item == 1) { - days.push(Ext.Date.dayNames[(cur+1)%7]); - } - }); - return days.join(', '); - } - }, - { - header: gettext('Start Time'), - width: 60, - sortable: true, - dataIndex: 'starttime' - }, - { - header: gettext('Storage'), - width: 100, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Selection'), - flex: 1, - sortable: false, - dataIndex: 'vmid', - renderer: function(value, metaData, record) { - /*jslint confusion: true */ - if (record.data.all) { - if (record.data.exclude) { - return Ext.String.format(me.allExceptText, record.data.exclude); - } - return me.allText; - } - if (record.data.vmid) { - return record.data.vmid; - } - - return "-"; - } - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-cluster-backup', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'starttime', 'dow', - 'storage', 'node', 'vmid', 'exclude', - 'mailto', - { name: 'enabled', type: 'boolean' }, - { name: 'all', type: 'boolean' }, - { name: 'snapshot', type: 'boolean' }, - { name: 'stop', type: 'boolean' }, - { name: 'suspend', type: 'boolean' }, - { name: 'compress', type: 'boolean' } - ] - }); -}); -Ext.define('PVE.dc.Support', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSupport', - pveGuidePath: '/pve-docs/index.html', - onlineHelp: 'getting_help', - - invalidHtml: '

No valid subscription

' + PVE.Utils.noSubKeyHtml, - - communityHtml: 'Please use the public community forum for any questions.', - - activeHtml: 'Please use our support portal for any questions. You can also use the public community forum to get additional information.', - - bugzillaHtml: '

Bug Tracking

Our bug tracking system is available here.', - - docuHtml: function() { - var me = this; - var guideUrl = window.location.origin + me.pveGuidePath; - var text = Ext.String.format('

Documentation

' - + 'The official Proxmox VE Administration Guide' - + ' is included with this installation and can be browsed at ' - + '{0}', guideUrl); - return text; - }, - - updateActive: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.activeHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateCommunity: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.communityHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateInactive: function(data) { - var me = this; - me.update(me.invalidHtml); - }, - - initComponent: function() { - var me = this; - - var reload = function() { - Proxmox.Utils.API2Request({ - url: '/nodes/localhost/subscription', - method: 'GET', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.update('Unable to load subscription status' + ": " + response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data; - - if (data.status === 'Active') { - if (data.level === 'c') { - me.updateCommunity(data); - } else { - me.updateActive(data); - } - } else { - me.updateInactive(data); - } - } - }); - }; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('pve-security-groups', { - extend: 'Ext.data.Model', - - fields: [ 'group', 'comment', 'digest' ], - idProperty: 'group' -}); - -Ext.define('PVE.SecurityGroupEdit', { - extend: 'Proxmox.window.Edit', - - base_url: "/cluster/firewall/groups", - - allow_iface: false, - - initComponent : function() { - var me = this; - - me.isCreate = (me.group_name === undefined); - - var subject; - - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - - var items = [ - { - xtype: 'textfield', - name: 'group', - value: me.group_name || '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: me.group_comment || '', - fieldLabel: gettext('Comment') - } - ]; - - if (me.isCreate) { - subject = gettext('Security Group'); - } else { - subject = gettext('Security Group') + " '" + me.group_name + "'"; - items.push({ - xtype: 'hiddenfield', - name: 'rename', - value: me.group_name - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - // InputPanel does not have a 'create' property, does it need a 'isCreate' - isCreate: me.isCreate, - items: items - }); - - - Ext.apply(me, { - subject: subject, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.SecurityGroupList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveSecurityGroupList', - - stateful: true, - stateId: 'grid-securitygroups', - - rule_panel: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - base_url: "/cluster/firewall/groups", - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (me.rule_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-security-groups', - proxy: { - type: 'proxmox', - url: '/api2/json' + me.base_url - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('group', oldrec.data.group); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.SecurityGroupEdit', { - digest: rec.data.digest, - group_name: rec.data.group, - group_comment: rec.data.comment - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('PVE.SecurityGroupEdit', {}); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - enableFn: function(rec) { - return (rec && me.base_url); - }, - callback: function() { - reload(); - } - }); - - Ext.apply(me, { - store: store, - tbar: [ '' + gettext('Group') + ':', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: gettext('Group'), dataIndex: 'group', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = '/cluster/firewall/groups/' + rec.data.group; - me.rule_panel.setBaseUrl(url); - }, - deselect: function() { - me.rule_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.SecurityGroups', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveSecurityGroups', - - title: 'Security Groups', - - initComponent: function() { - var me = this; - - var rule_panel = Ext.createWidget('pveFirewallRules', { - region: 'center', - allow_groups: false, - list_refs_url: '/cluster/firewall/refs', - tbar_prefix: '' + gettext('Rules') + ':', - border: false - }); - - var sglist = Ext.createWidget('pveSecurityGroupList', { - region: 'west', - rule_panel: rule_panel, - width: '25%', - border: false, - split: true - }); - - - Ext.apply(me, { - layout: 'border', - items: [ sglist, rule_panel ], - listeners: { - show: function() { - sglist.fireEvent('show', sglist); - } - } - }); - - me.callParent(); - } -}); -/* - * Datacenter config panel, located in the center of the ViewPort after the Datacenter view is selected - */ - -Ext.define('PVE.dc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.dc.Config', - - onlineHelp: 'pve_admin_guide', - - initComponent: function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.items = []; - - Ext.apply(me, { - title: gettext("Datacenter"), - hstateid: 'dctab' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - title: gettext('Summary'), - xtype: 'pveDcSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - }, - { - title: gettext('Cluster'), - xtype: 'pveClusterAdministration', - iconCls: 'fa fa-server', - itemId: 'cluster' - }, - { - xtype: 'pveDcOptionView', - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options' - }); - } - - if (caps.storage['Datastore.Allocate'] || caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveStorageView', - title: gettext('Storage'), - iconCls: 'fa fa-database', - itemId: 'storage' - }); - } - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveDcBackupView', - iconCls: 'fa fa-floppy-o', - title: gettext('Backup'), - itemId: 'backup' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - expandedOnInit: true - }); - } - - me.items.push({ - xtype: 'pveUserView', - groups: ['permissions'], - iconCls: 'fa fa-user', - title: gettext('Users'), - itemId: 'users' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveGroupView', - title: gettext('Groups'), - iconCls: 'fa fa-users', - groups: ['permissions'], - itemId: 'groups' - }, - { - xtype: 'pvePoolView', - title: gettext('Pools'), - iconCls: 'fa fa-tags', - groups: ['permissions'], - itemId: 'pools' - }, - { - xtype: 'pveRoleView', - title: gettext('Roles'), - iconCls: 'fa fa-male', - groups: ['permissions'], - itemId: 'roles' - }, - { - xtype: 'pveAuthView', - title: gettext('Authentication'), - groups: ['permissions'], - iconCls: 'fa fa-key', - itemId: 'domains' - }, - { - xtype: 'pveHAStatus', - title: 'HA', - iconCls: 'fa fa-heartbeat', - itemId: 'ha' - }, - { - title: gettext('Groups'), - groups: ['ha'], - xtype: 'pveHAGroupsView', - iconCls: 'fa fa-object-group', - itemId: 'ha-groups' - }, - { - title: gettext('Fencing'), - groups: ['ha'], - iconCls: 'fa fa-bolt', - xtype: 'pveFencingView', - itemId: 'ha-fencing' - }, - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/cluster/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - iconCls: 'fa fa-shield', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - groups: ['firewall'], - iconCls: 'fa fa-gear', - base_url: '/cluster/firewall/options', - onlineHelp: 'pve_firewall_cluster_wide_setup', - fwtype: 'dc', - itemId: 'firewall-options' - }, - { - xtype: 'pveSecurityGroups', - title: gettext('Security Group'), - groups: ['firewall'], - iconCls: 'fa fa-group', - itemId: 'firewall-sg' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: '/cluster/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: 'IPSet', - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: '/cluster/firewall/ipset', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall-ipset' - }, - { - xtype: 'pveDcSupport', - title: gettext('Support'), - itemId: 'support', - iconCls: 'fa fa-comments-o' - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.dc.NodeView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveDcNodeView', - - title: gettext('Nodes'), - disableSelection: true, - scrollable: true, - - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: 'ID', - width: 40, - sortable: true, - dataIndex: 'nodeid' - }, - { - header: gettext('Online'), - width: 60, - sortable: true, - dataIndex: 'online', - renderer: function(value) { - var cls = (value)?'good':'critical'; - return ''; - } - }, - { - header: gettext('Support'), - width: 100, - sortable: true, - dataIndex: 'level', - renderer: PVE.Utils.render_support_level - }, - { - header: gettext('Server Address'), - width: 115, - sortable: true, - dataIndex: 'ip' - }, - { - header: gettext('CPU usage'), - sortable: true, - width: 110, - dataIndex: 'cpuusage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Memory usage'), - width: 110, - sortable: true, - tdCls: 'x-progressbar-default-cell', - dataIndex: 'memoryusage', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Uptime'), - sortable: true, - dataIndex: 'uptime', - align: 'right', - renderer: Proxmox.Utils.render_uptime - } - ], - - stateful: true, - stateId: 'grid-cluster-nodes', - tools: [ - { - type: 'up', - handler: function(){ - var me = this.up('grid'); - var height = Math.max(me.getHeight()-50, 250); - me.setHeight(height); - } - }, - { - type: 'down', - handler: function(){ - var me = this.up('grid'); - var height = me.getHeight()+50; - me.setHeight(height); - } - } - ] -}, function() { - - Ext.define('pve-dc-nodes', { - extend: 'Ext.data.Model', - fields: [ 'id', 'type', 'name', 'nodeid', 'ip', 'level', 'local', 'online'], - idProperty: 'id' - }); - -}); - -Ext.define('PVE.widget.ProgressBar',{ - extend: 'Ext.Progress', - alias: 'widget.pveProgressBar', - - animate: true, - textTpl: [ - '{percent}%' - ], - - setValue: function(value){ - var me = this; - me.callParent([value]); - - me.removeCls(['warning', 'critical']); - - if (value > 0.89) { - me.addCls('critical'); - } else if (value > 0.59) { - me.addCls('warning'); - } - } -}); -/*jslint confusion: true*/ -Ext.define('pve-cluster-nodes', { - extend: 'Ext.data.Model', - fields: [ - 'node', { type: 'integer', name: 'nodeid' }, 'ring0_addr', 'ring1_addr', - { type: 'integer', name: 'quorum_votes' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/nodes" - }, - idProperty: 'nodeid' -}); - -Ext.define('pve-cluster-info', { - extend: 'Ext.data.Model', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/join" - } -}); - -Ext.define('PVE.ClusterAdministration', { - extend: 'Ext.panel.Panel', - xtype: 'pveClusterAdministration', - - title: gettext('Cluster Administration'), - onlineHelp: 'chapter_pvecm', - - border: false, - defaults: { border: false }, - - viewModel: { - parent: null, - data: { - totem: {}, - nodelist: [], - preferred_node: { - name: '', - fp: '', - addr: '' - }, - isInCluster: false, - nodecount: 0 - } - }, - - items: [ - { - xtype: 'panel', - title: gettext('Cluster Information'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store = Ext.create('Proxmox.data.UpdateStore', { - autoStart: true, - interval: 15 * 1000, - storeid: 'pve-cluster-info', - model: 'pve-cluster-info' - }); - view.store.on('load', this.onLoad, this); - view.on('destroy', view.store.stopUpdate); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records || !records[0].data) { - vm.set('totem', {}); - vm.set('isInCluster', false); - vm.set('nodelist', []); - vm.set('preferred_node', { - name: '', - addr: '', - fp: '' - }); - return; - } - var data = records[0].data; - vm.set('totem', data.totem); - vm.set('isInCluster', !!data.totem.cluster_name); - vm.set('nodelist', data.nodelist); - - var nodeinfo = Ext.Array.findBy(data.nodelist, function (el) { - return el.name === data.preferred_node; - }); - - vm.set('preferred_node', { - name: data.preferred_node, - addr: nodeinfo.pve_addr, - ring_addr: [ nodeinfo.ring0_addr, nodeinfo.ring1_addr ], - fp: nodeinfo.pve_fp - }); - }, - - onCreate: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterCreateWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - }, - - onClusterInfo: function() { - var vm = this.getViewModel(); - var win = Ext.create('PVE.ClusterInfoWindow', { - joinInfo: { - ipAddress: vm.get('preferred_node.addr'), - fingerprint: vm.get('preferred_node.fp'), - ring_addr: vm.get('preferred_node.ring_addr'), - totem: vm.get('totem') - } - }); - win.show(); - }, - - onJoin: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterJoinNodeWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - } - }, - tbar: [ - { - text: gettext('Create Cluster'), - reference: 'createButton', - handler: 'onCreate', - bind: { - disabled: '{isInCluster}' - } - }, - { - text: gettext('Join Information'), - reference: 'addButton', - handler: 'onClusterInfo', - bind: { - disabled: '{!isInCluster}' - } - }, - { - text: gettext('Join Cluster'), - reference: 'joinButton', - handler: 'onJoin', - bind: { - disabled: '{isInCluster}' - } - } - ], - layout: 'hbox', - bodyPadding: 5, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Cluster Name'), - bind: { - value: '{totem.cluster_name}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Config Version'), - bind: { - value: '{totem.config_version}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Number of Nodes'), - labelWidth: 120, - bind: { - value: '{nodecount}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - value: gettext('Standalone node - no cluster defined'), - bind: { - hidden: '{isInCluster}' - }, - flex: 1 - } - ] - }, - { - xtype: 'grid', - title: gettext('Cluster Nodes'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-cluster-nodes', - model: 'pve-cluster-nodes' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'nodeid', - order: 'DESC' - } - })); - Proxmox.Utils.monStoreErrors(view, view.rstore); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records) { - vm.set('nodecount', 0); - return; - } - vm.set('nodecount', records.length); - } - }, - columns: [ - { - header: gettext('Nodename'), - flex: 2, - dataIndex: 'name' - }, - { - header: gettext('ID'), - flex: 1, - dataIndex: 'nodeid' - }, - { - header: gettext('Votes'), - flex: 1, - dataIndex: 'quorum_votes' - }, - { - header: gettext('Ring 0'), - flex: 2, - dataIndex: 'ring0_addr' - }, - { - header: gettext('Ring 1'), - flex: 2, - dataIndex: 'ring1_addr' - } - ] - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.ClusterCreateWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterCreateWindow', - - title: gettext('Create Cluster'), - width: 600, - - method: 'POST', - url: '/cluster/config', - - isCreate: true, - subject: gettext('Cluster'), - showTaskViewer: true, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Cluster Name'), - allowBlank: false, - name: 'clustername' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Ring 0 Address'), - emptyText: gettext("Optional, defaults to IP resolved by node's hostname"), - name: 'ring0_addr', - skipEmptyText: true - } - // TODO: for advanced options: ring1_addr - ] -}); - -Ext.define('PVE.ClusterInfoWindow', { - extend: 'Ext.window.Window', - xtype: 'pveClusterInfoWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 800, - modal: true, - resizable: false, - title: gettext('Cluster Join Information'), - - joinInfo: { - ipAddress: undefined, - fingerprint: undefined, - totem: {} - }, - - items: [ - { - xtype: 'component', - border: false, - padding: '10 10 10 10', - html: gettext("Copy the Join Information here and use it on the node you want to add.") - }, - { - xtype: 'container', - layout: 'form', - border: false, - padding: '0 10 10 10', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('IP Address'), - cbind: { value: '{joinInfo.ipAddress}' }, - editable: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - cbind: { value: '{joinInfo.fingerprint}' }, - editable: false - }, - { - xtype: 'textarea', - inputId: 'pveSerializedClusterInfo', - fieldLabel: gettext('Join Information'), - grow: true, - cbind: { joinInfo: '{joinInfo}' }, - editable: false, - listeners: { - afterrender: function(field) { - if (!field.joinInfo) { - return; - } - var jsons = Ext.JSON.encode(field.joinInfo); - var base64s = Ext.util.Base64.encode(jsons); - field.setValue(base64s); - } - } - } - ] - } - ], - dockedItems: [{ - dock: 'bottom', - xtype: 'toolbar', - items: [{ - xtype: 'button', - handler: function(b) { - var el = document.getElementById('pveSerializedClusterInfo'); - el.select(); - document.execCommand("copy"); - }, - text: gettext('Copy Information') - }] - }] -}); - -Ext.define('PVE.ClusterJoinNodeWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterJoinNodeWindow', - - title: gettext('Cluster Join'), - width: 800, - - method: 'POST', - url: '/cluster/config/join', - - defaultFocus: 'textarea[name=serializedinfo]', - isCreate: true, - submitText: gettext('Join'), - showTaskViewer: true, - - onlineHelp: 'chapter_pvecm', - - viewModel: { - parent: null, - data: { - info: { - fp: '', - ip: '', - ring0Needed: false, - ring1Possible: false, - ring1Needed: false - } - }, - formulas: { - ring0EmptyText: function(get) { - if (get('info.ring0Needed')) { - return gettext("Cannot use default address safely"); - } else { - return gettext("Default: IP resolved by node's hostname"); - } - } - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - '#': { - close: function() { - delete PVE.Utils.silenceAuthFailures; - } - }, - 'proxmoxcheckbox[name=assistedEntry]': { - change: 'onInputTypeChange' - }, - 'textarea[name=serializedinfo]': { - change: 'recomputeSerializedInfo', - enable: 'resetField' - }, - 'proxmoxtextfield[name=ring1_addr]': { - enable: 'ring1Needed' - }, - 'textfield': { - disable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - ring1Needed: function(f) { - var vm = this.getViewModel(); - f.allowBlank = !vm.get('info.ring1Needed'); - }, - onInputTypeChange: function(field, assistedInput) { - var vm = this.getViewModel(); - if (!assistedInput) { - vm.set('info.ring1Possible', true); - } - }, - recomputeSerializedInfo: function(field, value) { - var vm = this.getViewModel(); - var jsons = Ext.util.Base64.decode(value); - var joinInfo = Ext.JSON.decode(jsons, true); - - var info = { - fp: '', - ring1Needed: false, - ring1Possible: false, - ip: '' - }; - - var totem = {}; - if (!(joinInfo && joinInfo.totem)) { - field.valid = false; - } else { - var ring0Needed = false; - if (joinInfo.ring_addr !== undefined) { - ring0Needed = joinInfo.ring_addr[0] !== joinInfo.ipAddress; - } - - info = { - ip: joinInfo.ipAddress, - fp: joinInfo.fingerprint, - ring0Needed: ring0Needed, - ring1Possible: !!joinInfo.totem['interface']['1'], - ring1Needed: !!joinInfo.totem['interface']['1'] - }; - totem = joinInfo.totem; - field.valid = true; - } - - vm.set('info', info); - } - }, - - submit: function() { - // joining may produce temporarily auth failures, ignore as long the task runs - PVE.Utils.silenceAuthFailures = true; - this.callParent(); - }, - - taskDone: function(success) { - delete PVE.Utils.silenceAuthFailures; - if (success) { - var txt = gettext('Cluster join task finished, node certificate may have changed, reload GUI!'); - // ensure user cannot do harm - Ext.getBody().mask(txt, ['pve-static-mask']); - // TaskView may hide above mask, so tell him directly - Ext.Msg.show({ - title: gettext('Join Task Finished'), - icon: Ext.Msg.INFO, - msg: txt - }); - // reload always (if user wasn't faster), but wait a bit for pveproxy - Ext.defer(function() { - window.location.reload(true); - }, 5000); - } - }, - - items: [{ - xtype: 'proxmoxcheckbox', - reference: 'assistedEntry', - name: 'assistedEntry', - submitValue: false, - value: true, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Select if join information should be extracted from pasted cluster information, deselect for manual entering') - }, - boxLabel: gettext('Assisted join: Paste encoded cluster join information and enter password.') - }, - { - xtype: 'textarea', - name: 'serializedinfo', - submitValue: false, - allowBlank: false, - fieldLabel: gettext('Information'), - emptyText: gettext('Paste encoded Cluster Information here'), - validator: function(val) { - return val === '' || this.valid || - gettext('Does not seem like a valid encoded Cluster Information!'); - }, - bind: { - disabled: '{!assistedEntry.checked}', - hidden: '{!assistedEntry.checked}' - }, - value: '' - }, - { - xtype: 'inputpanel', - column1: [ - { - xtype: 'textfield', - fieldLabel: gettext('Peer Address'), - allowBlank: false, - bind: { - value: '{info.ip}', - readOnly: '{assistedEntry.checked}' - }, - name: 'hostname' - }, - { - xtype: 'textfield', - inputType: 'password', - emptyText: gettext("Peer's root password"), - fieldLabel: gettext('Password'), - allowBlank: false, - name: 'password' - } - ], - column2: [ - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Corosync Ring 0'), - bind: { - emptyText: '{ring0EmptyText}', - allowBlank: '{!info.ring0Needed}' - }, - skipEmptyText: true, - name: 'ring0_addr' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Corosync Ring 1'), - skipEmptyText: true, - bind: { - disabled: '{!info.ring1Possible}' - }, - name: 'ring1_addr' - } - ], - columnB: [ - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - allowBlank: false, - bind: { - value: '{info.fp}', - readOnly: '{assistedEntry.checked}' - }, - name: 'fingerprint' - } - ] - }] -}); -/* - * Workspace base class - * - * popup login window when auth fails (call onLogin handler) - * update (re-login) ticket every 15 minutes - * - */ - -Ext.define('PVE.Workspace', { - extend: 'Ext.container.Viewport', - - title: 'Proxmox Virtual Environment', - - loginData: null, // Data from last login call - - onLogin: function(loginData) {}, - - // private - updateLoginData: function(loginData) { - var me = this; - me.loginData = loginData; - Proxmox.Utils.setAuthData(loginData); - - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(loginData.clustername); - - if (loginData.cap) { - Ext.state.Manager.set('GuiCap', loginData.cap); - } - - me.onLogin(loginData); - }, - - // private - showLogin: function() { - var me = this; - - Proxmox.Utils.authClear(); - Proxmox.UserName = null; - me.loginData = null; - - if (!me.login) { - me.login = Ext.create('PVE.window.LoginWindow', { - handler: function(data) { - me.login = null; - me.updateLoginData(data); - Proxmox.Utils.checked_command(function() {}); // display subscription status - } - }); - } - me.onLogin(null); - me.login.show(); - }, - - initComponent : function() { - var me = this; - - Ext.tip.QuickTipManager.init(); - - // fixme: what about other errors - Ext.Ajax.on('requestexception', function(conn, response, options) { - if (response.status == 401 && !PVE.Utils.silenceAuthFailures) { // auth failure - me.showLogin(); - } - }); - - me.callParent(); - - if (!Proxmox.Utils.authOK()) { - me.showLogin(); - } else { - if (me.loginData) { - me.onLogin(me.loginData); - } - } - - Ext.TaskManager.start({ - run: function() { - var ticket = Proxmox.Utils.authOK(); - if (!ticket || !Proxmox.UserName) { - return; - } - - Ext.Ajax.request({ - params: { - username: Proxmox.UserName, - password: ticket - }, - url: '/api2/json/access/ticket', - method: 'POST', - success: function(response, opts) { - var obj = Ext.decode(response.responseText); - me.updateLoginData(obj.data); - } - }); - }, - interval: 15*60*1000 - }); - - } -}); - -Ext.define('PVE.StdWorkspace', { - extend: 'PVE.Workspace', - - alias: ['widget.pveStdWorkspace'], - - // private - setContent: function(comp) { - var me = this; - - var cont = me.child('#content'); - - var lay = cont.getLayout(); - - var cur = lay.getActiveItem(); - - if (comp) { - Proxmox.Utils.setErrorMask(cont, false); - comp.border = false; - cont.add(comp); - if (cur !== null && lay.getNext()) { - lay.next(); - var task = Ext.create('Ext.util.DelayedTask', function(){ - cont.remove(cur); - }); - task.delay(10); - } - } - else { - // helper for cleaning the content when logging out - cont.removeAll(); - } - }, - - selectById: function(nodeid) { - var me = this; - var tree = me.down('pveResourceTree'); - tree.selectById(nodeid); - }, - - onLogin: function(loginData) { - var me = this; - - me.updateUserInfo(); - - if (loginData) { - PVE.data.ResourceStore.startUpdate(); - - Proxmox.Utils.API2Request({ - url: '/version', - method: 'GET', - success: function(response) { - PVE.VersionInfo = response.result.data; - me.updateVersionInfo(); - } - }); - } - }, - - updateUserInfo: function() { - var me = this; - - var ui = me.query('#userinfo')[0]; - - if (Proxmox.UserName) { - var msg = Ext.String.format(gettext("You are logged in as {0}"), "'" + Proxmox.UserName + "'"); - ui.update('
' + msg + '
'); - } else { - ui.update(''); - } - ui.updateLayout(); - }, - - updateVersionInfo: function() { - var me = this; - - var ui = me.query('#versioninfo')[0]; - - if (PVE.VersionInfo) { - var version = PVE.VersionInfo.version + '-' + PVE.VersionInfo.release; - ui.update('Virtual Environment ' + version); - } else { - ui.update('Virtual Environment'); - } - ui.updateLayout(); - }, - - initComponent : function() { - var me = this; - - Ext.History.init(); - - var sprovider = Ext.create('PVE.StateProvider'); - Ext.state.Manager.setProvider(sprovider); - - var selview = Ext.create('PVE.form.ViewSelector'); - - var rtree = Ext.createWidget('pveResourceTree', { - viewFilter: selview.getViewFilter(), - flex: 1, - selModel: { - selType: 'treemodel', - listeners: { - selectionchange: function(sm, selected) { - if (selected.length > 0) { - var n = selected[0]; - var tlckup = { - root: 'PVE.dc.Config', - node: 'PVE.node.Config', - qemu: 'PVE.qemu.Config', - lxc: 'PVE.lxc.Config', - storage: 'PVE.storage.Browser', - pool: 'pvePoolConfig' - }; - var comp = { - xtype: tlckup[n.data.type || 'root'] || - 'pvePanelConfig', - showSearch: (n.data.id === 'root') || - Ext.isDefined(n.data.groupbyid), - pveSelNode: n, - workspace: me, - viewFilter: selview.getViewFilter() - }; - PVE.curSelectedNode = n; - me.setContent(comp); - } - } - } - } - }); - - selview.on('select', function(combo, records) { - if (records) { - var view = combo.getViewFilter(); - rtree.setViewFilter(view); - } - }); - - var caps = sprovider.get('GuiCap'); - - var createVM = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-desktop', - text: gettext("Create VM"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.qemu.CreateWizard', {}); - wiz.show(); - } - }); - - var createCT = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-cube', - text: gettext("Create CT"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.lxc.CreateWizard', {}); - wiz.show(); - } - }); - - sprovider.on('statechange', function(sp, key, value) { - if (key === 'GuiCap' && value) { - caps = value; - createVM.setDisabled(!caps.vms['VM.Allocate']); - createCT.setDisabled(!caps.vms['VM.Allocate']); - } - }); - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - { - region: 'north', - layout: { - type: 'hbox', - align: 'middle' - }, - baseCls: 'x-plain', - defaults: { - baseCls: 'x-plain' - }, - border: false, - margin: '2 0 2 5', - items: [ - { - html: '' + - '' - }, - { - minWidth: 150, - id: 'versioninfo', - html: 'Virtual Environment' - }, - { - xtype: 'pveGlobalSearchField', - tree: rtree - }, - { - flex: 1 - }, - { - pack: 'end', - id: 'userinfo', - stateful: false - }, - { - xtype: 'button', - margin: '0 10 0 3', - iconCls: 'fa black fa-gear', - userCls: 'pointer', - handler: function() { - var win = Ext.create('PVE.window.Settings'); - win.show(); - } - }, - { - xtype: 'proxmoxHelpButton', - hidden: false, - baseCls: 'x-btn', - iconCls: 'fa fa-book x-btn-icon-el-default-toolbar-small ', - listenToGlobalEvent: false, - onlineHelp: 'pve_documentation_index', - text: gettext('Documentation'), - margin: '0 5 0 0' - }, - createVM, - createCT, - { - pack: 'end', - margin: '0 5 0 0', - xtype: 'button', - baseCls: 'x-btn', - iconCls: 'fa fa-sign-out', - text: gettext("Logout"), - handler: function() { - PVE.data.ResourceStore.loadData([], false); - me.showLogin(); - me.setContent(null); - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(undefined); - rt.clearTree(); - - // empty the stores of the StatusPanel child items - var statusPanels = Ext.ComponentQuery.query('pveStatusPanel grid'); - Ext.Array.forEach(statusPanels, function(comp) { - if (comp.getStore()) { - comp.getStore().loadData([], false); - } - }); - } - } - ] - }, - { - region: 'center', - stateful: true, - stateId: 'pvecenter', - minWidth: 100, - minHeight: 100, - id: 'content', - xtype: 'container', - layout: { type: 'card' }, - border: false, - margin: '0 5 0 0', - items: [] - }, - { - region: 'west', - stateful: true, - stateId: 'pvewest', - itemId: 'west', - xtype: 'container', - border: false, - layout: { type: 'vbox', align: 'stretch' }, - margin: '0 0 0 5', - split: true, - width: 200, - items: [ selview, rtree ], - listeners: { - resize: function(panel, width, height) { - var viewWidth = me.getSize().width; - if (width > viewWidth - 100) { - panel.setWidth(viewWidth - 100); - } - } - } - }, - { - xtype: 'pveStatusPanel', - stateful: true, - stateId: 'pvesouth', - itemId: 'south', - region: 'south', - margin:'0 5 5 5', - title: gettext('Logs'), - collapsible: true, - header: false, - height: 200, - split:true, - listeners: { - resize: function(panel, width, height) { - var viewHeight = me.getSize().height; - if (height > (viewHeight - 150)) { - panel.setHeight(viewHeight - 150); - } - } - } - } - ] - }); - - me.callParent(); - - me.updateUserInfo(); - - // on resize, center all modal windows - Ext.on('resize', function(){ - var wins = Ext.ComponentQuery.query('window[modal]'); - if (wins.length > 0) { - wins.forEach(function(win){ - win.alignTo(me, 'c-c'); - }); - } - }); - } -}); - diff --git a/serverside/jsmod/5.4-3/pvemanagerlib.js.original b/serverside/jsmod/5.4-3/pvemanagerlib.js.original deleted file mode 100644 index 6e17ec5..0000000 --- a/serverside/jsmod/5.4-3/pvemanagerlib.js.original +++ /dev/null @@ -1,38347 +0,0 @@ -var pveOnlineHelpInfo = { - "ceph_rados_block_devices" : { - "link" : "/pve-docs/chapter-pvesm.html#ceph_rados_block_devices", - "title" : "Ceph RADOS Block Devices (RBD)" - }, - "chapter_ha_manager" : { - "link" : "/pve-docs/chapter-ha-manager.html#chapter_ha_manager", - "title" : "High Availability" - }, - "chapter_lvm" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_lvm", - "title" : "Logical Volume Manager (LVM)" - }, - "chapter_pct" : { - "link" : "/pve-docs/chapter-pct.html#chapter_pct", - "title" : "Proxmox Container Toolkit" - }, - "chapter_pve_firewall" : { - "link" : "/pve-docs/chapter-pve-firewall.html#chapter_pve_firewall", - "title" : "Proxmox VE Firewall" - }, - "chapter_pveceph" : { - "link" : "/pve-docs/chapter-pveceph.html#chapter_pveceph", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "chapter_pvecm" : { - "link" : "/pve-docs/chapter-pvecm.html#chapter_pvecm", - "title" : "Cluster Manager" - }, - "chapter_pvesr" : { - "link" : "/pve-docs/chapter-pvesr.html#chapter_pvesr", - "title" : "Storage Replication" - }, - "chapter_storage" : { - "link" : "/pve-docs/chapter-pvesm.html#chapter_storage", - "title" : "Proxmox VE Storage" - }, - "chapter_system_administration" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_system_administration", - "title" : "Host System Administration" - }, - "chapter_user_management" : { - "link" : "/pve-docs/chapter-pveum.html#chapter_user_management", - "title" : "User Management" - }, - "chapter_virtual_machines" : { - "link" : "/pve-docs/chapter-qm.html#chapter_virtual_machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "chapter_vzdump" : { - "link" : "/pve-docs/chapter-vzdump.html#chapter_vzdump", - "title" : "Backup and Restore" - }, - "chapter_zfs" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_zfs", - "title" : "ZFS on Linux" - }, - "datacenter_configuration_file" : { - "link" : "/pve-docs/pve-admin-guide.html#datacenter_configuration_file", - "title" : "Datacenter Configuration" - }, - "getting_help" : { - "link" : "/pve-docs/pve-admin-guide.html#getting_help", - "title" : "Getting Help" - }, - "gui_my_settings" : { - "link" : "/pve-docs/chapter-pve-gui.html#gui_my_settings", - "subtitle" : "My Settings", - "title" : "Graphical User Interface" - }, - "ha_manager_fencing" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_fencing", - "subtitle" : "Fencing", - "title" : "High Availability" - }, - "ha_manager_groups" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_groups", - "subtitle" : "Groups", - "title" : "High Availability" - }, - "ha_manager_resource_config" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resource_config", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "ha_manager_resources" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resources", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "pct_configuration" : { - "link" : "/pve-docs/chapter-pct.html#pct_configuration", - "subtitle" : "Configuration", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_images" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_images", - "subtitle" : "Container Images", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_network" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_network", - "subtitle" : "Network", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_storage" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_storage", - "subtitle" : "Container Storage", - "title" : "Proxmox Container Toolkit" - }, - "pct_cpu" : { - "link" : "/pve-docs/chapter-pct.html#pct_cpu", - "subtitle" : "CPU", - "title" : "Proxmox Container Toolkit" - }, - "pct_general" : { - "link" : "/pve-docs/chapter-pct.html#pct_general", - "subtitle" : "General Settings", - "title" : "Proxmox Container Toolkit" - }, - "pct_memory" : { - "link" : "/pve-docs/chapter-pct.html#pct_memory", - "subtitle" : "Memory", - "title" : "Proxmox Container Toolkit" - }, - "pct_migration" : { - "link" : "/pve-docs/chapter-pct.html#pct_migration", - "subtitle" : "Migration", - "title" : "Proxmox Container Toolkit" - }, - "pct_options" : { - "link" : "/pve-docs/chapter-pct.html#pct_options", - "subtitle" : "Options", - "title" : "Proxmox Container Toolkit" - }, - "pct_snapshots" : { - "link" : "/pve-docs/chapter-pct.html#pct_snapshots", - "subtitle" : "Snapshots", - "title" : "Proxmox Container Toolkit" - }, - "pct_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-pct.html#pct_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Containers", - "title" : "Proxmox Container Toolkit" - }, - "pve_admin_guide" : { - "link" : "/pve-docs/pve-admin-guide.html", - "title" : "Proxmox VE Administration Guide" - }, - "pve_ceph_install" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_install", - "subtitle" : "Installation of Ceph Packages", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_monitors" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_monitors", - "subtitle" : "Creating Ceph Monitors", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_osds" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_osds", - "subtitle" : "Creating Ceph OSDs", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_pools" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_pools", - "subtitle" : "Creating Ceph Pools", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_documentation_index" : { - "link" : "/pve-docs/index.html", - "title" : "Proxmox VE Documentation Index" - }, - "pve_firewall_cluster_wide_setup" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_cluster_wide_setup", - "subtitle" : "Cluster Wide Setup", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_host_specific_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_host_specific_configuration", - "subtitle" : "Host Specific Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_aliases" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_aliases", - "subtitle" : "IP Aliases", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_sets" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_sets", - "subtitle" : "IP Sets", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_vm_container_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_vm_container_configuration", - "subtitle" : "VM/Container Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_service_daemons" : { - "link" : "/pve-docs/index.html#_service_daemons", - "title" : "Service Daemons" - }, - "pveceph_fs" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs", - "subtitle" : "CephFS", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pveceph_fs_create" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs_create", - "subtitle" : "Create a CephFS", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pveceph_fs_mds" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs_mds", - "subtitle" : "Metadata Server (MDS)", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pvesr_schedule_time_format" : { - "link" : "/pve-docs/chapter-pvesr.html#pvesr_schedule_time_format", - "subtitle" : "Schedule Format", - "title" : "Storage Replication" - }, - "pveum_authentication_realms" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_authentication_realms", - "subtitle" : "Authentication Realms", - "title" : "User Management" - }, - "pveum_groups" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_groups", - "subtitle" : "Groups", - "title" : "User Management" - }, - "pveum_permission_management" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_permission_management", - "subtitle" : "Permission Management", - "title" : "User Management" - }, - "pveum_pools" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_pools", - "subtitle" : "Pools", - "title" : "User Management" - }, - "pveum_roles" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_roles", - "subtitle" : "Roles", - "title" : "User Management" - }, - "pveum_tfa_auth" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_tfa_auth", - "subtitle" : "Two factor authentication", - "title" : "User Management" - }, - "pveum_users" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_users", - "subtitle" : "Users", - "title" : "User Management" - }, - "qm_bios_and_uefi" : { - "link" : "/pve-docs/chapter-qm.html#qm_bios_and_uefi", - "subtitle" : "BIOS and UEFI", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cloud_init" : { - "link" : "/pve-docs/chapter-qm.html#qm_cloud_init", - "title" : "Cloud-Init Support" - }, - "qm_copy_and_clone" : { - "link" : "/pve-docs/chapter-qm.html#qm_copy_and_clone", - "subtitle" : "Copies and Clones", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cpu" : { - "link" : "/pve-docs/chapter-qm.html#qm_cpu", - "subtitle" : "CPU", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_general_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_general_settings", - "subtitle" : "General Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_hard_disk" : { - "link" : "/pve-docs/chapter-qm.html#qm_hard_disk", - "subtitle" : "Hard Disk", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_memory" : { - "link" : "/pve-docs/chapter-qm.html#qm_memory", - "subtitle" : "Memory", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_migration" : { - "link" : "/pve-docs/chapter-qm.html#qm_migration", - "subtitle" : "Migration", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_network_device" : { - "link" : "/pve-docs/chapter-qm.html#qm_network_device", - "subtitle" : "Network Device", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_options" : { - "link" : "/pve-docs/chapter-qm.html#qm_options", - "subtitle" : "Options", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_os_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_os_settings", - "subtitle" : "OS Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_pci_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_pci_passthrough", - "title" : "PCI(e) Passthrough" - }, - "qm_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-qm.html#qm_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Virtual Machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_system_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_system_settings", - "subtitle" : "System Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_usb_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_usb_passthrough", - "subtitle" : "USB Passthrough", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_virtual_machines_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_virtual_machines_settings", - "subtitle" : "Virtual Machines Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "storage_cephfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cephfs", - "title" : "Ceph Filesystem (CephFS)" - }, - "storage_cifs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cifs", - "title" : "CIFS Backend" - }, - "storage_directory" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_directory", - "title" : "Directory Backend" - }, - "storage_glusterfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_glusterfs", - "title" : "GlusterFS Backend" - }, - "storage_lvm" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvm", - "title" : "LVM Backend" - }, - "storage_lvmthin" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvmthin", - "title" : "LVM thin Backend" - }, - "storage_nfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_nfs", - "title" : "NFS Backend" - }, - "storage_open_iscsi" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_open_iscsi", - "title" : "Open-iSCSI initiator" - }, - "storage_zfspool" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_zfspool", - "title" : "Local ZFS Pool Backend" - }, - "sysadmin_certificate_management" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_certificate_management", - "title" : "Certificate Management" - }, - "sysadmin_network_configuration" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_network_configuration", - "title" : "Network Configuration" - } -}; -Ext.ns('PVE'); - -// 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)) { - var console = { - log: function() {} - }; -} -console.log("Starting PVE Manager"); - -Ext.Ajax.defaultHeaders = { - 'Accept': 'application/json' -}; - -/*jslint confusion: true */ -Ext.define('PVE.Utils', { utilities: { - - // this singleton contains miscellaneous utilities - - toolkit: undefined, // (extjs|touch), set inside Toolkit.js - - bus_match: /^(ide|sata|virtio|scsi)\d+$/, - - log_severity_hash: { - 0: "panic", - 1: "alert", - 2: "critical", - 3: "error", - 4: "warning", - 5: "notice", - 6: "info", - 7: "debug" - }, - - support_level_hash: { - 'c': gettext('Community'), - 'b': gettext('Basic'), - 's': gettext('Standard'), - 'p': gettext('Premium') - }, - - noSubKeyHtml: 'You do not have a valid subscription for this server. Please visit www.proxmox.com to get a list of available options.', - - kvm_ostypes: { - 'Linux': [ - { desc: '4.X/3.X/2.6 Kernel', val: 'l26' }, - { desc: '2.4 Kernel', val: 'l24' } - ], - 'Microsoft Windows': [ - { desc: '10/2016', val: 'win10' }, - { desc: '8.x/2012/2012r2', val: 'win8' }, - { desc: '7/2008r2', val: 'win7' }, - { desc: 'Vista/2008', val: 'w2k8' }, - { desc: 'XP/2003', val: 'wxp' }, - { desc: '2000', val: 'w2k' } - ], - 'Solaris Kernel': [ - { desc: '-', val: 'solaris'} - ], - 'Other': [ - { desc: '-', val: 'other'} - ] - }, - - get_health_icon: function(state, circle) { - if (circle === undefined) { - circle = false; - } - - if (state === undefined) { - state = 'uknown'; - } - - var icon = 'faded fa-question'; - switch(state) { - case 'good': - icon = 'good fa-check'; - break; - case 'warning': - icon = 'warning fa-exclamation'; - break; - case 'critical': - icon = 'critical fa-times'; - break; - default: break; - } - - if (circle) { - icon += '-circle'; - } - - return icon; - }, - - map_ceph_health: { - 'HEALTH_OK':'good', - 'HEALTH_WARN':'warning', - 'HEALTH_ERR':'critical' - }, - - render_ceph_health: function(healthObj) { - var state = { - iconCls: PVE.Utils.get_health_icon(), - text: '' - }; - - if (!healthObj || !healthObj.status) { - return state; - } - - var health = PVE.Utils.map_ceph_health[healthObj.status]; - - state.iconCls = PVE.Utils.get_health_icon(health, true); - state.text = healthObj.status; - - return state; - }, - - render_zfs_health: function(value) { - if (typeof value == 'undefined'){ - return ""; - } - var iconCls = 'question-circle'; - switch (value) { - case 'AVAIL': - case 'ONLINE': - iconCls = 'check-circle good'; - break; - case 'REMOVED': - case 'DEGRADED': - iconCls = 'exclamation-circle warning'; - break; - case 'UNAVAIL': - case 'FAULTED': - case 'OFFLINE': - iconCls = 'times-circle critical'; - break; - default: //unknown - } - - return ' ' + value; - - }, - - get_kvm_osinfo: function(value) { - var info = { base: 'Other' }; // default - if (value) { - Ext.each(Object.keys(PVE.Utils.kvm_ostypes), function(k) { - Ext.each(PVE.Utils.kvm_ostypes[k], function(e) { - if (e.val === value) { - info = { desc: e.desc, base: k }; - } - }); - }); - } - return info; - }, - - render_kvm_ostype: function (value) { - var osinfo = PVE.Utils.get_kvm_osinfo(value); - if (osinfo.desc && osinfo.desc !== '-') { - return osinfo.base + ' ' + osinfo.desc; - } else { - return osinfo.base; - } - }, - - render_hotplug_features: function (value) { - var fa = []; - - if (!value || (value === '0')) { - return gettext('Disabled'); - } - - if (value === '1') { - value = 'disk,network,usb'; - } - - Ext.each(value.split(','), function(el) { - if (el === 'disk') { - fa.push(gettext('Disk')); - } else if (el === 'network') { - fa.push(gettext('Network')); - } else if (el === 'usb') { - fa.push('USB'); - } else if (el === 'memory') { - fa.push(gettext('Memory')); - } else if (el === 'cpu') { - fa.push(gettext('CPU')); - } else { - fa.push(el); - } - }); - - return fa.join(', '); - }, - - render_qga_features: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (' + Proxmox.Utils.disabledText + ')'; - } - var props = PVE.Parser.parsePropertyString(value, 'enabled'); - if (!PVE.Parser.parseBoolean(props.enabled)) { - return Proxmox.Utils.disabledText; - } - - delete props.enabled; - var agentstring = Proxmox.Utils.enabledText; - - Ext.Object.each(props, function(key, value) { - var keystring = '' ; - agentstring += ', ' + key + ': '; - - if (PVE.Parser.parseBoolean(value)) { - agentstring += Proxmox.Utils.enabledText; - } else { - agentstring += Proxmox.Utils.disabledText; - } - }); - - return agentstring; - }, - - render_qemu_machine: function(value) { - return value || (Proxmox.Utils.defaultText + ' (i440fx)'); - }, - - render_qemu_bios: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (SeaBIOS)'; - } else if (value === 'seabios') { - return "SeaBIOS"; - } else if (value === 'ovmf') { - return "OVMF (UEFI)"; - } else { - return value; - } - }, - - render_dc_ha_opts: function(value) { - if (!value) { - return Proxmox.Utils.defaultText; - } else { - return PVE.Parser.printPropertyString(value); - } - }, - render_as_property_string: function(value) { - return (!value) ? Proxmox.Utils.defaultText - : PVE.Parser.printPropertyString(value); - }, - - render_scsihw: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (LSI 53C895A)'; - } else if (value === 'lsi') { - return 'LSI 53C895A'; - } else if (value === 'lsi53c810') { - return 'LSI 53C810'; - } else if (value === 'megasas') { - return 'MegaRAID SAS 8708EM2'; - } else if (value === 'virtio-scsi-pci') { - return 'VirtIO SCSI'; - } else if (value === 'virtio-scsi-single') { - return 'VirtIO SCSI single'; - } else if (value === 'pvscsi') { - return 'VMware PVSCSI'; - } else { - return value; - } - }, - - // fixme: auto-generate this - // for now, please keep in sync with PVE::Tools::kvmkeymaps - kvm_keymaps: { - //ar: 'Arabic', - da: 'Danish', - de: 'German', - 'de-ch': 'German (Swiss)', - 'en-gb': 'English (UK)', - 'en-us': 'English (USA)', - es: 'Spanish', - //et: 'Estonia', - fi: 'Finnish', - //fo: 'Faroe Islands', - fr: 'French', - 'fr-be': 'French (Belgium)', - 'fr-ca': 'French (Canada)', - 'fr-ch': 'French (Swiss)', - //hr: 'Croatia', - hu: 'Hungarian', - is: 'Icelandic', - it: 'Italian', - ja: 'Japanese', - lt: 'Lithuanian', - //lv: 'Latvian', - mk: 'Macedonian', - nl: 'Dutch', - //'nl-be': 'Dutch (Belgium)', - no: 'Norwegian', - pl: 'Polish', - pt: 'Portuguese', - 'pt-br': 'Portuguese (Brazil)', - //ru: 'Russian', - sl: 'Slovenian', - sv: 'Swedish', - //th: 'Thai', - tr: 'Turkish' - }, - - kvm_vga_drivers: { - std: gettext('Standard VGA'), - vmware: gettext('VMware compatible'), - qxl: 'SPICE', - qxl2: 'SPICE dual monitor', - qxl3: 'SPICE three monitors', - qxl4: 'SPICE four monitors', - serial0: gettext('Serial terminal') + ' 0', - serial1: gettext('Serial terminal') + ' 1', - serial2: gettext('Serial terminal') + ' 2', - serial3: gettext('Serial terminal') + ' 3', - virtio: 'VirtIO-GPU', - none: Proxmox.Utils.noneText - }, - - render_kvm_language: function (value) { - if (!value || value === '__default__') { - return Proxmox.Utils.defaultText; - } - var text = PVE.Utils.kvm_keymaps[value]; - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_keymap_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_language('')]]; - Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) { - data.push([key, PVE.Utils.render_kvm_language(value)]); - }); - - return data; - }, - - console_map: { - '__default__': Proxmox.Utils.defaultText + ' (HTML5)', - 'vv': 'SPICE (remote-viewer)', - 'html5': 'HTML5 (noVNC)', - 'xtermjs': 'xterm.js' - }, - - render_console_viewer: function(value) { - value = value || '__default__'; - if (PVE.Utils.console_map[value]) { - return PVE.Utils.console_map[value]; - } - return value; - }, - - console_viewer_array: function() { - return Ext.Array.map(Object.keys(PVE.Utils.console_map), function(v) { - return [v, PVE.Utils.render_console_viewer(v)]; - }); - }, - - render_kvm_vga_driver: function (value) { - if (!value) { - return Proxmox.Utils.defaultText; - } - var vga = PVE.Parser.parsePropertyString(value, 'type'); - var text = PVE.Utils.kvm_vga_drivers[vga.type]; - if (!vga.type) { - text = Proxmox.Utils.defaultText; - } - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_vga_driver_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_vga_driver('')]]; - Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) { - data.push([key, PVE.Utils.render_kvm_vga_driver(value)]); - }); - - return data; - }, - - render_kvm_startup: function(value) { - var startup = PVE.Parser.parseStartup(value); - - var res = 'order='; - if (startup.order === undefined) { - res += 'any'; - } else { - res += startup.order; - } - if (startup.up !== undefined) { - res += ',up=' + startup.up; - } - if (startup.down !== undefined) { - res += ',down=' + startup.down; - } - - return res; - }, - - extractFormActionError: function(action) { - var msg; - switch (action.failureType) { - case Ext.form.action.Action.CLIENT_INVALID: - msg = gettext('Form fields may not be submitted with invalid values'); - break; - case Ext.form.action.Action.CONNECT_FAILURE: - msg = gettext('Connection error'); - var resp = action.response; - if (resp.status && resp.statusText) { - msg += " " + resp.status + ": " + resp.statusText; - } - break; - case Ext.form.action.Action.LOAD_FAILURE: - case Ext.form.action.Action.SERVER_INVALID: - msg = Proxmox.Utils.extractRequestError(action.result, true); - break; - } - return msg; - }, - - format_duration_short: function(ut) { - - if (ut < 60) { - return ut.toFixed(1) + 's'; - } - - if (ut < 3600) { - var mins = ut / 60; - return mins.toFixed(1) + 'm'; - } - - if (ut < 86400) { - var hours = ut / 3600; - return hours.toFixed(1) + 'h'; - } - - var days = ut / 86400; - return days.toFixed(1) + 'd'; - }, - - contentTypes: { - 'images': gettext('Disk image'), - 'backup': gettext('VZDump backup file'), - 'vztmpl': gettext('Container template'), - 'iso': gettext('ISO image'), - 'rootdir': gettext('Container'), - 'snippets': gettext('Snippets') - }, - - storageSchema: { - dir: { - name: Proxmox.Utils.directoryText, - ipanel: 'DirInputPanel', - faIcon: 'folder' - }, - lvm: { - name: 'LVM', - ipanel: 'LVMInputPanel', - faIcon: 'folder' - }, - lvmthin: { - name: 'LVM-Thin', - ipanel: 'LvmThinInputPanel', - faIcon: 'folder' - }, - nfs: { - name: 'NFS', - ipanel: 'NFSInputPanel', - faIcon: 'building' - }, - cifs: { - name: 'CIFS', - ipanel: 'CIFSInputPanel', - faIcon: 'building' - }, - glusterfs: { - name: 'GlusterFS', - ipanel: 'GlusterFsInputPanel', - faIcon: 'building' - }, - iscsi: { - name: 'iSCSI', - ipanel: 'IScsiInputPanel', - faIcon: 'building' - }, - sheepdog: { - name: 'Sheepdog', - ipanel: 'SheepdogInputPanel', - hideAdd: true, - faIcon: 'building' - }, - cephfs: { - name: 'CephFS', - ipanel: 'CephFSInputPanel', - faIcon: 'building' - }, - pvecephfs: { - name: 'CephFS (PVE)', - ipanel: 'CephFSInputPanel', - hideAdd: true, - faIcon: 'building' - }, - rbd: { - name: 'RBD', - ipanel: 'RBDInputPanel', - faIcon: 'building' - }, - pveceph: { - name: 'RBD (PVE)', - ipanel: 'RBDInputPanel', - hideAdd: true, - faIcon: 'building' - }, - zfs: { - name: 'ZFS over iSCSI', - ipanel: 'ZFSInputPanel', - faIcon: 'building' - }, - zfspool: { - name: 'ZFS', - ipanel: 'ZFSPoolInputPanel', - faIcon: 'folder' - }, - drbd: { - name: 'DRBD', - hideAdd: true - } - }, - - format_storage_type: function(value, md, record) { - if (value === 'rbd') { - value = (!record || record.get('monhost') ? 'rbd' : 'pveceph'); - } else if (value === 'cephfs') { - value = (!record || record.get('monhost') ? 'cephfs' : 'pvecephfs'); - } - - var schema = PVE.Utils.storageSchema[value]; - if (schema) { - return schema.name; - } - return Proxmox.Utils.unknownText; - }, - - format_ha: function(value) { - var text = Proxmox.Utils.noneText; - - if (value.managed) { - text = value.state || Proxmox.Utils.noneText; - - text += ', ' + Proxmox.Utils.groupText + ': '; - text += value.group || Proxmox.Utils.noneText; - } - - return text; - }, - - format_content_types: function(value) { - return value.split(',').sort().map(function(ct) { - return PVE.Utils.contentTypes[ct] || ct; - }).join(', '); - }, - - render_storage_content: function(value, metaData, record) { - var data = record.data; - if (Ext.isNumber(data.channel) && - Ext.isNumber(data.id) && - Ext.isNumber(data.lun)) { - return "CH " + - Ext.String.leftPad(data.channel,2, '0') + - " ID " + data.id + " LUN " + data.lun; - } - return data.volid.replace(/^.*:(.*\/)?/,''); - }, - - render_serverity: function (value) { - return PVE.Utils.log_severity_hash[value] || value; - }, - - render_cpu: function(value, metaData, record, rowIndex, colIndex, store) { - - if (!(record.data.uptime && Ext.isNumeric(value))) { - return ''; - } - - var maxcpu = record.data.maxcpu || 1; - - if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) { - return ''; - } - - var per = value * 100; - - return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU'); - }, - - render_size: function(value, metaData, record, rowIndex, colIndex, store) { - /*jslint confusion: true */ - - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value); - }, - - render_bandwidth: function(value) { - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value) + '/s'; - }, - - render_timestamp_human_readable: function(value) { - return Ext.Date.format(new Date(value * 1000), 'l d F Y H:i:s'); - }, - - render_duration: function(value) { - if (value === undefined) { - return '-'; - } - return PVE.Utils.format_duration_short(value); - }, - - calculate_mem_usage: function(data) { - if (!Ext.isNumeric(data.mem) || - data.maxmem === 0 || - data.uptime < 1) { - return -1; - } - - return (data.mem / data.maxmem); - }, - - render_mem_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - if (value > 1 ) { - // we got no percentage but bytes - var mem = value; - var maxmem = record.data.maxmem; - if (!record.data.uptime || - maxmem === 0 || - !Ext.isNumeric(mem)) { - return ''; - } - - return ((mem*100)/maxmem).toFixed(1) + " %"; - } - return (value*100).toFixed(1) + " %"; - }, - - render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var mem = value; - var maxmem = record.data.maxmem; - - if (!record.data.uptime) { - return ''; - } - - if (!(Ext.isNumeric(mem) && maxmem)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - calculate_disk_usage: function(data) { - - if (!Ext.isNumeric(data.disk) || - data.type === 'qemu' || - (data.type === 'lxc' && data.uptime === 0) || - data.maxdisk === 0) { - return -1; - } - - return (data.disk / data.maxdisk); - }, - - render_disk_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - - return (value * 100).toFixed(1) + " %"; - }, - - render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var disk = value; - var maxdisk = record.data.maxdisk; - var type = record.data.type; - - if (!Ext.isNumeric(disk) || - type === 'qemu' || - maxdisk === 0 || - (type === 'lxc' && record.data.uptime === 0)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - get_object_icon_class: function(type, record) { - var status = ''; - var objType = type; - - if (type === 'type') { - // for folder view - objType = record.groupbyid; - } else if (record.template) { - // templates - objType = 'template'; - status = type; - } else { - // everything else - status = record.status + ' ha-' + record.hastate; - } - - var defaults = PVE.tree.ResourceTree.typeDefaults[objType]; - if (defaults && defaults.iconCls) { - var retVal = defaults.iconCls + ' ' + status; - return retVal; - } - - return ''; - }, - - render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) { - - var cls = PVE.Utils.get_object_icon_class(value,record.data); - - var fa = ' '; - return fa + value; - }, - - render_support_level: function(value, metaData, record) { - return PVE.Utils.support_level_hash[value] || '-'; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - /* render functions for new status panel */ - - render_usage: function(val) { - return (val*100).toFixed(2) + '%'; - }, - - render_cpu_usage: function(val, max) { - return Ext.String.format(gettext('{0}% of {1}') + - ' ' + gettext('CPU(s)'), (val*100).toFixed(2), max); - }, - - render_size_usage: function(val, max) { - if (max === 0) { - return gettext('N/A'); - } - return (val*100/max).toFixed(2) + '% '+ '(' + - Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(val), PVE.Utils.render_size(max)) + ')'; - }, - - /* this is different for nodes */ - render_node_cpu_usage: function(value, record) { - return PVE.Utils.render_cpu_usage(value, record.cpus); - }, - - /* this is different for nodes */ - render_node_size_usage: function(record) { - return PVE.Utils.render_size_usage(record.used, record.total); - }, - - render_optional_url: function(value) { - var match; - if (value && (match = value.match(/^https?:\/\//)) !== null) { - return '' + value + ''; - } - return value; - }, - - render_san: function(value) { - var names = []; - if (Ext.isArray(value)) { - value.forEach(function(val) { - if (!Ext.isNumber(val)) { - names.push(val); - } - }); - return names.join('
'); - } - return value; - }, - - render_full_name: function(firstname, metaData, record) { - var first = firstname || ''; - var last = record.data.lastname || ''; - return Ext.htmlEncode(first + " " + last); - }, - - render_u2f_error: function(error) { - var ErrorNames = { - '1': gettext('Other Error'), - '2': gettext('Bad Request'), - '3': gettext('Configuration Unsupported'), - '4': gettext('Device Ineligible'), - '5': gettext('Timeout') - }; - return "U2F Error: " + ErrorNames[error] || Proxmox.Utils.unknownText; - }, - - windowHostname: function() { - return window.location.hostname.replace(Proxmox.Utils.IP6_bracket_match, - function(m, addr, offset, original) { return addr; }); - }, - - openDefaultConsoleWindow: function(consoles, vmtype, vmid, nodename, vmname, cmd) { - var dv = PVE.Utils.defaultViewer(consoles); - PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname, cmd); - }, - - openConsoleWindow: function(viewer, vmtype, vmid, nodename, vmname, cmd) { - // kvm, lxc, shell, upgrade - - if (vmid == undefined && (vmtype === 'kvm' || vmtype === 'lxc')) { - throw "missing vmid"; - } - - if (!nodename) { - throw "no nodename specified"; - } - - if (viewer === 'html5') { - PVE.Utils.openVNCViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'xtermjs') { - Proxmox.Utils.openXtermJsViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'vv') { - var url; - var params = { proxy: PVE.Utils.windowHostname() }; - if (vmtype === 'kvm') { - url = '/nodes/' + nodename + '/qemu/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'lxc') { - url = '/nodes/' + nodename + '/lxc/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'shell') { - url = '/nodes/' + nodename + '/spiceshell'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'upgrade') { - url = '/nodes/' + nodename + '/spiceshell'; - params.upgrade = 1; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'cmd') { - url = '/nodes/' + nodename + '/spiceshell'; - params.cmd = cmd; - PVE.Utils.openSpiceViewer(url, params); - } - } else { - throw "unknown viewer type"; - } - }, - - defaultViewer: function(consoles) { - - var allowSpice, allowXtermjs; - - if (consoles === true) { - allowSpice = true; - allowXtermjs = true; - } else if (typeof consoles === 'object') { - allowSpice = consoles.spice; - allowXtermjs = !!consoles.xtermjs; - } - var vncdefault = 'html5'; - var dv = PVE.VersionInfo.console || vncdefault; - if ((dv === 'vv' && !allowSpice) || (dv === 'xtermjs' && !allowXtermjs)) { - dv = vncdefault; - } - - return dv; - }, - - openVNCViewer: function(vmtype, vmid, nodename, vmname, cmd) { - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - novnc: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - resize: 'off', - cmd: cmd - }); - var nw = window.open("?" + url, '_blank', "innerWidth=745,innerheight=427"); - if (nw) { - nw.focus(); - } - }, - - openSpiceViewer: function(url, params){ - - var downloadWithName = function(uri, name) { - var link = Ext.DomHelper.append(document.body, { - tag: 'a', - href: uri, - css : 'display:none;visibility:hidden;height:0px;' - }); - - // Note: we need to tell android the correct file name extension - // but we do not set 'download' tag for other environments, because - // It can have strange side effects (additional user prompt on firefox) - var andriod = navigator.userAgent.match(/Android/i) ? true : false; - if (andriod) { - link.download = name; - } - - if (link.fireEvent) { - link.fireEvent('onclick'); - } else { - var evt = document.createEvent("MouseEvents"); - evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null); - link.dispatchEvent(evt); - } - }; - - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - failure: function(response, opts){ - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts){ - var raw = "[virt-viewer]\n"; - Ext.Object.each(response.result.data, function(k, v) { - raw += k + "=" + v + "\n"; - }); - var url = 'data:application/x-virt-viewer;charset=UTF-8,' + - encodeURIComponent(raw); - - downloadWithName(url, "pve-spice.vv"); - } - }); - }, - - openTreeConsole: function(tree, record, item, index, e) { - e.stopEvent(); - var nodename = record.data.node; - var vmid = record.data.vmid; - var vmname = record.data.name; - if (record.data.type === 'qemu' && !record.data.template) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var allowSpice = !!response.result.data.spice; - PVE.Utils.openDefaultConsoleWindow(allowSpice, 'kvm', vmid, nodename, vmname); - } - }); - } else if (record.data.type === 'lxc' && !record.data.template) { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - }, - - // test automation helper - call_menu_handler: function(menu, text) { - - var list = menu.query('menuitem'); - - Ext.Array.each(list, function(item) { - if (item.text === text) { - if (item.handler) { - item.handler(); - return 1; - } else { - return undefined; - } - } - }); - }, - - createCmdMenu: function(v, record, item, index, event) { - event.stopEvent(); - if (!(v instanceof Ext.tree.View)) { - v.select(record); - } - var menu; - var template = !!record.data.template; - var type = record.data.type; - - if (template) { - if (type === 'qemu' || type == 'lxc') { - menu = Ext.create('PVE.menu.TemplateMenu', { - pveSelNode: record - }); - } - } else if (type === 'qemu' || - type === 'lxc' || - type === 'node') { - menu = Ext.create('PVE.' + type + '.CmdMenu', { - pveSelNode: record, - nodename: record.data.node - }); - } else { - return; - } - - menu.showAt(event.getXY()); - return menu; - }, - - // helper for deleting field which are set to there default values - delete_if_default: function(values, fieldname, default_val, create) { - if (values[fieldname] === '' || values[fieldname] === default_val) { - if (!create) { - if (values['delete']) { - values['delete'] += ',' + fieldname; - } else { - values['delete'] = fieldname; - } - } - - delete values[fieldname]; - } - }, - - loadSSHKeyFromFile: function(file, callback) { - // ssh-keygen produces 740 bytes for an average 4096 bit rsa key, with - // a user@host comment, 1420 for 8192 bits; current max is 16kbit - // assume: 740*8 for max. 32kbit (5920 byte file) - // round upwards to nearest nice number => 8192 bytes, leaves lots of comment space - if (file.size > 8192) { - Ext.Msg.alert(gettext('Error'), gettext("Invalid file size: ") + file.size); - return; - } - /*global - FileReader - */ - var reader = new FileReader(); - reader.onload = function(evt) { - callback(evt.target.result); - }; - reader.readAsText(file); - }, - - bus_counts: { ide: 4, sata: 6, scsi: 16, virtio: 16 }, - - // types is either undefined (all busses), an array of busses, or a single bus - forEachBus: function(types, func) { - var busses = Object.keys(PVE.Utils.bus_counts); - var i, j, count, cont; - - if (Ext.isArray(types)) { - busses = types; - } else if (Ext.isDefined(types)) { - busses = [ types ]; - } - - // check if we only have valid busses - for (i = 0; i < busses.length; i++) { - if (!PVE.Utils.bus_counts[busses[i]]) { - throw "invalid bus: '" + busses[i] + "'"; - } - } - - for (i = 0; i < busses.length; i++) { - count = PVE.Utils.bus_counts[busses[i]]; - for (j = 0; j < count; j++) { - cont = func(busses[i], j); - if (!cont && cont !== undefined) { - return; - } - } - } - }, - - mp_counts: { mps: 256, unused: 256 }, - - forEachMP: function(func, includeUnused) { - var i, cont; - for (i = 0; i < PVE.Utils.mp_counts.mps; i++) { - cont = func('mp', i); - if (!cont && cont !== undefined) { - return; - } - } - - if (!includeUnused) { - return; - } - - for (i = 0; i < PVE.Utils.mp_counts.unused; i++) { - cont = func('unused', i); - if (!cont && cont !== undefined) { - return; - } - } - }, - - cleanEmptyObjectKeys: function (obj) { - var propName; - for (propName in obj) { - if (obj.hasOwnProperty(propName)) { - if (obj[propName] === null || obj[propName] === undefined) { - delete obj[propName]; - } - } - } - }, - - handleStoreErrorOrMask: function(me, store, regex, callback) { - - me.mon(store, 'load', function (proxy, response, success, operation) { - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - var msg; - - if (operation.error.statusText) { - if (operation.error.statusText.match(regex)) { - callback(me, operation.error); - return; - } else { - msg = operation.error.statusText + ' (' + operation.error.status + ')'; - } - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - showCephInstallOrMask: function(container, msg, nodename, callback){ - var regex = new RegExp("not (installed|initialized)", "i"); - if (msg.match(regex)) { - if (Proxmox.UserName === 'root@pam') { - container.el.mask(); - if (!container.down('pveCephInstallWindow')){ - var isInstalled = msg.match(/not initialized/i) ? true : false; - var win = Ext.create('PVE.ceph.Install', { - nodename: nodename - }); - win.getViewModel().set('isInstalled', isInstalled); - container.add(win); - win.show(); - callback(win); - } - } else { - container.mask(Ext.String.format(gettext('{0} not installed.') + - ' ' + gettext('Log in as root to install.'), 'Ceph'), ['pve-static-mask']); - } - return true; - } else { - return false; - } - } -}, - - singleton: true, - constructor: function() { - var me = this; - Ext.apply(me, me.utilities); - } - -}); - -// ExtJS related things - -Proxmox.Utils.toolkit = 'extjs'; - -// custom PVE specific VTypes -Ext.apply(Ext.form.field.VTypes, { - - 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"', - IP64AddressList: function(v) { - var list = v.split(/[\ \,\;]+/); - var i; - for (i = 0; i < list.length; i++) { - if (list[i] == '') { - continue; - } - - if (!Proxmox.Utils.IP64_match.test(list[i])) { - return false; - } - } - - return true; - }, - IP64AddressListText: gettext('Example') + ': 192.168.1.1,192.168.1.2', - IP64AddressListMask: /[A-Fa-f0-9\,\:\.\;\ ]/ -}); - -Ext.define('PVE.form.field.Display', { - override: 'Ext.form.field.Display', - - setSubmitValue: function(value) { - // do nothing, this is only to allow generalized bindings for the: - // `me.isCreate ? 'textfield' : 'displayfield'` cases we have. - } -}); -// Some configuration values are complex strings - -// so we need parsers/generators for them. - -Ext.define('PVE.Parser', { statics: { - - // this class only contains static functions - - parseACME: function(value) { - if (!value) { - return; - } - - var res = {}; - var errors = false; - - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; //continue - } - - var match_res; - if ((match_res = p.match(/^(?:domains=)?((?:[a-zA-Z0-9\-\.]+[;, ]?)+)$/)) !== null) { - res.domains = match_res[1].split(/[;, ]/); - } else { - errors = true; - return false; - } - }); - - if (errors || !res) { - return; - } - - return res; - }, - - parseBoolean: function(value, default_value) { - if (!Ext.isDefined(value)) { - return default_value; - } - value = value.toLowerCase(); - return value === '1' || - value === 'on' || - value === 'yes' || - value === 'true'; - }, - - parsePropertyString: function(value, defaultKey) { - var res = {}, - error; - - Ext.Array.each(value.split(','), function(p) { - var kv = p.split('=', 2); - if (Ext.isDefined(kv[1])) { - res[kv[0]] = kv[1]; - } else if (Ext.isDefined(defaultKey)) { - if (Ext.isDefined(res[defaultKey])) { - error = 'defaultKey may be only defined once in propertyString'; - return false; // break - } - res[defaultKey] = kv[0]; - } else { - error = 'invalid propertyString, not a key=value pair and no defaultKey defined'; - return false; // break - } - }); - - if (error !== undefined) { - console.error(error); - return; - } - - return res; - }, - - printPropertyString: function(data, defaultKey) { - var stringparts = []; - - Ext.Object.each(data, function(key, value) { - if (defaultKey !== undefined && key === defaultKey) { - stringparts.unshift(value); - } else { - stringparts.push(key + '=' + value); - } - }); - - return stringparts.join(','); - }, - - parseQemuNetwork: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(ne2k_pci|e1000|e1000-82540em|e1000-82544gc|e1000-82545em|vmxnet3|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i)) !== null) { - res.model = match_res[1].toLowerCase(); - if (match_res[3]) { - res.macaddr = match_res[3]; - } - } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) { - res.bridge = match_res[1]; - } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) { - res.rate = match_res[1]; - } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) { - res.tag = match_res[1]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - res.firewall = match_res[1]; - } else if ((match_res = p.match(/^link_down=(\d+)$/)) !== null) { - res.disconnect = match_res[1]; - } else if ((match_res = p.match(/^queues=(\d+)$/)) !== null) { - res.queues = match_res[1]; - } else if ((match_res = p.match(/^trunks=(\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*)$/)) !== null) { - res.trunks = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors || !res.model) { - return; - } - - return res; - }, - - printQemuNetwork: function(net) { - - var netstr = net.model; - if (net.macaddr) { - netstr += "=" + net.macaddr; - } - if (net.bridge) { - netstr += ",bridge=" + net.bridge; - if (net.tag) { - netstr += ",tag=" + net.tag; - } - if (net.firewall) { - netstr += ",firewall=" + net.firewall; - } - } - if (net.rate) { - netstr += ",rate=" + net.rate; - } - if (net.queues) { - netstr += ",queues=" + net.queues; - } - if (net.disconnect) { - netstr += ",link_down=" + net.disconnect; - } - if (net.trunks) { - netstr += ",trunks=" + net.trunks; - } - return netstr; - }, - - parseQemuDrive: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var match_res = key.match(/^([a-z]+)(\d+)$/); - if (!match_res) { - return; - } - res['interface'] = match_res[1]; - res.index = match_res[2]; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - if (k === 'cache' && v === 'off') { - v = 'none'; - } - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - return res; - }, - - printQemuDrive: function(drive) { - - var drivestr = drive.file; - - Ext.Object.each(drive, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'index' || key === 'interface') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseIPConfig: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - if ((match_res = p.match(/^ip=(\S+)$/)) !== null) { - res.ip = match_res[1]; - } else if ((match_res = p.match(/^gw=(\S+)$/)) !== null) { - res.gw = match_res[1]; - } else if ((match_res = p.match(/^ip6=(\S+)$/)) !== null) { - res.ip6 = match_res[1]; - } else if ((match_res = p.match(/^gw6=(\S+)$/)) !== null) { - res.gw6 = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printIPConfig: function(cfg) { - var c = ""; - var str = ""; - if (cfg.ip) { - str += "ip=" + cfg.ip; - c = ","; - } - if (cfg.gw) { - str += c + "gw=" + cfg.gw; - c = ","; - } - if (cfg.ip6) { - str += c + "ip6=" + cfg.ip6; - c = ","; - } - if (cfg.gw6) { - str += c + "gw6=" + cfg.gw6; - c = ","; - } - return str; - }, - - parseOpenVZNetIf: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(';'), function(item) { - if (!item || item.match(/^\s*$/)) { - return; // continue - } - - var data = {}; - Ext.Array.each(item.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - if (match_res[1] === 'bridge'){ - var bridgevlanf = match_res[2]; - var bridge_res = bridgevlanf.match(/^(vmbr(\d+))(v(\d+))?(f)?$/); - if (!bridge_res) { - errors = true; - return false; // break - } - data.bridge = bridge_res[1]; - data.tag = bridge_res[4]; - /*jslint confusion: true*/ - data.firewall = bridge_res[5] ? 1 : 0; - /*jslint confusion: false*/ - } else { - data[match_res[1]] = match_res[2]; - } - }); - - if (errors || !data.ifname) { - errors = true; - return false; // break - } - - data.raw = item; - - res[data.ifname] = data; - }); - - return errors ? undefined: res; - }, - - printOpenVZNetIf: function(netif) { - var netarray = []; - - Ext.Object.each(netif, function(iface, data) { - var tmparray = []; - Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname' , 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) { - var value = data[key]; - if (key === 'bridge'){ - if(data.tag){ - value = value + 'v' + data.tag; - } - if (data.firewall){ - value = value + 'f'; - } - } - if (value) { - tmparray.push(key + '=' + value); - } - - }); - netarray.push(tmparray.join(',')); - }); - - return netarray.join(';'); - }, - - parseLxcNetwork: function(value) { - if (!value) { - return; - } - - var data = {}; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|tag|rate)=(\S+)$/); - if (match_res) { - data[match_res[1]] = match_res[2]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - data.firewall = PVE.Parser.parseBoolean(match_res[1]); - } else { - // todo: simply ignore errors ? - return; // continue - } - }); - - return data; - }, - - printLxcNetwork: function(data) { - var tmparray = []; - Ext.Array.each(['bridge', 'hwaddr', 'mtu', 'name', 'ip', - 'gw', 'ip6', 'gw6', 'firewall', 'tag'], function(key) { - var value = data[key]; - if (value) { - tmparray.push(key + '=' + value); - } - }); - - /*jslint confusion: true*/ - if (data.rate > 0) { - tmparray.push('rate=' + data.rate); - } - /*jslint confusion: false*/ - return tmparray.join(','); - }, - - parseLxcMountPoint: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(.+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - var m = res.file.match(/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):/i); - if (m) { - res.storage = m[1]; - res.type = 'volume'; - } else if (res.file.match(/^\/dev\//)) { - res.type = 'device'; - } else { - res.type = 'bind'; - } - - return res; - }, - - printLxcMountPoint: function(mp) { - var drivestr = mp.file; - - Ext.Object.each(mp, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'type' || key === 'storage') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseStartup: function(value) { - if (value === undefined) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) { - res.order = match_res[2]; - } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) { - res.up = match_res[1]; - } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) { - res.down = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printStartup: function(startup) { - var arr = []; - if (startup.order !== undefined && startup.order !== '') { - arr.push('order=' + startup.order); - } - if (startup.up !== undefined && startup.up !== '') { - arr.push('up=' + startup.up); - } - if (startup.down !== undefined && startup.down !== '') { - arr.push('down=' + startup.down); - } - - return arr.join(','); - }, - - parseQemuSmbios1: function(value) { - var res = {}; - - Ext.Array.each(value.split(','), function(p) { - var kva = p.split('=', 2); - res[kva[0]] = kva[1]; - }); - - return res; - }, - - printQemuSmbios1: function(data) { - - var datastr = ''; - - Ext.Object.each(data, function(key, value) { - if (value === '') { return; } - datastr += (datastr !== '' ? ',' : '') + key + '=' + value; - }); - - return datastr; - }, - - parseTfaConfig: function(value) { - var res = {}; - - Ext.Array.each(value.split(','), function(p) { - var kva = p.split('=', 2); - res[kva[0]] = kva[1]; - }); - - return res; - }, - - parseQemuCpu: function(value) { - if (!value) { - return {}; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - if (!p.match(/\=/)) { - if (Ext.isDefined(res.cpu)) { - errors = true; - return false; // break - } - res.cputype = p; - return; // continue - } - - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - - var k = match_res[1]; - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - res[k] = match_res[2]; - }); - - if (errors || !res.cputype) { - return; - } - - return res; - }, - - printQemuCpu: function(cpu) { - var cpustr = cpu.cputype; - var optstr = ''; - - Ext.Object.each(cpu, function(key, value) { - if (!Ext.isDefined(value) || key === 'cputype') { - return; // continue - } - optstr += ',' + key + '=' + value; - }); - - if (!cpustr) { - if (optstr) { - return 'kvm64' + optstr; - } - return; - } - - return cpustr + optstr; - }, - - parseSSHKey: function(key) { - // |--- options can have quotes--| type key comment - var keyre = /^(?:((?:[^\s"]|\"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/; - var typere = /^(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)$/; - - var m = key.match(keyre); - if (!m) { - return null; - } - if (m.length < 3 || !m[2]) { // [2] is always either type or key - return null; - } - if (m[1] && m[1].match(typere)) { - return { - type: m[1], - key: m[2], - comment: m[3] - }; - } - if (m[2].match(typere)) { - return { - options: m[1], - type: m[2], - key: m[3], - comment: m[4] - }; - } - return null; - } -}}); -/* This state provider keeps part of the state inside - * the browser history. - * - * We compress (shorten) url using dictionary based compression - * i.e. use column separated list instead of url encoded hash: - * #v\d* version/format - * := indicates string values - * :\d+ lookup value in dictionary hash - * #v1:=value1:5:=value2:=value3:... -*/ - -Ext.define('PVE.StateProvider', { - extend: 'Ext.state.LocalStorageProvider', - - // private - setHV: function(name, newvalue, fireEvents) { - var me = this; - - var changes = false; - var oldtext = Ext.encode(me.UIState[name]); - var newtext = Ext.encode(newvalue); - if (newtext != oldtext) { - changes = true; - me.UIState[name] = newvalue; - //console.log("changed old " + name + " " + oldtext); - //console.log("changed new " + name + " " + newtext); - if (fireEvents) { - me.fireEvent("statechange", me, name, { value: newvalue }); - } - } - return changes; - }, - - // private - hslist: [ - // order is important for notifications - // [ name, default ] - ['view', 'server'], - ['rid', 'root'], - ['ltab', 'tasks'], - ['nodetab', ''], - ['storagetab', ''], - ['pooltab', ''], - ['kvmtab', ''], - ['lxctab', ''], - ['dctab', ''] - ], - - hprefix: 'v1', - - compDict: { - cloudinit: 52, - replication: 51, - system: 50, - monitor: 49, - 'ha-fencing': 48, - 'ha-groups': 47, - 'ha-resources': 46, - 'ceph-log': 45, - 'ceph-crushmap':44, - 'ceph-pools': 43, - 'ceph-osdtree': 42, - 'ceph-disklist': 41, - 'ceph-monlist': 40, - 'ceph-config': 39, - ceph: 38, - 'firewall-fwlog': 37, - 'firewall-options': 36, - 'firewall-ipset': 35, - 'firewall-aliases': 34, - 'firewall-sg': 33, - firewall: 32, - apt: 31, - members: 30, - snapshot: 29, - ha: 28, - support: 27, - pools: 26, - syslog: 25, - ubc: 24, - initlog: 23, - openvz: 22, - backup: 21, - resources: 20, - content: 19, - root: 18, - domains: 17, - roles: 16, - groups: 15, - users: 14, - time: 13, - dns: 12, - network: 11, - services: 10, - options: 9, - console: 8, - hardware: 7, - permissions: 6, - summary: 5, - tasks: 4, - clog: 3, - storage: 2, - folder: 1, - server: 0 - }, - - decodeHToken: function(token) { - var me = this; - - var state = {}; - if (!token) { - Ext.Array.each(me.hslist, function(rec) { - state[rec[0]] = rec[1]; - }); - return state; - } - - // return Ext.urlDecode(token); - - var items = token.split(':'); - var prefix = items.shift(); - - if (prefix != me.hprefix) { - return me.decodeHToken(); - } - - Ext.Array.each(me.hslist, function(rec) { - var value = items.shift(); - if (value) { - if (value[0] === '=') { - value = decodeURIComponent(value.slice(1)); - } else { - Ext.Object.each(me.compDict, function(key, cv) { - if (value == cv) { - value = key; - return false; - } - }); - } - } - state[rec[0]] = value; - }); - - return state; - }, - - encodeHToken: function(state) { - var me = this; - - // return Ext.urlEncode(state); - - var ctoken = me.hprefix; - Ext.Array.each(me.hslist, function(rec) { - var value = state[rec[0]]; - if (!Ext.isDefined(value)) { - value = rec[1]; - } - value = encodeURIComponent(value); - if (!value) { - ctoken += ':'; - } else { - var comp = me.compDict[value]; - if (Ext.isDefined(comp)) { - ctoken += ":" + comp; - } else { - ctoken += ":=" + value; - } - } - }); - - return ctoken; - }, - - constructor: function(config){ - var me = this; - - me.callParent([config]); - - me.UIState = me.decodeHToken(); // set default - - var history_change_cb = function(token) { - //console.log("HC " + token); - if (!token) { - var res = window.confirm(gettext('Are you sure you want to navigate away from this page?')); - if (res){ - // process text value and close... - Ext.History.back(); - } else { - Ext.History.forward(); - } - return; - } - - var newstate = me.decodeHToken(token); - Ext.Array.each(me.hslist, function(rec) { - if (typeof newstate[rec[0]] == "undefined") { - return; - } - me.setHV(rec[0], newstate[rec[0]], true); - }); - }; - - var start_token = Ext.History.getToken(); - if (start_token) { - history_change_cb(start_token); - } else { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - - Ext.History.on('change', history_change_cb); - }, - - get: function(name, defaultValue){ - /*jslint confusion: true */ - var me = this; - var data; - - if (typeof me.UIState[name] != "undefined") { - data = { value: me.UIState[name] }; - } else { - data = me.callParent(arguments); - if (!data && name === 'GuiCap') { - data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {} }; - } - } - - //console.log("GET " + name + " " + Ext.encode(data)); - return data; - }, - - clear: function(name){ - var me = this; - - if (typeof me.UIState[name] != "undefined") { - me.UIState[name] = null; - } - - me.callParent(arguments); - }, - - set: function(name, value){ - var me = this; - - //console.log("SET " + name + " " + Ext.encode(value)); - if (typeof me.UIState[name] != "undefined") { - var newvalue = value ? value.value : null; - if (me.setHV(name, newvalue, false)) { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - } else { - me.callParent(arguments); - } - } -}); -Ext.define('PVE.menu.Item', { - extend: 'Ext.menu.Item', - alias: 'widget.pveMenuItem', - - // set to wrap the handler callback in a confirm dialog showing this text - confirmMsg: false, - - // set to focus 'No' instead of 'Yes' button and show a warning symbol - dangerous: false, - - initComponent: function() { - var me = this; - - if (me.handler) { - me.setHandler(me.handler, me.scope); - } - - me.callParent(); - }, - - setHandler: function(fn, scope) { - var me = this; - me.scope = scope; - me.handler = function(button, e) { - var rec, msg; - if (me.confirmMsg) { - msg = me.confirmMsg; - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn === 'yes') { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - } - }); - } else { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - }; - } -}); -Ext.define('PVE.menu.TemplateMenu', { - extend: 'Ext.menu.Menu', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var guestType = me.pveSelNode.data.type; - if (guestType !== 'qemu' && guestType != 'lxc') { - throw "invalid guest type"; - } - - var vmname = me.pveSelNode.data.name; - - var template = me.pveSelNode.data.template; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - me.title = (guestType === 'qemu' ? 'VM ' : 'CT ') + vmid; - - me.items = [ - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: guestType, - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - handler: function() { - var win = Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: template - }); - win.show(); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.button.ConsoleButton', { - extend: 'Ext.button.Split', - alias: 'widget.pveConsoleButton', - - consoleType: 'shell', // one of 'shell', 'kvm', 'lxc', 'upgrade', 'cmd' - - cmd: undefined, - - consoleName: undefined, - - iconCls: 'fa fa-terminal', - - enableSpice: true, - enableXtermjs: true, - - nodename: undefined, - - vmid: 0, - - text: gettext('Console'), - - setEnableSpice: function(enable){ - var me = this; - - me.enableSpice = enable; - me.down('#spicemenu').setDisabled(!enable); - }, - - setEnableXtermJS: function(enable){ - var me = this; - - me.enableXtermjs = enable; - me.down('#xtermjs').setDisabled(!enable); - }, - - handler: function() { - var me = this; - var consoles = { - spice: me.enableSpice, - xtermjs: me.enableXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, me.consoleType, me.vmid, - me.nodename, me.consoleName, me.cmd); - }, - - menu: [ - { - xtype:'menuitem', - text: 'noVNC', - iconCls: 'pve-itype-icon-novnc', - type: 'html5', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - xterm: 'menuitem', - itemId: 'spicemenu', - text: 'SPICE', - type: 'vv', - iconCls: 'pve-itype-icon-virt-viewer', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - text: 'xterm.js', - itemId: 'xtermjs', - iconCls: 'pve-itype-icon-xtermjs', - type: 'xtermjs', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.callParent(); - } -}); -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - * - * does this for the button and every menu item - */ -Ext.define('PVE.button.Split', { - extend: 'Ext.button.Split', - alias: 'widget.pveSplitButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - handlerWrapper: function(button, event) { - var me = this; - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - // confirMsg can be boolean or function - /*jslint confusion: true*/ - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - /*jslint confusion: false*/ - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - callback: function(btn) { - if (btn !== 'yes') { - return; - } - me.realHandler(button, event, rec); - } - }); - } else { - me.realHandler(button, event, rec); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - me.realHandler = me.handler; - me.handler = me.handlerWrapper; - } - - if (me.menu && me.menu.items) { - me.menu.items.forEach(function(item) { - if (item.handler) { - item.realHandler = item.handler; - item.handler = me.handlerWrapper; - } - - if (item.selModel) { - me.mon(item.selModel, "selectionchange", function() { - var rec = item.selModel.getSelection()[0]; - if (!rec || (item.enableFn(rec) === false )) { - item.setDisabled(true); - } else { - item.setDisabled(false); - } - }); - } - }); - } - - me.callParent(); - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); -Ext.define('PVE.controller.StorageEdit', { - extend: 'Ext.app.ViewController', - alias: 'controller.storageEdit', - control: { - 'field[name=content]': { - change: function(field, value) { - var hasBackups = Ext.Array.contains(value, 'backup'); - var maxfiles = this.lookupReference('maxfiles'); - if (!maxfiles) { - return; - } - - if (!hasBackups) { - // clear values which will never be submitted - maxfiles.reset(); - } - maxfiles.setDisabled(!hasBackups); - } - } - } -}); -Ext.define('PVE.qemu.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'suspended': - stopped = false; - suspended = true; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = "VM " + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - hidden: running || suspended, - disabled: running || suspended, - handler: function() { - vm_command('start'); - } - }, - { - text: gettext('Pause'), - iconCls: 'fa fa-fw fa-pause', - hidden: stopped || suspended, - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmpause', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend'); - }); - } - }, - { - text: gettext('Hibernate'), - iconCls: 'fa fa-fw fa-download', - hidden: stopped || suspended, - disabled: stopped || suspended, - tooltip: gettext('Suspend to disk'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmsuspend', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend', { todisk: 1 }); - }); - } - }, - { - text: gettext('Resume'), - iconCls: 'fa fa-fw fa-play', - hidden: !suspended, - handler: function() { - vm_command('resume'); - } - }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: (standalone || !caps.vms['VM.Migrate']) && !caps.vms['VM.Allocate'] && !caps.vms['VM.Clone'] - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - hidden: !caps.vms['VM.Allocate'], - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmtemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var allowSpice = response.result.data.spice; - var allowXtermjs = response.result.data.serial; - var consoles = { - spice: allowSpice, - xtermjs: allowXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, 'kvm', vmid, nodename, vmname); - } - }); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.lxc.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no CT ID specified"; - } - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/lxc/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = 'CT ' + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - disabled: running, - handler: function() { - vm_command('start'); - } - }, -// { -// text: gettext('Suspend'), -// iconCls: 'fa fa-fw fa-pause', -// hidde: suspended, -// disabled: stopped || suspended, -// handler: function() { -// var msg = Proxmox.Utils.format_task_description('vzsuspend', vmid); -// Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { -// if (btn !== 'yes') { -// return; -// } -// -// vm_command('suspend'); -// }); -// } -// }, -// { -// text: gettext('Resume'), -// iconCls: 'fa fa-fw fa-play', -// hidden: !suspended, -// handler: function() { -// vm_command('resume'); -// } -// }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: standalone || !caps.vms['VM.Migrate'] - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'lxc'); - } - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - handler: function() { - var msg = Proxmox.Utils.format_task_description('vztemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.node.CmdMenu', { - extend: 'Ext.menu.Menu', - xtype: 'nodeCmdMenu', - - showSeparator: false, - - items: [ - { - text: gettext('Create VM'), - itemId: 'createvm', - iconCls: 'fa fa-desktop', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.qemu.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { - text: gettext('Create CT'), - itemId: 'createct', - iconCls: 'fa fa-cube', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.lxc.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Bulk Start'), - itemId: 'bulkstart', - iconCls: 'fa fa-fw fa-play', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - itemId: 'bulkstop', - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - itemId: 'bulkmigrate', - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Shell'), - itemId: 'shell', - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - var me = this.up('menu'); - PVE.Utils.openDefaultConsoleWindow(true, 'shell', undefined, me.nodename, undefined); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Wake-on-LAN'), - itemId: 'wakeonlan', - iconCls: 'fa fa-fw fa-power-off', - handler: function() { - var me = this.up('menu'); - Proxmox.Utils.API2Request({ - param: {}, - url: '/nodes/' + me.nodename + '/wakeonlan', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - Ext.Msg.show({ - title: 'Success', - icon: Ext.Msg.INFO, - msg: Ext.String.format(gettext("Wake on LAN packet send for '{0}': '{1}'"), me.nodename, response.result.data) - }); - } - }); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no nodename specified'; - } - - me.title = gettext('Node') + " '" + me.nodename + "'"; - me.callParent(); - - var caps = Ext.state.Manager.get('GuiCap'); - // disable not allowed options - if (!caps.vms['VM.Allocate']) { - me.getComponent('createct').setDisabled(true); - me.getComponent('createvm').setDisabled(true); - } - - if (!caps.nodes['Sys.PowerMgmt']) { - me.getComponent('bulkstart').setDisabled(true); - me.getComponent('bulkstop').setDisabled(true); - me.getComponent('bulkmigrate').setDisabled(true); - me.getComponent('wakeonlan').setDisabled(true); - } - - if (!caps.nodes['Sys.Console']) { - me.getComponent('shell').setDisabled(true); - } - - if (me.pveSelNode.data.running) { - me.getComponent('wakeonlan').setDisabled(true); - } - } -}); -Ext.define('PVE.noVncConsole', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNoVncConsole', - - nodename: undefined, - - vmid: undefined, - - cmd: undefined, - - consoleType: undefined, // lxc, kvm, shell, cmd - - layout: 'fit', - - xtermjs: false, - - border: false, - - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.consoleType) { - throw "no console type specified"; - } - - if (!me.vmid && me.consoleType !== 'shell' && me.consoleType !== 'cmd') { - throw "no VM ID specified"; - } - - // always use same iframe, to avoid running several noVnc clients - // at same time (to avoid performance problems) - var box = Ext.create('Ext.ux.IFrame', { itemid : "vncconsole" }); - - var type = me.xtermjs ? 'xtermjs' : 'novnc'; - Ext.apply(me, { - items: box, - listeners: { - activate: function() { - var queryDict = { - console: me.consoleType, // kvm, lxc, upgrade or shell - vmid: me.vmid, - node: me.nodename, - cmd: me.cmd, - resize: 'scale' - }; - queryDict[type] = 1; - PVE.Utils.cleanEmptyObjectKeys(queryDict); - var url = '/?' + Ext.Object.toQueryString(queryDict); - box.load(url); - } - } - }); - - me.callParent(); - - me.on('afterrender', function() { - me.focus(); - }); - } -}); - -Ext.define('PVE.data.PermPathStore', { - extend: 'Ext.data.Store', - alias: 'store.pvePermPath', - fields: [ 'value' ], - autoLoad: false, - data: [ - {'value': '/'}, - {'value': '/access'}, - {'value': '/nodes'}, - {'value': '/pool'}, - {'value': '/storage'}, - {'value': '/vms'} - ], - - constructor: function(config) { - var me = this; - - config = config || {}; - - me.callParent([config]); - - me.suspendEvents(); - PVE.data.ResourceStore.each(function(record) { - switch (record.get('type')) { - case 'node': - me.add({value: '/nodes/' + record.get('text')}); - break; - - case 'qemu': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'lxc': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'storage': - me.add({value: '/storage/' + record.get('storage')}); - break; - case 'pool': - me.add({value: '/pool/' + record.get('pool')}); - break; - } - }); - me.resumeEvents(); - - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - - me.sort({ - property: 'value', - direction: 'ASC' - }); - } -}); -Ext.define('PVE.data.ResourceStore', { - extend: 'Proxmox.data.UpdateStore', - singleton: true, - - findVMID: function(vmid) { - var me = this, i; - - return (me.findExact('vmid', parseInt(vmid, 10)) >= 0); - }, - - // returns the cached data from all nodes - getNodes: function() { - var me = this; - - var nodes = []; - me.each(function(record) { - if (record.get('type') == "node") { - nodes.push( record.getData() ); - } - }); - - return nodes; - }, - - storageIsShared: function(storage_path) { - var me = this; - - var index = me.findExact('id', storage_path); - - return me.getAt(index).data.shared; - }, - - guestNode: function(vmid) { - var me = this; - - var index = me.findExact('vmid', parseInt(vmid, 10)); - - return me.getAt(index).data.node; - }, - - constructor: function(config) { - // fixme: how to avoid those warnings - /*jslint confusion: true */ - - var me = this; - - config = config || {}; - - var field_defaults = { - type: { - header: gettext('Type'), - type: 'string', - renderer: PVE.Utils.render_resource_type, - sortable: true, - hideable: false, - width: 100 - }, - id: { - header: 'ID', - type: 'string', - hidden: true, - sortable: true, - width: 80 - }, - running: { - header: gettext('Online'), - type: 'boolean', - renderer: Proxmox.Utils.format_boolean, - hidden: true, - convert: function(value, record) { - var info = record.data; - return (Ext.isNumeric(info.uptime) && (info.uptime > 0)); - } - }, - text: { - header: gettext('Description'), - type: 'string', - sortable: true, - width: 200, - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - - if (Ext.isNumeric(info.vmid) && info.vmid > 0) { - text = String(info.vmid); - if (info.name) { - text += " (" + info.name + ')'; - } - } else { // node, pool, storage - text = info[info.type] || info.id; - if (info.node && info.type !== 'node') { - text += " (" + info.node + ")"; - } - } - - return text; - } - }, - vmid: { - header: 'VMID', - type: 'integer', - hidden: true, - sortable: true, - width: 80 - }, - name: { - header: gettext('Name'), - hidden: true, - sortable: true, - type: 'string' - }, - disk: { - header: gettext('Disk usage'), - type: 'integer', - renderer: PVE.Utils.render_disk_usage, - sortable: true, - width: 100, - hidden: true - }, - diskuse: { - header: gettext('Disk usage') + " %", - type: 'number', - sortable: true, - renderer: PVE.Utils.render_disk_usage_percent, - width: 100, - calculate: PVE.Utils.calculate_disk_usage, - sortType: 'asFloat' - }, - maxdisk: { - header: gettext('Disk size'), - type: 'integer', - renderer: PVE.Utils.render_size, - sortable: true, - hidden: true, - width: 100 - }, - mem: { - header: gettext('Memory usage'), - type: 'integer', - renderer: PVE.Utils.render_mem_usage, - sortable: true, - hidden: true, - width: 100 - }, - memuse: { - header: gettext('Memory usage') + " %", - type: 'number', - renderer: PVE.Utils.render_mem_usage_percent, - calculate: PVE.Utils.calculate_mem_usage, - sortType: 'asFloat', - sortable: true, - width: 100 - }, - maxmem: { - header: gettext('Memory size'), - type: 'integer', - renderer: PVE.Utils.render_size, - hidden: true, - sortable: true, - width: 100 - }, - cpu: { - header: gettext('CPU usage'), - type: 'float', - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100 - }, - maxcpu: { - header: gettext('maxcpu'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - diskread: { - header: gettext('Total Disk Read'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - diskwrite: { - header: gettext('Total Disk Write'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netin: { - header: gettext('Total NetIn'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netout: { - header: gettext('Total NetOut'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - template: { - header: gettext('Template'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - uptime: { - header: gettext('Uptime'), - type: 'integer', - renderer: Proxmox.Utils.render_uptime, - sortable: true, - width: 110 - }, - node: { - header: gettext('Node'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - storage: { - header: gettext('Storage'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - pool: { - header: gettext('Pool'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - hastate: { - header: gettext('HA State'), - type: 'string', - defaultValue: 'unmanaged', - hidden: true, - sortable: true - }, - status: { - header: gettext('Status'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - } - }; - - var fields = []; - var fieldNames = []; - Ext.Object.each(field_defaults, function(key, value) { - var field = {name: key, type: value.type}; - if (Ext.isDefined(value.convert)) { - field.convert = value.convert; - } - - if (Ext.isDefined(value.calculate)) { - field.calculate = value.calculate; - } - - if (Ext.isDefined(value.defaultValue)) { - field.defaultValue = value.defaultValue; - } - - fields.push(field); - fieldNames.push(key); - }); - - Ext.define('PVEResources', { - extend: "Ext.data.Model", - fields: fields, - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/resources' - } - }); - - Ext.define('PVETree', { - extend: "Ext.data.Model", - fields: fields, - proxy: { type: 'memory' } - }); - - Ext.apply(config, { - storeid: 'PVEResources', - model: 'PVEResources', - defaultColumns: function() { - var res = []; - Ext.Object.each(field_defaults, function(field, info) { - var fi = Ext.apply({ dataIndex: field }, info); - res.push(fi); - }); - return res; - }, - fieldNames: fieldNames - }); - - me.callParent([config]); - } -}); -Ext.define('pve-domains', { - extend: "Ext.data.Model", - fields: [ - 'realm', 'type', 'comment', 'default', 'tfa', - { - name: 'descr', - // Note: We use this in the RealmComboBox.js (see Bug #125) - convert: function(value, record) { - if (value) { - return value; - } - - var info = record.data; - // return realm if there is no comment - var text = info.comment || info.realm; - - if (info.tfa) { - text += " (+ " + info.tfa + ")"; - } - - return Ext.String.htmlEncode(text); - } - } - ], - idProperty: 'realm', - proxy: { - type: 'proxmox', - url: "/api2/json/access/domains" - } -}); -Ext.define('pve-rrd-node', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - { - name:'iowait', - // percentage - convert: function(value) { - return value*100; - } - }, - 'loadavg', - 'maxcpu', - 'memtotal', - 'memused', - 'netin', - 'netout', - 'roottotal', - 'rootused', - 'swaptotal', - 'swapused', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-guest', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - 'maxcpu', - 'netin', - 'netout', - 'mem', - 'maxmem', - 'disk', - 'maxdisk', - 'diskread', - 'diskwrite', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-storage', { - extend: 'Ext.data.Model', - fields: [ - 'used', - 'total', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); -Ext.define('PVE.form.VlanField', { - extend: 'Ext.form.field.Number', - alias: ['widget.pveVlanField'], - - deleteEmpty: false, - - emptyText: 'no VLAN', - - fieldLabel: gettext('VLAN Tag'), - - allowBlank: true, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val) { - data = {}; - data[me.getName()] = val; - } else if (me.deleteEmpty) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - minValue: 1, - maxValue: 4094 - }); - - me.callParent(); - } -}); -// boolean type including 'Default' (delete property from file) -Ext.define('PVE.form.Boolean', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.booleanfield'], - comboItems: [ - ['__default__', gettext('Default')], - [1, gettext('Yes')], - [0, gettext('No')] - ] -}); -Ext.define('PVE.form.CompressionSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveCompressionSelector'], - comboItems: [ - ['0', Proxmox.Utils.noneText], - ['lzo', 'LZO (' + gettext('fast') + ')'], - ['gzip', 'GZIP (' + gettext('good') + ')'] - ] -}); -Ext.define('PVE.form.PoolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pvePoolSelector'], - - allowBlank: false, - valueField: 'poolid', - displayField: 'poolid', - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: 'poolid' - }); - - Ext.apply(me, { - store: store, - autoSelect: false, - listConfig: { - columns: [ - { - header: gettext('Pool'), - sortable: true, - dataIndex: 'poolid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-pools', { - extend: 'Ext.data.Model', - fields: [ 'poolid', 'comment' ], - proxy: { - type: 'proxmox', - url: "/api2/json/pools" - }, - idProperty: 'poolid' - }); - -}); -Ext.define('PVE.form.PrivilegesSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'pvePrivilegesSelector', - - multiSelect: true, - - initComponent: function() { - var me = this; - - // So me.store is available. - me.callParent(); - - Proxmox.Utils.API2Request({ - url: '/access/roles/Administrator', - method: 'GET', - success: function(response, options) { - var data = [], key; - /*jslint forin: true */ - for (key in response.result.data) { - data.push([key, key]); - } - /*jslint forin: false */ - - me.store.setData(data); - - me.store.sort({ - property: 'key', - direction: 'ASC' - }); - }, - - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -Ext.define('pve-groups', { - extend: 'Ext.data.Model', - fields: [ 'groupid', 'comment' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/groups" - }, - idProperty: 'groupid' -}); - -Ext.define('PVE.form.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveGroupSelector', - - allowBlank: false, - autoSelect: false, - valueField: 'groupid', - displayField: 'groupid', - listConfig: { - columns: [ - { - header: gettext('Group'), - sortable: true, - dataIndex: 'groupid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - }, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: [{ - property: 'groupid' - }] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}); -Ext.define('PVE.form.UserSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUserSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'userid', - displayField: 'userid', - - editable: true, - anyMatch: true, - forceSelection: true, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-users', - sorters: [{ - property: 'userid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('User'), - sortable: true, - dataIndex: 'userid', - flex: 1 - }, - { - header: gettext('Name'), - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load({ params: { enabled: 1 }}); - } - -}, function() { - - Ext.define('pve-users', { - extend: 'Ext.data.Model', - fields: [ - 'userid', 'firstname', 'lastname' , 'email', 'comment', - { type: 'boolean', name: 'enable' }, - { type: 'date', dateFormat: 'timestamp', name: 'expire' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/users" - }, - idProperty: 'userid' - }); - -}); - - -Ext.define('PVE.form.RoleSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveRoleSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'roleid', - displayField: 'roleid', - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: [{ - property: 'roleid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Role'), - sortable: true, - dataIndex: 'roleid', - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-roles', { - extend: 'Ext.data.Model', - fields: [ 'roleid', 'privs' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/roles" - }, - idProperty: 'roleid' - }); - -}); -Ext.define('PVE.form.GuestIDSelector', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveGuestIDSelector', - - allowBlank: false, - - minValue: 100, - - maxValue: 999999999, - - validateExists: undefined, - - loadNextFreeID: false, - - guestType: undefined, - - validator: function(value) { - var me = this; - - if (!Ext.isNumeric(value) || - value < me.minValue || - value > me.maxValue) { - // check is done by ExtJS - return true; - } - - if (me.validateExists === true && !me.exists) { - return me.unknownID; - } - - if (me.validateExists === false && me.exists) { - return me.inUseID; - } - - return true; - }, - - initComponent: function() { - var me = this; - var label = '{0} ID'; - var unknownID = gettext('This {0} ID does not exists'); - var inUseID = gettext('This {0} ID is already in use'); - var type = 'CT/VM'; - - if (me.guestType === 'lxc') { - type = 'CT'; - } else if (me.guestType === 'qemu') { - type = 'VM'; - } - - me.label = Ext.String.format(label, type); - me.unknownID = Ext.String.format(unknownID, type); - me.inUseID = Ext.String.format(inUseID, type); - - Ext.apply(me, { - fieldLabel: me.label, - listeners: { - 'change': function(field, newValue, oldValue) { - if (!Ext.isDefined(me.validateExists)) { - return; - } - Proxmox.Utils.API2Request({ - params: { vmid: newValue }, - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.exists = false; - me.validate(); - }, - failure: function(response, opts) { - me.exists = true; - me.validate(); - } - }); - } - } - }); - - me.callParent(); - - if (me.loadNextFreeID) { - Proxmox.Utils.API2Request({ - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.setRawValue(response.result.data); - } - }); - } - } -}); -Ext.define('PVE.form.MemoryField', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveMemoryField', - - allowBlank: false, - - hotplug: false, - - minValue: 32, - - maxValue: 4178944, - - step: 32, - - value: '512', // qm default - - allowDecimals: false, - - allowExponential: false, - - computeUpDown: function(value) { - var me = this; - - if (!me.hotplug) { - return { up: value + me.step, down: value - me.step }; - } - - var dimm_size = 512; - var prev_dimm_size = 0; - var min_size = 1024; - var current_size = min_size; - var value_up = min_size; - var value_down = min_size; - var value_start = min_size; - - var i, j; - for (j = 0; j < 9; j++) { - for (i = 0; i < 32; i++) { - if ((value >= current_size) && (value < (current_size + dimm_size))) { - value_start = current_size; - value_up = current_size + dimm_size; - value_down = current_size - ((i === 0) ? prev_dimm_size : dimm_size); - } - current_size += dimm_size; - } - prev_dimm_size = dimm_size; - dimm_size = dimm_size*2; - } - - return { up: value_up, down: value_down, start: value_start }; - }, - - onSpinUp: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.up, me.minValue, me.maxValue)); - } - }, - - onSpinDown: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.down, me.minValue, me.maxValue)); - } - }, - - initComponent: function() { - var me = this; - - if (me.hotplug) { - me.minValue = 1024; - - me.on('blur', function(field) { - var value = me.getValue(); - var res = me.computeUpDown(value); - if (value === res.start || value === res.up || value === res.down) { - return; - } - field.setValue(res.up); - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.form.NetworkCardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveNetworkCardSelector', - comboItems: [ - ['e1000', 'Intel E1000'], - ['virtio', 'VirtIO (' + gettext('paravirtualized') + ')'], - ['rtl8139', 'Realtek RTL8139'], - ['vmxnet3', 'VMware vmxnet3'] - ] -}); -Ext.define('PVE.form.DiskFormatSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveDiskFormatSelector', - comboItems: [ - ['raw', gettext('Raw disk image') + ' (raw)'], - ['qcow2', gettext('QEMU image format') + ' (qcow2)'], - ['vmdk', gettext('VMware image format') + ' (vmdk)'] - ] -}); -Ext.define('PVE.form.DiskSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveDiskSelector', - - // can be - // undefined: all - // unused: only unused - // journal_disk: all disks with gpt - diskType: undefined, - - valueField: 'devpath', - displayField: 'devpath', - emptyText: gettext('No Disks unused'), - listConfig: { - columns: [ - { - header: gettext('Device'), - width: 80, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Size'), - width: 60, - sortable: false, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Serial'), - flex: 1, - sortable: true, - dataIndex: 'serial' - } - ] - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - filterOnLoad: true, - model: 'pve-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list", - extraParams: { type: me.diskType } - }, - sorters: [ - { - property : 'devpath', - direction: 'ASC' - } - ] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}, function() { - - Ext.define('pve-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial'], - idProperty: 'devpath' - }); -}); -Ext.define('PVE.form.BusTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveBusSelector', - - noVirtIO: false, - - initComponent: function() { - var me = this; - - me.comboItems = [['ide', 'IDE'], ['sata', 'SATA']]; - - if (!me.noVirtIO) { - me.comboItems.push(['virtio', 'VirtIO Block']); - } - - me.comboItems.push(['scsi', 'SCSI']); - - me.callParent(); - } -}); -Ext.define('PVE.form.ControllerSelector', { - extend: 'Ext.form.FieldContainer', - alias: 'widget.pveControllerSelector', - - statics: { - maxIds: { - ide: 3, - sata: 5, - virtio: 15, - scsi: 13 - } - }, - - noVirtIO: false, - - vmconfig: {}, // used to check for existing devices - - sortByPreviousUsage: function(vmconfig, controllerList) { - - var usedControllers = Ext.clone(PVE.form.ControllerSelector.maxIds); - - var type; - for (type in usedControllers) { - if(usedControllers.hasOwnProperty(type)) { - usedControllers[type] = 0; - } - } - - var property; - for (property in vmconfig) { - if (vmconfig.hasOwnProperty(property)) { - if (property.match(PVE.Utils.bus_match) && !vmconfig[property].match(/media=cdrom/)) { - var foundController = property.match(PVE.Utils.bus_match)[1]; - usedControllers[foundController]++; - } - } - } - - var vmDefaults = PVE.qemu.OSDefaults[vmconfig.ostype]; - - var sortPriority = vmDefaults && vmDefaults.busPriority - ? vmDefaults.busPriority : PVE.qemu.OSDefaults.generic; - - var sortedList = Ext.clone(controllerList); - sortedList.sort(function(a,b) { - if (usedControllers[b] == usedControllers[a]) { - return sortPriority[b] - sortPriority[a]; - } - return usedControllers[b] - usedControllers[a]; - }); - - return sortedList; - }, - - setVMConfig: function(vmconfig, autoSelect) { - var me = this; - - me.vmconfig = Ext.apply({}, vmconfig); - - var clist = ['ide', 'virtio', 'scsi', 'sata']; - var bussel = me.down('field[name=controller]'); - var deviceid = me.down('field[name=deviceid]'); - - if (autoSelect === 'cdrom') { - clist = ['ide', 'scsi', 'sata']; - if (!Ext.isDefined(me.vmconfig.ide2)) { - bussel.setValue('ide'); - deviceid.setValue(2); - return; - } - } else { - // in most cases we want to add a disk to the same controller - // we previously used - clist = me.sortByPreviousUsage(me.vmconfig, clist); - } - - Ext.Array.each(clist, function(controller) { - var confid, i; - bussel.setValue(controller); - for (i = 0; i <= PVE.form.ControllerSelector.maxIds[controller]; i++) { - confid = controller + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - deviceid.setValue(i); - return false; // break - } - } - }); - deviceid.validate(); - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - fieldLabel: gettext('Bus/Device'), - layout: 'hbox', - defaults: { - hideLabel: true - }, - items: [ - { - xtype: 'pveBusSelector', - name: 'controller', - value: PVE.qemu.OSDefaults.generic.busType, - noVirtIO: me.noVirtIO, - allowBlank: false, - flex: 2, - listeners: { - change: function(t, value) { - if (!value) { - return; - } - var field = me.down('field[name=deviceid]'); - field.setMaxValue(PVE.form.ControllerSelector.maxIds[value]); - field.validate(); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'deviceid', - minValue: 0, - maxValue: PVE.form.ControllerSelector.maxIds.ide, - value: '0', - flex: 1, - allowBlank: false, - validator: function(value) { - /*jslint confusion: true */ - if (!me.rendered) { - return; - } - var field = me.down('field[name=controller]'); - var controller = field.getValue(); - var confid = controller + value; - if (Ext.isDefined(me.vmconfig[confid])) { - return "This device is already in use."; - } - return true; - } - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.EmailNotificationSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveEmailNotificationSelector'], - comboItems: [ - ['always', gettext('Always')], - ['failure', gettext('On failure only')] - ] -}); -/*global Proxmox*/ -Ext.define('PVE.form.RealmComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveRealmComboBox'], - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store.on('load', this.onLoad, view); - }, - - onLoad: function(store, records, success) { - if (!success) { - return; - } - var me = this; - var val = me.getValue(); - if (!val || !me.store.findRecord('realm', val)) { - var def = 'pam'; - Ext.each(records, function(rec) { - if (rec.data && rec.data['default']) { - def = rec.data.realm; - } - }); - me.setValue(def); - } - } - }, - - fieldLabel: gettext('Realm'), - name: 'realm', - queryMode: 'local', - allowBlank: false, - editable: false, - forceSelection: true, - autoSelect: false, - triggerAction: 'all', - valueField: 'realm', - displayField: 'descr', - getState: function() { - return { value: this.getValue() }; - }, - applyState : function(state) { - if (state && state.value) { - this.setValue(state.value); - } - }, - stateEvents: [ 'select' ], - stateful: true, // last chosen auth realm is saved between page reloads - id: 'pveloginrealm', // We need stable ids when using stateful, not autogenerated - stateID: 'pveloginrealm', - - needOTP: function(realm) { - var me = this; - // use exact match - var rec = me.store.findRecord('realm', realm, 0, false, false, true); - return rec && rec.data && rec.data.tfa ? rec.data.tfa : undefined; - }, - - store: { - model: 'pve-domains', - autoLoad: true - } -}); -/* - * Top left combobox, used to select a view of the underneath RessourceTree - */ -Ext.define('PVE.form.ViewSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveViewSelector'], - - editable: false, - allowBlank: false, - forceSelection: true, - autoSelect: false, - valueField: 'key', - displayField: 'value', - hideLabel: true, - queryMode: 'local', - - initComponent: function() { - var me = this; - - var default_views = { - server: { - text: gettext('Server View'), - groups: ['node'] - }, - folder: { - text: gettext('Folder View'), - groups: ['type'] - }, - storage: { - text: gettext('Storage View'), - groups: ['node'], - filterfn: function(node) { - return node.data.type === 'storage' || node.data.type === 'node'; - } - }, - pool: { - text: gettext('Pool View'), - groups: ['pool'], - // Pool View only lists VMs and Containers - filterfn: function(node) { - return node.data.type === 'qemu' || node.data.type === 'lxc' || node.data.type === 'openvz' || - node.data.type === 'pool'; - } - } - }; - - var groupdef = []; - Ext.Object.each(default_views, function(viewname, value) { - groupdef.push([viewname, value.text]); - }); - - var store = Ext.create('Ext.data.Store', { - model: 'KeyValue', - proxy: { - type: 'memory', - reader: 'array' - }, - data: groupdef, - autoload: true - }); - - Ext.apply(me, { - store: store, - value: groupdef[0][0], - getViewFilter: function() { - var view = me.getValue(); - return Ext.apply({ id: view }, default_views[view] || default_views.server); - }, - - getState: function() { - return { value: me.getValue() }; - }, - - applyState : function(state, doSelect) { - var view = me.getValue(); - if (state && state.value && (view != state.value)) { - var record = store.findRecord('key', state.value); - if (record) { - me.setValue(state.value, true); - if (doSelect) { - me.fireEvent('select', me, [record]); - } - } - } - }, - stateEvents: [ 'select' ], - stateful: true, - stateId: 'pveview', - id: 'view' - }); - - me.callParent(); - - var statechange = function(sp, key, value) { - if (key === me.id) { - me.applyState(value, true); - } - }; - - var sp = Ext.state.Manager.getProvider(); - me.mon(sp, 'statechange', statechange, me); - } -}); -Ext.define('PVE.form.NodeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveNodeSelector'], - - // invalidate nodes which are offline - onlineValidator: false, - - selectCurNode: false, - - // do not allow those nodes (array) - disallowedNodes: undefined, - - // only allow those nodes (array) - allowedNodes: undefined, - // set default value to empty array, else it inits it with - // null and after the store load it is an empty array, - // triggering dirtychange - value: [], - valueField: 'node', - displayField: 'node', - store: { - fields: [ 'node', 'cpu', 'maxcpu', 'mem', 'maxmem', 'uptime' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes' - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - }, - { - property : 'mem', - direction: 'DESC' - } - ] - }, - - listConfig: { - columns: [ - { - header: gettext('Node'), - dataIndex: 'node', - sortable: true, - hideable: false, - flex: 1 - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 100, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100, - dataIndex: 'cpu' - } - ] - }, - - validator: function(value) { - /*jslint confusion: true */ - var me = this; - if (!me.onlineValidator || (me.allowBlank && !value)) { - return true; - } - - var offline = []; - var notAllowed = []; - - Ext.Array.each(value.split(/\s*,\s*/), function(node) { - var rec = me.store.findRecord(me.valueField, node); - if (!(rec && rec.data) || !Ext.isNumeric(rec.data.mem)) { - offline.push(node); - } else if (me.allowedNodes && !Ext.Array.contains(me.allowedNodes, node)) { - notAllowed.push(node); - } - }); - - if (value && notAllowed.length !== 0) { - return "Node " + notAllowed.join(', ') + " is not allowed for this action!"; - } - - if (value && offline.length !== 0) { - return "Node " + offline.join(', ') + " seems to be offline!"; - } - return true; - }, - - initComponent: function() { - var me = this; - - if (me.selectCurNode && PVE.curSelectedNode && PVE.curSelectedNode.data.node) { - me.preferredValue = PVE.curSelectedNode.data.node; - } - - me.callParent(); - me.getStore().load(); - - // filter out disallowed nodes - me.getStore().addFilter(new Ext.util.Filter({ - filterFn: function(item) { - if (Ext.isArray(me.disallowedNodes)) { - return !Ext.Array.contains(me.disallowedNodes, item.data.node); - } else { - return true; - } - } - })); - - me.mon(me.getStore(), 'load', function(){ - me.isValid(); - }); - } -}); -Ext.define('PVE.form.FileSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFileSelector', - - editable: true, - anyMatch: true, - forceSelection: true, - - listeners: { - afterrender: function() { - var me = this; - if (!me.disabled) { - me.setStorage(me.storage, me.nodename); - } - } - }, - - setStorage: function(storage, nodename) { - var me = this; - - var change = false; - if (storage && (me.storage !== storage)) { - me.storage = storage; - change = true; - } - - if (nodename && (me.nodename !== nodename)) { - me.nodename = nodename; - change = true; - } - - if (!(me.storage && me.nodename && change)) { - return; - } - - var url = '/api2/json/nodes/' + me.nodename + '/storage/' + me.storage + '/content'; - if (me.storageContent) { - url += '?content=' + me.storageContent; - } - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - me.store.removeAll(); - me.store.load(); - }, - - setNodename: function(nodename) { - this.setStorage(undefined, nodename); - }, - - store: { - model: 'pve-storage-content' - }, - - allowBlank: false, - autoSelect: false, - valueField: 'volid', - displayField: 'text', - - listConfig: { - width: 600, - columns: [ - { - header: gettext('Name'), - dataIndex: 'text', - hideable: false, - flex: 1 - }, - { - header: gettext('Format'), - width: 60, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - dataIndex: 'size', - renderer: Proxmox.Utils.format_size - } - ] - } -}); -Ext.define('PVE.form.StorageSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveStorageSelector', - - allowBlank: false, - valueField: 'storage', - displayField: 'storage', - listConfig: { - columns: [ - { - header: gettext('Name'), - dataIndex: 'storage', - hideable: false, - flex: 1 - }, - { - header: gettext('Type'), - width: 60, - dataIndex: 'type' - }, - { - header: gettext('Avail'), - width: 80, - dataIndex: 'avail', - renderer: Proxmox.Utils.format_size - }, - { - header: gettext('Capacity'), - width: 80, - dataIndex: 'total', - renderer: Proxmox.Utils.format_size - } - ] - }, - - reloadStorageList: function() { - var me = this; - if (!me.nodename) { - return; - } - - var params = { - format: 1 - }; - var url = '/api2/json/nodes/' + me.nodename + '/storage'; - if (me.storageContent) { - params.content = me.storageContent; - } - if (me.targetNode) { - params.target = me.targetNode; - params.enabled = 1; // skip disabled storages - } - me.store.setProxy({ - type: 'proxmox', - url: url, - extraParams: params - }); - - me.store.load(); - - }, - - setTargetNode: function(targetNode) { - var me = this; - - if (!targetNode || (me.targetNode === targetNode)) { - return; - } - - me.targetNode = targetNode; - - me.reloadStorageList(); - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.reloadStorageList(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - var store = Ext.create('Ext.data.Store', { - model: 'pve-storage-status', - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - if (nodename) { - me.setNodename(nodename); - } - } -}, function() { - - Ext.define('pve-storage-status', { - extend: 'Ext.data.Model', - fields: [ 'storage', 'active', 'type', 'avail', 'total' ], - idProperty: 'storage' - }); - -}); -Ext.define('PVE.form.DiskStorageSelector', { - extend: 'Ext.container.Container', - alias: 'widget.pveDiskStorageSelector', - - layout: 'fit', - defaults: { - margin: '0 0 5 0' - }, - - // the fieldLabel for the storageselector - storageLabel: gettext('Storage'), - - // the content to show (e.g., images or rootdir) - storageContent: undefined, - - // if true, selects the first available storage - autoSelect: false, - - allowBlank: false, - emptyText: '', - - // hides the selection field - // this is always hidden on creation, - // and only shown when the storage needs a selection and - // hideSelection is not true - hideSelection: undefined, - - // hides the size field (e.g, for the efi disk dialog) - hideSize: false, - - // sets the intial size value - // string because else we get a type confusion - defaultSize: '32', - - changeStorage: function(f, value) { - var me = this; - var formatsel = me.getComponent('diskformat'); - var hdfilesel = me.getComponent('hdimage'); - var hdsizesel = me.getComponent('disksize'); - - // initial store load, and reset/deletion of the storage - if (!value) { - hdfilesel.setDisabled(true); - hdfilesel.setVisible(false); - - formatsel.setDisabled(true); - return; - } - - var rec = f.store.getById(value); - // if the storage is not defined, or valid, - // we cannot know what to enable/disable - if (!rec) { - return; - } - - var selectformat = false; - if (rec.data.format) { - var format = rec.data.format[0]; // 0 is the formats, 1 the default in the backend - delete format.subvol; // we never need subvol in the gui - selectformat = (Ext.Object.getSize(format) > 1); - } - - var select = !!rec.data.select_existing && !me.hideSelection; - - formatsel.setDisabled(!selectformat); - formatsel.setValue(selectformat ? 'qcow2' : 'raw'); - - hdfilesel.setDisabled(!select); - hdfilesel.setVisible(select); - if (select) { - hdfilesel.setStorage(value); - } - - hdsizesel.setDisabled(select || me.hideSize); - hdsizesel.setVisible(!select && !me.hideSize); - }, - - setNodename: function(nodename) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - var hdfilesel = me.getComponent('hdimage'); - - hdstorage.setNodename(nodename); - hdfilesel.setNodename(nodename); - }, - - setDisabled: function(value) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - - // reset on disable - if (value) { - hdstorage.setValue(); - } - hdstorage.setDisabled(value); - - // disabling does not always fire this event and we do not need - // the value of the validity - hdstorage.fireEvent('validitychange'); - }, - - initComponent: function() { - var me = this; - - me.items = [ - { - xtype: 'pveStorageSelector', - itemId: 'hdstorage', - name: 'hdstorage', - reference: 'hdstorage', - fieldLabel: me.storageLabel, - nodename: me.nodename, - storageContent: me.storageContent, - disabled: me.disabled, - autoSelect: me.autoSelect, - allowBlank: me.allowBlank, - emptyText: me.emptyText, - listeners: { - change: { - fn: me.changeStorage, - scope: me - } - } - }, - { - xtype: 'pveFileSelector', - name: 'hdimage', - reference: 'hdimage', - itemId: 'hdimage', - fieldLabel: gettext('Disk image'), - nodename: me.nodename, - disabled: true, - hidden: true - }, - { - xtype: 'numberfield', - itemId: 'disksize', - reference: 'disksize', - name: 'disksize', - fieldLabel: gettext('Disk size') + ' (GiB)', - hidden: me.hideSize, - disabled: me.hideSize, - minValue: 0.001, - maxValue: 128*1024, - decimalPrecision: 3, - value: me.defaultSize, - allowBlank: false - }, - { - xtype: 'pveDiskFormatSelector', - itemId: 'diskformat', - reference: 'diskformat', - name: 'diskformat', - fieldLabel: gettext('Format'), - nodename: me.nodename, - disabled: true, - hidden: me.storageContent === 'rootdir', - value: 'qcow2', - allowBlank: false - } - ]; - - // use it to disable the children but not ourself - me.disabled = false; - - me.callParent(); - } -}); -Ext.define('PVE.form.BridgeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.BridgeSelector'], - - bridgeType: 'any_bridge', // bridge, OVSBridge or any_bridge - - store: { - fields: [ 'iface', 'active', 'type' ], - filterOnLoad: true, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }, - valueField: 'iface', - displayField: 'iface', - listConfig: { - columns: [ - { - header: gettext('Bridge'), - dataIndex: 'iface', - hideable: false, - width: 100 - }, - { - header: gettext('Active'), - width: 60, - dataIndex: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/network?type=' + - me.bridgeType - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.PCISelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pvePCISelector', - - store: { - fields: [ 'id','vendor_name', 'device_name', 'vendor', 'device', 'iommugroup', 'mdev' ], - filterOnLoad: true, - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }, - - autoSelect: false, - valueField: 'id', - displayField: 'id', - - // can contain a load callback for the store - // useful to determine the state of the IOMMU - onLoadCallBack: undefined, - - listConfig: { - width: 800, - columns: [ - { - header: 'ID', - dataIndex: 'id', - width: 80 - }, - { - header: gettext('IOMMU Group'), - dataIndex: 'iommugroup', - width: 50 - }, - { - header: gettext('Vendor'), - dataIndex: 'vendor_name', - flex: 2 - }, - { - header: gettext('Device'), - dataIndex: 'device_name', - flex: 6 - }, - { - header: gettext('Mediated Devices'), - dataIndex: 'mdev', - flex: 1, - renderer: function(val) { - return Proxmox.Utils.format_boolean(!!val); - } - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - if (me.onLoadCallBack !== undefined) { - me.mon(me.getStore(), 'load', me.onLoadCallBack); - } - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.MDevSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveMDevSelector', - - store: { - fields: [ 'type','available', 'description' ], - filterOnLoad: true, - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ] - }, - autoSelect: false, - valueField: 'type', - displayField: 'type', - listConfig: { - columns: [ - { - header: gettext('Type'), - dataIndex: 'type', - flex: 1 - }, - { - header: gettext('Available'), - dataIndex: 'available', - width: 80 - }, - { - header: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value) { - if (!value) { - return ''; - } - - return value.split('\n').join('
'); - } - } - ] - }, - - setPciID: function(pciid, force) { - var me = this; - - if (!force && (!pciid || (me.pciid === pciid))) { - return; - } - - me.pciid = pciid; - me.updateProxy(); - }, - - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - me.updateProxy(); - }, - - updateProxy: function() { - var me = this; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci/' + me.pciid + '/mdev' - }); - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no node name specified'; - } - - me.callParent(); - - if (me.pciid) { - me.setPciID(me.pciid, true); - } - } -}); - -Ext.define('PVE.form.SecurityGroupsSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveSecurityGroupsSelector'], - - valueField: 'group', - displayField: 'group', - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'group', 'comment' ], - idProperty: 'group', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/groups" - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Security Group'), - dataIndex: 'group', - hideable: false, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPRefSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPRefSelector'], - - base_url: undefined, - - preferredValue: '', // hack: else Form sets dirty flag? - - ref_type: undefined, // undefined = any [undefined, 'ipset' or 'alias'] - - valueField: 'ref', - displayField: 'ref', - - initComponent: function() { - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - - var url = "/api2/json" + me.base_url; - if (me.ref_type) { - url += "?type=" + me.ref_type; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'type', 'name', 'ref', 'comment' ], - idProperty: 'ref', - proxy: { - type: 'proxmox', - url: url - }, - sorters: { - property: 'ref', - order: 'DESC' - } - }); - - var disable_query_for_ips = function(f, value) { - if (value === null || - value.match(/^\d/)) { // IP address starts with \d - f.queryDelay = 9999999999; // hack: disbale with long delay - } else { - f.queryDelay = 10; - } - }; - - var columns = []; - - if (!me.ref_type) { - columns.push({ - header: gettext('Type'), - dataIndex: 'type', - hideable: false, - width: 60 - }); - } - - columns.push( - { - header: gettext('Name'), - dataIndex: 'ref', - hideable: false, - width: 140 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ); - - Ext.apply(me, { - store: store, - listConfig: { columns: columns } - }); - - me.on('change', disable_query_for_ips); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPProtocolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPProtocolSelector'], - valueField: 'p', - displayField: 'p', - listConfig: { - columns: [ - { - header: gettext('Protocol'), - dataIndex: 'p', - hideable: false, - sortable: false, - width: 100 - }, - { - header: gettext('Number'), - dataIndex: 'n', - hideable: false, - sortable: false, - width: 50 - }, - { - header: gettext('Description'), - dataIndex: 'd', - hideable: false, - sortable: false, - flex: 1 - } - ] - }, - store: { - fields: [ 'p', 'd', 'n'], - data: [ - { p: 'tcp', n: 6, d: 'Transmission Control Protocol' }, - { p: 'udp', n: 17, d: 'User Datagram Protocol' }, - { p: 'icmp', n: 1, d: 'Internet Control Message Protocol' }, - { p: 'igmp', n: 2, d: 'Internet Group Management' }, - { p: 'ggp', n: 3, d: 'gateway-gateway protocol' }, - { p: 'ipencap', n: 4, d: 'IP encapsulated in IP' }, - { p: 'st', n: 5, d: 'ST datagram mode' }, - { p: 'egp', n: 8, d: 'exterior gateway protocol' }, - { p: 'igp', n: 9, d: 'any private interior gateway (Cisco)' }, - { p: 'pup', n: 12, d: 'PARC universal packet protocol' }, - { p: 'hmp', n: 20, d: 'host monitoring protocol' }, - { p: 'xns-idp', n: 22, d: 'Xerox NS IDP' }, - { p: 'rdp', n: 27, d: '"reliable datagram" protocol' }, - { p: 'iso-tp4', n: 29, d: 'ISO Transport Protocol class 4 [RFC905]' }, - { p: 'dccp', n: 33, d: 'Datagram Congestion Control Prot. [RFC4340]' }, - { p: 'xtp', n: 36, d: 'Xpress Transfer Protocol' }, - { p: 'ddp', n: 37, d: 'Datagram Delivery Protocol' }, - { p: 'idpr-cmtp', n: 38, d: 'IDPR Control Message Transport' }, - { p: 'ipv6', n: 41, d: 'Internet Protocol, version 6' }, - { p: 'ipv6-route', n: 43, d: 'Routing Header for IPv6' }, - { p: 'ipv6-frag', n: 44, d: 'Fragment Header for IPv6' }, - { p: 'idrp', n: 45, d: 'Inter-Domain Routing Protocol' }, - { p: 'rsvp', n: 46, d: 'Reservation Protocol' }, - { p: 'gre', n: 47, d: 'General Routing Encapsulation' }, - { p: 'esp', n: 50, d: 'Encap Security Payload [RFC2406]' }, - { p: 'ah', n: 51, d: 'Authentication Header [RFC2402]' }, - { p: 'skip', n: 57, d: 'SKIP' }, - { p: 'ipv6-icmp', n: 58, d: 'ICMP for IPv6' }, - { p: 'ipv6-nonxt', n: 59, d: 'No Next Header for IPv6' }, - { p: 'ipv6-opts', n: 60, d: 'Destination Options for IPv6' }, - { p: 'vmtp', n: 81, d: 'Versatile Message Transport' }, - { p: 'eigrp', n: 88, d: 'Enhanced Interior Routing Protocol (Cisco)' }, - { p: 'ospf', n: 89, d: 'Open Shortest Path First IGP' }, - { p: 'ax.25', n: 93, d: 'AX.25 frames' }, - { p: 'ipip', n: 94, d: 'IP-within-IP Encapsulation Protocol' }, - { p: 'etherip', n: 97, d: 'Ethernet-within-IP Encapsulation [RFC3378]' }, - { p: 'encap', n: 98, d: 'Yet Another IP encapsulation [RFC1241]' }, - { p: 'pim', n: 103, d: 'Protocol Independent Multicast' }, - { p: 'ipcomp', n: 108, d: 'IP Payload Compression Protocol' }, - { p: 'vrrp', n: 112, d: 'Virtual Router Redundancy Protocol [RFC5798]' }, - { p: 'l2tp', n: 115, d: 'Layer Two Tunneling Protocol [RFC2661]' }, - { p: 'isis', n: 124, d: 'IS-IS over IPv4' }, - { p: 'sctp', n: 132, d: 'Stream Control Transmission Protocol' }, - { p: 'fc', n: 133, d: 'Fibre Channel' }, - { p: 'mobility-header', n: 135, d: 'Mobility Support for IPv6 [RFC3775]' }, - { p: 'udplite', n: 136, d: 'UDP-Lite [RFC3828]' }, - { p: 'mpls-in-ip', n: 137, d: 'MPLS-in-IP [RFC4023]' }, - { p: 'hip', n: 139, d: 'Host Identity Protocol' }, - { p: 'shim6', n: 140, d: 'Shim6 Protocol [RFC5533]' }, - { p: 'wesp', n: 141, d: 'Wrapped Encapsulating Security Payload' }, - { p: 'rohc', n: 142, d: 'Robust Header Compression' } - ] - } -}); -Ext.define('PVE.form.CPUModelSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.CPUModelSelector'], - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + ' (kvm64)'], - ['486', '486'], - ['athlon', 'athlon'], - ['core2duo', 'core2duo'], - ['coreduo', 'coreduo'], - ['kvm32', 'kvm32'], - ['kvm64', 'kvm64'], - ['pentium', 'pentium'], - ['pentium2', 'pentium2'], - ['pentium3', 'pentium3'], - ['phenom', 'phenom'], - ['qemu32', 'qemu32'], - ['qemu64', 'qemu64'], - ['Conroe', 'Conroe'], - ['Penryn', 'Penryn'], - ['Nehalem', 'Nehalem'], - ['Westmere', 'Westmere'], - ['SandyBridge', 'SandyBridge'], - ['IvyBridge', 'IvyBridge'], - ['Haswell', 'Haswell'], - ['Haswell-noTSX','Haswell-noTSX'], - ['Broadwell', 'Broadwell'], - ['Broadwell-noTSX','Broadwell-noTSX'], - ['Skylake-Client','Skylake-Client'], - ['Opteron_G1', 'Opteron_G1'], - ['Opteron_G2', 'Opteron_G2'], - ['Opteron_G3', 'Opteron_G3'], - ['Opteron_G4', 'Opteron_G4'], - ['Opteron_G5', 'Opteron_G5'], - ['EPYC', 'EPYC'], - ['host', 'host'] - - ] -}); -Ext.define('PVE.form.VNCKeyboardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.VNCKeyboardSelector'], - comboItems: PVE.Utils.kvm_keymap_array() -}); -Ext.define('PVE.form.CacheTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.CacheTypeSelector'], - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (" + gettext('No cache') + ")"], - ['directsync', 'Direct sync'], - ['writethrough', 'Write through'], - ['writeback', 'Write back'], - ['unsafe', 'Write back (' + gettext('unsafe') + ')'], - ['none', gettext('No cache')] - ] -}); -Ext.define('PVE.form.SnapshotSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.SnapshotSelector'], - - valueField: 'name', - displayField: 'name', - - loadStore: function(nodename, vmid) { - var me = this; - - if (!nodename) { - return; - } - - me.nodename = nodename; - - if (!vmid) { - return; - } - - me.vmid = vmid; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid +'/snapshot' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.guestType) { - throw "no guest type specified"; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'name'], - filterOnLoad: true - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Snapshot'), - dataIndex: 'name', - hideable: false, - flex: 1 - } - ] - } - }); - - me.callParent(); - - me.loadStore(me.nodename, me.vmid); - } -}); -Ext.define('PVE.form.ContentTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveContentTypeSelector'], - - cts: undefined, - - initComponent: function() { - var me = this; - - me.comboItems = []; - - if (me.cts === undefined) { - me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir', 'snippets']; - } - - Ext.Array.each(me.cts, function(ct) { - me.comboItems.push([ct, PVE.Utils.format_content_types(ct)]); - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.HotplugFeatureSelector', { - extend: 'Ext.form.CheckboxGroup', - alias: 'widget.pveHotplugFeatureSelector', - - columns: 1, - vertical: true, - - defaults: { - name: 'hotplug', - submitValue: false - }, - items: [ - { - boxLabel: gettext('Disk'), - inputValue: 'disk', - checked: true - }, - { - boxLabel: gettext('Network'), - inputValue: 'network', - checked: true - }, - { - boxLabel: 'USB', - inputValue: 'usb', - checked: true - }, - { - boxLabel: gettext('Memory'), - inputValue: 'memory' - }, - { - boxLabel: gettext('CPU'), - inputValue: 'cpu' - } - ], - - setValue: function(value) { - var me = this; - var newVal = []; - if (value === '1') { - newVal = ['disk', 'network', 'usb']; - } else if (value !== '0') { - newVal = value.split(','); - } - me.callParent([{ hotplug: newVal }]); - }, - - // overide framework function to - // assemble the hotplug value - getSubmitData: function() { - var me = this, - boxes = me.getBoxes(), - data = []; - Ext.Array.forEach(boxes, function(box){ - if (box.getValue()) { - data.push(box.inputValue); - } - }); - - /* because above is hotplug an array */ - /*jslint confusion: true*/ - if (data.length === 0) { - return { 'hotplug':'0' }; - } else { - return { 'hotplug': data.join(',') }; - } - } - -}); -Ext.define('PVE.form.AgentFeatureSelector', { - extend: 'Proxmox.panel.InputPanel', - alias: ['widget.pveAgentFeatureSelector'], - - initComponent: function() { - var me = this; - me.items= [ - { - xtype: 'proxmoxcheckbox', - boxLabel: gettext('Qemu Agent'), - name: 'enabled', - uncheckedValue: 0, - listeners: { - change: function(f, value, old) { - var gtcb = me.down('proxmoxcheckbox[name=fstrim_cloned_disks]'); - if (value) { - gtcb.setDisabled(false); - } else { - gtcb.setDisabled(true); - } - } - } - }, - { - xtype: 'proxmoxcheckbox', - boxLabel: gettext('Run guest-trim after clone disk'), - name: 'fstrim_cloned_disks', - disabled: true - } - ]; - me.callParent(); - }, - - onGetValues: function(values) { - var agentstr = PVE.Parser.printPropertyString(values, 'enabled'); - return { agent: agentstr }; - }, - - setValues: function(values) { - var agent = values.agent || ''; - var res = PVE.Parser.parsePropertyString(agent, 'enabled'); - this.callParent([res]); - } -}); -Ext.define('PVE.form.iScsiProviderSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveiScsiProviderSelector'], - comboItems: [ - ['comstar', 'Comstar'], - [ 'istgt', 'istgt'], - [ 'iet', 'IET'], - [ 'LIO', 'LIO'] - ] -}); -Ext.define('PVE.form.DayOfWeekSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveDayOfWeekSelector'], - comboItems:[], - initComponent: function(){ - var me = this; - me.comboItems = [ - ['mon', Ext.util.Format.htmlDecode(Ext.Date.dayNames[1])], - ['tue', Ext.util.Format.htmlDecode(Ext.Date.dayNames[2])], - ['wed', Ext.util.Format.htmlDecode(Ext.Date.dayNames[3])], - ['thu', Ext.util.Format.htmlDecode(Ext.Date.dayNames[4])], - ['fri', Ext.util.Format.htmlDecode(Ext.Date.dayNames[5])], - ['sat', Ext.util.Format.htmlDecode(Ext.Date.dayNames[6])], - ['sun', Ext.util.Format.htmlDecode(Ext.Date.dayNames[0])] - ]; - this.callParent(); - } -}); -Ext.define('PVE.form.BackupModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveBackupModeSelector'], - comboItems: [ - ['snapshot', gettext('Snapshot')], - ['suspend', gettext('Suspend')], - ['stop', gettext('Stop')] - ] -}); -Ext.define('PVE.form.ScsiHwSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveScsiHwSelector'], - comboItems: [ - ['__default__', PVE.Utils.render_scsihw('')], - ['lsi', PVE.Utils.render_scsihw('lsi')], - ['lsi53c810', PVE.Utils.render_scsihw('lsi53c810')], - ['megasas', PVE.Utils.render_scsihw('megasas')], - ['virtio-scsi-pci', PVE.Utils.render_scsihw('virtio-scsi-pci')], - ['virtio-scsi-single', PVE.Utils.render_scsihw('virtio-scsi-single')], - ['pvscsi', PVE.Utils.render_scsihw('pvscsi')] - ] -}); -Ext.define('PVE.form.FirewallPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallPolicySelector'], - comboItems: [ - ['ACCEPT', 'ACCEPT'], - ['REJECT', 'REJECT'], - [ 'DROP', 'DROP'] - ] -}); -/* - * This is a global search field - * it loads the /cluster/resources on focus - * and displays the result in a floating grid - * - * it filters and sorts the objects by the algorithm in - * the customFilter function - * - * also it does accept key up/down and enter for input - * and it opens to ctrl+shift+f and ctrl+space - */ -Ext.define('PVE.form.GlobalSearchField', { - extend: 'Ext.form.field.Text', - alias: 'widget.pveGlobalSearchField', - - emptyText: gettext('Search'), - enableKeyEvents: true, - selectOnFocus: true, - padding: '0 5 0 5', - - grid: { - xtype: 'gridpanel', - focusOnToFront: false, - floating: true, - emptyText: Proxmox.Utils.noneText, - width: 600, - height: 400, - scrollable: { - xtype: 'scroller', - y: true, - x:false - }, - store: { - model: 'PVEResources', - proxy:{ - type: 'proxmox', - url: '/api2/extjs/cluster/resources' - } - }, - plugins: { - ptype: 'bufferedrenderer', - trailingBufferZone: 20, - leadingBufferZone: 20 - }, - - hideMe: function() { - var me = this; - if (typeof me.ctxMenu !== 'undefined' && me.ctxMenu.isVisible()) { - return; - } - me.hasFocus = false; - if (!me.textfield.hasFocus) { - me.hide(); - } - }, - - setFocus: function() { - var me = this; - me.hasFocus = true; - }, - - listeners: { - rowclick: function(grid, record) { - var me = this; - me.textfield.selectAndHide(record.id); - }, - itemcontextmenu: function(v, record, item, index, event) { - var me = this; - me.ctxMenu = PVE.Utils.createCmdMenu(v, record, item, index, event); - }, - /* because of lint */ - focusleave: { - fn: 'hideMe' - }, - focusenter: 'setFocus' - }, - - columns: [ - { - text: gettext('Type'), - dataIndex: 'type', - width: 100, - renderer: PVE.Utils.render_resource_type - }, - { - text: gettext('Description'), - flex: 1, - dataIndex: 'text' - }, - { - text: gettext('Node'), - dataIndex: 'node' - }, - { - text: gettext('Pool'), - dataIndex: 'pool' - } - ] - }, - - customFilter: function(item) { - var me = this; - var match = 0; - var fieldArr = []; - var i,j, fields; - - // different types of objects have different fields to search - // for example, a node will never have a pool and vice versa - switch (item.data.type) { - case 'pool': fieldArr = ['type', 'pool', 'text']; break; - case 'node': fieldArr = ['type', 'node', 'text']; break; - case 'storage': fieldArr = ['type', 'pool', 'node', 'storage']; break; - default: fieldArr = ['name', 'type', 'node', 'pool', 'vmid']; - } - if (me.filterVal === '') { - item.data.relevance = 0; - return true; - } - - // all text is case insensitive and each word is - // searched alone - // for every partial match, the row gets - // 1 match point, for every exact match - // it gets 2 points - // - // results gets sorted by points (descending) - fields = me.filterVal.split(/\s+/); - for(i = 0; i < fieldArr.length; i++) { - var v = item.data[fieldArr[i]]; - if (v !== undefined) { - v = v.toString().toLowerCase(); - for(j = 0; j < fields.length; j++) { - if (v.indexOf(fields[j]) !== -1) { - match++; - if(v === fields[j]) { - match++; - } - } - } - } - } - // give the row the 'relevance' value - item.data.relevance = match; - return (match > 0); - }, - - updateFilter: function(field, newValue, oldValue) { - var me = this; - // parse input and filter store, - // show grid - me.grid.store.filterVal = newValue.toLowerCase().trim(); - me.grid.store.clearFilter(true); - me.grid.store.filterBy(me.customFilter); - me.grid.getSelectionModel().select(0); - }, - - selectAndHide: function(id) { - var me = this; - me.tree.selectById(id); - me.grid.hide(); - me.setValue(''); - me.blur(); - }, - - onKey: function(field, e) { - var me = this; - var key = e.getKey(); - - switch(key) { - case Ext.event.Event.ENTER: - // go to first entry if there is one - if (me.grid.store.getCount() > 0) { - me.selectAndHide(me.grid.getSelection()[0].data.id); - } - break; - case Ext.event.Event.UP: - me.grid.getSelectionModel().selectPrevious(); - break; - case Ext.event.Event.DOWN: - me.grid.getSelectionModel().selectNext(); - break; - case Ext.event.Event.ESC: - me.grid.hide(); - me.blur(); - break; - } - }, - - loadValues: function(field) { - var me = this; - var records = []; - - me.hasFocus = true; - me.grid.textfield = me; - me.grid.store.load(); - me.grid.showBy(me, 'tl-bl'); - }, - - hideGrid: function() { - var me = this; - - me.hasFocus = false; - if (!me.grid.hasFocus) { - me.grid.hide(); - } - }, - - listeners: { - change: { - fn: 'updateFilter', - buffer: 250 - }, - specialkey: 'onKey', - focusenter: 'loadValues', - focusleave: { - fn: 'hideGrid', - delay: 100 - } - }, - - toggleFocus: function() { - var me = this; - if (!me.hasFocus) { - me.focus(); - } else { - me.blur(); - } - }, - - initComponent: function() { - var me = this; - - if (!me.tree) { - throw "no tree given"; - } - - me.grid = Ext.create(me.grid); - - me.callParent(); - - /*jslint confusion: true*/ - /*because shift is also a function*/ - // bind ctrl+shift+f and ctrl+space - // to open/close the search - me.keymap = new Ext.KeyMap({ - target: Ext.get(document), - binding: [{ - key:'F', - ctrl: true, - shift: true, - fn: me.toggleFocus, - scope: me - },{ - key:' ', - ctrl: true, - fn: me.toggleFocus, - scope: me - }] - }); - - // always select first item and - // sort by relevance after load - me.mon(me.grid.store, 'load', function() { - me.grid.getSelectionModel().select(0); - me.grid.store.sort({ - property: 'relevance', - direction: 'DESC' - }); - }); - } - -}); -Ext.define('PVE.form.QemuBiosSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveQemuBiosSelector'], - - initComponent: function() { - var me = this; - - me.comboItems = [ - ['__default__', PVE.Utils.render_qemu_bios('')], - ['seabios', PVE.Utils.render_qemu_bios('seabios')], - ['ovmf', PVE.Utils.render_qemu_bios('ovmf')] - ]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -/* filter is a javascript builtin, but extjs calls it also filter */ -Ext.define('PVE.form.VMSelector', { - extend: 'Ext.grid.Panel', - alias: 'widget.vmselector', - - mixins: { - field: 'Ext.form.field.Field' - }, - - allowBlank: true, - selectAll: false, - isFormField: true, - - plugins: 'gridfilters', - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - // due to EXTJS-18711 - // we have to do a static list via a store - // but to avoid creating an object, - // we have to have a pseudo un function - un: function(){} - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - flex: 1, - filter: { - type: 'list' - } - } - ], - - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE' - }, - - checkChangeEvents: [ - 'selectionchange', - 'change' - ], - - listeners: { - selectionchange: function() { - // to trigger validity and error checks - this.checkChange(); - } - }, - - getValue: function() { - var me = this; - var sm = me.getSelectionModel(); - var selection = sm.getSelection(); - var values = []; - var store = me.getStore(); - selection.forEach(function(item) { - // only add if not filtered - if (store.findExact('vmid', item.data.vmid) !== -1) { - values.push(item.data.vmid); - } - }); - return values; - }, - - setValue: function(value) { - console.log(value); - var me = this; - var sm = me.getSelectionModel(); - if (!Ext.isArray(value)) { - value = value.split(','); - } - var selection = []; - var store = me.getStore(); - - value.forEach(function(item) { - var rec = store.findRecord('vmid',item, 0, false, true, true); - console.log(store); - - if (rec) { - console.log(rec); - selection.push(rec); - } - }); - - sm.select(selection); - - return me.mixins.field.setValue.call(me, value); - }, - - getErrors: function(value) { - var me = this; - if (me.allowBlank === false && - me.getSelectionModel().getCount() === 0) { - me.addBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return [gettext('No VM selected')]; - } - - me.removeBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return []; - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.nodename) { - me.store.filters.add({ - property: 'node', - exactMatch: true, - value: me.nodename - }); - } - - // only show the relevant guests by default - if (me.action) { - var statusfilter = ''; - switch (me.action) { - case 'startall': - statusfilter = 'stopped'; - break; - case 'stopall': - statusfilter = 'running'; - break; - } - if (statusfilter !== '') { - me.store.filters.add({ - property: 'template', - value: 0 - },{ - id: 'x-gridfilter-status', - operator: 'in', - property: 'status', - value: [statusfilter] - }); - } - } - - var store = me.getStore(); - var sm = me.getSelectionModel(); - - if (me.selectAll) { - me.mon(store,'load', function(){ - me.getSelectionModel().selectAll(false); - }); - } - } -}); - - -Ext.define('PVE.form.VMComboSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.vmComboSelector', - - valueField: 'vmid', - displayField: 'vmid', - - autoSelect: false, - editable: true, - anyMatch: true, - forceSelection: true, - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - - listConfig: { - width: 600, - plugins: 'gridfilters', - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - hidden: true, - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - un: function(){} // due to EXTJS-18711 - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - hidden: true, - flex: 1, - filter: { - type: 'list' - } - } - ] - } -}); -Ext.define('PVE.form.USBSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUSBSelector'], - allowBlank: false, - autoSelect: false, - displayField: 'usbid', - valueField: 'usbid', - editable: true, - - getUSBValue: function() { - var me = this; - var rec = me.store.findRecord('usbid', me.value); - var val = 'host='+ me.value; - if (rec && rec.data.speed === "5000") { - val = 'host=' + me.value + ",usb3=1"; - } - return val; - }, - - validator: function(value) { - var me = this; - if (me.type === 'device') { - return (/^[a-f0-9]{4}\:[a-f0-9]{4}$/i).test(value); - } else if (me.type === 'port') { - return (/^[0-9]+\-[0-9]+(\.[0-9]+)*$/).test(value); - } - return false; - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - - if (!nodename) { - throw "no nodename specified"; - } - - if (me.type !== 'device' && me.type !== 'port') { - throw "no valid type specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-usb-' + me.type, - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/scan/usb" - }, - filters: [ - function (item) { - return !!item.data.usbpath && !!item.data.prodid && item.data['class'] != 9; - } - ] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: (me.type === 'device')?gettext('Device'):gettext('Port'), - sortable: true, - dataIndex: 'usbid', - width: 80 - }, - { - header: gettext('Manufacturer'), - sortable: true, - dataIndex: 'manufacturer', - width: 100 - }, - { - header: gettext('Product'), - sortable: true, - dataIndex: 'product', - flex: 1 - }, - { - header: gettext('Speed'), - width: 70, - sortable: true, - dataIndex: 'speed', - renderer: function(value) { - if (value === "5000") { - return "USB 3.0"; - } else if (value === "480") { - return "USB 2.0"; - } else { - return "USB 1.x"; - } - } - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-usb-device', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val, data) { - if (val) { - return val; - } - return data.get('vendid') + ':' + data.get('prodid'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' } - ] - }); - - Ext.define('pve-usb-port', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val,data) { - if (val) { - return val; - } - return data.get('busnum') + '-' + data.get('usbpath'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' } - ] - }); -}); -Ext.define('PVE.form.CalendarEvent', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pveCalendarEvent', - - editable: true, - - valueField: 'value', - displayField: 'text', - queryMode: 'local', - - store: { - field: [ 'value', 'text'], - data: [ - { value: '*/30', text: Ext.String.format(gettext("Every {0} minutes"), 30) }, - { value: '*/2:00', text: gettext("Every two hours")}, - { value: '2,22:30', text: gettext("Every day") + " 02:30, 22:30"}, - { value: 'mon..fri', text: gettext("Monday to Friday") + " 00:00"}, - { value: 'mon..fri */1:00', text: gettext("Monday to Friday") + ': ' + gettext("hourly")}, - { value: 'sun 01:00', text: gettext("Sunday") + " 01:00"} - ] - }, - - tpl: [ - '
    ', - '
  • {text}
  • ', - '
' - ], - - displayTpl: [ - '', - '{value}', - '' - ] - -}); -Ext.define('PVE.form.CephPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephPoolSelector', - - allowBlank: false, - valueField: 'pool_name', - displayField: 'pool_name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/pools' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.form.PermPathSelector', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pvePermPathSelector', - - valueField: 'value', - displayField: 'value', - typeAhead: true, - queryMode: 'local', - store: { - type: 'pvePermPath' - } -}); -/* This class defines the "Tasks" tab of the bottom status panel - * Tasks are jobs with a start, end and log output - */ - -Ext.define('PVE.dc.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterTasks'], - - initComponent : function() { - var me = this; - - var taskstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-tasks', - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/tasks' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: taskstore, - sortAfterUpdate: true, - appendAtStart: true, - sorters: [ - { - property : 'pid', - direction: 'DESC' - }, - { - property : 'starttime', - direction: 'DESC' - } - ] - - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type == "vncproxy" || - record.data.type == "vncshell" || - record.data.type == "spiceproxy") { - metaData.tdCls = "x-grid-row-console"; - } else { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type != "vncproxy") { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return Proxmox.Utils.errorText + ': ' + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - show: taskstore.startUpdate, - destroy: taskstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* This class defines the "Cluster log" tab of the bottom status panel - * A log entry is a timestamp associated with an action on a cluster - */ - -Ext.define('PVE.dc.Log', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterLog'], - - initComponent : function() { - var me = this; - - var logstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-log', - model: 'proxmox-cluster-log', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/log' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: logstore, - appendAtStart: true - }); - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, - - getRowClass: function(record, index) { - var pri = record.get('pri'); - - if (pri && pri <= 3) { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Time"), - dataIndex: 'time', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 150 - }, - { - header: gettext("Service"), - dataIndex: 'tag', - width: 100 - }, - { - header: "PID", - dataIndex: 'pid', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Severity"), - dataIndex: 'pri', - renderer: PVE.Utils.render_serverity, - width: 100 - }, - { - header: gettext("Message"), - dataIndex: 'msg', - flex: 1 - } - ], - listeners: { - activate: logstore.startUpdate, - deactivate: logstore.stopUpdate, - destroy: logstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* - * This class describes the bottom panel - */ -Ext.define('PVE.panel.StatusPanel', { - extend: 'Ext.tab.Panel', - alias: 'widget.pveStatusPanel', - - - //title: "Logs", - //tabPosition: 'bottom', - - initComponent: function() { - var me = this; - - var stateid = 'ltab'; - var sp = Ext.state.Manager.getProvider(); - - var state = sp.get(stateid); - if (state && state.value) { - me.activeTab = state.value; - } - - Ext.apply(me, { - listeners: { - tabchange: function() { - var atab = me.getActiveTab().itemId; - var state = { value: atab }; - sp.set(stateid, state); - } - }, - items: [ - { - itemId: 'tasks', - title: gettext('Tasks'), - xtype: 'pveClusterTasks' - }, - { - itemId: 'clog', - title: gettext('Cluster log'), - xtype: 'pveClusterLog' - } - ] - }); - - me.callParent(); - - me.items.get(0).fireEvent('show', me.items.get(0)); - - var statechange = function(sp, key, state) { - if (key === stateid) { - var atab = me.getActiveTab().itemId; - var ntab = state.value; - if (state && ntab && (atab != ntab)) { - me.setActiveTab(ntab); - } - } - }; - - sp.on('statechange', statechange); - me.on('destroy', function() { - sp.un('statechange', statechange); - }); - - } -}); -Ext.define('PVE.panel.StatusView', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStatusView', - - layout: { - type: 'column' - }, - - title: gettext('Status'), - - getRecordValue: function(key, store) { - if (!key) { - throw "no key given"; - } - var me = this; - - if (store === undefined) { - store = me.getStore(); - } - - var rec = store.getById(key); - if (rec) { - return rec.data.value; - } - - return ''; - }, - - fieldRenderer: function(val,max) { - if (max === undefined) { - return val; - } - - if (!Ext.isNumeric(max) || max === 1) { - return PVE.Utils.render_usage(val); - } - return PVE.Utils.render_size_usage(val,max); - }, - - fieldCalculator: function(used, max) { - if (!Ext.isNumeric(max) && Ext.isNumeric(used)) { - return used; - } else if(!Ext.isNumeric(used)) { - /* we come here if the field is from a node - * where the records are not mem and maxmem - * but mem.used and mem.total - */ - if (used.used !== undefined && - used.total !== undefined) { - return used.used/used.total; - } - } - - return used/max; - }, - - updateField: function(field) { - var me = this; - var text = ''; - var renderer = me.fieldRenderer; - if (Ext.isFunction(field.renderer)) { - renderer = field.renderer; - } - if (field.multiField === true) { - field.updateValue(renderer.call(field, me.getStore().getRecord())); - } else if (field.textField !== undefined) { - field.updateValue(renderer.call(field, me.getRecordValue(field.textField))); - } else if(field.valueField !== undefined) { - var used = me.getRecordValue(field.valueField); - /*jslint confusion: true*/ - /* string and int */ - var max = field.maxField !== undefined ? me.getRecordValue(field.maxField) : 1; - - var calculate = me.fieldCalculator; - - if (Ext.isFunction(field.calculate)) { - calculate = field.calculate; - } - field.updateValue(renderer.call(field, used,max), calculate(used,max)); - } - }, - - getStore: function() { - var me = this; - if (!me.rstore) { - throw "there is no rstore"; - } - - return me.rstore; - }, - - updateTitle: function() { - var me = this; - me.setTitle(me.getRecordValue('name')); - }, - - updateValues: function(store, records, success) { - if (!success) { - return; // do not update if store load was not successful - } - var me = this; - var itemsToUpdate = me.query('pveInfoWidget'); - - itemsToUpdate.forEach(me.updateField, me); - - me.updateTitle(store); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - if (!me.title) { - throw "no title given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.callParent(); - - me.mon(me.rstore, 'load', 'updateValues'); - } - -}); -Ext.define('PVE.panel.GuestStatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveGuestStatusView', - mixins: ['Proxmox.Mixin.CBind'], - - height: 300, - - cbindData: function (initialConfig) { - var me = this; - return { - isQemu: me.pveSelNode.data.type === 'qemu', - isLxc: me.pveSelNode.data.type === 'lxc' - }; - }, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'status', - title: gettext('Status'), - iconCls: 'fa fa-info fa-fw', - printBar: false, - multiField: true, - renderer: function(record) { - var me = this; - var text = record.data.status; - var qmpstatus = record.data.qmpstatus; - if (qmpstatus && qmpstatus !== record.data.status) { - text += ' (' + qmpstatus + ')'; - } - return text; - } - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - xtype: 'pveInfoWidget', - itemId: 'node', - iconCls: 'fa fa-building fa-fw', - title: gettext('Node'), - cbind: { - text: '{pveSelNode.data.node}' - }, - printBar: false - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpus', - renderer: PVE.Utils.render_cpu_usage, - // in this specific api call - // we already have the correct value for the usage - calculate: Ext.identityFn - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory usage'), - valueField: 'mem', - maxField: 'maxmem' - }, - { - itemId: 'swap', - xtype: 'pveInfoWidget', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'maxswap', - cbind: { - hidden: '{isQemu}', - disabled: '{isQemu}' - } - }, - { - itemId: 'rootfs', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - valueField: 'disk', - maxField: 'maxdisk', - printBar: false, - renderer: function(used, max) { - var me = this; - me.setPrintBar(used > 0); - if (used === 0) { - return PVE.Utils.render_size(max); - } else { - return PVE.Utils.render_size_usage(used,max); - } - } - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'ips', - xtype: 'pveAgentIPView', - cbind: { - rstore: '{rstore}', - pveSelNode: '{pveSelNode}', - hidden: '{isLxc}', - disabled: '{isLxc}' - } - } - ], - - updateTitle: function() { - var me = this; - var uptime = me.getRecordValue('uptime'); - - var text = ""; - if (Number(uptime) > 0) { - text = " (" + gettext('Uptime') + ': ' + Proxmox.Utils.format_duration_long(uptime) - + ')'; - } - - me.setTitle(me.getRecordValue('name') + text); - } -}); -/* - * This is a running chart widget - * you add time datapoints to it, - * and we only show the last x of it - * used for ceph performance charts - */ -Ext.define('PVE.widget.RunningChart', { - extend: 'Ext.container.Container', - alias: 'widget.pveRunningChart', - - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - width: 80, - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}:

' - }, - { - flex: 1, - xtype: 'cartesian', - height: '100%', - itemId: 'chart', - border: false, - axes: [ - { - type: 'numeric', - position: 'left', - hidden: true, - minimum: 0 - }, - { - type: 'numeric', - position: 'bottom', - hidden: true - } - ], - - store: { - data: {} - }, - - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '0 B/s', - textAlign: 'end', - textBaseline: 'middle', - fontSize: 14 - }], - - series: [{ - type: 'line', - xField: 'time', - yField: 'val', - fill: 'true', - colors: ['#cfcfcf'], - tooltip: { - trackMouse: true, - renderer: function( tooltip, record, ctx) { - var me = this.getChart(); - var date = new Date(record.data.time); - var value = me.up().renderer(record.data.val); - tooltip.setHtml( - me.up().title + ': ' + value + '
' + - Ext.Date.format(date, 'H:i:s') - ); - } - }, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - } - }] - } - ], - - // the renderer for the tooltip and last value, - // default just the value - renderer: Ext.identityFn, - - // show the last x seconds - // default is 5 minutes - timeFrame: 5*60, - - addDataPoint: function(value, time) { - var me = this.chart; - var panel = me.up(); - var now = new Date(); - var begin = new Date(now.getTime() - (1000*panel.timeFrame)); - - me.store.add({ - time: time || now.getTime(), - val: value || 0 - }); - - // delete all old records when we have 20 times more datapoints - // than seconds in our timeframe (so even a subsecond graph does - // not trigger this often) - // - // records in the store do not take much space, but like this, - // we prevent a memory leak when someone has the site open for a long time - // with minimal graphical glitches - if (me.store.count() > panel.timeFrame * 20) { - var oldData = me.store.getData().createFiltered(function(item) { - return item.data.time < begin.getTime(); - }); - - me.store.remove(oldData.getRange()); - } - - me.timeaxis.setMinimum(begin.getTime()); - me.timeaxis.setMaximum(now.getTime()); - me.valuesprite.setText(panel.renderer(value || 0).toString()); - me.valuesprite.setAttributes({ - x: me.getWidth() - 15, - y: me.getHeight()/2 - }, true); - me.redraw(); - }, - - setTitle: function(title) { - this.title = title; - var me = this.getComponent('title'); - me.update({title: title}); - }, - - initComponent: function(){ - var me = this; - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.chart = me.getComponent('chart'); - me.chart.timeaxis = me.chart.getAxes()[1]; - me.chart.valuesprite = me.chart.getSurface('chart').get('valueSprite'); - if (me.color) { - me.chart.series[0].setStyle({ - fill: me.color, - stroke: me.color - }); - } - } -}); -Ext.define('PVE.widget.Info',{ - extend: 'Ext.container.Container', - alias: 'widget.pveInfoWidget', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - value: 0, - maximum: 1, - printBar: true, - items: [ - { - xtype: 'component', - itemId: 'label', - data: { - title: '', - usage: '', - iconCls: undefined - }, - tpl: [ - '
', - '', - ' ', - '', - '{title}
 
{usage}
' - ] - }, - { - height: 2, - border: 0 - }, - { - xtype: 'progressbar', - itemId: 'progress', - height: 5, - value: 0, - animate: true - } - ], - - warningThreshold: 0.6, - criticalThreshold: 0.9, - - setPrintBar: function(enable) { - var me = this; - me.printBar = enable; - me.getComponent('progress').setVisible(enable); - }, - - setIconCls: function(iconCls) { - var me = this; - me.getComponent('label').data.iconCls = iconCls; - }, - - updateValue: function(text, usage) { - var me = this; - var label = me.getComponent('label'); - label.update(Ext.apply(label.data, {title: me.title, usage:text})); - - if (usage !== undefined && - me.printBar && - Ext.isNumeric(usage) && - usage >= 0) { - var progressBar = me.getComponent('progress'); - progressBar.updateProgress(usage, ''); - if (usage > me.criticalThreshold) { - progressBar.removeCls('warning'); - progressBar.addCls('critical'); - } else if (usage > me.warningThreshold) { - progressBar.removeCls('critical'); - progressBar.addCls('warning'); - } else { - progressBar.removeCls('warning'); - progressBar.removeCls('critical'); - } - } - }, - - initComponent: function() { - var me = this; - - if (!me.title) { - throw "no title defined"; - } - - me.callParent(); - - me.getComponent('progress').setVisible(me.printBar); - - me.updateValue(me.text, me.value); - me.setIconCls(me.iconCls); - } - -}); -Ext.define('PVE.panel.TemplateStatusView',{ - extend: 'PVE.panel.StatusView', - alias: 'widget.pveTemplateStatusView', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - printBar: false, - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - itemId: 'node', - iconCls: 'fa fa-fw fa-building', - title: gettext('Node') - }, - { - xtype: 'box', - height: 20 - }, - { - itemId: 'cpus', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('Processors'), - textField: 'cpus' - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory'), - textField: 'maxmem', - renderer: PVE.Utils.render_size - }, - { - itemId: 'swap', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('Swap'), - textField: 'maxswap', - renderer: PVE.Utils.render_size - }, - { - itemId: 'disk', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - textField: 'maxdisk', - renderer: PVE.Utils.render_size - }, - { - xtype: 'box', - height: 20 - } - ], - - initComponent: function() { - var me = this; - - var name = me.pveSelNode.data.name; - if (!name) { - throw "no name specified"; - } - - me.title = name; - - me.callParent(); - if (me.pveSelNode.data.type !== 'lxc') { - me.remove(me.getComponent('swap')); - } - me.getComponent('node').updateValue(me.pveSelNode.data.node); - } -}); -Ext.define('PVE.widget.HealthWidget', { - extend: 'Ext.Component', - alias: 'widget.pveHealthWidget', - - data: { - iconCls: PVE.Utils.get_health_icon(undefined, true), - text: '', - title: '' - }, - - style: { - 'text-align':'center' - }, - - tpl: [ - '

{title}

', - '', - '

', - '{text}' - ], - - updateHealth: function(data) { - var me = this; - me.update(Ext.apply(me.data, data)); - }, - - initComponent: function(){ - var me = this; - - if (me.title) { - me.config.data.title = me.title; - } - - me.callParent(); - } - -}); -/*global u2f*/ -Ext.define('PVE.window.LoginWindow', { - extend: 'Ext.window.Window', - - controller: { - - xclass: 'Ext.app.ViewController', - - onLogon: function() { - var me = this; - - var form = this.lookupReference('loginForm'); - var unField = this.lookupReference('usernameField'); - var saveunField = this.lookupReference('saveunField'); - var view = this.getView(); - - if (!form.isValid()) { - return; - } - - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - - // set or clear username - var sp = Ext.state.Manager.getProvider(); - if (saveunField.getValue() === true) { - sp.set(unField.getStateId(), unField.getValue()); - } else { - sp.clear(unField.getStateId()); - } - sp.set(saveunField.getStateId(), saveunField.getValue()); - - form.submit({ - failure: function(f, resp){ - me.failure(resp); - }, - success: function(f, resp){ - view.el.unmask(); - - var data = resp.result.data; - if (Ext.isDefined(data.NeedTFA)) { - // Store first factor login information first: - data.LoggedOut = true; - Proxmox.Utils.setAuthData(data); - - if (Ext.isDefined(data.U2FChallenge)) { - me.perform_u2f(data); - } else { - me.perform_otp(); - } - } else { - me.success(data); - } - } - }); - - }, - failure: function(resp) { - var me = this; - var view = me.getView(); - view.el.unmask(); - var handler = function() { - var uf = me.lookupReference('usernameField'); - uf.focus(true, true); - }; - - Ext.MessageBox.alert(gettext('Error'), - gettext("Login failed. Please try again"), - handler); - }, - success: function(data) { - var me = this; - var view = me.getView(); - var handler = view.handler || Ext.emptyFn; - handler.call(me, data); - view.close(); - }, - - perform_otp: function() { - var me = this; - var win = Ext.create('PVE.window.TFALoginWindow', { - onLogin: function(value) { - me.finish_tfa(value); - }, - onCancel: function() { - Proxmox.LoggedOut = false; - Proxmox.Utils.authClear(); - me.getView().show(); - } - }); - win.show(); - }, - - perform_u2f: function(data) { - var me = this; - // Show the message: - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Verification'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - var chlg = data.U2FChallenge; - var key = { - version: chlg.version, - keyHandle: chlg.keyHandle - }; - u2f.sign(chlg.appId, chlg.challenge, [key], function(res) { - msg.close(); - if (res.errorCode) { - Proxmox.Utils.authClear(); - Ext.Msg.alert(gettext('Error'), PVE.Utils.render_u2f_error(res.errorCode)); - return; - } - delete res.errorCode; - me.finish_tfa(JSON.stringify(res)); - }); - }, - finish_tfa: function(res) { - var me = this; - var view = me.getView(); - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - var params = { response: res }; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'POST', - timeout: 5000, // it'll delay both success & failure - success: function(resp, opts) { - view.el.unmask(); - // Fill in what we copy over from the 1st factor: - var data = resp.result.data; - data.CSRFPreventionToken = Proxmox.CSRFPreventionToken; - data.username = Proxmox.UserName; - // Finish logging in: - me.success(data); - }, - failure: function(resp, opts) { - Proxmox.Utils.authClear(); - me.failure(resp); - } - }); - }, - - control: { - 'field[name=username]': { - specialkey: function(f, e) { - if (e.getKey() === e.ENTER) { - var pf = this.lookupReference('passwordField'); - if (!pf.getValue()) { - pf.focus(false); - } - } - } - }, - 'field[name=lang]': { - change: function(f, value) { - var dt = Ext.Date.add(new Date(), Ext.Date.YEAR, 10); - Ext.util.Cookies.set('PVELangCookie', value, dt); - this.getView().mask(gettext('Please wait...'), 'x-mask-loading'); - window.location.reload(); - } - }, - 'button[reference=loginButton]': { - click: 'onLogon' - }, - '#': { - show: function() { - var sp = Ext.state.Manager.getProvider(); - var checkboxField = this.lookupReference('saveunField'); - var unField = this.lookupReference('usernameField'); - - var checked = sp.get(checkboxField.getStateId()); - checkboxField.setValue(checked); - - if(checked === true) { - var username = sp.get(unField.getStateId()); - unField.setValue(username); - var pwField = this.lookupReference('passwordField'); - pwField.focus(); - } - } - } - } - }, - - width: 400, - - modal: true, - - border: false, - - draggable: true, - - closable: false, - - resizable: false, - - layout: 'auto', - - title: gettext('Proxmox VE Login'), - - defaultFocus: 'usernameField', - - defaultButton: 'loginButton', - - items: [{ - xtype: 'form', - layout: 'form', - url: '/api2/extjs/access/ticket', - reference: 'loginForm', - - fieldDefaults: { - labelAlign: 'right', - allowBlank: false - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('User name'), - name: 'username', - itemId: 'usernameField', - reference: 'usernameField', - stateId: 'login-username' - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - name: 'password', - reference: 'passwordField' - }, - { - xtype: 'pveRealmComboBox', - name: 'realm' - }, - { - xtype: 'proxmoxLanguageSelector', - fieldLabel: gettext('Language'), - value: Ext.util.Cookies.get('PVELangCookie') || Proxmox.defaultLang || 'en', - name: 'lang', - reference: 'langField', - submitValue: false - } - ], - buttons: [ - { - xtype: 'checkbox', - fieldLabel: gettext('Save User name'), - name: 'saveusername', - reference: 'saveunField', - stateId: 'login-saveusername', - labelWidth: 'auto', - labelAlign: 'right', - submitValue: false - }, - { - text: gettext('Login'), - reference: 'loginButton' - } - ] - }] - }); -Ext.define('PVE.window.TFALoginWindow', { - extend: 'Ext.window.Window', - - modal: true, - resizable: false, - title: 'Two-Factor Authentication', - layout: 'form', - defaultButton: 'loginButton', - defaultFocus: 'otpField', - - controller: { - xclass: 'Ext.app.ViewController', - login: function() { - var me = this; - var view = me.getView(); - view.onLogin(me.lookup('otpField').value); - view.close(); - }, - cancel: function() { - var me = this; - var view = me.getView(); - view.onCancel(); - view.close(); - } - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Please enter your OTP verification code:'), - name: 'otp', - itemId: 'otpField', - reference: 'otpField', - allowBlank: false - } - ], - - buttons: [ - { - text: gettext('Login'), - reference: 'loginButton', - handler: 'login' - }, - { - text: gettext('Cancel'), - handler: 'cancel' - } - ] -}); -Ext.define('PVE.window.Wizard', { - extend: 'Ext.window.Window', - - activeTitle: '', // used for automated testing - - width: 700, - height: 510, - - modal: true, - border: false, - - draggable: true, - closable: true, - resizable: false, - - layout: 'border', - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.down('form').getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - initComponent: function() { - var me = this; - - var tabs = me.items || []; - delete me.items; - - /* - * Items may have the following functions: - * validator(): per tab custom validation - * onSubmit(): submit handler - * onGetValues(): overwrite getValues results - */ - - Ext.Array.each(tabs, function(tab) { - tab.disabled = true; - }); - tabs[0].disabled = false; - - var maxidx = 0; - var curidx = 0; - - var check_card = function(card) { - var valid = true; - var fields = card.query('field, fieldcontainer'); - if (card.isXType('fieldcontainer')) { - fields.unshift(card); - } - Ext.Array.each(fields, function(field) { - // Note: not all fielcontainer have isValid() - if (Ext.isFunction(field.isValid) && !field.isValid()) { - valid = false; - } - }); - - if (Ext.isFunction(card.validator)) { - return card.validator(); - } - - return valid; - }; - - var disable_at = function(card) { - var tp = me.down('#wizcontent'); - var idx = tp.items.indexOf(card); - for(;idx < tp.items.getCount();idx++) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }; - - var tabchange = function(tp, newcard, oldcard) { - if (newcard.onSubmit) { - me.down('#next').setVisible(false); - me.down('#submit').setVisible(true); - } else { - me.down('#next').setVisible(true); - me.down('#submit').setVisible(false); - } - var valid = check_card(newcard); - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - me.down('#back').setDisabled(tp.items.indexOf(newcard) == 0); - - var idx = tp.items.indexOf(newcard); - if (idx > maxidx) { - maxidx = idx; - } - curidx = idx; - - var next = idx + 1; - var ntab = tp.items.getAt(next); - if (valid && ntab && !newcard.onSubmit) { - ntab.enable(); - } - }; - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, true, false); - } - - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - - Ext.apply(me, { - items: [ - { - xtype: 'form', - region: 'center', - layout: 'fit', - border: false, - margins: '5 5 0 5', - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [{ - itemId: 'wizcontent', - xtype: 'tabpanel', - activeItem: 0, - bodyPadding: 10, - listeners: { - afterrender: function(tp) { - var atab = this.getActiveTab(); - tabchange(tp, atab); - }, - tabchange: function(tp, newcard, oldcard) { - tabchange(tp, newcard, oldcard); - } - }, - items: tabs - }] - } - ], - fbar: [ - { - xtype: 'proxmoxHelpButton', - itemId: 'help' - }, - '->', - { - xtype: 'proxmoxcheckbox', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - value: advchecked, - listeners: { - change: function(cb, val) { - var tp = me.down('#wizcontent'); - tp.query('inputpanel').forEach(function(ip) { - ip.setAdvancedVisible(val); - }); - - sp.set('proxmox-advanced-cb', val); - } - } - }, - { - text: gettext('Back'), - disabled: true, - itemId: 'back', - minWidth: 60, - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - var prev = tp.items.indexOf(atab) - 1; - if (prev < 0) { - return; - } - var ntab = tp.items.getAt(prev); - if (ntab) { - tp.setActiveTab(ntab); - } - } - }, - { - text: gettext('Next'), - disabled: true, - itemId: 'next', - minWidth: 60, - handler: function() { - - var form = me.down('form').getForm(); - - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - if (!check_card(atab)) { - return; - } - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - - } - }, - { - text: gettext('Finish'), - minWidth: 60, - hidden: true, - itemId: 'submit', - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - atab.onSubmit(); - } - } - ] - }); - me.callParent(); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setAdvancedVisible(advchecked); - }); - - Ext.Array.each(me.query('field'), function(field) { - var validcheck = function() { - var tp = me.down('#wizcontent'); - - // check tabs from current to the last enabled for validity - // since we might have changed a validity on a later one - var i; - for (i = curidx; i <= maxidx && i < tp.items.getCount(); i++) { - var tab = tp.items.getAt(i); - var valid = check_card(tab); - - // only set the buttons on the current panel - if (i === curidx) { - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - } - - // if a panel is invalid, then disable it and all following, - // else enable it and go to the next - var ntab = tp.items.getAt(i + 1); - if (!valid) { - disable_at(ntab); - return; - } else if (ntab && !tab.onSubmit) { - ntab.enable(); - } - } - }; - field.on('change', validcheck); - field.on('validitychange', validcheck); - }); - } -}); -Ext.define('PVE.window.NotesEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - title: gettext('Notes'), - width: 600, - height: '400px', - resizable: true, - layout: 'fit', - defaultButton: undefined, - items: { - xtype: 'textarea', - name: 'description', - height: '100%', - value: '', - hideLabel: true - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.window.Backup', { - extend: 'Ext.window.Window', - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.vmtype) { - throw "no VM type specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: me.storage, - fieldLabel: gettext('Storage'), - storageContent: 'backup', - allowBlank: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - storagesel, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'pveCompressionSelector', - name: 'compress', - value: 'lzo', - fieldLabel: gettext('Compression') - }, - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto', - emptyText: Proxmox.Utils.noneText - } - ] - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Backup'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - var params = { - storage: storage, - vmid: me.vmid, - mode: values.mode, - remove: 0 - }; - - if ( values.mailto ) { - params.mailto = values.mailto; - } - - if (values.compress) { - params.compress = values.compress; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/vzdump', - params: params, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - }, - success: function(response, options) { - // close later so we reload the grid - // after the task has completed - me.hide(); - - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - close: function() { - me.close(); - } - } - }); - win.show(); - } - }); - } - }); - - var helpBtn = Ext.create('Proxmox.button.Help', { - onlineHelp: 'chapter_vzdump', - listenToGlobalEvent: false, - hidden: false - }); - - var title = gettext('Backup') + " " + - ((me.vmtype === 'lxc') ? "CT" : "VM") + - " " + me.vmid; - - Ext.apply(me, { - title: title, - width: 350, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ helpBtn, '->', submitBtn ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.window.Restore', { - extend: 'Ext.window.Window', // fixme: Proxmox.window.Edit? - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.volid) { - throw "no volume ID specified"; - } - - if (!me.vmtype) { - throw "no vmtype specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: '', - fieldLabel: gettext('Storage'), - storageContent: (me.vmtype === 'lxc') ? 'rootdir' : 'images', - allowBlank: true - }); - - var IDfield; - if (me.vmid) { - IDfield = Ext.create('Ext.form.field.Display', { - name: 'vmid', - value: me.vmid, - fieldLabel: (me.vmtype === 'lxc') ? 'CT' : 'VM' - }); - } else { - IDfield = Ext.create('PVE.form.GuestIDSelector', { - name: 'vmid', - guestType: me.vmtype, - loadNextFreeID: true, - validateExists: false - }); - } - - var items = [ - { - xtype: 'displayfield', - value: me.volidText || me.volid, - fieldLabel: gettext('Source') - }, - storagesel, - IDfield, - { - xtype: 'proxmoxintegerfield', - name: 'bwlimit', - fieldLabel: gettext('Read Limit (MiB/s)'), - minValue: 0, - emptyText: gettext('Defaults to target storage restore limit'), - autoEl: { - tag: 'div', - 'data-qtip': gettext("Use '0' to disable all bandwidth limits.") - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'unique', - fieldLabel: gettext('Unique'), - hidden: !!me.vmid, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses') - }, - checked: false - } - ]; - - /*jslint confusion: true*/ - if (me.vmtype === 'lxc') { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - fieldLabel: gettext('Unprivileged container') - }); - } - /*jslint confusion: false*/ - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var doRestore = function(url, params) { - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - waitMsgTarget: me, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.close(); - } - }); - }; - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Restore'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - - var params = { - storage: storage, - vmid: me.vmid || values.vmid, - force: me.vmid ? 1 : 0 - }; - if (values.unique) { params.unique = 1; } - - if (values.bwlimit !== undefined) { - params.bwlimit = values.bwlimit * 1024; - } - - var url; - var msg; - if (me.vmtype === 'lxc') { - url = '/nodes/' + me.nodename + '/lxc'; - params.ostemplate = me.volid; - params.restore = 1; - if (values.unprivileged) { params.unprivileged = 1; } - msg = Proxmox.Utils.format_task_description('vzrestore', params.vmid); - } else if (me.vmtype === 'qemu') { - url = '/nodes/' + me.nodename + '/qemu'; - params.archive = me.volid; - msg = Proxmox.Utils.format_task_description('qmrestore', params.vmid); - } else { - throw 'unknown VM type'; - } - - if (me.vmid) { - msg += '. ' + gettext('This will permanently erase current VM data.'); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - doRestore(url, params); - }); - } else { - doRestore(url, params); - } - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - var title = gettext('Restore') + ": " + ( - (me.vmtype === 'lxc') ? 'CT' : 'VM'); - - if (me.vmid) { - title += " " + me.vmid; - } - - Ext.apply(me, { - title: title, - width: 500, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); -/* Popup a message window - * where the user has to manually enter the ressource ID - * to enable the destroy button - */ -Ext.define('PVE.window.SafeDestroy', { - extend: 'Ext.window.Window', - alias: 'widget.pveSafeDestroy', - - title: gettext('Confirm'), - modal: true, - buttonAlign: 'center', - bodyPadding: 10, - width: 450, - layout: { type:'hbox' }, - defaultFocus: 'confirmField', - showProgress: false, - - config: { - item: { - id: undefined, - type: undefined - }, - url: undefined, - params: {} - }, - - getParams: function() { - var me = this; - if (Ext.Object.isEmpty(me.params)) { - return ''; - } - return '?' + Ext.Object.toQueryString(me.params); - }, - - controller: { - - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=confirm]': { - change: function(f, value) { - var view = this.getView(); - var removeButton = this.lookupReference('removeButton'); - if (value === view.getItem().id.toString()) { - removeButton.enable(); - } else { - removeButton.disable(); - } - }, - specialkey: function (field, event) { - var removeButton = this.lookupReference('removeButton'); - if (!removeButton.isDisabled() && event.getKey() == event.ENTER) { - removeButton.fireEvent('click', removeButton, event); - } - } - }, - 'button[reference=removeButton]': { - click: function() { - var view = this.getView(); - Proxmox.Utils.API2Request({ - url: view.getUrl() + view.getParams(), - method: 'DELETE', - waitMsgTarget: view, - failure: function(response, opts) { - view.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = view.showProgress && - response.result.data ? true : false; - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - view.hide(); - - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: function () { - view.close(); - } - } - }); - win.show(); - } else { - view.close(); - } - } - }); - } - } - } - }, - - items: [ - { - xtype: 'component', - cls: [ Ext.baseCSSPrefix + 'message-box-icon', - Ext.baseCSSPrefix + 'message-box-warning', - Ext.baseCSSPrefix + 'dlg-icon'] - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'component', - reference: 'messageCmp' - }, - { - itemId: 'confirmField', - reference: 'confirmField', - xtype: 'textfield', - name: 'confirm', - labelWidth: 300, - hideTrigger: true, - allowBlank: false - } - ] - } - ], - buttons: [ - { - reference: 'removeButton', - text: gettext('Remove'), - disabled: true - } - ], - - initComponent : function() { - var me = this; - - me.callParent(); - - var item = me.getItem(); - - if (!Ext.isDefined(item.id)) { - throw "no ID specified"; - } - - if (!Ext.isDefined(item.type)) { - throw "no VM type specified"; - } - - var messageCmp = me.lookupReference('messageCmp'); - var msg; - - if (item.type === 'VM') { - msg = Proxmox.Utils.format_task_description('qmdestroy', item.id); - } else if (item.type === 'CT') { - msg = Proxmox.Utils.format_task_description('vzdestroy', item.id); - } else if (item.type === 'CephPool') { - msg = Proxmox.Utils.format_task_description('cephdestroypool', item.id); - } else if (item.type === 'Image') { - msg = Proxmox.Utils.format_task_description('unknownimgdel', item.id); - } else { - throw "unknown item type specified"; - } - - messageCmp.setHtml(msg); - - var confirmField = me.lookupReference('confirmField'); - msg = gettext('Please enter the ID to confirm') + - ' (' + item.id + ')'; - confirmField.setFieldLabel(msg); - } -}); -Ext.define('PVE.window.BackupConfig', { - extend: 'Ext.window.Window', - title: gettext('Configuration'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: { - xtype: 'component', - itemId: 'configtext', - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }, - - initComponent: function() { - var me = this; - - if (!me.volume) { - throw "no volume specified"; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.callParent(); - - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/vzdump/extractconfig", - method: 'GET', - params: { - volume: me.volume - }, - failure: function(response, opts) { - me.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response,options) { - me.show(); - me.down('#configtext').update(Ext.htmlEncode(response.result.data)); - } - }); - } -}); -Ext.define('PVE.window.Settings', { - extend: 'Ext.window.Window', - - width: '800px', - title: gettext('My Settings'), - iconCls: 'fa fa-gear', - modal: true, - bodyPadding: 10, - resizable: false, - - buttons: [ - { - xtype: 'proxmoxHelpButton', - onlineHelp: 'gui_my_settings', - hidden: false - }, - '->', - { - text: gettext('Close'), - handler: function() { - this.up('window').close(); - } - } - ], - - layout: { - type: 'hbox', - align: 'top' - }, - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - var username = sp.get('login-username') || Proxmox.Utils.noneText; - me.lookupReference('savedUserName').setValue(username); - - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var val = localStorage.getItem('pve-xterm-' + setting); - if (val !== undefined && val !== null) { - var field = me.lookup(setting); - field.setValue(val); - field.resetOriginalValue(); - } - }); - }, - - set_button_status: function() { - var me = this; - - var form = me.lookup('xtermform'); - var valid = form.isValid(); - var dirty = form.isDirty(); - - var hasvalues = false; - var values = form.getValues(); - Ext.Object.eachValue(values, function(value) { - if (value) { - hasvalues = true; - return false; - } - }); - - me.lookup('xtermsave').setDisabled(!dirty || !valid); - me.lookup('xtermreset').setDisabled(!hasvalues); - }, - - control: { - '#xtermjs form': { - dirtychange: 'set_button_status', - validitychange: 'set_button_status' - }, - '#xtermjs button': { - click: function(button) { - var me = this; - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var field = me.lookup(setting); - if (button.reference === 'xtermsave') { - var value = field.getValue(); - if (value) { - localStorage.setItem('pve-xterm-' + setting, value); - } else { - localStorage.removeItem('pve-xterm-' + setting); - } - } else if (button.reference === 'xtermreset') { - field.setValue(undefined); - localStorage.removeItem('pve-xterm-' + setting); - } - field.resetOriginalValue(); - }); - me.set_button_status(); - } - }, - 'button[name=reset]': { - click: function () { - var blacklist = ['GuiCap', 'login-username', 'dash-storages']; - var sp = Ext.state.Manager.getProvider(); - var state; - for (state in sp.state) { - if (sp.state.hasOwnProperty(state)) { - if (blacklist.indexOf(state) !== -1) { - continue; - } - - sp.clear(state); - } - } - - window.location.reload(); - } - }, - 'button[name=clear-username]': { - click: function () { - var me = this; - var usernamefield = me.lookupReference('savedUserName'); - var sp = Ext.state.Manager.getProvider(); - - usernamefield.setValue(Proxmox.Utils.noneText); - sp.clear('login-username'); - } - }, - 'grid[reference=dashboard-storages]': { - selectionchange: function(grid, selected) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - // saves the selected storageids as - // "id1,id2,id3,..." - // or clears the variable - if (selected.length > 0) { - sp.set('dash-storages', - Ext.Array.pluck(selected, 'id').join(',')); - } else { - sp.clear('dash-storages'); - } - }, - afterrender: function(grid) { - var me = grid; - var sp = Ext.state.Manager.getProvider(); - var store = me.getStore(); - var items = []; - me.suspendEvent('selectionchange'); - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - // we have to get the records - // to be able to select them - if (storage !== '') { - var item = store.getById(storage); - if (item) { - items.push(item); - } - } - }); - me.getSelectionModel().select(items); - me.resumeEvent('selectionchange'); - } - } - } - }, - - items: [{ - xtype: 'fieldset', - width: '50%', - title: gettext('Webinterface Settings'), - margin: '5', - layout: { - type: 'vbox', - align: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Dashboard Storages'), - labelAlign: 'left', - labelWidth: '50%' - }, - { - xtype: 'grid', - maxHeight: 150, - reference: 'dashboard-storages', - selModel: { - selType: 'checkboxmodel' - }, - columns: [{ - header: gettext('Name'), - dataIndex: 'storage', - flex: 1 - },{ - header: gettext('Node'), - dataIndex: 'node', - flex: 1 - }], - store: { - type: 'diff', - field: ['type', 'storage', 'id', 'node'], - rstore: PVE.data.ResourceStore, - filters: [{ - property: 'type', - value: 'storage' - }], - sorters: [ 'node','storage'] - } - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Saved User name'), - labelAlign: 'left', - labelWidth: '50%', - stateId: 'login-username', - reference: 'savedUserName', - value: '' - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Clear User name'), - width: 'auto', - name: 'clear-username' - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Layout'), - labelAlign: 'left', - labelWidth: '50%' - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Reset Layout'), - width: 'auto', - name: 'reset' - } - ] - },{ - xtype: 'fieldset', - itemId: 'xtermjs', - width: '50%', - margin: '5', - title: gettext('xterm.js Settings'), - items: [{ - xtype: 'form', - reference: 'xtermform', - border: false, - layout: { - type: 'vbox', - algin: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0' - }, - items: [ - { - xtype: 'textfield', - name: 'fontFamily', - reference: 'fontFamily', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Font-Family') - }, - { - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - name: 'fontSize', - reference: 'fontSize', - minValue: 1, - fieldLabel: gettext('Font-Size') - }, - { - xtype: 'numberfield', - name: 'letterSpacing', - reference: 'letterSpacing', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Letter Spacing') - }, - { - xtype: 'numberfield', - name: 'lineHeight', - minValue: 0.1, - reference: 'lineHeight', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Line Height') - }, - { - xtype: 'container', - layout: { - type: 'hbox', - pack: 'end' - }, - items: [ - { - xtype: 'button', - reference: 'xtermreset', - disabled: true, - text: gettext('Reset') - }, - { - xtype: 'button', - reference: 'xtermsave', - disabled: true, - text: gettext('Save') - } - ] - } - ] - }] - }], - - onShow: function() { - var me = this; - me.callParent(); - } -}); -Ext.define('PVE.panel.StartupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'qm_startup_and_shutdown', - - onGetValues: function(values) { - var me = this; - - var res = PVE.Parser.printStartup(values); - - if (res === undefined || res === '') { - return { 'delete': 'startup' }; - } - - return { startup: res }; - }, - - setStartup: function(value) { - var me = this; - - var startup = PVE.Parser.parseStartup(value); - if (startup) { - me.setValues(startup); - } - }, - - initComponent : function() { - var me = this; - - me.items = [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Shutdown timeout') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.window.StartupEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveWindowStartupEdit', - onlineHelp: undefined, - - initComponent : function() { - - var me = this; - var ipanelConfig = me.onlineHelp ? {onlineHelp: me.onlineHelp} : {}; - var ipanel = Ext.create('PVE.panel.StartupInputPanel', ipanelConfig); - - Ext.applyIf(me, { - subject: gettext('Start/Shutdown order'), - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - ipanel.setStartup(me.vmconfig.startup); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.Install', { - extend: 'Ext.window.Window', - xtype: 'pveCephInstallWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 220, - header: false, - resizable: false, - draggable: false, - modal: true, - nodename: undefined, - shadow: false, - border: false, - bodyBorder: false, - closable: false, - cls: 'install-mask', - bodyCls: 'install-mask', - layout: { - align: 'stretch', - pack: 'center', - type: 'vbox' - }, - viewModel: { - data: { - cephVersion: 'luminous', - isInstalled: false - }, - formulas: { - buttonText: function (get){ - if (get('isInstalled')) { - return gettext('Configure Ceph'); - } else { - return gettext('Install Ceph-') + get('cephVersion'); - } - }, - windowText: function (get) { - if (get('isInstalled')) { - return '

' + - Ext.String.format(gettext('{0} is not initialized.'), 'Ceph') + ' '+ - gettext('You need to create a initial config once.') + '

'; - } else { - return '

' + - Ext.String.format(gettext('{0} is not installed on this node.'), 'Ceph') + '
' + - gettext('Would you like to install it now?') + '

'; - } - } - } - }, - items: [ - { - bind: { - html: '{windowText}' - }, - border: false, - padding: 5, - bodyCls: 'install-mask' - - }, - { - xtype: 'button', - bind: { - text: '{buttonText}' - }, - viewModel: {}, - cbind: { - nodename: '{nodename}' - }, - handler: function() { - var me = this.up('pveCephInstallWindow'); - var win = Ext.create('PVE.ceph.CephInstallWizard',{ - nodename: me.nodename - }); - win.getViewModel().set('isInstalled', this.getViewModel().get('isInstalled')); - win.show(); - me.mon(win,'beforeClose', function(){ - me.fireEvent("cephInstallWindowClosed"); - me.close(); - }); - - } - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallEnableEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveFirewallEnableEdit'], - mixins: ['Proxmox.Mixin.CBind'], - - subject: gettext('Firewall'), - cbindData: { - defaultValue: 0 - }, - width: 350, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - uncheckedValue: 0, - cbind: { - defaultValue: '{defaultValue}', - checked: '{defaultValue}' - }, - deleteDefaultValue: false, - fieldLabel: gettext('Firewall') - }, - { - xtype: 'displayfield', - name: 'warning', - userCls: 'pve-hint', - value: gettext('Warning: Firewall still disabled at datacenter level!'), - hidden: true - } - ], - - beforeShow: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/cluster/firewall/options', - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - if (!response.result.data.enable) { - me.down('displayfield[name=warning]').setVisible(true); - } - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallLograteInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveFirewallLograteInputPanel', - - viewModel: {}, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - reference: 'enable', - fieldLabel: gettext('Enable'), - value: false - }, - { - layout: 'hbox', - border: false, - items: [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Log rate limit'), - minValue: 1, - maxValue: 99, - allowBlank: false, - flex: 2, - value: 1 - }, - { - html: '
/
' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'unit', - comboItems: [['second', 'second'], ['minute', 'minute'], - ['hour', 'hour'], ['day', 'day']], - allowBlank: false, - flex: 1, - value: 'second' - } - ] - }, - { - xtype: 'numberfield', - name: 'burst', - fieldLabel: gettext('Log burst limit'), - minValue: 1, - maxValue: 99, - value: 5 - } - ], - - onGetValues: function(values) { - var me = this; - - var vals = {}; - vals.enable = values.enable !== undefined ? 1 : 0; - vals.rate = values.rate + '/' + values.unit; - vals.burst = values.burst; - var properties = PVE.Parser.printPropertyString(vals, undefined); - if (properties == '') { - return { 'delete': 'log_ratelimit' }; - } - return { log_ratelimit: properties }; - }, - - setValues: function(values) { - var me = this; - - var properties = {}; - if (values.log_ratelimit !== undefined) { - properties = PVE.Parser.parsePropertyString(values.log_ratelimit); - var matches = properties.rate.match(/^(\d+)\/(second|minute|hour|day)$/); - if (matches) { - properties.rate = matches[1]; - properties.unit = matches[2]; - } - } - me.callParent([properties]); - } -}); - -Ext.define('PVE.FirewallLograteEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveFirewallLograteEdit', - - subject: gettext('Log rate limit'), - - items: [{ - xtype: 'pveFirewallLograteInputPanel' - }], - autoLoad: true -}); -Ext.define('PVE.panel.NotesView', { - extend: 'Ext.panel.Panel', - xtype: 'pveNotesView', - - title: gettext("Notes"), - bodyStyle: 'white-space:pre', - bodyPadding: 10, - scrollable: true, - - tbar: { - itemId: 'tbar', - hidden: true, - items: [ - { - text: gettext('Edit'), - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - } - ] - }, - - run_editor: function() { - var me = this; - var win = Ext.create('PVE.window.NotesEdit', { - pveSelNode: me.pveSelNode, - url: me.url - }); - win.show(); - win.on('destroy', me.load, me); - }, - - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data.description || ''; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - listeners: { - render: function(c) { - var me = this; - me.getEl().on('dblclick', me.run_editor, me); - } - }, - - tools: [{ - type: 'gear', - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - }], - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var type = me.pveSelNode.data.type; - if (!Ext.Array.contains(['node', 'qemu', 'lxc'], type)) { - throw 'invalid type specified'; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid && type !== 'node') { - throw "no VM ID specified"; - } - - me.url = '/api2/extjs/nodes/' + nodename + '/'; - - // add the type specific path if qemu/lxc - if (type === 'qemu' || type === 'lxc') { - me.url += type + '/' + vmid + '/'; - } - - me.url += 'config'; - - me.callParent(); - if (type === 'node') { - me.down('#tbar').setVisible(true); - } - me.load(); - } -}); -Ext.define('PVE.grid.ResourceGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveResourceGrid'], - - border: false, - defaultSorter: { - property: 'type', - direction: 'ASC' - }, - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - var coldef = rstore.defaultColumns(); - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: me.defaultSorter, - proxy: { type: 'memory' } - }); - - var textfilter = ''; - - var textfilter_match = function(item) { - var match = false; - Ext.each(['name', 'storage', 'node', 'type', 'text'], function(field) { - var v = item.data[field]; - if (v !== undefined) { - v = v.toLowerCase(); - if (v.indexOf(textfilter) >= 0) { - match = true; - return false; - } - } - }); - return match; - }; - - var updateGrid = function() { - - var filterfn = me.viewFilter ? me.viewFilter.filterfn : null; - - //console.log("START GRID UPDATE " + me.viewFilter); - - store.suspendEvents(); - - var nodeidx = {}; - var gather_child_nodes = function(cn) { - if (!cn) { - return; - } - var cs = cn.childNodes; - if (!cs) { - return; - } - var len = cs.length, i = 0, n, res; - - for (; i < len; i++) { - var child = cs[i]; - var orgnode = rstore.data.get(child.data.id); - if (orgnode) { - if ((!filterfn || filterfn(child)) && - (!textfilter || textfilter_match(child))) { - nodeidx[child.data.id] = orgnode; - } - } - gather_child_nodes(child); - } - }; - gather_child_nodes(me.pveSelNode); - - // remove vanished items - var rmlist = []; - store.each(function(olditem) { - var item = nodeidx[olditem.data.id]; - if (!item) { - //console.log("GRID REM UID: " + olditem.data.id); - rmlist.push(olditem); - } - }); - - if (rmlist.length) { - store.remove(rmlist); - } - - // add new items - var addlist = []; - var key; - for (key in nodeidx) { - if (nodeidx.hasOwnProperty(key)) { - var item = nodeidx[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var olditem = store.getById(item.data.id); - var olditem = store.data.get(item.data.id); - - if (!olditem) { - //console.log("GRID ADD UID: " + item.data.id); - var info = Ext.apply({}, item.data); - var child = Ext.create(store.model, info); - addlist.push(item); - continue; - } - // try to detect changes - var changes = false; - var fieldkeys = PVE.data.ResourceStore.fieldNames; - var fieldcount = fieldkeys.length; - var fieldind; - for (fieldind = 0; fieldind < fieldcount; fieldind++) { - var field = fieldkeys[fieldind]; - if (field != 'id' && item.data[field] != olditem.data[field]) { - changes = true; - //console.log("changed item " + item.id + " " + field + " " + item.data[field] + " != " + olditem.data[field]); - olditem.beginEdit(); - olditem.set(field, item.data[field]); - } - } - if (changes) { - olditem.endEdit(true); - olditem.commit(true); - } - } - } - - if (addlist.length) { - store.add(addlist); - } - - store.sort(); - - store.resumeEvents(); - - store.fireEvent('refresh', store); - - //console.log("END GRID UPDATE"); - }; - - var filter_task = new Ext.util.DelayedTask(function(){ - updateGrid(); - }); - - var load_cb = function() { - updateGrid(); - }; - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-resource', - tbar: [ - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - value: textfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - var v = field.getValue(); - textfilter = v.toLowerCase(); - filter_task.delay(500); - } - } - } - ], - viewConfig: { - stripeRows: true - }, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - destroy: function() { - rstore.un("load", load_cb); - } - }, - columns: coldef - }); - me.callParent(); - updateGrid(); - rstore.on("load", load_cb); - } -}); -Ext.define('PVE.pool.AddVM', { - extend: 'Proxmox.window.Edit', - width: 600, - height: 400, - isAdd: true, - isCreate: true, - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - var vmsField = Ext.create('Ext.form.field.Text', { - name: 'vms', - hidden: true, - allowBlank: false - }); - - var vmStore = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property: 'vmid', - order: 'ASC' - } - ], - filters: [ - function(item) { - return ((item.data.type === 'lxc' || item.data.type === 'qemu') && item.data.pool === ''); - } - ] - }); - - var vmGrid = Ext.create('widget.grid',{ - store: vmStore, - border: true, - height: 300, - scrollable: true, - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected, opts) { - var selectedVms = []; - selected.forEach(function(vm) { - selectedVms.push(vm.data.vmid); - }); - vmsField.setValue(selectedVms); - } - } - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - Ext.apply(me, { - subject: gettext('Virtual Machine'), - items: [ vmsField, vmGrid ] - }); - - me.callParent(); - vmStore.load(); - } -}); - -Ext.define('PVE.pool.AddStorage', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.isCreate = true; - me.isAdd = true; - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - Ext.apply(me, { - subject: gettext('Storage'), - width: 350, - items: [ - { - xtype: 'pveStorageSelector', - name: 'storage', - nodename: 'localhost', - autoSelect: false, - value: '', - fieldLabel: gettext("Storage") - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.grid.PoolMembers', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pvePoolMembers'], - - // fixme: dynamic status update ? - - stateful: true, - stateId: 'grid-pool-members', - - initComponent : function() { - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ], - proxy: { - type: 'proxmox', - root: 'data.members', - url: "/api2/json/pools/" + me.pool - } - }); - - var coldef = PVE.data.ResourceStore.defaultColumns(); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - var params = { 'delete': 1 }; - if (rec.data.type === 'storage') { - params.storage = rec.data.storage; - } else if (rec.data.type === 'qemu' || rec.data.type === 'lxc' || rec.data.type === 'openvz') { - params.vms = rec.data.vmid; - } else { - throw "unknown resource type"; - } - - Proxmox.Utils.API2Request({ - url: '/pools/' + me.pool, - method: 'PUT', - params: params, - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Virtual Machine'), - iconCls: 'pve-itype-icon-qemu', - handler: function() { - var win = Ext.create('PVE.pool.AddVM', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Storage'), - iconCls: 'pve-itype-icon-storage', - handler: function() { - var win = Ext.create('PVE.pool.AddStorage', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - remove_btn - ], - viewConfig: { - stripeRows: true - }, - columns: coldef, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.FWMacroSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFWMacroSelector', - allowBlank: true, - autoSelect: false, - valueField: 'macro', - displayField: 'macro', - listConfig: { - columns: [ - { - header: gettext('Macro'), - dataIndex: 'macro', - hideable: false, - width: 100 - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - flex: 1, - dataIndex: 'descr' - } - ] - }, - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'macro', 'descr' ], - idProperty: 'macro', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/macros" - }, - sorters: { - property: 'macro', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRulePanel', { - extend: 'Proxmox.panel.InputPanel', - - allow_iface: false, - - list_refs_url: undefined, - - onGetValues: function(values) { - var me = this; - - // hack: editable ComboGrid returns nothing when empty, so we need to set '' - // Also, disabled text fields return nothing, so we need to set '' - - Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'log'], function(key) { - if (values[key] === undefined) { - values[key] = ''; - } - }); - - delete values.modified_marker; - - return values; - }, - - initComponent : function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.column1 = [ - { - // hack: we use this field to mark the form 'dirty' when the - // record has errors- so that the user can safe the unmodified - // form again. - xtype: 'hiddenfield', - name: 'modified_marker', - value: '' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'type', - value: 'in', - comboItems: [['in', 'in'], ['out', 'out']], - fieldLabel: gettext('Direction'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - name: 'action', - value: 'ACCEPT', - comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']], - fieldLabel: gettext('Action'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - me.column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } else { - me.column1.push({ - xtype: 'displayfield', - fieldLabel: '', - value: '' - }); - } - - me.column1.push( - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'pveIPRefSelector', - name: 'source', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Source') - - }, - { - xtype: 'pveIPRefSelector', - name: 'dest', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Destination') - } - ); - - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - }, - { - xtype: 'pveFWMacroSelector', - name: 'macro', - fieldLabel: gettext('Macro'), - editable: true, - allowBlank: true, - listeners: { - change: function(f, value) { - if (value === null) { - me.down('field[name=proto]').setDisabled(false); - me.down('field[name=sport]').setDisabled(false); - me.down('field[name=dport]').setDisabled(false); - } else { - me.down('field[name=proto]').setDisabled(true); - me.down('field[name=proto]').setValue(''); - me.down('field[name=sport]').setDisabled(true); - me.down('field[name=sport]').setValue(''); - me.down('field[name=dport]').setDisabled(true); - me.down('field[name=dport]').setValue(''); - } - } - } - }, - { - xtype: 'pveIPProtocolSelector', - name: 'proto', - autoSelect: false, - editable: true, - value: '', - fieldLabel: gettext('Protocol') - }, - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'textfield', - name: 'sport', - value: '', - fieldLabel: gettext('Source port') - }, - { - xtype: 'textfield', - name: 'dport', - value: '', - fieldLabel: gettext('Dest. port') - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'pveFirewallLogLevels' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - list_refs_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.FirewallRulePanel', { - isCreate: me.isCreate, - list_refs_url: me.list_refs_url, - allow_iface: me.allow_iface, - rule_pos: me.rule_pos - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - if (values.errors) { - var field = me.query('[isFormField][name=modified_marker]')[0]; - field.setValue(1); - Ext.Function.defer(function() { - var form = ipanel.up('form').getForm(); - form.markInvalid(values.errors); - }, 100); - } - } - }); - } else if (me.rec) { - ipanel.setValues(me.rec.data); - } - } -}); - -Ext.define('PVE.FirewallGroupRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var column1 = [ - { - xtype: 'hiddenfield', - name: 'type', - value: 'group' - }, - { - xtype: 'pveSecurityGroupsSelector', - name: 'action', - value: '', - fieldLabel: gettext('Security Group'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.FirewallRules', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveFirewallRules', - - onlineHelp: 'chapter_pve_firewall', - - stateful: true, - stateId: 'grid-firewall-rules', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - groupBtn: undefined, - - tbar_prefix: undefined, - - allow_groups: true, - allow_iface: false, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - if (me.groupBtn) { - me.groupBtn.setDisabled(true); - } - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - if (me.groupBtn) { - me.groupBtn.setDisabled(false); - } - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - moveRule: function(from, to) { - var me = this; - - if (!me.base_url) { - return; - } - - Proxmox.Utils.API2Request({ - url: me.base_url + "/" + from, - method: 'PUT', - params: { moveto: to }, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - updateRule: function(rule) { - var me = this; - - if (!me.base_url) { - return; - } - - rule.enable = rule.enable ? 1 : 0; - - var pos = rule.pos; - delete rule.pos; - delete rule.errors; - - Proxmox.Utils.API2Request({ - url: me.base_url + '/' + pos.toString(), - method: 'PUT', - params: rule, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-fw-rule' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type; - - var editor; - if (type === 'in' || type === 'out') { - editor = 'PVE.FirewallRuleEdit'; - } else if (type === 'group') { - editor = 'PVE.FirewallGroupRuleEdit'; - } else { - return; - } - - var win = Ext.create(editor, { - digest: rec.data.digest, - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rule_pos: rec.data.pos - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - var run_copy_editor = function() { - var rec = sm.getSelection()[0]; - - if (!rec) { - return; - } - var type = rec.data.type; - - - if (!(type === 'in' || type === 'out')) { - return; - } - - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rec: rec - }); - - win.show(); - win.on('destroy', reload); - }; - - me.copyBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Copy'), - selModel: sm, - enableFn: function(rec) { - return (rec.data.type === 'in' || rec.data.type === 'out'); - }, - disabled: true, - handler: run_copy_editor - }); - - if (me.allow_groups) { - me.groupBtn = Ext.create('Ext.Button', { - text: gettext('Insert') + ': ' + - gettext('Security Group'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallGroupRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - baseurl: me.base_url + '/', - confirmMsg: false, - getRecordName: function(rec) { - var rule = rec.data; - return rule.pos.toString() + - '?digest=' + encodeURIComponent(rule.digest); - }, - callback: function() { - me.store.load(); - } - }); - - var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : []; - tbar.push(me.addBtn, me.copyBtn); - if (me.groupBtn) { - tbar.push(me.groupBtn); - } - tbar.push(me.removeBtn, me.editBtn); - - var render_errors = function(name, value, metaData, record) { - var errors = record.data.errors; - if (errors && errors[name]) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(errors[name]) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - return value; - }; - - var columns = [ - { - // similar to xtype: 'rownumberer', - dataIndex: 'pos', - resizable: false, - width: 23, - sortable: false, - align: 'right', - hideable: false, - menuDisabled: true, - renderer: function(value, metaData, record, rowIdx, colIdx, store) { - metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special'; - if (value >= 0) { - return value; - } - return ''; - } - }, - { - xtype: 'checkcolumn', - header: gettext('Enable'), - dataIndex: 'enable', - listeners: { - checkchange: function(column, recordIndex, checked) { - var record = me.getStore().getData().items[recordIndex]; - record.commit(); - var data = {}; - Ext.Array.forEach(record.getFields(), function(field) { - data[field.name] = record.get(field.name); - }); - if (!me.allow_iface || !data.iface) { - delete data.iface; - } - me.updateRule(data); - } - }, - width: 50 - }, - { - header: gettext('Type'), - dataIndex: 'type', - renderer: function(value, metaData, record) { - return render_errors('type', value, metaData, record); - }, - width: 50 - }, - { - header: gettext('Action'), - dataIndex: 'action', - renderer: function(value, metaData, record) { - return render_errors('action', value, metaData, record); - }, - width: 80 - }, - { - header: gettext('Macro'), - dataIndex: 'macro', - renderer: function(value, metaData, record) { - return render_errors('macro', value, metaData, record); - }, - width: 80 - } - ]; - - if (me.allow_iface) { - columns.push({ - header: gettext('Interface'), - dataIndex: 'iface', - renderer: function(value, metaData, record) { - return render_errors('iface', value, metaData, record); - }, - width: 80 - }); - } - - columns.push( - { - header: gettext('Source'), - dataIndex: 'source', - renderer: function(value, metaData, record) { - return render_errors('source', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Destination'), - dataIndex: 'dest', - renderer: function(value, metaData, record) { - return render_errors('dest', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Protocol'), - dataIndex: 'proto', - renderer: function(value, metaData, record) { - return render_errors('proto', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Dest. port'), - dataIndex: 'dport', - renderer: function(value, metaData, record) { - return render_errors('dport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Source port'), - dataIndex: 'sport', - renderer: function(value, metaData, record) { - return render_errors('sport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Log level'), - dataIndex: 'log', - renderer: function(value, metaData, record) { - return render_errors('log', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value, metaData, record) { - return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record); - } - } - ); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - plugins: [ - { - ptype: 'gridviewdragdrop', - dragGroup: 'FWRuleDDGroup', - dropGroup: 'FWRuleDDGroup' - } - ], - listeners: { - beforedrop: function(node, data, dropRec, dropPosition) { - if (!dropRec) { - return false; // empty view - } - var moveto = dropRec.get('pos'); - if (dropPosition === 'after') { - moveto++; - } - var pos = data.records[0].get('pos'); - me.moveRule(pos, moveto); - return 0; - }, - itemdblclick: run_editor - } - }, - sortableColumns: false, - columns: columns - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-fw-rule', { - extend: 'Ext.data.Model', - fields: [ { name: 'enable', type: 'boolean' }, - 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface', - 'dport', 'sport', 'comment', 'pos', 'digest', 'errors' ], - idProperty: 'pos' - }); - -}); -Ext.define('PVE.FirewallAliasEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - alias_name: undefined, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.alias_name === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.alias_name; - me.method = 'PUT'; - } - - var items = [ - { - xtype: 'textfield', - name: me.isCreate ? 'name' : 'rename', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'cidr', - fieldLabel: gettext('IP/CIDR'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ]; - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - items: items - }); - - Ext.apply(me, { - subject: gettext('Alias'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - values.rename = values.name; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('pve-fw-aliases', { - extend: 'Ext.data.Model', - - fields: [ 'name', 'cidr', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.FirewallAliases', { - extend: 'Ext.grid.Panel', - alias: ['widget.pveFirewallAliases'], - - onlineHelp: 'pve_firewall_ip_aliases', - - stateful: true, - stateId: 'grid-firewall-aliases', - - base_url: undefined, - - title: gettext('Alias'), - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-aliases', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url, - alias_name: rec.data.name - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - - Ext.apply(me, { - store: store, - tbar: [ me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: gettext('Name'), dataIndex: 'name', width: 100 }, - { header: gettext('IP/CIDR'), dataIndex: 'cidr', width: 100 }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - me.on('activate', reload); - } -}); -Ext.define('PVE.FirewallOptions', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveFirewallOptions'], - - fwtype: undefined, // 'dc', 'node' or 'vm' - - base_url: undefined, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - if (me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm') { - if (me.fwtype === 'node') { - me.cwidth1 = 250; - } - } else { - throw "unknown firewall option type"; - } - - me.rows = {}; - - var add_boolean_row = function(name, text, defaultValue) { - me.add_boolean_row(name, text, { defaultValue: defaultValue }); - }; - var add_integer_row = function(name, text, minValue, labelWidth) { - me.add_integer_row(name, text, { - minValue: minValue, - deleteEmpty: true, - labelWidth: labelWidth, - renderer: function(value) { - if (value === undefined) { - return Proxmox.Utils.defaultText; - } - - return value; - } - }); - }; - - var add_log_row = function(name, labelWidth) { - me.rows[name] = { - header: name, - required: true, - defaultValue: 'nolog', - editor: { - xtype: 'proxmoxWindowEdit', - subject: name, - fieldDefaults: { labelWidth: labelWidth || 100 }, - items: { - xtype: 'pveFirewallLogLevels', - name: name, - fieldLabel: name - } - } - }; - }; - - if (me.fwtype === 'node') { - me.rows.enable = { - required: true, - defaultValue: 1, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 1 - } - }; - add_boolean_row('nosmurfs', gettext('SMURFS filter'), 1); - add_boolean_row('tcpflags', gettext('TCP flags filter'), 0); - add_boolean_row('ndp', 'NDP', 1); - add_integer_row('nf_conntrack_max', 'nf_conntrack_max', 32768, 120); - add_integer_row('nf_conntrack_tcp_timeout_established', - 'nf_conntrack_tcp_timeout_established', 7875, 250); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - add_log_row('tcp_flags_log_level', 120); - add_log_row('smurf_log_level'); - } else if (me.fwtype === 'vm') { - me.rows.enable = { - required: true, - defaultValue: 0, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 0 - } - }; - add_boolean_row('dhcp', 'DHCP', 1); - add_boolean_row('ndp', 'NDP', 1); - add_boolean_row('radv', gettext('Router Advertisement'), 0); - add_boolean_row('macfilter', gettext('MAC filter'), 1); - add_boolean_row('ipfilter', gettext('IP filter'), 0); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - } else if (me.fwtype === 'dc') { - add_boolean_row('enable', gettext('Firewall'), 0); - add_boolean_row('ebtables', 'ebtables', 1); - me.rows.log_ratelimit = { - header: gettext('Log rate limit'), - required: true, - defaultValue: 'enable=0', - editor: { - xtype: 'pveFirewallLograteEdit' - } - }; - } - - if (me.fwtype === 'dc' || me.fwtype === 'vm') { - me.rows.policy_in = { - header: gettext('Input Policy'), - required: true, - defaultValue: 'DROP', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Input Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_in', - value: 'DROP', - fieldLabel: gettext('Input Policy') - } - } - }; - - me.rows.policy_out = { - header: gettext('Output Policy'), - required: true, - defaultValue: 'ACCEPT', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Output Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_out', - value: 'ACCEPT', - fieldLabel: gettext('Output Policy') - } - } - }; - } - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - var rowdef = me.rows[rec.data.key]; - edit_btn.setDisabled(!rowdef.editor); - }; - - Ext.apply(me, { - url: "/api2/json" + me.base_url, - tbar: [ edit_btn ], - editorConfig: { - url: '/api2/extjs/' + me.base_url - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); - - -Ext.define('PVE.FirewallLogLevels', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallLogLevels'], - - name: 'log', - fieldLabel: gettext('Log level'), - value: 'nolog', - comboItems: [['nolog', 'nolog'], ['emerg', 'emerg'], ['alert', 'alert'], - ['crit', 'crit'], ['err', 'err'], ['warning', 'warning'], - ['notice', 'notice'], ['info', 'info'], ['debug', 'debug']] -}); -/* - * Left Treepanel, containing all the ressources we manage in this datacenter: server nodes, server storages, VMs and Containers - */ -Ext.define('PVE.tree.ResourceTree', { - extend: 'Ext.tree.TreePanel', - alias: ['widget.pveResourceTree'], - - statics: { - typeDefaults: { - node: { - iconCls: 'fa fa-building', - text: gettext('Nodes') - }, - pool: { - iconCls: 'fa fa-tags', - text: gettext('Resource Pool') - }, - storage: { - iconCls: 'fa fa-database', - text: gettext('Storage') - }, - qemu: { - iconCls: 'fa fa-desktop', - text: gettext('Virtual Machine') - }, - lxc: { - //iconCls: 'x-tree-node-lxc', - iconCls: 'fa fa-cube', - text: gettext('LXC Container') - }, - template: { - iconCls: 'fa fa-file-o' - } - } - }, - - useArrows: true, - - // private - nodeSortFn: function(node1, node2) { - var n1 = node1.data; - var n2 = node2.data; - - if ((n1.groupbyid && n2.groupbyid) || - !(n1.groupbyid || n2.groupbyid)) { - - var tcmp; - - var v1 = n1.type; - var v2 = n2.type; - - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - - // numeric compare for VM IDs - // sort templates after regular VMs - if (v1 === 'qemu' || v1 === 'lxc') { - if (n1.template && !n2.template) { - return 1; - } else if (n2.template && !n1.template) { - return -1; - } - v1 = n1.vmid; - v2 = n2.vmid; - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - } - - return n1.id > n2.id ? 1 : (n1.id < n2.id ? -1 : 0); - } else if (n1.groupbyid) { - return -1; - } else if (n2.groupbyid) { - return 1; - } - }, - - // private: fast binary search - findInsertIndex: function(node, child, start, end) { - var me = this; - - var diff = end - start; - - var mid = start + (diff>>1); - - if (diff <= 0) { - return start; - } - - var res = me.nodeSortFn(child, node.childNodes[mid]); - if (res <= 0) { - return me.findInsertIndex(node, child, start, mid); - } else { - return me.findInsertIndex(node, child, mid + 1, end); - } - }, - - setIconCls: function(info) { - var me = this; - - var cls = PVE.Utils.get_object_icon_class(info.type, info); - - if (cls !== '') { - info.iconCls = cls; - } - }, - - // add additional elements to text - // at the moment only the usage indicator for storages - setText: function(info) { - var me = this; - - var status = ''; - if (info.type === 'storage') { - var maxdisk = info.maxdisk; - var disk = info.disk; - var usage = disk/maxdisk; - var cls = ''; - if (usage <= 1.0 && usage >= 0.0) { - var height = (usage*100).toFixed(0); - var neg_height = (100-usage*100).toFixed(0); - status = '
'; - status += '
'; - status += '
'; - status += '
'; - } - } - - info.text = status + info.text; - }, - - setToolTip: function(info) { - if (info.type === 'pool' || info.groupbyid !== undefined) { - return; - } - - var qtips = [gettext('Status') + ': ' + (info.qmpstatus || info.status)]; - if (info.hastate != 'unmanaged') { - qtips.push(gettext('HA State') + ": " + info.hastate); - } - - info.qtip = qtips.join(', '); - }, - - // private - addChildSorted: function(node, info) { - var me = this; - - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - - var defaults; - if (info.groupbyid) { - info.text = info.groupbyid; - if (info.type === 'type') { - defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid]; - if (defaults && defaults.text) { - info.text = defaults.text; - } - } - } - var child = Ext.create('PVETree', info); - - var cs = node.childNodes; - var pos; - if (cs) { - pos = cs[me.findInsertIndex(node, child, 0, cs.length)]; - } - - node.insertBefore(child, pos); - - return child; - }, - - // private - groupChild: function(node, info, groups, level) { - var me = this; - - var groupby = groups[level]; - var v = info[groupby]; - - if (v) { - var group = node.findChild('groupbyid', v); - if (!group) { - var groupinfo; - if (info.type === groupby) { - groupinfo = info; - } else { - groupinfo = { - type: groupby, - id : groupby + "/" + v - }; - if (groupby !== 'type') { - groupinfo[groupby] = v; - } - } - groupinfo.leaf = false; - groupinfo.groupbyid = v; - group = me.addChildSorted(node, groupinfo); - } - if (info.type === groupby) { - return group; - } - if (group) { - return me.groupChild(group, info, groups, level + 1); - } - } - - return me.addChildSorted(node, info); - }, - - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - if (!me.viewFilter) { - me.viewFilter = {}; - } - - var pdata = { - dataIndex: {}, - updateCount: 0 - }; - - var store = Ext.create('Ext.data.TreeStore', { - model: 'PVETree', - root: { - expanded: true, - id: 'root', - text: gettext('Datacenter'), - iconCls: 'fa fa-server' - } - }); - - var stateid = 'rid'; - - var updateTree = function() { - var tmp; - - store.suspendEvents(); - - var rootnode = me.store.getRootNode(); - // remember selected node (and all parents) - var sm = me.getSelectionModel(); - - var lastsel = sm.getSelection()[0]; - var reselect = false; - var parents = []; - var p = lastsel; - while (p && !!(p = p.parentNode)) { - parents.push(p); - } - - var index = pdata.dataIndex; - - var groups = me.viewFilter.groups || []; - var filterfn = me.viewFilter.filterfn; - - // remove vanished or moved items - // update in place changed items - var key; - for (key in index) { - if (index.hasOwnProperty(key)) { - var olditem = index[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var item = rstore.getById(olditem.data.id); - var item = rstore.data.get(olditem.data.id); - - var changed = false; - var moved = false; - if (item) { - // test if any grouping attributes changed - // this will also catch migrated nodes - // in server view - var i, len; - for (i = 0, len = groups.length; i < len; i++) { - var attr = groups[i]; - if (item.data[attr] != olditem.data[attr]) { - //console.log("changed " + attr); - moved = true; - break; - } - } - - // explicitely check for node, since - // in some views, node is not a grouping - // attribute - if (!moved && item.data.node !== olditem.data.node) { - moved = true; - } - - // tree item has been updated - if ((item.data.text !== olditem.data.text) || - (item.data.running !== olditem.data.running) || - (item.data.template !== olditem.data.template) || - (item.data.status !== olditem.data.status) || - (item.data.hastate!== olditem.data.hastate)) { - //console.log("changed node/text/running " + olditem.data.id); - changed = true; - } - - // fixme: also test filterfn()? - } - - if (changed) { - olditem.beginEdit(); - //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running); - var info = olditem.data; - Ext.apply(info, item.data); - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - olditem.commit(); - } - if ((!item || moved) && olditem.isLeaf()) { - //console.log("REM UID: " + key + " ITEM " + olditem.data.id); - delete index[key]; - var parentNode = olditem.parentNode; - // when the selected item disappears, - // we have to deselect it here, and reselect it - // later - if (lastsel && olditem.data.id === lastsel.data.id) { - reselect = true; - sm.deselect(olditem); - } - // since the store events are suspended, we - // manually remove the item from the store also - store.remove(olditem); - parentNode.removeChild(olditem, true); - } - } - } - - // add new items - rstore.each(function(item) { - var olditem = index[item.data.id]; - if (olditem) { - return; - } - - if (filterfn && !filterfn(item)) { - return; - } - - //console.log("ADD UID: " + item.data.id); - - var info = Ext.apply({ leaf: true }, item.data); - - var child = me.groupChild(rootnode, info, groups, 0); - if (child) { - index[item.data.id] = child; - } - }); - - store.resumeEvents(); - store.fireEvent('refresh', store); - - // select parent node is selection vanished - if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) { - lastsel = rootnode; - while (!!(p = parents.shift())) { - if (!!(tmp = rootnode.findChild('id', p.data.id, true))) { - lastsel = tmp; - break; - } - } - me.selectById(lastsel.data.id); - } else if (lastsel && reselect) { - me.selectById(lastsel.data.id); - } - - // on first tree load set the selection from the stateful provider - if (!pdata.updateCount) { - rootnode.expand(); - me.applyState(sp.get(stateid)); - } - - pdata.updateCount++; - }; - - var statechange = function(sp, key, value) { - if (key === stateid) { - me.applyState(value); - } - }; - - sp.on('statechange', statechange); - - Ext.apply(me, { - allowSelection: true, - store: store, - viewConfig: { - // note: animate cause problems with applyState - animate: false - }, - //useArrows: true, - //rootVisible: false, - //title: 'Resource Tree', - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - destroy: function() { - rstore.un("load", updateTree); - }, - beforecellmousedown: function (tree, td, cellIndex, record, tr, rowIndex, ev) { - var sm = me.getSelectionModel(); - // disable selection when right clicking - // except the record is already selected - me.allowSelection = (ev.button !== 2) || sm.isSelected(record); - }, - beforeselect: function (tree, record, index, eopts) { - var allow = me.allowSelection; - me.allowSelection = true; - return allow; - }, - itemdblclick: PVE.Utils.openTreeConsole - }, - setViewFilter: function(view) { - me.viewFilter = view; - me.clearTree(); - updateTree(); - }, - setDatacenterText: function(clustername) { - var rootnode = me.store.getRootNode(); - - var rnodeText = gettext('Datacenter'); - if (clustername !== undefined) { - rnodeText += ' (' + clustername + ')'; - } - - rootnode.beginEdit(); - rootnode.data.text = rnodeText; - rootnode.commit(); - }, - clearTree: function() { - pdata.updateCount = 0; - var rootnode = me.store.getRootNode(); - rootnode.collapse(); - rootnode.removeAll(); - pdata.dataIndex = {}; - me.getSelectionModel().deselectAll(); - }, - selectExpand: function(node) { - var sm = me.getSelectionModel(); - if (!sm.isSelected(node)) { - sm.select(node); - var cn = node; - while (!!(cn = cn.parentNode)) { - if (!cn.isExpanded()) { - cn.expand(); - } - } - me.getView().focusRow(node); - } - }, - selectById: function(nodeid) { - var rootnode = me.store.getRootNode(); - var sm = me.getSelectionModel(); - var node; - if (nodeid === 'root') { - node = rootnode; - } else { - node = rootnode.findChild('id', nodeid, true); - } - if (node) { - me.selectExpand(node); - } - return node; - }, - applyState : function(state) { - var sm = me.getSelectionModel(); - if (state && state.value) { - me.selectById(state.value); - } else { - sm.deselectAll(); - } - } - }); - - me.callParent(); - - var sm = me.getSelectionModel(); - sm.on('select', function(sm, n) { - sp.set(stateid, { value: n.data.id}); - }); - - rstore.on("load", updateTree); - rstore.startUpdate(); - //rstore.stopUpdate(); - } - -}); -Ext.define('pve-fw-ipsets', { - extend: 'Ext.data.Model', - fields: [ 'name', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.IPSetList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetList', - - stateful: true, - stateId: 'grid-firewall-ipsetlist', - - ipset_panel: undefined, - - base_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - initComponent: function() { - - var me = this; - - if (me.ipset_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-ipsets', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('Proxmox.window.Edit', { - subject: "IPSet '" + rec.data.name + "'", - url: me.base_url, - method: 'POST', - digest: rec.data.digest, - items: [ - { - xtype: 'hiddenfield', - name: 'rename', - value: rec.data.name - }, - { - xtype: 'textfield', - name: 'name', - value: rec.data.name, - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: rec.data.comment, - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('Proxmox.window.Edit', { - subject: 'IPSet', - url: me.base_url, - method: 'POST', - items: [ - { - xtype: 'textfield', - name: 'name', - value: '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - Ext.apply(me, { - store: store, - tbar: [ 'IPSet:', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: 'IPSet', dataIndex: 'name', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = me.base_url + '/' + rec.data.name; - me.ipset_panel.setBaseUrl(url); - }, - deselect: function() { - me.ipset_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.IPSetCidrEdit', { - extend: 'Proxmox.window.Edit', - - cidr: undefined, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.cidr === undefined); - - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.cidr; - me.method = 'PUT'; - } - - var column1 = []; - - if (me.isCreate) { - if (!me.list_refs_url) { - throw "no alias_base_url specified"; - } - - column1.push({ - xtype: 'pveIPRefSelector', - name: 'cidr', - ref_type: 'alias', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } else { - column1.push({ - xtype: 'displayfield', - name: 'cidr', - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'nomatch', - checked: false, - uncheckedValue: 0, - fieldLabel: 'nomatch' - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('IP/CIDR'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.IPSetGrid', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetGrid', - - stateful: true, - stateId: 'grid-firewall-ipsets', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no1 list_refs_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-ipset' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - cidr: rec.data.cidr - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Add'), - disabled: true, - handler: function() { - if (!me.base_url) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - var render_errors = function(value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors.cidr || errors.nomatch; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - Ext.apply(me, { - tbar: [ 'IP/CIDR:', me.addBtn, me.removeBtn, me.editBtn ], - store: store, - selModel: sm, - listeners: { - itemdblclick: run_editor - }, - columns: [ - { - xtype: 'rownumberer' - }, - { - header: gettext('IP/CIDR'), - dataIndex: 'cidr', - width: 150, - renderer: function(value, metaData, record) { - value = render_errors(value, metaData, record); - if (record.data.nomatch) { - return '! ' + value; - } - return value; - } - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value) { - return Ext.util.Format.htmlEncode(value); - } - } - ] - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-ipset', { - extend: 'Ext.data.Model', - fields: [ { name: 'nomatch', type: 'boolean' }, - 'cidr', 'comment', 'errors' ], - idProperty: 'cidr' - }); - -}); - -Ext.define('PVE.IPSet', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveIPSet', - - title: 'IPSet', - - onlineHelp: 'pve_firewall_ip_sets', - - list_refs_url: undefined, - - initComponent: function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var ipset_panel = Ext.createWidget('pveIPSetGrid', { - region: 'center', - list_refs_url: me.list_refs_url, - border: false - }); - - var ipset_list = Ext.createWidget('pveIPSetList', { - region: 'west', - ipset_panel: ipset_panel, - base_url: me.base_url, - width: '50%', - border: false, - split: true - }); - - Ext.apply(me, { - layout: 'border', - items: [ ipset_list, ipset_panel ], - listeners: { - show: function() { - ipset_list.fireEvent('show', ipset_list); - } - } - }); - - me.callParent(); - } -}); -/* - * Base class for all the multitab config panels - * - * How to use this: - * - * You create a subclass of this, and then define your wanted tabs - * as items like this: - * - * items: [{ - * title: "myTitle", - * xytpe: "somextype", - * iconCls: 'fa fa-icon', - * groups: ['somegroup'], - * expandedOnInit: true, - * itemId: 'someId' - * }] - * - * this has to be in the declarative syntax, else we - * cannot save them for later - * (so no Ext.create or Ext.apply of an item in the subclass) - * - * the groups array expects the itemids of the items - * which are the parents, which have to come before they - * are used - * - * if you want following the tree: - * - * Option1 - * Option2 - * -> SubOption1 - * -> SubSubOption1 - * - * the suboption1 group array has to look like this: - * groups: ['itemid-of-option2'] - * - * and of subsuboption1: - * groups: ['itemid-of-option2', 'itemid-of-suboption1'] - * - * setting the expandedOnInit determines if the item/group is expanded - * initially (false by default) - */ -Ext.define('PVE.panel.Config', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePanelConfig', - - showSearch: true, // add a ressource grid with a search button as first tab - viewFilter: undefined, // a filter to pass to that ressource grid - - tbarSpacing: true, // if true, adds a spacer after the title in tbar - - dockedItems: [{ - // this is needed for the overflow handler - xtype: 'toolbar', - overflowHandler: 'scroller', - dock: 'left', - style: { - backgroundColor: '#f5f5f5', - padding: 0, - margin: 0 - }, - items: { - xtype: 'treelist', - itemId: 'menu', - ui: 'nav', - expanderOnly: true, - expanderFirst: false, - animation: false, - singleExpand: false, - listeners: { - selectionchange: function(treeList, selection) { - var me = this.up('panel'); - me.suspendLayout = true; - me.activateCard(selection.data.id); - me.suspendLayout = false; - me.updateLayout(); - }, - itemclick: function(treelist, info) { - var olditem = treelist.getSelection(); - var newitem = info.node; - - // when clicking on the expand arrow, - // we dont select items, but still want - // the original behaviour - if (info.select === false) { - return; - } - - // if you click on a different item which is open, - // leave it open - // else toggle the clicked item - if (olditem.data.id !== newitem.data.id && - newitem.data.expanded === true) { - info.toggle = false; - } else { - info.toggle = true; - } - } - } - } - }, - { - xtype: 'toolbar', - itemId: 'toolbar', - dock: 'top', - height: 36, - overflowHandler: 'scroller' - }], - - firstItem: '', - layout: 'card', - border: 0, - - // used for automated test - selectById: function(cardid) { - var me = this; - - var root = me.store.getRoot(); - var selection = root.findChild('id', cardid, true); - - if (selection) { - selection.expand(); - var menu = me.down('#menu'); - menu.setSelection(selection); - return cardid; - } - }, - - activateCard: function(cardid) { - var me = this; - if (me.savedItems[cardid]) { - var curcard = me.getLayout().getActiveItem(); - var newcard = me.add(me.savedItems[cardid]); - me.helpButton.setOnlineHelp(newcard.onlineHelp || me.onlineHelp); - if (curcard) { - me.setActiveItem(cardid); - me.remove(curcard, true); - - // trigger state change - - var ncard = cardid; - // Note: '' is alias for first tab. - // First tab can be 'search' or something else - if (cardid === me.firstItem) { - ncard = ''; - } - if (me.hstateid) { - me.sp.set(me.hstateid, { value: ncard }); - } - } - } - }, - - initComponent: function() { - var me = this; - - var stateid = me.hstateid; - - me.sp = Ext.state.Manager.getProvider(); - - var activeTab; // leaving this undefined means items[0] will be the default tab - - if (stateid) { - var state = me.sp.get(stateid); - if (state && state.value) { - // if this tab does not exists, it chooses the first - activeTab = state.value; - } - } - - // get title - var title = me.title || me.pveSelNode.data.text; - me.title = undefined; - - // create toolbar - var tbar = me.tbar || []; - me.tbar = undefined; - - if (!me.onlineHelp) { - switch(me.pveSelNode.data.id) { - case 'type/storage':me.onlineHelp = 'chapter-pvesm.html'; break; - case 'type/qemu':me.onlineHelp = 'chapter-qm.html'; break; - case 'type/lxc':me.onlineHelp = 'chapter-pct.html'; break; - case 'type/pool':me.onlineHelp = 'chapter-pveum.html#_pools'; break; - case 'type/node':me.onlineHelp = 'chapter-sysadmin.html'; break; - } - } - - if (me.tbarSpacing) { - tbar.unshift('->'); - } - tbar.unshift({ - xtype: 'tbtext', - text: title, - baseCls: 'x-panel-header-text' - }); - - me.helpButton = Ext.create('Proxmox.button.Help', { - hidden: false, - listenToGlobalEvent: false, - onlineHelp: me.onlineHelp || undefined - }); - - tbar.push(me.helpButton); - - me.dockedItems[1].items = tbar; - - // include search tab - me.items = me.items || []; - if (me.showSearch) { - me.items.unshift({ - itemId: 'search', - title: gettext('Search'), - iconCls: 'fa fa-search', - xtype: 'pveResourceGrid', - pveSelNode: me.pveSelNode - }); - } - - me.savedItems = {}; - /*jslint confusion:true*/ - if (me.items[0]) { - me.firstItem = me.items[0].itemId; - } - /*jslint confusion:false*/ - - me.store = Ext.create('Ext.data.TreeStore', { - root: { - expanded: true - } - }); - var root = me.store.getRoot(); - me.items.forEach(function(item){ - var treeitem = Ext.create('Ext.data.TreeModel',{ - id: item.itemId, - text: item.title, - iconCls: item.iconCls, - leaf: true, - expanded: item.expandedOnInit - }); - item.header = false; - if (me.savedItems[item.itemId] !== undefined) { - throw "itemId already exists, please use another"; - } - me.savedItems[item.itemId] = item; - - var group; - var curnode = root; - - // get/create the group items - while (Ext.isArray(item.groups) && item.groups.length > 0) { - group = item.groups.shift(); - - var child = curnode.findChild('id', group); - if (child === null) { - // did not find the group item - // so add it where we are - break; - } - curnode = child; - } - - // insert the item - - // lets see if it already exists - var node = curnode.findChild('id', item.itemId); - - if (node === null) { - curnode.appendChild(treeitem); - } else { - // should not happen! - throw "id already exists"; - } - }); - - delete me.items; - me.defaults = me.defaults || {}; - Ext.apply(me.defaults, { - pveSelNode: me.pveSelNode, - viewFilter: me.viewFilter, - workspace: me.workspace, - border: 0 - }); - - me.callParent(); - - var menu = me.down('#menu'); - var selection = root.findChild('id', activeTab, true) || root.firstChild; - var node = selection; - while (node !== root) { - node.expand(); - node = node.parentNode; - } - menu.setStore(me.store); - menu.setSelection(selection); - - // on a state change, - // select the new item - var statechange = function(sp, key, state) { - // it the state change is for this panel - if (stateid && (key === stateid) && state) { - // get active item - var acard = me.getLayout().getActiveItem().itemId; - // get the itemid of the new value - var ncard = state.value || me.firstItem; - if (ncard && (acard != ncard)) { - // select the chosen item - menu.setSelection(root.findChild('id', ncard, true) || root.firstChild); - } - } - }; - - if (stateid) { - me.mon(me.sp, 'statechange', statechange); - } - } -}); -Ext.define('PVE.grid.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveBackupView'], - - onlineHelp: 'chapter_vzdump', - - stateful: true, - stateId: 'grid-guest-backup', - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmtype = me.pveSelNode.data.type; - if (!vmtype) { - throw "no VM type specified"; - } - - var vmtypeFilter; - if (vmtype === 'openvz') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-openvz-'); - }; - } else if (vmtype === 'lxc') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-lxc-'); - }; - } else if (vmtype === 'qemu') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-qemu-'); - }; - } else { - throw "unsupported VM type '" + vmtype + "'"; - } - - var searchFilter = { - property: 'volid', - // on initial store display only our vmid backups - // surround with minus sign to prevent the 2016 VMID bug - value: vmtype + '-' + vmid + '-', - anyMatch: true, - caseSensitive: false - }; - - me.store = Ext.create('Ext.data.Store', { - model: 'pve-storage-content', - sorters: { - property: 'volid', - order: 'DESC' - }, - filters: [ - vmtypeFilter, - searchFilter - ] - }); - - var reload = Ext.Function.createBuffered(function() { - if (me.store) { - me.store.load(); - } - }, 100); - - var setStorage = function(storage) { - var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content'; - url += '?content=backup'; - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - reload(); - }; - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'backup', - allowBlank: false, - listeners: { - change: function(f, value) { - setStorage(value); - } - } - }); - - var storagefilter = Ext.create('Ext.form.field.Text', { - fieldLabel: gettext('Search'), - labelWidth: 50, - labelAlign: 'right', - enableKeyEvents: true, - value: searchFilter.value, - listeners: { - buffer: 500, - keyup: function(field) { - me.store.clearFilter(true); - searchFilter.value = field.getValue(); - me.store.filter([ - vmtypeFilter, - searchFilter - ]); - } - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var backup_btn = Ext.create('Ext.button.Button', { - text: gettext('Backup now'), - handler: function() { - var win = Ext.create('PVE.window.Backup', { - nodename: nodename, - vmid: vmid, - vmtype: vmtype, - storage: storagesel.getValue(), - listeners : { - close: function() { - reload(); - } - } - }); - win.show(); - } - }); - - var restore_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Restore'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var volid = rec.data.volid; - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - vmid: vmid, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }); - - var delete_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - dangerous: true, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.volid + "'"); - msg += " " + gettext('This will permanently erase all data.'); - - return msg; - }, - getUrl: function(rec) { - var storage = storagesel.getValue(); - return '/nodes/' + nodename + '/storage/' + storage + '/content/' + rec.data.volid; - }, - callback: function() { - reload(); - } - }); - - var config_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var storage = storagesel.getValue(); - if (!storage) { - return; - } - - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }); - - Ext.apply(me, { - selModel: sm, - tbar: [ backup_btn, restore_btn, delete_btn,config_btn, '->', storagesel, storagefilter ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'volid' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ] - }); - - me.callParent(); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.CephCreateFS', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreateFS', - - showTaskViewer: true, - onlineHelp: 'pveceph_fs_create', - - subject: 'Ceph FS', - isCreate: true, - method: 'POST', - - setFSName: function(fsName) { - var me = this; - - if (fsName === '' || fsName === undefined) { - fsName = 'cephfs'; - } - - me.url = "/nodes/" + me.nodename + "/ceph/fs/" + fsName; - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - value: 'cephfs', - listeners: { - change: function(f, value) { - this.up('pveCephCreateFS').setFSName(value); - } - }, - submitValue: false, // already encoded in apicall URL - emptyText: 'cephfs' - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'Placement Groups', - name: 'pg_num', - value: 128, - emptyText: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add Storage'), - value: true, - name: 'add-storage' - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.setFSName(); - - me.callParent(); - } -}); - -Ext.define('PVE.CephCreateMDS', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreateMDS', - - showProgress: true, - onlineHelp: 'pveceph_fs_mds', - - subject: 'Ceph MDS', - isCreate: true, - method: 'POST', - - setNode: function(nodename) { - var me = this; - - me.nodename = nodename; - me.url = "/nodes/" + nodename + "/ceph/mds/" + nodename; - }, - - items: [ - { - xtype: 'pveNodeSelector', - fieldLabel: gettext('Node'), - selectCurNode: true, - submitValue: false, - allowBlank: false, - listeners: { - change: function(f, value) { - this.up('pveCephCreateMDS').setNode(value); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.setNode(me.nodename); - - me.callParent(); - } -}); - -Ext.define('PVE.NodeCephFSPanel', { - extend: 'Ext.panel.Panel', - xtype: 'pveNodeCephFSPanel', - mixins: ['Proxmox.Mixin.CBind'], - - title: gettext('CephFS'), - onlineHelp: 'pveceph_fs', - - border: false, - defaults: { - border: false, - cbind: { - nodename: '{nodename}' - } - }, - - viewModel: { - parent: null, - data: { - cephfsConfigured: false, - mdsCount: 0 - }, - formulas: { - canCreateFS: function(get) { - return (!get('cephfsConfigured') && get('mdsCount') > 0); - } - } - }, - - items: [ - { - xtype: 'grid', - emptyText: Ext.String.format(gettext('No {0} configured.'), 'CephFS'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-ceph-fs', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + view.nodename + '/ceph/fs' - }, - model: 'pve-ceph-fs' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'name', - order: 'DESC' - } - })); - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){ - me.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.rstore.startUpdate(); - }); - } - ); - }); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onCreate: function() { - var view = this.getView(); - view.rstore.stopUpdate(); - var win = Ext.create('PVE.CephCreateFS', { - autoShow: true, - nodename: view.nodename, - listeners: { - destroy: function() { - view.rstore.startUpdate(); - } - } - }); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!(success && records && records.length > 0)) { - vm.set('cephfsConfigured', false); - return; - } - vm.set('cephfsConfigured', true); - } - }, - tbar: [ - { - text: gettext('Create CephFS'), - reference: 'createButton', - handler: 'onCreate', - bind: { - // only one CephFS per Ceph cluster makes sense for now - disabled: '{!canCreateFS}' - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - dataIndex: 'name' - }, - { - header: 'Data Pool', - flex: 1, - dataIndex: 'data_pool' - }, - { - header: 'Metadata Pool', - flex: 1, - dataIndex: 'metadata_pool' - } - ], - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'grid', - title: gettext('Metadata Servers'), - emptyText: Ext.String.format(gettext('No {0} configured.'), 'MDS'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 3 * 1000, - autoStart: true, - storeid: 'pve-ceph-mds', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/'+ view.nodename +'/ceph/mds' - }, - model: 'pve-ceph-mds' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'id', - order: 'DESC' - } - })); - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){ - me.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.rstore.startUpdate(); - }); - } - ); - }); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records) { - vm.set('mdsCount', 0); - return; - } - vm.set('mdsCount', records.length); - }, - onCreateMDS: function() { - var view = this.getView(); - view.rstore.stopUpdate(); - var win = Ext.create('PVE.CephCreateMDS', { - autoShow: true, - nodename: view.nodename, - listeners: { - destroy: function() { - view.rstore.startUpdate(); - } - } - }); - } - }, - tbar: [ - { - text: gettext('Create MDS'), - reference: 'createButton', - handler: 'onCreateMDS' - }, - { - text: gettext('Destroy MDS'), - xtype: 'proxmoxStdRemoveButton', - getUrl: function(rec) { - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - return "/nodes/" + rec.data.host + "/ceph/mds/" + rec.data.name; - }, - callback: function(options, success, response) { - if (!success) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - return; - } - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - dataIndex: 'name' - }, - { - header: gettext('Host'), - flex: 1, - dataIndex: 'host' - }, - { - header: gettext('Address'), - flex: 1, - dataIndex: 'addr' - }, - { - header: gettext('State'), - flex: 1, - dataIndex: 'state' - } - ], - cbind: { - nodename: '{nodename}' - } - } - ] -}, function() { - Ext.define('pve-ceph-mds', { - extend: 'Ext.data.Model', - fields: [ 'name', 'host', 'addr', 'state' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/localhost/ceph/mds" - }, - idProperty: 'name' - }); - Ext.define('pve-ceph-fs', { - extend: 'Ext.data.Model', - fields: [ 'name', 'data_pool', 'metadata_pool' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/localhost/ceph/fs" - }, - idProperty: 'name' - }); -}); -Ext.define('PVE.CephCreatePool', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreatePool', - - showProgress: true, - onlineHelp: 'pve_ceph_pools', - - subject: 'Ceph Pool', - isCreate: true, - method: 'POST', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Size'), - name: 'size', - value: 3, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Min. Size'), - name: 'min_size', - value: 2, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'pveCephRuleSelector', - fieldLabel: 'Crush Rule', // do not localize - name: 'crush_rule', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'pg_num', - name: 'pg_num', - value: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add Storage'), - name: 'add_storages' - } - ], - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: "/nodes/" + me.nodename + "/ceph/pools", - defaults: { - nodename: me.nodename - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephPoolList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeCephPoolList', - - onlineHelp: 'chapter_pveceph', - stateful: true, - stateId: 'grid-ceph-pools', - bufferedRenderer: false, - features: [ { ftype: 'summary'} ], - columns: [ - { - header: gettext('Name'), - width: 100, - sortable: true, - dataIndex: 'pool_name' - }, - { - header: gettext('Size') + '/min', - width: 80, - sortable: false, - renderer: function(v, meta, rec) { - return v + '/' + rec.data.min_size; - }, - dataIndex: 'size' - }, - { - header: 'pg_num', - width: 100, - sortable: false, - dataIndex: 'pg_num' - }, - { - header: 'rule', - width: 50, - sortable: false, - dataIndex: 'crush_rule' - }, - { - header: 'rule_name', - width: 50, - sortable: false, - dataIndex: 'crush_rule_name' - }, - { - header: gettext('Used'), - columns: [ - { - header: '%', - width: 80, - sortable: true, - align: 'right', - renderer: Ext.util.Format.numberRenderer('0.00'), - dataIndex: 'percent_used', - summaryType: 'sum', - summaryRenderer: Ext.util.Format.numberRenderer('0.00') - }, - { - header: gettext('Total'), - width: 100, - sortable: true, - renderer: PVE.Utils.render_size, - align: 'right', - dataIndex: 'bytes_used', - summaryType: 'sum', - summaryRenderer: PVE.Utils.render_size - } - ] - } - ], - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'ceph-pool-list' + nodename, - model: 'ceph-pool-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/ceph/pools" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore }); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ - me.store.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.rstore.startUpdate(); - }); - } - ); - }); - - var create_btn = new Ext.Button({ - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.CephCreatePool', { - nodename: nodename - }); - win.show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - var destroy_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Destroy'), - selModel: sm, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - if (!rec.data.pool_name) { - return; - } - var base_url = '/nodes/' + nodename + '/ceph/pools/' + - rec.data.pool_name; - - var win = Ext.create('PVE.window.SafeDestroy', { - showProgress: true, - url: base_url, - params: { - remove_storages: 1 - }, - item: { type: 'CephPool', id: rec.data.pool_name } - }).show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ create_btn, destroy_btn ], - listeners: { - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('ceph-pool-list', { - extend: 'Ext.data.Model', - fields: [ 'pool_name', - { name: 'pool', type: 'integer'}, - { name: 'size', type: 'integer'}, - { name: 'min_size', type: 'integer'}, - { name: 'pg_num', type: 'integer'}, - { name: 'bytes_used', type: 'integer'}, - { name: 'percent_used', type: 'number'}, - { name: 'crush_rule', type: 'integer'}, - { name: 'crush_rule_name', type: 'string'} - ], - idProperty: 'pool_name' - }); -}); - -Ext.define('PVE.form.CephRuleSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephRuleSelector', - - allowBlank: false, - valueField: 'name', - displayField: 'name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/rules' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.CephCreateOsd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephCreateOsd'], - - subject: 'Ceph OSD', - - showProgress: true, - - onlineHelp: 'pve_ceph_osds', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'dev', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'pveDiskSelector', - name: 'journal_dev', - nodename: me.nodename, - diskType: 'journal_disks', - fieldLabel: gettext('Journal/DB Disk'), - value: '', - autoSelect: false, - allowBlank: true, - emptyText: 'use OSD disk' - }, - { - xtype: 'proxmoxcheckbox', - name: 'bluestore', - fieldLabel: 'Bluestore', - uncheckedValue: '0', - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.CephRemoveOsd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephRemoveOsd'], - - isRemove: true, - - showProgress: true, - method: 'DELETE', - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'cleanup', - checked: true, - labelWidth: 130, - fieldLabel: gettext('Remove Partitions') - } - ], - initComponent : function() { - - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - if (me.osdid === undefined || me.osdid < 0) { - throw "no osdid specified"; - } - - me.isCreate = true; - - me.title = gettext('Destroy') + ': Ceph OSD osd.' + me.osdid.toString(); - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd/" + me.osdid.toString() - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephOsdTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveNodeCephOsdTree'], - onlineHelp: 'chapter_pveceph', - stateful: true, - stateId: 'grid-ceph-osd', - columns: [ - { - xtype: 'treecolumn', - text: 'Name', - dataIndex: 'name', - width: 150 - }, - { - text: 'Type', - dataIndex: 'type', - align: 'right', - width: 60 - }, - { - text: gettext("Class"), - dataIndex: 'device_class', - align: 'right', - width: 40 - }, - { - text: "OSD Type", - dataIndex: 'osdtype', - align: 'right', - width: 40 - }, - { - text: "Bluestore Device", - dataIndex: 'blfsdev', - align: 'right', - width: 40, - hidden: true - }, - { - text: "DB Device", - dataIndex: 'dbdev', - align: 'right', - width: 40, - hidden: true - }, - { - text: "WAL Device", - dataIndex: 'waldev', - align: 'right', - renderer: function(value, metaData, rec) { - if (!value && - rec.data.osdtype === 'bluestore' && - rec.data.type === 'osd') { - return 'N/A'; - } - return value; - }, - width: 40, - hidden: true - }, - { - text: 'Status', - dataIndex: 'status', - align: 'right', - renderer: function(value, metaData, rec) { - if (!value) { - return value; - } - var inout = rec.data['in'] ? 'in' : 'out'; - var updownicon = value === 'up' ? 'good fa-arrow-circle-up' : - 'critical fa-arrow-circle-down'; - - var inouticon = rec.data['in'] ? 'good fa-circle' : - 'warning fa-circle-o'; - - var text = value + ' / ' + - inout + ' '; - - return text; - }, - width: 80 - }, - { - text: 'weight', - dataIndex: 'crush_weight', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return value; - }, - width: 80 - }, - { - text: 'reweight', - dataIndex: 'reweight', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return value; - }, - width: 90 - }, - { - header: gettext('Used'), - columns: [ - { - text: '%', - dataIndex: 'percent_used', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return Ext.util.Format.number(value, '0.00'); - }, - width: 80 - }, - { - text: gettext('Total'), - dataIndex: 'total_space', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return PVE.Utils.render_size(value); - }, - width: 100 - } - ] - }, - { - header: gettext('Latency (ms)'), - columns: [ - { - text: 'Apply', - dataIndex: 'apply_latency_ms', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return value; - }, - width: 60 - }, - { - text: 'Commit', - dataIndex: 'commit_latency_ms', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return value; - }, - width: 60 - } - ] - } - ], - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - // we expect noout to be not set by default - var noout = false; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - var set_button_status; // defined later - - var reload = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/ceph/osd", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - reload(); - }); - } - ); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data.root); - me.expandAll(); - // extract noout flag - if (response.result.data.flags && - response.result.data.flags.search(/noout/) !== -1) { - noout = true; - } else { - noout = false; - } - set_button_status(); - } - }); - }; - - var osd_cmd = function(cmd) { - var rec = sm.getSelection()[0]; - if (!(rec && (rec.data.id >= 0) && rec.data.host)) { - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/osd/" + - rec.data.id + '/' + cmd, - waitMsgTarget: me, - method: 'POST', - success: reload, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var service_cmd = function(cmd) { - var rec = sm.getSelection()[0]; - if (!(rec && rec.data.name && rec.data.host)) { - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/" + cmd, - params: { service: rec.data.name }, - waitMsgTarget: me, - method: 'POST', - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.mon(win, 'close', reload, me); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var create_btn = new Proxmox.button.Button({ - text: gettext('Create') + ': OSD', - handler: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.CephCreateOsd', { - nodename: nodename - }); - win.show(); - me.mon(win, 'close', reload, me); - } - }); - - var start_btn = new Ext.Button({ - text: gettext('Start'), - disabled: true, - handler: function(){ service_cmd('start'); } - }); - - var stop_btn = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: function(){ service_cmd('stop'); } - }); - - var restart_btn = new Ext.Button({ - text: gettext('Restart'), - disabled: true, - handler: function(){ service_cmd('restart'); } - }); - - var osd_out_btn = new Ext.Button({ - text: 'Out', - disabled: true, - handler: function(){ osd_cmd('out'); } - }); - - var osd_in_btn = new Ext.Button({ - text: 'In', - disabled: true, - handler: function(){ osd_cmd('in'); } - }); - - var remove_btn = new Ext.Button({ - text: gettext('Destroy'), - disabled: true, - handler: function(){ - var rec = sm.getSelection()[0]; - if (!(rec && (rec.data.id >= 0) && rec.data.host)) { - return; - } - - var win = Ext.create('PVE.CephRemoveOsd', { - nodename: rec.data.host, - osdid: rec.data.id - }); - win.show(); - me.mon(win, 'close', reload, me); - } - }); - - var noout_btn = new Ext.Button({ - text: gettext('Set noout'), - handler: function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/ceph/flags/noout", - waitMsgTarget: me, - method: noout ? 'DELETE' : 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: reload - }); - } - }); - - var osd_label = new Ext.toolbar.TextItem({ - data: { - osd: undefined - }, - tpl: [ - '', - '{osd}:', - '', - gettext('No OSD selected'), - '' - ] - }); - - set_button_status = function() { - var rec = sm.getSelection()[0]; - noout_btn.setText(noout?gettext('Unset noout'):gettext('Set noout')); - - if (!rec) { - start_btn.setDisabled(true); - stop_btn.setDisabled(true); - restart_btn.setDisabled(true); - remove_btn.setDisabled(true); - osd_out_btn.setDisabled(true); - osd_in_btn.setDisabled(true); - return; - } - - var isOsd = (rec.data.host && (rec.data.type === 'osd') && (rec.data.id >= 0)); - - start_btn.setDisabled(!(isOsd && (rec.data.status !== 'up'))); - stop_btn.setDisabled(!(isOsd && (rec.data.status !== 'down'))); - restart_btn.setDisabled(!(isOsd && (rec.data.status !== 'down'))); - remove_btn.setDisabled(!(isOsd && (rec.data.status === 'down'))); - - osd_out_btn.setDisabled(!(isOsd && rec.data['in'])); - osd_in_btn.setDisabled(!(isOsd && !rec.data['in'])); - - osd_label.update(isOsd?{osd:rec.data.name}:undefined); - }; - - sm.on('selectionchange', set_button_status); - - var reload_btn = new Ext.Button({ - text: gettext('Reload'), - handler: reload - }); - - Ext.apply(me, { - tbar: [ create_btn, reload_btn, noout_btn, '->', osd_label, start_btn, stop_btn, restart_btn, osd_out_btn, osd_in_btn, remove_btn ], - rootVisible: false, - useArrows: true, - fields: ['name', 'type', 'status', 'host', 'in', 'id' , - { type: 'number', name: 'reweight' }, - { type: 'number', name: 'percent_used' }, - { type: 'integer', name: 'bytes_used' }, - { type: 'integer', name: 'total_space' }, - { type: 'integer', name: 'apply_latency_ms' }, - { type: 'integer', name: 'commit_latency_ms' }, - { type: 'string', name: 'device_class' }, - { type: 'string', name: 'osdtype' }, - { type: 'string', name: 'blfsdev' }, - { type: 'string', name: 'dbdev' }, - { type: 'string', name: 'waldev' }, - { type: 'string', name: 'iconCls', calculate: function(data) { - var iconCls = 'fa x-fa-tree fa-'; - switch (data.type) { - case 'host': - iconCls += 'building'; - break; - case 'osd': - iconCls += 'hdd-o'; - break; - case 'root': - iconCls += 'server'; - break; - default: - return undefined; - } - return iconCls; - } }, - { type: 'number', name: 'crush_weight' }], - selModel: sm, - - listeners: { - activate: function() { - reload(); - } - } - }); - - me.callParent(); - - reload(); - } -}); -Ext.define('PVE.CephCreateMon', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephCreateMon'], - - subject: 'Ceph Monitor/Manager', - onlineHelp: 'pve_ceph_monitors', - - showProgress: true, - - setNode: function(nodename) { - var me = this; - - me.nodename = nodename; - me.url = "/nodes/" + nodename + "/ceph/mon"; - }, - - initComponent : function() { - - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.setNode(me.nodename); - - me.isCreate = true; - - Ext.applyIf(me, { - method: 'POST', - items: [ - { - xtype: 'pveNodeSelector', - submitValue: false, - fieldLabel: gettext('Host'), - selectCurNode: true, - allowBlank: false, - listeners: { - change: function(f, value) { - me.setNode(value); - } - } - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephMonList', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveNodeCephMonList'], - - onlineHelp: 'chapter_pveceph', - - stateful: true, - stateId: 'grid-ceph-monitor', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'ceph-mon-list' + nodename, - model: 'ceph-mon-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/ceph/mon" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - sorters: [{ property: 'name'}] - }); - - - var service_cmd = function(cmd) { - var rec = sm.getSelection()[0]; - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/" + cmd, - method: 'POST', - params: { service: "mon." + rec.data.name }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var start_btn = new Proxmox.button.Button({ - text: gettext('Start'), - selModel: sm, - disabled: true, - handler: function(){ - service_cmd("start"); - } - }); - - var stop_btn = new Proxmox.button.Button({ - text: gettext('Stop'), - selModel: sm, - disabled: true, - handler: function(){ - service_cmd("stop"); - } - }); - - var restart_btn = new Proxmox.button.Button({ - text: gettext('Restart'), - selModel: sm, - disabled: true, - handler: function(){ - service_cmd("restart"); - } - }); - - var create_btn = new Ext.Button({ - text: gettext('Create'), - handler: function(){ - var win = Ext.create('PVE.CephCreateMon', { - nodename: nodename - }); - win.show(); - } - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - selModel: sm, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/mon/" + - rec.data.name, - method: 'DELETE', - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ start_btn, stop_btn, restart_btn, '-', create_btn, remove_btn ], - columns: [ - { - header: gettext('Name'), - width: 100, - sortable: true, - renderer: function(v) { return "mon." + v; }, - dataIndex: 'name' - }, - { - header: gettext('Host'), - width: 100, - sortable: true, - renderer: function(v) { - return v || 'unknown'; - }, - dataIndex: 'host' - }, - { - header: gettext('Quorum'), - width: 70, - sortable: false, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'quorum' - }, - { - header: gettext('Address'), - flex: 1, - sortable: true, - dataIndex: 'addr' - } - ], - listeners: { - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ - me.store.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.rstore.startUpdate(); - }); - } - ); - }); - - me.callParent(); - } -}, function() { - - Ext.define('ceph-mon-list', { - extend: 'Ext.data.Model', - fields: [ 'addr', 'name', 'rank', 'host', 'quorum' ], - idProperty: 'name' - }); -}); -Ext.define('PVE.node.CephCrushMap', { - extend: 'Ext.panel.Panel', - alias: ['widget.pveNodeCephCrushMap'], - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - stateful: true, - stateId: 'layout-ceph-crush', - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/crush', - - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.node.CephStatus', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephStatus', - - onlineHelp: 'chapter_pveceph', - - scrollable: true, - - bodyPadding: 5, - - layout: { - type: 'column' - }, - - defaults: { - padding: 5 - }, - - items: [ - { - xtype: 'panel', - title: gettext('Health'), - bodyPadding: 10, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - }, - minHeight: 210, - layout: { - type: 'hbox', - align: 'stretch' - }, - items: [ - { - flex: 1, - itemId: 'overallhealth', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - flex: 2, - itemId: 'warnings', - stateful: true, - stateId: 'ceph-status-warnings', - xtype: 'grid', - // since we load the store manually, - // to show the emptytext, we have to - // specify an empty store - store: { data:[] }, - emptyText: gettext('No Warnings/Errors'), - columns: [ - { - dataIndex: 'severity', - header: gettext('Severity'), - align: 'center', - width: 70, - renderer: function(value) { - var health = PVE.Utils.map_ceph_health[value]; - var classes = PVE.Utils.get_health_icon(health); - - return ''; - }, - sorter: { - sorterFn: function(a,b) { - var healthArr = ['HEALTH_ERR', 'HEALTH_WARN', 'HEALTH_OK']; - return healthArr.indexOf(b.data.severity) - healthArr.indexOf(a.data.severity); - } - } - }, - { - dataIndex: 'summary', - header: gettext('Summary'), - flex: 1 - }, - { - xtype: 'actioncolumn', - width: 40, - align: 'center', - tooltip: gettext('Detail'), - items: [ - { - iconCls: 'x-fa fa-info-circle', - handler: function(grid, rowindex, colindex, item, e, record) { - var win = Ext.create('Ext.window.Window', { - title: gettext('Detail'), - resizable: true, - modal: true, - width: 650, - height: 400, - layout: { - type: 'fit' - }, - items: [{ - scrollable: true, - padding: 10, - xtype: 'box', - html: [ - '' + Ext.htmlEncode(record.data.summary) + '', - '
' + Ext.htmlEncode(record.data.detail) + '
' - ] - }] - }); - win.show(); - } - } - ] - } - ] - } - ] - }, - { - xtype: 'pveCephStatusDetail', - itemId: 'statusdetail', - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - }, - title: gettext('Status') - }, - { - xtype: 'panel', - title: gettext('Performance'), - columnWidth: 1, - bodyPadding: 5, - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - flex: 1, - xtype: 'proxmoxGauge', - itemId: 'space', - title: gettext('Usage') - }, - { - flex: 2, - xtype: 'container', - defaults: { - padding: 0, - height: 100 - }, - items: [ - { - itemId: 'reads', - xtype: 'pveRunningChart', - title: gettext('Reads'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'writes', - xtype: 'pveRunningChart', - title: gettext('Writes'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'iops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS', // do not localize - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'readiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Reads'), - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'writeiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Writes'), - renderer: Ext.util.Format.numberRenderer('0,000') - } - ] - } - ] - } - ], - - generateCheckData: function(health) { - var result = []; - var checks = health.checks || {}; - var keys = Ext.Object.getKeys(checks).sort(); - - Ext.Array.forEach(keys, function(key) { - var details = checks[key].detail || []; - result.push({ - id: key, - summary: checks[key].summary.message, - detail: Ext.Array.reduce( - checks[key].detail, - function(first, second) { - return first + '\n' + second.message; - }, - '' - ), - severity: checks[key].severity - }); - }); - - return result; - }, - - updateAll: function(store, records, success) { - if (!success || records.length === 0) { - return; - } - - var me = this; - var rec = records[0]; - - // add health panel - me.down('#overallhealth').updateHealth(PVE.Utils.render_ceph_health(rec.data.health || {})); - // add errors to gridstore - me.down('#warnings').getStore().loadRawData(me.generateCheckData(rec.data.health || {}), false); - - // update detailstatus panel - me.getComponent('statusdetail').updateAll( - rec.data.health || {}, - rec.data.monmap || {}, - rec.data.pgmap || {}, - rec.data.osdmap || {}, - rec.data.quorum_names || []); - - // add performance data - var used = rec.data.pgmap.bytes_used; - var total = rec.data.pgmap.bytes_total; - - var text = Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(used), - PVE.Utils.render_size(total) - ); - - // update the usage widget - me.down('#space').updateValue(used/total, text); - - // TODO: logic for jewel (iops splitted in read/write) - - var iops = rec.data.pgmap.op_per_sec; - var readiops = rec.data.pgmap.read_op_per_sec; - var writeiops = rec.data.pgmap.write_op_per_sec; - var reads = rec.data.pgmap.read_bytes_sec || 0; - var writes = rec.data.pgmap.write_bytes_sec || 0; - - if (iops !== undefined && me.version !== 'hammer') { - me.change_version('hammer'); - } else if((readiops !== undefined || writeiops !== undefined) && me.version !== 'jewel') { - me.change_version('jewel'); - } - // update the graphs - me.reads.addDataPoint(reads); - me.writes.addDataPoint(writes); - me.iops.addDataPoint(iops); - me.readiops.addDataPoint(readiops); - me.writeiops.addDataPoint(writeiops); - }, - - change_version: function(version) { - var me = this; - me.version = version; - me.sp.set('ceph-version', version); - me.iops.setVisible(version === 'hammer'); - me.readiops.setVisible(version === 'jewel'); - me.writeiops.setVisible(version === 'jewel'); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.callParent(); - me.store = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + nodename, - interval: 5000, - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/ceph/status' - } - }); - - // save references for the updatefunction - me.iops = me.down('#iops'); - me.readiops = me.down('#readiops'); - me.writeiops = me.down('#writeiops'); - me.reads = me.down('#reads'); - me.writes = me.down('#writes'); - - // get ceph version - me.sp = Ext.state.Manager.getProvider(); - me.version = me.sp.get('ceph-version'); - me.change_version(me.version); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, me.store, regex, function(me, error){ - me.store.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.startUpdate(); - }); - } - ); - }); - - me.mon(me.store, 'load', me.updateAll, me); - me.on('destroy', me.store.stopUpdate); - me.store.startUpdate(); - } - -}); -Ext.define('PVE.ceph.StatusDetail', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveCephStatusDetail', - - layout: { - type: 'hbox', - align: 'stretch' - }, - - bodyPadding: '0 5 20', - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [{ - flex: 1, - itemId: 'monitors', - xtype: 'container', - items: [ - { - xtype: 'box', - width: '100%', - html: '

' + gettext('Monitors') + '

' - } - ] - },{ - flex: 1, - itemId: 'osds', - data: { - total: 0, - upin: 0, - upout: 0, - downin: 0, - downout: 0 - }, - tpl: [ - '

' + 'OSDs' + '

', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '
', - gettext('In'), - '', - gettext('Out'), - '
', - gettext('Up'), - '{upin}{upout}
', - gettext('Down'), - '{downin}{downout}
', - '
', - gettext('Total'), - ': {total}', - '
' - ] - }, - { - flex: 1.6, - itemId: 'pgs', - padding: '0 10', - data: { - states: [] - }, - tpl: [ - '

' + 'PGs' + '

', - '', - '
{state_name}:
', - '
{count}

', - '
', - '
' - ] - }], - - updateAll: function(health, monmap, pgmap, osdmap, quorum_names) { - var me = this; - me.suspendLayout = true; - - // update pgs sorted - var pgs_by_state = pgmap.pgs_by_state || []; - pgs_by_state.sort(function(a,b){ - return (a.state_name < b.state_name)?-1:(a.state_name === b.state_name)?0:1; - }); - me.getComponent('pgs').update({states: pgs_by_state}); - - var downinregex = /(\d+) osds down/; - var monnameregex = /^mon.(\S+) /; - var downin_osds = 0; - var monmsgs = {}; - - // we collect monitor/osd information from the checks - Ext.Object.each(health.checks, function(key, value, obj) { - var found = null; - if (key === 'OSD_DOWN') { - found = value.summary.message.match(downinregex); - if (found !== null) { - downin_osds = parseInt(found[1],10); - } - } - else if (Ext.String.startsWith(key, 'MON_')) { - if (!value.detail) { - return; - } - found = value.detail[0].message.match(monnameregex); - if (found !== null) { - if (!monmsgs[found[1]]) { - monmsgs[found[1]] = []; - } - monmsgs[found[1]].push({ - text: Ext.Array.reduce(value.detail, function(first, second) { - return first + '\n' + second.message; - }, ''), - severity: value.severity - }); - } - } - }); - - // update osds counts - - var total_osds = osdmap.osdmap.num_osds || 0; - var in_osds = osdmap.osdmap.num_in_osds || 0; - var up_osds = osdmap.osdmap.num_up_osds || 0; - var out_osds = total_osds - in_osds; - var down_osds = total_osds - up_osds; - - var downout_osds = down_osds - downin_osds; - var upin_osds = in_osds - downin_osds; - var upout_osds = up_osds - upin_osds; - var osds = { - total: total_osds, - upin: upin_osds, - upout: upout_osds, - downin: downin_osds, - downout: downout_osds - }; - me.getComponent('osds').update(osds); - - // update the monitors - var mons = monmap.mons.sort(function(a,b) { - return (a.name < b.name)?-1:(a.name > b.name)?1:0; - }); - - var monContainer = me.getComponent('monitors'); - - var i; - for (i = 0; i < mons.length; i++) { - var monitor = monContainer.getComponent('mon.' + mons[i].name); - if (!monitor) { - // since mons are already sorted, and - // we always have a sorted list - // we can add it at the mons+1 position (because of the title) - monitor = monContainer.insert(i+1, { - xtype: 'pveCephMonitorWidget', - itemId: 'mon.' + mons[i].name - }); - } - monitor.updateMonitor(mons[i], monmsgs, quorum_names); - } - me.suspendLayout = false; - me.updateLayout(); - } -}); - -Ext.define('PVE.ceph.MonitorWidget', { - extend: 'Ext.Component', - alias: 'widget.pveCephMonitorWidget', - - userCls: 'monitor inline-block', - data: { - name: '0', - health: 'HEALTH_ERR', - text: '', - iconCls: PVE.Utils.get_health_icon(), - addr: '' - }, - - tpl: [ - '{name}: ', - '' - ], - - // expects 3 variables which are - // timestate: the status from timechecks.mons - // data: the monmap.mons data - // quorum_names: the quorum_names array - updateMonitor: function(data, monmsgs, quorum_names) { - var me = this; - var state = 'HEALTH_ERR'; - var text = ''; - var healthstates = { - 'HEALTH_OK': 3, - 'HEALTH_WARN': 2, - 'HEALTH_ERR': 1 - }; - - if (quorum_names && - quorum_names.indexOf(data.name) !== -1) { - state = 'HEALTH_OK'; - } - - if (monmsgs[data.name]) { - Ext.Array.forEach(monmsgs[data.name], function(msg) { - if (healthstates[msg.severity] < healthstates[state]) { - state = msg.severity; - } - - text += msg.text + "\n"; - }); - } - - me.update(Ext.apply(me.data, { - health: state, - text: text, - addr: data.addr, - name: data.name, - iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[state]) - })); - }, - - listeners: { - mouseenter: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (!me) { - return; - } - if (!me.tooltip) { - me.tooltip = Ext.create('Ext.tip.ToolTip', { - target: me.el, - trackMouse: true, - renderTo: Ext.getBody(), - html: gettext('Monitor') + ': ' + me.data.name + '
' + - gettext('Address') + ': ' + me.data.addr + '
' + - gettext('Health') + ': ' + me.data.health + '
' + - me.data.text - }); - } - me.tooltip.show(); - } - }, - mouseleave: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (me.tooltip) { - me.tooltip.destroy(); - delete me.tooltip; - } - } - } - } -}); -Ext.define('PVE.node.CephConfig', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfig', - - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/config', - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.CephConfigCrush', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfigCrush', - - onlineHelp: 'chapter_pveceph', - - layout: 'border', - items: [{ - title: gettext('Configuration'), - xtype: 'pveNodeCephConfig', - region: 'center' - }, - { - title: 'Crush Map', // do not localize - xtype: 'pveNodeCephCrushMap', - region: 'east', - split: true, - width: '50%' - }], - - initComponent: function() { - var me = this; - me.defaults = { - pveSelNode: me.pveSelNode - }; - me.callParent(); - } -}); -Ext.define('PVE.ceph.Log', { - extend: 'Proxmox.panel.LogView', - xtype: 'cephLogView', - nodename: undefined, - failCallback: function(response) { - var me = this; - var msg = response.htmlStatus; - var windowShow = PVE.Utils.showCephInstallOrMask(me, msg, me.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.loadTask.delay(200); - }); - } - ); - if (!windowShow) { - Proxmox.Utils.setErrorMask(me, msg); - } - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.CephInstallWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveCephInstallWizard', - mixins: ['Proxmox.Mixin.CBind'], - resizable: false, - nodename: undefined, - viewModel: { - data: { - nodename: '', - configuration: true, - isInstalled: false - } - }, - cbindData: { - nodename: undefined - }, - title: gettext('Setup'), - navigateNext: function() { - var tp = this.down('#wizcontent'); - var atab = tp.getActiveTab(); - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - }, - setInitialTab: function (index) { - var tp = this.down('#wizcontent'); - var initialTab = tp.items.getAt(index); - initialTab.enable(); - tp.setActiveTab(initialTab); - }, - onShow: function() { - this.callParent(arguments); - var isInstalled = this.getViewModel().get('isInstalled'); - if (isInstalled) { - this.getViewModel().set('configuration', false); - this.setInitialTab(2); - } - }, - items: [ - { - title: gettext('Info'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'chapter_pveceph', - html: '

Ceph?

'+ - '

"Ceph is a unified, distributed storage system designed for excellent performance, reliability and scalability."

'+ - '

Ceph is currently not installed on this node, click on the next button below to start the installation.'+ - ' This wizard will guide you through the necessary steps, after the initial installation you will be offered to create a initial configuration.'+ - ' The configuration step is only needed once per cluster and will be skipped if a config is already present.

'+ - '

Please take a look at our documentation, by clicking the help button below, before starting the installation, '+ - 'if you want to gain deeper knowledge about Ceph visit ceph.com.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#back').hide(true); - this.up('pveCephInstallWizard').down('#next').setText(gettext('Start installation')); - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#next').setText(gettext('Next')); - } - } - }, - { - title: gettext('Installation'), - xtype: 'panel', - layout: 'fit', - cbind:{ - nodename: '{nodename}' - }, - viewModel: {}, // needed to inherit parent viewModel data - listeners: { - afterrender: function() { - var me = this; - if (this.getViewModel().get('isInstalled')) { - this.mask("Ceph is already installed, click next to create your configuration.",['pve-static-mask']); - } else { - me.down('pveNoVncConsole').fireEvent('activate'); - } - }, - activate: function() { - var me = this; - var nodename = me.nodename; - me.updateStore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + nodename, - interval: 1000, - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/ceph/status' - }, - listeners: { - load: function(rec, response, success, operation) { - - if (success) { - me.updateStore.stopUpdate(); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("not initialized", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',false); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("rados_connect failed", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',true); - me.down('textfield').setValue('success'); - } else if (!operation.error.statusText.match("not installed", "i")) { - Proxmox.Utils.setErrorMask(me, operation.error.statusText); - } - } - } - }); - me.updateStore.startUpdate(); - }, - destroy: function() { - var me = this; - if (me.updateStore) { - me.updateStore.stopUpdate(); - } - } - }, - items: [ - { - itemId: 'jsconsole', - consoleType: 'cmd', - xtermjs: true, - xtype: 'pveNoVncConsole', - cbind:{ - nodename: '{nodename}' - }, - cmd: 'ceph_install' - }, - { - xtype: 'textfield', - name: 'installSuccess', - value: '', - allowBlank: false, - submitValue: false, - hidden: true - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Configuration'), - onlineHelp: 'chapter_pveceph', - cbind: { - nodename: '{nodename}' - }, - viewModel: { - data: { - replicas: undefined, - minreplicas: undefined - } - }, - listeners: { - activate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Next')); - }, - beforeshow: function() { - if (this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - this.mask("Coniguration already initialized",['pve-static-mask']); - } else { - this.unmask(); - } - }, - deactivate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Finish')); - } - }, - column1: [ - { - xtype: 'displayfield', - value: gettext('Ceph cluster configuration') + ':' - }, - { - xtype: 'textfield', - name: 'network', - vtype: 'IPCIDRAddress', - value: '', - fieldLabel: 'Public Network IP/CIDR', - bind: { - allowBlank: '{configuration}' - }, - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - this.validate(); - } - }, - { - xtype: 'textfield', - name: 'cluster-network', - vtype: 'IPCIDRAddress', - fieldLabel: 'Cluster Network IP/CIDR', - allowBlank: true, - emptyText: gettext('Same as Public Network') - } - // FIXME: add hint about cluster network and/or reference user to docs?? - ], - column2: [ - { - xtype: 'displayfield', - value: gettext('First Ceph monitor') + ':' - }, - { - xtype: 'pveNodeSelector', - fieldLabel: gettext('Monitor node'), - name: 'mon-node', - selectCurNode: true, - allowBlank: false - }, - { - xtype: 'displayfield', - value: gettext('Additional monitors are recommended. They can be created at any time in the Monitor tab.'), - userCls: 'pve-hint' - } - ], - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'size', - fieldLabel: 'Number of replicas', - bind: { - value: '{replicas}' - }, - maxValue: 7, - minValue: 2, - emptyText: '3' - }, - { - xtype: 'numberfield', - name: 'min_size', - fieldLabel: 'Minimum replicas', - bind: { - maxValue: '{replicas}', - value: '{minreplicas}' - }, - minValue: 2, - maxValue: 3, - setMaxValue: function(value) { - this.maxValue = Ext.Number.from(value, 2); - // allow enough to avoid split brains with max 'size', but more makes simply no sense - if (this.maxValue > 4) { - this.maxValue = 4; - } - this.toggleSpinners(); - this.validate(); - }, - emptyText: '2' - } - ], - onGetValues: function(values) { - ['cluster-network', 'size', 'min_size'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - return values; - }, - onSubmit: function() { - var me = this; - if (!this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - var wizard = me.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - var monNode = kv['mon-node']; - delete kv['mon-node']; - var nodename = me.nodename; - delete kv.nodename; - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/ceph/init', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + monNode + '/ceph/mon', - waitMsgTarget: wizard, - method: 'POST', - success: function() { - me.up('pveCephInstallWizard').navigateNext(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - - } else { - me.up('pveCephInstallWizard').navigateNext(); - } - } - }, - { - title: gettext('Success'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'pve_ceph_install', - html: '

Installation successful!

'+ - '

The basic installation and configuration is completed, depending on your setup some of the following steps are required to start using Ceph:

'+ - '
  1. Install Ceph on other nodes
  2. '+ - '
  3. Create additional Ceph Monitors
  4. '+ - '
  5. Create Ceph OSDs
  6. '+ - '
  7. Create Ceph Pools
'+ - '

To learn more click on the help button below.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - - var tp = this.up('#wizcontent'); - var idx = tp.items.indexOf(this)-1; - for(;idx >= 0;idx--) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - onSubmit: function() { - var wizard = this.up('pveCephInstallWizard'); - wizard.close(); - } - } - ] - }); -Ext.define('PVE.node.DiskList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeDiskList', - emptyText: gettext('No Disks found'), - stateful: true, - stateId: 'grid-node-disks', - columns: [ - { - header: gettext('Device'), - width: 100, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Type'), - width: 80, - sortable: true, - dataIndex: 'type', - renderer: function(v) { - if (v === 'ssd') { - return 'SSD'; - } else if (v === 'hdd') { - return 'Hard Disk'; - } else if (v === 'usb'){ - return 'USB'; - } else { - return gettext('Unknown'); - } - } - }, - { - header: gettext('Usage'), - width: 80, - sortable: false, - renderer: function(v, metaData, rec) { - if (rec) { - if (rec.data.osdid >= 0) { - var bluestore = ''; - if (rec.data.bluestore === 1) { - bluestore = ' (Bluestore)'; - } - return "Ceph osd." + rec.data.osdid.toString() + bluestore; - } - - var types = []; - if (rec.data.journals > 0) { - types.push('Journal'); - } - - if (rec.data.db > 0) { - types.push('DB'); - } - - if (rec.data.wal > 0) { - types.push('WAL'); - } - - if (types.length > 0) { - return 'Ceph (' + types.join(', ') + ')'; - } - } - - return v || Proxmox.Utils.noText; - }, - dataIndex: 'used' - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: 'GPT', - width: 60, - align: 'right', - renderer: function(value) { - if (value) { - return Proxmox.Utils.yesText; - } else { - return Proxmox.Utils.noText; - } - }, - dataIndex: 'gpt' - }, - { - header: gettext('Vendor'), - width: 100, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'vendor' - }, - { - header: gettext('Model'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'model' - }, - { - header: gettext('Serial'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'serial' - }, - { - header: 'S.M.A.R.T.', - width: 100, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'health' - }, - { - header: 'Wearout', - width: 100, - sortable: true, - dataIndex: 'wearout', - renderer: function(value) { - if (Ext.isNumeric(value)) { - return (100 - value).toString() + '%'; - } - return 'N/A'; - } - } - ], - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var store = Ext.create('Ext.data.Store', { - storeid: 'node-disk-list' + nodename, - model: 'node-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list" - }, - sorters: [ - { - property : 'dev', - direction: 'ASC' - } - ] - }); - - var reloadButton = Ext.create('Proxmox.button.Button', { - text: gettext('Reload'), - handler: function() { - me.store.load(); - } - }); - - var smartButton = Ext.create('Proxmox.button.Button', { - text: gettext('Show S.M.A.R.T. values'), - selModel: sm, - enableFn: function() { - return !!sm.getSelection().length; - }, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - }); - - var initButton = Ext.create('Proxmox.button.Button', { - text: gettext('Initialize Disk with GPT'), - selModel: sm, - enableFn: function() { - var selection = sm.getSelection(); - - if (!selection.length || selection[0].data.used) { - return false; - } else { - return true; - } - }, - disabled: true, - - handler: function() { - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + nodename + '/disks/initgpt', - waitMsgTarget: me, - method: 'POST', - params: { disk: rec.data.devpath}, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - } - }); - - me.loadCount = 1; // avoid duplicate loadmask - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ reloadButton, smartButton, initButton ], - listeners: { - itemdblclick: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - } - }); - - - me.callParent(); - me.store.load(); - } -}, function() { - - Ext.define('node-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial', 'rpm', 'type', 'health', 'wearout' ], - idProperty: 'devpath' - }); -}); - -Ext.define('PVE.DiskSmartWindow', { - extend: 'Ext.window.Window', - alias: 'widget.pveSmartWindow', - - modal: true, - - items: [ - { - xtype: 'gridpanel', - layout: { - type: 'fit' - }, - emptyText: gettext('No S.M.A.R.T. Values'), - scrollable: true, - flex: 1, - itemId: 'smarts', - reserveScrollbar: true, - columns: [ - { text: 'ID', dataIndex: 'id', width: 50 }, - { text: gettext('Attribute'), flex: 1, dataIndex: 'name', renderer: Ext.String.htmlEncode }, - { text: gettext('Value'), dataIndex: 'raw', renderer: Ext.String.htmlEncode }, - { text: gettext('Normalized'), dataIndex: 'value', width: 60}, - { text: gettext('Threshold'), dataIndex: 'threshold', width: 60}, - { text: gettext('Worst'), dataIndex: 'worst', width: 60}, - { text: gettext('Flags'), dataIndex: 'flags'}, - { text: gettext('Failing'), dataIndex: 'fail', renderer: Ext.String.htmlEncode } - ] - }, - { - xtype: 'component', - itemId: 'text', - layout: { - type: 'fit' - }, - hidden: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace' - } - } - ], - - buttons: [ - { - text: gettext('Reload'), - name: 'reload', - handler: function() { - var me = this; - me.up('window').store.reload(); - } - }, - { - text: gettext('Close'), - name: 'close', - handler: function() { - var me = this; - me.up('window').close(); - } - } - ], - - layout: { - type: 'vbox', - align: 'stretch' - }, - width: 800, - height: 500, - minWidth: 600, - minHeight: 400, - bodyPadding: 5, - title: gettext('S.M.A.R.T. Values'), - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var dev = me.dev; - if (!dev) { - throw "no device specified"; - } - - me.store = Ext.create('Ext.data.Store', { - model: 'disk-smart', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/smart?disk=" + dev - } - }); - - me.callParent(); - var grid = me.down('#smarts'); - var text = me.down('#text'); - - Proxmox.Utils.monStoreErrors(grid, me.store); - me.mon(me.store, 'load', function(s, records, success) { - if (success && records.length > 0) { - var rec = records[0]; - switch (rec.data.type) { - case 'text': - grid.setVisible(false); - text.setVisible(true); - text.setHtml(Ext.String.htmlEncode(rec.data.text)); - break; - default: - // includes 'ata' - // cannot use empty case because - // of jslint - grid.setVisible(true); - text.setVisible(false); - grid.setStore(rec.attributes()); - break; - } - } - }); - - me.store.load(); - } -}, function() { - - Ext.define('disk-smart', { - extend: 'Ext.data.Model', - fields: [ - { name:'health'}, - { name:'type'}, - { name:'text'} - ], - hasMany: {model: 'smart-attribute', name: 'attributes'} - }); - Ext.define('smart-attribute', { - extend: 'Ext.data.Model', - fields: [ - { name:'id', type:'number' }, 'name', 'value', 'worst', 'threshold', 'flags', 'fail', 'raw' - ] - }); -}); -Ext.define('PVE.node.CreateLVM', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVM', - - subject: 'LVM Volume Group', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvm", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMList', { - extend: 'Ext.tree.Panel', - xtype: 'pveLVMList', - emptyText: gettext('No Volume Groups found'), - stateful: true, - stateId: 'grid-node-lvm', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Number of LVs'), - dataIndex: 'lvcount', - width: 150, - align: 'right' - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Volume Group', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVM', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/lvm", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'size', 'free', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - txt += (data.leaf) ? 'hdd-o' : 'object-group'; - return txt; - } - }, - { - type: 'number', - name: 'usage', - calculate: function(data) { - return ((data.size-data.free)/data.size); - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.CreateLVMThin', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVMThin', - - subject: 'LVM Thinpool', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvmthin", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMThinList', { - extend: 'Ext.grid.Panel', - xtype: 'pveLVMThinList', - - emptyText: gettext('No thinpools found'), - stateful: true, - stateId: 'grid-node-lvmthin', - columns: [ - { - text: gettext('Name'), - dataIndex: 'lv', - flex: 1 - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'lv_size' - }, - { - header: gettext('Used'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'used' - }, - { - header: gettext('Metadata Usage'), - width: 120, - dataIndex: 'metadata_usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Metadata Size'), - width: 120, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_size' - }, - { - header: gettext('Metadata Used'), - width: 125, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_used' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Thinpool', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVMThin', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['lv', 'lv_size', 'used', 'metadata_size', 'metadata_used', - { - type: 'number', - name: 'usage', - calculate: function(data) { - return data.used/data.lv_size; - } - }, - { - type: 'number', - name: 'metadata_usage', - calculate: function(data) { - return data.metadata_used/data.metadata_size; - } - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/lvmthin' - }, - sorters: 'lv' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.CreateDirectory', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateDirectory', - - subject: Proxmox.Utils.directoryText, - - showProgress: true, - - onlineHelp: 'chapter_storage', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/directory", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['ext4', 'ext4'], - ['xfs', 'xfs'] - ], - fieldLabel: gettext('Filesystem'), - name: 'filesystem', - value: '', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.Directorylist', { - extend: 'Ext.grid.Panel', - xtype: 'pveDirectoryList', - - stateful: true, - stateId: 'grid-node-directory', - columns: [ - { - text: gettext('Path'), - dataIndex: 'path', - flex: 1 - }, - { - header: gettext('Device'), - flex: 1, - dataIndex: 'device' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'type' - }, - { - header: gettext('Options'), - width: 100, - dataIndex: 'options' - }, - { - header: gettext('Unit File'), - hidden: true, - dataIndex: 'unitfile' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Directory', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateDirectory', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['path', 'device', 'type', 'options', 'unitfile' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/directory' - }, - sorters: 'path' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -/*jslint confusion: true*/ -Ext.define('PVE.node.CreateZFS', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateZFS', - - subject: 'ZFS', - - showProgress: true, - - onlineHelp: 'chapter_zfs', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - var update_disklist = function() { - var grid = me.down('#disklist'); - var disks = grid.getSelection(); - - var val = []; - disks.sort(function(a,b) { - var aorder = a.get('order') || 0; - var border = b.get('order') || 0; - return (aorder - border); - }); - - disks.forEach(function(disk) { - val.push(disk.get('devpath')); - }); - - me.down('field[name=devices]').setValue(val.join(',')); - }; - - Ext.apply(me, { - url: '/nodes/' + me.nodename + '/disks/zfs', - method: 'POST', - items: [ - { - xtype: 'inputpanel', - onGetValues: function(values) { - return values; - }, - column1: [ - { - xtype: 'textfield', - hidden: true, - name: 'devices', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ], - column2: [ - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('RAID Level'), - name: 'raidlevel', - value: 'single', - comboItems: [ - ['single', gettext('Single Disk')], - ['mirror', 'Mirror'], - ['raid10', 'RAID10'], - ['raidz', 'RAIDZ'], - ['raidz2', 'RAIDZ2'], - ['raidz3', 'RAIDZ3'] - ] - }, - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Compression'), - name: 'compression', - value: 'on', - comboItems: [ - ['on', 'on'], - ['off', 'off'], - ['gzip', 'gzip'], - ['lz4', 'lz4'], - ['lzjb', 'lzjb'], - ['zle', 'zle'] - ] - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('ashift'), - minValue: 9, - maxValue: 16, - value: '12', - name: 'ashift' - } - ], - columnB: [ - { - xtype: 'grid', - height: 200, - emptyText: gettext('No Disks unused'), - itemId: 'disklist', - selModel: 'checkboxmodel', - listeners: { - selectionchange: update_disklist - }, - store: { - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/disks/list?type=unused' - } - }, - columns: [ - { - text: gettext('Device'), - dataIndex: 'devpath', - flex: 1 - }, - { - text: gettext('Serial'), - dataIndex: 'serial' - }, - { - text: gettext('Size'), - dataIndex: 'size', - renderer: PVE.Utils.render_size - }, - { - header: gettext('Order'), - xtype: 'widgetcolumn', - dataIndex: 'order', - sortable: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 1, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('order', value); - update_disklist(record); - } - } - } - } - ] - } - ] - } - ] - }); - - me.callParent(); - me.down('#disklist').getStore().load(); - } -}); - -Ext.define('PVE.node.ZFSDevices', { - extend: 'Ext.tree.Panel', - xtype: 'pveZFSDevices', - stateful: true, - stateId: 'grid-node-zfsstatus', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'state' - }, - { - text: 'READ', - dataIndex: 'read' - }, - { - text: 'WRITE', - dataIndex: 'write' - }, - { - text: 'CKSUM', - dataIndex: 'cksum' - }, - { - text: gettext('Message'), - dataIndex: 'msg' - } - ], - - rootVisible: true, - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/zfs/" + me.zpool, - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'status', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - if (data.leaf) { - return txt + 'hdd-o'; - } - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSStatus', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveZFSStatus', - layout: 'fit', - border: false, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - me.url = "/api2/extjs/nodes/" + me.nodename + "/disks/zfs/" + me.zpool; - - me.rows = { - scan: { - header: gettext('Scan') - }, - status: { - header: gettext('Status') - }, - action: { - header: gettext('Action') - }, - errors: { - header: gettext('Errors') - } - }; - - me.callParent(); - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSList', { - extend: 'Ext.grid.Panel', - xtype: 'pveZFSList', - - stateful: true, - stateId: 'grid-node-zfs', - columns: [ - { - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Size'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - }, - { - header: gettext('Allocated'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'alloc' - }, - { - header: gettext('Fragmentation'), - renderer: function(value) { - return value.toString() + '%'; - }, - dataIndex: 'frag' - }, - { - header: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'health' - }, - { - header: gettext('Deduplication'), - hidden: true, - renderer: function(value) { - return value.toFixed(2).toString() + 'x'; - }, - dataIndex: 'dedup' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': ZFS', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateZFS', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - }, - { - text: gettext('Detail'), - itemId: 'detailbtn', - disabled: true, - handler: function() { - var me = this.up('panel'); - var selection = me.getSelection(); - if (selection.length < 1) { - return; - } - me.show_detail(selection[0].get('name')); - } - } - ], - - show_detail: function(zpool) { - var me = this; - - var detailsgrid = Ext.create('PVE.node.ZFSStatus', { - layout: 'fit', - nodename: me.nodename, - flex: 0, - zpool: zpool - }); - - var devicetree = Ext.create('PVE.node.ZFSDevices', { - title: gettext('Devices'), - nodename: me.nodename, - flex: 1, - zpool: zpool - }); - - - var win = Ext.create('Ext.window.Window', { - modal: true, - width: 800, - height: 400, - resizable: true, - layout: 'fit', - title: gettext('Status') + ': ' + zpool, - items:[{ - xtype: 'panel', - region: 'center', - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [detailsgrid, devicetree], - tbar: [{ - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - - devicetree.reload(); - detailsgrid.reload(); - } - }] - }] - }).show(); - }, - - set_button_status: function() { - var me = this; - var selection = me.getSelection(); - me.down('#detailbtn').setDisabled(selection.length === 0); - }, - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - }, - selectionchange: function() { - this.set_button_status(); - }, - itemdblclick: function(grid, record) { - var me = this; - me.show_detail(record.get('name')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['name', 'size', 'free', 'alloc', 'dedup', 'frag', 'health'], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/zfs' - }, - sorters: 'name' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveNodeStatus', - - height: 300, - bodyPadding: '20 15 20 15', - - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 15 5 15' - }, - - items: [ - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpuinfo', - renderer: PVE.Utils.render_node_cpu_usage - }, - { - itemId: 'wait', - iconCls: 'fa fa-fw fa-clock-o', - title: gettext('IO delay'), - valueField: 'wait', - rowspan: 2 - }, - { - itemId: 'load', - iconCls: 'fa fa-fw fa-tasks', - title: gettext('Load average'), - printBar: false, - textField: 'loadavg' - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - itemId: 'memory', - title: gettext('RAM usage'), - valueField: 'memory', - maxField: 'memory', - renderer: PVE.Utils.render_node_size_usage - }, - { - itemId: 'ksm', - printBar: false, - title: gettext('KSM sharing'), - textField: 'ksm', - renderer: function(record) { - return PVE.Utils.render_size(record.shared); - }, - padding: '0 15 10 15' - }, - { - iconCls: 'fa fa-fw fa-hdd-o', - itemId: 'rootfs', - title: gettext('HD space') + '(root)', - valueField: 'rootfs', - maxField: 'rootfs', - renderer: PVE.Utils.render_node_size_usage - }, - { - iconCls: 'fa fa-fw fa-refresh', - itemId: 'swap', - printSize: true, - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'swap', - renderer: PVE.Utils.render_node_size_usage - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - itemId: 'cpus', - colspan: 2, - printBar: false, - title: gettext('CPU(s)'), - textField: 'cpuinfo', - renderer: function(cpuinfo) { - return cpuinfo.cpus + " x " + cpuinfo.model + " (" + - cpuinfo.sockets.toString() + " " + - (cpuinfo.sockets > 1 ? - gettext('Sockets') : - gettext('Socket') - ) + ")"; - }, - value: '' - }, - { - itemId: 'kversion', - colspan: 2, - title: gettext('Kernel Version'), - printBar: false, - textField: 'kversion', - value: '' - }, - { - itemId: 'version', - colspan: 2, - printBar: false, - title: gettext('PVE Manager Version'), - textField: 'pveversion', - value: '' - } - ], - - updateTitle: function() { - var me = this; - var uptime = Proxmox.Utils.render_uptime(me.getRecordValue('uptime')); - me.setTitle(me.pveSelNode.data.node + ' (' + gettext('Uptime') + ': ' + uptime + ')'); - } - -}); -Ext.define('PVE.node.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeSummary', - - scrollable: true, - bodyPadding: 5, - - showVersions: function() { - var me = this; - - // Note: we use simply text/html here, because ExtJS grid has problems - // with cut&paste - - var nodename = me.pveSelNode.data.node; - - var view = Ext.createWidget('component', { - autoScroll: true, - padding: 5, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Package versions'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + nodename + "/apt/versions", - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - var text = ''; - - Ext.Array.each(response.result.data, function(rec) { - var version = "not correctly installed"; - var pkg = rec.Package; - if (rec.OldVersion && rec.CurrentState === 'Installed') { - version = rec.OldVersion; - } - if (rec.RunningKernel) { - text += pkg + ': ' + version + ' (running kernel: ' + - rec.RunningKernel + ')\n'; - } else if (rec.ManagerVersion) { - text += pkg + ': ' + version + ' (running version: ' + - rec.ManagerVersion + ')\n'; - } else { - text += pkg + ': ' + version + '\n'; - } - }); - - view.update(Ext.htmlEncode(text)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var rstore = me.statusStore; - - var version_btn = new Ext.Button({ - text: gettext('Package versions'), - handler: function(){ - Proxmox.Utils.checked_command(function() { me.showVersions(); }); - } - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/rrddata", - model: 'pve-rrd-node' - }); - - Ext.apply(me, { - tbar: [version_btn, '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: 'column', - defaults: { - minHeight: 320, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: [ - { - xtype: 'pveNodeStatus', - rstore: rstore, - width: 770, - pveSelNode: me.pveSelNode - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - fields: ['cpu','iowait'], - fieldTitles: [gettext('CPU usage'), gettext('IO delay')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Server load'), - fields: ['loadavg'], - fieldTitles: [gettext('Load average')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - fields: ['memtotal','memused'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - fields: ['netin','netout'], - store: rrdstore - } - ] - } - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - } -}); -/*global Blob*/ -Ext.define('PVE.node.SubscriptionKeyEdit', { - extend: 'Proxmox.window.Edit', - title: gettext('Upload Subscription Key'), - width: 300, - items: { - xtype: 'textfield', - name: 'key', - value: '', - fieldLabel: gettext('Subscription Key') - }, - initComponent : function() { - var me = this; - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.Subscription', { - extend: 'Proxmox.grid.ObjectGrid', - - alias: ['widget.pveNodeSubscription'], - - onlineHelp: 'getting_help', - - viewConfig: { - enableTextSelection: true - }, - - showReport: function() { - var me = this; - var nodename = me.pveSelNode.data.node; - - var getReportFileName = function() { - var now = Ext.Date.format(new Date(), 'D-d-F-Y-G-i'); - return me.nodename + '-report-' + now + '.txt'; - }; - - var view = Ext.createWidget('component', { - itemId: 'system-report-view', - scrollable: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var reportWindow = Ext.create('Ext.window.Window', { - title: gettext('System Report'), - width: 1024, - height: 600, - layout: 'fit', - modal: true, - buttons: [ - '->', - { - text: gettext('Download'), - handler: function() { - var fileContent = reportWindow.getComponent('system-report-view').html; - var fileName = getReportFileName(); - - // Internet Explorer - if (window.navigator.msSaveOrOpenBlob) { - navigator.msSaveOrOpenBlob(new Blob([fileContent]), fileName); - } else { - var element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset=utf-8,' - + encodeURIComponent(fileContent)); - element.setAttribute('download', fileName); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - } - } - } - ], - items: view - }); - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + me.nodename + '/report', - method: 'GET', - waitMsgTarget: me, - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var report = Ext.htmlEncode(response.result.data); - reportWindow.show(); - view.update(report); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var reload = function() { - me.rstore.load(); - }; - - var baseurl = '/nodes/' + me.nodename + '/subscription'; - - var render_status = function(value) { - - var message = me.getObjectValue('message'); - - if (message) { - return value + ": " + message; - } - return value; - }; - - var rows = { - productname: { - header: gettext('Type') - }, - key: { - header: gettext('Subscription Key') - }, - status: { - header: gettext('Status'), - renderer: render_status - }, - message: { - visible: false - }, - serverid: { - header: gettext('Server ID') - }, - sockets: { - header: gettext('Sockets') - }, - checktime: { - header: gettext('Last checked'), - renderer: Proxmox.Utils.render_timestamp - }, - nextduedate: { - header: gettext('Next due date') - } - }; - - Ext.apply(me, { - url: '/api2/json' + baseurl, - cwidth1: 170, - tbar: [ - { - text: gettext('Upload Subscription Key'), - handler: function() { - var win = Ext.create('PVE.node.SubscriptionKeyEdit', { - url: '/api2/extjs/' + baseurl - }); - win.show(); - win.on('destroy', reload); - } - }, - { - text: gettext('Check'), - handler: function() { - Proxmox.Utils.API2Request({ - params: { force: 1 }, - url: baseurl, - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: reload - }); - } - }, - { - text: gettext('System Report'), - handler: function() { - Proxmox.Utils.checked_command(function (){ me.showReport(); }); - } - } - ], - rows: rows, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.node.CertificateView', { - extend: 'Ext.container.Container', - xtype: 'pveCertificatesView', - - onlineHelp: 'sysadmin_certificate_management', - - mixins: ['Proxmox.Mixin.CBind' ], - - items: [ - { - xtype: 'pveCertView', - border: 0, - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'pveACMEView', - border: 0, - cbind: { - nodename: '{nodename}' - } - } - ] - -}); - -Ext.define('PVE.node.CertificateViewer', { - extend: 'Proxmox.window.Edit', - - title: gettext('Certificate'), - - fieldDefaults: { - labelWidth: 120 - }, - width: 800, - resizable: true, - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Name'), - name: 'filename' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Fingerprint'), - name: 'fingerprint' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Issuer'), - name: 'issuer' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject'), - name: 'subject' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Valid Since'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notbefore' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Expires'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notafter' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject Alternative Names'), - name: 'san', - renderer: PVE.Utils.render_san - }, - { - xtype: 'textarea', - editable: false, - grow: true, - growMax: 200, - fieldLabel: gettext('Certificate'), - name: 'pem' - } - ], - - initComponent: function() { - var me = this; - - if (!me.cert) { - throw "no cert given"; - } - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/info'; - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - if (Ext.isArray(response.result.data)) { - Ext.Array.each(response.result.data, function(item) { - if (item.filename === me.cert) { - me.setValues(item); - return false; - } - }); - } - } - }); - } -}); - -Ext.define('PVE.node.CertUpload', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCertUpload', - - title: gettext('Upload Custom Certificate'), - resizable: false, - isCreate: true, - submitText: gettext('Upload'), - method: 'POST', - width: 600, - - apiCallDone: function(success, response, options) { - if (!success) { - return; - } - - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - items: [ - { - fieldLabel: gettext('Private Key (Optional)'), - labelAlign: 'top', - emptyText: gettext('No change'), - name: 'key', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=key]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'box', - autoEl: 'hr' - }, - { - fieldLabel: gettext('Certificate Chain'), - labelAlign: 'top', - allowBlank: false, - name: 'certificates', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=certificates]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'hidden', - name: 'restart', - value: '1' - }, - { - xtype: 'hidden', - name: 'force', - value: '1' - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/custom'; - - me.callParent(); - } -}); - -Ext.define('pve-certificate', { - extend: 'Ext.data.Model', - - fields: [ 'filename', 'fingerprint', 'issuer', 'notafter', 'notbefore', 'subject', 'san' ], - idProperty: 'filename' -}); - -Ext.define('PVE.node.Certificates', { - extend: 'Ext.grid.Panel', - xtype: 'pveCertView', - - tbar: [ - { - xtype: 'button', - text: gettext('Upload Custom Certificate'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.CertUpload', { - nodename: me.nodename - }); - win.show(); - win.on('destroy', me.reload, me); - } - }, - { - xtype: 'button', - itemId: 'deletebtn', - text: gettext('Delete Custom Certificate'), - handler: function() { - var me = this.up('grid'); - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/certificates/custom?restart=1', - method: 'DELETE', - success: function(response, opt) { - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - '-', - { - xtype: 'proxmoxButton', - itemId: 'viewbtn', - disabled: true, - text: gettext('View Certificate'), - handler: function() { - var me = this.up('grid'); - me.view_certificate(); - } - } - ], - - columns: [ - { - header: gettext('File'), - width: 150, - dataIndex: 'filename' - }, - { - header: gettext('Issuer'), - flex: 1, - dataIndex: 'issuer' - }, - { - header: gettext('Subject'), - flex: 1, - dataIndex: 'subject' - }, - { - header: gettext('Valid Since'), - width: 150, - dataIndex: 'notbefore', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Expires'), - width: 150, - dataIndex: 'notafter', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Subject Alternative Names'), - flex: 1, - dataIndex: 'san', - renderer: PVE.Utils.render_san - }, - { - header: gettext('Fingerprint'), - dataIndex: 'fingerprint', - hidden: true - }, - { - header: gettext('PEM'), - dataIndex: 'pem', - hidden: true - } - ], - - reload: function() { - var me = this; - me.rstore.load(); - }, - - set_button_status: function() { - var me = this; - var rec = me.rstore.getById('pveproxy-ssl.pem'); - - me.down('#deletebtn').setDisabled(!rec); - }, - - view_certificate: function() { - var me = this; - var selection = me.getSelection(); - if (!selection || selection.length < 1) { - return; - } - var win = Ext.create('PVE.node.CertificateViewer', { - cert: selection[0].data.filename, - nodename : me.nodename - }); - win.show(); - }, - - listeners: { - itemdblclick: 'view_certificate' - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'certs-' + me.nodename, - model: 'pve-certificate', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/certificates/info' - } - }); - - me.store = { - type: 'diff', - rstore: me.rstore - }; - - me.callParent(); - - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - } -}); -Ext.define('PVE.node.ACMEEditor', { - extend: 'Proxmox.window.Edit', - xtype: 'pveACMEEditor', - - subject: gettext('Domains'), - items: [ - { - xtype: 'inputpanel', - items: [ - { - xtype: 'textarea', - fieldLabel: gettext('Domains'), - emptyText: "domain1.example.com\ndomain2.example.com", - name: 'domains' - } - ], - onGetValues: function(values) { - if (!values.domains) { - return { - 'delete': 'acme' - }; - } - var domains = values.domains.split(/\n/).join(';'); - return { - 'acme': 'domains=' + domains - }; - } - } - ], - - initComponent: function() { - var me = this; - me.callParent(); - - me.load({ - success: function(response, opts) { - var res = PVE.Parser.parseACME(response.result.data.acme); - if (res) { - res.domains = res.domains.join(' '); - me.setValues(res); - } - } - }); - } -}); - -Ext.define('PVE.node.ACMEAccountCreate', { - extend: 'Proxmox.window.Edit', - - width: 400, - title: gettext('Register Account'), - isCreate: true, - method: 'POST', - submitText: gettext('Register'), - url: '/cluster/acme/account', - showTaskViewer: true, - - items: [ - { - xtype: 'proxmoxComboGrid', - name: 'directory', - allowBlank: false, - valueField: 'url', - displayField: 'name', - fieldLabel: gettext('ACME Directory'), - store: { - autoLoad: true, - fields: ['name', 'url'], - idProperty: ['name'], - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/acme/directories' - }, - sorters: { - property: 'name', - order: 'ASC' - } - }, - listConfig: { - columns: [ - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('URL'), - dataIndex: 'url', - flex: 1 - } - ] - }, - listeners: { - change: function(combogrid, value) { - var me = this; - if (!value) { - return; - } - - var disp = me.up('window').down('#tos_url_display'); - var field = me.up('window').down('#tos_url'); - var checkbox = me.up('window').down('#tos_checkbox'); - - disp.setValue(gettext('Loading')); - field.setValue(undefined); - checkbox.setValue(undefined); - - Proxmox.Utils.API2Request({ - url: '/cluster/acme/tos', - method: 'GET', - params: { - directory: value - }, - success: function(response, opt) { - me.up('window').down('#tos_url').setValue(response.result.data); - me.up('window').down('#tos_url_display').setValue(response.result.data); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - }, - { - xtype: 'displayfield', - itemId: 'tos_url_display', - fieldLabel: gettext('Terms of Service'), - renderer: PVE.Utils.render_optional_url, - name: 'tos_url_display' - }, - { - xtype: 'hidden', - itemId: 'tos_url', - name: 'tos_url' - }, - { - xtype: 'proxmoxcheckbox', - itemId: 'tos_checkbox', - fieldLabel: gettext('Accept TOS'), - submitValue: false, - validateValue: function(value) { - if (value && this.checked) { - return true; - } - return false; - } - }, - { - xtype: 'textfield', - name: 'contact', - vtype: 'email', - allowBlank: false, - fieldLabel: gettext('E-Mail') - } - ] - -}); - -Ext.define('PVE.node.ACMEAccountView', { - extend: 'Proxmox.window.Edit', - - width: 600, - fieldDefaults: { - labelWidth: 140 - }, - - title: gettext('Account'), - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('E-Mail'), - name: 'email' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Created'), - name: 'createdAt' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Status'), - name: 'status' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Directory'), - renderer: PVE.Utils.render_optional_url, - name: 'directory' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Terms of Services'), - renderer: PVE.Utils.render_optional_url, - name: 'tos' - } - ], - - initComponent: function() { - var me = this; - - if (!me.accountname) { - throw "no account name defined"; - } - - me.url = '/cluster/acme/account/' + me.accountname; - - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - var data = response.result.data; - data.email = data.account.contact[0]; - data.createdAt = data.account.createdAt; - data.status = data.account.status; - me.setValues(data); - } - }); - } -}); - -Ext.define('PVE.node.ACME', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveACMEView', - - margin: '10 0 0 0', - title: 'ACME', - - tbar: [ - { - xtype: 'button', - itemId: 'edit', - text: gettext('Edit Domains'), - handler: function() { - this.up('grid').run_editor(); - } - }, - { - xtype: 'button', - itemId: 'createaccount', - text: gettext('Register Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountCreate', { - taskDone: function() { - me.load_account(); - me.reload(); - } - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'viewaccount', - text: gettext('View Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountView', { - accountname: 'default' - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'order', - text: gettext('Order Certificate'), - handler: function() { - var me = this.up('grid'); - - Proxmox.Utils.API2Request({ - method: 'POST', - params: { - force: 1 - }, - url: '/nodes/' + me.nodename + '/certificates/acme/certificate', - success: function(response, opt) { - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: response.result.data, - taskDone: function(success) { - me.certificate_order_finished(success); - } - }); - win.show(); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ], - - certificate_order_finished: function(success) { - if (!success) { - return; - } - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - set_button_status: function() { - var me = this; - - var account = !!me.account; - var acmeObj = PVE.Parser.parseACME(me.getObjectValue('acme')); - var domains = acmeObj ? acmeObj.domains.length : 0; - - var order = me.down('#order'); - order.setVisible(account); - order.setDisabled(!account || !domains); - - me.down('#createaccount').setVisible(!account); - me.down('#viewaccount').setVisible(account); - }, - - load_account: function() { - var me = this; - - // for now we only use the 'default' account - Proxmox.Utils.API2Request({ - url: '/cluster/acme/account/default', - success: function(response, opt) { - me.account = response.result.data; - me.set_button_status(); - }, - failure: function(response, opt) { - me.account = undefined; - me.set_button_status(); - } - }); - }, - - run_editor: function() { - var me = this; - var win = Ext.create(me.rows.acme.editor, me.editorConfig); - win.show(); - win.on('destroy', me.reload, me); - }, - - listeners: { - itemdblclick: 'run_editor' - }, - - // account data gets loaded here - account: undefined, - - disableSelection: true, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/api2/json/nodes/' + me.nodename + '/config'; - - me.editorConfig = { - url: '/api2/extjs/nodes/' + me.nodename + '/config' - }; - /*jslint confusion: true*/ - /*acme is a string above*/ - me.rows = { - acme: { - defaultValue: '', - header: gettext('Domains'), - editor: 'PVE.node.ACMEEditor', - renderer: function(value) { - var acmeObj = PVE.Parser.parseACME(value); - if (acmeObj) { - return acmeObj.domains.join('
'); - } - return Proxmox.Utils.noneText; - } - } - }; - /*jslint confusion: false*/ - - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - me.load_account(); - } -}); -Ext.define('PVE.node.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.node.Config', - - onlineHelp: 'chapter_system_administration', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/status", - interval: 1000 - }); - - var node_command = function(cmd) { - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/status', - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var actionBtn = Ext.create('Ext.Button', { - text: gettext('Bulk Actions'), - iconCls: 'fa fa-fw fa-ellipsis-v', - disabled: !caps.nodes['Sys.PowerMgmt'], - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Bulk Start'), - iconCls: 'fa fa-fw fa-play', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - } - ] - }) - }); - - var restartBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Reboot'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Reboot node '{0}'?"), nodename), - handler: function() { - node_command('reboot'); - }, - iconCls: 'fa fa-undo' - }); - - var shutdownBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Shutdown'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Shutdown node '{0}'?"), nodename), - handler: function() { - node_command('shutdown'); - }, - iconCls: 'fa fa-power-off' - }); - - var shellBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.nodes['Sys.Console'], - text: gettext('Shell'), - consoleType: 'shell', - nodename: nodename - }); - - me.items = []; - - Ext.apply(me, { - title: gettext('Node') + " '" + nodename + "'", - hstateid: 'nodetab', - defaults: { statusStore: me.statusStore }, - tbar: [ restartBtn, shutdownBtn, shellBtn, actionBtn] - }); - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - itemId: 'summary', - xtype: 'pveNodeSummary' - }, - { - title: gettext('Notes'), - iconCls: 'fa fa-sticky-note-o', - itemId: 'notes', - xtype: 'pveNotesView' - } - ); - } - - if (caps.nodes['Sys.Console']) { - me.items.push( - { - title: gettext('Shell'), - iconCls: 'fa fa-terminal', - itemId: 'jsconsole', - xtype: 'pveNoVncConsole', - consoleType: 'shell', - xtermjs: true, - nodename: nodename - } - ); - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('System'), - iconCls: 'fa fa-cogs', - itemId: 'services', - expandedOnInit: true, - startOnlyServices: { - 'pveproxy': true, - 'pvedaemon': true, - 'pve-cluster': true - }, - nodename: nodename, - onlineHelp: 'pve_service_daemons', - xtype: 'proxmoxNodeServiceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - groups: ['services'], - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeNetworkView' - }, - { - title: gettext('Certificates'), - iconCls: 'fa fa-certificate', - itemId: 'certificates', - groups: ['services'], - nodename: nodename, - xtype: 'pveCertificatesView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'dns', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeDNSView' - }, - { - title: gettext('Hosts'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'hosts', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeHostsView' - }, - { - title: gettext('Time'), - itemId: 'time', - groups: ['services'], - nodename: nodename, - xtype: 'proxmoxNodeTimeView', - iconCls: 'fa fa-clock-o' - }); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push({ - title: 'Syslog', - iconCls: 'fa fa-list', - groups: ['services'], - disabled: !caps.nodes['Sys.Syslog'], - itemId: 'syslog', - xtype: 'proxmoxLogView', - url: "/api2/extjs/nodes/" + nodename + "/syslog", - log_select_timespan: 1 - }); - - if (caps.nodes['Sys.Modify']) { - me.items.push({ - title: gettext('Updates'), - iconCls: 'fa fa-refresh', - disabled: !caps.nodes['Sys.Console'], - // do we want to link to system updates instead? - itemId: 'apt', - xtype: 'proxmoxNodeAPT', - upgradeBtn: { - xtype: 'pveConsoleButton', - disabled: Proxmox.UserName !== 'root@pam', - text: gettext('Upgrade'), - consoleType: 'upgrade', - nodename: nodename - }, - nodename: nodename - }); - } - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - xtype: 'pveFirewallRules', - iconCls: 'fa fa-shield', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/nodes/' + nodename + '/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_host_specific_configuration', - groups: ['firewall'], - base_url: '/nodes/' + nodename + '/firewall/options', - fwtype: 'node', - itemId: 'firewall-options' - }); - } - - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Disks'), - itemId: 'storage', - expandedOnInit: true, - iconCls: 'fa fa-hdd-o', - xtype: 'pveNodeDiskList' - }, - { - title: 'LVM', - itemId: 'lvm', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square', - groups: ['storage'], - xtype: 'pveLVMList' - }, - { - title: 'LVM-Thin', - itemId: 'lvmthin', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square-o', - groups: ['storage'], - xtype: 'pveLVMThinList' - }, - { - title: Proxmox.Utils.directoryText, - itemId: 'directory', - onlineHelp: 'chapter_storage', - iconCls: 'fa fa-folder', - groups: ['storage'], - xtype: 'pveDirectoryList' - }, - { - title: 'ZFS', - itemId: 'zfs', - onlineHelp: 'chapter_zfs', - iconCls: 'fa fa-th-large', - groups: ['storage'], - xtype: 'pveZFSList' - }, - { - title: 'Ceph', - itemId: 'ceph', - iconCls: 'fa fa-ceph', - xtype: 'pveNodeCephStatus' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveNodeCephConfigCrush', - title: gettext('Configuration'), - iconCls: 'fa fa-gear', - groups: ['ceph'], - itemId: 'ceph-config' - }, - { - xtype: 'pveNodeCephMonList', - title: gettext('Monitor'), - iconCls: 'fa fa-tv', - groups: ['ceph'], - itemId: 'ceph-monlist' - }, - { - xtype: 'pveNodeCephOsdTree', - title: 'OSD', - iconCls: 'fa fa-hdd-o', - groups: ['ceph'], - itemId: 'ceph-osdtree' - }, - { - xtype: 'pveNodeCephFSPanel', - title: 'CephFS', - iconCls: 'fa fa-folder', - groups: ['ceph'], - nodename: nodename, - itemId: 'ceph-cephfspanel' - }, - { - xtype: 'pveNodeCephPoolList', - title: 'Pools', - iconCls: 'fa fa-sitemap', - groups: ['ceph'], - itemId: 'ceph-pools' - } - ); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push( - { - xtype: 'proxmoxLogView', - title: gettext('Log'), - iconCls: 'fa fa-list', - groups: ['firewall'], - onlineHelp: 'chapter_pve_firewall', - url: '/api2/extjs/nodes/' + nodename + '/firewall/log', - itemId: 'firewall-fwlog' - }, - { - title: gettext('Log'), - itemId: 'ceph-log', - iconCls: 'fa fa-list', - groups: ['ceph'], - onlineHelp: 'chapter_pveceph', - xtype: 'cephLogView', - url: "/api2/extjs/nodes/" + nodename + "/ceph/log", - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Task History'), - iconCls: 'fa fa-list', - itemId: 'tasks', - nodename: nodename, - xtype: 'proxmoxNodeTasks' - }, - { - title: gettext('Subscription'), - iconCls: 'fa fa-support', - itemId: 'support', - xtype: 'pveNodeSubscription', - nodename: nodename - } - ); - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var uptimerec = s.data.get('uptime'); - var powermgmt = uptimerec ? uptimerec.data.value : false; - if (!caps.nodes['Sys.PowerMgmt']) { - powermgmt = false; - } - restartBtn.setDisabled(!powermgmt); - shutdownBtn.setDisabled(!powermgmt); - shellBtn.setDisabled(!powermgmt); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -Ext.define('PVE.window.Migrate', { - extend: 'Ext.window.Window', - - config: { - vmtype: undefined, - nodename: undefined, - vmid: undefined - }, - // private, used to store the migration mode after checking if the guest runs - liveMode: undefined, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=formPanel]': { - validityChange: function(panel, isValid) { - this.lookup('submitButton').setDisabled(!isValid); - } - }, - 'button[reference=submitButton]': { - click: function() { - var me = this; - var view = me.getView(); - - var values = me.lookup('formPanel').getValues(); - var params = { - target: values.target - }; - - if (view.liveMode) { - params[view.liveMode] = 1; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + view.nodename + '/' + view.vmtype + '/' + view.vmid + '/migrate', - waitMsgTarget: view, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var extraTitle = Ext.String.format(' ({0} ---> {1})', view.nodename, params.target); - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - extraTitle: extraTitle - }).show(); - - view.close(); - } - }); - } - } - } - }, - - width: 350, - modal: true, - layout: 'auto', - border: false, - resizable: false, - items: [ - { - xtype: 'form', - reference: 'formPanel', - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - xtype: 'pveNodeSelector', - reference: 'pveNodeSelector', - name: 'target', - fieldLabel: gettext('Target node'), - allowBlank: false, - disallowedNodes: undefined, - onlineValidator: true - }, - { - xtype: 'displayfield', - reference: 'migrationMode', - fieldLabel: gettext('Mode'), - value: gettext('Offline') - } - ] - } - ], - buttons: [ - { - xtype: 'proxmoxHelpButton', - reference: 'proxmoxHelpButton', - onlineHelp: 'pct_migration', - listenToGlobalEvent: false, - hidden: false - }, - '->', - { - xtype: 'button', - reference: 'submitButton', - text: gettext('Migrate') - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.vmtype) { - throw "no VM type specified"; - } - - me.callParent(); - - var title = gettext('Migrate') + (' CT ') + me.vmid; - me.liveMode = 'restart'; - - if (me.vmtype === 'qemu') { - me.lookup('proxmoxHelpButton').setHelpConfig({ - onlineHelp: 'qm_migration' - }); - title = gettext('Migrate') + (' VM ') + me.vmid; - me.liveMode = 'online'; - } - - var running = false; - var vmrec = PVE.data.ResourceStore.findRecord('vmid', me.vmid, - 0, false, false, true); - if (vmrec && vmrec.data && vmrec.data.running) { - running = true; - } - - if (running) { - var displayField = me.lookup('migrationMode'); - if (me.vmtype === 'qemu') { - displayField.setValue(gettext('Online')); - me.liveMode = 'online'; - } else { - displayField.setValue(gettext('Restart Mode')); - me.liveMode = 'restart'; - } - } - - me.setTitle(title); - me.lookup('pveNodeSelector').disallowedNodes = [me.nodename]; - me.lookup('formPanel').isValid(); - } -});Ext.define('PVE.window.BulkAction', { - extend: 'Ext.window.Window', - - resizable: true, - width: 800, - modal: true, - layout: { - type: 'fit' - }, - border: false, - - // the action to be set - // currently there are - // startall - // migrateall - // stopall - action: undefined, - - submit: function(params) { - var me = this; - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.action, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.hide(); - win.on('destroy', function() { - me.close(); - }); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.action) { - throw "no action specified"; - } - - if (!me.btnText) { - throw "no button text specified"; - } - - if (!me.title) { - throw "no title specified"; - } - - var items = []; - - if (me.action === 'migrateall') { - /*jslint confusion: true*/ - /*value is string and number*/ - items.push( - { - xtype: 'pveNodeSelector', - name: 'target', - disallowedNodes: [me.nodename], - fieldLabel: gettext('Target node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'maxworkers', - minValue: 1, - maxValue: 100, - value: 1, - fieldLabel: gettext('Parallel jobs'), - allowBlank: false - }, - { - itemId: 'lxcwarning', - xtype: 'displayfield', - userCls: 'pve-hint', - value: 'Warning: Running CTs will be migrated in Restart Mode.', - hidden: true // only visible if running container chosen - } - ); - /*jslint confusion: false*/ - } else if (me.action === 'startall') { - items.push({ - xtype: 'hiddenfield', - name: 'force', - value: 1 - }); - } - - items.push({ - xtype: 'vmselector', - itemId: 'vms', - name: 'vms', - flex: 1, - height: 300, - selectAll: true, - allowBlank: false, - nodename: me.nodename, - action: me.action, - listeners: { - selectionchange: function(vmselector, records) { - if (me.action == 'migrateall') { - var showWarning = records.some(function(item) { - return (item.data.type == 'lxc' && - item.data.status == 'running'); - }); - me.down('#lxcwarning').setVisible(showWarning); - } - } - } - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - layout: { - type: 'vbox', - align: 'stretch' - }, - fieldDefaults: { - labelWidth: 300, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: me.btnText, - handler: function() { - form.isValid(); - me.submit(form.getValues()); - } - }); - - Ext.apply(me, { - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - - form.on('validitychange', function() { - var valid = form.isValid(); - submitBtn.setDisabled(!valid); - }); - form.isValid(); - } -}); -Ext.define('PVE.window.Clone', { - extend: 'Ext.window.Window', - - resizable: false, - - isTemplate: false, - - onlineHelp: 'qm_copy_and_clone', - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=cloneform]': { - validitychange: 'disableSubmit' - } - }, - disableSubmit: function(form) { - this.lookupReference('submitBtn').setDisabled(!form.isValid()); - } - }, - - statics: { - // display a snapshot selector only if needed - wrap: function(nodename, vmid, isTemplate, guestType) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid +'/snapshot', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var snapshotList = response.result.data; - var hasSnapshots = snapshotList.length === 1 && - snapshotList[0].name === 'current' ? false : true; - - Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: isTemplate, - hasSnapshots: hasSnapshots - }).show(); - } - }); - } - }, - - create_clone: function(values) { - var me = this; - - var params = { newid: values.newvmid }; - - if (values.snapname && values.snapname !== 'current') { - params.snapname = values.snapname; - } - - if (values.pool) { - params.pool = values.pool; - } - - if (values.name) { - if (me.guestType === 'lxc') { - params.hostname = values.name; - } else { - params.name = values.name; - } - } - - if (values.target) { - params.target = values.target; - } - - if (values.clonemode === 'copy') { - params.full = 1; - if (values.hdstorage) { - params.storage = values.hdstorage; - if (values.diskformat && me.guestType !== 'lxc') { - params.format = values.diskformat; - } - } - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/clone', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - - }, - - // disable the Storage selector when clone mode is linked clone - updateVisibility: function() { - var me = this; - var clonemode = me.lookupReference('clonemodesel').getValue(); - var disksel = me.lookup('diskselector'); - disksel.setDisabled(clonemode === 'clone'); - }, - - // add to the list of valid nodes each node where - // all the VM disks are available - verifyFeature: function() { - var me = this; - - var snapname = me.lookupReference('snapshotsel').getValue(); - var clonemode = me.lookupReference('clonemodesel').getValue(); - - var params = { feature: clonemode }; - if (snapname !== 'current') { - params.snapname = snapname; - } - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/feature', - params: params, - method: 'GET', - failure: function(response, opts) { - me.lookupReference('submitBtn').setDisabled(true); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var res = response.result.data; - - me.lookupReference('targetsel').allowedNodes = res.nodes; - me.lookupReference('targetsel').validate(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.snapname) { - me.snapname = 'current'; - } - - if (!me.guestType) { - throw "no Guest Type specified"; - } - - var titletext = me.guestType === 'lxc' ? 'CT' : 'VM'; - if (me.isTemplate) { - titletext += ' Template'; - } - me.title = "Clone " + titletext + " " + me.vmid; - - var col1 = []; - var col2 = []; - - col1.push({ - xtype: 'pveNodeSelector', - name: 'target', - reference: 'targetsel', - fieldLabel: gettext('Target node'), - selectCurNode: true, - allowBlank: false, - onlineValidator: true, - listeners: { - change: function(f, value) { - me.lookupReference('hdstorage').setTargetNode(value); - } - } - }); - - var modelist = [['copy', gettext('Full Clone')]]; - if (me.isTemplate) { - modelist.push(['clone', gettext('Linked Clone')]); - } - - col1.push({ - xtype: 'pveGuestIDSelector', - name: 'newvmid', - guestType: me.guestType, - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - allowBlank: true, - fieldLabel: me.guestType === 'lxc' ? gettext('Hostname') : gettext('Name') - }, - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ); - - col2.push({ - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Mode'), - name: 'clonemode', - reference: 'clonemodesel', - allowBlank: false, - hidden: !me.isTemplate, - value: me.isTemplate ? 'clone' : 'copy', - comboItems: modelist, - listeners: { - change: function(t, value) { - me.updateVisibility(); - me.verifyFeature(); - } - } - }, - { - xtype: 'PVE.form.SnapshotSelector', - name: 'snapname', - reference: 'snapshotsel', - fieldLabel: gettext('Snapshot'), - nodename: me.nodename, - guestType: me.guestType, - vmid: me.vmid, - hidden: me.isTemplate || !me.hasSnapshots ? true : false, - disabled: false, - allowBlank: false, - value : me.snapname, - listeners: { - change: function(f, value) { - me.verifyFeature(); - } - } - }, - { - xtype: 'pveDiskStorageSelector', - reference: 'diskselector', - nodename: me.nodename, - autoSelect: false, - hideSize: true, - hideSelection: true, - storageLabel: gettext('Target Storage'), - allowBlank: true, - storageContent: me.guestType === 'qemu' ? 'images' : 'rootdir', - emptyText: gettext('Same as source'), - disabled: me.isTemplate ? true : false // because default mode is clone for templates - }); - - var formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - reference: 'cloneform', - border: false, - layout: 'column', - defaultType: 'container', - columns: 2, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: col1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: col2 - } - ] - }); - - Ext.apply(me, { - modal: true, - width: 600, - height: 250, - border: false, - layout: 'fit', - buttons: [ { - xtype: 'proxmoxHelpButton', - listenToGlobalEvent: false, - hidden: false, - onlineHelp: me.onlineHelp - }, - '->', - { - reference: 'submitBtn', - text: gettext('Clone'), - disabled: true, - handler: function() { - var cloneForm = me.lookupReference('cloneform'); - if (cloneForm.isValid()) { - me.create_clone(cloneForm.getValues()); - } - } - } ], - items: [ formPanel ] - }); - - me.callParent(); - - me.verifyFeature(); - } -}); -Ext.define('PVE.qemu.Monitor', { - extend: 'Ext.panel.Panel', - - alias: 'widget.pveQemuMonitor', - - maxLines: 500, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var history = []; - var histNum = -1; - var lines = []; - - var textbox = Ext.createWidget('panel', { - region: 'center', - xtype: 'panel', - autoScroll: true, - border: true, - margins: '5 5 5 5', - bodyStyle: 'font-family: monospace;' - }); - - var scrollToEnd = function() { - var el = textbox.getTargetEl(); - var dom = Ext.getDom(el); - - var clientHeight = dom.clientHeight; - // BrowserBug: clientHeight reports 0 in IE9 StrictMode - // Instead we are using offsetHeight and hardcoding borders - if (Ext.isIE9 && Ext.isStrict) { - clientHeight = dom.offsetHeight + 2; - } - dom.scrollTop = dom.scrollHeight - clientHeight; - }; - - var refresh = function() { - textbox.update('
' + lines.join('\n') + '
'); - scrollToEnd(); - }; - - var addLine = function(line) { - lines.push(line); - if (lines.length > me.maxLines) { - lines.shift(); - } - }; - - var executeCmd = function(cmd) { - addLine("# " + Ext.htmlEncode(cmd)); - if (cmd) { - history.unshift(cmd); - if (history.length > 20) { - history.splice(20); - } - } - histNum = -1; - - refresh(); - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/monitor", - method: 'POST', - waitMsgTarget: me, - success: function(response, opts) { - var res = response.result.data; - Ext.Array.each(res.split('\n'), function(line) { - addLine(Ext.htmlEncode(line)); - }); - refresh(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - textbox, - { - region: 'south', - margins:'0 5 5 5', - border: false, - xtype: 'textfield', - name: 'cmd', - value: '', - fieldStyle: 'font-family: monospace;', - allowBlank: true, - listeners: { - afterrender: function(f) { - f.focus(false); - addLine("Type 'help' for help."); - refresh(); - }, - specialkey: function(f, e) { - var key = e.getKey(); - switch (key) { - case e.ENTER: - var cmd = f.getValue(); - f.setValue(''); - executeCmd(cmd); - break; - case e.PAGE_UP: - textbox.scrollBy(0, -0.9*textbox.getHeight(), false); - break; - case e.PAGE_DOWN: - textbox.scrollBy(0, 0.9*textbox.getHeight(), false); - break; - case e.UP: - if (histNum + 1 < history.length) { - f.setValue(history[++histNum]); - } - e.preventDefault(); - break; - case e.DOWN: - if (histNum > 0) { - f.setValue(history[--histNum]); - } - e.preventDefault(); - break; - default: - break; - } - } - } - } - ], - listeners: { - show: function() { - var field = me.query('textfield[name="cmd"]')[0]; - field.focus(false, true); - } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.qemu.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveQemuSummary', - - scrollable: true, - bodyPadding: 5, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - if (!me.workspace) { - throw "no workspace specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var template = !!me.pveSelNode.data.template; - var rstore = me.statusStore; - - var width = template ? 1 : 0.5; - var items = [ - { - xtype: template ? 'pveTemplateStatusView' : 'pveGuestStatusView', - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - }, - itemId: 'gueststatus', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'pveNotesView', - maxHeight: 330, - itemId: 'notesview', - pveSelNode: me.pveSelNode, - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - } - } - ]; - - var rrdstore; - if (!template) { - - rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/qemu/" + vmid + "/rrddata", - model: 'pve-rrd-guest' - }); - - items.push( - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - pveSelNode: me.pveSelNode, - fields: ['cpu'], - fieldTitles: [gettext('CPU usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - pveSelNode: me.pveSelNode, - fields: ['maxmem', 'mem'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - pveSelNode: me.pveSelNode, - fields: ['netin','netout'], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Disk IO'), - pveSelNode: me.pveSelNode, - fields: ['diskread','diskwrite'], - store: rrdstore - } - ); - - } - - Ext.apply(me, { - tbar: [ '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: { - type: 'column' - }, - defaults: { - minHeight: 330, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: items - } - ] - }); - - me.callParent(); - if (!template) { - rrdstore.startUpdate(); - me.on('destroy', rrdstore.stopUpdate); - } - } -}); -Ext.define('PVE.qemu.OSTypeInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuOSTypePanel', - onlineHelp: 'qm_os_settings', - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'combobox[name=osbase]': { - change: 'onOSBaseChange' - }, - 'combobox[name=ostype]': { - afterrender: 'onOSTypeChange', - change: 'onOSTypeChange' - } - }, - onOSBaseChange: function(field, value) { - this.lookup('ostype').getStore().setData(PVE.Utils.kvm_ostypes[value]); - }, - onOSTypeChange: function(field) { - var me = this, ostype = field.getValue(); - if (!me.getView().insideWizard) { - return; - } - var targetValues = PVE.qemu.OSDefaults.getDefaults(ostype); - - me.setWidget('pveBusSelector', targetValues.busType); - me.setWidget('pveNetworkCardSelector', targetValues.networkCard); - var scsihw = targetValues.scsihw || '__default__'; - this.getViewModel().set('current.scsihw', scsihw); - }, - setWidget: function(widget, newValue) { - // changing a widget is safe only if ComponentQuery.query returns us - // a single value array - var widgets = Ext.ComponentQuery.query('pveQemuCreateWizard ' + widget); - if (widgets.length === 1) { - widgets[0].setValue(newValue); - } else { - throw 'non unique widget :' + widget + ' in Wizard'; - } - } - }, - - initComponent : function() { - var me = this; - - /*jslint confusion: true */ - me.items = [ - { - xtype: 'displayfield', - value: gettext('Guest OS') + ':', - hidden: !me.insideWizard - }, - { - xtype: 'combobox', - submitValue: false, - name: 'osbase', - fieldLabel: gettext('Type'), - editable: false, - queryMode: 'local', - value: 'Linux', - store: Object.keys(PVE.Utils.kvm_ostypes) - }, - { - xtype: 'combobox', - name: 'ostype', - reference: 'ostype', - fieldLabel: gettext('Version'), - value: 'l26', - allowBlank : false, - editable: false, - queryMode: 'local', - valueField: 'val', - displayField: 'desc', - store: { - fields: ['desc', 'val'], - data: PVE.Utils.kvm_ostypes.Linux, - listeners: { - datachanged: function (store) { - var ostype = me.lookup('ostype'); - var old_val = ostype.getValue(); - if (!me.insideWizard && old_val && store.find('val', old_val) != -1) { - ostype.setValue(old_val); - } else { - ostype.setValue(store.getAt(0)); - } - } - } - } - } - ]; - /*jslint confusion: false */ - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.OSTypeEdit', { - extend: 'Proxmox.window.Edit', - - subject: 'OS Type', - - items: [{ xtype: 'pveQemuOSTypePanel' }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response, options) { - var value = response.result.data.ostype || 'other'; - var osinfo = PVE.Utils.get_kvm_osinfo(value); - me.setValues({ ostype: value, osbase: osinfo.base }); - } - }); - } -}); -/* - * This class holds performance *recommended* settings for the PVE Qemu wizards - * the *mandatory* settings are set in the PVE::QemuServer - * config_to_command sub - * We store this here until we get the data from the API server -*/ - -// this is how you would add an hypothetic FreeBSD > 10 entry -// -//virtio-blk is stable but virtIO net still -// problematic as of 10.3 -// see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059 -// addOS({ -// parent: 'generic', // inherits defaults -// pveOS: 'freebsd10', // must match a radiofield in OSTypeEdit.js -// busType: 'virtio' // must match a pveBusController value -// // networkCard muss match a pveNetworkCardSelector - - -Ext.define('PVE.qemu.OSDefaults', { - singleton: true, // will also force creation when loaded - - constructor: function() { - var me = this; - - var addOS = function(settings) { - if (me.hasOwnProperty(settings.parent)) { - var child = Ext.clone(me[settings.parent]); - me[settings.pveOS] = Ext.apply(child, settings); - - } else { - throw("Could not find your genitor"); - } - }; - - // default values - me.generic = { - busType: 'ide', - networkCard: 'e1000', - busPriority: { - ide: 4, - sata: 3, - scsi: 2, - virtio: 1 - }, - scsihw: 'virtio-scsi-pci' - }; - - // virtio-net is in kernel since 2.6.25 - // virtio-scsi since 3.2 but backported in RHEL with 2.6 kernel - addOS({ - pveOS: 'l26', - parent : 'generic', - busType: 'scsi', - busPriority: { - scsi: 4, - virtio: 3, - sata: 2, - ide: 1 - }, - networkCard: 'virtio' - }); - - // recommandation from http://wiki.qemu.org/Windows2000 - addOS({ - pveOS: 'w2k', - parent : 'generic', - networkCard: 'rtl8139', - scsihw: '' - }); - // https://pve.proxmox.com/wiki/Windows_XP_Guest_Notes - addOS({ - pveOS: 'wxp', - parent : 'w2k' - }); - - me.getDefaults = function(ostype) { - if (PVE.qemu.OSDefaults[ostype]) { - return PVE.qemu.OSDefaults[ostype]; - } else { - return PVE.qemu.OSDefaults.generic; - } - }; - } -}); -Ext.define('PVE.qemu.ProcessorInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuProcessorPanel', - onlineHelp: 'qm_cpu', - - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - - updateCores: function() { - var me = this.getView(); - var sockets = me.down('field[name=sockets]').getValue(); - var cores = me.down('field[name=cores]').getValue(); - me.down('field[name=totalcores]').setValue(sockets*cores); - var vcpus = me.down('field[name=vcpus]'); - vcpus.setMaxValue(sockets*cores); - vcpus.setEmptyText(sockets*cores); - vcpus.validate(); - }, - - control: { - 'field[name=sockets]': { - change: 'updateCores' - }, - 'field[name=cores]': { - change: 'updateCores' - } - } - }, - - onGetValues: function(values) { - var me = this; - - if (Array.isArray(values['delete'])) { - values['delete'] = values['delete'].join(','); - } - - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - // build the cpu options: - me.cpu.cputype = values.cputype; - - var flags = []; - - ['pcid', 'spec-ctrl'].forEach(function(flag) { - if (values[flag]) { - flags.push('+' + flag.toString()); - } - delete values[flag]; - }); - - me.cpu.flags = flags.length ? flags.join(';') : undefined; - - delete values.cputype; - delete values.flags; - var cpustring = PVE.Parser.printQemuCpu(me.cpu); - - // remove cputype delete request: - var del = values['delete']; - delete values['delete']; - if (del) { - del = del.split(','); - Ext.Array.remove(del, 'cputype'); - } else { - del = []; - } - - if (cpustring) { - values.cpu = cpustring; - } else { - del.push('cpu'); - } - - var delarr = del.join(','); - if (delarr) { - values['delete'] = delarr; - } - - return values; - }, - - cpu: {}, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'sockets', - minValue: 1, - maxValue: 4, - value: '1', - fieldLabel: gettext('Sockets'), - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: '1', - fieldLabel: gettext('Cores'), - allowBlank: false - } - ], - - column2: [ - { - xtype: 'CPUModelSelector', - name: 'cputype', - value: '__default__', - fieldLabel: gettext('Type') - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Total cores'), - name: 'totalcores', - value: '1' - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxintegerfield', - name: 'vcpus', - minValue: 1, - maxValue: 1, - value: '', - fieldLabel: gettext('VCPUs'), - deleteEmpty: true, - allowBlank: true, - emptyText: '1' - }, - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - maxValue: 128, // api maximum - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - minValue: 8, - maxValue: 500000, - value: '1024', - deleteEmpty: true, - allowBlank: true - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable NUMA'), - name: 'numa', - uncheckedValue: 0 - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'PCID', - name: 'pcid', - uncheckedValue: 0 - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'SPEC-CTRL', - name: 'spec-ctrl', - uncheckedValue: 0 - } - ] -}); - -Ext.define('PVE.qemu.ProcessorEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.ProcessorInputPanel'); - - Ext.apply(me, { - subject: gettext('Processors'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - var value = data.cpu; - if (value) { - var cpu = PVE.Parser.parseQemuCpu(value); - ipanel.cpu = cpu; - data.cputype = cpu.cputype; - if (cpu.flags) { - var flags = cpu.flags.split(';'); - flags.forEach(function(flag) { - var sign = flag.substr(0,1); - flag = flag.substr(1); - data[flag] = (sign === '+'); - }); - } - } - me.setValues(data); - } - }); - } -}); -Ext.define('PVE.qemu.BootOrderPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuBootOrderPanel', - vmconfig: {}, // store loaded vm config - - bootdisk: undefined, - selection: [], - list: [], - comboboxes: [], - - isBootDisk: function(value) { - return PVE.Utils.bus_match.test(value); - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - var order = me.vmconfig.boot || 'cdn'; - me.bootdisk = me.vmconfig.bootdisk || undefined; - - // get the first 3 characters - // ignore the rest (there should never be more than 3) - me.selection = order.split('').slice(0,3); - - // build bootdev list - me.list = []; - Ext.Object.each(me.vmconfig, function(key, value) { - if (me.isBootDisk(key) && - !(/media=cdrom/).test(value)) { - me.list.push([key, "Disk '" + key + "'"]); - } - }); - - me.list.push(['d', 'CD-ROM']); - me.list.push(['n', gettext('Network')]); - me.list.push(['__none__', Proxmox.Utils.noneText]); - - me.recomputeList(); - - me.comboboxes.forEach(function(box) { - box.resetOriginalValue(); - }); - }, - - onGetValues: function(values) { - var me = this; - var order = me.selection.join(''); - var res = { boot: order }; - - if (me.bootdisk && order.indexOf('c') !== -1) { - res.bootdisk = me.bootdisk; - } else { - res['delete'] = 'bootdisk'; - } - - return res; - }, - - recomputeSelection: function(combobox, newVal, oldVal) { - var me = this.up('#inputpanel'); - me.selection = []; - me.comboboxes.forEach(function(item) { - var val = item.getValue(); - - // when selecting an already selected item, - // switch it around - if ((val === newVal || (me.isBootDisk(val) && me.isBootDisk(newVal))) && - item.name !== combobox.name && - newVal !== '__none__') { - // swap items - val = oldVal; - } - - // push 'c','d' or 'n' in the array - if (me.isBootDisk(val)) { - me.selection.push('c'); - me.bootdisk = val; - } else if (val === 'd' || - val === 'n') { - me.selection.push(val); - } - }); - - me.recomputeList(); - }, - - recomputeList: function(){ - var me = this; - // set the correct values in the kvcomboboxes - var cnt = 0; - me.comboboxes.forEach(function(item) { - if (cnt === 0) { - // never show 'none' on first combobox - item.store.loadData(me.list.slice(0, me.list.length-1)); - } else { - item.store.loadData(me.list); - } - item.suspendEvent('change'); - if (cnt < me.selection.length) { - item.setValue((me.selection[cnt] !== 'c')?me.selection[cnt]:me.bootdisk); - } else if (cnt === 0){ - item.setValue(''); - } else { - item.setValue('__none__'); - } - cnt++; - item.resumeEvent('change'); - item.validate(); - }); - }, - - initComponent : function() { - var me = this; - - // this has to be done here, because of - // the way our inputPanel class handles items - me.comboboxes = [ - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 1", - labelWidth: 120, - name: 'bd1', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 2", - labelWidth: 120, - name: 'bd2', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 3", - labelWidth: 120, - name: 'bd3', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }) - ]; - Ext.apply(me, { items: me.comboboxes }); - me.callParent(); - } -}); - -Ext.define('PVE.qemu.BootOrderEdit', { - extend: 'Proxmox.window.Edit', - - items: [{ - xtype: 'pveQemuBootOrderPanel', - itemId: 'inputpanel' - }], - - subject: gettext('Boot Order'), - - initComponent : function() { - var me = this; - me.callParent(); - me.load({ - success: function(response, options) { - me.down('#inputpanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuMemoryPanel', - onlineHelp: 'qm_memory', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var res = {}; - - res.memory = values.memory; - res.balloon = values.balloon; - - if (!values.ballooning) { - res.balloon = 0; - res['delete'] = 'shares'; - } else if (values.memory === values.balloon) { - delete res.balloon; - res['delete'] = 'balloon,shares'; - } else if (Ext.isDefined(values.shares) && (values.shares !== "")) { - res.shares = values.shares; - } else { - res['delete'] = "shares"; - } - - return res; - }, - - initComponent: function() { - var me = this; - var labelWidth = 160; - - me.items= [ - { - xtype: 'pveMemoryField', - labelWidth: labelWidth, - fieldLabel: gettext('Memory') + ' (MiB)', - name: 'memory', - minValue: 1, - step: 32, - hotplug: me.hotplug, - listeners: { - change: function(f, value, old) { - var bf = me.down('field[name=balloon]'); - var balloon = bf.getValue(); - bf.setMaxValue(value); - if (balloon === old) { - bf.setValue(value); - } - bf.validate(); - } - } - } - ]; - - me.advancedItems= [ - { - xtype: 'pveMemoryField', - name: 'balloon', - minValue: 1, - step: 32, - fieldLabel: gettext('Minimum memory') + ' (MiB)', - hotplug: me.hotplug, - labelWidth: labelWidth, - allowBlank: false, - listeners: { - change: function(f, value) { - var memory = me.down('field[name=memory]').getValue(); - var shares = me.down('field[name=shares]'); - shares.setDisabled(value === memory); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'shares', - disabled: true, - minValue: 0, - maxValue: 50000, - value: '', - step: 10, - fieldLabel: gettext('Shares'), - labelWidth: labelWidth, - allowBlank: true, - emptyText: Proxmox.Utils.defaultText + ' (1000)', - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - labelWidth: labelWidth, - value: '1', - name: 'ballooning', - fieldLabel: gettext('Ballooning Device'), - listeners: { - change: function(f, value) { - var bf = me.down('field[name=balloon]'); - var shares = me.down('field[name=shares]'); - var memory = me.down('field[name=memory]'); - bf.setDisabled(!value); - shares.setDisabled(!value || (bf.getValue() === memory.getValue())); - } - } - } - ]; - - if (me.insideWizard) { - me.column1 = me.items; - me.items = undefined; - me.advancedColumn1 = me.advancedItems; - me.advancedItems = undefined; - } - me.callParent(); - } - -}); - -Ext.define('PVE.qemu.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent: function() { - var me = this; - - var memoryhotplug; - if(me.hotplug) { - Ext.each(me.hotplug.split(','), function(el) { - if (el === 'memory') { - memoryhotplug = 1; - } - }); - } - - var ipanel = Ext.create('PVE.qemu.MemoryInputPanel', { - hotplug: memoryhotplug - }); - - Ext.apply(me, { - subject: gettext('Memory'), - items: [ ipanel ], - // uncomment the following to use the async configiguration API - // backgroundDelay: 5, - width: 400 - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - - var values = { - ballooning: data.balloon === 0 ? '0' : '1', - shares: data.shares, - memory: data.memory || '512', - balloon: data.balloon > 0 ? data.balloon : (data.memory || '512') - }; - - ipanel.setValues(values); - } - }); - } -}); -Ext.define('PVE.qemu.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuNetworkInputPanel', - onlineHelp: 'qm_network_device', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - me.network.model = values.model; - if (values.nonetwork) { - return {}; - } else { - me.network.bridge = values.bridge; - me.network.tag = values.tag; - me.network.firewall = values.firewall; - } - me.network.macaddr = values.macaddr; - me.network.disconnect = values.disconnect; - me.network.queues = values.queues; - - if (values.rate) { - me.network.rate = values.rate; - } else { - delete me.network.rate; - } - - var params = {}; - - params[me.confid] = PVE.Parser.printQemuNetwork(me.network); - - return params; - }, - - setNetwork: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data) { - data.networkmode = data.bridge ? 'bridge' : 'nat'; - } else { - data = {}; - data.networkmode = 'bridge'; - } - me.network = data; - - me.setValues(me.network); - }, - - setNodename: function(nodename) { - var me = this; - - me.bridgesel.setNodename(nodename); - }, - - initComponent : function() { - var me = this; - - me.network = {}; - me.confid = 'net0'; - - me.column1 = []; - me.column2 = []; - - me.bridgesel = Ext.create('PVE.form.BridgeSelector', { - name: 'bridge', - fieldLabel: gettext('Bridge'), - nodename: me.nodename, - autoSelect: true, - allowBlank: false - }); - - me.column1 = [ - me.bridgesel, - { - xtype: 'pveVlanField', - name: 'tag', - value: '' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - checked: (me.insideWizard || me.isCreate) - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Disconnect'), - name: 'disconnect' - } - ]; - - if (me.insideWizard) { - me.column1.unshift({ - xtype: 'checkbox', - name: 'nonetwork', - inputValue: 'none', - boxLabel: gettext('No network device'), - listeners: { - change: function(cb, value) { - var fields = [ - 'disconnect', - 'bridge', - 'tag', - 'firewall', - 'model', - 'macaddr', - 'rate', - 'queues' - ]; - fields.forEach(function(fieldname) { - me.down('field[name='+fieldname+']').setDisabled(value); - }); - me.down('field[name=bridge]').validate(); - } - } - }); - me.column2.unshift({ - xtype: 'displayfield' - }); - } - - me.column2.push( - { - xtype: 'pveNetworkCardSelector', - name: 'model', - fieldLabel: gettext('Model'), - value: PVE.qemu.OSDefaults.generic.networkCard, - allowBlank: false - }, - { - xtype: 'textfield', - name: 'macaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - allowBlank: true, - emptyText: 'auto' - }); - me.advancedColumn2 = [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: '', - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'queues', - fieldLabel: 'Multiqueue', - minValue: 1, - maxValue: 8, - value: '', - allowBlank: true - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.NetworkInputPanel', { - confid: me.confid, - nodename: nodename, - isCreate: me.isCreate - }); - - Ext.applyIf(me, { - subject: gettext('Network Device'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - if (!me.isCreate) { - var value = me.vmconfig[me.confid]; - var network = PVE.Parser.parseQemuNetwork(me.confid, value); - if (!network) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse network options'); - me.close(); - return; - } - ipanel.setNetwork(me.confid, network); - } else { - for (i = 0; i < 100; i++) { - confid = 'net' + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - me.confid = confid; - break; - } - } - ipanel.setNetwork(me.confid); - } - } - }); - } -}); -Ext.define('PVE.qemu.Smbios1InputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.PVE.qemu.Smbios1InputPanel', - - insideWizard: false, - - smbios1: {}, - - onGetValues: function(values) { - var me = this; - - var params = { - smbios1: PVE.Parser.printQemuSmbios1(values) - }; - - return params; - }, - - setSmbios1: function(data) { - var me = this; - - me.smbios1 = data; - - me.setValues(me.smbios1); - }, - - initComponent : function() { - var me = this; - - - me.items = [ - { - xtype: 'textfield', - fieldLabel: 'UUID', - regex: /^[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$/, - name: 'uuid' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Manufacturer'), - regex: /^\S+$/, - name: 'manufacturer' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Product'), - regex: /^\S+$/, - name: 'product' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Version'), - regex: /^\S+$/, - name: 'version' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Serial'), - regex: /^\S+$/, - name: 'serial' - }, - { - xtype: 'textfield', - fieldLabel: 'SKU', - regex: /^\S+$/, - name: 'sku' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Family'), - regex: /^\S+$/, - name: 'family' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.Smbios1Edit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var ipanel = Ext.create('PVE.qemu.Smbios1InputPanel', {}); - - Ext.applyIf(me, { - subject: gettext('SMBIOS settings (type1)'), - width: 450, - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - var value = me.vmconfig.smbios1; - if (value) { - var data = PVE.Parser.parseQemuSmbios1(value); - if (!data) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse smbios options'); - me.close(); - return; - } - ipanel.setSmbios1(data); - } - } - }); - } -}); -Ext.define('PVE.qemu.CDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuCDInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || (values.controller + values.deviceid); - - me.drive.media = 'cdrom'; - if (values.mediaType === 'iso') { - me.drive.file = values.cdimage; - } else if (values.mediaType === 'cdrom') { - me.drive.file = 'cdrom'; - } else { - me.drive.file = 'none'; - } - - var params = {}; - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig, 'cdrom'); - } - }, - - setDrive: function(drive) { - var me = this; - - var values = {}; - if (drive.file === 'cdrom') { - values.mediaType = 'cdrom'; - } else if (drive.file === 'none') { - values.mediaType = 'none'; - } else { - values.mediaType = 'iso'; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.cdstorage = match[1]; - values.cdimage = drive.file; - } - } - - me.drive = drive; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - - me.cdstoragesel.setNodename(nodename); - me.cdfilesel.setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - var items = []; - - if (!me.confid) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - noVirtIO: true - }); - items.push(me.bussel); - } - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'iso', - boxLabel: gettext('Use CD/DVD disc image file (iso)'), - checked: true, - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=cdstorage]').setDisabled(!value); - me.down('field[name=cdimage]').setDisabled(!value); - me.down('field[name=cdimage]').validate(); - } - } - }); - - me.cdfilesel = Ext.create('PVE.form.FileSelector', { - name: 'cdimage', - nodename: me.nodename, - storageContent: 'iso', - fieldLabel: gettext('ISO image'), - labelAlign: 'right', - allowBlank: false - }); - - me.cdstoragesel = Ext.create('PVE.form.StorageSelector', { - name: 'cdstorage', - nodename: me.nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'iso', - allowBlank: false, - autoSelect: me.insideWizard, - listeners: { - change: function(f, value) { - me.cdfilesel.setStorage(value); - } - } - }); - - items.push(me.cdstoragesel); - items.push(me.cdfilesel); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'cdrom', - boxLabel: gettext('Use physical CD/DVD Drive') - }); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'none', - boxLabel: gettext('Do not use any media') - }); - - me.items = items; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CDEdit', { - extend: 'Proxmox.window.Edit', - - width: 400, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.CDInputPanel', { - confid: me.confid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: 'CD/DVD Drive', - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert('Error', 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - } - } - }); - } -}); -/*jslint confusion: true */ -/* 'change' property is assigned a string and then a function */ -Ext.define('PVE.qemu.HDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuHDInputPanel', - onlineHelp: 'qm_hard_disk', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - controller: { - - xclass: 'Ext.app.ViewController', - - onControllerChange: function(field) { - var value = field.getValue(); - - var allowIOthread = value.match(/^(virtio|scsi)/); - this.lookup('iothread').setDisabled(!allowIOthread); - if (!allowIOthread) { - this.lookup('iothread').setValue(false); - } - - var virtio = value.match(/^virtio/); - this.lookup('discard').setDisabled(virtio); - this.lookup('ssd').setDisabled(virtio); - if (virtio) { - this.lookup('discard').setValue(false); - this.lookup('ssd').setValue(false); - } - - this.lookup('scsiController').setVisible(value.match(/^scsi/)); - }, - - control: { - 'field[name=controller]': { - change: 'onControllerChange', - afterrender: 'onControllerChange' - }, - 'field[name=iothread]' : { - change: function(f, value) { - if (!this.getView().insideWizard) { - return; - } - var vmScsiType = value ? 'virtio-scsi-single': 'virtio-scsi-pci'; - this.lookupReference('scsiController').setValue(vmScsiType); - } - } - } - }, - - onGetValues: function(values) { - var me = this; - - var params = {}; - var confid = me.confid || (values.controller + values.deviceid); - - if (me.unused) { - me.drive.file = me.vmconfig[values.unusedId]; - confid = values.controller + values.deviceid; - } else if (me.isCreate) { - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - me.drive.file = values.hdstorage + ":" + values.disksize; - } - me.drive.format = values.diskformat; - } - - if (values.nobackup) { - me.drive.backup = 'no'; - } else { - delete me.drive.backup; - } - - if (values.noreplicate) { - me.drive.replicate = 'no'; - } else { - delete me.drive.replicate; - } - - if (values.discard) { - me.drive.discard = 'on'; - } else { - delete me.drive.discard; - } - - if (values.ssd) { - me.drive.ssd = 'on'; - } else { - delete me.drive.ssd; - } - - if (values.iothread) { - me.drive.iothread = 'on'; - } else { - delete me.drive.iothread; - } - - if (values.cache) { - me.drive.cache = values.cache; - } else { - delete me.drive.cache; - } - - var names = ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr']; - Ext.Array.each(names, function(name) { - if (values[name]) { - me.drive[name] = values[name]; - } else { - delete me.drive[name]; - } - var burst_name = name + '_max'; - if (values[burst_name] && values[name]) { - me.drive[burst_name] = values[burst_name]; - } else { - delete me.drive[burst_name]; - } - }); - - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - me.vmconfig = vmconfig; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig); - me.scsiController.setValue(vmconfig.scsihw); - } - if (me.unusedDisks) { - var disklist = []; - Ext.Object.each(vmconfig, function(key, value) { - if (key.match(/^unused\d+$/)) { - disklist.push([key, value]); - } - }); - me.unusedDisks.store.loadData(disklist); - me.unusedDisks.setValue(me.confid); - } - }, - - setDrive: function(drive) { - var me = this; - - me.drive = drive; - - var values = {}; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.hdstorage = match[1]; - } - - values.hdimage = drive.file; - values.nobackup = !PVE.Parser.parseBoolean(drive.backup, 1); - values.noreplicate = !PVE.Parser.parseBoolean(drive.replicate, 1); - values.diskformat = drive.format || 'raw'; - values.cache = drive.cache || '__default__'; - values.discard = (drive.discard === 'on'); - values.ssd = PVE.Parser.parseBoolean(drive.ssd); - values.iothread = PVE.Parser.parseBoolean(drive.iothread); - - values.mbps_rd = drive.mbps_rd; - values.mbps_wr = drive.mbps_wr; - values.iops_rd = drive.iops_rd; - values.iops_wr = drive.iops_wr; - values.mbps_rd_max = drive.mbps_rd_max; - values.mbps_wr_max = drive.mbps_wr_max; - values.iops_rd_max = drive.iops_rd_max; - values.iops_wr_max = drive.iops_wr_max; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - var labelWidth = 140; - - me.drive = {}; - - me.column1 = []; - me.column2 = []; - - me.advancedColumn1 = []; - me.advancedColumn2 = []; - - if (!me.confid || me.unused) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - vmconfig: me.insideWizard ? {ide2: 'cdrom'} : {} - }); - me.column1.push(me.bussel); - - me.scsiController = Ext.create('Ext.form.field.Display', { - fieldLabel: gettext('SCSI Controller'), - reference: 'scsiController', - bind: me.insideWizard ? { - value: '{current.scsihw}' - } : undefined, - renderer: PVE.Utils.render_scsihw, - submitValue: false, - hidden: true - }); - me.column1.push(me.scsiController); - } - - if (me.unused) { - me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', { - name: 'unusedId', - fieldLabel: gettext('Disk image'), - matchFieldWidth: false, - listConfig: { - width: 350 - }, - data: [], - allowBlank: false - }); - me.column1.push(me.unusedDisks); - } else if (me.isCreate) { - me.column1.push({ - xtype: 'pveDiskStorageSelector', - storageContent: 'images', - name: 'disk', - nodename: me.nodename, - autoSelect: me.insideWizard - }); - } else { - me.column1.push({ - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'hdimage' - }); - } - - me.column2.push( - { - xtype: 'CacheTypeSelector', - name: 'cache', - value: '__default__', - fieldLabel: gettext('Cache') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Discard'), - disabled: me.confid && me.confid.match(/^virtio/), - reference: 'discard', - name: 'discard' - } - ); - - me.advancedColumn1.push( - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && me.confid.match(/^virtio/), - fieldLabel: gettext('SSD emulation'), - labelWidth: labelWidth, - name: 'ssd', - reference: 'ssd' - }, - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && !me.confid.match(/^(virtio|scsi)/), - fieldLabel: 'IO thread', - labelWidth: labelWidth, - reference: 'iothread', - name: 'iothread' - }, - { - xtype: 'numberfield', - name: 'mbps_rd', - minValue: 1, - step: 1, - fieldLabel: gettext('Read limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'numberfield', - name: 'mbps_wr', - minValue: 1, - step: 1, - fieldLabel: gettext('Write limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd', - minValue: 10, - step: 10, - fieldLabel: gettext('Read limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr', - minValue: 10, - step: 10, - fieldLabel: gettext('Write limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - } - ); - - me.advancedColumn2.push( - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('No backup'), - labelWidth: labelWidth, - name: 'nobackup' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Skip replication'), - labelWidth: labelWidth, - name: 'noreplicate' - }, - { - xtype: 'numberfield', - name: 'mbps_rd_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Read max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'numberfield', - name: 'mbps_wr_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Write max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Read max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Write max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - } - ); - - me.callParent(); - } -}); -/*jslint confusion: false */ - -Ext.define('PVE.qemu.HDEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - backgroundDelay: 5, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.qemu.HDInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - me.subject = gettext('Unused Disk'); - } else if (me.isCreate) { - me.subject = gettext('Hard Disk'); - } else { - me.subject = gettext('Hard Disk') + ' (' + me.confid + ')'; - } - - me.items = [ ipanel ]; - - me.callParent(); - /*jslint confusion: true*/ - /* 'data' is assigned an empty array in same file, and here we - * use it like an object - */ - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - me.isValid(); // trigger validation - } - } - }); - /*jslint confusion: false*/ - } -}); -Ext.define('PVE.window.HDResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 140, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 250, - height: 150, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -Ext.define('PVE.window.HDMove', { - extend: 'Ext.window.Window', - - resizable: false, - - - move_disk: function(disk, storage, format, delete_disk) { - var me = this; - var qemu = (me.type === 'qemu'); - var params = {}; - params.storage = storage; - params[qemu ? 'disk':'volume'] = disk; - - if (format && qemu) { - params.format = format; - } - - if (delete_disk) { - params['delete'] = 1; - } - - var url = '/nodes/' + me.nodename + '/' + me.type + '/' + me.vmid + '/'; - url += qemu ? 'move_disk' : 'move_volume'; - - Proxmox.Utils.API2Request({ - params: params, - url: url, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - win.on('destroy', function() { me.close(); }); - } - }); - - }, - - initComponent : function() { - var me = this; - - var diskarray = []; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.type) { - me.type = 'qemu'; - } - - var qemu = (me.type === 'qemu'); - - var items = [ - { - xtype: 'displayfield', - name: qemu ? 'disk' : 'volume', - value: me.disk, - fieldLabel: qemu ? gettext('Disk') : gettext('Mount Point'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - items.push({ - xtype: 'pveDiskStorageSelector', - storageLabel: gettext('Target Storage'), - nodename: me.nodename, - storageContent: qemu ? 'images' : 'rootdir', - hideSize: true - }); - - items.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Delete source'), - name: 'deleteDisk', - uncheckedValue: 0, - checked: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = qemu ? gettext("Move disk") : gettext('Move Volume'); - submitBtn = Ext.create('Ext.Button', { - text: me.title, - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.move_disk(me.disk, values.hdstorage, values.diskformat, - values.deleteDisk); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 350, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - me.mon(me.formPanel, 'validitychange', function(fp, isValid) { - submitBtn.setDisabled(!isValid); - }); - - me.formPanel.isValid(); - } -}); -Ext.define('PVE.qemu.EFIDiskInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveEFIDiskInputPanel', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var confid = 'efidisk0'; - - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - // we use 1 here, because for efi the size gets overridden from the backend - me.drive.file = values.hdstorage + ":1"; - } - - me.drive.format = values.diskformat; - var params = {}; - params[confid] = PVE.Parser.printQemuDrive(me.drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items= []; - - me.items.push({ - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - }); - me.callParent(); - } -}); - -Ext.define('PVE.qemu.EFIDiskEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - subject: gettext('EFI Disk'), - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveEFIDiskInputPanel', - onlineHelp: 'qm_bios_and_uefi', - confid: me.confid, - nodename: nodename, - isCreate: true - }]; - - me.callParent(); - } -}); -Ext.define('PVE.qemu.DisplayInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveDisplayInputPanel', - - onGetValues: function(values) { - var ret = PVE.Parser.printPropertyString(values, 'type'); - if (ret === '') { - return { - 'delete': 'vga' - }; - } - return { - vga: ret - }; - }, - - items: [{ - name: 'type', - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - comboItems: PVE.Utils.kvm_vga_driver_array(), - validator: function() { - var v = this.getValue(); - var cfg = this.up('proxmoxWindowEdit').vmconfig || {}; - - if (v.match(/^serial\d+$/) && (!cfg[v] || cfg[v] !== 'socket')) { - var fmt = gettext("Serial interface '{0}' is not correctly configured."); - return Ext.String.format(fmt, v); - } - return true; - }, - listeners: { - change: function(cb, val) { - var me = this.up('panel'); - if (!val) { - return; - } - var disable = false; - var emptyText = Proxmox.Utils.defaultText; - switch (val) { - case "cirrus": - emptyText = "4"; - break; - case "std": - emptyText = "16"; - break; - case "qxl": - case "qxl2": - case "qxl3": - case "qxl4": - emptyText = "16"; - break; - case "vmware": - emptyText = "16"; - break; - case "none": - case "serial0": - case "serial1": - case "serial2": - case "serial3": - emptyText = 'N/A'; - disable = true; - break; - case "virtio": - emptyText = "256"; - break; - default: - break; - } - var memoryfield = me.down('field[name=memory]'); - memoryfield.setEmptyText(emptyText); - memoryfield.setDisabled(disable); - } - } - },{ - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Memory') + ' (MiB)', - minValue: 4, - maxValue: 512, - step: 4, - name: 'memory' - }] -}); - -Ext.define('PVE.qemu.DisplayEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - subject: gettext('Display'), - width: 350, - - items: [{ - xtype: 'pveDisplayInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response) { - me.vmconfig = response.result.data; - var vga = me.vmconfig.vga || '__default__'; - me.setValues(PVE.Parser.parsePropertyString(vga, 'type')); - } - }); - } -}); -Ext.define('PVE.qemu.KeyboardEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('Keyboard Layout'), - items: { - xtype: 'VNCKeyboardSelector', - name: 'keyboard', - value: '__default__', - fieldLabel: gettext('Keyboard Layout') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.HardwareView', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.HardwareView'], - - onlineHelp: 'qm_virtual_machines_settings', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - var iconCls = rowdef.iconCls; - var icon = ''; - var txt = (rowdef.header || key); - - metaData.tdAttr = "valign=middle"; - - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - if (rowdef.tdCls == 'pve-itype-icon-storage') { - var value = me.getObjectValue(key, '', false); - if (value === '') { - value = me.getObjectValue(key, '', true); - } - if (value.match(/vm-.*-cloudinit/)) { - metaData.tdCls = 'pve-itype-icon-cloud'; - return rowdef.cloudheader; - } else if (value.match(/media=cdrom/)) { - metaData.tdCls = 'pve-itype-icon-cdrom'; - return rowdef.cdheader; - } - } - } else if (iconCls) { - icon = ""; - metaData.tdCls += " pve-itype-fa"; - } - return icon + txt; - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - /*jslint confusion: true */ - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined, - never_delete: true, - defaultValue: '512', - tdCls: 'pve-itype-icon-memory', - group: 2, - multiKey: ['memory', 'balloon', 'shares'], - renderer: function(value, metaData, record, ri, ci, store, pending) { - var res = ''; - - var max = me.getObjectValue('memory', 512, pending); - var balloon = me.getObjectValue('balloon', undefined, pending); - var shares = me.getObjectValue('shares', undefined, pending); - - res = Proxmox.Utils.format_size(max*1024*1024); - - if (balloon !== undefined && balloon > 0) { - res = Proxmox.Utils.format_size(balloon*1024*1024) + "/" + res; - - if (shares) { - res += ' [shares=' + shares +']'; - } - } else if (balloon === 0) { - res += ' [balloon=0]'; - } - return res; - } - }, - sockets: { - header: gettext('Processors'), - never_delete: true, - editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ? - 'PVE.qemu.ProcessorEdit' : undefined, - tdCls: 'pve-itype-icon-processor', - group: 3, - defaultValue: '1', - multiKey: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'], - renderer: function(value, metaData, record, rowIndex, colIndex, store, pending) { - - var sockets = me.getObjectValue('sockets', 1, pending); - var model = me.getObjectValue('cpu', undefined, pending); - var cores = me.getObjectValue('cores', 1, pending); - var numa = me.getObjectValue('numa', undefined, pending); - var vcpus = me.getObjectValue('vcpus', undefined, pending); - var cpulimit = me.getObjectValue('cpulimit', undefined, pending); - var cpuunits = me.getObjectValue('cpuunits', undefined, pending); - - var res = Ext.String.format('{0} ({1} sockets, {2} cores)', - sockets*cores, sockets, cores); - - if (model) { - res += ' [' + model + ']'; - } - - if (numa) { - res += ' [numa=' + numa +']'; - } - - if (vcpus) { - res += ' [vcpus=' + vcpus +']'; - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit +']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits +']'; - } - - return res; - } - }, - bios: { - header: 'BIOS', - group: 4, - never_delete: true, - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.BiosEdit' : undefined, - defaultValue: '', - iconCls: 'microchip', - renderer: PVE.Utils.render_qemu_bios - }, - vga: { - header: gettext('Display'), - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined, - never_delete: true, - tdCls: 'pve-itype-icon-display', - group:5, - defaultValue: '', - renderer: PVE.Utils.render_kvm_vga_driver - }, - machine: { - header: gettext('Machine'), - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Machine'), - width: 350, - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - }]} : undefined, - iconCls: 'cogs', - never_delete: true, - group: 6, - defaultValue: '', - renderer: PVE.Utils.render_qemu_machine - }, - scsihw: { - header: gettext('SCSI Controller'), - iconCls: 'database', - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.ScsiHwEdit' : undefined, - renderer: PVE.Utils.render_scsihw, - group: 7, - never_delete: true, - defaultValue: '' - }, - cores: { - visible: false - }, - cpu: { - visible: false - }, - numa: { - visible: false - }, - balloon: { - visible: false - }, - hotplug: { - visible: false - }, - vcpus: { - visible: false - }, - cpuunits: { - visible: false - }, - cpulimit: { - visible: false - }, - shares: { - visible: false - } - }; - /*jslint confusion: false */ - - PVE.Utils.forEachBus(undefined, function(type, id) { - var confid = type + id; - rows[confid] = { - group: 10, - tdCls: 'pve-itype-icon-storage', - editor: 'PVE.qemu.HDEdit', - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - header: gettext('Hard Disk') + ' (' + confid +')', - cdheader: gettext('CD/DVD Drive') + ' (' + confid +')', - cloudheader: gettext('CloudInit Drive') + ' (' + confid + ')' - }; - }); - for (i = 0; i < 32; i++) { - confid = "net" + i.toString(); - rows[confid] = { - group: 15, - order: i, - tdCls: 'pve-itype-icon-network', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined, - never_delete: caps.vms['VM.Config.Network'] ? false : true, - header: gettext('Network Device') + ' (' + confid +')' - }; - } - rows.efidisk0 = { - group: 20, - tdCls: 'pve-itype-icon-storage', - editor: null, - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - header: gettext('EFI Disk') - }; - for (i = 0; i < 5; i++) { - confid = "usb" + i.toString(); - rows[confid] = { - group: 25, - order: i, - tdCls: 'pve-itype-icon-usb', - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined, - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('USB Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < 4; i++) { - confid = "hostpci" + i.toString(); - rows[confid] = { - group: 30, - order: i, - tdCls: 'pve-itype-icon-pci', - never_delete: caps.nodes['Sys.Console'] ? false : true, - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.PCIEdit' : undefined, - header: gettext('PCI Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < 4; i++) { - confid = "serial" + i.toString(); - rows[confid] = { - group: 35, - order: i, - tdCls: 'pve-itype-icon-serial', - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('Serial Port') + ' (' + confid + ')' - }; - } - for (i = 0; i < 256; i++) { - rows["unused" + i.toString()] = { - group: 99, - order: i, - tdCls: 'pve-itype-icon-storage', - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined, - header: gettext('Unused Disk') + ' ' + i.toString() - }; - } - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - var reload = function() { - me.rstore.load(); - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var editor = rowdef.editor; - if (rowdef.tdCls == 'pve-itype-icon-storage') { - if (!diskCap) { - return; - } - var value = me.getObjectValue(rec.data.key, '', true); - if (value.match(/vm-.*-cloudinit/)) { - return; - } else if (value.match(/media=cdrom/)) { - editor = 'PVE.qemu.CDEdit'; - } - } - - var win; - - if (Ext.isString(editor)) { - win = Ext.create(editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', reload); - }; - - var run_resize = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', reload); - }; - - var run_move = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', reload); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - handler: run_editor - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: sm, - disabled: true, - handler: run_resize - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move disk'), - selModel: sm, - disabled: true, - handler: run_move - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - defaultText: gettext('Remove'), - altText: gettext('Detach'), - selModel: sm, - disabled: true, - dangerous: true, - RESTMethod: 'PUT', - confirmMsg: function(rec) { - var warn = gettext('Are you sure you want to remove entry {0}'); - if (this.text === this.altText) { - warn = gettext('Are you sure you want to detach entry {0}'); - } - - var entry = rec.data.key; - var msg = Ext.String.format(warn, "'" - + me.renderKey(entry, {}, rec) + "'"); - - if (entry.match(/^unused\d+$/)) { - msg += " " + gettext('This will permanently erase all data.'); - } - - return msg; - }, - handler: function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: b.RESTMethod, - params: { - 'delete': rec.data.key - }, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - if (b.RESTMethod === 'POST') { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: function () { - me.reload(); - } - } - }); - win.show(); - } - } - }); - }, - listeners: { - render: function(btn) { - // hack: calculate an optimal button width on first display - // to prevent the whole toolbar to move when we switch - // between the "Remove" and "Detach" labels - var def = btn.getSize().width; - - btn.setText(btn.altText); - var alt = btn.getSize().width; - - btn.setText(btn.defaultText); - - var optimal = alt > def ? alt : def; - btn.setSize({ width: optimal }); - } - } - }); - - var revert_btn = new Proxmox.button.Button({ - text: gettext('Revert'), - selModel: sm, - disabled: true, - handler: function(b, e, rec) { - var rowdef = me.rows[rec.data.key] || {}; - var keys = rowdef.multiKey || [ rec.data.key ]; - var revert = keys.join(','); - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'revert': revert - }, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - } - }); - } - }); - - var efidisk_menuitem = Ext.create('Ext.menu.Item',{ - text: gettext('EFI Disk'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - - var rstoredata = me.rstore.getData().map; - // check if ovmf is configured - if (rstoredata.bios && rstoredata.bios.data.value === 'ovmf') { - var win = Ext.create('PVE.qemu.EFIDiskEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } else { - Ext.Msg.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.')); - } - - } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - // disable button when we have an efidisk already - // disable is ok in this case, because you can instantly - // see that there is already one - efidisk_menuitem.setDisabled(me.rstore.getData().map.efidisk0 !== undefined); - // en/disable usb add button - var usbcount = 0; - var pcicount = 0; - var hasCloudInit = false; - me.rstore.getData().items.forEach(function(item){ - if (/^usb\d+/.test(item.id)) { - usbcount++; - } else if (/^hostpci\d+/.test(item.id)) { - pcicount++; - } - if (!hasCloudInit && /vm-.*-cloudinit/.test(item.data.value)) { - hasCloudInit = true; - } - }); - - // heuristic only for disabling some stuff, the backend has the final word. - var noSysConsolePerm = !caps.nodes['Sys.Console']; - - me.down('#addusb').setDisabled(noSysConsolePerm || (usbcount >= 5)); - me.down('#addpci').setDisabled(noSysConsolePerm || (pcicount >= 4)); - me.down('#addci').setDisabled(noSysConsolePerm || hasCloudInit); - - if (!rec) { - remove_btn.disable(); - edit_btn.disable(); - resize_btn.disable(); - move_btn.disable(); - revert_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var isUnusedDisk = key.match(/^unused\d+/); - var isUsedDisk = !isUnusedDisk && - rowdef.tdCls == 'pve-itype-icon-storage' && - (value && !value.match(/media=cdrom/)); - - var isCloudInit = (value && value.toString().match(/vm-.*-cloudinit/)); - - var isEfi = (key === 'efidisk0'); - - remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete === true) || (isUnusedDisk && !diskCap)); - remove_btn.setText((isUsedDisk && !isCloudInit) ? remove_btn.altText : remove_btn.defaultText); - remove_btn.RESTMethod = isUnusedDisk ? 'POST':'PUT'; - - edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor || isCloudInit || !diskCap); - - resize_btn.setDisabled(pending || !isUsedDisk || !diskCap); - - move_btn.setDisabled(pending || !isUsedDisk || !diskCap); - - revert_btn.setDisabled(!pending); - - }; - - Ext.apply(me, { - url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + '/pending', - interval: 5000, - selModel: sm, - run_editor: run_editor, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Hard Disk'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.HDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('CD/DVD Drive'), - iconCls: 'pve-itype-icon-cdrom', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.CDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Network Device'), - iconCls: 'pve-itype-icon-network', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.qemu.NetworkEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode, - isCreate: true - }); - win.on('destroy', reload); - win.show(); - } - }, - efidisk_menuitem, - { - text: gettext('USB Device'), - itemId: 'addusb', - iconCls: 'pve-itype-icon-usb', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.USBEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('PCI Device'), - itemId: 'addpci', - iconCls: 'pve-itype-icon-pci', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.PCIEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Serial Port'), - itemId: 'addserial', - iconCls: 'pve-itype-icon-serial', - disabled: !caps.vms['VM.Config.Options'], - handler: function() { - var win = Ext.create('PVE.qemu.SerialEdit', { - url: '/api2/extjs/' + baseurl - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('CloudInit Drive'), - itemId: 'addci', - iconCls: 'pve-itype-icon-cloud', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.CIDriveEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - remove_btn, - edit_btn, - resize_btn, - move_btn, - revert_btn - ], - rows: rows, - sorterFn: sorterFn, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - me.mon(me.rstore, 'refresh', function() { - set_button_status(); - }); - } -}); -Ext.define('PVE.qemu.ScsiHwEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('SCSI Controller Type'), - items: { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - fieldLabel: gettext('Type') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.BiosEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveQemuBiosEdit', - - initComponent : function() { - var me = this; - - var EFIHint = Ext.createWidget({ - xtype: 'displayfield', //submitValue is false, so we don't get submitted - userCls: 'pve-hint', - value: 'You need to add an EFI disk for storing the ' + - 'EFI settings. See the online help for details.', - hidden: true - }); - - Ext.applyIf(me, { - subject: 'BIOS', - items: [ { - xtype: 'pveQemuBiosSelector', - onlineHelp: 'qm_bios_and_uefi', - name: 'bios', - value: '__default__', - fieldLabel: 'BIOS', - listeners: { - 'change' : function(field, newValue) { - if (newValue == 'ovmf') { - Proxmox.Utils.API2Request({ - url : me.url, - method : 'GET', - failure : function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success : function(response, opts) { - var vmConfig = response.result.data; - // there can be only one - if (!vmConfig.efidisk0) { - EFIHint.setVisible(true); - } - } - }); - } else { - if (EFIHint.isVisible()) { - EFIHint.setVisible(false); - } - } - } - } - }, - EFIHint - ] }); - - me.callParent(); - - me.load(); - - } -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.Options', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.Options'], - - onlineHelp: 'qm_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - name: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Name'), - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Name'), - items: { - xtype: 'inputpanel', - items:{ - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - }, - onGetValues: function(values) { - var params = values; - if (values.name === undefined || - values.name === null || - values.name === '') { - params = { 'delete':'name'}; - } - return params; - } - } - } : undefined - }, - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'qm_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.OSTypeEdit' : undefined, - renderer: PVE.Utils.render_kvm_ostype, - defaultValue: 'other' - }, - bootdisk: { - visible: false - }, - boot: { - header: gettext('Boot Order'), - defaultValue: 'cdn', - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.BootOrderEdit' : undefined, - multiKey: ['boot', 'bootdisk'], - renderer: function(order, metaData, record, rowIndex, colIndex, store, pending) { - var i; - var text = ''; - var bootdisk = me.getObjectValue('bootdisk', undefined, pending); - order = order || 'cdn'; - for (i = 0; i < order.length; i++) { - var sel = order.substring(i, i + 1); - if (text) { - text += ', '; - } - if (sel === 'c') { - if (bootdisk) { - text += "Disk '" + bootdisk + "'"; - } else { - text += "Disk"; - } - } else if (sel === 'n') { - text += 'Network'; - } else if (sel === 'a') { - text += 'Floppy'; - } else if (sel === 'd') { - text += 'CD-ROM'; - } else { - text += sel; - } - } - return text; - } - }, - tablet: { - header: gettext('Use tablet for pointer'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use tablet for pointer'), - items: { - xtype: 'proxmoxcheckbox', - name: 'tablet', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - hotplug: { - header: gettext('Hotplug'), - defaultValue: 'disk,network,usb', - renderer: PVE.Utils.render_hotplug_features, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hotplug'), - items: { - xtype: 'pveHotplugFeatureSelector', - name: 'hotplug', - value: '', - multiSelect: true, - fieldLabel: gettext('Hotplug'), - allowBlank: true - } - } : undefined - }, - acpi: { - header: gettext('ACPI support'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('ACPI support'), - items: { - xtype: 'proxmoxcheckbox', - name: 'acpi', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - kvm: { - header: gettext('KVM hardware virtualization'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('KVM hardware virtualization'), - items: { - xtype: 'proxmoxcheckbox', - name: 'kvm', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - freeze: { - header: gettext('Freeze CPU at startup'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.PowerMgmt'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Freeze CPU at startup'), - items: { - xtype: 'proxmoxcheckbox', - name: 'freeze', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Freeze CPU at startup') - } - } : undefined - }, - localtime: { - header: gettext('Use local time for RTC'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use local time for RTC'), - items: { - xtype: 'proxmoxcheckbox', - name: 'localtime', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Use local time for RTC') - } - } : undefined - }, - startdate: { - header: gettext('RTC start date'), - defaultValue: 'now', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('RTC start date'), - items: { - xtype: 'proxmoxtextfield', - name: 'startdate', - deleteEmpty: true, - value: 'now', - fieldLabel: gettext('RTC start date'), - vtype: 'QemuStartDate', - allowBlank: true - } - } : undefined - }, - smbios1: { - header: gettext('SMBIOS settings (type1)'), - defaultValue: '', - renderer: Ext.String.htmlEncode, - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.Smbios1Edit' : undefined - }, - agent: { - header: gettext('Qemu Agent'), - defaultValue: false, - renderer: PVE.Utils.render_qga_features, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Qemu Agent'), - items: { - xtype: 'pveAgentFeatureSelector', - name: 'agent' - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var revert_btn = new Proxmox.button.Button({ - text: gettext('Revert'), - disabled: true, - handler: function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = me.rows[rec.data.key] || {}; - var keys = rowdef.multiKey || [ rec.data.key ]; - var revert = keys.join(','); - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'revert': revert - }, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - } - }); - } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - - var key = rec.data.key; - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var rowdef = rows[key]; - - edit_btn.setDisabled(!rowdef.editor); - revert_btn.setDisabled(!pending); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/qemu/" + vmid + "/pending", - interval: 5000, - cwidth1: 250, - tbar: [ edit_btn, revert_btn ], - rows: rows, - editorConfig: { - url: "/api2/extjs/" + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - me.rstore.on('datachanged', function() { - set_button_status(); - }); - } -}); - -Ext.define('PVE.window.Snapshot', { - extend: 'Ext.window.Window', - - resizable: false, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - defaultFocus: 'field', - - take_snapshot: function(snapname, descr, vmstate) { - var me = this; - var params = { snapname: snapname, vmstate: vmstate ? 1 : 0 }; - if (descr) { - params.description = descr; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot", - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - update_snapshot: function(snapname, descr) { - var me = this; - Proxmox.Utils.API2Request({ - params: { description: descr }, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot/" + - snapname + '/config', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var summarystore = Ext.create('Ext.data.Store', { - model: 'KeyValue', - sorters: [ - { - property : 'key', - direction: 'ASC' - } - ] - }); - - var items = [ - { - xtype: me.snapname ? 'displayfield' : 'textfield', - name: 'snapname', - value: me.snapname, - fieldLabel: gettext('Name'), - vtype: 'ConfigId', - allowBlank: false - } - ]; - - if (me.snapname) { - items.push({ - xtype: 'displayfield', - name: 'snaptime', - renderer: PVE.Utils.render_timestamp_human_readable, - fieldLabel: gettext('Timestamp') - }); - } else { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'vmstate', - uncheckedValue: 0, - defaultValue: 0, - checked: 1, - fieldLabel: gettext('Include RAM') - }); - } - - items.push({ - xtype: 'textareafield', - grow: true, - name: 'description', - fieldLabel: gettext('Description') - }); - - if (me.snapname) { - items.push({ - title: gettext('Settings'), - xtype: 'grid', - height: 200, - store: summarystore, - columns: [ - {header: gettext('Key'), width: 150, dataIndex: 'key'}, - {header: gettext('Value'), flex: 1, dataIndex: 'value'} - ] - }); - } - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - if (me.snapname) { - me.title = gettext('Edit') + ': ' + gettext('Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Update'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.update_snapshot(me.snapname, values.description); - } - } - }); - } else { - me.title ="VM " + me.vmid + ': ' + gettext('Take Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Take Snapshot'), - reference: 'submitbutton', - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.take_snapshot(values.snapname, values.description, values.vmstate); - } - } - }); - } - - Ext.apply(me, { - modal: true, - width: 450, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - if (me.snapname) { - Ext.apply(me, { - width: 620, - height: 420 - }); - } - - me.callParent(); - - if (!me.snapname) { - return; - } - - // else load data - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot/" + - me.snapname + '/config', - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.close(); - }, - success: function(response, options) { - var data = response.result.data; - var kvarray = []; - Ext.Object.each(data, function(key, value) { - if (key === 'description' || key === 'snaptime') { - return; - } - kvarray.push({ key: key, value: value }); - }); - - summarystore.suspendEvents(); - summarystore.add(kvarray); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh', summarystore); - - form.findField('snaptime').setValue(data.snaptime); - form.findField('description').setValue(data.description); - } - }); - } -}); -Ext.define('PVE.qemu.SnapshotTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveQemuSnapshotTree'], - - load_delay: 3000, - - old_digest: 'invalid', - - stateful: true, - stateId: 'grid-qemu-snapshots', - - sorterFn: function(rec1, rec2) { - var v1 = rec1.data.snaptime; - var v2 = rec2.data.snaptime; - - if (rec1.data.name === 'current') { - return 1; - } - if (rec2.data.name === 'current') { - return -1; - } - - return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); - }, - - reload: function(repeat) { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot', - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - me.load_task.delay(me.load_delay); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var digest = 'invalid'; - var idhash = {}; - var root = { name: '__root', expanded: true, children: [] }; - Ext.Array.each(response.result.data, function(item) { - item.leaf = true; - item.children = []; - if (item.name === 'current') { - digest = item.digest + item.running; - if (item.running) { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running'; - } else { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree'; - } - } else { - item.iconCls = 'fa fa-fw fa-history x-fa-tree'; - } - idhash[item.name] = item; - }); - - if (digest !== me.old_digest) { - me.old_digest = digest; - - Ext.Array.each(response.result.data, function(item) { - if (item.parent && idhash[item.parent]) { - var parent_item = idhash[item.parent]; - parent_item.children.push(item); - parent_item.leaf = false; - parent_item.expanded = true; - parent_item.expandable = false; - } else { - root.children.push(item); - } - }); - - me.setRootNode(root); - } - - me.load_task.delay(me.load_delay); - } - }); - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/feature', - params: { feature: 'snapshot' }, - method: 'GET', - success: function(response, options) { - var res = response.result.data; - if (res.hasFeature) { - var snpBtns = Ext.ComponentQuery.query('#snapshotBtn'); - snpBtns.forEach(function(item){ - item.enable(); - }); - } - } - }); - - - }, - - listeners: { - beforestatesave: function(grid, state, eopts) { - // extjs cannot serialize functions, - // so a the sorter with only the sorterFn will - // not be a valid sorter when restoring the state - delete state.storeState.sorters; - } - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.vmid = me.pveSelNode.data.vmid; - if (!me.vmid) { - throw "no VM ID specified"; - } - - me.load_task = new Ext.util.DelayedTask(me.reload, me); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var valid_snapshot = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current'; - }; - - var valid_snapshot_rollback = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current' && !record.data.snapstate; - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (valid_snapshot(rec)) { - var win = Ext.create('PVE.window.Snapshot', { - snapname: rec.data.name, - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - me.mon(win, 'close', me.reload, me); - } - }; - - var editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot, - handler: run_editor - }); - - var rollbackBtn = new Proxmox.button.Button({ - text: gettext('Rollback'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot_rollback, - confirmMsg: function(rec) { - return Proxmox.Utils.format_task_description('qmrollback', me.vmid) + - " '" + rec.data.name + "'"; - }, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname + '/rollback', - method: 'POST', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var removeBtn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.name + "'"); - return msg; - }, - enableFn: valid_snapshot, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var snapshotBtn = Ext.create('Ext.Button', { - itemId: 'snapshotBtn', - text: gettext('Take Snapshot'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.window.Snapshot', { - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - } - }); - - Ext.apply(me, { - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - selModel: sm, - tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ], - fields: [ - 'name', 'description', 'snapstate', 'vmstate', 'running', - { name: 'snaptime', type: 'date', dateFormat: 'timestamp' } - ], - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - width: 200, - renderer: function(value, metaData, record) { - if (value === 'current') { - return "NOW"; - } else { - return value; - } - } - }, - { - text: gettext('RAM'), - align: 'center', - resizable: false, - dataIndex: 'vmstate', - width: 50, - renderer: function(value, metaData, record) { - if (record.data.name !== 'current') { - return Proxmox.Utils.format_boolean(value); - } - } - }, - { - text: gettext('Date') + "/" + gettext("Status"), - dataIndex: 'snaptime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.snapstate) { - return record.data.snapstate; - } - if (value) { - return Ext.Date.format(value,'Y-m-d H:i:s'); - } - } - }, - { - text: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value, metaData, record) { - if (record.data.name === 'current') { - return gettext("You are here!"); - } else { - return Ext.String.htmlEncode(value); - } - } - } - ], - columnLines: true, // will work in 4.1? - listeners: { - activate: me.reload, - destroy: me.load_task.cancel, - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.store.sorters.add(new Ext.util.Sorter({ - sorterFn: me.sorterFn - })); - } -}); - -Ext.define('PVE.qemu.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.qemu.Config', - - onlineHelp: 'chapter_virtual_machines', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!me.pveSelNode.data.template; - - var running = !!me.pveSelNode.data.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + "/qemu/" + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + '/status/' + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var resumeBtn = Ext.create('Ext.Button', { - text: gettext('Resume'), - disabled: !caps.vms['VM.PowerMgmt'], - hidden: true, - handler: function() { - vm_command('resume'); - }, - iconCls: 'fa fa-play' - }); - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('qmtemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = me.pveSelNode.data.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - itemId: 'removeBtn', - disabled: !caps.vms['VM.Allocate'], - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'VM', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('qmshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items: [{ - text: gettext('Pause'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmpause', vmid), - handler: function() { - vm_command("suspend"); - }, - iconCls: 'fa fa-pause' - },{ - text: gettext('Hibernate'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmsuspend', vmid), - tooltip: gettext('Suspend to disk'), - handler: function() { - vm_command("suspend", { todisk: 1 }); - }, - iconCls: 'fa fa-download' - },{ - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - dangerous: true, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - confirmMsg: Proxmox.Utils.format_task_description('qmstop', vmid), - handler: function() { - vm_command("stop", { timeout: 30 }); - }, - iconCls: 'fa fa-stop' - },{ - text: gettext('Reset'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmreset', vmid), - handler: function() { - vm_command("reset"); - }, - iconCls: 'fa fa-bolt' - }] - }, - iconCls: 'fa fa-power-off' - }); - - var vm = me.pveSelNode.data; - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - hidden: template, - consoleType: 'kvm', - consoleName: vm.name, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - Ext.apply(me, { - title: Ext.String.format(gettext("Virtual Machine {0} on node '{1}'"), vm.text, nodename), - hstateid: 'kvmtab', - tbarSpacing: false, - tbar: [ statusTxt, '->', resumeBtn, startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveQemuSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push({ - title: gettext('Console'), - itemId: 'console', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'kvm', - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Hardware'), - itemId: 'hardware', - iconCls: 'fa fa-desktop', - xtype: 'PVE.qemu.HardwareView' - }, - { - title: 'Cloud-Init', - itemId: 'cloudinit', - iconCls: 'fa fa-cloud', - xtype: 'pveCiPanel' - }, - { - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options', - xtype: 'PVE.qemu.Options' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - xtype: 'proxmoxNodeTasks', - iconCls: 'fa fa-list', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Monitor'] && !template) { - me.items.push({ - title: gettext('Monitor'), - iconCls: 'fa fa-eye', - itemId: 'monitor', - xtype: 'pveQemuMonitor' - }); - } - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - xtype: 'pveQemuSnapshotTree', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var qmpstatus; - var spice = false; - var xtermjs = false; - var lock; - - if (!success) { - status = qmpstatus = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('qmpstatus'); - qmpstatus = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - - spice = s.data.get('spice') ? true : false; - xtermjs = s.data.get('serial') ? true : false; - - } - - if (template) { - return; - } - - var resume = (['prelaunch', 'paused', 'suspended'].indexOf(qmpstatus) !== -1); - - if (resume || lock === 'suspended') { - startBtn.setVisible(false); - resumeBtn.setVisible(true); - } else { - startBtn.setVisible(true); - resumeBtn.setVisible(false); - } - - consoleBtn.setEnableSpice(spice); - consoleBtn.setEnableXtermJS(xtermjs); - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.CreateWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveQemuCreateWizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - current: { - scsihw: '' - } - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('Virtual Machine'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'qm_general_settings', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', - guestType: 'qemu', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ], - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - ], - advancedColumn2: [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - labelWidth: 120, - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Shutdown timeout') - } - ], - onGetValues: function(values) { - - ['name', 'pool', 'onboot', 'agent'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - - var res = PVE.Parser.printStartup({ - order: values.order, - up: values.up, - down: values.down - }); - - if (res) { - values.startup = res; - } - - delete values.order; - delete values.up; - delete values.down; - - return values; - } - }, - { - xtype: 'container', - layout: 'hbox', - defaults: { - flex: 1, - padding: '0 10' - }, - title: gettext('OS'), - items: [ - { - xtype: 'pveQemuCDInputPanel', - bind: { - nodename: '{nodename}' - }, - confid: 'ide2', - insideWizard: true - }, - { - xtype: 'pveQemuOSTypePanel', - insideWizard: true - } - ] - }, - { - xtype: 'pveQemuSystemPanel', - title: gettext('System'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuHDInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Hard Disk'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuProcessorPanel', - insideWizard: true, - title: gettext('CPU') - }, - { - xtype: 'pveQemuMemoryPanel', - insideWizard: true, - title: gettext('Memory') - }, - { - xtype: 'pveQemuNetworkInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Network'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var kv = this.up('window').getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete') { // ignore - return; - } - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response){ - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - - -Ext.define('PVE.qemu.USBInputPanel', { - extend: 'Proxmox.panel.InputPanel', - mixins: ['Proxmox.Mixin.CBind' ], - - autoComplete: false, - onlineHelp: 'qm_usb_passthrough', - - controller: { - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=usb]': { - change: function(field, newValue, oldValue) { - var hwidfield = this.lookupReference('hwid'); - var portfield = this.lookupReference('port'); - var usb3field = this.lookupReference('usb3'); - if (field.inputValue === 'hostdevice') { - hwidfield.setDisabled(!newValue); - } else if(field.inputValue === 'port') { - portfield.setDisabled(!newValue); - } else if(field.inputValue === 'spice') { - usb3field.setDisabled(newValue); - } - } - }, - 'pveUSBSelector': { - change: function(field, newValue, oldValue) { - var usbval = field.getUSBValue(); - var usb3field = this.lookupReference('usb3'); - var usb3 = /usb3/.test(usbval); - if(usb3 && !usb3field.isDisabled()) { - usb3field.savedVal = usb3field.getValue(); - usb3field.setValue(true); - usb3field.setDisabled(true); - } else if(!usb3 && usb3field.isDisabled()){ - var val = (usb3field.savedVal === undefined)?usb3field.originalValue:usb3field.savedVal; - usb3field.setValue(val); - usb3field.setDisabled(false); - } - } - } - } - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - }, - - onGetValues: function(values) { - var me = this; - if(!me.confid) { - var i; - for (i = 0; i < 6; i++) { - if (!me.vmconfig['usb' + i.toString()]) { - me.confid = 'usb' + i.toString(); - break; - } - } - } - var val = ""; - var type = me.down('radiofield').getGroupValue(); - switch (type) { - case 'spice': - val = 'spice'; break; - case 'hostdevice': - case 'port': - val = me.down('pveUSBSelector[name=' + type + ']').getUSBValue(); - if (!/usb3/.test(val) && me.down('field[name=usb3]').getValue() === true) { - val += ',usb3=1'; - } - break; - default: - throw "invalid type selected"; - } - - values[me.confid] = val; - return values; - }, - - items: [ - { - xtype: 'fieldcontainer', - defaultType: 'radiofield', - items:[ - { - name: 'usb', - inputValue: 'spice', - boxLabel: gettext('Spice Port'), - submitValue: false, - checked: true - }, - { - name: 'usb', - inputValue: 'hostdevice', - boxLabel: gettext('Use USB Vendor/Device ID'), - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - type: 'device', - name: 'hostdevice', - cbind: { pveSelNode: '{pveSelNode}' }, - editable: true, - reference: 'hwid', - allowBlank: false, - fieldLabel: 'Choose Device', - labelAlign: 'right', - submitValue: false - }, - { - name: 'usb', - inputValue: 'port', - boxLabel: gettext('Use USB Port'), - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - name: 'port', - cbind: { pveSelNode: '{pveSelNode}' }, - editable: true, - type: 'port', - reference: 'port', - allowBlank: false, - fieldLabel: gettext('Choose Port'), - labelAlign: 'right', - submitValue: false - }, - { - xtype: 'checkbox', - name: 'usb3', - submitValue: false, - reference: 'usb3', - fieldLabel: gettext('Use USB3') - } - ] - } - ] -}); - -Ext.define('PVE.qemu.USBEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('USB Device'), - - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.USBInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var data = response.result.data[me.confid].split(','); - var port, hostdevice, usb3 = false; - var type = 'spice'; - var i; - for (i = 0; i < data.length; i++) { - if (/^(host=)?(0x)?[a-zA-Z0-9]{4}\:(0x)?[a-zA-Z0-9]{4}$/.test(data[i])) { - hostdevice = data[i]; - hostdevice = hostdevice.replace('host=', '').replace('0x',''); - type = 'hostdevice'; - } else if (/^(host=)?(\d+)\-(\d+(\.\d+)*)$/.test(data[i])) { - port = data[i]; - port = port.replace('host=',''); - type = 'port'; - } - - if (/^usb3=(1|on|true)$/.test(data[i])) { - usb3 = true; - } - } - var values = { - usb : type, - hostdevice: hostdevice, - port: port, - usb3: usb3 - }; - - ipanel.setValues(values); - } - } - }); - } -}); -Ext.define('PVE.qemu.PCIInputPanel', { - extend: 'Proxmox.panel.InputPanel', - - onlineHelp: 'qm_pci_passthrough', - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - - var hostpci = me.vmconfig[me.confid] || ''; - - var values = PVE.Parser.parsePropertyString(hostpci, 'host'); - if (values.host && values.host.length < 6) { // 00:00 format not 00:00.0 - values.host += ".0"; - values.multifunction = true; - } - values['x-vga'] = PVE.Parser.parseBoolean(values['x-vga'], 0); - values.pcie = PVE.Parser.parseBoolean(values.pcie, 0); - values.rombar = PVE.Parser.parseBoolean(values.rombar, 1); - - me.setValues(values); - if (!me.vmconfig.machine || me.vmconfig.machine.indexOf('q35') === -1) { - // machine is not set to some variant of q35, so we disable pcie - var pcie = me.down('field[name=pcie]'); - pcie.setDisabled(true); - pcie.setBoxLabel(gettext('Q35 only')); - } - - if (values.romfile) { - me.down('field[name=romfile]').setVisible(true); - } - }, - - onGetValues: function(values) { - var me = this; - var ret = {}; - if(!me.confid) { - var i; - for (i = 0; i < 5; i++) { - if (!me.vmconfig['hostpci' + i.toString()]) { - me.confid = 'hostpci' + i.toString(); - break; - } - } - } - if (values.multifunction) { - // modify host to skip the '.X' - values.host = values.host.substring(0,5); - delete values.multifunction; - } - - if (values.rombar) { - delete values.rombar; - } else { - values.rombar = 0; - } - - if (!values.romfile) { - delete values.romfile; - } - - ret[me.confid] = PVE.Parser.printPropertyString(values, 'host'); - return ret; - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.column1 = [ - { - xtype: 'pvePCISelector', - fieldLabel: gettext('Device'), - name: 'host', - nodename: me.nodename, - allowBlank: false, - onLoadCallBack: function(store, records, success) { - if (!success || !records.length) { - return; - } - - var first = records[0]; - if (first.data.iommugroup === -1) { - // no iommu groups - var warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - value: 'No IOMMU detected, please activate it.' + - 'See Documentation for further information.', - userCls: 'pve-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } - }, - listeners: { - change: function(pcisel, value) { - if (!value) { - return; - } - var pcidev = pcisel.getStore().getById(value); - var mdevfield = me.down('field[name=mdev]'); - mdevfield.setDisabled(!pcidev || !pcidev.data.mdev); - if (!pcidev) { - return; - } - var id = pcidev.data.id.substring(0,5); // 00:00 - var iommu = pcidev.data.iommugroup; - // try to find out if there are more devices - // in that iommu group - if (iommu !== -1) { - var count = 0; - pcisel.getStore().each(function(record) { - if (record.data.iommugroup === iommu && - record.data.id.substring(0,5) !== id) - { - count++; - return false; - } - }); - var warning = me.down('#iommuwarning'); - if (count && !warning) { - warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - itemId: 'iommuwarning', - value: 'The selected Device is not in a seperate' + - 'IOMMU group, make sure this is intended.', - userCls: 'pve-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } else if (!count && warning) { - me.remove(warning); - } - } - if (pcidev.data.mdev) { - mdevfield.setPciID(value); - } - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('All Functions'), - name: 'multifunction' - } - ]; - - me.column2 = [ - { - xtype: 'pveMDevSelector', - name: 'mdev', - disabled: true, - fieldLabel: gettext('MDev Type'), - nodename: me.nodename, - listeners: { - change: function(field, value) { - var mf = me.down('field[name=multifunction]'); - if (!!value) { - mf.setValue(false); - } - mf.setDisabled(!!value); - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Primary GPU'), - name: 'x-vga' - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'ROM-Bar', - name: 'rombar' - }, - { - xtype: 'displayfield', - submitValue: true, - hidden: true, - fieldLabel: 'ROM-File', - name: 'romfile' - } - ]; - - me.advancedColumn2 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'PCI-Express', - name: 'pcie' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.PCIEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('PCI Device'), - - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.PCIInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.SerialnputPanel', { - extend: 'Proxmox.panel.InputPanel', - - autoComplete: false, - - setVMConfig: function(vmconfig) { - var me = this, i; - me.vmconfig = vmconfig; - - for (i = 0; i < 4; i++) { - var port = 'serial' + i.toString(); - if (!me.vmconfig[port]) { - me.down('field[name=serialid]').setValue(i); - break; - } - } - - }, - - onGetValues: function(values) { - var me = this; - - var id = 'serial' + values.serialid; - delete values.serialid; - values[id] = 'socket'; - return values; - }, - - items: [ - { - xtype: 'proxmoxintegerfield', - name: 'serialid', - fieldLabel: gettext('Serial Port'), - minValue: 0, - maxValue: 3, - allowBlank: false, - validator: function(id) { - if (!this.rendered) { - return true; - } - var me = this.up('panel'); - if (me.vmconfig !== undefined && Ext.isDefined(me.vmconfig['serial' + id])) { - return "This device is already in use."; - } - return true; - } - } - ] -}); - -Ext.define('PVE.qemu.SerialEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('Serial Port'), - - initComponent : function() { - var me = this; - - // for now create of (socket) serial port only - me.isCreate = true; - - var ipanel = Ext.create('PVE.qemu.SerialnputPanel', {}); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.window.IPInfo', { - extend: 'Ext.window.Window', - width: 600, - title: gettext('Guest Agent Network Information'), - height: 300, - layout: { - type: 'fit' - }, - modal: true, - items: [ - { - xtype: 'grid', - emptyText: gettext('No network information'), - columns: [ - { - dataIndex: 'name', - text: gettext('Name'), - flex: 3 - }, - { - dataIndex: 'hardware-address', - text: gettext('MAC address'), - width: 140 - }, - { - dataIndex: 'ip-addresses', - text: gettext('IP address'), - align: 'right', - flex: 4, - renderer: function(val) { - if (!Ext.isArray(val)) { - return ''; - } - var ips = []; - val.forEach(function(ip) { - var addr = ip['ip-address']; - var pref = ip.prefix; - if (addr && pref) { - ips.push(addr + '/' + pref); - } - }); - return ips.join('
'); - } - } - ] - } - ] -}); - -Ext.define('PVE.qemu.AgentIPView', { - extend: 'Ext.container.Container', - xtype: 'pveAgentIPView', - - layout: { - type: 'hbox', - align: 'top' - }, - - nics: [], - - items: [ - { - xtype: 'box', - html: ' IPs' - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'right', - pack: 'end' - }, - items: [ - { - xtype: 'label', - flex: 1, - itemId: 'ipBox', - style: { - 'text-align': 'right' - } - }, - { - xtype: 'button', - itemId: 'moreBtn', - hidden: true, - ui: 'default-toolbar', - handler: function(btn) { - var me = this.up('pveAgentIPView'); - - var win = Ext.create('PVE.window.IPInfo'); - win.down('grid').getStore().setData(me.nics); - win.show(); - }, - text: gettext('More') - } - ] - } - ], - - getDefaultIps: function(nics) { - var me = this; - var ips = []; - nics.forEach(function(nic) { - if (nic['hardware-address'] && - nic['hardware-address'] != '00:00:00:00:00:00') { - - var nic_ips = nic['ip-addresses'] || []; - nic_ips.forEach(function(ip) { - var p = ip['ip-address']; - // show 2 ips at maximum - if (ips.length < 2) { - ips.push(p); - } - }); - } - }); - - return ips; - }, - - startIPStore: function(store, records, success) { - var me = this; - var agentRec = store.getById('agent'); - /*jslint confusion: true*/ - /* value is number and string */ - me.agent = (agentRec && agentRec.data.value === 1); - me.running = (store.getById('status').data.value === 'running'); - /*jslint confusion: false*/ - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!caps.vms['VM.Monitor']) { - var errorText = gettext("Requires '{0}' Privileges"); - me.updateStatus(false, Ext.String.format(errorText, 'VM.Monitor')); - return; - } - - if (me.agent && me.running && me.ipStore.isStopped) { - me.ipStore.startUpdate(); - } else if (me.ipStore.isStopped) { - me.updateStatus(); - } - }, - - updateStatus: function(unsuccessful, defaulttext) { - var me = this; - var text = defaulttext || gettext('No network information'); - var more = false; - if (unsuccessful) { - text = gettext('Guest Agent not running'); - } else if (me.agent && me.running) { - if (Ext.isArray(me.nics) && me.nics.length) { - more = true; - var ips = me.getDefaultIps(me.nics); - if (ips.length !== 0) { - text = ips.join('
'); - } - } else if (me.nics && me.nics.error) { - var msg = gettext('Cannot get info from Guest Agent
Error: {0}'); - text = Ext.String.format(text, me.nics.error.desc); - } - } else if (me.agent) { - text = gettext('Guest Agent not running'); - } else { - text = gettext('No Guest Agent configured'); - } - - var ipBox = me.down('#ipBox'); - ipBox.update(text); - - var moreBtn = me.down('#moreBtn'); - moreBtn.setVisible(more); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw 'rstore not given'; - } - - if (!me.pveSelNode) { - throw 'pveSelNode not given'; - } - - var nodename = me.pveSelNode.data.node; - var vmid = me.pveSelNode.data.vmid; - - me.ipStore = Ext.create('Proxmox.data.UpdateStore', { - interval: 10000, - storeid: 'pve-qemu-agent-' + vmid, - method: 'POST', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/qemu/' + vmid + '/agent/network-get-interfaces' - } - }); - - me.callParent(); - - me.mon(me.ipStore, 'load', function(store, records, success) { - if (records && records.length) { - me.nics = records[0].data.result; - } else { - me.nics = undefined; - } - me.updateStatus(!success); - }); - - me.on('destroy', me.ipStore.stopUpdate); - - // if we already have info about the vm, use it immediately - if (me.rstore.getCount()) { - me.startIPStore(me.rstore, me.rstore.getData(), false); - } - - // check if the guest agent is there on every statusstore load - me.mon(me.rstore, 'load', me.startIPStore, me); - } -}); -Ext.define('PVE.qemu.CloudInit', { - extend: 'Proxmox.grid.PendingObjectGrid', - xtype: 'pveCiPanel', - - onlineHelp: 'qm_cloud_init', - - tbar: [ - { - xtype: 'proxmoxButton', - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var me = this.up('grid'); - var warn = gettext('Are you sure you want to remove entry {0}'); - - var entry = rec.data.key; - var msg = Ext.String.format(warn, "'" - + me.renderKey(entry, {}, rec) + "'"); - - return msg; - }, - enableFn: function(record) { - var me = this.up('grid'); - var caps = Ext.state.Manager.get('GuiCap'); - if (me.rows[record.data.key].never_delete || - !caps.vms['VM.Config.Network']) { - return false; - } - - if (record.data.key === 'cipassword' && !record.data.value) { - return false; - } - return true; - }, - handler: function() { - var me = this.up('grid'); - var records = me.getSelection(); - if (!records || !records.length) { - return; - } - - var id = records[0].data.key; - var match = id.match(/^net(\d+)$/); - if (match) { - id = 'ipconfig' + match[1]; - } - - var params = {}; - params['delete'] = id; - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: params, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - callback: function() { - me.reload(); - } - }); - }, - text: gettext('Remove') - }, - { - xtype: 'proxmoxButton', - disabled: true, - handler: function() { - var me = this.up('grid'); - me.run_editor(); - }, - text: gettext('Edit') - }, - '-', - { - xtype: 'button', - itemId: 'savebtn', - text: gettext('Regenerate Image'), - handler: function() { - var me = this.up('grid'); - var eject_params = {}; - var insert_params = {}; - var disk = PVE.Parser.parseQemuDrive(me.ciDriveId, me.ciDrive); - var storage = ''; - var stormatch = disk.file.match(/^([^\:]+)\:/); - if (stormatch) { - storage = stormatch[1]; - } - eject_params[me.ciDriveId] = 'none,media=cdrom'; - insert_params[me.ciDriveId] = storage + ':cloudinit'; - - var failure = function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }; - - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: eject_params, - failure: failure, - callback: function() { - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: insert_params, - failure: failure, - callback: function() { - me.reload(); - } - }); - } - }); - } - } - ], - - border: false, - - set_button_status: function(rstore, records, success) { - if (!success || records.length < 1) { - return; - } - var me = this; - var found; - records.forEach(function(record) { - if (found) { - return; - } - var id = record.data.key; - var value = record.data.value; - var ciregex = new RegExp("vm-" + me.pveSelNode.data.vmid + "-cloudinit"); - if (id.match(/^(ide|scsi|sata)\d+$/) && ciregex.test(value)) { - found = id; - me.ciDriveId = found; - me.ciDrive = value; - } - }); - - me.down('#savebtn').setDisabled(!found); - me.setDisabled(!found); - if (!found) { - me.getView().mask(gettext('No CloudInit Drive found'), ['pve-static-mask']); - } else { - me.getView().unmask(); - } - }, - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - - var icon = ""; - if (rowdef.iconCls) { - icon = ' '; - } - return icon + (rowdef.header || key); - }, - - listeners: { - activate: function () { - var me = this; - me.rstore.startUpdate(); - }, - itemdblclick: function() { - var me = this; - me.run_editor(); - } - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - var caps = Ext.state.Manager.get('GuiCap'); - me.baseurl = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid; - me.url = me.baseurl + '/pending'; - me.editorConfig.url = me.baseurl + '/config'; - me.editorConfig.pveSelNode = me.pveSelNode; - - /*jslint confusion: true*/ - /* editor is string and object */ - me.rows = { - ciuser: { - header: gettext('User'), - iconCls: 'fa fa-user', - never_delete: true, - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('User'), - items: [ - { - xtype: 'proxmoxtextfield', - deleteEmpty: true, - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('User'), - name: 'ciuser' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.defaultText; - } - }, - cipassword: { - header: gettext('Password'), - iconCls: 'fa fa-unlock', - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Password'), - items: [ - { - xtype: 'proxmoxtextfield', - inputType: 'password', - deleteEmpty: true, - emptyText: Proxmox.Utils.noneText, - fieldLabel: gettext('Password'), - name: 'cipassword' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.noneText; - } - }, - searchdomain: { - header: gettext('DNS domain'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - nameserver: { - header: gettext('DNS servers'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - sshkeys: { - header: gettext('SSH public key'), - iconCls: 'fa fa-key', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.SSHKeyEdit' : undefined, - never_delete: true, - renderer: function(value) { - value = decodeURIComponent(value); - var keys = value.split('\n'); - var text = []; - keys.forEach(function(key) { - if (key.length) { - // First erase all quoted strings (eg. command="foo" - var v = key.replace(/"(?:\\.|[^"\\])*"/g, ''); - // Now try to detect the comment: - var res = v.match(/^\s*(\S+\s+)?(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)\s+\S+\s+(.*?)\s*$/, ''); - if (res) { - key = Ext.String.htmlEncode(res[2]); - if (res[1]) { - key += ' (' + gettext('with options') + ')'; - } - text.push(key); - return; - } - // Most likely invalid at this point, so just stick to - // the old value. - text.push(Ext.String.htmlEncode(key)); - } - }); - if (text.length) { - return text.join('
'); - } else { - return Proxmox.Utils.noneText; - } - }, - defaultValue: '' - } - }; - var i; - var ipconfig_renderer = function(value, md, record, ri, ci, store, pending) { - var id = record.data.key; - var match = id.match(/^net(\d+)$/); - var val = ''; - if (match) { - val = me.getObjectValue('ipconfig'+match[1], '', pending); - } - return val; - }; - for (i = 0; i < 32; i++) { - // we want to show an entry for every network device - // even if it is empty - me.rows['net' + i.toString()] = { - multiKey: ['ipconfig' + i.toString(), 'net' + i.toString()], - header: gettext('IP Config') + ' (net' + i.toString() +')', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.IPConfigEdit' : undefined, - iconCls: 'fa fa-exchange', - renderer: ipconfig_renderer - }; - me.rows['ipconfig' + i.toString()] = { - visible: false - }; - } - /*jslint confusion: false*/ - - PVE.Utils.forEachBus(['ide', 'scsi', 'sata'], function(type, id) { - me.rows[type+id] = { - visible: false - }; - }); - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - } -}); -Ext.define('PVE.qemu.CIDriveInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveCIDriveInputPanel', - - insideWizard: false, - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var drive = {}; - var params = {}; - drive.file = values.hdstorage + ":cloudinit"; - drive.format = values.diskformat; - params[values.controller + values.deviceid] = PVE.Parser.printQemuDrive(drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - setVMConfig: function(config) { - var me = this; - me.down('#drive').setVMConfig(config, 'cdrom'); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items = [ - { - xtype: 'pveControllerSelector', - noVirtIO: true, - itemId: 'drive', - fieldLabel: gettext('CloudInit Drive'), - name: 'drive' - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'storselector', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - } - ]; - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CIDriveEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCIDriveEdit', - - isCreate: true, - subject: gettext('CloudInit Drive'), - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveCIDriveInputPanel', - itemId: 'cipanel', - nodename: nodename - }]; - - me.callParent(); - - me.load({ - success: function(response, opts) { - me.down('#cipanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.SSHKeyInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSSHKeyInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - if (values.sshkeys) { - values.sshkeys.trim(); - } - if (!values.sshkeys.length) { - values = {}; - values['delete'] = 'sshkeys'; - return values; - } else { - values.sshkeys = encodeURIComponent(values.sshkeys); - } - return values; - }, - - items: [ - { - xtype: 'textarea', - itemId: 'sshkeys', - name: 'sshkeys', - height: 250 - }, - { - xtype: 'filebutton', - itemId: 'filebutton', - name: 'file', - text: gettext('Load SSH Key File'), - fieldLabel: 'test', - listeners: { - change: function(btn, e, value) { - var me = this.up('inputpanel'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - var keysField = me.down('#sshkeys'); - var old = keysField.getValue(); - keysField.setValue(old + res); - }); - }); - btn.reset(); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - if (!window.FileReader) { - me.down('#filebutton').setVisible(false); - } - - } -}); - -Ext.define('PVE.qemu.SSHKeyEdit', { - extend: 'Proxmox.window.Edit', - - width: 800, - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.SSHKeyInputPanel'); - - Ext.apply(me, { - subject: gettext('SSH Keys'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.create) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.sshkeys) { - data.sshkeys = decodeURIComponent(data.sshkeys); - ipanel.setValues(data); - } - } - }); - } - } -}); -Ext.define('PVE.qemu.IPConfigPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveIPConfigPanel', - - insideWizard: false, - - vmconfig: {}, - - onGetValues: function(values) { - var me = this; - - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - - var params = {}; - - var cfg = PVE.Parser.printIPConfig(values); - if (cfg === '') { - params['delete'] = [me.confid]; - } else { - params[me.confid] = cfg; - } - return params; - }, - - setVMConfig: function(config) { - var me = this; - me.vmconfig = config; - }, - - setIPConfig: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data.ip === 'dhcp') { - data.ipv4mode = data.ip; - data.ip = ''; - } else { - data.ipv4mode = 'static'; - } - if (data.ip6 === 'dhcp' || data.ip6 === 'auto') { - data.ipv6mode = data.ip6; - data.ip6 = ''; - } else { - data.ipv6mode = 'static'; - } - - me.ipconfig = data; - me.setValues(me.ipconfig); - }, - - initComponent : function() { - var me = this; - - me.ipconfig = {}; - - me.column1 = [ - { - xtype: 'displayfield', - fieldLabel: gettext('Network Device'), - value: me.netid - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv4') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv4mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: '', - disabled: true, - fieldLabel: gettext('IPv4/CIDR') - }, - { - xtype: 'textfield', - name: 'gw', - value: '', - vtype: 'IPAddress', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv4') +')' - } - ]; - - me.column2 = [ - { - xtype: 'displayfield' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv6') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv6mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: '', - vtype: 'IP6CIDRAddress', - disabled: true, - fieldLabel: gettext('IPv6/CIDR') - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: '', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv6') +')' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.IPConfigEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - // convert confid from netX to ipconfigX - var match = me.confid.match(/^net(\d+)$/); - if (match) { - me.netid = me.confid; - me.confid = 'ipconfig' + match[1]; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.IPConfigPanel', { - confid: me.confid, - netid: me.netid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: gettext('Network Config'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - me.vmconfig = response.result.data; - var ipconfig = {}; - var value = me.vmconfig[me.confid]; - if (value) { - ipconfig = PVE.Parser.parseIPConfig(me.confid, value); - if (!ipconfig) { - Ext.Msg.alert(gettext('Error'), gettext('Unable to parse network configuration')); - me.close(); - return; - } - } - ipanel.setIPConfig(me.confid, ipconfig); - ipanel.setVMConfig(me.vmconfig); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.SystemInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSystemPanel', - - onlineHelp: 'qm_system_settings', - - viewModel: { - data: { - efi: false, - addefi: true - }, - - formulas: { - efidisk: function(get) { - return get('efi') && get('addefi'); - } - } - }, - - onGetValues: function(values) { - if (values.vga && values.vga.substr(0,6) === 'serial') { - values['serial' + values.vga.substr(6,1)] = 'socket'; - } - - var efidrive = {}; - if (values.hdimage) { - efidrive.file = values.hdimage; - } else if (values.hdstorage) { - efidrive.file = values.hdstorage + ":1"; - } - - if (values.diskformat) { - efidrive.format = values.diskformat; - } - - delete values.hdimage; - delete values.hdstorage; - delete values.diskformat; - - if (efidrive.file) { - values.efidisk0 = PVE.Parser.printQemuDrive(efidrive); - } - - return values; - }, - - controller: { - xclass: 'Ext.app.ViewController', - - scsihwChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('current.scsihw', value); - } - }, - - biosChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('efi', value === 'ovmf'); - } - }, - - control: { - 'pveScsiHwSelector': { - change: 'scsihwChange' - }, - 'pveQemuBiosSelector': { - change: 'biosChange' - } - } - }, - - column1: [ - { - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - name: 'vga', - comboItems: PVE.Utils.kvm_vga_driver_array() - }, - { - xtype: 'proxmoxcheckbox', - name: 'agent', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Qemu Agent') - } - ], - - column2: [ - { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - bind: { - value: '{current.scsihw}' - }, - fieldLabel: gettext('SCSI Controller') - } - ], - - advancedColumn1: [ - { - xtype: 'pveQemuBiosSelector', - name: 'bios', - value: '__default__', - fieldLabel: 'BIOS' - }, - { - xtype: 'proxmoxcheckbox', - bind: { - value: '{addefi}', - hidden: '{!efi}', - disabled: '{!efi}' - }, - hidden: true, - submitValue: false, - disabled: true, - fieldLabel: gettext('Add EFI Disk') - }, - { - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - bind: { - nodename: '{nodename}', - hidden: '{!efi}', - disabled: '{!efidisk}' - }, - autoSelect: false, - disabled: true, - hidden: true, - hideSize: true - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - } - ] - -}); -Ext.define('PVE.lxc.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveLxcSummary', - - scrollable: true, - bodyPadding: 5, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - if (!me.workspace) { - throw "no workspace specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var template = !!me.pveSelNode.data.template; - var rstore = me.statusStore; - - var width = template ? 1 : 0.5; - var items = [ - { - xtype: template ? 'pveTemplateStatusView' : 'pveGuestStatusView', - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - }, - itemId: 'gueststatus', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'pveNotesView', - maxHeight: 320, - itemId: 'notesview', - pveSelNode: me.pveSelNode, - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - } - } - ]; - - var rrdstore; - if (!template) { - - rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/rrddata", - model: 'pve-rrd-guest' - }); - - items.push( - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - pveSelNode: me.pveSelNode, - fields: ['cpu'], - fieldTitles: [gettext('CPU usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - pveSelNode: me.pveSelNode, - fields: ['maxmem', 'mem'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - pveSelNode: me.pveSelNode, - fields: ['netin','netout'], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Disk IO'), - pveSelNode: me.pveSelNode, - fields: ['diskread','diskwrite'], - store: rrdstore - } - ); - - } - - Ext.apply(me, { - tbar: [ '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: { - type: 'column' - }, - defaults: { - minHeight: 320, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: items - } - ] - }); - - me.callParent(); - if (!template) { - rrdstore.startUpdate(); - me.on('destroy', rrdstore.stopUpdate); - } - } -}); -Ext.define('PVE.lxc.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcNetworkInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_network', - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - var bridgesel = me.query("[isFormField][name=bridge]")[0]; - bridgesel.setNodename(nodename); - }, - - onGetValues: function(values) { - var me = this; - - var id; - if (me.isCreate) { - id = values.id; - delete values.id; - } else { - id = me.ifname; - } - - if (!id) { - return {}; - } - - var newdata = {}; - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - newdata[id] = PVE.Parser.printLxcNetwork(values); - return newdata; - }, - - initComponent : function() { - var me = this; - - var cdata = {}; - - if (me.insideWizard) { - me.ifname = 'net0'; - cdata.name = 'eth0'; - me.dataCache = {}; - } - cdata.firewall = (me.insideWizard || me.isCreate); - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.isCreate) { - if (!me.ifname) { - throw "no interface name specified"; - } - if (!me.dataCache[me.ifname]) { - throw "no such interface '" + me.ifname + "'"; - } - - cdata = PVE.Parser.parseLxcNetwork(me.dataCache[me.ifname]); - } - - var i; - for (i = 0; i < 10; i++) { - if (me.isCreate && !me.dataCache['net'+i.toString()]) { - me.ifname = 'net' + i.toString(); - break; - } - } - - var idselector = { - xtype: 'hidden', - name: 'id', - value: me.ifname - }; - - me.column1 = [ - idselector, - { - xtype: 'textfield', - name: 'name', - fieldLabel: gettext('Name'), - emptyText: '(e.g., eth0)', - allowBlank: false, - value: cdata.name, - validator: function(value) { - var result = ''; - Ext.Object.each(me.dataCache, function(key, netstr) { - if (!key.match(/^net\d+/) || key === me.ifname) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(netstr); - if (net.name === value) { - result = "interface name already in use"; - return false; - } - }); - if (result !== '') { - return result; - } - // validator can return bool/string - /*jslint confusion:true*/ - return true; - } - }, - { - xtype: 'textfield', - name: 'hwaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - value: cdata.hwaddr, - allowBlank: true, - emptyText: 'auto' - }, - { - xtype: 'PVE.form.BridgeSelector', - name: 'bridge', - nodename: me.nodename, - fieldLabel: gettext('Bridge'), - value: cdata.bridge, - allowBlank: false - }, - { - xtype: 'pveVlanField', - name: 'tag', - value: cdata.tag - }, - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: cdata.rate, - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - value: cdata.firewall - } - ]; - - var dhcp4 = (cdata.ip === 'dhcp'); - if (dhcp4) { - cdata.ip = ''; - cdata.gw = ''; - } - - var auto6 = (cdata.ip6 === 'auto'); - var dhcp6 = (cdata.ip6 === 'dhcp'); - if (auto6 || dhcp6) { - cdata.ip6 = ''; - cdata.gw6 = ''; - } - - me.column2 = [ - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv4:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: !dhcp4, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv4mode', - inputValue: 'dhcp', - checked: dhcp4, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: cdata.ip, - disabled: dhcp4, - fieldLabel: 'IPv4/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw', - value: cdata.gw, - vtype: 'IPAddress', - disabled: dhcp4, - fieldLabel: gettext('Gateway') + ' (IPv4)', - margin: '0 0 3 0' // override bottom margin to account for the menuseparator - }, - { - xtype: 'menuseparator', - height: '3', - margin: '0' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv6:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: !(auto6 || dhcp6), - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv6mode', - inputValue: 'dhcp', - checked: dhcp6, - margin: '0 0 0 10' - }, - { - xtype: 'radiofield', - boxLabel: 'SLAAC', // do not localize - name: 'ipv6mode', - inputValue: 'auto', - checked: auto6, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: cdata.ip6, - vtype: 'IP6CIDRAddress', - disabled: (dhcp6 || auto6), - fieldLabel: 'IPv6/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: cdata.gw6, - disabled: (dhcp6 || auto6), - fieldLabel: gettext('Gateway') + ' (IPv6)' - } - ]; - - me.callParent(); - } -}); - - -Ext.define('PVE.lxc.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - var me = this; - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.nodename) { - throw "no node name specified"; - } - - var ipanel = Ext.create('PVE.lxc.NetworkInputPanel', { - ifname: me.ifname, - nodename: me.nodename, - dataCache: me.dataCache, - isCreate: me.isCreate - }); - - Ext.apply(me, { - subject: gettext('Network Device') + ' (veth)', - digest: me.dataCache.digest, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.NetworkView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveLxcNetworkView', - - onlineHelp: 'pct_container_network', - - dataCache: {}, // used to store result of last load - - stateful: true, - stateId: 'grid-lxc-network', - - load: function() { - var me = this; - - Proxmox.Utils.setErrorMask(me, true); - - Proxmox.Utils.API2Request({ - url: me.url, - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, gettext('Error') + ': ' + response.htmlStatus); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var result = Ext.decode(response.responseText); - var data = result.data || {}; - me.dataCache = data; - var records = []; - Ext.Object.each(data, function(key, value) { - if (!key.match(/^net\d+/)) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(value); - net.id = key; - records.push(net); - }); - me.store.loadData(records); - me.down('button[name=addButton]').setDisabled((records.length >= 10)); - } - }); - }, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.url = '/nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var store = new Ext.data.Store({ - model: 'pve-lxc-network', - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!caps.vms['VM.Config.Network']; - }, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - method: 'PUT', - params: { 'delete': rec.data.id, digest: me.dataCache.digest }, - callback: function() { - me.load(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (!caps.vms['VM.Config.Network']) { - return false; - } - - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - dataCache: me.dataCache, - ifname: rec.data.id - }); - win.on('destroy', me.load, me); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!caps.vms['VM.Config.Network']) { - return false; - } - return true; - }, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - name: 'addButton', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - isCreate: true, - dataCache: me.dataCache - }); - win.on('destroy', me.load, me); - win.show(); - } - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - width: 50, - dataIndex: 'id' - }, - { - header: gettext('Name'), - width: 80, - dataIndex: 'name' - }, - { - header: gettext('Bridge'), - width: 80, - dataIndex: 'bridge' - }, - { - header: gettext('Firewall'), - width: 80, - dataIndex: 'firewall', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('VLAN Tag'), - width: 80, - dataIndex: 'tag' - }, - { - header: gettext('MAC address'), - width: 110, - dataIndex: 'hwaddr' - }, - { - header: gettext('IP address'), - width: 150, - dataIndex: 'ip', - renderer: function(value, metaData, rec) { - if (rec.data.ip && rec.data.ip6) { - return rec.data.ip + "
" + rec.data.ip6; - } else if (rec.data.ip6) { - return rec.data.ip6; - } else { - return rec.data.ip; - } - } - }, - { - header: gettext('Gateway'), - width: 150, - dataIndex: 'gw', - renderer: function(value, metaData, rec) { - if (rec.data.gw && rec.data.gw6) { - return rec.data.gw + "
" + rec.data.gw6; - } else if (rec.data.gw6) { - return rec.data.gw6; - } else { - return rec.data.gw; - } - } - } - ], - listeners: { - activate: me.load, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-lxc-network', { - extend: "Ext.data.Model", - proxy: { type: 'memory' }, - fields: [ 'id', 'name', 'hwaddr', 'bridge', - 'ip', 'gw', 'ip6', 'gw6', 'tag', 'firewall' ] - }); - -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.RessourceView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcRessourceView'], - - onlineHelp: 'pct_configuration', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rowdef = me.rows[key] || {}; - - metaData.tdAttr = "valign=middle"; - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - } - return rowdef.header || key; - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - var mpeditor = caps.vms['VM.Config.Disk'] ? 'PVE.lxc.MountPointEdit' : undefined; - - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-memory', - group: 1, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - swap: { - header: gettext('Swap'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-swap', - group: 2, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - cores: { - header: gettext('Cores'), - editor: caps.vms['VM.Config.CPU'] ? 'PVE.lxc.CPUEdit' : undefined, - defaultValue: '', - tdCls: 'pve-itype-icon-processor', - group: 3, - renderer: function(value) { - var cpulimit = me.getObjectValue('cpulimit'); - var cpuunits = me.getObjectValue('cpuunits'); - var res; - if (value) { - res = value; - } else { - res = gettext('unlimited'); - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit + ']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits + ']'; - } - return res; - } - }, - rootfs: { - header: gettext('Root Disk'), - defaultValue: Proxmox.Utils.noneText, - editor: mpeditor, - tdCls: 'pve-itype-icon-storage', - group: 4 - }, - cpulimit: { - visible: false - }, - cpuunits: { - visible: false - }, - unprivileged: { - visible: false - } - }; - - PVE.Utils.forEachMP(function(bus, i) { - confid = bus + i; - var group = 5; - var header; - if (bus === 'mp') { - header = gettext('Mount Point') + ' (' + confid + ')'; - } else { - header = gettext('Unused Disk') + ' ' + i; - group += 1; - } - rows[confid] = { - group: group, - order: i, - tdCls: 'pve-itype-icon-storage', - editor: mpeditor, - header: header - }; - }, true); - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - var run_resize = function() { - var rec = me.selModel.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.MPResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - }; - - var run_remove = function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'delete': rec.data.key - }, - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var run_move = function(b, e, rec) { - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid, - type: 'lxc' - }); - - win.show(); - - win.on('destroy', me.reload, me); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: me.selModel, - disabled: true, - enableFn: function(rec) { - if (!rec) { - return false; - } - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: me.selModel, - disabled: true, - handler: run_resize - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - selModel: me.selModel, - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + me.renderKey(rec.data.key, {}, rec) + "'"); - if (rec.data.key.match(/^unused\d+$/)) { - msg += " " + gettext('This will permanently erase all data.'); - } - - return msg; - }, - handler: run_remove - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move Volume'), - selModel: me.selModel, - disabled: true, - dangerous: true, - handler: run_move - }); - - var set_button_status = function() { - var rec = me.selModel.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - remove_btn.disable(); - resize_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var isDisk = (rowdef.tdCls == 'pve-itype-icon-storage'); - - var noedit = rec.data['delete'] || !rowdef.editor; - if (!noedit && Proxmox.UserName !== 'root@pam' && key.match(/^mp\d+$/)) { - var mp = PVE.Parser.parseLxcMountPoint(value); - if (mp.type !== 'volume') { - noedit = true; - } - } - edit_btn.setDisabled(noedit); - - remove_btn.setDisabled(!isDisk || rec.data.key === 'rootfs' || !diskCap); - resize_btn.setDisabled(!isDisk || !diskCap); - move_btn.setDisabled(!isDisk || !diskCap); - - }; - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - Ext.apply(me, { - url: '/api2/json/' + baseurl, - selModel: me.selModel, - interval: 2000, - cwidth1: 170, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Mount Point'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.lxc.MountPointEdit', { - url: '/api2/extjs/' + baseurl, - unprivileged: me.getObjectValue('unprivileged'), - pveSelNode: me.pveSelNode - }); - win.show(); - } - } - ] - }) - }, - edit_btn, - remove_btn, - resize_btn, - move_btn - ], - rows: rows, - sorterFn: sorterFn, - editorConfig: { - pveSelNode: me.pveSelNode, - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - Ext.apply(me.editorConfig, { unprivileged: me.getObjectValue('unprivileged') }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.FeaturesInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcFeaturesInputPanel', - - // used to save the mounts fstypes until sending - mounts: [], - - fstypes: ['nfs', 'cifs'], - - viewModel: { - parent: null, - data: { - unprivileged: false - }, - formulas: { - privilegedOnly: function(get) { - return (get('unprivileged') ? gettext('privileged only') : ''); - }, - unprivilegedOnly: function(get) { - return (!get('unprivileged') ? gettext('unprivileged only') : ''); - } - } - }, - - items: [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('keyctl'), - name: 'keyctl', - bind: { - disabled: '{!unprivileged}', - boxLabel: '{unprivilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Nesting'), - name: 'nesting' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nfs', - fieldLabel: 'NFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'cifs', - fieldLabel: 'CIFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'fuse', - fieldLabel: 'FUSE' - } - ], - - onGetValues: function(values) { - var me = this; - var mounts = me.mounts; - me.fstypes.forEach(function(fs) { - if (values[fs]) { - mounts.push(fs); - } - delete values[fs]; - }); - - if (mounts.length) { - values.mount = mounts.join(';'); - } - - var featuresstring = PVE.Parser.printPropertyString(values, undefined); - if (featuresstring == '') { - return { 'delete': 'features' }; - } - return { features: featuresstring }; - }, - - setValues: function(values) { - var me = this; - - me.viewModel.set({ unprivileged: values.unprivileged }); - - if (values.features) { - var res = PVE.Parser.parsePropertyString(values.features); - me.mounts = []; - if (res.mount) { - res.mount.split(/[; ]/).forEach(function(item) { - if (me.fstypes.indexOf(item) === -1) { - me.mounts.push(item); - } else { - res[item] = 1; - } - }); - } - this.callParent([res]); - } - } -}); - -Ext.define('PVE.lxc.FeaturesEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveLxcFeaturesEdit', - - subject: gettext('Features'), - - items: [{ - xtype: 'pveLxcFeaturesInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load(); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.lxc.Options', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcOptions'], - - onlineHelp: 'pct_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'pct_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - defaultValue: Proxmox.Utils.unknownText - }, - arch: { - header: gettext('Architecture'), - defaultValue: Proxmox.Utils.unknownText - }, - console: { - header: '/dev/console', - defaultValue: 1, - renderer: Proxmox.Utils.format_enabled_toggle, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: '/dev/console', - items: { - xtype: 'proxmoxcheckbox', - name: 'console', - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - checked: true, - fieldLabel: '/dev/console' - } - } : undefined - }, - tty: { - header: gettext('TTY count'), - defaultValue: 2, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('TTY count'), - items: { - xtype: 'proxmoxintegerfield', - name: 'tty', - minValue: 0, - maxValue: 6, - value: 2, - fieldLabel: gettext('TTY count'), - emptyText: gettext('Default'), - deleteEmpty: true - } - } : undefined - }, - cmode: { - header: gettext('Console mode'), - defaultValue: 'tty', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Console mode'), - items: { - xtype: 'proxmoxKVComboBox', - name: 'cmode', - deleteEmpty: true, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (tty)"], - ['tty', "/dev/tty[X]"], - ['console', "/dev/console"], - ['shell', "shell"] - ], - fieldLabel: gettext('Console mode') - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - unprivileged: { - header: gettext('Unprivileged container'), - renderer: Proxmox.Utils.format_boolean, - defaultValue: 0 - }, - features: { - header: gettext('Features'), - defaultValue: Proxmox.Utils.noneText, - editor: Proxmox.UserName === 'root@pam' ? - 'PVE.lxc.FeaturesEdit' : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - Ext.apply(me, { - url: "/api2/json/" + baseurl, - selModel: sm, - interval: 5000, - tbar: [ edit_btn ], - rows: rows, - editorConfig: { - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - } -}); - -Ext.define('PVE.lxc.DNSInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcDNSInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var deletes = []; - if (!values.searchdomain && !me.insideWizard) { - deletes.push('searchdomain'); - } - - if (values.nameserver) { - var list = values.nameserver.split(/[\ \,\;]+/); - values.nameserver = list.join(' '); - } else if(!me.insideWizard) { - deletes.push('nameserver'); - } - - if (deletes.length) { - values['delete'] = deletes.join(','); - } - - return values; - }, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxtextfield', - name: 'searchdomain', - skipEmptyText: true, - fieldLabel: gettext('DNS domain'), - emptyText: gettext('use host settings'), - allowBlank: true - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS servers'), - vtype: 'IP64AddressList', - allowBlank: true, - emptyText: gettext('use host settings'), - name: 'nameserver', - itemId: 'nameserver' - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.DNSEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.lxc.DNSInputPanel'); - - Ext.apply(me, { - subject: gettext('Resources'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - if (values.nameserver) { - values.nameserver.replace(/[,;]/, ' '); - values.nameserver.replace(/^\s+/, ''); - } - - ipanel.setValues(values); - } - }); - } - } -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.DNS', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcDNS'], - - onlineHelp: 'pct_container_network', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - hostname: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Hostname'), - editor: caps.vms['VM.Config.Network'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hostname'), - items: { - xtype: 'inputpanel', - items:{ - fieldLabel: gettext('Hostname'), - xtype: 'textfield', - name: 'hostname', - vtype: 'DnsName', - allowBlank: true, - emptyText: 'CT' + vmid.toString() - }, - onGetValues: function(values) { - var params = values; - if (values.hostname === undefined || - values.hostname === null || - values.hostname === '') { - params = { hostname: 'CT'+vmid.toString()}; - } - return params; - } - } - } : undefined - }, - searchdomain: { - header: gettext('DNS domain'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - }, - nameserver: { - header: gettext('DNS server'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var reload = function() { - me.rstore.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - if (Ext.isString(rowdef.editor)) { - win = Ext.create(rowdef.editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - //win.load(); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: run_editor - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - var rowdef = rows[rec.data.key]; - edit_btn.setDisabled(!rowdef.editor); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/config", - selModel: sm, - cwidth1: 150, - run_editor: run_editor, - tbar: [ edit_btn ], - rows: rows, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status, - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.lxc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.lxc.Config', - - onlineHelp: 'chapter_pct', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!me.pveSelNode.data.template; - - var running = !!me.pveSelNode.data.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + '/lxc/' + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + "/status/" + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var stopBtn = Ext.create('Ext.menu.Item',{ - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('vzstop', vmid), - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - dangerous: true, - handler: function() { - vm_command("stop"); - }, - iconCls: 'fa fa-stop' - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('vzshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items:[stopBtn] - }, - iconCls: 'fa fa-power-off' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'lxc'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('vztemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = me.pveSelNode.data.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - guestType: 'ct', - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - disabled: !caps.vms['VM.Allocate'], - itemId: 'removeBtn', - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'CT', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var vm = me.pveSelNode.data; - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - consoleType: 'lxc', - consoleName: vm.name, - hidden: template, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - - Ext.apply(me, { - title: Ext.String.format(gettext("Container {0} on node '{1}'"), vm.text, nodename), - hstateid: 'lxctab', - tbarSpacing: false, - tbar: [ statusTxt, '->', startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveLxcSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push( - { - title: gettext('Console'), - itemId: 'consolejs', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'lxc', - xtermjs: true, - nodename: nodename - } - ); - } - - me.items.push( - { - title: gettext('Resources'), - itemId: 'resources', - expandedOnInit: true, - iconCls: 'fa fa-cube', - xtype: 'pveLxcRessourceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - xtype: 'pveLxcNetworkView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - itemId: 'dns', - xtype: 'pveLxcDNS' - }, - { - title: gettext('Options'), - itemId: 'options', - iconCls: 'fa fa-gear', - xtype: 'pveLxcOptions' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - iconCls: 'fa fa-list', - xtype: 'proxmoxNodeTasks', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - xtype: 'pveLxcSnapshotTree', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - itemId: 'permissions', - iconCls: 'fa fa-unlock', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var lock; - if (!success) { - status = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - } - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - stopBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'stopped'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.CreateWizard', { - extend: 'PVE.window.Wizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - storage: '', - unprivileged: true - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('LXC Container'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'pct_general', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', // backend only knows vmid - guestType: 'lxc', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'proxmoxtextfield', - name: 'hostname', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Hostname'), - skipEmptyText: true, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - bind: { - value: '{unprivileged}' - }, - fieldLabel: gettext('Unprivileged container') - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'password', - value: '', - fieldLabel: gettext('Password'), - allowBlank: false, - minLength: 5, - change: function(f, value) { - if (f.rendered) { - f.up().down('field[name=confirmpw]').validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'confirmpw', - value: '', - fieldLabel: gettext('Confirm password'), - allowBlank: true, - submitValue: false, - validator: function(value) { - var pw = this.up().down('field[name=password]').getValue(); - if (pw !== value) { - return "Passwords do not match!"; - } - return true; - } - }, - { - xtype: 'proxmoxtextfield', - name: 'ssh-public-keys', - value: '', - fieldLabel: gettext('SSH public key'), - allowBlank: true, - validator: function(value) { - var pwfield = this.up().down('field[name=password]'); - if (value.length) { - var key = PVE.Parser.parseSSHKey(value); - if (!key) { - return "Failed to recognize ssh key"; - } - pwfield.allowBlank = true; - } else { - pwfield.allowBlank = false; - } - pwfield.validate(); - return true; - }, - afterRender: function() { - if (!window.FileReader) { - // No FileReader support in this browser - return; - } - var cancel = function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - }; - var field = this; - field.inputEl.on('dragover', cancel); - field.inputEl.on('dragenter', cancel); - field.inputEl.on('drop', function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - var files = ev.dataTransfer.files; - PVE.Utils.loadSSHKeyFromFile(files[0], function(v) { - field.setValue(v); - }); - }); - } - }, - { - xtype: 'filebutton', - name: 'file', - hidden: !window.FileReader, - text: gettext('Load SSH Key File'), - listeners: { - change: function(btn, e, value) { - e = e.event; - var field = this.up().down('proxmoxtextfield[name=ssh-public-keys]'); - PVE.Utils.loadSSHKeyFromFile(e.target.files[0], function(v) { - field.setValue(v); - }); - btn.reset(); - } - } - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Template'), - onlineHelp: 'pct_container_images', - column1: [ - { - xtype: 'pveStorageSelector', - name: 'tmplstorage', - fieldLabel: gettext('Storage'), - storageContent: 'vztmpl', - autoSelect: true, - allowBlank: false, - bind: { - value: '{storage}', - nodename: '{nodename}' - } - }, - { - xtype: 'pveFileSelector', - name: 'ostemplate', - storageContent: 'vztmpl', - fieldLabel: gettext('Template'), - bind: { - storage: '{storage}', - nodename: '{nodename}' - }, - allowBlank: false - } - ] - }, - { - xtype: 'pveLxcMountPointInputPanel', - title: gettext('Root Disk'), - insideWizard: true, - isCreate: true, - unused: false, - bind: { - nodename: '{nodename}', - unprivileged: '{unprivileged}' - }, - confid: 'rootfs' - }, - { - xtype: 'pveLxcCPUInputPanel', - title: gettext('CPU'), - insideWizard: true - }, - { - xtype: 'pveLxcMemoryInputPanel', - title: gettext('Memory'), - insideWizard: true - }, - { - xtype: 'pveLxcNetworkInputPanel', - title: gettext('Network'), - insideWizard: true, - bind: { - nodename: '{nodename}' - }, - isCreate: true - }, - { - xtype: 'pveLxcDNSInputPanel', - title: gettext('DNS'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var wizard = this.up('window'); - var kv = wizard.getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete' || key === 'tmplstorage') { // ignore - return; - } - if (key === 'password') { // don't show pw - return; - } - var html = Ext.htmlEncode(Ext.JSON.encode(value)); - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - delete kv.tmplstorage; - - if (!kv.pool.length) { - delete kv.pool; - } - - if (!kv.password.length && kv['ssh-public-keys']) { - delete kv.password; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response, opts){ - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - -Ext.define('PVE.lxc.SnapshotTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveLxcSnapshotTree'], - - onlineHelp: 'pct_snapshots', - - load_delay: 3000, - - old_digest: 'invalid', - - stateful: true, - stateId: 'grid-lxc-snapshots', - - sorterFn: function(rec1, rec2) { - var v1 = rec1.data.snaptime; - var v2 = rec2.data.snaptime; - - if (rec1.data.name === 'current') { - return 1; - } - if (rec2.data.name === 'current') { - return -1; - } - - return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); - }, - - reload: function(repeat) { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot', - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - me.load_task.delay(me.load_delay); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var digest = 'invalid'; - var idhash = {}; - var root = { name: '__root', expanded: true, children: [] }; - Ext.Array.each(response.result.data, function(item) { - item.leaf = true; - item.children = []; - if (item.name === 'current') { - digest = item.digest + item.running; - if (item.running) { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running'; - } else { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree'; - } - } else { - item.iconCls = 'fa fa-fw fa-history x-fa-tree'; - } - idhash[item.name] = item; - }); - - if (digest !== me.old_digest) { - me.old_digest = digest; - - Ext.Array.each(response.result.data, function(item) { - if (item.parent && idhash[item.parent]) { - var parent_item = idhash[item.parent]; - parent_item.children.push(item); - parent_item.leaf = false; - parent_item.expanded = true; - parent_item.expandable = false; - } else { - root.children.push(item); - } - }); - - me.setRootNode(root); - } - - me.load_task.delay(me.load_delay); - } - }); - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/feature', - params: { feature: 'snapshot' }, - method: 'GET', - success: function(response, options) { - var res = response.result.data; - if (res.hasFeature) { - var snpBtns = Ext.ComponentQuery.query('#snapshotBtn'); - snpBtns.forEach(function(item){ - item.enable(); - }); - } - } - }); - - - }, - - listeners: { - beforestatesave: function(grid, state, eopts) { - // extjs cannot serialize functions, - // so a the sorter with only the sorterFn will - // not be a valid sorter when restoring the state - delete state.storeState.sorters; - } - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.vmid = me.pveSelNode.data.vmid; - if (!me.vmid) { - throw "no VM ID specified"; - } - - me.load_task = new Ext.util.DelayedTask(me.reload, me); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var valid_snapshot = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current'; - }; - - var valid_snapshot_rollback = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current' && !record.data.snapstate; - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (valid_snapshot(rec)) { - var win = Ext.create('PVE.window.LxcSnapshot', { - snapname: rec.data.name, - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - me.mon(win, 'close', me.reload, me); - } - }; - - var editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot, - handler: run_editor - }); - - var rollbackBtn = new Proxmox.button.Button({ - text: gettext('Rollback'), - disabled: true, - dangerous: true, - selModel: sm, - enableFn: valid_snapshot_rollback, - confirmMsg: function(rec) { - var taskdescription = Proxmox.Utils.format_task_description('vzrollback', me.vmid); - var snaptime = Ext.Date.format(rec.data.snaptime,'Y-m-d H:i:s'); - var snapname = rec.data.name; - - var msg = Ext.String.format(gettext('{0} to {1} ({2})'), - taskdescription, snapname, snaptime); - msg += '

' + gettext('Note: Rollback stops CT') + '

'; - - return msg; - }, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname + '/rollback', - method: 'POST', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var removeBtn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.name + "'"); - return msg; - }, - enableFn: valid_snapshot, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var snapshotBtn = Ext.create('Ext.Button', { - itemId: 'snapshotBtn', - text: gettext('Take Snapshot'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.window.LxcSnapshot', { - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - } - }); - - Ext.apply(me, { - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - selModel: sm, - tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ], - fields: [ - 'name', 'description', 'snapstate', 'vmstate', 'running', - { name: 'snaptime', type: 'date', dateFormat: 'timestamp' } - ], - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - width: 200, - renderer: function(value, metaData, record) { - if (value === 'current') { - return "NOW"; - } else { - return value; - } - } - }, -// { -// text: gettext('RAM'), -// align: 'center', -// resizable: false, -// dataIndex: 'vmstate', -// width: 50, -// renderer: function(value, metaData, record) { -// if (record.data.name !== 'current') { -// return Proxmox.Utils.format_boolean(value); -// } -// } -// }, - { - text: gettext('Date') + "/" + gettext("Status"), - dataIndex: 'snaptime', - resizable: false, - width: 150, - renderer: function(value, metaData, record) { - if (record.data.snapstate) { - return record.data.snapstate; - } - if (value) { - return Ext.Date.format(value,'Y-m-d H:i:s'); - } - } - }, - { - text: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value, metaData, record) { - if (record.data.name === 'current') { - return gettext("You are here!"); - } else { - return Ext.String.htmlEncode(value); - } - } - } - ], - columnLines: true, - listeners: { - activate: me.reload, - destroy: me.load_task.cancel, - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.store.sorters.add(new Ext.util.Sorter({ - sorterFn: me.sorterFn - })); - } -}); -Ext.define('PVE.window.LxcSnapshot', { - extend: 'Ext.window.Window', - - resizable: false, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - defaultFocus: 'field', - - take_snapshot: function(snapname, descr, vmstate) { - var me = this; - var params = { snapname: snapname }; - if (descr) { - params.description = descr; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot", - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - update_snapshot: function(snapname, descr) { - var me = this; - Proxmox.Utils.API2Request({ - params: { description: descr }, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot/" + - snapname + '/config', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var summarystore = Ext.create('Ext.data.Store', { - model: 'KeyValue', - sorters: [ - { - property : 'key', - direction: 'ASC' - } - ] - }); - - var items = [ - { - xtype: me.snapname ? 'displayfield' : 'textfield', - name: 'snapname', - value: me.snapname, - fieldLabel: gettext('Name'), - vtype: 'ConfigId', - allowBlank: false - } - ]; - - if (me.snapname) { - items.push({ - xtype: 'displayfield', - name: 'snaptime', - renderer: PVE.Utils.render_timestamp_human_readable, - fieldLabel: gettext('Timestamp') - }); - } - - items.push({ - xtype: 'textareafield', - grow: true, - name: 'description', - fieldLabel: gettext('Description') - }); - - if (me.snapname) { - items.push({ - title: gettext('Settings'), - xtype: 'grid', - height: 200, - store: summarystore, - columns: [ - {header: gettext('Key'), width: 150, dataIndex: 'key'}, - {header: gettext('Value'), flex: 1, dataIndex: 'value'} - ] - }); - } - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - if (me.snapname) { - me.title = gettext('Edit') + ': ' + gettext('Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Update'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.update_snapshot(me.snapname, values.description); - } - } - }); - } else { - me.title ="VM " + me.vmid + ': ' + gettext('Take Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Take Snapshot'), - reference: 'submitbutton', - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.take_snapshot(values.snapname, values.description); - } - } - }); - } - - Ext.apply(me, { - modal: true, - width: 450, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - if (me.snapname) { - Ext.apply(me, { - width: 620, - height: 420 - }); - } - - me.callParent(); - - if (!me.snapname) { - return; - } - - // else load data - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot/" + - me.snapname + '/config', - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.close(); - }, - success: function(response, options) { - var data = response.result.data; - var kvarray = []; - Ext.Object.each(data, function(key, value) { - if (key === 'description' || key === 'snaptime') { - return; - } - kvarray.push({ key: key, value: value }); - }); - - summarystore.suspendEvents(); - summarystore.add(kvarray); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh', summarystore); - - form.findField('snaptime').setValue(data.snaptime); - form.findField('description').setValue(data.description); - } - }); - } -}); -/*jslint confusion: true */ -var labelWidth = 120; - -Ext.define('PVE.lxc.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('Memory'), - items: Ext.create('PVE.lxc.MemoryInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - - -Ext.define('PVE.lxc.CPUEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('CPU'), - items: Ext.create('PVE.lxc.CPUInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.lxc.CPUInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcCPUInputPanel', - - onlineHelp: 'pct_cpu', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - PVE.Utils.delete_if_default(values, 'cores', '', me.insideWizard); - // cpu{limit,unit} aren't in the wizard so create is always false - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - return values; - }, - - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - value: 1024, - minValue: 8, - maxValue: 500000, - labelWidth: labelWidth, - allowBlank: false - } - ], - - initComponent: function() { - var me = this; - - me.column1 = [ - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: me.insideWizard ? 1 : '', - fieldLabel: gettext('Cores'), - allowBlank: true, - deleteEmpty: true, - emptyText: gettext('unlimited') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcMemoryInputPanel', - - onlineHelp: 'pct_memory', - - insideWizard: false, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxintegerfield', - name: 'memory', - minValue: 16, - value: '512', - step: 32, - fieldLabel: gettext('Memory') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'swap', - minValue: 0, - value: '512', - step: 32, - fieldLabel: gettext('Swap') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); -Ext.define('PVE.window.MPResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 120, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -/*jslint confusion: true*/ -/* hidden: boolean and string - * bind: function and object - * disabled: boolean and string - */ -Ext.define('PVE.lxc.MountPointInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcMountPointInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_storage', - - unused: false, // add unused disk imaged - - unprivileged: false, - - vmconfig: {}, // used to select unused disks - - setUnprivileged: function(unprivileged) { - var me = this; - var vm = me.getViewModel(); - me.unprivileged = unprivileged; - vm.set('unpriv', unprivileged); - }, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || "mp"+values.mpid; - values.file = me.down('field[name=file]').getValue(); - - if (me.unused) { - confid = "mp"+values.mpid; - } else if (me.isCreate) { - values.file = values.hdstorage + ':' + values.disksize; - } - - // delete unnecessary fields - delete values.mpid; - delete values.hdstorage; - delete values.disksize; - delete values.diskformat; - - var res = {}; - res[confid] = PVE.Parser.printLxcMountPoint(values); - return res; - }, - - - setMountPoint: function(mp) { - var me = this; - var vm = this.getViewModel(); - vm.set('mptype', mp.type); - me.setValues(mp); - }, - - setVMConfig: function(vmconfig) { - var me = this; - var vm = me.getViewModel(); - me.vmconfig = vmconfig; - vm.set('unpriv', vmconfig.unprivileged); - vm.notify(); - - PVE.Utils.forEachMP(function(bus, i) { - var name = "mp" + i.toString(); - if (!Ext.isDefined(vmconfig[name])) { - me.down('field[name=mpid]').setValue(i); - return false; - } - }); - }, - - setNodename: function(nodename) { - var me = this; - var vm = me.getViewModel(); - vm.set('node', nodename); - vm.notify(); - me.down('#diskstorage').setNodename(nodename); - }, - - controller: { - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=mpid]': { - change: function(field, value) { - field.validate(); - } - }, - '#hdstorage': { - change: function(field, newValue) { - var me = this; - if (!newValue) { - return; - } - - var rec = field.store.getById(newValue); - if (!rec) { - return; - } - - var vm = me.getViewModel(); - vm.set('type', rec.data.type); - vm.notify(); - } - } - }, - - init: function(view) { - var me = this; - var vm = this.getViewModel(); - vm.set('confid', view.confid); - vm.set('unused', view.unused); - vm.set('node', view.nodename); - vm.set('unpriv', view.unprivileged); - vm.set('hideStorSelector', view.unused || !view.isCreate); - vm.notify(); - } - }, - - viewModel: { - data: { - unpriv: false, - unused: false, - showStorageSelector: false, - mptype: '', - type: '', - confid: '', - node: '' - }, - - formulas: { - quota: function(get) { - return !(get('type') === 'zfs' || - get('type') === 'zfspool' || - get('unpriv') || - get('isBind')); - }, - hasMP: function(get) { - return !!get('confid') && !get('unused'); - }, - isRoot: function(get) { - return get('confid') === 'rootfs'; - }, - isBind: function(get) { - return get('mptype') === 'bind'; - }, - isBindOrRoot: function(get) { - return get('isBind') || get('isRoot'); - } - } - }, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'mpid', - fieldLabel: gettext('Mount Point ID'), - minValue: 0, - maxValue: PVE.Utils.mp_counts.mps - 1, - hidden: true, - allowBlank: false, - disabled: true, - bind: { - hidden: '{hasMP}', - disabled: '{hasMP}' - }, - validator: function(value) { - var me = this.up('inputpanel'); - if (!me.rendered) { - return; - } - if (Ext.isDefined(me.vmconfig["mp"+value])) { - return "Mount point is already in use."; - } - /*jslint confusion: true*/ - /* returns a string above */ - return true; - } - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'diskstorage', - storageContent: 'rootdir', - hidden: true, - autoSelect: true, - selectformat: false, - defaultSize: 8, - bind: { - hidden: '{hideStorSelector}', - disabled: '{hideStorSelector}', - nodename: '{node}' - } - }, - { - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'file', - bind: { - hidden: '{!hideStorSelector}' - } - } - ], - - column2: [ - { - xtype: 'textfield', - name: 'mp', - value: '', - emptyText: gettext('/some/path'), - allowBlank: false, - disabled: true, - fieldLabel: gettext('Path'), - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'backup', - fieldLabel: gettext('Backup'), - bind: { - hidden: '{isRoot}', - disabled: '{isBindOrRoot}' - } - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'quota', - defaultValue: 0, - bind: { - disabled: '{!quota}' - }, - fieldLabel: gettext('Enable quota'), - listeners: { - disable: function() { - this.reset(); - } - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'ro', - defaultValue: 0, - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - }, - fieldLabel: gettext('Read-only') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'acl', - fieldLabel: 'ACLs', - deleteEmpty: false, - comboItems: [ - ['__default__', Proxmox.Utils.defaultText], - ['1', Proxmox.Utils.enabledText], - ['0', Proxmox.Utils.disabledText] - ], - value: '__default__', - bind: { - disabled: '{isBind}' - }, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - inputValue: '0', // reverses the logic - name: 'replicate', - fieldLabel: gettext('Skip replication') - } - ] -}); - -Ext.define('PVE.lxc.MountPointEdit', { - extend: 'Proxmox.window.Edit', - - unprivileged: false, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.lxc.MountPointInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - unprivileged: me.unprivileged, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - subject = gettext('Unused Disk'); - } else if (me.isCreate) { - subject = gettext('Mount Point'); - } else { - subject = gettext('Mount Point') + ' (' + me.confid + ')'; - } - - Ext.apply(me, { - subject: subject, - defaultFocus: me.confid !== 'rootfs' ? 'textfield[name=mp]' : 'tool', - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - /*jslint confusion: true*/ - /*data is defined as array above*/ - var value = response.result.data[me.confid]; - /*jslint confusion: false*/ - var mp = PVE.Parser.parseLxcMountPoint(value); - - if (!mp) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse mount point options'); - me.close(); - return; - } - - ipanel.setMountPoint(mp); - me.isValid(); // trigger validation - } - } - }); - } -}); -Ext.define('PVE.pool.StatusView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pvePoolStatusView'], - disabled: true, - - title: gettext('Status'), - cwidth1: 150, - interval: 30000, - //height: 195, - initComponent : function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var rows = { - comment: { - header: gettext('Comment'), - renderer: Ext.String.htmlEncode, - required: true - } - }; - - Ext.apply(me, { - url: "/api2/json/pools/" + pool, - rows: rows - }); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePoolSummary', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var statusview = Ext.create('PVE.pool.StatusView', { - pveSelNode: me.pveSelNode, - style: 'padding-top:0px' - }); - - var rstore = statusview.rstore; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - defaults: { - style: 'padding-top:10px', - width: 800 - }, - items: [ statusview ] - }); - - me.on('activate', rstore.startUpdate); - me.on('destroy', rstore.stopUpdate); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.pvePoolConfig', - - onlineHelp: 'pveum_pools', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - Ext.apply(me, { - title: Ext.String.format(gettext("Resource Pool") + ': ' + pool), - hstateid: 'pooltab', - items: [ - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - xtype: 'pvePoolSummary', - itemId: 'summary' - }, - { - title: gettext('Members'), - xtype: 'pvePoolMembers', - iconCls: 'fa fa-th', - pool: pool, - itemId: 'members' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/pool/' + pool - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.panel.StorageBase', { - extend: 'Proxmox.panel.InputPanel', - controller: 'storageEdit', - - type: '', - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = me.type; - } else { - delete values.storage; - } - - values.disable = values.enable ? 0 : 1; - delete values.enable; - - return values; - }, - - initComponent : function() { - var me = this; - - me.column1.unshift({ - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'storage', - value: me.storageId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }); - - me.column2.unshift( - { - xtype: 'pveNodeSelector', - name: 'nodes', - disabled: me.storageId === 'local', - fieldLabel: gettext('Nodes'), - emptyText: gettext('All') + ' (' + gettext('No restrictions') +')', - multiSelect: true, - autoSelect: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - me.isCreate = !me.storageId; - - if (me.isCreate) { - me.url = '/api2/extjs/storage'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/storage/' + me.storageId; - me.method = 'PUT'; - } - - var ipanel = Ext.create(me.paneltype, { - type: me.type, - isCreate: me.isCreate, - storageId: me.storageId - }); - - Ext.apply(me, { - subject: PVE.Utils.format_storage_type(me.type), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - var ctypes = values.content || ''; - - values.content = ctypes.split(','); - - if (values.nodes) { - values.nodes = values.nodes.split(','); - } - values.enable = values.disable ? 0 : 1; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.grid.TemplateSelector', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveTemplateSelector', - - stateful: true, - stateId: 'grid-template-selector', - viewConfig: { - trackOver: false - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/aplinfo"; - var store = new Ext.data.Store({ - model: 'pve-aplinfo', - groupField: 'section', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{[ "Section: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - '->', - gettext('Search'), - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - var value = field.getValue().toLowerCase(); - store.clearFilter(true); - store.filterBy(function(rec) { - return (rec.data['package'].toLowerCase().indexOf(value) !== -1) - || (rec.data.headline.toLowerCase().indexOf(value) !== -1); - }); - } - } - } - ], - features: [ groupingFeature ], - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Package'), - flex: 1, - dataIndex: 'package' - }, - { - header: gettext('Version'), - width: 80, - dataIndex: 'version' - }, - { - header: gettext('Description'), - flex: 1.5, - renderer: Ext.String.htmlEncode, - dataIndex: 'headline' - } - ], - listeners: { - afterRender: reload - } - }); - - me.callParent(); - } - -}, function() { - - Ext.define('pve-aplinfo', { - extend: 'Ext.data.Model', - fields: [ - 'template', 'type', 'package', 'version', 'headline', 'infopage', - 'description', 'os', 'section' - ], - idProperty: 'template' - }); - -}); - -Ext.define('PVE.storage.TemplateDownload', { - extend: 'Ext.window.Window', - alias: 'widget.pveTemplateDownload', - - modal: true, - title: gettext('Templates'), - layout: 'fit', - width: 900, - height: 600, - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var grid = Ext.create('PVE.grid.TemplateSelector', { - border: false, - scrollable: true, - nodename: me.nodename - }); - - var sm = grid.getSelectionModel(); - - var submitBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Download'), - disabled: true, - selModel: sm, - handler: function(button, event, rec) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/aplinfo', - params: { - storage: me.storage, - template: rec.data.template - }, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - destroy: me.reloadGrid - } - }).show(); - - me.close(); - } - }); - } - }); - - Ext.apply(me, { - items: grid, - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.Upload', { - extend: 'Ext.window.Window', - alias: 'widget.pveStorageUpload', - - resizable: false, - - modal: true, - - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var xhr; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/storage/" + me.storage + "/upload"; - - var pbar = Ext.create('Ext.ProgressBar', { - text: 'Ready', - hidden: true - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - method: 'POST', - waitMsgTarget: true, - bodyPadding: 10, - border: false, - width: 300, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - xtype: 'pveContentTypeSelector', - cts: me.contents, - fieldLabel: gettext('Content'), - name: 'content', - value: me.contents[0] || '', - allowBlank: false - }, - { - xtype: 'filefield', - name: 'filename', - buttonText: gettext('Select File...'), - allowBlank: false - }, - pbar - ] - }); - - var form = me.formPanel.getForm(); - - var doStandardSubmit = function() { - form.submit({ - url: "/api2/htmljs" + baseurl, - waitMsg: gettext('Uploading file...'), - success: function(f, action) { - me.close(); - }, - failure: function(f, action) { - var msg = PVE.Utils.extractFormActionError(action); - Ext.Msg.alert(gettext('Error'), msg); - } - }); - }; - - var updateProgress = function(per, bytes) { - var text = (per * 100).toFixed(2) + '%'; - if (bytes) { - text += " (" + Proxmox.Utils.format_size(bytes) + ')'; - } - pbar.updateProgress(per, text); - }; - - var abortBtn = Ext.create('Ext.Button', { - text: gettext('Abort'), - disabled: true, - handler: function() { - me.close(); - } - }); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Upload'), - disabled: true, - handler: function(button) { - var fd; - try { - fd = new FormData(); - } catch (err) { - doStandardSubmit(); - return; - } - - button.setDisabled(true); - abortBtn.setDisabled(false); - - var field = form.findField('content'); - fd.append("content", field.getValue()); - field.setDisabled(true); - - field = form.findField('filename'); - var file = field.fileInputEl.dom; - fd.append("filename", file.files[0]); - field.setDisabled(true); - - pbar.setVisible(true); - updateProgress(0); - - xhr = new XMLHttpRequest(); - - xhr.addEventListener("load", function(e) { - if (xhr.status == 200) { - me.close(); - } else { - var msg = gettext('Error') + " " + xhr.status.toString() + ": " + Ext.htmlEncode(xhr.statusText); - var result = Ext.decode(xhr.responseText); - result.message = msg; - var htmlStatus = Proxmox.Utils.extractRequestError(result, true); - Ext.Msg.alert(gettext('Error'), htmlStatus, function(btn) { - me.close(); - }); - - } - }, false); - - xhr.addEventListener("error", function(e) { - var msg = "Error " + e.target.status.toString() + " occurred while receiving the document."; - Ext.Msg.alert(gettext('Error'), msg, function(btn) { - me.close(); - }); - }); - - xhr.upload.addEventListener("progress", function(evt) { - if (evt.lengthComputable) { - var percentComplete = evt.loaded / evt.total; - updateProgress(percentComplete, evt.loaded); - } - }, false); - - xhr.open("POST", "/api2/json" + baseurl, true); - xhr.send(fd); - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - Ext.apply(me, { - title: gettext('Upload'), - items: me.formPanel, - buttons: [ abortBtn, submitBtn ], - listeners: { - close: function() { - if (xhr) { - xhr.abort(); - } - } - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ContentView', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveStorageContentView', - - stateful: true, - stateId: 'grid-storage-content', - viewConfig: { - trackOver: false, - loadMask: false - }, - features: [ - { - ftype: 'grouping', - groupHeaderTpl: '{name} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - } - ], - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + nodename + "/storage/" + storage + "/content"; - var store = Ext.create('Ext.data.Store',{ - model: 'pve-storage-content', - groupField: 'content', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - }, - sorters: { - property: 'volid', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - me.statusStore.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var templateButton = Ext.create('Proxmox.button.Button',{ - itemId: 'tmpl-btn', - text: gettext('Templates'), - handler: function() { - var win = Ext.create('PVE.storage.TemplateDownload', { - nodename: nodename, - storage: storage, - reloadGrid: reload - }); - win.show(); - } - }); - - var uploadButton = Ext.create('Proxmox.button.Button', { - contents : ['iso','vztmpl'], - text: gettext('Upload'), - handler: function() { - var me = this; - var win = Ext.create('PVE.storage.Upload', { - nodename: nodename, - storage: storage, - contents: me.contents - }); - win.show(); - win.on('destroy', reload); - } - }); - - var imageRemoveButton; - var removeButton = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - enableFn: function(rec) { - if (rec && rec.data.content !== 'images') { - imageRemoveButton.setVisible(false); - removeButton.setVisible(true); - return true; - } - return false; - }, - callback: function() { - reload(); - }, - baseurl: baseurl + '/' - }); - - imageRemoveButton = Ext.create('Proxmox.button.Button',{ - selModel: sm, - hidden: true, - text: gettext('Remove'), - enableFn: function(rec) { - if (rec && rec.data.content === 'images') { - removeButton.setVisible(false); - imageRemoveButton.setVisible(true); - return true; - } - return false; - }, - handler: function(btn, event, rec) { - me = this; - - var url = baseurl + '/' + rec.data.volid; - var vmid = rec.data.vmid; - - var store = PVE.data.ResourceStore; - - if (vmid && store.findVMID(vmid)) { - var guest_node = store.guestNode(vmid); - var storage_path = 'storage/' + nodename + '/' + storage; - - // allow to delete local backed images if a VMID exists on another node. - if (store.storageIsShared(storage_path) || guest_node == nodename) { - var msg = Ext.String.format( - gettext("Cannot remove image, a guest with VMID '{0}' exists!"), vmid); - msg += '
' + gettext("You can delete the image from the guest's hardware pane"); - - Ext.Msg.show({ - title: gettext('Cannot remove disk image.'), - icon: Ext.Msg.ERROR, - msg: msg - }); - return; - } - } - var win = Ext.create('PVE.window.SafeDestroy', { - title: Ext.String.format(gettext("Destroy '{0}'"), rec.data.volid), - showProgress: true, - url: url, - item: { type: 'Image', id: vmid } - }).show(); - win.on('destroy', function() { - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - reload(); - - }); - } - }); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - xtype: 'proxmoxButton', - text: gettext('Restore'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b, e, rec) { - var vmtype; - if (rec.data.volid.match(/vzdump-qemu-/)) { - vmtype = 'qemu'; - } else if (rec.data.volid.match(/vzdump-openvz-/) || rec.data.volid.match(/vzdump-lxc-/)) { - vmtype = 'lxc'; - } else { - return; - } - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }, - removeButton, - imageRemoveButton, - templateButton, - uploadButton, - { - xtype: 'proxmoxButton', - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b,e,rec) { - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }, - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - store.clearFilter(true); - store.filter([ - { - property: 'text', - value: field.getValue(), - anyMatch: true, - caseSensitive: false - } - ]); - } - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'text' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ], - listeners: { - activate: reload - } - }); - - me.callParent(); - - // disable the buttons/restrict the upload window - // if templates or uploads are not allowed - me.mon(me.statusStore, 'load', function(s,records,succes) { - var availcontent = []; - Ext.Array.each(records, function(item){ - if (item.id === 'content') { - availcontent = item.data.value.split(','); - } - }); - var templ = false; - var upload = false; - var cts = []; - - Ext.Array.each(availcontent, function(content) { - if (content === 'vztmpl') { - templ = true; - cts.push('vztmpl'); - } else if (content === 'iso') { - upload = true; - cts.push('iso'); - } - }); - - if (templ !== upload) { - uploadButton.contents = cts; - } - - templateButton.setDisabled(!templ); - uploadButton.setDisabled(!upload && !templ); - }); - } -}, function() { - - Ext.define('pve-storage-content', { - extend: 'Ext.data.Model', - fields: [ - 'volid', 'content', 'format', 'size', 'used', 'vmid', - 'channel', 'id', 'lun', - { - name: 'text', - convert: function(value, record) { - // check for volid, because if you click on a grouping header, - // it calls convert (but with an empty volid) - if (value || record.data.volid === null) { - return value; - } - return PVE.Utils.render_storage_content(value, {}, record); - } - } - ], - idProperty: 'volid' - }); - -}); -Ext.define('PVE.storage.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveStorageStatusView', - - height: 230, - title: gettext('Status'), - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 30 5 30' - }, - items: [ - { - xtype: 'box', - height: 30 - }, - { - itemId: 'enabled', - title: gettext('Enabled'), - printBar: false, - textField: 'disabled', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - itemId: 'active', - title: gettext('Active'), - printBar: false, - textField: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - itemId: 'content', - title: gettext('Content'), - printBar: false, - textField: 'content', - renderer: PVE.Utils.format_content_types - }, - { - itemId: 'type', - title: gettext('Type'), - printBar: false, - textField: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - xtype: 'box', - height: 10 - }, - { - itemId: 'usage', - title: gettext('Usage'), - valueField: 'used', - maxField: 'total' - } - ], - - updateTitle: function() { - return; - } -}); -Ext.define('PVE.storage.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStorageSummary', - scrollable: true, - bodyPadding: 5, - tbar: [ - '->', - { - xtype: 'proxmoxRRDTypeSelector' - } - ], - layout: { - type: 'column' - }, - defaults: { - padding: 5, - columnWidth: 1 - }, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var rstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/status", - interval: 1000 - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/rrddata", - model: 'pve-rrd-storage' - }); - - Ext.apply(me, { - items: [ - { - xtype: 'pveStorageStatusView', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Usage'), - fields: ['total','used'], - fieldTitles: ['Total Size', 'Used Size'], - store: rrdstore - } - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.storage.Browser', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.storage.Browser', - - onlineHelp: 'chapter_storage', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storeid = me.pveSelNode.data.storage; - if (!storeid) { - throw "no storage ID specified"; - } - - - me.items = [ - { - title: gettext('Summary'), - xtype: 'pveStorageSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ]; - - var caps = Ext.state.Manager.get('GuiCap'); - - Ext.apply(me, { - title: Ext.String.format(gettext("Storage {0} on node {1}"), - "'" + storeid + "'", "'" + nodename + "'"), - hstateid: 'storagetab' - }); - - if (caps.storage['Datastore.Allocate'] || - caps.storage['Datastore.AllocateSpace'] || - caps.storage['Datastore.Audit']) { - me.items.push({ - xtype: 'pveStorageContentView', - title: gettext('Content'), - iconCls: 'fa fa-th', - itemId: 'content' - }); - } - - if (caps.storage['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/storage/' + storeid - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.storage.DirInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_directory', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'path', - value: '', - fieldLabel: gettext('Directory'), - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.NFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveNFSScan', - - queryParam: 'server', - - valueField: 'path', - displayField: 'path', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.nfsServer) { - me.store.removeAll(); - } - - me.allQuery = me.nfsServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.nfsServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'path', 'options' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/nfs' - } - }); - - store.sort('path', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.NFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_nfs', - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - // hack: for now we always create nvf v3 - // fixme: make this configurable - values.options = 'vers=3'; - } - - return me.callParent([values]); - }, - - initComponent : function() { - var me = this; - - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=export]'); - exportField.setServer(value); - exportField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'pveNFSScan' : 'displayfield', - name: 'export', - value: '', - fieldLabel: 'Export', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.CIFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCIFSScan', - - queryParam: 'server', - - valueField: 'share', - displayField: 'share', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.cifsServer) { - me.store.removeAll(); - } - - var params = {}; - if (me.cifsUsername && me.cifsPassword) { - params.username = me.cifsUsername; - params.password = me.cifsPassword; - } - - if (me.cifsDomain) { - params.domain = me.cifsDomain; - } - - me.store.getProxy().setExtraParams(params); - me.allQuery = me.cifsServer; - - me.callParent(); - }, - - setServer: function(server) { - this.cifsServer = server; - }, - - setUsername: function(username) { - this.cifsUsername = username; - }, - - setPassword: function(password) { - this.cifsPassword = password; - }, - - setDomain: function(domain) { - this.cifsDomain = domain; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['description', 'share'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/cifs' - } - }); - store.sort('share', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.CIFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_cifs', - - initComponent : function() { - var me = this; - - var passwordfield = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - inputType: 'password', - name: 'password', - value: me.isCreate ? '' : '********', - fieldLabel: gettext('Password'), - allowBlank: false, - disabled: me.isCreate, - minLength: 1, - listeners: { - change: function(f, value) { - - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setPassword(value); - } - } - } - }); - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setServer(value); - } - } - } - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: '', - fieldLabel: gettext('Username'), - emptyText: gettext('Guest user'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (!me.isCreate) { - return; - } - var exportField = me.down('field[name=share]'); - exportField.setUsername(value); - - if (value == "") { - passwordfield.disable(); - } else { - passwordfield.enable(); - } - passwordfield.validate(); - } - } - }, - passwordfield, - { - xtype: me.isCreate ? 'pveCIFSScan' : 'displayfield', - name: 'share', - value: '', - fieldLabel: 'Share', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'domain', - value: me.isCreate ? '' : undefined, - fieldLabel: gettext('Domain'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (me.isCreate) { - - var exportField = me.down('field[name=share]'); - exportField.setDomain(value); - } - } - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.GlusterFsScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveGlusterFsScan', - - queryParam: 'server', - - valueField: 'volname', - displayField: 'volname', - matchFieldWidth: false, - listConfig: { - loadingText: 'Scanning...', - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.glusterServer) { - me.store.removeAll(); - } - - me.allQuery = me.glusterServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.glusterServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'volname' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/glusterfs' - } - }); - - store.sort('volname', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.GlusterFsInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_glusterfs', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var volumeField = me.down('field[name=volume]'); - volumeField.setServer(value); - volumeField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'server2', - value: '', - fieldLabel: gettext('Second Server'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'pveGlusterFsScan' : 'displayfield', - name: 'volume', - value: '', - fieldLabel: 'Volume name', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'iso', 'backup', 'vztmpl', 'snippets'], - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.IScsiScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveIScsiScan', - - queryParam: 'portal', - valueField: 'target', - displayField: 'target', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.portal) { - me.store.removeAll(); - } - - me.allQuery = me.portal; - - me.callParent(); - }, - - setPortal: function(portal) { - var me = this; - - me.portal = portal; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'target', 'portal' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/iscsi' - } - }); - - store.sort('target', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.IScsiInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_open_iscsi', - - onGetValues: function(values) { - var me = this; - - values.content = values.luns ? 'images' : 'none'; - delete values.luns; - - return me.callParent([values]); - }, - - setValues: function(values) { - values.luns = (values.content.indexOf('images') !== -1) ? true : false; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: 'Portal', - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=target]'); - exportField.setPortal(value); - exportField.setValue(''); - } - } - } - }, - { - readOnly: !me.isCreate, - xtype: me.isCreate ? 'pveIScsiScan' : 'displayfield', - name: 'target', - value: '', - fieldLabel: 'Target', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'checkbox', - name: 'luns', - checked: true, - fieldLabel: gettext('Use LUNs directly') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.VgSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveVgSelector', - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'vg', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - store.sort('vg', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseStorageSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseStorageSelector', - - existingGroupsText: gettext("Existing volume groups"), - queryMode: 'local', - editable: false, - value: '', - valueField: 'storage', - displayField: 'text', - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: { - addRecords: true, - params: { - type: 'iscsi' - } - }, - fields: [ 'storage', 'type', 'content', - { - name: 'text', - convert: function(value, record) { - if (record.data.storage) { - return record.data.storage + " (iSCSI)"; - } else { - return me.existingGroupsText; - } - } - }], - proxy: { - type: 'proxmox', - url: '/api2/json/storage/' - } - }); - - store.loadData([{ storage: '' }], true); - - store.sort('storage', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LVMInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvm', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.VgSelector', { - name: 'vgname', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var baseField = Ext.createWidget('pveFileSelector', { - name: 'base', - hidden: true, - disabled: true, - nodename: 'localhost', - storageContent: 'images', - fieldLabel: gettext('Base volume'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseStorageSelector', - name: 'basesel', - fieldLabel: gettext('Base storage'), - submitValue: false, - listeners: { - change: function(f, value) { - if (value) { - vgnameField.setVisible(true); - vgnameField.setDisabled(false); - vgField.setVisible(false); - vgField.setDisabled(true); - baseField.setVisible(true); - baseField.setDisabled(false); - } else { - vgnameField.setVisible(false); - vgnameField.setDisabled(true); - vgField.setVisible(true); - vgField.setDisabled(false); - baseField.setVisible(false); - baseField.setDisabled(true); - } - baseField.setStorage(value); - } - } - }); - - me.column1.push(baseField); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.TPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveTPSelector', - - queryParam: 'vg', - valueField: 'lv', - displayField: 'lv', - editable: false, - - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.vg) { - me.store.removeAll(); - } - - me.allQuery = me.vg; - - me.callParent(); - }, - - setVG: function(myvg) { - var me = this; - - me.vg = myvg; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'lv' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvmthin' - } - }); - - store.sort('lv', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseVGSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseVGSelector', - - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, - fields: [ 'vg', 'size', 'free'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LvmThinInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvmthin', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var thinpoolField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'thinpool', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.TPoolSelector', { - name: 'thinpool', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseVGSelector', - name: 'vgname', - fieldLabel: gettext('Volume group'), - listeners: { - change: function(f, value) { - if (me.isCreate) { - vgField.setVG(value); - vgField.setValue(''); - } - } - } - }); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - me.column1.push(thinpoolField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = []; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.CephFSInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'storage_cephfs', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'cephfs'; - - me.column1 = []; - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - value: '', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: 'admin', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['backup', 'iso', 'vztmpl', 'snippets'], - fieldLabel: gettext('Content'), - name: 'content', - value: 'backup', - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged cephFS') - }]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.Ceph.Model', { - extend: 'Ext.app.ViewModel', - alias: 'viewmodel.cephstorage', - - data: { - pveceph: true, - pvecephPossible: true - } -}); - -Ext.define('PVE.storage.Ceph.Controller', { - extend: 'PVE.controller.StorageEdit', - alias: 'controller.cephstorage', - - control: { - '#': { - afterrender: 'queryMonitors' - }, - 'textfield[name=username]': { - disable: 'resetField' - }, - 'displayfield[name=monhost]': { - enable: 'queryMonitors' - }, - 'textfield[name=monhost]': { - disable: 'resetField', - enable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - queryMonitors: function(field, newVal, oldVal) { - // we get called with two signatures, the above one for a field - // change event and the afterrender from the view, this check only - // can be true for the field change one and omit the API request if - // pveceph got unchecked - as it's not needed there. - if (field && !newVal && oldVal) { - return; - } - var view = this.getView(); - var vm = this.getViewModel(); - if (!(view.isCreate || vm.get('pveceph'))) { - return; // only query on create or if editing a pveceph store - } - - var monhostField = this.lookupReference('monhost'); - - Proxmox.Utils.API2Request({ - url: '/api2/json/nodes/localhost/ceph/mon', - method: 'GET', - scope: this, - callback: function(options, success, response) { - var data = response.result.data; - if (response.status === 200) { - if (data.length > 0) { - var monhost = Ext.Array.pluck(data, 'name').sort().join(','); - monhostField.setValue(monhost); - monhostField.resetOriginalValue(); - if (view.isCreate) { - vm.set('pvecephPossible', true); - } - } else { - vm.set('pveceph', false); - } - } else { - vm.set('pveceph', false); - vm.set('pvecephPossible', false); - } - } - }); - } -}); - -Ext.define('PVE.storage.RBDInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'ceph_rados_block_devices', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'rbd'; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push({ - xtype: 'pveCephPoolSelector', - nodename: me.nodename, - name: 'pool', - bind: { - disabled: '{!pveceph}', - submitValue: '{pveceph}', - hidden: '{!pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - },{ - xtype: 'textfield', - name: 'pool', - value: 'rbd', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } else { - me.column1.push({ - xtype: 'displayfield', - nodename: me.nodename, - name: 'pool', - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - value: 'admin', - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images'], - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'krbd', - uncheckedValue: 0, - fieldLabel: 'KRBD' - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged ceph pool') - }]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.SheepdogInputPanel', { - extend: 'PVE.panel.StorageBase', - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.content = 'images'; - } - - return me.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '127.0.0.1:7000', - fieldLabel: gettext('Gateway'), - allowBlank: false - } - ]; - me.column2 = []; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.ZFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - viewModel: { - parent: null, - data: { - isLIO: false, - isComstar: true, - hasWriteCacheOption: true - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[name=iscsiprovider]': { - change: 'changeISCSIProvider' - } - }, - changeISCSIProvider: function(f, newVal, oldVal) { - var vm = this.getViewModel(); - vm.set('isLIO', newVal === 'LIO'); - vm.set('isComstar', newVal === 'comstar'); - vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt'); - } - }, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.content = 'images'; - } - - values.nowritecache = values.writecache ? 0 : 1; - delete values.writecache; - - return me.callParent([values]); - }, - - setValues: function diff(values) { - values.writecache = values.nowritecache ? 0 : 1; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: gettext('Portal'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'pool', - value: '', - fieldLabel: gettext('Pool'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'blocksize', - value: '4k', - fieldLabel: gettext('Block Size'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'target', - value: '', - fieldLabel: gettext('Target'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_tg', - value: '', - fieldLabel: gettext('Target group'), - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - allowBlank: true - } - ]; - - me.column2 = [ - { - xtype: me.isCreate ? 'pveiScsiProviderSelector' : 'displayfield', - name: 'iscsiprovider', - value: 'comstar', - fieldLabel: gettext('iSCSI Provider'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'proxmoxcheckbox', - name: 'writecache', - checked: true, - bind: me.isCreate ? { disabled: '{!hasWriteCacheOption}' } : { hidden: '{!hasWriteCacheOption}' }, - uncheckedValue: 0, - fieldLabel: gettext('Write cache') - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_hg', - value: '', - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - fieldLabel: gettext('Host group'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'lio_tpg', - value: '', - bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' }, - allowBlank: false, - fieldLabel: gettext('Target portal group') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.ZFSPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveZFSPoolSelector', - valueField: 'pool', - displayField: 'pool', - queryMode: 'local', - editable: false, - listConfig: { - loadingText: gettext('Scanning...') - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'pool', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/zfs' - } - }); - - store.sort('pool', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ZFSPoolInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_zfspool', - - initComponent : function() { - var me = this; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push(Ext.create('PVE.storage.ZFSPoolSelector', { - name: 'pool', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } else { - me.column1.push(Ext.createWidget('displayfield', { - name: 'pool', - value: '', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } - - // value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push( - {xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'textfield', - name: 'blocksize', - emptyText: '8k', - fieldLabel: gettext('Block Size'), - allowBlank: true - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.ha.StatusView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAStatusView'], - - onlineHelp: 'chapter_ha_manager', - - sortPriority: { - quorum: 1, - master: 2, - lrm: 3, - service: 4 - }, - - initComponent : function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sortAfterUpdate: true, - sorters: [{ - sorterFn: function(rec1, rec2) { - var p1 = me.sortPriority[rec1.data.type]; - var p2 = me.sortPriority[rec2.data.type]; - return (p1 !== p2) ? ((p1 > p2) ? 1 : -1) : 0; - } - }], - filters: { - property: 'type', - value: 'service', - operator: '!=' - } - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Status'), - width: 80, - flex: 1, - dataIndex: 'status' - } - ] - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - } -}, function() { - - Ext.define('pve-ha-status', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'type', 'node', 'status', 'sid', - 'state', 'group', 'comment', - 'max_restart', 'max_relocate', 'type', - 'crm_state', 'request_state' - ], - idProperty: 'id' - }); - -}); -Ext.define('PVE.ha.Status', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveHAStatus', - - onlineHelp: 'chapter_ha_manager', - layout: { - type: 'vbox', - align: 'stretch' - }, - - initComponent: function() { - var me = this; - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - interval: me.interval, - model: 'pve-ha-status', - storeid: 'pve-store-' + (++Ext.idSeed), - groupField: 'type', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/ha/status/current' - } - }); - - me.items = [{ - xtype: 'pveHAStatusView', - title: gettext('Status'), - rstore: me.rstore, - border: 0, - collapsible: true, - padding: '0 0 20 0' - },{ - xtype: 'pveHAResourcesView', - flex: 1, - collapsible: true, - title: gettext('Resources'), - border: 0, - rstore: me.rstore - }]; - - me.callParent(); - me.on('activate', me.rstore.startUpdate); - } -}); -Ext.define('PVE.ha.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveHAGroupSelector'], - - value: [], - autoSelect: false, - valueField: 'group', - displayField: 'group', - listConfig: { - columns: [ - { - header: gettext('Group'), - width: 100, - sortable: true, - dataIndex: 'group' - }, - { - header: gettext('Nodes'), - width: 100, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode - } - ] - }, - store: { - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }, - - initComponent: function() { - var me = this; - me.callParent(); - me.getStore().load(); - } - -}, function() { - - Ext.define('pve-ha-groups', { - extend: 'Ext.data.Model', - fields: [ - 'group', 'type', 'digest', 'nodes', 'comment', - { - name : 'restricted', - type: 'boolean' - }, - { - name : 'nofailback', - type: 'boolean' - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/ha/groups" - }, - idProperty: 'group' - }); -}); -Ext.define('PVE.ha.VMResourceInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_resource_config', - vmid: undefined, - - onGetValues: function(values) { - var me = this; - - if (values.vmid) { - values.sid = values.vmid; - } - delete values.vmid; - - PVE.Utils.delete_if_default(values, 'group', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_restart', '1', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_relocate', '1', me.isCreate); - - return values; - }, - - initComponent : function() { - var me = this; - var MIN_QUORUM_VOTES = 3; - - var disabledHint = Ext.createWidget({ - xtype: 'displayfield', // won't get submitted by default - userCls: 'pve-hint', - value: 'Disabling the resource will stop the guest system. ' + - 'See the online help for details.', - hidden: true - }); - - var fewVotesHint = Ext.createWidget({ - itemId: 'fewVotesHint', - xtype: 'displayfield', - userCls: 'pve-hint', - value: 'At least three quorum votes are recommended for reliable HA.', - hidden: true - }); - - Proxmox.Utils.API2Request({ - url: '/cluster/config/nodes', - method: 'GET', - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var nodes = response.result.data; - var votes = 0; - Ext.Array.forEach(nodes, function(node) { - var vote = parseInt(node.quorum_votes, 10); // parse as base 10 - votes += vote || 0; // parseInt might return NaN, which is false - }); - - if (votes < MIN_QUORUM_VOTES) { - fewVotesHint.setVisible(true); - } - } - }); - - /*jslint confusion: true */ - var vmidStore = (me.vmid) ? {} : { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [ - { - property: 'type', - value: /lxc|qemu/ - }, - { - property: 'hastate', - value: /unmanaged/ - } - ] - }; - - // value is a string above, but a number below - me.column1 = [ - { - xtype: me.vmid ? 'displayfield' : 'vmComboSelector', - submitValue: me.isCreate, - name: 'vmid', - fieldLabel: (me.vmid && me.guestType === 'ct') ? 'CT' : 'VM', - value: me.vmid, - store: vmidStore, - validateExists: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_restart', - fieldLabel: gettext('Max. Restart'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_relocate', - fieldLabel: gettext('Max. Relocate'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - } - ]; - /*jslint confusion: false */ - - me.column2 = [ - { - xtype: 'pveHAGroupSelector', - name: 'group', - fieldLabel: gettext('Group') - }, - { - xtype: 'proxmoxKVComboBox', - name: 'state', - value: 'started', - fieldLabel: gettext('Request State'), - comboItems: [ - ['started', 'started'], - ['stopped', 'stopped'], - ['ignored', 'ignored'], - ['disabled', 'disabled'] - ], - listeners: { - 'change': function(field, newValue) { - if (newValue === 'disabled') { - disabledHint.setVisible(true); - } - else { - if (disabledHint.isVisible()) { - disabledHint.setVisible(false); - } - } - } - } - }, - disabledHint - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - fewVotesHint - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.VMResourceEdit', { - extend: 'Proxmox.window.Edit', - - vmid: undefined, - guestType: undefined, - isCreate: undefined, - - initComponent : function() { - var me = this; - - if (me.isCreate === undefined) { - me.isCreate = !me.vmid; - } - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/resources'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/resources/' + me.vmid; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.VMResourceInputPanel', { - isCreate: me.isCreate, - vmid: me.vmid, - guestType: me.guestType - }); - - Ext.apply(me, { - subject: gettext('Resource') + ': ' + gettext('Container') + - '/' + gettext('Virtual Machine'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(values.sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - throw "got unexpected resource type"; - } - - values.vmid = res[2]; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.ResourcesView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAResourcesView'], - - onlineHelp: 'ha_manager_resources', - - stateful: true, - stateId: 'grid-ha-resources', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!me.rstore) { - throw "no store given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - filters: { - property: 'type', - value: 'service' - } - }); - - var reload = function() { - me.rstore.load(); - }; - - var render_error = function(dataIndex, value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors[dataIndex]; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - var sid = rec.data.sid; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - return; - } - var guestType = res[1]; - var vmid = res[2]; - - var win = Ext.create('PVE.ha.VMResourceEdit',{ - guestType: guestType, - vmid: vmid - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/resources/', - getUrl: function(rec) { - var me = this; - return me.baseurl + '/' + rec.get('sid'); - }, - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.VMResourceEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - - columns: [ - { - header: 'ID', - width: 100, - sortable: true, - dataIndex: 'sid' - }, - { - header: gettext('State'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Request State'), - width: 100, - hidden: true, - sortable: true, - renderer: function(v) { - return v || 'started'; - }, - dataIndex: 'request_state' - }, - { - header: gettext('CRM State'), - width: 100, - hidden: true, - sortable: true, - dataIndex: 'crm_state' - }, - { - header: gettext('Max. Restart'), - width: 100, - sortable: true, - renderer: function(v) { - return v || '1'; - }, - dataIndex: 'max_restart' - }, - { - header: gettext('Max. Relocate'), - width: 100, - sortable: true, - renderer: function(v) { - return v || '1'; - }, - dataIndex: 'max_relocate' - }, - { - header: gettext('Group'), - width: 200, - sortable: true, - renderer: function(value, metaData, record) { - return render_error('group', value, metaData, record); - }, - dataIndex: 'group' - }, - { - header: gettext('Description'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-ha-resources', { - extend: 'Ext.data.Model', - fields: [ - 'sid', 'state', 'digest', 'errors', 'group', 'comment', - 'max_restart', 'max_relocate', 'type', 'status', 'node', - 'crm_state', 'request_state' - ], - idProperty: 'sid' - }); - -}); -Ext.define('PVE.ha.GroupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_groups', - - groupId: undefined, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = 'group'; - } - - return values; - }, - - initComponent : function() { - var me = this; - - var update_nodefield, update_node_selection; - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - update_nodefield(selected); - } - } - }); - - // use already cached data to avoid an API call - var data = PVE.data.ResourceStore.getNodes(); - - var store = Ext.create('Ext.data.Store', { - fields: [ 'node', 'mem', 'cpu', 'priority' ], - data: data, - proxy: { - type: 'memory', - reader: {type: 'json'} - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - } - ] - }); - - var nodegrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - columns: [ - { - header: gettext('Node'), - flex: 1, - dataIndex: 'node' - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 150, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 150, - dataIndex: 'cpu' - }, - { - header: 'Priority', - xtype: 'widgetcolumn', - dataIndex: 'priority', - sortable: true, - stopSelection: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 0, - maxValue: 1000, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('priority', value); - update_nodefield(sm.getSelection()); - } - } - } - } - ] - }); - - var nodefield = Ext.create('Ext.form.field.Hidden', { - name: 'nodes', - value: '', - listeners: { - change: function (nodefield, value) { - update_node_selection(value); - } - }, - isValid: function () { - var value = nodefield.getValue(); - return (value && 0 !== value.length); - } - }); - - update_node_selection = function(string) { - sm.deselectAll(true); - - string.split(',').forEach(function (e, idx, array) { - var res = e.split(':'); - - store.each(function(record) { - var node = record.get('node'); - - if (node == res[0]) { - sm.select(record, true); - record.set('priority', res[1]); - record.commit(); - } - }); - }); - nodegrid.reconfigure(store); - - }; - - update_nodefield = function(selected) { - var nodes = ''; - var first_iteration = true; - Ext.Array.each(selected, function(record) { - if (!first_iteration) { - nodes += ','; - } - first_iteration = false; - - nodes += record.data.node; - if (record.data.priority) { - nodes += ':' + record.data.priority; - } - }); - - // nodefield change listener calls us again, which results in a - // endless recursion, suspend the event temporary to avoid this - nodefield.suspendEvent('change'); - nodefield.setValue(nodes); - nodefield.resumeEvent('change'); - }; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'group', - value: me.groupId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }, - nodefield - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'restricted', - uncheckedValue: 0, - fieldLabel: 'restricted' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nofailback', - uncheckedValue: 0, - fieldLabel: 'nofailback' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - nodegrid - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.GroupEdit', { - extend: 'Proxmox.window.Edit', - - groupId: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupId; - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/groups'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/groups/' + me.groupId; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.GroupInputPanel', { - isCreate: me.isCreate, - groupId: me.groupId - }); - - Ext.apply(me, { - subject: gettext('HA Group'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.GroupsView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAGroupsView'], - - onlineHelp: 'ha_manager_groups', - - stateful: true, - stateId: 'grid-ha-groups', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.ha.GroupEdit',{ - groupId: rec.data.group - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/groups/', - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Create'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.GroupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - columns: [ - { - header: gettext('Group'), - width: 150, - sortable: true, - dataIndex: 'group' - }, - { - header: 'restricted', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'restricted' - }, - { - header: 'nofailback', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'nofailback' - }, - { - header: gettext('Nodes'), - flex: 1, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - activate: reload, - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.ha.FencingView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveFencingView'], - - onlineHelp: 'ha_manager_fencing', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-ha-fencing', - data: [] - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false, - deferEmptyText: false, - emptyText: 'Use watchdog based fencing.' - }, - columns: [ - { - header: 'Node', - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Command'), - flex: 1, - dataIndex: 'command' - } - ] - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-ha-fencing', { - extend: 'Ext.data.Model', - fields: [ - 'node', 'command', 'digest' - ] - }); - -}); -Ext.define('PVE.dc.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSummary', - - scrollable: true, - - bodyPadding: 5, - - layout: 'column', - - defaults: { - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - - items: [ - { - itemId: 'dcHealth', - xtype: 'pveDcHealth' - }, - { - itemId: 'dcGuests', - xtype: 'pveDcGuests' - }, - { - title: gettext('Resources'), - xtype: 'panel', - minHeight: 250, - bodyPadding: 5, - layout: 'hbox', - defaults: { - xtype: 'proxmoxGauge', - flex: 1 - }, - items:[ - { - title: gettext('CPU'), - itemId: 'cpu' - }, - { - title: gettext('Memory'), - itemId: 'memory' - }, - { - title: gettext('Storage'), - itemId: 'storage' - } - ] - }, - { - itemId: 'nodeview', - xtype: 'pveDcNodeView', - height: 250 - }, - { - title: gettext('Subscriptions'), - height: 220, - items: [ - { - itemId: 'subscriptions', - xtype: 'pveHealthWidget', - userCls: 'pointer', - listeners: { - element: 'el', - click: function() { - if (this.component.userCls === 'pointer') { - window.open('https://www.proxmox.com/en/proxmox-ve/pricing', '_blank'); - } - } - } - } - ] - } - ], - - initComponent: function() { - var me = this; - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-status', - model: 'pve-dc-nodes', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/status" - } - }); - - var gridstore = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - filters: { - property: 'type', - value: 'node' - }, - sorters: { - property: 'id', - direction: 'ASC' - } - }); - - me.callParent(); - - me.getComponent('nodeview').setStore(gridstore); - - var gueststatus = me.getComponent('dcGuests'); - - var cpustat = me.down('#cpu'); - var memorystat = me.down('#memory'); - var storagestat = me.down('#storage'); - var sp = Ext.state.Manager.getProvider(); - - me.mon(PVE.data.ResourceStore, 'load', function(curstore, results) { - me.suspendLayout = true; - - var cpu = 0; - var maxcpu = 0; - - var nodes = 0; - - var memory = 0; - var maxmem = 0; - - var countedStorages = {}; - var used = 0; - var total = 0; - var usableStorages = {}; - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - if (storage !== '') { - usableStorages[storage] = true; - } - }); - - var qemu = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var lxc = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var error = 0; - - var i; - - for (i = 0; i < results.length; i++) { - var item = results[i]; - switch(item.data.type) { - case 'node': - cpu += (item.data.cpu * item.data.maxcpu); - maxcpu += item.data.maxcpu || 0; - memory += item.data.mem || 0; - maxmem += item.data.maxmem || 0; - nodes++; - - // update grid also - var griditem = gridstore.getById(item.data.id); - if (griditem) { - griditem.set('cpuusage', item.data.cpu); - var max = item.data.maxmem || 1; - var val = item.data.mem || 0; - griditem.set('memoryusage', val/max); - griditem.set('uptime', item.data.uptime); - griditem.commit(); //else it marks the fields as dirty - } - break; - case 'storage': - if (!Ext.Object.isEmpty(usableStorages)) { - if (usableStorages[item.data.id] === true) { - used += item.data.disk; - total += item.data.maxdisk; - } - break; - } - if (!countedStorages[item.data.storage] || - (item.data.storage === 'local' && - !countedStorages[item.data.id])) { - used += item.data.disk; - total += item.data.maxdisk; - - countedStorages[item.data.storage === 'local'?item.data.id:item.data.storage] = true; - } - break; - case 'qemu': - qemu[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - case 'lxc': - lxc[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - default: break; - } - } - - var text = Ext.String.format(gettext('of {0} CPU(s)'), maxcpu); - cpustat.updateValue((cpu/maxcpu), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(memory), PVE.Utils.render_size(maxmem)); - memorystat.updateValue((memory/maxmem), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(used), PVE.Utils.render_size(total)); - storagestat.updateValue((used/total), text); - - gueststatus.updateValues(qemu,lxc,error); - - me.suspendLayout = false; - me.updateLayout(true); - }); - - var dcHealth = me.getComponent('dcHealth'); - me.mon(rstore, 'load', dcHealth.updateStatus, dcHealth); - - var subs = me.down('#subscriptions'); - me.mon(rstore, 'load', function(store, records, success) { - var i; - var level; - var curlevel; - for (i = 0; i < records.length; i++) { - if (records[i].get('type') !== 'node') { - continue; - } - - curlevel = records[i].get('level'); - if (level === undefined || !curlevel) { - level = curlevel; - continue; - } - - if (level !== curlevel) { - break; - } - } - - if (level === '') { - subs.setData({ - title: gettext('No Subscription'), - iconCls: PVE.Utils.get_health_icon('critical', true), - text: gettext('You have at least one node without subscription.') - }); - subs.setUserCls('pointer'); - } else if (level !== curlevel) { - subs.setData({ - title: gettext('Mixed Subscriptions'), - iconCls: PVE.Utils.get_health_icon('warning', true), - text: gettext('Warning: Your subscription levels are not the same.') - }); - subs.setUserCls('pointer'); - } else { - subs.setData({ - title: PVE.Utils.render_support_level(level), - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext('Your subscription status is valid.') - }); - subs.setUserCls(''); - } - }); - - me.on('destroy', function(){ - rstore.stopUpdate(); - }); - - rstore.startUpdate(); - } - -}); -Ext.define('PVE.window.ReplicaEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveReplicaEdit', - - subject: gettext('Replication Job'), - - - url: '/cluster/replication', - method: 'POST', - - initComponent: function() { - var me = this; - - var vmid = me.pveSelNode.data.vmid; - var nodename = me.pveSelNode.data.node; - - var items = []; - - items.push({ - xtype: (me.isCreate && !vmid)?'pveGuestIDSelector':'displayfield', - name: 'guest', - fieldLabel: 'CT/VM ID', - value: vmid || '' - }); - - items.push( - { - xtype: me.isCreate ? 'pveNodeSelector':'displayfield', - name: 'target', - disallowedNodes: [nodename], - allowBlank: false, - onlineValidator: true, - fieldLabel: gettext("Target") - }, - { - xtype: 'pveCalendarEvent', - fieldLabel: gettext('Schedule'), - emptyText: '*/15 - ' + Ext.String.format(gettext('Every {0} minutes'), 15), - name: 'schedule' - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - step: 1, - minValue: 1, - emptyText: gettext('unlimited'), - name: 'rate' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment' - }, - { - xtype: 'proxmoxcheckbox', - name: 'enabled', - defaultValue: 'on', - checked: true, - fieldLabel: gettext('Enabled') - } - ); - - me.items = [ - { - xtype: 'inputpanel', - itemId: 'ipanel', - onlineHelp: 'pvesr_schedule_time_format', - - onGetValues: function(values) { - var me = this.up('window'); - - values.disable = values.enabled ? 0 : 1; - delete values.enabled; - - PVE.Utils.delete_if_default(values, 'rate', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'disable', 0, me.isCreate); - PVE.Utils.delete_if_default(values, 'schedule', '*/15', me.isCreate); - PVE.Utils.delete_if_default(values, 'comment', '', me.isCreate); - - if (me.isCreate) { - values.type = 'local'; - var vm = vmid || values.guest; - var id = -1; - if (me.highestids[vm] !== undefined) { - id = me.highestids[vm]; - } - id++; - values.id = vm + '-' + id.toString(); - delete values.guest; - } - return values; - }, - items: items - } - ]; - - me.callParent(); - - if (me.isCreate) { - me.load({ - success: function(response) { - var jobs = response.result.data; - var highestids = {}; - Ext.Array.forEach(jobs, function(job) { - var match = /^([0-9]+)\-([0-9]+)$/.exec(job.id); - if (match) { - var vmid = parseInt(match[1],10); - var id = parseInt(match[2],10); - if (highestids[vmid] < id || - highestids[vmid] === undefined) { - highestids[vmid] = id; - } - } - }); - - me.highestids = highestids; - } - }); - - } else { - me.load({ - success: function(response, options) { - response.result.data.enabled = !response.result.data.disable; - me.setValues(response.result.data); - me.digest = response.result.data.digest; - } - }); - } - } -}); - -/*jslint confusion: true */ -/* callback is a function and string */ -Ext.define('PVE.grid.ReplicaView', { - extend: 'Ext.grid.Panel', - xtype: 'pveReplicaView', - - onlineHelp: 'chapter_pvesr', - - stateful: true, - stateId: 'grid-pve-replication-status', - - controller: { - xclass: 'Ext.app.ViewController', - - addJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var win = Ext.create('PVE.window.ReplicaEdit', { - isCreate: true, - method: 'POST', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - editJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var data = rec.data; - var win = Ext.create('PVE.window.ReplicaEdit', { - url: '/cluster/replication/' + data.id, - method: 'PUT', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - scheduleJobNow: function(button,event,rec) { - var me = this.getView(); - var controller = this; - - Proxmox.Utils.API2Request({ - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/schedule_now", - method: 'POST', - waitMsgTarget: me, - callback: function() { controller.reload(); }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - showLog: function(button, event, rec) { - var me = this.getView(); - var controller = this; - var logView = Ext.create('Proxmox.panel.LogView', { - border: false, - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/log" - }); - var win = Ext.create('Ext.window.Window', { - items: [ logView ], - layout: 'fit', - width: 800, - height: 400, - modal: true, - title: gettext("Replication Log") - }); - var task = { - run: function() { - logView.requestUpdate(); - }, - interval: 1000 - }; - Ext.TaskManager.start(task); - win.on('destroy', function() { - Ext.TaskManager.stop(task); - controller.reload(); - }); - win.show(); - }, - - reload: function() { - var me = this.getView(); - me.rstore.load(); - }, - - dblClick: function(grid, record, item) { - var me = this; - me.editJob(undefined, undefined, record); - }, - - // check for cluster - // currently replication is for cluster only, so we disable the whole - // component - checkPrerequisites: function() { - var me = this.getView(); - if (PVE.data.ResourceStore.getNodes().length < 2) { - me.mask(gettext("Replication needs at least two nodes"), ['pve-static-mask']); - } - }, - - control: { - '#': { - itemdblclick: 'dblClick', - afterlayout: 'checkPrerequisites' - } - } - }, - - tbar: [ - { - text: gettext('Add'), - itemId: 'addButton', - handler: 'addJob' - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - itemId: 'editButton', - handler: 'editJob', - disabled: true - }, - { - xtype: 'proxmoxStdRemoveButton', - itemId: 'removeButton', - baseurl: '/api2/extjs/cluster/replication/', - dangerous: true, - callback: 'reload' - }, - { - xtype: 'proxmoxButton', - text: gettext('Log'), - itemId: 'logButton', - handler: 'showLog', - disabled: true - }, - { - xtype: 'proxmoxButton', - text: gettext('Schedule now'), - itemId: 'scheduleNowButton', - handler: 'scheduleJobNow', - disabled: true - } - ], - - initComponent: function() { - var me = this; - var mode = ''; - var url = '/cluster/replication'; - - me.nodename = me.pveSelNode.data.node; - me.vmid = me.pveSelNode.data.vmid; - - me.columns = [ - { - text: gettext('Enabled'), - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true - }, - { - text: 'ID', - dataIndex: 'id', - width: 60, - hidden: true - }, - { - text: gettext('Guest'), - dataIndex: 'guest', - width: 75 - }, - { - text: gettext('Job'), - dataIndex: 'jobnum', - width: 60 - }, - { - text: gettext('Target'), - dataIndex: 'target' - } - ]; - - if (!me.nodename) { - mode = 'dc'; - me.stateId = 'grid-pve-replication-dc'; - } else if (!me.vmid) { - mode = 'node'; - url = '/nodes/' + me.nodename + '/replication'; - } else { - mode = 'vm'; - url = '/nodes/' + me.nodename + '/replication' + '?guest=' + me.vmid; - } - - if (mode !== 'dc') { - me.columns.push( - { - text: gettext('Status'), - dataIndex: 'state', - minWidth: 160, - flex: 1, - renderer: function(value, metadata, record) { - - if (record.data.pid) { - metadata.tdCls = 'x-grid-row-loading'; - return ''; - } - - var icons = []; - var states = []; - - if (record.data.remove_job) { - icons.push(''); - states.push(gettext("Removal Scheduled")); - } - - if (record.data.error) { - icons.push(''); - states.push(record.data.error); - } - - if (icons.length == 0) { - icons.push(''); - states.push(gettext('OK')); - } - - return icons.join(',') + ' ' + states.join(','); - } - }, - { - text: gettext('Last Sync'), - dataIndex: 'last_sync', - width: 150, - renderer: function(value, metadata, record) { - if (!value) { - return '-'; - } - - if (record.data.pid) { - return gettext('syncing'); - } - - return Proxmox.Utils.render_timestamp(value); - } - }, - { - text: gettext('Duration'), - dataIndex: 'duration', - width: 60, - renderer: PVE.Utils.render_duration - }, - { - text: gettext('Next Sync'), - dataIndex: 'next_sync', - width: 150, - renderer: function(value) { - if (!value) { - return '-'; - } - - var now = new Date(); - var next = new Date(value*1000); - - if (next < now) { - return gettext('pending'); - } - - return Proxmox.Utils.render_timestamp(value); - } - } - ); - } - - me.columns.push( - { - text: gettext('Schedule'), - width: 75, - dataIndex: 'schedule' - }, - { - text: gettext('Rate limit'), - dataIndex: 'rate', - renderer: function(value) { - if (!value) { - return gettext('unlimited'); - } - - return value.toString() + ' MB/s'; - }, - hidden: true - }, - { - text: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.htmlEncode - } - ); - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-replica-' + me.nodename + me.vmid, - model: (mode === 'dc')? 'pve-replication' : 'pve-replication-state', - interval: 3000, - proxy: { - type: 'proxmox', - url: "/api2/json" + url - } - }); - - me.store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sorters: [ - { - property: 'guest' - }, - { - property: 'jobnum' - } - ] - }); - - me.callParent(); - - // we cannot access the log and scheduleNow button - // in the datacenter, because - // we do not know where/if the jobs runs - if (mode === 'dc') { - me.down('#logButton').setHidden(true); - me.down('#scheduleNowButton').setHidden(true); - } - - // if we set the warning mask, we do not want to load - // or set the mask on store errors - if (PVE.data.ResourceStore.getNodes().length < 2) { - return; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.on('destroy', me.rstore.stopUpdate); - me.rstore.startUpdate(); - } -}, function() { - - Ext.define('pve-replication', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'target', 'comment', 'rate', 'type', - { name: 'guest', type: 'integer' }, - { name: 'jobnum', type: 'integer' }, - { name: 'schedule', defaultValue: '*/15' }, - { name: 'disable', defaultValue: '' }, - { name: 'enabled', calculate: function(data) { return !data.disable; } } - ] - }); - - Ext.define('pve-replication-state', { - extend: 'pve-replication', - fields: [ - 'last_sync', 'next_sync', 'error', 'duration', 'state', - 'fail_count', 'remove_job', 'pid' - ] - }); - -}); -Ext.define('PVE.dc.Health', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcHealth', - - title: gettext('Health'), - - bodyPadding: 10, - height: 220, - layout: { - type: 'hbox', - align: 'stretch' - }, - - defaults: { - flex: 1, - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - nodeList: [], - nodeIndex: 0, - - updateStatus: function(store, records, success) { - var me = this; - if (!success) { - return; - } - - var cluster = { - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext("Standalone node - no cluster defined") - }; - - var nodes = { - online: 0, - offline: 0 - }; - - // by default we have one node - var numNodes = 1; - var i; - - for (i = 0; i < records.length; i++) { - var item = records[i]; - if (item.data.type === 'node') { - nodes[item.data.online === 1 ? 'online':'offline']++; - } else if(item.data.type === 'cluster') { - cluster.text = gettext("Cluster") + ": "; - cluster.text += item.data.name + ", "; - cluster.text += gettext("Quorate") + ": "; - cluster.text += Proxmox.Utils.format_boolean(item.data.quorate); - if (item.data.quorate != 1) { - cluster.iconCls = PVE.Utils.get_health_icon('critical', true); - } - - numNodes = item.data.nodes; - } - } - - if (numNodes !== (nodes.online + nodes.offline)) { - nodes.offline = numNodes - nodes.online; - } - - me.getComponent('clusterstatus').updateHealth(cluster); - me.getComponent('nodestatus').update(nodes); - }, - - updateCeph: function(store, records, success) { - var me = this; - var cephstatus = me.getComponent('ceph'); - if (!success || records.length < 1) { - - // if ceph status is already visible - // dont stop to update - if (cephstatus.isVisible()) { - return; - } - - // try all nodes until we either get a successfull api call, - // or we tried all nodes - if (++me.nodeIndex >= me.nodeList.length) { - me.cephstore.stopUpdate(); - } else { - store.getProxy().setUrl('/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status'); - } - - return; - } - - var state = PVE.Utils.render_ceph_health(records[0].data.health || {}); - cephstatus.updateHealth(state); - cephstatus.setVisible(true); - }, - - listeners: { - destroy: function() { - var me = this; - me.cephstore.stopUpdate(); - } - }, - - items: [ - { - itemId: 'clusterstatus', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - itemId: 'nodestatus', - data: { - online: 0, - offline: 0 - }, - tpl: [ - '

' + gettext('Nodes') + '


', - '
', - '
', - ' ', - gettext('Online'), - '
', - '
{online}
', - '

', - '
', - ' ', - gettext('Offline'), - '
', - '
{offline}
', - '
' - ] - }, - { - itemId: 'ceph', - width: 250, - columnWidth: undefined, - userCls: 'pointer', - title: 'Ceph', - xtype: 'pveHealthWidget', - hidden: true, - listeners: { - element: 'el', - click: function() { - var me = this.component.up('pveDcHealth'); - var sp = Ext.state.Manager.getProvider(); - - // preselect the ceph tab - sp.set('nodetab', {value:'ceph'}); - - // select the node that had the successfull api call - var id = me.nodeList[me.nodeIndex].id; - Ext.ComponentQuery.query('pveResourceTree')[0].selectById(id); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.nodeList = PVE.data.ResourceStore.getNodes(); - me.nodeIndex = 0; - me.cephstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-ceph', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status' - } - }); - me.callParent(); - me.mon(me.cephstore, 'load', me.updateCeph, me); - me.cephstore.startUpdate(); - } -}); -Ext.define('PVE.dc.Guests', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcGuests', - - - title: gettext('Guests'), - height: 220, - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - bodyPadding: '0 20 20 20', - - defaults: { - xtype: 'box', - padding: '0 50 0 50', - style: { - 'text-align':'center', - 'line-height':'1.2' - } - }, - items: [{ - itemId: 'qemu', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("Virtual Machines") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'lxc', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("LXC Container") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'error', - colspan: 2, - data: { - num: 0 - }, - columnWidth: 1, - padding: '10 250 0 250', - tpl: [ - '', - '
', - ' ', - gettext('Error'), - '
', - '
{num}
', - '
' - ] - }], - - updateValues: function(qemu, lxc, error) { - var me = this; - me.getComponent('qemu').update(qemu); - me.getComponent('lxc').update(lxc); - me.getComponent('error').update({num: error}); - } -}); - /*jslint confusion: true*/ -Ext.define('PVE.dc.OptionView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveDcOptionView'], - - onlineHelp: 'datacenter_configuration_file', - - monStoreErrors: true, - - add_inputpanel_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - var canEdit = (opts.caps === undefined || opts.caps); - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: canEdit ? { - xtype: 'proxmoxWindowEdit', - width: 350, - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - setValues: function(values) { - // FIXME: run through parsePropertyString if not an object? - var edit_value = values[name]; - Ext.Array.each(this.query('inputpanel'), function(panel) { - panel.setValues(edit_value); - }); - }, - url: opts.url, - items: [{ - xtype: 'inputpanel', - onGetValues: function(values) { - if (values === undefined || Object.keys(values).length === 0) { - return { 'delete': name }; - } - var ret_val = {}; - ret_val[name] = PVE.Parser.printPropertyString(values); - return ret_val; - }, - items: opts.items - }] - } : undefined - }; - }, - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.add_combobox_row('keyboard', gettext('Keyboard Layout'), { - renderer: PVE.Utils.render_kvm_language, - comboItems: PVE.Utils.kvm_keymap_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('http_proxy', gettext('HTTP proxy'), { - defaultValue: Proxmox.Utils.noneText, - vtype: 'HttpProxy', - deleteEmpty: true - }); - me.add_combobox_row('console', gettext('Console Viewer'), { - renderer: PVE.Utils.render_console_viewer, - comboItems: PVE.Utils.console_viewer_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('email_from', gettext('Email from address'), { - deleteEmpty: true, - vtype: 'proxmoxMail', - defaultValue: 'root@$hostname' - }); - me.add_text_row('mac_prefix', gettext('MAC address prefix'), { - deleteEmpty: true, - vtype: 'MacPrefix', - defaultValue: Proxmox.Utils.noneText - }); - me.add_inputpanel_row('migration', gettext('Migration Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.vms['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - defaultKey: 'type', - items: [{ - xtype: 'displayfield', - name: 'type', - fieldLabel: gettext('Type'), - value: 'secure', - submitValue: true, - vtype: 'IPCIDRAddress' - }, { - xtype: 'textfield', - name: 'network', - fieldLabel: gettext('Network'), - vtype: 'IPCIDRAddress', - emptyText: Proxmox.Utils.defaultText, - value: '' - }] - }); - me.add_inputpanel_row('ha', gettext('HA Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.vms['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'shutdown_policy', - fieldLabel: gettext('Shutdown Policy'), - deleteEmpty: false, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + ' (conditional)' ], - ['freeze', 'freeze'], - ['failover', 'failover'], - ['conditional', 'conditional'] - ], - defaultValue: '__default__' - }] - }); - - // TODO: bwlimits, migration net, u2f? - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - Ext.apply(me, { - tbar: [{ - text: gettext('Edit'), - xtype: 'proxmoxButton', - disabled: true, - handler: function() { me.run_editor(); }, - selModel: me.selModel - }], - url: "/api2/json/cluster/options", - editorConfig: { - url: "/api2/extjs/cluster/options" - }, - interval: 5000, - cwidth1: 200, - listeners: { - itemdblclick: me.run_editor - } - }); - - me.callParent(); - - // set the new value for the default console - me.mon(me.rstore, 'load', function(store, records, success) { - if (!success) { - return; - } - - var rec = store.getById('console'); - PVE.VersionInfo.console = rec.data.value; - if (rec.data.value === '__default__') { - delete PVE.VersionInfo.console; - } - }); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); -Ext.define('PVE.dc.StorageView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveStorageView'], - - onlineHelp: 'chapter_storage', - - stateful: true, - stateId: 'grid-dc-storage', - - createStorageEditWindow: function(type, sid) { - var schema = PVE.Utils.storageSchema[type]; - if (!schema || !schema.ipanel) { - throw "no editor registered for storage type: " + type; - } - - Ext.create('PVE.storage.BaseEdit', { - paneltype: 'PVE.storage.' + schema.ipanel, - type: type, - storageId: sid, - autoShow: true, - listeners: { - destroy: this.reloadStore - } - }); - }, - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-storage', - proxy: { - type: 'proxmox', - url: "/api2/json/storage" - }, - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type, - sid = rec.data.storage; - - me.createStorageEditWindow(type, sid); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/storage/', - callback: reload - }); - - // else we cannot dynamically generate the add menu handlers - var addHandleGenerator = function(type) { - return function() { me.createStorageEditWindow(type); }; - }; - var addMenuItems = [], type; - /*jslint forin: true */ - for (type in PVE.Utils.storageSchema) { - var storage = PVE.Utils.storageSchema[type]; - if (storage.hideAdd) { - continue; - } - addMenuItems.push({ - text: PVE.Utils.format_storage_type(type), - iconCls: 'fa fa-fw fa-' + storage.faIcon, - handler: addHandleGenerator(type) - }); - } - - Ext.apply(me, { - store: store, - reloadStore: reload, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: addMenuItems - }) - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - flex: 2, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Type'), - flex: 1, - sortable: true, - dataIndex: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - header: gettext('Content'), - flex: 3, - sortable: true, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Path') + '/' + gettext('Target'), - flex: 2, - sortable: true, - dataIndex: 'path', - renderer: function(value, metaData, record) { - if (record.data.target) { - return record.data.target; - } - return value; - } - }, - { - header: gettext('Shared'), - flex: 1, - sortable: true, - dataIndex: 'shared', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Enabled'), - flex: 1, - sortable: true, - dataIndex: 'disable', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - header: gettext('Bandwidth Limit'), - flex: 2, - sortable: true, - dataIndex: 'bwlimit' - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-storage', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'content', 'server', 'portal', 'target', 'export', 'storage', - { name: 'shared', type: 'boolean'}, - { name: 'disable', type: 'boolean'} - ], - idProperty: 'storage' - }); - -}); -/*global u2f,QRCode,Uint8Array*/ -/*jslint confusion: true*/ -Ext.define('PVE.window.TFAEdit', { - extend: 'Ext.window.Window', - mixins: ['Proxmox.Mixin.CBind'], - - onlineHelp: 'pveum_tfa_auth', // fake to ensure this gets a link target - - modal: true, - resizable: false, - title: gettext('Two Factor Authentication'), - subject: 'TFA', - url: '/api2/extjs/access/tfa', - width: 512, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - updateQrCode: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var algorithm = values.algorithm; - if (!algorithm) { - algorithm = 'SHA1'; - } - - me.qrcode.makeCode( - 'otpauth://totp/' + encodeURIComponent(me.userid) + - '?secret=' + values.secret + - '&period=' + values.step + - '&digits=' + values.digits + - '&algorithm=' + algorithm + - '&issuer=' + encodeURIComponent(values.issuer) - ); - - me.lookup('challenge').setVisible(true); - me.down('#qrbox').setVisible(true); - }, - - showError: function(error) { - Ext.Msg.alert( - gettext('Error'), - PVE.Utils.render_u2f_error(error) - ); - }, - - doU2FChallenge: function(response) { - var me = this; - - var data = response.result.data; - me.lookup('password').setDisabled(true); - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Setup'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - Ext.Function.defer(function() { - u2f.register(data.appId, [data], [], function(data) { - msg.close(); - if (data.errorCode) { - me.showError(data.errorCode); - } else { - me.respondToU2FChallenge(data); - } - }); - }, 500, me); - }, - - respondToU2FChallenge: function(data) { - var me = this; - var params = { - userid: me.userid, - action: 'confirm', - response: JSON.stringify(data) - }; - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - success: function() { - me.close(); - Ext.Msg.show({ - title: gettext('Success'), - message: gettext('U2F Device successfully connected.'), - buttons: Ext.Msg.OK - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - viewModel: { - data: { - in_totp_tab: true, - tfa_required: false, - has_tfa: false, - valid: false, - u2f_available: true - }, - formulas: { - canDeleteTFA: function(get) { - return (get('has_tfa') && !get('tfa_required')); - } - } - }, - - afterLoadingRealm: function(realm_tfa_type) { - var me = this; - var viewmodel = me.getViewModel(); - if (!realm_tfa_type) { - // There's no TFA enforced by the realm, everything works. - viewmodel.set('u2f_available', true); - viewmodel.set('tfa_required', false); - } else if (realm_tfa_type === 'oath') { - // The realm explicitly requires TOTP - viewmodel.set('tfa_required', true); - viewmodel.set('u2f_available', false); - } else { - // The realm enforces some other TFA type (yubico) - me.close(); - Ext.Msg.alert( - gettext('Error'), - Ext.String.format( - gettext("Custom 2nd factor configuration is not supported on realms with '{0}' TFA."), - realm_tfa_type - ) - ); - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[qrupdate=true]': { - change: function() { - var me = this.getView(); - me.updateQrCode(); - } - }, - 'field': { - validitychange: function(field, valid) { - var me = this; - var viewModel = me.getViewModel(); - var form = me.lookup('totp_form'); - var challenge = me.lookup('challenge'); - var password = me.lookup('password'); - viewModel.set('valid', form.isValid() && challenge.isValid() && password.isValid()); - } - }, - '#': { - show: function() { - var me = this.getView(); - var viewmodel = this.getViewModel(); - - me.qrdiv = document.createElement('center'); - me.qrcode = new QRCode(me.qrdiv, { - width: 256, - height: 256, - correctLevel: QRCode.CorrectLevel.M - }); - me.down('#qrbox').getEl().appendChild(me.qrdiv); - - viewmodel.set('has_tfa', me.hasTFA); - if (!me.hasTFA) { - this.randomizeSecret(); - } else { - me.down('#qrbox').setVisible(false); - me.lookup('challenge').setVisible(false); - } - - if (Proxmox.UserName === 'root@pam') { - me.lookup('password').setVisible(false); - me.lookup('password').setDisabled(true); - } - } - }, - '#tfatabs': { - tabchange: function(panel, newcard) { - var viewmodel = this.getViewModel(); - viewmodel.set('in_totp_tab', newcard.itemId === 'totp-panel'); - } - } - }, - - applySettings: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'new', - key: values.secret, - config: PVE.Parser.printPropertyString({ - type: 'oath', - digits: values.digits, - step: values.step - }), - // this is used to verify that the client generates the correct codes: - response: me.lookup('challenge').value - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - deleteTFA: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'delete' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - randomizeSecret: function() { - var me = this; - var rnd = new Uint8Array(16); - window.crypto.getRandomValues(rnd); - var data = ''; - rnd.forEach(function(b) { - // just use the first 5 bit - b = b & 0x1f; - if (b < 26) { - // A..Z - data += String.fromCharCode(b + 0x41); - } else { - // 2..7 - data += String.fromCharCode(b-26 + 0x32); - } - }); - me.lookup('tfa_secret').setValue(data); - }, - - startU2FRegistration: function() { - var me = this; - - var params = { - userid: me.getView().userid, - action: 'new' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response) { - me.getView().doU2FChallenge(response); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - - items: [ - { - xtype: 'tabpanel', - itemId: 'tfatabs', - border: false, - items: [ - { - xtype: 'panel', - title: 'TOTP', - itemId: 'totp-panel', - border: false, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'form', - layout: 'anchor', - border: false, - reference: 'totp_form', - fieldDefaults: { - anchor: '100%', - padding: '0 5' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('User name'), - cbind: { - value: '{userid}' - } - }, - { - layout: 'hbox', - border: false, - padding: '0 0 5 0', - items: [{ - xtype: 'textfield', - fieldLabel: gettext('Secret'), - emptyText: gettext('Unchanged'), - name: 'secret', - reference: 'tfa_secret', - regex: /^[A-Z2-7=]+$/, - regexText: 'Must be base32 [A-Z2-7=]', - maskRe: /[A-Z2-7=]/, - qrupdate: true, - flex: 4 - }, - { - xtype: 'button', - text: gettext('Randomize'), - reference: 'randomize_button', - handler: 'randomizeSecret', - flex: 1 - }] - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Time period'), - name: 'step', - // Google Authenticator ignores this and generates bogus data - hidden: true, - value: 30, - minValue: 10, - qrupdate: true - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Digits'), - name: 'digits', - value: 6, - // Google Authenticator ignores this and generates bogus data - hidden: true, - minValue: 6, - maxValue: 8, - qrupdate: true - }, - { - xtype: 'textfield', - fieldLabel: gettext('Issuer Name'), - name: 'issuer', - value: 'Proxmox Web UI', - qrupdate: true - } - ] - }, - { - xtype: 'box', - itemId: 'qrbox', - visible: false, // will be enabled when generating a qr code - style: { - 'background-color': 'white', - padding: '5px', - width: '266px', - height: '266px' - } - }, - { - xtype: 'textfield', - fieldLabel: gettext('Verification Code'), - allowBlank: false, - reference: 'challenge', - padding: '0 5', - emptyText: gettext('Scan QR code and enter TOTP auth. code to verify') - } - ] - }, - { - title: 'U2F', - itemId: 'u2f-panel', - reference: 'u2f_panel', - border: false, - padding: '5 5', - layout: { - type: 'vbox', - align: 'middle' - }, - bind: { - disabled: '{!u2f_available}' - }, - items: [ - { - xtype: 'label', - width: 500, - text: gettext('To register a U2F device, connect the device, then click the button and follow the instructions.') - } - ] - } - ] - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - reference: 'password', - allowBlank: false, - validateBlank: true, - padding: '0 0 5 5', - emptyText: gettext('verify current password') - } - ], - - buttons: [ - { - xtype: 'proxmoxHelpButton' - }, - '->', - { - text: gettext('Apply'), - handler: 'applySettings', - bind: { - hidden: '{!in_totp_tab}', - disabled: '{!valid}' - } - }, - { - xtype: 'button', - text: gettext('Register U2F Device'), - handler: 'startU2FRegistration', - bind: { - hidden: '{in_totp_tab}' - } - }, - { - text: gettext('Delete'), - reference: 'delete_button', - handler: 'deleteTFA', - bind: { - disabled: '{!canDeleteTFA}' - } - } - ], - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-domains', - autoLoad: true - }); - - store.on('load', function() { - var user_realm = me.userid.split('@')[1]; - var realm = me.store.findRecord('realm', user_realm); - me.afterLoadingRealm(realm && realm.data && realm.data.tfa); - }, me); - - Ext.apply(me, { store: store }); - - me.callParent(); - - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', 'pveum_tfa_auth'); - } -}); -Ext.define('PVE.dc.UserEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcUserEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.userid; - - var url; - var method; - var realm; - - if (me.isCreate) { - url = '/api2/extjs/access/users'; - method = 'POST'; - } else { - url = '/api2/extjs/access/users/' + me.userid; - method = 'PUT'; - } - - var verifypw; - var pwfield; - - var validate_pw = function() { - if (verifypw.getValue() !== pwfield.getValue()) { - return gettext("Passwords do not match"); - } - return true; - }; - - verifypw = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - submitValue: false, - disabled: true, - hidden: true, - validator: validate_pw - }); - - pwfield = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - name: 'password', - disabled: true, - hidden: true, - validator: validate_pw - }); - - var update_passwd_field = function(realm) { - if (realm === 'pve') { - pwfield.setVisible(true); - pwfield.setDisabled(false); - verifypw.setVisible(true); - verifypw.setDisabled(false); - } else { - pwfield.setVisible(false); - pwfield.setDisabled(true); - verifypw.setVisible(false); - verifypw.setDisabled(true); - } - - }; - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'userid', - fieldLabel: gettext('User name'), - value: me.userid, - allowBlank: false, - submitValue: me.isCreate ? true : false - }, - pwfield, verifypw, - { - xtype: 'pveGroupSelector', - name: 'groups', - multiSelect: true, - allowBlank: true, - fieldLabel: gettext('Group') - }, - { - xtype: 'datefield', - name: 'expire', - emptyText: 'never', - format: 'Y-m-d', - submitFormat: 'U', - fieldLabel: gettext('Expire') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enabled'), - name: 'enable', - uncheckedValue: 0, - defaultValue: 1, - checked: true - } - ]; - - var column2 = [ - { - xtype: 'textfield', - name: 'firstname', - fieldLabel: gettext('First Name') - }, - { - xtype: 'textfield', - name: 'lastname', - fieldLabel: gettext('Last Name') - }, - { - xtype: 'textfield', - name: 'email', - fieldLabel: gettext('E-Mail'), - vtype: 'proxmoxMail' - } - ]; - - if (me.isCreate) { - column1.splice(1,0,{ - xtype: 'pveRealmComboBox', - name: 'realm', - fieldLabel: gettext('Realm'), - allowBlank: false, - matchFieldWidth: false, - listConfig: { width: 300 }, - listeners: { - change: function(combo, newValue){ - realm = newValue; - update_passwd_field(realm); - } - }, - submitValue: false - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ], - advancedItems: [ - { - xtype: 'textfield', - name: 'keys', - fieldLabel: gettext('Key IDs') - } - ], - onGetValues: function(values) { - // hack: ExtJS datefield does not submit 0, so we need to set that - if (!values.expire) { - values.expire = 0; - } - - if (realm) { - values.userid = values.userid + '@' + realm; - } - - if (!values.password) { - delete values.password; - } - - return values; - } - }); - - Ext.applyIf(me, { - subject: gettext('User'), - url: url, - method: method, - fieldDefaults: { - labelWidth: 110 // for spanish translation - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (Ext.isDefined(data.expire)) { - if (data.expire) { - data.expire = new Date(data.expire * 1000); - } else { - // display 'never' instead of '1970-01-01' - data.expire = null; - } - } - me.setValues(data); - } - }); - } - } -}); -/*jslint confusion: true */ -Ext.define('PVE.dc.UserView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveUserView'], - - onlineHelp: 'pveum_users', - - stateful: true, - stateId: 'grid-users', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - id: "users", - model: 'pve-users', - sorters: { - property: 'userid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/access/users/', - enableFn: function(rec) { - if (!caps.access['User.Modify']) { - return false; - } - return rec.data.userid !== 'root@pam'; - }, - callback: function() { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec || !caps.access['User.Modify']) { - return; - } - - var win = Ext.create('PVE.dc.UserEdit',{ - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - enableFn: function(rec) { - return !!caps.access['User.Modify']; - }, - selModel: sm, - handler: run_editor - }); - - var pwchange_btn = new Proxmox.button.Button({ - text: gettext('Password'), - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var win = Ext.create('Proxmox.window.PasswordEdit', { - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var tfachange_btn = new Proxmox.button.Button({ - text: 'TFA', - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var d = rec.data; - var win = Ext.create('PVE.window.TFAEdit',{ - hasTFA: d.keys != undefined && d.keys.length, - userid: d.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - disabled: !caps.access['User.Modify'], - handler: function() { - var win = Ext.create('PVE.dc.UserEdit',{ - }); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn, pwchange_btn, tfachange_btn - ]; - - var render_username = function(userid) { - return userid.match(/^(.+)(@[^@]+)$/)[1]; - }; - - var render_realm = function(userid) { - return userid.match(/@([^@]+)$/)[1]; - }; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('User name'), - width: 200, - sortable: true, - renderer: render_username, - dataIndex: 'userid' - }, - { - header: gettext('Realm'), - width: 100, - sortable: true, - renderer: render_realm, - dataIndex: 'userid' - }, - { - header: gettext('Enabled'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'enable' - }, - { - header: gettext('Expire'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_expire, - dataIndex: 'expire' - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname' - }, - { - header: 'TFA', - width: 50, - sortable: true, - renderer: function(v) { - return Proxmox.Utils.format_boolean(v !== undefined && v.length); - }, - dataIndex: 'keys' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.PoolView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pvePoolView'], - - onlineHelp: 'pveum_pools', - - stateful: true, - stateId: 'grid-pools', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: { - property: 'poolid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/pools/', - callback: function () { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.PoolEdit',{ - poolid: rec.data.poolid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.PoolEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'poolid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.PoolEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcPoolEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.poolid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/pools'; - method = 'POST'; - } else { - url = '/api2/extjs/pools/' + me.poolid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Pool'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'poolid', - value: me.poolid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.GroupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveGroupView'], - - onlineHelp: 'pveum_groups', - - stateful: true, - stateId: 'grid-groups', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: { - property: 'groupid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/groups/' - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.GroupEdit',{ - groupid: rec.data.groupid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.GroupEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'groupid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.GroupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcGroupEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/groups'; - method = 'POST'; - } else { - url = '/api2/extjs/access/groups/' + me.groupid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Group'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'groupid', - value: me.groupid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.RoleView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveRoleView'], - - onlineHelp: 'pveum_roles', - - stateful: true, - stateId: 'grid-roles', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: { - property: 'roleid', - order: 'DESC' - } - }); - - var render_privs = function(value, metaData) { - - if (!value) { - return '-'; - } - - // allow word wrap - metaData.style = 'white-space:normal;'; - - return value.replace(/\,/g, ' '); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (rec.data.special === "1") { - return; - } - - var win = Ext.create('PVE.dc.RoleEdit',{ - roleid: rec.data.roleid, - privs: rec.data.privs - }); - win.on('destroy', reload); - win.show(); - }; - - Ext.apply(me, { - store: store, - selModel: sm, - - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Built-In'), - width: 65, - sortable: true, - dataIndex: 'special', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - dataIndex: 'roleid' - }, - { - itemid: 'privs', - header: gettext('Privileges'), - sortable: false, - renderer: render_privs, - dataIndex: 'privs', - flex: 1 - } - ], - listeners: { - activate: function() { - store.load(); - }, - itemdblclick: run_editor - }, - tbar: [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.RoleEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor, - enableFn: function(record) { - return record.data.special !== '1'; - } - }, - { - xtype: 'proxmoxStdRemoveButton', - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/roles/', - enableFn: function(record) { - return record.data.special !== '1'; - } - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.RoleEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveDcRoleEdit', - - width: 400, - - initComponent : function() { - var me = this; - - me.isCreate = !me.roleid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/roles'; - method = 'POST'; - } else { - url = '/api2/extjs/access/roles/' + me.roleid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Role'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'roleid', - value: me.roleid, - allowBlank: false, - fieldLabel: gettext('Name') - }, - { - xtype: 'pvePrivilegesSelector', - name: 'privs', - value: me.privs, - allowBlank: false, - fieldLabel: gettext('Privileges') - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response) { - var data = response.result.data; - var keys = Ext.Object.getKeys(data); - - me.setValues({ - privs: keys, - roleid: me.roleid - }); - } - }); - } - } -}); -Ext.define('PVE.dc.ACLAdd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveACLAdd'], - url: '/access/acl', - method: 'PUT', - isAdd: true, - initComponent : function() { - - var me = this; - - me.isCreate = true; - - var items = [ - { - xtype: me.path ? 'hiddenfield' : 'pvePermPathSelector', - name: 'path', - value: me.path, - allowBlank: false, - fieldLabel: gettext('Path') - } - ]; - - if (me.aclType === 'group') { - me.subject = gettext("Group Permission"); - items.push({ - xtype: 'pveGroupSelector', - name: 'groups', - fieldLabel: gettext('Group') - }); - } else if (me.aclType === 'user') { - me.subject = gettext("User Permission"); - items.push({ - xtype: 'pveUserSelector', - name: 'users', - fieldLabel: gettext('User') - }); - } else { - throw "unknown ACL type"; - } - - items.push({ - xtype: 'pveRoleSelector', - name: 'roles', - value: 'NoAccess', - fieldLabel: gettext('Role') - }); - - if (!me.path) { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'propagate', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Propagate') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - items: items, - onlineHelp: 'pveum_permission_management' - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.dc.ACLView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveACLView'], - - onlineHelp: 'chapter_user_management', - - stateful: true, - stateId: 'grid-acls', - - // use fixed path - path: undefined, - - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-acl', - proxy: { - type: 'proxmox', - url: "/api2/json/access/acl" - }, - sorters: { - property: 'path', - order: 'DESC' - } - }); - - if (me.path) { - store.addFilter(Ext.create('Ext.util.Filter',{ - filterFn: function(item) { - if (item.data.path === me.path) { - return true; - } - } - })); - } - - var render_ugid = function(ugid, metaData, record) { - if (record.data.type == 'group') { - return '@' + ugid; - } - - return ugid; - }; - - var columns = [ - { - header: gettext('User') + '/' + gettext('Group'), - flex: 1, - sortable: true, - renderer: render_ugid, - dataIndex: 'ugid' - }, - { - header: gettext('Role'), - flex: 1, - sortable: true, - dataIndex: 'roleid' - } - ]; - - if (!me.path) { - columns.unshift({ - header: gettext('Path'), - flex: 1, - sortable: true, - dataIndex: 'path' - }); - columns.push({ - header: gettext('Propagate'), - width: 80, - sortable: true, - dataIndex: 'propagate' - }); - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: gettext('Are you sure you want to remove this entry'), - handler: function(btn, event, rec) { - var params = { - 'delete': 1, - path: rec.data.path, - roles: rec.data.roleid - }; - if (rec.data.type === 'group') { - params.groups = rec.data.ugid; - } else if (rec.data.type === 'user') { - params.users = rec.data.ugid; - } else { - throw 'unknown data type'; - } - - Proxmox.Utils.API2Request({ - url: '/access/acl', - params: params, - method: 'PUT', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: { - xtype: 'menu', - items: [ - { - text: gettext('Group Permission'), - iconCls: 'fa fa-fw fa-group', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'group', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('User Permission'), - iconCls: 'fa fa-fw fa-user', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'user', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - } - ] - } - }, - remove_btn - ], - viewConfig: { - trackOver: false - }, - columns: columns, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-acl', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'ugid', 'roleid', - { - name: 'propagate', - type: 'boolean' - } - ] - }); - -}); -Ext.define('PVE.dc.AuthView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveAuthView'], - - onlineHelp: 'pveum_authentication_realms', - - stateful: true, - stateId: 'grid-authrealms', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-domains', - sorters: { - property: 'realm', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.AuthEdit',{ - realm: rec.data.realm, - authType: rec.data.type - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - baseurl: '/access/domains/', - selModel: sm, - enableFn: function(rec) { - return !(rec.data.type === 'pve' || rec.data.type === 'pam'); - }, - callback: function() { - reload(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Active Directory Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit', { - authType: 'ad' - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('LDAP Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit',{ - authType: 'ldap' - }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - edit_btn, remove_btn - ]; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Realm'), - width: 100, - sortable: true, - dataIndex: 'realm' - }, - { - header: gettext('Type'), - width: 100, - sortable: true, - dataIndex: 'type' - }, - { - header: gettext('TFA'), - width: 100, - sortable: true, - dataIndex: 'tfa' - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.AuthEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcAuthEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.realm; - - var url; - var method; - var serverlist; - - if (me.isCreate) { - url = '/api2/extjs/access/domains'; - method = 'POST'; - } else { - url = '/api2/extjs/access/domains/' + me.realm; - method = 'PUT'; - } - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'realm', - fieldLabel: gettext('Realm'), - value: me.realm, - allowBlank: false - } - ]; - - if (me.authType === 'ad') { - - me.subject = gettext('Active Directory Server'); - - column1.push({ - xtype: 'textfield', - name: 'domain', - fieldLabel: gettext('Domain'), - emptyText: 'company.net', - allowBlank: false - }); - - } else if (me.authType === 'ldap') { - - me.subject = gettext('LDAP Server'); - - column1.push({ - xtype: 'textfield', - name: 'base_dn', - fieldLabel: gettext('Base Domain Name'), - emptyText: 'CN=Users,DC=Company,DC=net', - allowBlank: false - }); - - column1.push({ - xtype: 'textfield', - name: 'user_attr', - emptyText: 'uid / sAMAccountName', - fieldLabel: gettext('User Attribute Name'), - allowBlank: false - }); - } else if (me.authType === 'pve') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'Proxmox VE authentication server'; - - } else if (me.authType === 'pam') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'linux PAM'; - - } else { - throw 'unknown auth type '; - } - - column1.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Default'), - name: 'default', - uncheckedValue: 0 - }); - - var column2 = []; - - if (me.authType === 'ldap' || me.authType === 'ad') { - column2.push( - { - xtype: 'textfield', - fieldLabel: gettext('Server'), - name: 'server1', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Fallback Server'), - deleteEmpty: !me.isCreate, - name: 'server2' - }, - { - xtype: 'proxmoxintegerfield', - name: 'port', - fieldLabel: gettext('Port'), - minValue: 1, - maxValue: 65535, - emptyText: gettext('Default'), - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'SSL', - name: 'secure', - uncheckedValue: 0 - } - ); - } - - // Two Factor Auth settings - - column2.push({ - xtype: 'proxmoxKVComboBox', - name: 'tfa', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('TFA'), - comboItems: [ ['__default__', Proxmox.Utils.noneText], ['oath', 'OATH'], ['yubico', 'Yubico']], - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=oath_step]').setVisible(value === 'oath'); - me.down('field[name=oath_digits]').setVisible(value === 'oath'); - me.down('field[name=yubico_api_id]').setVisible(value === 'yubico'); - me.down('field[name=yubico_api_key]').setVisible(value === 'yubico'); - me.down('field[name=yubico_url]').setVisible(value === 'yubico'); - } - } - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_step', - value: '', - minValue: 10, - emptyText: Proxmox.Utils.defaultText + ' (30)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH time step' - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_digits', - value: '', - minValue: 6, - maxValue: 8, - emptyText: Proxmox.Utils.defaultText + ' (6)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH password length' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_id', - hidden: true, - fieldLabel: 'Yubico API Id' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_key', - hidden: true, - fieldLabel: 'Yubico API Key' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_url', - hidden: true, - fieldLabel: 'Yubico URL' - }); - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [{ - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }], - onGetValues: function(values) { - if (!values.port) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'port' }); - } - delete values.port; - } - - if (me.isCreate) { - values.type = me.authType; - } - - if (values.tfa === 'oath') { - values.tfa = "type=oath"; - if (values.oath_step) { - values.tfa += ",step=" + values.oath_step; - } - if (values.oath_digits) { - values.tfa += ",digits=" + values.oath_digits; - } - } else if (values.tfa === 'yubico') { - values.tfa = "type=yubico"; - values.tfa += ",id=" + values.yubico_api_id; - values.tfa += ",key=" + values.yubico_api_key; - if (values.yubico_url) { - values.tfa += ",url=" + values.yubico_url; - } - } else { - delete values.tfa; - } - - delete values.oath_step; - delete values.oath_digits; - delete values.yubico_api_id; - delete values.yubico_api_key; - delete values.yubico_url; - - return values; - } - }); - - Ext.applyIf(me, { - url: url, - method: method, - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data || {}; - // just to be sure (should not happen) - if (data.type !== me.authType) { - me.close(); - throw "got wrong auth type"; - } - - if (data.tfa) { - var tfacfg = PVE.Parser.parseTfaConfig(data.tfa); - data.tfa = tfacfg.type; - if (tfacfg.type === 'yubico') { - data.yubico_api_key = tfacfg.key; - data.yubico_api_id = tfacfg.id; - data.yubico_url = tfacfg.url; - } else if (tfacfg.type === 'oath') { - // step is a number before - /*jslint confusion: true*/ - data.oath_step = tfacfg.step; - data.oath_digits = tfacfg.digits; - /*jslint confusion: false*/ - } - } - - me.setValues(data); - } - }); - } - } -}); -Ext.define('PVE.dc.BackupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcBackupEdit'], - - defaultFocus: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.jobid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/cluster/backup'; - method = 'POST'; - } else { - url = '/api2/extjs/cluster/backup/' + me.jobid; - method = 'PUT'; - } - - var vmidField = Ext.create('Ext.form.field.Hidden', { - name: 'vmid' - }); - - /*jslint confusion: true*/ - // 'value' can be assigned a string or an array - var selModeField = Ext.create('Proxmox.form.KVComboBox', { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['include', gettext('Include selected VMs')], - ['all', gettext('All')], - ['exclude', gettext('Exclude selected VMs')] - ], - fieldLabel: gettext('Selection mode'), - name: 'selMode', - value: '' - }); - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - var sel = []; - Ext.Array.each(selected, function(record) { - sel.push(record.data.vmid); - }); - - // to avoid endless recursion suspend the vmidField change - // event temporary as it calls us again - vmidField.suspendEvent('change'); - vmidField.setValue(sel); - vmidField.resumeEvent('change'); - } - } - }); - - var storagesel = Ext.create('PVE.form.StorageSelector', { - fieldLabel: gettext('Storage'), - nodename: 'localhost', - storageContent: 'backup', - allowBlank: false, - name: 'storage' - }); - - var store = new Ext.data.Store({ - model: 'PVEResources', - sorters: { - property: 'vmid', - order: 'ASC' - } - }); - - var vmgrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - disabled: true, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - - var nodesel = Ext.create('PVE.form.NodeSelector', { - name: 'node', - fieldLabel: gettext('Node'), - allowBlank: true, - editable: true, - autoSelect: false, - emptyText: '-- ' + gettext('All') + ' --', - listeners: { - change: function(f, value) { - storagesel.setNodename(value || 'localhost'); - var mode = selModeField.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!value || rec.get('node') === value); - }); - if (mode === 'all') { - sm.selectAll(true); - } - } - } - }); - - var column1 = [ - nodesel, - storagesel, - { - xtype: 'pveDayOfWeekSelector', - name: 'dow', - fieldLabel: gettext('Day of week'), - multiSelect: true, - value: ['sat'], - allowBlank: false - }, - { - xtype: 'timefield', - fieldLabel: gettext('Start Time'), - name: 'starttime', - format: 'H:i', - formatText: 'HH:MM', - value: '00:00', - allowBlank: false - }, - selModeField - ]; - - var column2 = [ - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto' - }, - { - xtype: 'pveEmailNotificationSelector', - fieldLabel: gettext('Email notification'), - name: 'mailnotification', - deleteEmpty: me.isCreate ? false : true, - value: me.isCreate ? 'always' : '' - }, - { - xtype: 'pveCompressionSelector', - fieldLabel: gettext('Compression'), - name: 'compress', - deleteEmpty: me.isCreate ? false : true, - value: 'lzo' - }, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable'), - name: 'enabled', - uncheckedValue: 0, - defaultValue: 1, - checked: true - }, - vmidField - ]; - /*jslint confusion: false*/ - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - onlineHelp: 'chapter_vzdump', - column1: column1, - column2: column2, - onGetValues: function(values) { - if (!values.node) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'node' }); - } - delete values.node; - } - - var selMode = values.selMode; - delete values.selMode; - - if (selMode === 'all') { - values.all = 1; - values.exclude = ''; - delete values.vmid; - } else if (selMode === 'exclude') { - values.all = 1; - values.exclude = values.vmid; - delete values.vmid; - } - return values; - } - }); - - var update_vmid_selection = function(list, mode) { - if (mode !== 'all') { - sm.deselectAll(true); - if (list) { - Ext.Array.each(list.split(','), function(vmid) { - var rec = store.findRecord('vmid', vmid); - if (rec) { - sm.select(rec, true); - } - }); - } - } - }; - - vmidField.on('change', function(f, value) { - var mode = selModeField.getValue(); - update_vmid_selection(value, mode); - }); - - selModeField.on('change', function(f, value, oldValue) { - if (value === 'all') { - sm.selectAll(true); - vmgrid.setDisabled(true); - } else { - vmgrid.setDisabled(false); - } - if (oldValue === 'all') { - sm.deselectAll(true); - vmidField.setValue(''); - } - var list = vmidField.getValue(); - update_vmid_selection(list, value); - }); - - var reload = function() { - store.load({ - params: { type: 'vm' }, - callback: function() { - var node = nodesel.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!node || node.length === 0 || rec.get('node') === node); - }); - var list = vmidField.getValue(); - var mode = selModeField.getValue(); - if (mode === 'all') { - sm.selectAll(true); - } else { - update_vmid_selection(list, mode); - } - } - }); - }; - - Ext.applyIf(me, { - subject: gettext("Backup Job"), - url: url, - method: method, - items: [ ipanel, vmgrid ] - }); - - me.callParent(); - - if (me.isCreate) { - selModeField.setValue('include'); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - - data.dow = data.dow.split(','); - - if (data.all || data.exclude) { - if (data.exclude) { - data.vmid = data.exclude; - data.selMode = 'exclude'; - } else { - data.vmid = ''; - data.selMode = 'all'; - } - } else { - data.selMode = 'include'; - } - - me.setValues(data); - } - }); - } - - reload(); - } -}); - - -Ext.define('PVE.dc.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveDcBackupView'], - - onlineHelp: 'chapter_vzdump', - - allText: '-- ' + gettext('All') + ' --', - allExceptText: gettext('All except {0}'), - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-cluster-backup', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/backup" - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.BackupEdit',{ - jobid: rec.data.id - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/backup', - callback: function() { - reload(); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - stateful: true, - stateId: 'grid-dc-backup', - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.dc.BackupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: gettext('Enabled'), - width: 80, - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true, - disabledCls: 'x-item-enabled', - stopSelection: false - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node', - renderer: function(value) { - if (value) { - return value; - } - return me.allText; - } - }, - { - header: gettext('Day of week'), - width: 200, - sortable: false, - dataIndex: 'dow', - renderer: function(val) { - var dows = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; - var selected = []; - var cur = -1; - val.split(',').forEach(function(day){ - cur++; - var dow = (dows.indexOf(day)+6)%7; - if (cur === dow) { - if (selected.length === 0 || selected[selected.length-1] === 0) { - selected.push(1); - } else { - selected[selected.length-1]++; - } - } else { - while (cur < dow) { - cur++; - selected.push(0); - } - selected.push(1); - } - }); - - cur = -1; - var days = []; - selected.forEach(function(item) { - cur++; - if (item > 2) { - days.push(Ext.Date.dayNames[(cur+1)] + '-' + Ext.Date.dayNames[(cur+item)%7]); - cur += item-1; - } else if (item == 2) { - days.push(Ext.Date.dayNames[cur+1]); - days.push(Ext.Date.dayNames[(cur+2)%7]); - cur++; - } else if (item == 1) { - days.push(Ext.Date.dayNames[(cur+1)%7]); - } - }); - return days.join(', '); - } - }, - { - header: gettext('Start Time'), - width: 60, - sortable: true, - dataIndex: 'starttime' - }, - { - header: gettext('Storage'), - width: 100, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Selection'), - flex: 1, - sortable: false, - dataIndex: 'vmid', - renderer: function(value, metaData, record) { - /*jslint confusion: true */ - if (record.data.all) { - if (record.data.exclude) { - return Ext.String.format(me.allExceptText, record.data.exclude); - } - return me.allText; - } - if (record.data.vmid) { - return record.data.vmid; - } - - return "-"; - } - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-cluster-backup', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'starttime', 'dow', - 'storage', 'node', 'vmid', 'exclude', - 'mailto', - { name: 'enabled', type: 'boolean' }, - { name: 'all', type: 'boolean' }, - { name: 'snapshot', type: 'boolean' }, - { name: 'stop', type: 'boolean' }, - { name: 'suspend', type: 'boolean' }, - { name: 'compress', type: 'boolean' } - ] - }); -}); -Ext.define('PVE.dc.Support', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSupport', - pveGuidePath: '/pve-docs/index.html', - onlineHelp: 'getting_help', - - invalidHtml: '

No valid subscription

' + PVE.Utils.noSubKeyHtml, - - communityHtml: 'Please use the public community forum for any questions.', - - activeHtml: 'Please use our support portal for any questions. You can also use the public community forum to get additional information.', - - bugzillaHtml: '

Bug Tracking

Our bug tracking system is available here.', - - docuHtml: function() { - var me = this; - var guideUrl = window.location.origin + me.pveGuidePath; - var text = Ext.String.format('

Documentation

' - + 'The official Proxmox VE Administration Guide' - + ' is included with this installation and can be browsed at ' - + '{0}', guideUrl); - return text; - }, - - updateActive: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.activeHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateCommunity: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.communityHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateInactive: function(data) { - var me = this; - me.update(me.invalidHtml); - }, - - initComponent: function() { - var me = this; - - var reload = function() { - Proxmox.Utils.API2Request({ - url: '/nodes/localhost/subscription', - method: 'GET', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.update('Unable to load subscription status' + ": " + response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data; - - if (data.status === 'Active') { - if (data.level === 'c') { - me.updateCommunity(data); - } else { - me.updateActive(data); - } - } else { - me.updateInactive(data); - } - } - }); - }; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('pve-security-groups', { - extend: 'Ext.data.Model', - - fields: [ 'group', 'comment', 'digest' ], - idProperty: 'group' -}); - -Ext.define('PVE.SecurityGroupEdit', { - extend: 'Proxmox.window.Edit', - - base_url: "/cluster/firewall/groups", - - allow_iface: false, - - initComponent : function() { - var me = this; - - me.isCreate = (me.group_name === undefined); - - var subject; - - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - - var items = [ - { - xtype: 'textfield', - name: 'group', - value: me.group_name || '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: me.group_comment || '', - fieldLabel: gettext('Comment') - } - ]; - - if (me.isCreate) { - subject = gettext('Security Group'); - } else { - subject = gettext('Security Group') + " '" + me.group_name + "'"; - items.push({ - xtype: 'hiddenfield', - name: 'rename', - value: me.group_name - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - // InputPanel does not have a 'create' property, does it need a 'isCreate' - isCreate: me.isCreate, - items: items - }); - - - Ext.apply(me, { - subject: subject, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.SecurityGroupList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveSecurityGroupList', - - stateful: true, - stateId: 'grid-securitygroups', - - rule_panel: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - base_url: "/cluster/firewall/groups", - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (me.rule_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-security-groups', - proxy: { - type: 'proxmox', - url: '/api2/json' + me.base_url - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('group', oldrec.data.group); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.SecurityGroupEdit', { - digest: rec.data.digest, - group_name: rec.data.group, - group_comment: rec.data.comment - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('PVE.SecurityGroupEdit', {}); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - enableFn: function(rec) { - return (rec && me.base_url); - }, - callback: function() { - reload(); - } - }); - - Ext.apply(me, { - store: store, - tbar: [ '' + gettext('Group') + ':', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: gettext('Group'), dataIndex: 'group', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = '/cluster/firewall/groups/' + rec.data.group; - me.rule_panel.setBaseUrl(url); - }, - deselect: function() { - me.rule_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.SecurityGroups', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveSecurityGroups', - - title: 'Security Groups', - - initComponent: function() { - var me = this; - - var rule_panel = Ext.createWidget('pveFirewallRules', { - region: 'center', - allow_groups: false, - list_refs_url: '/cluster/firewall/refs', - tbar_prefix: '' + gettext('Rules') + ':', - border: false - }); - - var sglist = Ext.createWidget('pveSecurityGroupList', { - region: 'west', - rule_panel: rule_panel, - width: '25%', - border: false, - split: true - }); - - - Ext.apply(me, { - layout: 'border', - items: [ sglist, rule_panel ], - listeners: { - show: function() { - sglist.fireEvent('show', sglist); - } - } - }); - - me.callParent(); - } -}); -/* - * Datacenter config panel, located in the center of the ViewPort after the Datacenter view is selected - */ - -Ext.define('PVE.dc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.dc.Config', - - onlineHelp: 'pve_admin_guide', - - initComponent: function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.items = []; - - Ext.apply(me, { - title: gettext("Datacenter"), - hstateid: 'dctab' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - title: gettext('Summary'), - xtype: 'pveDcSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - }, - { - title: gettext('Cluster'), - xtype: 'pveClusterAdministration', - iconCls: 'fa fa-server', - itemId: 'cluster' - }, - { - xtype: 'pveDcOptionView', - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options' - }); - } - - if (caps.storage['Datastore.Allocate'] || caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveStorageView', - title: gettext('Storage'), - iconCls: 'fa fa-database', - itemId: 'storage' - }); - } - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveDcBackupView', - iconCls: 'fa fa-floppy-o', - title: gettext('Backup'), - itemId: 'backup' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - expandedOnInit: true - }); - } - - me.items.push({ - xtype: 'pveUserView', - groups: ['permissions'], - iconCls: 'fa fa-user', - title: gettext('Users'), - itemId: 'users' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveGroupView', - title: gettext('Groups'), - iconCls: 'fa fa-users', - groups: ['permissions'], - itemId: 'groups' - }, - { - xtype: 'pvePoolView', - title: gettext('Pools'), - iconCls: 'fa fa-tags', - groups: ['permissions'], - itemId: 'pools' - }, - { - xtype: 'pveRoleView', - title: gettext('Roles'), - iconCls: 'fa fa-male', - groups: ['permissions'], - itemId: 'roles' - }, - { - xtype: 'pveAuthView', - title: gettext('Authentication'), - groups: ['permissions'], - iconCls: 'fa fa-key', - itemId: 'domains' - }, - { - xtype: 'pveHAStatus', - title: 'HA', - iconCls: 'fa fa-heartbeat', - itemId: 'ha' - }, - { - title: gettext('Groups'), - groups: ['ha'], - xtype: 'pveHAGroupsView', - iconCls: 'fa fa-object-group', - itemId: 'ha-groups' - }, - { - title: gettext('Fencing'), - groups: ['ha'], - iconCls: 'fa fa-bolt', - xtype: 'pveFencingView', - itemId: 'ha-fencing' - }, - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/cluster/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - iconCls: 'fa fa-shield', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - groups: ['firewall'], - iconCls: 'fa fa-gear', - base_url: '/cluster/firewall/options', - onlineHelp: 'pve_firewall_cluster_wide_setup', - fwtype: 'dc', - itemId: 'firewall-options' - }, - { - xtype: 'pveSecurityGroups', - title: gettext('Security Group'), - groups: ['firewall'], - iconCls: 'fa fa-group', - itemId: 'firewall-sg' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: '/cluster/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: 'IPSet', - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: '/cluster/firewall/ipset', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall-ipset' - }, - { - xtype: 'pveDcSupport', - title: gettext('Support'), - itemId: 'support', - iconCls: 'fa fa-comments-o' - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.dc.NodeView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveDcNodeView', - - title: gettext('Nodes'), - disableSelection: true, - scrollable: true, - - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: 'ID', - width: 40, - sortable: true, - dataIndex: 'nodeid' - }, - { - header: gettext('Online'), - width: 60, - sortable: true, - dataIndex: 'online', - renderer: function(value) { - var cls = (value)?'good':'critical'; - return ''; - } - }, - { - header: gettext('Support'), - width: 100, - sortable: true, - dataIndex: 'level', - renderer: PVE.Utils.render_support_level - }, - { - header: gettext('Server Address'), - width: 115, - sortable: true, - dataIndex: 'ip' - }, - { - header: gettext('CPU usage'), - sortable: true, - width: 110, - dataIndex: 'cpuusage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Memory usage'), - width: 110, - sortable: true, - tdCls: 'x-progressbar-default-cell', - dataIndex: 'memoryusage', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Uptime'), - sortable: true, - dataIndex: 'uptime', - align: 'right', - renderer: Proxmox.Utils.render_uptime - } - ], - - stateful: true, - stateId: 'grid-cluster-nodes', - tools: [ - { - type: 'up', - handler: function(){ - var me = this.up('grid'); - var height = Math.max(me.getHeight()-50, 250); - me.setHeight(height); - } - }, - { - type: 'down', - handler: function(){ - var me = this.up('grid'); - var height = me.getHeight()+50; - me.setHeight(height); - } - } - ] -}, function() { - - Ext.define('pve-dc-nodes', { - extend: 'Ext.data.Model', - fields: [ 'id', 'type', 'name', 'nodeid', 'ip', 'level', 'local', 'online'], - idProperty: 'id' - }); - -}); - -Ext.define('PVE.widget.ProgressBar',{ - extend: 'Ext.Progress', - alias: 'widget.pveProgressBar', - - animate: true, - textTpl: [ - '{percent}%' - ], - - setValue: function(value){ - var me = this; - me.callParent([value]); - - me.removeCls(['warning', 'critical']); - - if (value > 0.89) { - me.addCls('critical'); - } else if (value > 0.59) { - me.addCls('warning'); - } - } -}); -/*jslint confusion: true*/ -Ext.define('pve-cluster-nodes', { - extend: 'Ext.data.Model', - fields: [ - 'node', { type: 'integer', name: 'nodeid' }, 'ring0_addr', 'ring1_addr', - { type: 'integer', name: 'quorum_votes' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/nodes" - }, - idProperty: 'nodeid' -}); - -Ext.define('pve-cluster-info', { - extend: 'Ext.data.Model', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/join" - } -}); - -Ext.define('PVE.ClusterAdministration', { - extend: 'Ext.panel.Panel', - xtype: 'pveClusterAdministration', - - title: gettext('Cluster Administration'), - onlineHelp: 'chapter_pvecm', - - border: false, - defaults: { border: false }, - - viewModel: { - parent: null, - data: { - totem: {}, - nodelist: [], - preferred_node: { - name: '', - fp: '', - addr: '' - }, - isInCluster: false, - nodecount: 0 - } - }, - - items: [ - { - xtype: 'panel', - title: gettext('Cluster Information'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store = Ext.create('Proxmox.data.UpdateStore', { - autoStart: true, - interval: 15 * 1000, - storeid: 'pve-cluster-info', - model: 'pve-cluster-info' - }); - view.store.on('load', this.onLoad, this); - view.on('destroy', view.store.stopUpdate); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records || !records[0].data) { - vm.set('totem', {}); - vm.set('isInCluster', false); - vm.set('nodelist', []); - vm.set('preferred_node', { - name: '', - addr: '', - fp: '' - }); - return; - } - var data = records[0].data; - vm.set('totem', data.totem); - vm.set('isInCluster', !!data.totem.cluster_name); - vm.set('nodelist', data.nodelist); - - var nodeinfo = Ext.Array.findBy(data.nodelist, function (el) { - return el.name === data.preferred_node; - }); - - vm.set('preferred_node', { - name: data.preferred_node, - addr: nodeinfo.pve_addr, - ring_addr: [ nodeinfo.ring0_addr, nodeinfo.ring1_addr ], - fp: nodeinfo.pve_fp - }); - }, - - onCreate: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterCreateWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - }, - - onClusterInfo: function() { - var vm = this.getViewModel(); - var win = Ext.create('PVE.ClusterInfoWindow', { - joinInfo: { - ipAddress: vm.get('preferred_node.addr'), - fingerprint: vm.get('preferred_node.fp'), - ring_addr: vm.get('preferred_node.ring_addr'), - totem: vm.get('totem') - } - }); - win.show(); - }, - - onJoin: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterJoinNodeWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - } - }, - tbar: [ - { - text: gettext('Create Cluster'), - reference: 'createButton', - handler: 'onCreate', - bind: { - disabled: '{isInCluster}' - } - }, - { - text: gettext('Join Information'), - reference: 'addButton', - handler: 'onClusterInfo', - bind: { - disabled: '{!isInCluster}' - } - }, - { - text: gettext('Join Cluster'), - reference: 'joinButton', - handler: 'onJoin', - bind: { - disabled: '{isInCluster}' - } - } - ], - layout: 'hbox', - bodyPadding: 5, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Cluster Name'), - bind: { - value: '{totem.cluster_name}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Config Version'), - bind: { - value: '{totem.config_version}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Number of Nodes'), - labelWidth: 120, - bind: { - value: '{nodecount}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - value: gettext('Standalone node - no cluster defined'), - bind: { - hidden: '{isInCluster}' - }, - flex: 1 - } - ] - }, - { - xtype: 'grid', - title: gettext('Cluster Nodes'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-cluster-nodes', - model: 'pve-cluster-nodes' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'nodeid', - order: 'DESC' - } - })); - Proxmox.Utils.monStoreErrors(view, view.rstore); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records) { - vm.set('nodecount', 0); - return; - } - vm.set('nodecount', records.length); - } - }, - columns: [ - { - header: gettext('Nodename'), - flex: 2, - dataIndex: 'name' - }, - { - header: gettext('ID'), - flex: 1, - dataIndex: 'nodeid' - }, - { - header: gettext('Votes'), - flex: 1, - dataIndex: 'quorum_votes' - }, - { - header: gettext('Ring 0'), - flex: 2, - dataIndex: 'ring0_addr' - }, - { - header: gettext('Ring 1'), - flex: 2, - dataIndex: 'ring1_addr' - } - ] - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.ClusterCreateWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterCreateWindow', - - title: gettext('Create Cluster'), - width: 600, - - method: 'POST', - url: '/cluster/config', - - isCreate: true, - subject: gettext('Cluster'), - showTaskViewer: true, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Cluster Name'), - allowBlank: false, - name: 'clustername' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Ring 0 Address'), - emptyText: gettext("Optional, defaults to IP resolved by node's hostname"), - name: 'ring0_addr', - skipEmptyText: true - } - // TODO: for advanced options: ring1_addr - ] -}); - -Ext.define('PVE.ClusterInfoWindow', { - extend: 'Ext.window.Window', - xtype: 'pveClusterInfoWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 800, - modal: true, - resizable: false, - title: gettext('Cluster Join Information'), - - joinInfo: { - ipAddress: undefined, - fingerprint: undefined, - totem: {} - }, - - items: [ - { - xtype: 'component', - border: false, - padding: '10 10 10 10', - html: gettext("Copy the Join Information here and use it on the node you want to add.") - }, - { - xtype: 'container', - layout: 'form', - border: false, - padding: '0 10 10 10', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('IP Address'), - cbind: { value: '{joinInfo.ipAddress}' }, - editable: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - cbind: { value: '{joinInfo.fingerprint}' }, - editable: false - }, - { - xtype: 'textarea', - inputId: 'pveSerializedClusterInfo', - fieldLabel: gettext('Join Information'), - grow: true, - cbind: { joinInfo: '{joinInfo}' }, - editable: false, - listeners: { - afterrender: function(field) { - if (!field.joinInfo) { - return; - } - var jsons = Ext.JSON.encode(field.joinInfo); - var base64s = Ext.util.Base64.encode(jsons); - field.setValue(base64s); - } - } - } - ] - } - ], - dockedItems: [{ - dock: 'bottom', - xtype: 'toolbar', - items: [{ - xtype: 'button', - handler: function(b) { - var el = document.getElementById('pveSerializedClusterInfo'); - el.select(); - document.execCommand("copy"); - }, - text: gettext('Copy Information') - }] - }] -}); - -Ext.define('PVE.ClusterJoinNodeWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterJoinNodeWindow', - - title: gettext('Cluster Join'), - width: 800, - - method: 'POST', - url: '/cluster/config/join', - - defaultFocus: 'textarea[name=serializedinfo]', - isCreate: true, - submitText: gettext('Join'), - showTaskViewer: true, - - onlineHelp: 'chapter_pvecm', - - viewModel: { - parent: null, - data: { - info: { - fp: '', - ip: '', - ring0Needed: false, - ring1Possible: false, - ring1Needed: false - } - }, - formulas: { - ring0EmptyText: function(get) { - if (get('info.ring0Needed')) { - return gettext("Cannot use default address safely"); - } else { - return gettext("Default: IP resolved by node's hostname"); - } - } - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - '#': { - close: function() { - delete PVE.Utils.silenceAuthFailures; - } - }, - 'proxmoxcheckbox[name=assistedEntry]': { - change: 'onInputTypeChange' - }, - 'textarea[name=serializedinfo]': { - change: 'recomputeSerializedInfo', - enable: 'resetField' - }, - 'proxmoxtextfield[name=ring1_addr]': { - enable: 'ring1Needed' - }, - 'textfield': { - disable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - ring1Needed: function(f) { - var vm = this.getViewModel(); - f.allowBlank = !vm.get('info.ring1Needed'); - }, - onInputTypeChange: function(field, assistedInput) { - var vm = this.getViewModel(); - if (!assistedInput) { - vm.set('info.ring1Possible', true); - } - }, - recomputeSerializedInfo: function(field, value) { - var vm = this.getViewModel(); - var jsons = Ext.util.Base64.decode(value); - var joinInfo = Ext.JSON.decode(jsons, true); - - var info = { - fp: '', - ring1Needed: false, - ring1Possible: false, - ip: '' - }; - - var totem = {}; - if (!(joinInfo && joinInfo.totem)) { - field.valid = false; - } else { - var ring0Needed = false; - if (joinInfo.ring_addr !== undefined) { - ring0Needed = joinInfo.ring_addr[0] !== joinInfo.ipAddress; - } - - info = { - ip: joinInfo.ipAddress, - fp: joinInfo.fingerprint, - ring0Needed: ring0Needed, - ring1Possible: !!joinInfo.totem['interface']['1'], - ring1Needed: !!joinInfo.totem['interface']['1'] - }; - totem = joinInfo.totem; - field.valid = true; - } - - vm.set('info', info); - } - }, - - submit: function() { - // joining may produce temporarily auth failures, ignore as long the task runs - PVE.Utils.silenceAuthFailures = true; - this.callParent(); - }, - - taskDone: function(success) { - delete PVE.Utils.silenceAuthFailures; - if (success) { - var txt = gettext('Cluster join task finished, node certificate may have changed, reload GUI!'); - // ensure user cannot do harm - Ext.getBody().mask(txt, ['pve-static-mask']); - // TaskView may hide above mask, so tell him directly - Ext.Msg.show({ - title: gettext('Join Task Finished'), - icon: Ext.Msg.INFO, - msg: txt - }); - // reload always (if user wasn't faster), but wait a bit for pveproxy - Ext.defer(function() { - window.location.reload(true); - }, 5000); - } - }, - - items: [{ - xtype: 'proxmoxcheckbox', - reference: 'assistedEntry', - name: 'assistedEntry', - submitValue: false, - value: true, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Select if join information should be extracted from pasted cluster information, deselect for manual entering') - }, - boxLabel: gettext('Assisted join: Paste encoded cluster join information and enter password.') - }, - { - xtype: 'textarea', - name: 'serializedinfo', - submitValue: false, - allowBlank: false, - fieldLabel: gettext('Information'), - emptyText: gettext('Paste encoded Cluster Information here'), - validator: function(val) { - return val === '' || this.valid || - gettext('Does not seem like a valid encoded Cluster Information!'); - }, - bind: { - disabled: '{!assistedEntry.checked}', - hidden: '{!assistedEntry.checked}' - }, - value: '' - }, - { - xtype: 'inputpanel', - column1: [ - { - xtype: 'textfield', - fieldLabel: gettext('Peer Address'), - allowBlank: false, - bind: { - value: '{info.ip}', - readOnly: '{assistedEntry.checked}' - }, - name: 'hostname' - }, - { - xtype: 'textfield', - inputType: 'password', - emptyText: gettext("Peer's root password"), - fieldLabel: gettext('Password'), - allowBlank: false, - name: 'password' - } - ], - column2: [ - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Corosync Ring 0'), - bind: { - emptyText: '{ring0EmptyText}', - allowBlank: '{!info.ring0Needed}' - }, - skipEmptyText: true, - name: 'ring0_addr' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Corosync Ring 1'), - skipEmptyText: true, - bind: { - disabled: '{!info.ring1Possible}' - }, - name: 'ring1_addr' - } - ], - columnB: [ - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - allowBlank: false, - bind: { - value: '{info.fp}', - readOnly: '{assistedEntry.checked}' - }, - name: 'fingerprint' - } - ] - }] -}); -/* - * Workspace base class - * - * popup login window when auth fails (call onLogin handler) - * update (re-login) ticket every 15 minutes - * - */ - -Ext.define('PVE.Workspace', { - extend: 'Ext.container.Viewport', - - title: 'Proxmox Virtual Environment', - - loginData: null, // Data from last login call - - onLogin: function(loginData) {}, - - // private - updateLoginData: function(loginData) { - var me = this; - me.loginData = loginData; - Proxmox.Utils.setAuthData(loginData); - - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(loginData.clustername); - - if (loginData.cap) { - Ext.state.Manager.set('GuiCap', loginData.cap); - } - - me.onLogin(loginData); - }, - - // private - showLogin: function() { - var me = this; - - Proxmox.Utils.authClear(); - Proxmox.UserName = null; - me.loginData = null; - - if (!me.login) { - me.login = Ext.create('PVE.window.LoginWindow', { - handler: function(data) { - me.login = null; - me.updateLoginData(data); - Proxmox.Utils.checked_command(function() {}); // display subscription status - } - }); - } - me.onLogin(null); - me.login.show(); - }, - - initComponent : function() { - var me = this; - - Ext.tip.QuickTipManager.init(); - - // fixme: what about other errors - Ext.Ajax.on('requestexception', function(conn, response, options) { - if (response.status == 401 && !PVE.Utils.silenceAuthFailures) { // auth failure - me.showLogin(); - } - }); - - me.callParent(); - - if (!Proxmox.Utils.authOK()) { - me.showLogin(); - } else { - if (me.loginData) { - me.onLogin(me.loginData); - } - } - - Ext.TaskManager.start({ - run: function() { - var ticket = Proxmox.Utils.authOK(); - if (!ticket || !Proxmox.UserName) { - return; - } - - Ext.Ajax.request({ - params: { - username: Proxmox.UserName, - password: ticket - }, - url: '/api2/json/access/ticket', - method: 'POST', - success: function(response, opts) { - var obj = Ext.decode(response.responseText); - me.updateLoginData(obj.data); - } - }); - }, - interval: 15*60*1000 - }); - - } -}); - -Ext.define('PVE.StdWorkspace', { - extend: 'PVE.Workspace', - - alias: ['widget.pveStdWorkspace'], - - // private - setContent: function(comp) { - var me = this; - - var cont = me.child('#content'); - - var lay = cont.getLayout(); - - var cur = lay.getActiveItem(); - - if (comp) { - Proxmox.Utils.setErrorMask(cont, false); - comp.border = false; - cont.add(comp); - if (cur !== null && lay.getNext()) { - lay.next(); - var task = Ext.create('Ext.util.DelayedTask', function(){ - cont.remove(cur); - }); - task.delay(10); - } - } - else { - // helper for cleaning the content when logging out - cont.removeAll(); - } - }, - - selectById: function(nodeid) { - var me = this; - var tree = me.down('pveResourceTree'); - tree.selectById(nodeid); - }, - - onLogin: function(loginData) { - var me = this; - - me.updateUserInfo(); - - if (loginData) { - PVE.data.ResourceStore.startUpdate(); - - Proxmox.Utils.API2Request({ - url: '/version', - method: 'GET', - success: function(response) { - PVE.VersionInfo = response.result.data; - me.updateVersionInfo(); - } - }); - } - }, - - updateUserInfo: function() { - var me = this; - - var ui = me.query('#userinfo')[0]; - - if (Proxmox.UserName) { - var msg = Ext.String.format(gettext("You are logged in as {0}"), "'" + Proxmox.UserName + "'"); - ui.update('
' + msg + '
'); - } else { - ui.update(''); - } - ui.updateLayout(); - }, - - updateVersionInfo: function() { - var me = this; - - var ui = me.query('#versioninfo')[0]; - - if (PVE.VersionInfo) { - var version = PVE.VersionInfo.version + '-' + PVE.VersionInfo.release; - ui.update('Virtual Environment ' + version); - } else { - ui.update('Virtual Environment'); - } - ui.updateLayout(); - }, - - initComponent : function() { - var me = this; - - Ext.History.init(); - - var sprovider = Ext.create('PVE.StateProvider'); - Ext.state.Manager.setProvider(sprovider); - - var selview = Ext.create('PVE.form.ViewSelector'); - - var rtree = Ext.createWidget('pveResourceTree', { - viewFilter: selview.getViewFilter(), - flex: 1, - selModel: { - selType: 'treemodel', - listeners: { - selectionchange: function(sm, selected) { - if (selected.length > 0) { - var n = selected[0]; - var tlckup = { - root: 'PVE.dc.Config', - node: 'PVE.node.Config', - qemu: 'PVE.qemu.Config', - lxc: 'PVE.lxc.Config', - storage: 'PVE.storage.Browser', - pool: 'pvePoolConfig' - }; - var comp = { - xtype: tlckup[n.data.type || 'root'] || - 'pvePanelConfig', - showSearch: (n.data.id === 'root') || - Ext.isDefined(n.data.groupbyid), - pveSelNode: n, - workspace: me, - viewFilter: selview.getViewFilter() - }; - PVE.curSelectedNode = n; - me.setContent(comp); - } - } - } - } - }); - - selview.on('select', function(combo, records) { - if (records) { - var view = combo.getViewFilter(); - rtree.setViewFilter(view); - } - }); - - var caps = sprovider.get('GuiCap'); - - var createVM = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-desktop', - text: gettext("Create VM"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.qemu.CreateWizard', {}); - wiz.show(); - } - }); - - var createCT = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-cube', - text: gettext("Create CT"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.lxc.CreateWizard', {}); - wiz.show(); - } - }); - - sprovider.on('statechange', function(sp, key, value) { - if (key === 'GuiCap' && value) { - caps = value; - createVM.setDisabled(!caps.vms['VM.Allocate']); - createCT.setDisabled(!caps.vms['VM.Allocate']); - } - }); - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - { - region: 'north', - layout: { - type: 'hbox', - align: 'middle' - }, - baseCls: 'x-plain', - defaults: { - baseCls: 'x-plain' - }, - border: false, - margin: '2 0 2 5', - items: [ - { - html: '' + - '' - }, - { - minWidth: 150, - id: 'versioninfo', - html: 'Virtual Environment' - }, - { - xtype: 'pveGlobalSearchField', - tree: rtree - }, - { - flex: 1 - }, - { - pack: 'end', - id: 'userinfo', - stateful: false - }, - { - xtype: 'button', - margin: '0 10 0 3', - iconCls: 'fa black fa-gear', - userCls: 'pointer', - handler: function() { - var win = Ext.create('PVE.window.Settings'); - win.show(); - } - }, - { - xtype: 'proxmoxHelpButton', - hidden: false, - baseCls: 'x-btn', - iconCls: 'fa fa-book x-btn-icon-el-default-toolbar-small ', - listenToGlobalEvent: false, - onlineHelp: 'pve_documentation_index', - text: gettext('Documentation'), - margin: '0 5 0 0' - }, - createVM, - createCT, - { - pack: 'end', - margin: '0 5 0 0', - xtype: 'button', - baseCls: 'x-btn', - iconCls: 'fa fa-sign-out', - text: gettext("Logout"), - handler: function() { - PVE.data.ResourceStore.loadData([], false); - me.showLogin(); - me.setContent(null); - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(undefined); - rt.clearTree(); - - // empty the stores of the StatusPanel child items - var statusPanels = Ext.ComponentQuery.query('pveStatusPanel grid'); - Ext.Array.forEach(statusPanels, function(comp) { - if (comp.getStore()) { - comp.getStore().loadData([], false); - } - }); - } - } - ] - }, - { - region: 'center', - stateful: true, - stateId: 'pvecenter', - minWidth: 100, - minHeight: 100, - id: 'content', - xtype: 'container', - layout: { type: 'card' }, - border: false, - margin: '0 5 0 0', - items: [] - }, - { - region: 'west', - stateful: true, - stateId: 'pvewest', - itemId: 'west', - xtype: 'container', - border: false, - layout: { type: 'vbox', align: 'stretch' }, - margin: '0 0 0 5', - split: true, - width: 200, - items: [ selview, rtree ], - listeners: { - resize: function(panel, width, height) { - var viewWidth = me.getSize().width; - if (width > viewWidth - 100) { - panel.setWidth(viewWidth - 100); - } - } - } - }, - { - xtype: 'pveStatusPanel', - stateful: true, - stateId: 'pvesouth', - itemId: 'south', - region: 'south', - margin:'0 5 5 5', - title: gettext('Logs'), - collapsible: true, - header: false, - height: 200, - split:true, - listeners: { - resize: function(panel, width, height) { - var viewHeight = me.getSize().height; - if (height > (viewHeight - 150)) { - panel.setHeight(viewHeight - 150); - } - } - } - } - ] - }); - - me.callParent(); - - me.updateUserInfo(); - - // on resize, center all modal windows - Ext.on('resize', function(){ - var wins = Ext.ComponentQuery.query('window[modal]'); - if (wins.length > 0) { - wins.forEach(function(win){ - win.alignTo(me, 'c-c'); - }); - } - }); - } -}); - diff --git a/serverside/jsmod/6.0-4.sh b/serverside/jsmod/6.0-4.sh deleted file mode 100644 index 66c0f54..0000000 --- a/serverside/jsmod/6.0-4.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -Say () { - printf "\e[1;34m $1 \e[0m \n"; -} - -DotSay () { - printf "[\e[1;34m*\e[0m] \e[1;34m $1 \e[0m \n"; -} - - -Say '[PVE Discord Dark UI Theme JSMOD Installer]' -Say 'Internet connection REQUIRED.' -Say '!!ONLY FOR PVE 6.0-4 - 6.1-x!!' -Say '>Press any key to begin installation' -read -p "" -Say ' ' -DotSay 'Backing up files' -cp /usr/share/pve-manager/js/pvemanagerlib.js /usr/share/pve-manager/js/pvemanagerlib.js.bak -cp /usr/share/javascript/extjs/charts.js /usr/share/javascript/extjs/charts.js.bak -cp /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.bak -DotSay 'Replacing files with modded versions' -rm /usr/share/pve-manager/js/pvemanagerlib.js -wget https://raw.githubusercontent.com/Weilbyte/PVEDiscordDark/master/serverside/jsmod/6.0-4/pvemanagerlib.js -P /usr/share/pve-manager/js/ &> /dev/null -rm /usr/share/javascript/extjs/charts.js -wget https://raw.githubusercontent.com/Weilbyte/PVEDiscordDark/master/serverside/jsmod/6.0-4/charts.js -P /usr/share/javascript/extjs/ &> /dev/null -rm /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js -wget https://raw.githubusercontent.com/Weilbyte/PVEDiscordDark/master/serverside/jsmod/6.0-4/proxmoxlib.js -P /usr/share/javascript/proxmox-widget-toolkit/ &> /dev/null -DotSay 'Applied successfully.' -Say '' -Say 'Installation finished!' -Say 'o7' diff --git a/serverside/jsmod/6.0-4/charts.js b/serverside/jsmod/6.0-4/charts.js deleted file mode 100644 index 713bec3..0000000 --- a/serverside/jsmod/6.0-4/charts.js +++ /dev/null @@ -1,22013 +0,0 @@ -Ext.define("Ext.draw.ContainerBase", { - extend: "Ext.panel.Panel", - requires: ["Ext.window.Window"], - previewTitleText: "Chart Preview", - previewAltText: "Chart preview", - layout: "container", - addElementListener: function() { - var b = this, - a = arguments; - if (b.rendered) { - b.el.on.apply(b.el, a) - } else { - b.on("render", function() { - b.el.on.apply(b.el, a) - }) - } - }, - removeElementListener: function() { - var b = this, - a = arguments; - if (b.rendered) { - b.el.un.apply(b.el, a) - } - }, - afterRender: function() { - this.callParent(arguments); - this.initAnimator() - }, - getItems: function() { - var b = this, - a = b.items; - if (!a || !a.isMixedCollection) { - b.initItems() - } - return b.items - }, - onRender: function() { - this.callParent(arguments); - this.element = this.el; - this.innerElement = this.body - }, - setItems: function(a) { - this.items = a; - return a - }, - setSurfaceSize: function(b, a) { - this.resizeHandler({ - width: b, - height: a - }); - this.renderFrame() - }, - onResize: function(c, a, b, e) { - var d = this; - d.callParent([c, a, b, e]); - d.setBodySize({ - width: c, - height: a - }) - }, - preview: function() { - var a = this.getImage(); - new Ext.window.Window({ - title: this.previewTitleText, - closeable: true, - renderTo: Ext.getBody(), - autoShow: true, - maximizeable: true, - maximized: true, - border: true, - layout: { - type: "hbox", - pack: "center", - align: "middle" - }, - items: { - xtype: "container", - items: { - xtype: "image", - mode: "img", - cls: Ext.baseCSSPrefix + "chart-image", - alt: this.previewAltText, - src: a.data, - listeners: { - afterrender: function() { - var e = this, - b = e.imgEl.dom, - d = a.type === "svg" ? 1 : (window.devicePixelRatio || 1), - c; - if (!b.naturalWidth || !b.naturalHeight) { - b.onload = function() { - var g = b.naturalWidth, - f = b.naturalHeight; - e.setWidth(Math.floor(g / d)); - e.setHeight(Math.floor(f / d)) - } - } else { - c = e.getSize(); - e.setWidth(Math.floor(c.width / d)); - e.setHeight(Math.floor(c.height / d)) - } - } - } - } - } - }) - }, - privates: { - getTargetEl: function() { - return this.innerElement - }, - reattachToBody: function() { - var a = this; - if (a.pendingDetachSize) { - a.onBodyResize() - } - a.pendingDetachSize = false; - a.callParent() - } - } -}); -Ext.define("Ext.draw.SurfaceBase", { - extend: "Ext.Widget", - getOwnerBody: function() { - return this.ownerCt.body - }, - destroy: function() { - var a = this; - if (a.hasListeners.destroy) { - a.fireEvent("destroy", a) - } - a.callParent() - } -}); -Ext.define("Ext.draw.Color", { - statics: { - colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/, - rgbToHexRe: /\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/, - rgbaToHexRe: /\s*rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\.\d]+)\)/, - hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/, - NONE: "none", - RGBA_NONE: "rgba(0, 0, 0, 0)" - }, - isColor: true, - lightnessFactor: 0.2, - constructor: function(d, b, a, c) { - this.setRGB(d, b, a, c) - }, - setRGB: function(e, c, a, d) { - var b = this; - b.r = Math.min(255, Math.max(0, e)); - b.g = Math.min(255, Math.max(0, c)); - b.b = Math.min(255, Math.max(0, a)); - if (d === undefined) { - b.a = 1 - } else { - b.a = Math.min(1, Math.max(0, d)) - } - }, - getGrayscale: function() { - return this.r * 0.3 + this.g * 0.59 + this.b * 0.11 - }, - getHSL: function() { - var i = this, - a = i.r / 255, - f = i.g / 255, - j = i.b / 255, - k = Math.max(a, f, j), - d = Math.min(a, f, j), - m = k - d, - e, n = 0, - c = 0.5 * (k + d); - if (d !== k) { - n = (c <= 0.5) ? m / (k + d) : m / (2 - k - d); - if (a === k) { - e = 60 * (f - j) / m - } else { - if (f === k) { - e = 120 + 60 * (j - a) / m - } else { - e = 240 + 60 * (a - f) / m - } - } - if (e < 0) { - e += 360 - } - if (e >= 360) { - e -= 360 - } - } - return [e, n, c] - }, - getHSV: function() { - var i = this, - a = i.r / 255, - f = i.g / 255, - j = i.b / 255, - k = Math.max(a, f, j), - d = Math.min(a, f, j), - c = k - d, - e, m = 0, - l = k; - if (d != k) { - m = l ? c / l : 0; - if (a === k) { - e = 60 * (f - j) / c - } else { - if (f === k) { - e = 60 * (j - a) / c + 120 - } else { - e = 60 * (a - f) / c + 240 - } - } - if (e < 0) { - e += 360 - } - if (e >= 360) { - e -= 360 - } - } - return [e, m, l] - }, - setHSL: function(g, f, e) { - var i = this, - d = Math.abs, - j, b, a; - g = (g % 360 + 360) % 360; - f = f > 1 ? 1 : f < 0 ? 0 : f; - e = e > 1 ? 1 : e < 0 ? 0 : e; - if (f === 0 || g === null) { - e *= 255; - i.setRGB(e, e, e) - } else { - g /= 60; - j = f * (1 - d(2 * e - 1)); - b = j * (1 - d(g % 2 - 1)); - a = e - j / 2; - a *= 255; - j *= 255; - b *= 255; - switch (Math.floor(g)) { - case 0: - i.setRGB(j + a, b + a, a); - break; - case 1: - i.setRGB(b + a, j + a, a); - break; - case 2: - i.setRGB(a, j + a, b + a); - break; - case 3: - i.setRGB(a, b + a, j + a); - break; - case 4: - i.setRGB(b + a, a, j + a); - break; - case 5: - i.setRGB(j + a, a, b + a); - break - } - } - return i - }, - setHSV: function(f, e, d) { - var g = this, - i, b, a; - f = (f % 360 + 360) % 360; - e = e > 1 ? 1 : e < 0 ? 0 : e; - d = d > 1 ? 1 : d < 0 ? 0 : d; - if (e === 0 || f === null) { - d *= 255; - g.setRGB(d, d, d) - } else { - f /= 60; - i = d * e; - b = i * (1 - Math.abs(f % 2 - 1)); - a = d - i; - a *= 255; - i *= 255; - b *= 255; - switch (Math.floor(f)) { - case 0: - g.setRGB(i + a, b + a, a); - break; - case 1: - g.setRGB(b + a, i + a, a); - break; - case 2: - g.setRGB(a, i + a, b + a); - break; - case 3: - g.setRGB(a, b + a, i + a); - break; - case 4: - g.setRGB(b + a, a, i + a); - break; - case 5: - g.setRGB(i + a, a, b + a); - break - } - } - return g - }, - createLighter: function(b) { - if (!b && b !== 0) { - b = this.lightnessFactor - } - var a = this.getHSL(); - a[2] = Ext.Number.constrain(a[2] + b, 0, 1); - return Ext.draw.Color.fromHSL(a[0], a[1], a[2]) - }, - createDarker: function(a) { - if (!a && a !== 0) { - a = this.lightnessFactor - } - return this.createLighter(-a) - }, - toString: function() { - var f = this, - c = Math.round; - if (f.a === 1) { - var e = c(f.r).toString(16), - d = c(f.g).toString(16), - a = c(f.b).toString(16); - e = (e.length === 1) ? "0" + e : e; - d = (d.length === 1) ? "0" + d : d; - a = (a.length === 1) ? "0" + a : a; - return ["#", e, d, a].join("") - } else { - return "rgba(" + [c(f.r), c(f.g), c(f.b), f.a === 0 ? 0 : f.a.toFixed(15)].join(", ") + ")" - } - }, - toHex: function(b) { - if (Ext.isArray(b)) { - b = b[0] - } - if (!Ext.isString(b)) { - return "" - } - if (b.substr(0, 1) === "#") { - return b - } - var e = Ext.draw.Color.colorToHexRe.exec(b); - if (Ext.isArray(e)) { - var f = parseInt(e[2], 10), - d = parseInt(e[3], 10), - a = parseInt(e[4], 10), - c = a | (d << 8) | (f << 16); - return e[1] + "#" + ("000000" + c.toString(16)).slice(-6) - } else { - return "" - } - }, - setFromString: function(j) { - var e, h, f, c, d = 1, - i = parseInt; - if (j === Ext.draw.Color.NONE) { - this.r = this.g = this.b = this.a = 0; - return this - } - if ((j.length === 4 || j.length === 7) && j.substr(0, 1) === "#") { - e = j.match(Ext.draw.Color.hexRe); - if (e) { - h = i(e[1], 16) >> 0; - f = i(e[2], 16) >> 0; - c = i(e[3], 16) >> 0; - if (j.length === 4) { - h += (h * 16); - f += (f * 16); - c += (c * 16) - } - } - } else { - if ((e = j.match(Ext.draw.Color.rgbToHexRe))) { - h = +e[1]; - f = +e[2]; - c = +e[3] - } else { - if ((e = j.match(Ext.draw.Color.rgbaToHexRe))) { - h = +e[1]; - f = +e[2]; - c = +e[3]; - d = +e[4] - } else { - if (Ext.draw.Color.ColorList.hasOwnProperty(j.toLowerCase())) { - return this.setFromString(Ext.draw.Color.ColorList[j.toLowerCase()]) - } - } - } - } - if (typeof h === "undefined") { - return this - } - this.r = h; - this.g = f; - this.b = c; - this.a = d; - return this - } -}, function() { - var a = new this(); - this.addStatics({ - fly: function(f, e, c, d) { - switch (arguments.length) { - case 1: - a.setFromString(f); - break; - case 3: - case 4: - a.setRGB(f, e, c, d); - break; - default: - return null - } - return a - }, - ColorList: { - aliceblue: "#f0f8ff", - antiquewhite: "#faebd7", - aqua: "#00ffff", - aquamarine: "#7fffd4", - azure: "#f0ffff", - beige: "#f5f5dc", - bisque: "#ffe4c4", - black: "#000000", - blanchedalmond: "#ffebcd", - blue: "#0000ff", - blueviolet: "#8a2be2", - brown: "#a52a2a", - burlywood: "#deb887", - cadetblue: "#5f9ea0", - chartreuse: "#7fff00", - chocolate: "#d2691e", - coral: "#ff7f50", - cornflowerblue: "#6495ed", - cornsilk: "#fff8dc", - crimson: "#dc143c", - cyan: "#00ffff", - darkblue: "#00008b", - darkcyan: "#008b8b", - darkgoldenrod: "#b8860b", - darkgray: "#a9a9a9", - darkgreen: "#006400", - darkkhaki: "#bdb76b", - darkmagenta: "#8b008b", - darkolivegreen: "#556b2f", - darkorange: "#ff8c00", - darkorchid: "#9932cc", - darkred: "#8b0000", - darksalmon: "#e9967a", - darkseagreen: "#8fbc8f", - darkslateblue: "#483d8b", - darkslategray: "#2f4f4f", - darkturquoise: "#00ced1", - darkviolet: "#9400d3", - deeppink: "#ff1493", - deepskyblue: "#00bfff", - dimgray: "#696969", - dodgerblue: "#1e90ff", - firebrick: "#b22222", - floralwhite: "#fffaf0", - forestgreen: "#228b22", - fuchsia: "#ff00ff", - gainsboro: "#dcdcdc", - ghostwhite: "#f8f8ff", - gold: "#ffd700", - goldenrod: "#daa520", - gray: "#808080", - green: "#008000", - greenyellow: "#adff2f", - honeydew: "#f0fff0", - hotpink: "#ff69b4", - indianred: "#cd5c5c", - indigo: "#4b0082", - ivory: "#fffff0", - khaki: "#f0e68c", - lavender: "#e6e6fa", - lavenderblush: "#fff0f5", - lawngreen: "#7cfc00", - lemonchiffon: "#fffacd", - lightblue: "#add8e6", - lightcoral: "#f08080", - lightcyan: "#e0ffff", - lightgoldenrodyellow: "#fafad2", - lightgray: "#d3d3d3", - lightgrey: "#d3d3d3", - lightgreen: "#90ee90", - lightpink: "#ffb6c1", - lightsalmon: "#ffa07a", - lightseagreen: "#20b2aa", - lightskyblue: "#87cefa", - lightslategray: "#778899", - lightsteelblue: "#b0c4de", - lightyellow: "#ffffe0", - lime: "#00ff00", - limegreen: "#32cd32", - linen: "#faf0e6", - magenta: "#ff00ff", - maroon: "#800000", - mediumaquamarine: "#66cdaa", - mediumblue: "#0000cd", - mediumorchid: "#ba55d3", - mediumpurple: "#9370d8", - mediumseagreen: "#3cb371", - mediumslateblue: "#7b68ee", - mediumspringgreen: "#00fa9a", - mediumturquoise: "#48d1cc", - mediumvioletred: "#c71585", - midnightblue: "#191970", - mintcream: "#f5fffa", - mistyrose: "#ffe4e1", - moccasin: "#ffe4b5", - navajowhite: "#ffdead", - navy: "#000080", - oldlace: "#fdf5e6", - olive: "#808000", - olivedrab: "#6b8e23", - orange: "#ffa500", - orangered: "#ff4500", - orchid: "#da70d6", - palegoldenrod: "#eee8aa", - palegreen: "#98fb98", - paleturquoise: "#afeeee", - palevioletred: "#d87093", - papayawhip: "#ffefd5", - peachpuff: "#ffdab9", - peru: "#cd853f", - pink: "#ffc0cb", - plum: "#dda0dd", - powderblue: "#b0e0e6", - purple: "#800080", - red: "#ff0000", - rosybrown: "#bc8f8f", - royalblue: "#4169e1", - saddlebrown: "#8b4513", - salmon: "#fa8072", - sandybrown: "#f4a460", - seagreen: "#2e8b57", - seashell: "#fff5ee", - sienna: "#a0522d", - silver: "#c0c0c0", - skyblue: "#87ceeb", - slateblue: "#6a5acd", - slategray: "#708090", - snow: "#fffafa", - springgreen: "#00ff7f", - steelblue: "#4682b4", - tan: "#d2b48c", - teal: "#008080", - thistle: "#d8bfd8", - tomato: "#ff6347", - turquoise: "#40e0d0", - violet: "#ee82ee", - wheat: "#f5deb3", - white: "#ffffff", - whitesmoke: "#f5f5f5", - yellow: "#ffff00", - yellowgreen: "#9acd32" - }, - fromHSL: function(d, c, b) { - return (new this(0, 0, 0, 0)).setHSL(d, c, b) - }, - fromHSV: function(d, c, b) { - return (new this(0, 0, 0, 0)).setHSL(d, c, b) - }, - fromString: function(b) { - return (new this(0, 0, 0, 0)).setFromString(b) - }, - create: function(b) { - if (b instanceof this) { - return b - } else { - if (Ext.isArray(b)) { - return new Ext.draw.Color(b[0], b[1], b[2], b[3]) - } else { - if (Ext.isString(b)) { - return Ext.draw.Color.fromString(b) - } else { - if (arguments.length > 2) { - return new Ext.draw.Color(arguments[0], arguments[1], arguments[2], arguments[3]) - } else { - return new Ext.draw.Color(0, 0, 0, 0) - } - } - } - } - } - }) -}); -Ext.define("Ext.draw.sprite.AnimationParser", function() { - function a(d, c, b) { - return d + (c - d) * b - } - return { - singleton: true, - attributeRe: /^url\(#([a-zA-Z\-]+)\)$/, - requires: ["Ext.draw.Color"], - color: { - parseInitial: function(c, b) { - if (Ext.isString(c)) { - c = Ext.draw.Color.create(c) - } - if (Ext.isString(b)) { - b = Ext.draw.Color.create(b) - } - if ((c instanceof Ext.draw.Color) && (b instanceof Ext.draw.Color)) { - return [ - [c.r, c.g, c.b, c.a], - [b.r, b.g, b.b, b.a] - ] - } else { - return [c || b, b || c] - } - }, - compute: function(d, c, b) { - if (!Ext.isArray(d) || !Ext.isArray(c)) { - return c || d - } else { - return [a(d[0], c[0], b), a(d[1], c[1], b), a(d[2], c[2], b), a(d[3], c[3], b)] - } - }, - serve: function(c) { - var b = Ext.draw.Color.fly(c[0], c[1], c[2], c[3]); - return b.toString() - } - }, - number: { - parse: function(b) { - return b === null ? null : +b - }, - compute: function(d, c, b) { - if (!Ext.isNumber(d) || !Ext.isNumber(c)) { - return c || d - } else { - return a(d, c, b) - } - } - }, - angle: { - parseInitial: function(c, b) { - if (b - c > Math.PI) { - b -= Math.PI * 2 - } else { - if (b - c < -Math.PI) { - b += Math.PI * 2 - } - } - return [c, b] - }, - compute: function(d, c, b) { - if (!Ext.isNumber(d) || !Ext.isNumber(c)) { - return c || d - } else { - return a(d, c, b) - } - } - }, - path: { - parseInitial: function(m, n) { - var c = m.toStripes(), - o = n.toStripes(), - e, d, k = c.length, - p = o.length, - h, f, b, g = o[p - 1], - l = [g[g.length - 2], g[g.length - 1]]; - for (e = k; e < p; e++) { - c.push(c[k - 1].slice(0)) - } - for (e = p; e < k; e++) { - o.push(l.slice(0)) - } - b = c.length; - o.path = n; - o.temp = new Ext.draw.Path(); - for (e = 0; e < b; e++) { - h = c[e]; - f = o[e]; - k = h.length; - p = f.length; - o.temp.commands.push("M"); - for (d = p; d < k; d += 6) { - f.push(l[0], l[1], l[0], l[1], l[0], l[1]) - } - g = o[o.length - 1]; - l = [g[g.length - 2], g[g.length - 1]]; - for (d = k; d < p; d += 6) { - h.push(l[0], l[1], l[0], l[1], l[0], l[1]) - } - for (e = 0; e < f.length; e++) { - f[e] -= h[e] - } - for (e = 2; e < f.length; e += 6) { - o.temp.commands.push("C") - } - } - return [c, o] - }, - compute: function(c, l, m) { - if (m >= 1) { - return l.path - } - var e = 0, - f = c.length, - d = 0, - b, k, h, n = l.temp.params, - g = 0; - for (; e < f; e++) { - k = c[e]; - h = l[e]; - b = k.length; - for (d = 0; d < b; d++) { - n[g++] = h[d] * m + k[d] - } - } - return l.temp - } - }, - data: { - compute: function(h, j, k, g) { - var m = h.length - 1, - b = j.length - 1, - e = Math.max(m, b), - d, l, c; - if (!g || g === h) { - g = [] - } - g.length = e + 1; - for (c = 0; c <= e; c++) { - d = h[Math.min(c, m)]; - l = j[Math.min(c, b)]; - if (Ext.isNumber(d)) { - if (!Ext.isNumber(l)) { - l = 0 - } - g[c] = (l - d) * k + d - } else { - g[c] = l - } - } - return g - } - }, - text: { - compute: function(d, c, b) { - return d.substr(0, Math.round(d.length * (1 - b))) + c.substr(Math.round(c.length * (1 - b))) - } - }, - limited: "number", - limited01: "number" - } -}); -(function() { - if (!Ext.global.Float32Array) { - var a = function(d) { - if (typeof d === "number") { - this.length = d - } else { - if ("length" in d) { - this.length = d.length; - for (var c = 0, b = d.length; c < b; c++) { - this[c] = +d[c] - } - } - } - }; - a.prototype = []; - Ext.global.Float32Array = a - } -})(); -Ext.define("Ext.draw.Draw", { - singleton: true, - radian: Math.PI / 180, - pi2: Math.PI * 2, - reflectFn: function(b) { - return b - }, - rad: function(a) { - return (a % 360) * this.radian - }, - degrees: function(a) { - return (a / this.radian) % 360 - }, - isBBoxIntersect: function(b, a, c) { - c = c || 0; - return (Math.max(b.x, a.x) - c > Math.min(b.x + b.width, a.x + a.width)) || (Math.max(b.y, a.y) - c > Math.min(b.y + b.height, a.y + a.height)) - }, - isPointInBBox: function(a, c, b) { - return !!b && a >= b.x && a <= (b.x + b.width) && c >= b.y && c <= (b.y + b.height) - }, - spline: function(m) { - var e, c, k = m.length, - b, h, l, f, a = 0, - g = new Float32Array(m.length), - n = new Float32Array(m.length * 3 - 2); - g[0] = 0; - g[k - 1] = 0; - for (e = 1; e < k - 1; e++) { - g[e] = (m[e + 1] + m[e - 1] - 2 * m[e]) - g[e - 1]; - a = 1 / (4 - a); - g[e] *= a - } - for (e = k - 2; e > 0; e--) { - a = 3.732050807568877 + 48.248711305964385 / (-13.928203230275537 + Math.pow(0.07179676972449123, e)); - g[e] -= g[e + 1] * a - } - f = m[0]; - b = f - g[0]; - for (e = 0, c = 0; e < k - 1; c += 3) { - l = f; - h = b; - e++; - f = m[e]; - b = f - g[e]; - n[c] = l; - n[c + 1] = (b + 2 * h) / 3; - n[c + 2] = (b * 2 + h) / 3 - } - n[c] = f; - return n - }, - getAnchors: function(e, d, i, h, t, s, o) { - o = o || 4; - var n = Math.PI, - p = n / 2, - k = Math.abs, - a = Math.sin, - b = Math.cos, - f = Math.atan, - r, q, g, j, m, l, v, u, c; - r = (i - e) / o; - q = (t - i) / o; - if ((h >= d && h >= s) || (h <= d && h <= s)) { - g = j = p - } else { - g = f((i - e) / k(h - d)); - if (d < h) { - g = n - g - } - j = f((t - i) / k(h - s)); - if (s < h) { - j = n - j - } - } - c = p - ((g + j) % (n * 2)) / 2; - if (c > p) { - c -= n - } - g += c; - j += c; - m = i - r * a(g); - l = h + r * b(g); - v = i + q * a(j); - u = h + q * b(j); - if ((h > d && l < d) || (h < d && l > d)) { - m += k(d - l) * (m - i) / (l - h); - l = d - } - if ((h > s && u < s) || (h < s && u > s)) { - v -= k(s - u) * (v - i) / (u - h); - u = s - } - return { - x1: m, - y1: l, - x2: v, - y2: u - } - }, - smooth: function(l, j, o) { - var k = l.length, - h, g, c, b, q, p, n, m, f = [], - e = [], - d, a; - for (d = 0; d < k - 1; d++) { - h = l[d]; - g = j[d]; - if (d === 0) { - n = h; - m = g; - f.push(n); - e.push(m); - if (k === 1) { - break - } - } - c = l[d + 1]; - b = j[d + 1]; - q = l[d + 2]; - p = j[d + 2]; - if (!Ext.isNumber(q + p)) { - f.push(n, c, c); - e.push(m, b, b); - break - } - a = this.getAnchors(h, g, c, b, q, p, o); - f.push(n, a.x1, c); - e.push(m, a.y1, b); - n = a.x2; - m = a.y2 - } - return { - smoothX: f, - smoothY: e - } - }, - beginUpdateIOS: Ext.os.is.iOS ? function() { - this.iosUpdateEl = Ext.getBody().createChild({ - style: "position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; background: rgba(0,0,0,0.001); z-index: 100000" - }) - } : Ext.emptyFn, - endUpdateIOS: function() { - this.iosUpdateEl = Ext.destroy(this.iosUpdateEl) - } -}); -Ext.define("Ext.draw.gradient.Gradient", { - requires: ["Ext.draw.Color"], - isGradient: true, - config: { - stops: [] - }, - applyStops: function(f) { - var e = [], - d = f.length, - c, b, a; - for (c = 0; c < d; c++) { - b = f[c]; - a = b.color; - if (!(a && a.isColor)) { - a = Ext.draw.Color.fly(a || Ext.draw.Color.NONE) - } - e.push({ - offset: Math.min(1, Math.max(0, "offset" in b ? b.offset : b.position || 0)), - color: a.toString() - }) - } - e.sort(function(h, g) { - return h.offset - g.offset - }); - return e - }, - onClassExtended: function(a, b) { - if (!b.alias && b.type) { - b.alias = "gradient." + b.type - } - }, - constructor: function(a) { - this.initConfig(a) - }, - generateGradient: Ext.emptyFn -}); -Ext.define("Ext.draw.gradient.GradientDefinition", { - singleton: true, - urlStringRe: /^url\(#([\w\-]+)\)$/, - gradients: {}, - add: function(a) { - var b = this.gradients, - c, e, d; - for (c = 0, e = a.length; c < e; c++) { - d = a[c]; - if (Ext.isString(d.id)) { - b[d.id] = d - } - } - }, - get: function(d) { - var a = this.gradients, - b = d.match(this.urlStringRe), - c; - if (b && b[1] && (c = a[b[1]])) { - return c || d - } - return d - } -}); -Ext.define("Ext.draw.sprite.AttributeParser", { - singleton: true, - attributeRe: /^url\(#([a-zA-Z\-]+)\)$/, - requires: ["Ext.draw.Color", "Ext.draw.gradient.GradientDefinition"], - "default": Ext.identityFn, - string: function(a) { - return String(a) - }, - number: function(a) { - if (Ext.isNumber(+a)) { - return a - } - }, - angle: function(a) { - if (Ext.isNumber(a)) { - a %= Math.PI * 2; - if (a < -Math.PI) { - a += Math.PI * 2 - } else { - if (a >= Math.PI) { - a -= Math.PI * 2 - } - } - return a - } - }, - data: function(a) { - if (Ext.isArray(a)) { - return a.slice() - } else { - if (a instanceof Float32Array) { - return new Float32Array(a) - } - } - }, - bool: function(a) { - return !!a - }, - color: function(a) { - if (a instanceof Ext.draw.Color) { - return a.toString() - } else { - if (a instanceof Ext.draw.gradient.Gradient) { - return a - } else { - if (!a) { - return Ext.draw.Color.NONE - } else { - if (Ext.isString(a)) { - if (a.substr(0, 3) === "url") { - a = Ext.draw.gradient.GradientDefinition.get(a); - if (Ext.isString(a)) { - return a - } - } else { - return Ext.draw.Color.fly(a).toString() - } - } - } - } - } - if (a.type === "linear") { - return Ext.create("Ext.draw.gradient.Linear", a) - } else { - if (a.type === "radial") { - return Ext.create("Ext.draw.gradient.Radial", a) - } else { - if (a.type === "pattern") { - return Ext.create("Ext.draw.gradient.Pattern", a) - } else { - return Ext.draw.Color.NONE - } - } - } - }, - limited: function(a, b) { - return function(c) { - c = +c; - return Ext.isNumber(c) ? Math.min(Math.max(c, a), b) : undefined - } - }, - limited01: function(a) { - a = +a; - return Ext.isNumber(a) ? Math.min(Math.max(a, 0), 1) : undefined - }, - enums: function() { - var d = {}, - a = Array.prototype.slice.call(arguments, 0), - b, c; - for (b = 0, c = a.length; b < c; b++) { - d[a[b]] = true - } - return function(e) { - return e in d ? e : undefined - } - } -}); -Ext.define("Ext.draw.sprite.AttributeDefinition", { - requires: ["Ext.draw.sprite.AttributeParser", "Ext.draw.sprite.AnimationParser"], - config: { - defaults: { - $value: {}, - lazy: true - }, - aliases: {}, - animationProcessors: {}, - processors: { - $value: {}, - lazy: true - }, - dirtyTriggers: {}, - triggers: {}, - updaters: {} - }, - inheritableStatics: { - processorFactoryRe: /^(\w+)\(([\w\-,]*)\)$/ - }, - spriteClass: null, - constructor: function(a) { - var b = this; - b.initConfig(a) - }, - applyDefaults: function(b, a) { - a = Ext.apply(a || {}, this.normalize(b)); - return a - }, - applyAliases: function(b, a) { - return Ext.apply(a || {}, b) - }, - applyProcessors: function(e, i) { - this.getAnimationProcessors(); - var j = i || {}, - h = Ext.draw.sprite.AttributeParser, - a = this.self.processorFactoryRe, - g = {}, - d, b, c, f; - for (b in e) { - f = e[b]; - if (typeof f === "string") { - c = f.match(a); - if (c) { - f = h[c[1]].apply(h, c[2].split(",")) - } else { - if (h[f]) { - g[b] = f; - d = true; - f = h[f] - } - } - } - j[b] = f - } - if (d) { - this.setAnimationProcessors(g) - } - return j - }, - applyAnimationProcessors: function(c, a) { - var e = Ext.draw.sprite.AnimationParser, - b, d; - if (!a) { - a = {} - } - for (b in c) { - d = c[b]; - if (d === "none") { - a[b] = null - } else { - if (Ext.isString(d) && !(b in a)) { - if (d in e) { - while (Ext.isString(e[d])) { - d = e[d] - } - a[b] = e[d] - } - } else { - if (Ext.isObject(d)) { - a[b] = d - } - } - } - } - return a - }, - updateDirtyTriggers: function(a) { - this.setTriggers(a) - }, - applyTriggers: function(b, c) { - if (!c) { - c = {} - } - for (var a in b) { - c[a] = b[a].split(",") - } - return c - }, - applyUpdaters: function(b, a) { - return Ext.apply(a || {}, b) - }, - batchedNormalize: function(f, n) { - if (!f) { - return {} - } - var j = this.getProcessors(), - d = this.getAliases(), - a = f.translation || f.translate, - o = {}, - g, h, b, e, p, c, m, l, k; - if ("rotation" in f) { - p = f.rotation - } else { - p = ("rotate" in f) ? f.rotate : undefined - } - if ("scaling" in f) { - c = f.scaling - } else { - c = ("scale" in f) ? f.scale : undefined - } - if (typeof c !== "undefined") { - if (Ext.isNumber(c)) { - o.scalingX = c; - o.scalingY = c - } else { - if ("x" in c) { - o.scalingX = c.x - } - if ("y" in c) { - o.scalingY = c.y - } - if ("centerX" in c) { - o.scalingCenterX = c.centerX - } - if ("centerY" in c) { - o.scalingCenterY = c.centerY - } - } - } - if (typeof p !== "undefined") { - if (Ext.isNumber(p)) { - p = Ext.draw.Draw.rad(p); - o.rotationRads = p - } else { - if ("rads" in p) { - o.rotationRads = p.rads - } else { - if ("degrees" in p) { - if (Ext.isArray(p.degrees)) { - o.rotationRads = Ext.Array.map(p.degrees, function(i) { - return Ext.draw.Draw.rad(i) - }) - } else { - o.rotationRads = Ext.draw.Draw.rad(p.degrees) - } - } - } - if ("centerX" in p) { - o.rotationCenterX = p.centerX - } - if ("centerY" in p) { - o.rotationCenterY = p.centerY - } - } - } - if (typeof a !== "undefined") { - if ("x" in a) { - o.translationX = a.x - } - if ("y" in a) { - o.translationY = a.y - } - } - if ("matrix" in f) { - m = Ext.draw.Matrix.create(f.matrix); - k = m.split(); - o.matrix = m; - o.rotationRads = k.rotation; - o.rotationCenterX = 0; - o.rotationCenterY = 0; - o.scalingX = k.scaleX; - o.scalingY = k.scaleY; - o.scalingCenterX = 0; - o.scalingCenterY = 0; - o.translationX = k.translateX; - o.translationY = k.translateY - } - for (b in f) { - e = f[b]; - if (typeof e === "undefined") { - continue - } else { - if (Ext.isArray(e)) { - if (b in d) { - b = d[b] - } - if (b in j) { - o[b] = []; - for (g = 0, h = e.length; g < h; g++) { - l = j[b].call(this, e[g]); - if (typeof l !== "undefined") { - o[b][g] = l - } - } - } else { - if (n) { - o[b] = e - } - } - } else { - if (b in d) { - b = d[b] - } - if (b in j) { - e = j[b].call(this, e); - if (typeof e !== "undefined") { - o[b] = e - } - } else { - if (n) { - o[b] = e - } - } - } - } - } - return o - }, - normalize: function(i, j) { - if (!i) { - return {} - } - var f = this.getProcessors(), - d = this.getAliases(), - a = i.translation || i.translate, - k = {}, - b, e, l, c, h, g; - if ("rotation" in i) { - l = i.rotation - } else { - l = ("rotate" in i) ? i.rotate : undefined - } - if ("scaling" in i) { - c = i.scaling - } else { - c = ("scale" in i) ? i.scale : undefined - } - if (a) { - if ("x" in a) { - k.translationX = a.x - } - if ("y" in a) { - k.translationY = a.y - } - } - if (typeof c !== "undefined") { - if (Ext.isNumber(c)) { - k.scalingX = c; - k.scalingY = c - } else { - if ("x" in c) { - k.scalingX = c.x - } - if ("y" in c) { - k.scalingY = c.y - } - if ("centerX" in c) { - k.scalingCenterX = c.centerX - } - if ("centerY" in c) { - k.scalingCenterY = c.centerY - } - } - } - if (typeof l !== "undefined") { - if (Ext.isNumber(l)) { - l = Ext.draw.Draw.rad(l); - k.rotationRads = l - } else { - if ("rads" in l) { - k.rotationRads = l.rads - } else { - if ("degrees" in l) { - k.rotationRads = Ext.draw.Draw.rad(l.degrees) - } - } - if ("centerX" in l) { - k.rotationCenterX = l.centerX - } - if ("centerY" in l) { - k.rotationCenterY = l.centerY - } - } - } - if ("matrix" in i) { - h = Ext.draw.Matrix.create(i.matrix); - g = h.split(); - k.matrix = h; - k.rotationRads = g.rotation; - k.rotationCenterX = 0; - k.rotationCenterY = 0; - k.scalingX = g.scaleX; - k.scalingY = g.scaleY; - k.scalingCenterX = 0; - k.scalingCenterY = 0; - k.translationX = g.translateX; - k.translationY = g.translateY - } - for (b in i) { - e = i[b]; - if (typeof e === "undefined") { - continue - } - if (b in d) { - b = d[b] - } - if (b in f) { - e = f[b].call(this, e); - if (typeof e !== "undefined") { - k[b] = e - } - } else { - if (j) { - k[b] = e - } - } - } - return k - }, - setBypassingNormalization: function(a, c, b) { - return c.pushDown(a, b) - }, - set: function(a, c, b) { - b = this.normalize(b); - return this.setBypassingNormalization(a, c, b) - } -}); -Ext.define("Ext.draw.Matrix", { - isMatrix: true, - statics: { - createAffineMatrixFromTwoPair: function(h, t, g, s, k, o, i, j) { - var v = g - h, - u = s - t, - e = i - k, - q = j - o, - d = 1 / (v * v + u * u), - p = v * e + u * q, - n = e * u - v * q, - m = -p * h - n * t, - l = n * h - p * t; - return new this(p * d, -n * d, n * d, p * d, m * d + k, l * d + o) - }, - createPanZoomFromTwoPair: function(q, e, p, c, h, s, n, g) { - if (arguments.length === 2) { - return this.createPanZoomFromTwoPair.apply(this, q.concat(e)) - } - var k = p - q, - j = c - e, - d = (q + p) * 0.5, - b = (e + c) * 0.5, - o = n - h, - a = g - s, - f = (h + n) * 0.5, - l = (s + g) * 0.5, - m = k * k + j * j, - i = o * o + a * a, - t = Math.sqrt(i / m); - return new this(t, 0, 0, t, f - t * d, l - t * b) - }, - fly: (function() { - var a = null, - b = function(c) { - a.elements = c; - return a - }; - return function(c) { - if (!a) { - a = new Ext.draw.Matrix() - } - a.elements = c; - Ext.draw.Matrix.fly = b; - return a - } - })(), - create: function(a) { - if (a instanceof this) { - return a - } - return new this(a) - } - }, - constructor: function(e, d, a, f, c, b) { - if (e && e.length === 6) { - this.elements = e.slice() - } else { - if (e !== undefined) { - this.elements = [e, d, a, f, c, b] - } else { - this.elements = [1, 0, 0, 1, 0, 0] - } - } - }, - prepend: function(a, l, h, g, m, k) { - var b = this.elements, - d = b[0], - j = b[1], - e = b[2], - c = b[3], - i = b[4], - f = b[5]; - b[0] = a * d + h * j; - b[1] = l * d + g * j; - b[2] = a * e + h * c; - b[3] = l * e + g * c; - b[4] = a * i + h * f + m; - b[5] = l * i + g * f + k; - return this - }, - prependMatrix: function(a) { - return this.prepend.apply(this, a.elements) - }, - append: function(a, l, h, g, m, k) { - var b = this.elements, - d = b[0], - j = b[1], - e = b[2], - c = b[3], - i = b[4], - f = b[5]; - b[0] = a * d + l * e; - b[1] = a * j + l * c; - b[2] = h * d + g * e; - b[3] = h * j + g * c; - b[4] = m * d + k * e + i; - b[5] = m * j + k * c + f; - return this - }, - appendMatrix: function(a) { - return this.append.apply(this, a.elements) - }, - set: function(f, e, a, g, c, b) { - var d = this.elements; - d[0] = f; - d[1] = e; - d[2] = a; - d[3] = g; - d[4] = c; - d[5] = b; - return this - }, - inverse: function(i) { - var g = this.elements, - o = g[0], - m = g[1], - l = g[2], - k = g[3], - j = g[4], - h = g[5], - n = 1 / (o * k - m * l); - o *= n; - m *= n; - l *= n; - k *= n; - if (i) { - i.set(k, -m, -l, o, l * h - k * j, m * j - o * h); - return i - } else { - return new Ext.draw.Matrix(k, -m, -l, o, l * h - k * j, m * j - o * h) - } - }, - translate: function(a, c, b) { - if (b) { - return this.prepend(1, 0, 0, 1, a, c) - } else { - return this.append(1, 0, 0, 1, a, c) - } - }, - scale: function(f, e, c, a, b) { - var d = this; - if (e == null) { - e = f - } - if (c === undefined) { - c = 0 - } - if (a === undefined) { - a = 0 - } - if (b) { - return d.prepend(f, 0, 0, e, c - c * f, a - a * e) - } else { - return d.append(f, 0, 0, e, c - c * f, a - a * e) - } - }, - rotate: function(g, e, c, b) { - var d = this, - f = Math.cos(g), - a = Math.sin(g); - e = e || 0; - c = c || 0; - if (b) { - return d.prepend(f, a, -a, f, e - f * e + c * a, c - f * c - e * a) - } else { - return d.append(f, a, -a, f, e - f * e + c * a, c - f * c - e * a) - } - }, - rotateFromVector: function(a, h, c) { - var e = this, - g = Math.sqrt(a * a + h * h), - f = a / g, - b = h / g; - if (c) { - return e.prepend(f, b, -b, f, 0, 0) - } else { - return e.append(f, b, -b, f, 0, 0) - } - }, - clone: function() { - return new Ext.draw.Matrix(this.elements) - }, - flipX: function() { - return this.append(-1, 0, 0, 1, 0, 0) - }, - flipY: function() { - return this.append(1, 0, 0, -1, 0, 0) - }, - skewX: function(a) { - return this.append(1, 0, Math.tan(a), 1, 0, 0) - }, - skewY: function(a) { - return this.append(1, Math.tan(a), 0, 1, 0, 0) - }, - shearX: function(a) { - return this.append(1, 0, a, 1, 0, 0) - }, - shearY: function(a) { - return this.append(1, a, 0, 1, 0, 0) - }, - reset: function() { - return this.set(1, 0, 0, 1, 0, 0) - }, - precisionCompensate: function(j, g) { - var c = this.elements, - f = c[0], - e = c[1], - i = c[2], - h = c[3], - d = c[4], - b = c[5], - a = e * i - f * h; - g.b = j * e / f; - g.c = j * i / h; - g.d = j; - g.xx = f / j; - g.yy = h / j; - g.dx = (b * f * i - d * f * h) / a / j; - g.dy = (d * e * h - b * f * h) / a / j - }, - precisionCompensateRect: function(j, g) { - var b = this.elements, - f = b[0], - e = b[1], - i = b[2], - h = b[3], - c = b[4], - a = b[5], - d = i / f; - g.b = j * e / f; - g.c = j * d; - g.d = j * h / f; - g.xx = f / j; - g.yy = f / j; - g.dx = (a * i - c * h) / (e * d - h) / j; - g.dy = -(a * f - c * e) / (e * d - h) / j - }, - x: function(a, c) { - var b = this.elements; - return a * b[0] + c * b[2] + b[4] - }, - y: function(a, c) { - var b = this.elements; - return a * b[1] + c * b[3] + b[5] - }, - get: function(b, a) { - return +this.elements[b + a * 2].toFixed(4) - }, - transformPoint: function(b) { - var c = this.elements, - a, d; - if (b.isPoint) { - a = b.x; - d = b.y - } else { - a = b[0]; - d = b[1] - } - return [a * c[0] + d * c[2] + c[4], a * c[1] + d * c[3] + c[5]] - }, - transformBBox: function(q, i, j) { - var b = this.elements, - d = q.x, - r = q.y, - g = q.width * 0.5, - o = q.height * 0.5, - a = b[0], - s = b[1], - n = b[2], - k = b[3], - e = d + g, - c = r + o, - p, f, m; - if (i) { - g -= i; - o -= i; - m = [Math.sqrt(b[0] * b[0] + b[2] * b[2]), Math.sqrt(b[1] * b[1] + b[3] * b[3])]; - p = Math.abs(g * a) + Math.abs(o * n) + Math.abs(m[0] * i); - f = Math.abs(g * s) + Math.abs(o * k) + Math.abs(m[1] * i) - } else { - p = Math.abs(g * a) + Math.abs(o * n); - f = Math.abs(g * s) + Math.abs(o * k) - } - if (!j) { - j = {} - } - j.x = e * a + c * n + b[4] - p; - j.y = e * s + c * k + b[5] - f; - j.width = p + p; - j.height = f + f; - return j - }, - transformList: function(e) { - var b = this.elements, - a = b[0], - h = b[2], - l = b[4], - k = b[1], - g = b[3], - j = b[5], - f = e.length, - c, d; - for (d = 0; d < f; d++) { - c = e[d]; - e[d] = [c[0] * a + c[1] * h + l, c[0] * k + c[1] * g + j] - } - return e - }, - isIdentity: function() { - var a = this.elements; - return a[0] === 1 && a[1] === 0 && a[2] === 0 && a[3] === 1 && a[4] === 0 && a[5] === 0 - }, - isEqual: function(a) { - var c = a && a.isMatrix ? a.elements : a, - b = this.elements; - return b[0] === c[0] && b[1] === c[1] && b[2] === c[2] && b[3] === c[3] && b[4] === c[4] && b[5] === c[5] - }, - equals: function(a) { - return this.isEqual(a) - }, - toArray: function() { - var a = this.elements; - return [a[0], a[2], a[4], a[1], a[3], a[5]] - }, - toVerticalArray: function() { - return this.elements.slice() - }, - toString: function() { - var a = this; - return [a.get(0, 0), a.get(0, 1), a.get(1, 0), a.get(1, 1), a.get(2, 0), a.get(2, 1)].join(",") - }, - toContext: function(a) { - a.transform.apply(a, this.elements); - return this - }, - toSvg: function() { - var a = this.elements; - return "matrix(" + a[0].toFixed(9) + "," + a[1].toFixed(9) + "," + a[2].toFixed(9) + "," + a[3].toFixed(9) + "," + a[4].toFixed(9) + "," + a[5].toFixed(9) + ")" - }, - getScaleX: function() { - var a = this.elements; - return Math.sqrt(a[0] * a[0] + a[2] * a[2]) - }, - getScaleY: function() { - var a = this.elements; - return Math.sqrt(a[1] * a[1] + a[3] * a[3]) - }, - getXX: function() { - return this.elements[0] - }, - getXY: function() { - return this.elements[1] - }, - getYX: function() { - return this.elements[2] - }, - getYY: function() { - return this.elements[3] - }, - getDX: function() { - return this.elements[4] - }, - getDY: function() { - return this.elements[5] - }, - split: function() { - var b = this.elements, - d = b[0], - c = b[1], - e = b[3], - a = { - translateX: b[4], - translateY: b[5] - }; - a.rotate = a.rotation = Math.atan2(c, d); - a.scaleX = d / Math.cos(a.rotate); - a.scaleY = e / d * a.scaleX; - return a - } -}, function() { - function b(e, c, d) { - e[c] = { - get: function() { - return this.elements[d] - }, - set: function(f) { - this.elements[d] = f - } - } - } - if (Object.defineProperties) { - var a = {}; - b(a, "a", 0); - b(a, "b", 1); - b(a, "c", 2); - b(a, "d", 3); - b(a, "e", 4); - b(a, "f", 5); - Object.defineProperties(this.prototype, a) - } - this.prototype.multiply = this.prototype.appendMatrix -}); -Ext.define("Ext.draw.modifier.Modifier", { - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - previous: null, - next: null, - sprite: null - }, - constructor: function(a) { - this.mixins.observable.constructor.call(this, a) - }, - updateNext: function(a) { - if (a) { - a.setPrevious(this) - } - }, - updatePrevious: function(a) { - if (a) { - a.setNext(this) - } - }, - prepareAttributes: function(a) { - if (this._previous) { - this._previous.prepareAttributes(a) - } - }, - popUp: function(a, b) { - if (this._next) { - this._next.popUp(a, b) - } else { - Ext.apply(a, b) - } - }, - pushDown: function(a, c) { - if (this._previous) { - return this._previous.pushDown(a, c) - } else { - for (var b in c) { - if (c[b] === a[b]) { - delete c[b] - } - } - return c - } - } -}); -Ext.define("Ext.draw.modifier.Target", { - requires: ["Ext.draw.Matrix"], - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.target", - statics: { - uniqueId: 0 - }, - prepareAttributes: function(a) { - var b = this.getPrevious(); - if (b) { - b.prepareAttributes(a) - } - a.attributeId = "attribute-" + Ext.draw.modifier.Target.uniqueId++; - if (!a.hasOwnProperty("canvasAttributes")) { - a.bbox = { - plain: { - dirty: true - }, - transform: { - dirty: true - } - }; - a.dirty = true; - a.pendingUpdaters = {}; - a.canvasAttributes = {}; - a.matrix = new Ext.draw.Matrix(); - a.inverseMatrix = new Ext.draw.Matrix() - } - }, - applyChanges: function(f, k) { - Ext.apply(f, k); - var l = this.getSprite(), - o = f.pendingUpdaters, - h = l.self.def.getTriggers(), - p, a, m, b, e, n, d, c, g; - for (b in k) { - e = true; - if ((p = h[b])) { - l.scheduleUpdaters(f, p, [b]) - } - if (f.template && k.removeFromInstance && k.removeFromInstance[b]) { - delete f[b] - } - } - if (!e) { - return - } - if (o.canvas) { - n = o.canvas; - delete o.canvas; - for (d = 0, g = n.length; d < g; d++) { - b = n[d]; - f.canvasAttributes[b] = f[b] - } - } - if (f.hasOwnProperty("children")) { - a = f.children; - for (d = 0, g = a.length; d < g; d++) { - m = a[d]; - Ext.apply(m.pendingUpdaters, o); - if (n) { - for (c = 0; c < n.length; c++) { - b = n[c]; - m.canvasAttributes[b] = m[b] - } - } - l.callUpdaters(m) - } - } - l.setDirty(true); - l.callUpdaters(f) - }, - popUp: function(a, b) { - this.applyChanges(a, b) - }, - pushDown: function(a, b) { - var c = this.getPrevious(); - if (c) { - b = c.pushDown(a, b) - } - this.applyChanges(a, b); - return b - } -}); -Ext.define("Ext.draw.TimingFunctions", function() { - var g = Math.pow, - j = Math.sin, - m = Math.cos, - l = Math.sqrt, - e = Math.PI, - b = ["quad", "cube", "quart", "quint"], - c = { - pow: function(o, i) { - return g(o, i || 6) - }, - expo: function(i) { - return g(2, 8 * (i - 1)) - }, - circ: function(i) { - return 1 - l(1 - i * i) - }, - sine: function(i) { - return 1 - j((1 - i) * e / 2) - }, - back: function(i, o) { - o = o || 1.616; - return i * i * ((o + 1) * i - o) - }, - bounce: function(q) { - for (var o = 0, i = 1; 1; o += i, i /= 2) { - if (q >= (7 - 4 * o) / 11) { - return i * i - g((11 - 6 * o - 11 * q) / 4, 2) - } - } - }, - elastic: function(o, i) { - return g(2, 10 * --o) * m(20 * o * e * (i || 1) / 3) - } - }, - k = {}, - a, f, d; - - function h(i) { - return function(o) { - return g(o, i) - } - } - - function n(i, o) { - k[i + "In"] = function(p) { - return o(p) - }; - k[i + "Out"] = function(p) { - return 1 - o(1 - p) - }; - k[i + "InOut"] = function(p) { - return (p <= 0.5) ? o(2 * p) / 2 : (2 - o(2 * (1 - p))) / 2 - } - } - for (d = 0, f = b.length; d < f; ++d) { - c[b[d]] = h(d + 2) - } - for (a in c) { - n(a, c[a]) - } - k.linear = Ext.identityFn; - k.easeIn = k.quadIn; - k.easeOut = k.quadOut; - k.easeInOut = k.quadInOut; - return { - singleton: true, - easingMap: k - } -}, function(a) { - Ext.apply(a, a.easingMap) -}); -Ext.define("Ext.draw.Animator", { - uses: ["Ext.draw.Draw"], - singleton: true, - frameCallbacks: {}, - frameCallbackId: 0, - scheduled: 0, - frameStartTimeOffset: Ext.now(), - animations: [], - running: false, - animationTime: function() { - return Ext.AnimationQueue.frameStartTime - this.frameStartTimeOffset - }, - add: function(b) { - var a = this; - if (!a.contains(b)) { - a.animations.push(b); - a.ignite(); - if ("fireEvent" in b) { - b.fireEvent("animationstart", b) - } - } - }, - remove: function(d) { - var c = this, - e = c.animations, - b = 0, - a = e.length; - for (; b < a; ++b) { - if (e[b] === d) { - e.splice(b, 1); - if ("fireEvent" in d) { - d.fireEvent("animationend", d) - } - return - } - } - }, - contains: function(a) { - return Ext.Array.indexOf(this.animations, a) > -1 - }, - empty: function() { - return this.animations.length === 0 - }, - step: function(d) { - var c = this, - f = c.animations, - e, a = 0, - b = f.length; - for (; a < b; a++) { - e = f[a]; - e.step(d); - if (!e.animating) { - f.splice(a, 1); - a--; - b--; - if (e.fireEvent) { - e.fireEvent("animationend", e) - } - } - } - }, - schedule: function(c, a) { - a = a || this; - var b = "frameCallback" + (this.frameCallbackId++); - if (Ext.isString(c)) { - c = a[c] - } - Ext.draw.Animator.frameCallbacks[b] = { - fn: c, - scope: a, - once: true - }; - this.scheduled++; - Ext.draw.Animator.ignite(); - return b - }, - scheduleIf: function(e, b) { - b = b || this; - var c = Ext.draw.Animator.frameCallbacks, - a, d; - if (Ext.isString(e)) { - e = b[e] - } - for (d in c) { - a = c[d]; - if (a.once && a.fn === e && a.scope === b) { - return null - } - } - return this.schedule(e, b) - }, - cancel: function(a) { - if (Ext.draw.Animator.frameCallbacks[a] && Ext.draw.Animator.frameCallbacks[a].once) { - this.scheduled--; - delete Ext.draw.Animator.frameCallbacks[a] - } - }, - addFrameCallback: function(c, a) { - a = a || this; - if (Ext.isString(c)) { - c = a[c] - } - var b = "frameCallback" + (this.frameCallbackId++); - Ext.draw.Animator.frameCallbacks[b] = { - fn: c, - scope: a - }; - return b - }, - removeFrameCallback: function(a) { - delete Ext.draw.Animator.frameCallbacks[a] - }, - fireFrameCallbacks: function() { - var c = this.frameCallbacks, - d, b, a; - for (d in c) { - a = c[d]; - b = a.fn; - if (Ext.isString(b)) { - b = a.scope[b] - } - b.call(a.scope); - if (c[d] && a.once) { - this.scheduled--; - delete c[d] - } - } - }, - handleFrame: function() { - this.step(this.animationTime()); - this.fireFrameCallbacks(); - if (!this.scheduled && this.empty()) { - Ext.AnimationQueue.stop(this.handleFrame, this); - this.running = false; - Ext.draw.Draw.endUpdateIOS() - } - }, - ignite: function() { - if (!this.running) { - this.running = true; - Ext.AnimationQueue.start(this.handleFrame, this); - Ext.draw.Draw.beginUpdateIOS() - } - } -}); -Ext.define("Ext.draw.modifier.Animation", { - requires: ["Ext.draw.TimingFunctions", "Ext.draw.Animator"], - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.animation", - config: { - easing: Ext.identityFn, - duration: 0, - customEasings: {}, - customDurations: {}, - customDuration: null - }, - constructor: function(a) { - var b = this; - b.anyAnimation = b.anySpecialAnimations = false; - b.animating = 0; - b.animatingPool = []; - b.callParent([a]) - }, - prepareAttributes: function(a) { - if (!a.hasOwnProperty("timers")) { - a.animating = false; - a.timers = {}; - a.animationOriginal = Ext.Object.chain(a); - a.animationOriginal.prototype = a - } - if (this._previous) { - this._previous.prepareAttributes(a.animationOriginal) - } - }, - updateSprite: function(a) { - this.setConfig(a.config.fx) - }, - updateDuration: function(a) { - this.anyAnimation = a > 0 - }, - applyEasing: function(a) { - if (typeof a === "string") { - a = Ext.draw.TimingFunctions.easingMap[a] - } - return a - }, - applyCustomEasings: function(a, e) { - e = e || {}; - var g, d, b, h, c, f; - for (d in a) { - g = true; - h = a[d]; - b = d.split(","); - if (typeof h === "string") { - h = Ext.draw.TimingFunctions.easingMap[h] - } - for (c = 0, f = b.length; c < f; c++) { - e[b[c]] = h - } - } - if (g) { - this.anySpecialAnimations = g - } - return e - }, - setEasingOn: function(a, e) { - a = Ext.Array.from(a).slice(); - var c = {}, - d = a.length, - b = 0; - for (; b < d; b++) { - c[a[b]] = e - } - this.setCustomEasings(c) - }, - clearEasingOn: function(a) { - a = Ext.Array.from(a, true); - var b = 0, - c = a.length; - for (; b < c; b++) { - delete this._customEasings[a[b]] - } - }, - applyCustomDurations: function(g, h) { - h = h || {}; - var e, c, f, a, b, d; - for (c in g) { - e = true; - f = g[c]; - a = c.split(","); - for (b = 0, d = a.length; b < d; b++) { - h[a[b]] = f - } - } - if (e) { - this.anySpecialAnimations = e - } - return h - }, - applyCustomDuration: function(a, b) { - if (a) { - this.getCustomDurations(); - this.setCustomDurations(a) - } - }, - setDurationOn: function(b, e) { - b = Ext.Array.from(b).slice(); - var a = {}, - c = 0, - d = b.length; - for (; c < d; c++) { - a[b[c]] = e - } - this.setCustomDurations(a) - }, - clearDurationOn: function(a) { - a = Ext.Array.from(a, true); - var b = 0, - c = a.length; - for (; b < c; b++) { - delete this._customDurations[a[b]] - } - }, - setAnimating: function(a, b) { - var e = this, - d = e.animatingPool; - if (a.animating !== b) { - a.animating = b; - if (b) { - d.push(a); - if (e.animating === 0) { - Ext.draw.Animator.add(e) - } - e.animating++ - } else { - for (var c = d.length; c--;) { - if (d[c] === a) { - d.splice(c, 1) - } - } - e.animating = d.length - } - } - }, - setAttrs: function(r, t) { - var s = this, - m = r.timers, - h = s._sprite.self.def._animationProcessors, - f = s._easing, - e = s._duration, - j = s._customDurations, - i = s._customEasings, - g = s.anySpecialAnimations, - n = s.anyAnimation || g, - o = r.animationOriginal, - d = false, - k, u, l, p, c, q, a; - if (!n) { - for (u in t) { - if (r[u] === t[u]) { - delete t[u] - } else { - r[u] = t[u] - } - delete o[u]; - delete m[u] - } - return t - } else { - for (u in t) { - l = t[u]; - p = r[u]; - if (l !== p && p !== undefined && p !== null && (c = h[u])) { - q = f; - a = e; - if (g) { - if (u in i) { - q = i[u] - } - if (u in j) { - a = j[u] - } - } - if (p && p.isGradient || l && l.isGradient) { - a = 0 - } - if (a) { - if (!m[u]) { - m[u] = {} - } - k = m[u]; - k.start = 0; - k.easing = q; - k.duration = a; - k.compute = c.compute; - k.serve = c.serve || Ext.identityFn; - k.remove = t.removeFromInstance && t.removeFromInstance[u]; - if (c.parseInitial) { - var b = c.parseInitial(p, l); - k.source = b[0]; - k.target = b[1] - } else { - if (c.parse) { - k.source = c.parse(p); - k.target = c.parse(l) - } else { - k.source = p; - k.target = l - } - } - o[u] = l; - delete t[u]; - d = true; - continue - } else { - delete o[u] - } - } else { - delete o[u] - } - delete m[u] - } - } - if (d && !r.animating) { - s.setAnimating(r, true) - } - return t - }, - updateAttributes: function(g) { - if (!g.animating) { - return {} - } - var h = {}, - e = false, - d = g.timers, - f = g.animationOriginal, - c = Ext.draw.Animator.animationTime(), - a, b, i; - if (g.lastUpdate === c) { - return null - } - for (a in d) { - b = d[a]; - if (!b.start) { - b.start = c; - i = 0 - } else { - i = (c - b.start) / b.duration - } - if (i >= 1) { - h[a] = f[a]; - delete f[a]; - if (d[a].remove) { - h.removeFromInstance = h.removeFromInstance || {}; - h.removeFromInstance[a] = true - } - delete d[a] - } else { - h[a] = b.serve(b.compute(b.source, b.target, b.easing(i), g[a])); - e = true - } - } - g.lastUpdate = c; - this.setAnimating(g, e); - return h - }, - pushDown: function(a, b) { - b = this.callParent([a.animationOriginal, b]); - return this.setAttrs(a, b) - }, - popUp: function(a, b) { - a = a.prototype; - b = this.setAttrs(a, b); - if (this._next) { - return this._next.popUp(a, b) - } else { - return Ext.apply(a, b) - } - }, - step: function(g) { - var f = this, - c = f.animatingPool.slice(), - e = c.length, - b = 0, - a, d; - for (; b < e; b++) { - a = c[b]; - d = f.updateAttributes(a); - if (d && f._next) { - f._next.popUp(a, d) - } - } - }, - stop: function() { - this.step(); - var d = this, - b = d.animatingPool, - a, c; - for (a = 0, c = b.length; a < c; a++) { - b[a].animating = false - } - d.animatingPool.length = 0; - d.animating = 0; - Ext.draw.Animator.remove(d) - }, - destroy: function() { - this.animatingPool.length = 0; - this.animating = 0; - this.callParent() - } -}); -Ext.define("Ext.draw.modifier.Highlight", { - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.highlight", - config: { - enabled: false, - highlightStyle: null - }, - preFx: true, - applyHighlightStyle: function(b, a) { - a = a || {}; - if (this.getSprite()) { - Ext.apply(a, this.getSprite().self.def.normalize(b)) - } else { - Ext.apply(a, b) - } - return a - }, - prepareAttributes: function(a) { - if (!a.hasOwnProperty("highlightOriginal")) { - a.highlighted = false; - a.highlightOriginal = Ext.Object.chain(a); - a.highlightOriginal.prototype = a; - a.highlightOriginal.removeFromInstance = {} - } - if (this._previous) { - this._previous.prepareAttributes(a.highlightOriginal) - } - }, - updateSprite: function(b, a) { - if (b) { - if (this.getHighlightStyle()) { - this._highlightStyle = b.self.def.normalize(this.getHighlightStyle()) - } - this.setHighlightStyle(b.config.highlight) - } - b.self.def.setConfig({ - defaults: { - highlighted: false - }, - processors: { - highlighted: "bool" - } - }); - this.setSprite(b) - }, - filterChanges: function(a, d) { - var e = this, - f = a.highlightOriginal, - c = e.getHighlightStyle(), - b; - if (a.highlighted) { - for (b in d) { - if (c.hasOwnProperty(b)) { - f[b] = d[b]; - delete d[b] - } - } - } - for (b in d) { - if (b !== "highlighted" && f[b] === d[b]) { - delete d[b] - } - } - return d - }, - pushDown: function(e, g) { - var f = this.getHighlightStyle(), - c = e.highlightOriginal, - i = c.removeFromInstance, - d, a, h, b; - if (g.hasOwnProperty("highlighted")) { - d = g.highlighted; - delete g.highlighted; - if (this._previous) { - g = this._previous.pushDown(c, g) - } - g = this.filterChanges(e, g); - if (d !== e.highlighted) { - if (d) { - for (a in f) { - if (a in g) { - c[a] = g[a] - } else { - h = e.template && e.template.ownAttr; - if (h && !e.prototype.hasOwnProperty(a)) { - i[a] = true; - c[a] = h.animationOriginal[a] - } else { - b = c.timers[a]; - if (b && b.remove) { - i[a] = true - } - c[a] = e[a] - } - } - if (c[a] !== f[a]) { - g[a] = f[a] - } - } - } else { - for (a in f) { - if (!(a in g)) { - g[a] = c[a] - } - delete c[a] - } - g.removeFromInstance = g.removeFromInstance || {}; - Ext.apply(g.removeFromInstance, i); - c.removeFromInstance = {} - } - g.highlighted = d - } - } else { - if (this._previous) { - g = this._previous.pushDown(c, g) - } - g = this.filterChanges(e, g) - } - return g - }, - popUp: function(a, b) { - b = this.filterChanges(a, b); - Ext.draw.modifier.Modifier.prototype.popUp.call(this, a, b) - } -}); -Ext.define("Ext.draw.sprite.Sprite", { - alias: "sprite.sprite", - mixins: { - observable: "Ext.mixin.Observable" - }, - requires: ["Ext.draw.Draw", "Ext.draw.gradient.Gradient", "Ext.draw.sprite.AttributeDefinition", "Ext.draw.modifier.Target", "Ext.draw.modifier.Animation", "Ext.draw.modifier.Highlight"], - isSprite: true, - statics: { - defaultHitTestOptions: { - fill: true, - stroke: true - } - }, - inheritableStatics: { - def: { - processors: { - strokeStyle: "color", - fillStyle: "color", - strokeOpacity: "limited01", - fillOpacity: "limited01", - lineWidth: "number", - lineCap: "enums(butt,round,square)", - lineJoin: "enums(round,bevel,miter)", - lineDash: "data", - lineDashOffset: "number", - miterLimit: "number", - shadowColor: "color", - shadowOffsetX: "number", - shadowOffsetY: "number", - shadowBlur: "number", - globalAlpha: "limited01", - globalCompositeOperation: "enums(source-over,destination-over,source-in,destination-in,source-out,destination-out,source-atop,destination-atop,lighter,xor,copy)", - hidden: "bool", - transformFillStroke: "bool", - zIndex: "number", - translationX: "number", - translationY: "number", - rotationRads: "number", - rotationCenterX: "number", - rotationCenterY: "number", - scalingX: "number", - scalingY: "number", - scalingCenterX: "number", - scalingCenterY: "number", - constrainGradients: "bool" - }, - aliases: { - stroke: "strokeStyle", - fill: "fillStyle", - color: "fillStyle", - "stroke-width": "lineWidth", - "stroke-linecap": "lineCap", - "stroke-linejoin": "lineJoin", - "stroke-miterlimit": "miterLimit", - "text-anchor": "textAlign", - opacity: "globalAlpha", - translateX: "translationX", - translateY: "translationY", - rotateRads: "rotationRads", - rotateCenterX: "rotationCenterX", - rotateCenterY: "rotationCenterY", - scaleX: "scalingX", - scaleY: "scalingY", - scaleCenterX: "scalingCenterX", - scaleCenterY: "scalingCenterY" - }, - defaults: { - hidden: false, - zIndex: 0, - strokeStyle: "none", - fillStyle: "none", - lineWidth: 1, - lineDash: [], - lineDashOffset: 0, - lineCap: "butt", - lineJoin: "miter", - miterLimit: 10, - shadowColor: "none", - shadowOffsetX: 0, - shadowOffsetY: 0, - shadowBlur: 0, - globalAlpha: 1, - strokeOpacity: 1, - fillOpacity: 1, - transformFillStroke: false, - translationX: 0, - translationY: 0, - rotationRads: 0, - rotationCenterX: null, - rotationCenterY: null, - scalingX: 1, - scalingY: 1, - scalingCenterX: null, - scalingCenterY: null, - constrainGradients: false - }, - triggers: { - zIndex: "zIndex", - globalAlpha: "canvas", - globalCompositeOperation: "canvas", - transformFillStroke: "canvas", - strokeStyle: "canvas", - fillStyle: "canvas", - strokeOpacity: "canvas", - fillOpacity: "canvas", - lineWidth: "canvas", - lineCap: "canvas", - lineJoin: "canvas", - lineDash: "canvas", - lineDashOffset: "canvas", - miterLimit: "canvas", - shadowColor: "canvas", - shadowOffsetX: "canvas", - shadowOffsetY: "canvas", - shadowBlur: "canvas", - translationX: "transform", - translationY: "transform", - rotationRads: "transform", - rotationCenterX: "transform", - rotationCenterY: "transform", - scalingX: "transform", - scalingY: "transform", - scalingCenterX: "transform", - scalingCenterY: "transform", - constrainGradients: "canvas" - }, - updaters: { - bbox: "bboxUpdater", - zIndex: function(a) { - a.dirtyZIndex = true - }, - transform: function(a) { - a.dirtyTransform = true; - a.bbox.transform.dirty = true - } - } - } - }, - config: { - parent: null, - surface: null - }, - onClassExtended: function(d, c) { - var b = d.superclass.self.def.initialConfig, - e = c.inheritableStatics && c.inheritableStatics.def, - a; - if (e) { - a = Ext.Object.merge({}, b, e); - d.def = new Ext.draw.sprite.AttributeDefinition(a); - delete c.inheritableStatics.def - } else { - d.def = new Ext.draw.sprite.AttributeDefinition(b) - } - d.def.spriteClass = d - }, - constructor: function(b) { - var d = this, - c = d.self.def, - e = c.getDefaults(), - a; - b = Ext.isObject(b) ? b : {}; - d.id = b.id || Ext.id(null, "ext-sprite-"); - d.attr = {}; - d.mixins.observable.constructor.apply(d, arguments); - a = Ext.Array.from(b.modifiers, true); - d.prepareModifiers(a); - d.initializeAttributes(); - d.setAttributes(e, true); - d.setAttributes(b) - }, - getDirty: function() { - return this.attr.dirty - }, - setDirty: function(b) { - this.attr.dirty = b; - if (b) { - var a = this.getParent(); - if (a) { - a.setDirty(true) - } - } - }, - addModifier: function(a, b) { - var c = this; - if (!(a instanceof Ext.draw.modifier.Modifier)) { - a = Ext.factory(a, null, null, "modifier") - } - a.setSprite(c); - if (a.preFx || a.config && a.config.preFx) { - if (c.fx.getPrevious()) { - c.fx.getPrevious().setNext(a) - } - a.setNext(c.fx) - } else { - c.topModifier.getPrevious().setNext(a); - a.setNext(c.topModifier) - } - if (b) { - c.initializeAttributes() - } - return a - }, - prepareModifiers: function(d) { - var c = this, - a, b; - c.topModifier = new Ext.draw.modifier.Target({ - sprite: c - }); - c.fx = new Ext.draw.modifier.Animation({ - sprite: c - }); - c.fx.setNext(c.topModifier); - for (a = 0, b = d.length; a < b; a++) { - c.addModifier(d[a], false) - } - }, - getAnimation: function() { - return this.fx - }, - setAnimation: function(a) { - this.fx.setConfig(a) - }, - initializeAttributes: function() { - this.topModifier.prepareAttributes(this.attr) - }, - callUpdaters: function(d) { - var e = this, - h = d.pendingUpdaters, - i = e.self.def.getUpdaters(), - c = false, - a = false, - b, g, f; - e.callUpdaters = Ext.emptyFn; - do { - c = false; - for (g in h) { - c = true; - b = h[g]; - delete h[g]; - f = i[g]; - if (typeof f === "string") { - f = e[f] - } - if (f) { - f.call(e, d, b) - } - } - a = a || c - } while (c); - delete e.callUpdaters; - if (a) { - e.setDirty(true) - } - }, - scheduleUpdaters: function(a, e, c) { - var f; - if (c) { - for (var b = 0, d = e.length; b < d; b++) { - f = e[b]; - this.scheduleUpdater(a, f, c) - } - } else { - for (f in e) { - c = e[f]; - this.scheduleUpdater(a, f, c) - } - } - }, - scheduleUpdater: function(a, c, b) { - b = b || []; - var d = a.pendingUpdaters; - if (c in d) { - if (b.length) { - d[c] = Ext.Array.merge(d[c], b) - } - } else { - d[c] = b - } - }, - setAttributes: function(d, g, c) { - var a = this.attr, - b, e, f; - if (g) { - if (c) { - this.topModifier.pushDown(a, d) - } else { - f = {}; - for (b in d) { - e = d[b]; - if (e !== a[b]) { - f[b] = e - } - } - this.topModifier.pushDown(a, f) - } - } else { - this.topModifier.pushDown(a, this.self.def.normalize(d)) - } - }, - setAttributesBypassingNormalization: function(b, a) { - return this.setAttributes(b, true, a) - }, - bboxUpdater: function(b) { - var c = b.rotationRads !== 0, - a = b.scalingX !== 1 || b.scalingY !== 1, - d = b.rotationCenterX === null || b.rotationCenterY === null, - e = b.scalingCenterX === null || b.scalingCenterY === null; - b.bbox.plain.dirty = true; - b.bbox.transform.dirty = true; - if (c && d || a && e) { - this.scheduleUpdater(b, "transform") - } - }, - getBBox: function(d) { - var e = this, - a = e.attr, - f = a.bbox, - c = f.plain, - b = f.transform; - if (c.dirty) { - e.updatePlainBBox(c); - c.dirty = false - } - if (!d) { - e.applyTransformations(); - if (b.dirty) { - e.updateTransformedBBox(b, c); - b.dirty = false - } - return b - } - return c - }, - updatePlainBBox: Ext.emptyFn, - updateTransformedBBox: function(a, b) { - this.attr.matrix.transformBBox(b, 0, a) - }, - getBBoxCenter: function(a) { - var b = this.getBBox(a); - if (b) { - return [b.x + b.width * 0.5, b.y + b.height * 0.5] - } else { - return [0, 0] - } - }, - hide: function() { - this.attr.hidden = true; - this.setDirty(true); - return this - }, - show: function() { - this.attr.hidden = false; - this.setDirty(true); - return this - }, - useAttributes: function(i, f) { - this.applyTransformations(); - var d = this.attr, - h = d.canvasAttributes, - e = h.strokeStyle, - g = h.fillStyle, - b = h.lineDash, - c = h.lineDashOffset, - a; - if (e) { - if (e.isGradient) { - i.strokeStyle = "black"; - i.strokeGradient = e - } else { - i.strokeGradient = false - } - } - if (g) { - if (g.isGradient) { - i.fillStyle = "black"; - i.fillGradient = g - } else { - i.fillGradient = false - } - } - if (b) { - i.setLineDash(b) - } - if (Ext.isNumber(c + i.lineDashOffset)) { - i.lineDashOffset = c - } - for (a in h) { - if (h[a] !== undefined && h[a] !== i[a]) { - i[a] = h[a] - } - } - this.setGradientBBox(i, f) - }, - setGradientBBox: function(b, c) { - var a = this.attr; - if (a.constrainGradients) { - b.setGradientBBox({ - x: c[0], - y: c[1], - width: c[2], - height: c[3] - }) - } else { - b.setGradientBBox(this.getBBox(a.transformFillStroke)) - } - }, - applyTransformations: function(b) { - if (!b && !this.attr.dirtyTransform) { - return - } - var r = this, - k = r.attr, - p = r.getBBoxCenter(true), - g = p[0], - f = p[1], - q = k.translationX, - o = k.translationY, - j = k.scalingX, - i = k.scalingY === null ? k.scalingX : k.scalingY, - m = k.scalingCenterX === null ? g : k.scalingCenterX, - l = k.scalingCenterY === null ? f : k.scalingCenterY, - s = k.rotationRads, - e = k.rotationCenterX === null ? g : k.rotationCenterX, - d = k.rotationCenterY === null ? f : k.rotationCenterY, - c = Math.cos(s), - a = Math.sin(s), - n, h; - if (j === 1 && i === 1) { - m = 0; - l = 0 - } - if (s === 0) { - e = 0; - d = 0 - } - n = m * (1 - j) - e; - h = l * (1 - i) - d; - k.matrix.elements = [c * j, a * j, -a * i, c * i, c * n - a * h + e + q, a * n + c * h + d + o]; - k.matrix.inverse(k.inverseMatrix); - k.dirtyTransform = false; - k.bbox.transform.dirty = true - }, - transform: function(b, c) { - var a = this.attr, - e = a.matrix, - d; - if (b && b.isMatrix) { - d = b.elements - } else { - d = b - } - e.prepend.apply(e, d.slice()); - e.inverse(a.inverseMatrix); - if (c) { - this.updateTransformAttributes() - } - a.dirtyTransform = false; - a.bbox.transform.dirty = true; - this.setDirty(true); - return this - }, - updateTransformAttributes: function() { - var a = this.attr, - b = a.matrix.split(); - a.rotationRads = b.rotate; - a.rotationCenterX = 0; - a.rotationCenterY = 0; - a.scalingX = b.scaleX; - a.scalingY = b.scaleY; - a.scalingCenterX = 0; - a.scalingCenterY = 0; - a.translationX = b.translateX; - a.translationY = b.translateY - }, - resetTransform: function(b) { - var a = this.attr; - a.matrix.reset(); - a.inverseMatrix.reset(); - if (!b) { - this.updateTransformAttributes() - } - a.dirtyTransform = false; - a.bbox.transform.dirty = true; - this.setDirty(true); - return this - }, - setTransform: function(a, b) { - this.resetTransform(true); - this.transform.call(this, a, b); - return this - }, - preRender: Ext.emptyFn, - render: Ext.emptyFn, - hitTest: function(b, c) { - if (this.isVisible()) { - var a = b[0], - f = b[1], - e = this.getBBox(), - d = e && a >= e.x && a <= (e.x + e.width) && f >= e.y && f <= (e.y + e.height); - if (d) { - return { - sprite: this - } - } - } - return null - }, - isVisible: function() { - var e = this.attr, - f = this.getParent(), - g = f && (f.isSurface || f.isVisible()), - d = g && !e.hidden && e.globalAlpha, - b = Ext.draw.Color.NONE, - a = Ext.draw.Color.RGBA_NONE, - c = e.fillOpacity && e.fillStyle !== b && e.fillStyle !== a, - i = e.strokeOpacity && e.strokeStyle !== b && e.strokeStyle !== a, - h = d && (c || i); - return !!h - }, - repaint: function() { - var a = this.getSurface(); - if (a) { - a.renderFrame() - } - }, - remove: function() { - var a = this.getSurface(); - if (a && a.isSurface) { - return a.remove(this) - } - return null - }, - destroy: function() { - var b = this, - a = b.topModifier, - c; - while (a) { - c = a; - a = a.getPrevious(); - c.destroy() - } - delete b.attr; - b.remove(); - if (b.fireEvent("beforedestroy", b) !== false) { - b.fireEvent("destroy", b) - } - b.callParent() - } -}, function() { - this.def = new Ext.draw.sprite.AttributeDefinition(this.def); - this.def.spriteClass = this -}); -Ext.define("Ext.draw.Path", { - requires: ["Ext.draw.Draw"], - statics: { - pathRe: /,?([achlmqrstvxz]),?/gi, - pathRe2: /-/gi, - pathSplitRe: /\s|,/g - }, - svgString: "", - constructor: function(a) { - var b = this; - b.commands = []; - b.params = []; - b.cursor = null; - b.startX = 0; - b.startY = 0; - if (a) { - b.fromSvgString(a) - } - }, - clear: function() { - var a = this; - a.params.length = 0; - a.commands.length = 0; - a.cursor = null; - a.startX = 0; - a.startY = 0; - a.dirt() - }, - dirt: function() { - this.svgString = "" - }, - moveTo: function(a, c) { - var b = this; - if (!b.cursor) { - b.cursor = [a, c] - } - b.params.push(a, c); - b.commands.push("M"); - b.startX = a; - b.startY = c; - b.cursor[0] = a; - b.cursor[1] = c; - b.dirt() - }, - lineTo: function(a, c) { - var b = this; - if (!b.cursor) { - b.cursor = [a, c]; - b.params.push(a, c); - b.commands.push("M") - } else { - b.params.push(a, c); - b.commands.push("L") - } - b.cursor[0] = a; - b.cursor[1] = c; - b.dirt() - }, - bezierCurveTo: function(c, e, b, d, a, g) { - var f = this; - if (!f.cursor) { - f.moveTo(c, e) - } - f.params.push(c, e, b, d, a, g); - f.commands.push("C"); - f.cursor[0] = a; - f.cursor[1] = g; - f.dirt() - }, - quadraticCurveTo: function(b, e, a, d) { - var c = this; - if (!c.cursor) { - c.moveTo(b, e) - } - c.bezierCurveTo((2 * b + c.cursor[0]) / 3, (2 * e + c.cursor[1]) / 3, (2 * b + a) / 3, (2 * e + d) / 3, a, d) - }, - closePath: function() { - var a = this; - if (a.cursor) { - a.cursor = null; - a.commands.push("Z"); - a.dirt() - } - }, - arcTo: function(A, f, z, d, j, i, v) { - var E = this; - if (i === undefined) { - i = j - } - if (v === undefined) { - v = 0 - } - if (!E.cursor) { - E.moveTo(A, f); - return - } - if (j === 0 || i === 0) { - E.lineTo(A, f); - return - } - z -= A; - d -= f; - var B = E.cursor[0] - A, - g = E.cursor[1] - f, - C = z * g - d * B, - b, a, l, r, k, q, x = Math.sqrt(B * B + g * g), - u = Math.sqrt(z * z + d * d), - t, e, c; - if (C === 0) { - E.lineTo(A, f); - return - } - if (i !== j) { - b = Math.cos(v); - a = Math.sin(v); - l = b / j; - r = a / i; - k = -a / j; - q = b / i; - var D = l * B + r * g; - g = k * B + q * g; - B = D; - D = l * z + r * d; - d = k * z + q * d; - z = D - } else { - B /= j; - g /= i; - z /= j; - d /= i - } - e = B * u + z * x; - c = g * u + d * x; - t = 1 / (Math.sin(Math.asin(Math.abs(C) / (x * u)) * 0.5) * Math.sqrt(e * e + c * c)); - e *= t; - c *= t; - var o = (e * B + c * g) / (B * B + g * g), - m = (e * z + c * d) / (z * z + d * d); - var n = B * o - e, - p = g * o - c, - h = z * m - e, - y = d * m - c, - w = Math.atan2(p, n), - s = Math.atan2(y, h); - if (C > 0) { - if (s < w) { - s += Math.PI * 2 - } - } else { - if (w < s) { - w += Math.PI * 2 - } - } - if (i !== j) { - e = b * e * j - a * c * i + A; - c = a * c * i + b * c * i + f; - E.lineTo(b * j * n - a * i * p + e, a * j * n + b * i * p + c); - E.ellipse(e, c, j, i, v, w, s, C < 0) - } else { - e = e * j + A; - c = c * i + f; - E.lineTo(j * n + e, i * p + c); - E.ellipse(e, c, j, i, v, w, s, C < 0) - } - }, - ellipse: function(h, f, c, a, q, n, d, e) { - var o = this, - g = o.params, - b = g.length, - m, l, k; - if (d - n >= Math.PI * 2) { - o.ellipse(h, f, c, a, q, n, n + Math.PI, e); - o.ellipse(h, f, c, a, q, n + Math.PI, d, e); - return - } - if (!e) { - if (d < n) { - d += Math.PI * 2 - } - m = o.approximateArc(g, h, f, c, a, q, n, d) - } else { - if (n < d) { - n += Math.PI * 2 - } - m = o.approximateArc(g, h, f, c, a, q, d, n); - for (l = b, k = g.length - 2; l < k; l += 2, k -= 2) { - var p = g[l]; - g[l] = g[k]; - g[k] = p; - p = g[l + 1]; - g[l + 1] = g[k + 1]; - g[k + 1] = p - } - } - if (!o.cursor) { - o.cursor = [g[g.length - 2], g[g.length - 1]]; - o.commands.push("M") - } else { - o.cursor[0] = g[g.length - 2]; - o.cursor[1] = g[g.length - 1]; - o.commands.push("L") - } - for (l = 2; l < m; l += 6) { - o.commands.push("C") - } - o.dirt() - }, - arc: function(b, f, a, d, c, e) { - this.ellipse(b, f, a, a, 0, d, c, e) - }, - rect: function(b, e, c, a) { - if (c == 0 || a == 0) { - return - } - var d = this; - d.moveTo(b, e); - d.lineTo(b + c, e); - d.lineTo(b + c, e + a); - d.lineTo(b, e + a); - d.closePath() - }, - approximateArc: function(s, i, f, o, n, d, x, v) { - var e = Math.cos(d), - z = Math.sin(d), - k = Math.cos(x), - l = Math.sin(x), - q = e * k * o - z * l * n, - y = -e * l * o - z * k * n, - p = z * k * o + e * l * n, - w = -z * l * o + e * k * n, - m = Math.PI / 2, - r = 2, - j = q, - u = y, - h = p, - t = w, - b = 0.547443256150549, - C, g, A, a, B, c; - v -= x; - if (v < 0) { - v += Math.PI * 2 - } - s.push(q + i, p + f); - while (v >= m) { - s.push(j + u * b + i, h + t * b + f, j * b + u + i, h * b + t + f, u + i, t + f); - r += 6; - v -= m; - C = j; - j = u; - u = -C; - C = h; - h = t; - t = -C - } - if (v) { - g = (0.3294738052815987 + 0.012120855841304373 * v) * v; - A = Math.cos(v); - a = Math.sin(v); - B = A + g * a; - c = a - g * A; - s.push(j + u * g + i, h + t * g + f, j * B + u * c + i, h * B + t * c + f, j * A + u * a + i, h * A + t * a + f); - r += 6 - } - return r - }, - arcSvg: function(j, h, r, m, w, t, c) { - if (j < 0) { - j = -j - } - if (h < 0) { - h = -h - } - var x = this, - u = x.cursor[0], - f = x.cursor[1], - a = (u - t) / 2, - y = (f - c) / 2, - d = Math.cos(r), - s = Math.sin(r), - o = a * d + y * s, - v = -a * s + y * d, - i = o / j, - g = v / h, - p = i * i + g * g, - e = (u + t) * 0.5, - b = (f + c) * 0.5, - l = 0, - k = 0; - if (p >= 1) { - p = Math.sqrt(p); - j *= p; - h *= p - } else { - p = Math.sqrt(1 / p - 1); - if (m === w) { - p = -p - } - l = p * j * g; - k = -p * h * i; - e += d * l - s * k; - b += s * l + d * k - } - var q = Math.atan2((v - k) / h, (o - l) / j), - n = Math.atan2((-v - k) / h, (-o - l) / j) - q; - if (w) { - if (n <= 0) { - n += Math.PI * 2 - } - } else { - if (n >= 0) { - n -= Math.PI * 2 - } - } - x.ellipse(e, b, j, h, r, q, q + n, 1 - w) - }, - fromSvgString: function(e) { - if (!e) { - return - } - var m = this, - h, l = { - a: 7, - c: 6, - h: 1, - l: 2, - m: 2, - q: 4, - s: 4, - t: 2, - v: 1, - z: 0, - A: 7, - C: 6, - H: 1, - L: 2, - M: 2, - Q: 4, - S: 4, - T: 2, - V: 1, - Z: 0 - }, - k = "", - g, f, c = 0, - b = 0, - d = false, - j, n, a; - if (Ext.isString(e)) { - h = e.replace(Ext.draw.Path.pathRe, " $1 ").replace(Ext.draw.Path.pathRe2, " -").split(Ext.draw.Path.pathSplitRe) - } else { - if (Ext.isArray(e)) { - h = e.join(",").split(Ext.draw.Path.pathSplitRe) - } - } - for (j = 0, n = 0; j < h.length; j++) { - if (h[j] !== "") { - h[n++] = h[j] - } - } - h.length = n; - m.clear(); - for (j = 0; j < h.length;) { - k = d; - d = h[j]; - a = (d.toUpperCase() !== d); - j++; - switch (d) { - case "M": - m.moveTo(c = +h[j], b = +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2 - } - break; - case "L": - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2 - } - break; - case "A": - while (j < n && !l.hasOwnProperty(h[j])) { - m.arcSvg(+h[j], +h[j + 1], +h[j + 2] * Math.PI / 180, +h[j + 3], +h[j + 4], c = +h[j + 5], b = +h[j + 6]); - j += 7 - } - break; - case "C": - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(+h[j], +h[j + 1], g = +h[j + 2], f = +h[j + 3], c = +h[j + 4], b = +h[j + 5]); - j += 6 - } - break; - case "Z": - m.closePath(); - break; - case "m": - m.moveTo(c += +h[j], b += +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2 - } - break; - case "l": - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2 - } - break; - case "a": - while (j < n && !l.hasOwnProperty(h[j])) { - m.arcSvg(+h[j], +h[j + 1], +h[j + 2] * Math.PI / 180, +h[j + 3], +h[j + 4], c += +h[j + 5], b += +h[j + 6]); - j += 7 - } - break; - case "c": - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + (+h[j]), b + (+h[j + 1]), g = c + (+h[j + 2]), f = b + (+h[j + 3]), c += +h[j + 4], b += +h[j + 5]); - j += 6 - } - break; - case "z": - m.closePath(); - break; - case "s": - if (!(k === "c" || k === "C" || k === "s" || k === "S")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + c - g, b + b - f, g = c + (+h[j]), f = b + (+h[j + 1]), c += +h[j + 2], b += +h[j + 3]); - j += 4 - } - break; - case "S": - if (!(k === "c" || k === "C" || k === "s" || k === "S")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + c - g, b + b - f, g = +h[j], f = +h[j + 1], c = (+h[j + 2]), b = (+h[j + 3])); - j += 4 - } - break; - case "q": - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + (+h[j]), f = b + (+h[j + 1]), c += +h[j + 2], b += +h[j + 3]); - j += 4 - } - break; - case "Q": - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = +h[j], f = +h[j + 1], c = +h[j + 2], b = +h[j + 3]); - j += 4 - } - break; - case "t": - if (!(k === "q" || k === "Q" || k === "t" || k === "T")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + c - g, f = b + b - f, c += +h[j + 1], b += +h[j + 2]); - j += 2 - } - break; - case "T": - if (!(k === "q" || k === "Q" || k === "t" || k === "T")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + c - g, f = b + b - f, c = (+h[j + 1]), b = (+h[j + 2])); - j += 2 - } - break; - case "h": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b); - j++ - } - break; - case "H": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b); - j++ - } - break; - case "v": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c, b += +h[j]); - j++ - } - break; - case "V": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c, b = +h[j]); - j++ - } - break - } - } - }, - clone: function() { - var a = this, - b = new Ext.draw.Path(); - b.params = a.params.slice(0); - b.commands = a.commands.slice(0); - b.cursor = a.cursor ? a.cursor.slice(0) : null; - b.startX = a.startX; - b.startY = a.startY; - b.svgString = a.svgString; - return b - }, - transform: function(j) { - if (j.isIdentity()) { - return - } - var a = j.getXX(), - f = j.getYX(), - m = j.getDX(), - l = j.getXY(), - e = j.getYY(), - k = j.getDY(), - b = this.params, - c = 0, - d = b.length, - h, g; - for (; c < d; c += 2) { - h = b[c]; - g = b[c + 1]; - b[c] = h * a + g * f + m; - b[c + 1] = h * l + g * e + k - } - this.dirt() - }, - getDimension: function(f) { - if (!f) { - f = {} - } - if (!this.commands || !this.commands.length) { - f.x = 0; - f.y = 0; - f.width = 0; - f.height = 0; - return f - } - f.left = Infinity; - f.top = Infinity; - f.right = -Infinity; - f.bottom = -Infinity; - var d = 0, - c = 0, - b = this.commands, - g = this.params, - e = b.length, - a, h; - for (; d < e; d++) { - switch (b[d]) { - case "M": - case "L": - a = g[c]; - h = g[c + 1]; - f.left = Math.min(a, f.left); - f.top = Math.min(h, f.top); - f.right = Math.max(a, f.right); - f.bottom = Math.max(h, f.bottom); - c += 2; - break; - case "C": - this.expandDimension(f, a, h, g[c], g[c + 1], g[c + 2], g[c + 3], a = g[c + 4], h = g[c + 5]); - c += 6; - break - } - } - f.x = f.left; - f.y = f.top; - f.width = f.right - f.left; - f.height = f.bottom - f.top; - return f - }, - getDimensionWithTransform: function(n, f) { - if (!this.commands || !this.commands.length) { - if (!f) { - f = {} - } - f.x = 0; - f.y = 0; - f.width = 0; - f.height = 0; - return f - } - f.left = Infinity; - f.top = Infinity; - f.right = -Infinity; - f.bottom = -Infinity; - var a = n.getXX(), - k = n.getYX(), - q = n.getDX(), - p = n.getXY(), - h = n.getYY(), - o = n.getDY(), - e = 0, - d = 0, - b = this.commands, - c = this.params, - g = b.length, - m, l; - for (; e < g; e++) { - switch (b[e]) { - case "M": - case "L": - m = c[d] * a + c[d + 1] * k + q; - l = c[d] * p + c[d + 1] * h + o; - f.left = Math.min(m, f.left); - f.top = Math.min(l, f.top); - f.right = Math.max(m, f.right); - f.bottom = Math.max(l, f.bottom); - d += 2; - break; - case "C": - this.expandDimension(f, m, l, c[d] * a + c[d + 1] * k + q, c[d] * p + c[d + 1] * h + o, c[d + 2] * a + c[d + 3] * k + q, c[d + 2] * p + c[d + 3] * h + o, m = c[d + 4] * a + c[d + 5] * k + q, l = c[d + 4] * p + c[d + 5] * h + o); - d += 6; - break - } - } - if (!f) { - f = {} - } - f.x = f.left; - f.y = f.top; - f.width = f.right - f.left; - f.height = f.bottom - f.top; - return f - }, - expandDimension: function(i, d, p, k, g, j, e, c, o) { - var m = this, - f = i.left, - a = i.right, - q = i.top, - n = i.bottom, - h = m.dim || (m.dim = []); - m.curveDimension(d, k, j, c, h); - f = Math.min(f, h[0]); - a = Math.max(a, h[1]); - m.curveDimension(p, g, e, o, h); - q = Math.min(q, h[0]); - n = Math.max(n, h[1]); - i.left = f; - i.right = a; - i.top = q; - i.bottom = n - }, - curveDimension: function(p, n, k, j, h) { - var i = 3 * (-p + 3 * (n - k) + j), - g = 6 * (p - 2 * n + k), - f = -3 * (p - n), - o, m, e = Math.min(p, j), - l = Math.max(p, j), - q; - if (i === 0) { - if (g === 0) { - h[0] = e; - h[1] = l; - return - } else { - o = -f / g; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - } - } else { - q = g * g - 4 * i * f; - if (q >= 0) { - q = Math.sqrt(q); - o = (q - g) / 2 / i; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - if (q > 0) { - o -= q / i; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - } - } - } - h[0] = e; - h[1] = l - }, - interpolate: function(f, e, j, i, g) { - if (g === 0) { - return f - } - if (g === 1) { - return i - } - var h = (1 - g) / g; - return g * g * g * (i + h * (3 * j + h * (3 * e + h * f))) - }, - fromStripes: function(g) { - var e = this, - c = 0, - d = g.length, - b, a, f; - e.clear(); - for (; c < d; c++) { - f = g[c]; - e.params.push.apply(e.params, f); - e.commands.push("M"); - for (b = 2, a = f.length; b < a; b += 6) { - e.commands.push("C") - } - } - if (!e.cursor) { - e.cursor = [] - } - e.cursor[0] = e.params[e.params.length - 2]; - e.cursor[1] = e.params[e.params.length - 1]; - e.dirt() - }, - toStripes: function(k) { - var o = k || [], - p, n, m, b, a, h, g, f, e, c = this.commands, - d = this.params, - l = c.length; - for (f = 0, e = 0; f < l; f++) { - switch (c[f]) { - case "M": - p = [h = b = d[e++], g = a = d[e++]]; - o.push(p); - break; - case "L": - n = d[e++]; - m = d[e++]; - p.push((b + b + n) / 3, (a + a + m) / 3, (b + n + n) / 3, (a + m + m) / 3, b = n, a = m); - break; - case "C": - p.push(d[e++], d[e++], d[e++], d[e++], b = d[e++], a = d[e++]); - break; - case "Z": - n = h; - m = g; - p.push((b + b + n) / 3, (a + a + m) / 3, (b + n + n) / 3, (a + m + m) / 3, b = n, a = m); - break - } - } - return o - }, - updateSvgString: function() { - var b = [], - a = this.commands, - f = this.params, - e = a.length, - d = 0, - c = 0; - for (; d < e; d++) { - switch (a[d]) { - case "M": - b.push("M" + f[c] + "," + f[c + 1]); - c += 2; - break; - case "L": - b.push("L" + f[c] + "," + f[c + 1]); - c += 2; - break; - case "C": - b.push("C" + f[c] + "," + f[c + 1] + " " + f[c + 2] + "," + f[c + 3] + " " + f[c + 4] + "," + f[c + 5]); - c += 6; - break; - case "Z": - b.push("Z"); - break - } - } - this.svgString = b.join("") - }, - toString: function() { - if (!this.svgString) { - this.updateSvgString() - } - return this.svgString - } -}); -Ext.define("Ext.draw.overrides.Path", { - override: "Ext.draw.Path", - rayOrigin: { - x: -10000, - y: -10000 - }, - isPointInPath: function(o, n) { - var m = this, - c = m.commands, - q = Ext.draw.PathUtil, - p = m.rayOrigin, - f = m.params, - l = c.length, - e = null, - d = null, - b = 0, - a = 0, - k = 0, - h, g; - for (h = 0, g = 0; h < l; h++) { - switch (c[h]) { - case "M": - if (e !== null) { - if (q.linesIntersection(e, d, b, a, p.x, p.y, o, n)) { - k += 1 - } - } - e = b = f[g]; - d = a = f[g + 1]; - g += 2; - break; - case "L": - if (q.linesIntersection(b, a, f[g], f[g + 1], p.x, p.y, o, n)) { - k += 1 - } - b = f[g]; - a = f[g + 1]; - g += 2; - break; - case "C": - k += q.cubicLineIntersections(b, f[g], f[g + 2], f[g + 4], a, f[g + 1], f[g + 3], f[g + 5], p.x, p.y, o, n).length; - b = f[g + 4]; - a = f[g + 5]; - g += 6; - break; - case "Z": - if (e !== null) { - if (q.linesIntersection(e, d, b, a, p.x, p.y, o, n)) { - k += 1 - } - } - break - } - } - return k % 2 === 1 - }, - isPointOnPath: function(n, m) { - var l = this, - c = l.commands, - o = Ext.draw.PathUtil, - f = l.params, - k = c.length, - e = null, - d = null, - b = 0, - a = 0, - h, g; - for (h = 0, g = 0; h < k; h++) { - switch (c[h]) { - case "M": - if (e !== null) { - if (o.pointOnLine(e, d, b, a, n, m)) { - return true - } - } - e = b = f[g]; - d = a = f[g + 1]; - g += 2; - break; - case "L": - if (o.pointOnLine(b, a, f[g], f[g + 1], n, m)) { - return true - } - b = f[g]; - a = f[g + 1]; - g += 2; - break; - case "C": - if (o.pointOnCubic(b, f[g], f[g + 2], f[g + 4], a, f[g + 1], f[g + 3], f[g + 5], n, m)) { - return true - } - b = f[g + 4]; - a = f[g + 5]; - g += 6; - break; - case "Z": - if (e !== null) { - if (o.pointOnLine(e, d, b, a, n, m)) { - return true - } - } - break - } - } - return false - }, - getSegmentIntersections: function(t, d, s, c, r, b, o, a) { - var w = this, - g = arguments.length, - v = Ext.draw.PathUtil, - f = w.commands, - u = w.params, - k = f.length, - m = null, - l = null, - h = 0, - e = 0, - x = [], - q, n, p; - for (q = 0, n = 0; q < k; q++) { - switch (f[q]) { - case "M": - if (m !== null) { - switch (g) { - case 4: - p = v.linesIntersection(m, l, h, e, t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, m, l, h, e); - x.push.apply(x, p); - break - } - } - m = h = u[n]; - l = e = u[n + 1]; - n += 2; - break; - case "L": - switch (g) { - case 4: - p = v.linesIntersection(h, e, u[n], u[n + 1], t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, h, e, u[n], u[n + 1]); - x.push.apply(x, p); - break - } - h = u[n]; - e = u[n + 1]; - n += 2; - break; - case "C": - switch (g) { - case 4: - p = v.cubicLineIntersections(h, u[n], u[n + 2], u[n + 4], e, u[n + 1], u[n + 3], u[n + 5], t, d, s, c); - x.push.apply(x, p); - break; - case 8: - p = v.cubicsIntersections(h, u[n], u[n + 2], u[n + 4], e, u[n + 1], u[n + 3], u[n + 5], t, s, r, o, d, c, b, a); - x.push.apply(x, p); - break - } - h = u[n + 4]; - e = u[n + 5]; - n += 6; - break; - case "Z": - if (m !== null) { - switch (g) { - case 4: - p = v.linesIntersection(m, l, h, e, t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, m, l, h, e); - x.push.apply(x, p); - break - } - } - break - } - } - return x - }, - getIntersections: function(o) { - var m = this, - c = m.commands, - g = m.params, - l = c.length, - f = null, - e = null, - b = 0, - a = 0, - d = [], - k, h, n; - for (k = 0, h = 0; k < l; k++) { - switch (c[k]) { - case "M": - if (f !== null) { - n = o.getSegmentIntersections.call(o, f, e, b, a); - d.push.apply(d, n) - } - f = b = g[h]; - e = a = g[h + 1]; - h += 2; - break; - case "L": - n = o.getSegmentIntersections.call(o, b, a, g[h], g[h + 1]); - d.push.apply(d, n); - b = g[h]; - a = g[h + 1]; - h += 2; - break; - case "C": - n = o.getSegmentIntersections.call(o, b, a, g[h], g[h + 1], g[h + 2], g[h + 3], g[h + 4], g[h + 5]); - d.push.apply(d, n); - b = g[h + 4]; - a = g[h + 5]; - h += 6; - break; - case "Z": - if (f !== null) { - n = o.getSegmentIntersections.call(o, f, e, b, a); - d.push.apply(d, n) - } - break - } - } - return d - } -}); -Ext.define("Ext.draw.sprite.Path", { - extend: "Ext.draw.sprite.Sprite", - requires: ["Ext.draw.Draw", "Ext.draw.Path"], - alias: ["sprite.path", "Ext.draw.Sprite"], - type: "path", - isPath: true, - inheritableStatics: { - def: { - processors: { - path: function(b, a) { - if (!(b instanceof Ext.draw.Path)) { - b = new Ext.draw.Path(b) - } - return b - } - }, - aliases: { - d: "path" - }, - triggers: { - path: "bbox" - }, - updaters: { - path: function(a) { - var b = a.path; - if (!b || b.bindAttr !== a) { - b = new Ext.draw.Path(); - b.bindAttr = a; - a.path = b - } - b.clear(); - this.updatePath(b, a); - this.scheduleUpdater(a, "bbox", ["path"]) - } - } - } - }, - updatePlainBBox: function(a) { - if (this.attr.path) { - this.attr.path.getDimension(a) - } - }, - updateTransformedBBox: function(a) { - if (this.attr.path) { - this.attr.path.getDimensionWithTransform(this.attr.matrix, a) - } - }, - render: function(b, c) { - var d = this.attr.matrix, - a = this.attr; - if (!a.path || a.path.params.length === 0) { - return - } - d.toContext(c); - c.appendPath(a.path); - c.fillStroke(a) - }, - updatePath: function(b, a) {} -}); -Ext.define("Ext.draw.overrides.sprite.Path", { - override: "Ext.draw.sprite.Path", - requires: ["Ext.draw.Color"], - isPointInPath: function(c, g) { - var b = this.attr; - if (b.fillStyle === Ext.draw.Color.RGBA_NONE) { - return this.isPointOnPath(c, g) - } - var e = b.path, - d = b.matrix, - f, a; - if (!d.isIdentity()) { - f = e.params.slice(0); - e.transform(b.matrix) - } - a = e.isPointInPath(c, g); - if (f) { - e.params = f - } - return a - }, - isPointOnPath: function(c, g) { - var b = this.attr, - e = b.path, - d = b.matrix, - f, a; - if (!d.isIdentity()) { - f = e.params.slice(0); - e.transform(b.matrix) - } - a = e.isPointOnPath(c, g); - if (f) { - e.params = f - } - return a - }, - hitTest: function(i, l) { - var e = this, - c = e.attr, - k = c.path, - g = c.matrix, - h = i[0], - f = i[1], - d = e.callParent([i, l]), - j = null, - a, b; - if (!d) { - return j - } - l = l || Ext.draw.sprite.Sprite.defaultHitTestOptions; - if (!g.isIdentity()) { - a = k.params.slice(0); - k.transform(c.matrix) - } - if (l.fill && l.stroke) { - b = c.fillStyle !== Ext.draw.Color.NONE && c.fillStyle !== Ext.draw.Color.RGBA_NONE; - if (b) { - if (k.isPointInPath(h, f)) { - j = { - sprite: e - } - } - } else { - if (k.isPointInPath(h, f) || k.isPointOnPath(h, f)) { - j = { - sprite: e - } - } - } - } else { - if (l.stroke && !l.fill) { - if (k.isPointOnPath(h, f)) { - j = { - sprite: e - } - } - } else { - if (l.fill && !l.stroke) { - if (k.isPointInPath(h, f)) { - j = { - sprite: e - } - } - } - } - } - if (a) { - k.params = a - } - return j - }, - getIntersections: function(j) { - if (!(j.isSprite && j.isPath)) { - return [] - } - var e = this.attr, - d = j.attr, - i = e.path, - h = d.path, - g = e.matrix, - a = d.matrix, - c, f, b; - if (!g.isIdentity()) { - c = i.params.slice(0); - i.transform(e.matrix) - } - if (!a.isIdentity()) { - f = h.params.slice(0); - h.transform(d.matrix) - } - b = i.getIntersections(h); - if (c) { - i.params = c - } - if (f) { - h.params = f - } - return b - } -}); -Ext.define("Ext.draw.sprite.Circle", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.circle", - type: "circle", - inheritableStatics: { - def: { - processors: { - cx: "number", - cy: "number", - r: "number" - }, - aliases: { - radius: "r", - x: "cx", - y: "cy", - centerX: "cx", - centerY: "cy" - }, - defaults: { - cx: 0, - cy: 0, - r: 4 - }, - triggers: { - cx: "path", - cy: "path", - r: "path" - } - } - }, - updatePlainBBox: function(c) { - var b = this.attr, - a = b.cx, - e = b.cy, - d = b.r; - c.x = a - d; - c.y = e - d; - c.width = d + d; - c.height = d + d - }, - updateTransformedBBox: function(d) { - var g = this.attr, - f = g.cx, - e = g.cy, - a = g.r, - h = g.matrix, - j = h.getScaleX(), - i = h.getScaleY(), - c, b; - c = j * a; - b = i * a; - d.x = h.x(f, e) - c; - d.y = h.y(f, e) - b; - d.width = c + c; - d.height = b + b - }, - updatePath: function(b, a) { - b.arc(a.cx, a.cy, a.r, 0, Math.PI * 2, false) - } -}); -Ext.define("Ext.draw.sprite.Arc", { - extend: "Ext.draw.sprite.Circle", - alias: "sprite.arc", - type: "arc", - inheritableStatics: { - def: { - processors: { - startAngle: "number", - endAngle: "number", - anticlockwise: "bool" - }, - aliases: { - from: "startAngle", - to: "endAngle", - start: "startAngle", - end: "endAngle" - }, - defaults: { - startAngle: 0, - endAngle: Math.PI * 2, - anticlockwise: false - }, - triggers: { - startAngle: "path", - endAngle: "path", - anticlockwise: "path" - } - } - }, - updatePath: function(b, a) { - b.arc(a.cx, a.cy, a.r, a.startAngle, a.endAngle, a.anticlockwise) - } -}); -Ext.define("Ext.draw.sprite.Arrow", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.arrow", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 1.5, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c * 0.7, ",", e - c * 0.4, "l", [c * 0.6, 0, 0, -c * 0.4, c, c * 0.8, -c, c * 0.8, 0, -c * 0.4, -c * 0.6, 0], "z")) - } -}); -Ext.define("Ext.draw.sprite.Composite", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.composite", - type: "composite", - isComposite: true, - config: { - sprites: [] - }, - constructor: function() { - this.sprites = []; - this.sprites.map = {}; - this.callParent(arguments) - }, - add: function(c) { - if (!c) { - return null - } - if (!c.isSprite) { - c = Ext.create("sprite." + c.type, c); - c.setParent(this); - c.setSurface(this.getSurface()) - } - var d = this, - a = d.attr, - b = c.applyTransformations; - c.applyTransformations = function() { - if (c.attr.dirtyTransform) { - a.dirtyTransform = true; - a.bbox.plain.dirty = true; - a.bbox.transform.dirty = true - } - b.call(c) - }; - d.sprites.push(c); - d.sprites.map[c.id] = c.getId(); - a.bbox.plain.dirty = true; - a.bbox.transform.dirty = true; - return c - }, - updateSurface: function(a) { - for (var b = 0, c = this.sprites.length; b < c; b++) { - this.sprites[b].setSurface(a) - } - }, - addAll: function(b) { - if (b.isSprite || b.type) { - this.add(b) - } else { - if (Ext.isArray(b)) { - var a = 0; - while (a < b.length) { - this.add(b[a++]) - } - } - } - }, - updatePlainBBox: function(g) { - var e = this, - b = Infinity, - h = -Infinity, - f = Infinity, - a = -Infinity, - j, k, c, d; - for (c = 0, d = e.sprites.length; c < d; c++) { - j = e.sprites[c]; - j.applyTransformations(); - k = j.getBBox(); - if (b > k.x) { - b = k.x - } - if (h < k.x + k.width) { - h = k.x + k.width - } - if (f > k.y) { - f = k.y - } - if (a < k.y + k.height) { - a = k.y + k.height - } - } - g.x = b; - g.y = f; - g.width = h - b; - g.height = a - f - }, - render: function(a, b, f) { - var d = this.attr.matrix, - c, e; - d.toContext(b); - for (c = 0, e = this.sprites.length; c < e; c++) { - a.renderSprite(this.sprites[c], f) - } - }, - destroy: function() { - var c = this, - d = c.sprites, - b = d.length, - a; - c.callParent(); - for (a = 0; a < b; a++) { - d[a].destroy() - } - d.length = 0 - } -}); -Ext.define("Ext.draw.sprite.Cross", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.cross", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size / 1.7, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c, ",", e, "l", [-c, -c, c, -c, c, c, c, -c, c, c, -c, c, c, c, -c, c, -c, -c, -c, c, -c, -c, "z"])) - } -}); -Ext.define("Ext.draw.sprite.Diamond", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.diamond", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 1.25, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString(["M", a, e - c, "l", c, c, -c, c, -c, -c, c, -c, "z"]) - } -}); -Ext.define("Ext.draw.sprite.Ellipse", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.ellipse", - type: "ellipse", - inheritableStatics: { - def: { - processors: { - cx: "number", - cy: "number", - rx: "number", - ry: "number", - axisRotation: "number" - }, - aliases: { - radius: "r", - x: "cx", - y: "cy", - centerX: "cx", - centerY: "cy", - radiusX: "rx", - radiusY: "ry" - }, - defaults: { - cx: 0, - cy: 0, - rx: 1, - ry: 1, - axisRotation: 0 - }, - triggers: { - cx: "path", - cy: "path", - rx: "path", - ry: "path", - axisRotation: "path" - } - } - }, - updatePlainBBox: function(c) { - var b = this.attr, - a = b.cx, - f = b.cy, - e = b.rx, - d = b.ry; - c.x = a - e; - c.y = f - d; - c.width = e + e; - c.height = d + d - }, - updateTransformedBBox: function(d) { - var i = this.attr, - f = i.cx, - e = i.cy, - c = i.rx, - b = i.ry, - l = b / c, - m = i.matrix.clone(), - a, q, k, j, p, o, n, g; - m.append(1, 0, 0, l, 0, e * (1 - l)); - a = m.getXX(); - k = m.getYX(); - p = m.getDX(); - q = m.getXY(); - j = m.getYY(); - o = m.getDY(); - n = Math.sqrt(a * a + k * k) * c; - g = Math.sqrt(q * q + j * j) * c; - d.x = f * a + e * k + p - n; - d.y = f * q + e * j + o - g; - d.width = n + n; - d.height = g + g - }, - updatePath: function(b, a) { - b.ellipse(a.cx, a.cy, a.rx, a.ry, a.axisRotation, 0, Math.PI * 2, false) - } -}); -Ext.define("Ext.draw.sprite.EllipticalArc", { - extend: "Ext.draw.sprite.Ellipse", - alias: "sprite.ellipticalArc", - type: "ellipticalArc", - inheritableStatics: { - def: { - processors: { - startAngle: "number", - endAngle: "number", - anticlockwise: "bool" - }, - aliases: { - from: "startAngle", - to: "endAngle", - start: "startAngle", - end: "endAngle" - }, - defaults: { - startAngle: 0, - endAngle: Math.PI * 2, - anticlockwise: false - }, - triggers: { - startAngle: "path", - endAngle: "path", - anticlockwise: "path" - } - } - }, - updatePath: function(b, a) { - b.ellipse(a.cx, a.cy, a.rx, a.ry, a.axisRotation, a.startAngle, a.endAngle, a.anticlockwise) - } -}); -Ext.define("Ext.draw.sprite.Rect", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.rect", - type: "rect", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number", - radius: "number" - }, - aliases: {}, - triggers: { - x: "path", - y: "path", - width: "path", - height: "path", - radius: "path" - }, - defaults: { - x: 0, - y: 0, - width: 8, - height: 8, - radius: 0 - } - } - }, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.x; - b.y = a.y; - b.width = a.width; - b.height = a.height - }, - updateTransformedBBox: function(a, b) { - this.attr.matrix.transformBBox(b, this.attr.radius, a) - }, - updatePath: function(f, d) { - var c = d.x, - g = d.y, - e = d.width, - b = d.height, - a = Math.min(d.radius, Math.abs(d.height) * 0.5, Math.abs(d.width) * 0.5); - if (a === 0) { - f.rect(c, g, e, b) - } else { - f.moveTo(c + a, g); - f.arcTo(c + e, g, c + e, g + b, a); - f.arcTo(c + e, g + b, c, g + b, a); - f.arcTo(c, g + b, c, g, a); - f.arcTo(c, g, c + a, g, a) - } - } -}); -Ext.define("Ext.draw.sprite.Image", { - extend: "Ext.draw.sprite.Rect", - alias: "sprite.image", - type: "image", - statics: { - imageLoaders: {} - }, - inheritableStatics: { - def: { - processors: { - src: "string" - }, - defaults: { - src: "", - width: null, - height: null - } - } - }, - render: function(c, o) { - var j = this, - h = j.attr, - n = h.matrix, - a = h.src, - l = h.x, - k = h.y, - b = h.width, - m = h.height, - g = Ext.draw.sprite.Image.imageLoaders[a], - f, d, e; - if (g && g.done) { - n.toContext(o); - d = g.image; - o.drawImage(d, l, k, b || (d.naturalWidth || d.width) / c.devicePixelRatio, m || (d.naturalHeight || d.height) / c.devicePixelRatio) - } else { - if (!g) { - f = new Image(); - g = Ext.draw.sprite.Image.imageLoaders[a] = { - image: f, - done: false, - pendingSprites: [j], - pendingSurfaces: [c] - }; - f.width = b; - f.height = m; - f.onload = function() { - if (!g.done) { - g.done = true; - for (e = 0; e < g.pendingSprites.length; e++) { - g.pendingSprites[e].setDirty(true) - } - for (e in g.pendingSurfaces) { - g.pendingSurfaces[e].renderFrame() - } - } - }; - f.src = a - } else { - Ext.Array.include(g.pendingSprites, j); - Ext.Array.include(g.pendingSurfaces, c) - } - } - } -}); -Ext.define("Ext.draw.sprite.Instancing", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.instancing", - type: "instancing", - isInstancing: true, - config: { - template: null - }, - instances: null, - applyTemplate: function(a) { - if (!a.isSprite) { - if (!a.xclass && !a.type) { - a.type = "circle" - } - a = Ext.create(a.xclass || "sprite." + a.type, a) - } - a.setParent(this); - return a - }, - updateTemplate: function(a, b) { - if (b) { - delete b.ownAttr - } - a.setSurface(this.getSurface()); - a.ownAttr = a.attr; - this.clearAll() - }, - updateSurface: function(a) { - var b = this.getTemplate(); - if (b) { - b.setSurface(a) - } - }, - get: function(a) { - return this.instances[a] - }, - getCount: function() { - return this.instances.length - }, - clearAll: function() { - var a = this.getTemplate(); - a.attr.children = this.instances = []; - this.position = 0 - }, - createInstance: function(d, f, c) { - var e = this.getTemplate(), - b = e.attr, - a = Ext.Object.chain(b); - e.topModifier.prepareAttributes(a); - e.attr = a; - e.setAttributes(d, f, c); - a.template = e; - this.instances.push(a); - e.attr = b; - this.position++; - return a - }, - getBBox: function() { - return null - }, - getBBoxFor: function(b, d) { - var c = this.getTemplate(), - a = c.attr, - e; - c.attr = this.instances[b]; - e = c.getBBox(d); - c.attr = a; - return e - }, - isVisible: function() { - var b = this.attr, - c = this.getParent(), - a; - a = c && c.isSurface && !b.hidden && b.globalAlpha; - return !!a - }, - isInstanceVisible: function(c) { - var e = this, - d = e.getTemplate(), - b = d.attr, - f = e.instances, - a = false; - if (!Ext.isNumber(c) || c < 0 || c >= f.length || !e.isVisible()) { - return a - } - d.attr = f[c]; - a = d.isVisible(point, options); - d.attr = b; - return a - }, - render: function(b, l, d, h) { - var g = this, - j = g.getTemplate(), - k = g.attr.matrix, - c = j.attr, - a = g.instances, - e, f = g.position; - k.toContext(l); - j.preRender(b, l, d, h); - j.useAttributes(l, h); - for (e = 0; e < f; e++) { - if (a[e].dirtyZIndex) { - break - } - } - for (e = 0; e < f; e++) { - if (a[e].hidden) { - continue - } - l.save(); - j.attr = a[e]; - j.useAttributes(l, h); - j.render(b, l, d, h); - l.restore() - } - j.attr = c - }, - setAttributesFor: function(c, e, f) { - var d = this.getTemplate(), - b = d.attr, - a = this.instances[c]; - if (!a) { - return - } - d.attr = a; - if (f) { - e = Ext.apply({}, e) - } else { - e = d.self.def.normalize(e) - } - d.topModifier.pushDown(a, e); - d.attr = b - }, - destroy: function() { - var b = this, - a = b.getTemplate(); - b.instances = null; - if (a) { - a.destroy() - } - b.callParent() - } -}); -Ext.define("Ext.draw.overrides.sprite.Instancing", { - override: "Ext.draw.sprite.Instancing", - hitTest: function(f, j) { - var e = this, - g = e.getTemplate(), - b = g.attr, - a = e.instances, - d = a.length, - c = 0, - h = null; - if (!e.isVisible()) { - return h - } - for (; c < d; c++) { - g.attr = a[c]; - h = g.hitTest(f, j); - if (h) { - h.isInstance = true; - h.template = h.sprite; - h.sprite = this; - h.instance = a[c]; - h.index = c; - return h - } - } - g.attr = b; - return h - } -}); -Ext.define("Ext.draw.sprite.Line", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.line", - type: "line", - inheritableStatics: { - def: { - processors: { - fromX: "number", - fromY: "number", - toX: "number", - toY: "number" - }, - defaults: { - fromX: 0, - fromY: 0, - toX: 1, - toY: 1, - strokeStyle: "black" - }, - aliases: { - x1: "fromX", - y1: "fromY", - x2: "toX", - y2: "toY" - } - } - }, - updateLineBBox: function(b, i, s, g, r, f) { - var o = this.attr, - q = o.matrix, - h = o.lineWidth / 2, - m, l, d, c, k, j, n; - if (i) { - n = q.transformPoint([s, g]); - s = n[0]; - g = n[1]; - n = q.transformPoint([r, f]); - r = n[0]; - f = n[1] - } - m = Math.min(s, r); - d = Math.max(s, r); - l = Math.min(g, f); - c = Math.max(g, f); - var t = Math.atan2(d - m, c - l), - a = Math.sin(t), - e = Math.cos(t), - k = h * e, - j = h * a; - m -= k; - l -= j; - d += k; - c += j; - b.x = m; - b.y = l; - b.width = d - m; - b.height = c - l - }, - updatePlainBBox: function(b) { - var a = this.attr; - this.updateLineBBox(b, false, a.fromX, a.fromY, a.toX, a.toY) - }, - updateTransformedBBox: function(b, c) { - var a = this.attr; - this.updateLineBBox(b, true, a.fromX, a.fromY, a.toX, a.toY) - }, - render: function(b, c) { - var a = this.attr, - d = this.attr.matrix; - d.toContext(c); - c.beginPath(); - c.moveTo(a.fromX, a.fromY); - c.lineTo(a.toX, a.toY); - c.stroke() - } -}); -Ext.define("Ext.draw.sprite.Plus", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.plus", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size / 1.3, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c / 2, ",", e - c / 2, "l", [0, -c, c, 0, 0, c, c, 0, 0, c, -c, 0, 0, c, -c, 0, 0, -c, -c, 0, 0, -c, "z"])) - } -}); -Ext.define("Ext.draw.sprite.Sector", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.sector", - type: "sector", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - margin: "number" - }, - aliases: { - rho: "endRho" - }, - triggers: { - centerX: "path,bbox", - centerY: "path,bbox", - startAngle: "path,bbox", - endAngle: "path,bbox", - startRho: "path,bbox", - endRho: "path,bbox", - margin: "path,bbox" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: 0, - endAngle: 0, - startRho: 0, - endRho: 150, - margin: 0, - path: "M 0,0" - } - } - }, - getMidAngle: function() { - return this.midAngle || 0 - }, - updatePath: function(j, h) { - var g = Math.min(h.startAngle, h.endAngle), - c = Math.max(h.startAngle, h.endAngle), - b = this.midAngle = (g + c) * 0.5, - d = h.margin, - f = h.centerX, - e = h.centerY, - i = Math.min(h.startRho, h.endRho), - a = Math.max(h.startRho, h.endRho); - if (d) { - f += d * Math.cos(b); - e += d * Math.sin(b) - } - j.moveTo(f + i * Math.cos(g), e + i * Math.sin(g)); - j.lineTo(f + a * Math.cos(g), e + a * Math.sin(g)); - j.arc(f, e, a, g, c, false); - j.lineTo(f + i * Math.cos(c), e + i * Math.sin(c)); - j.arc(f, e, i, c, g, true) - } -}); -Ext.define("Ext.draw.sprite.Square", { - extend: "Ext.draw.sprite.Rect", - alias: "sprite.square", - inheritableStatics: { - def: { - processors: { - size: "number" - }, - defaults: { - size: 4 - }, - triggers: { - size: "size" - }, - updaters: { - size: function(a) { - var c = a.size, - b = a.lineWidth / 2; - this.setAttributes({ - x: a.x - c - b, - y: a.y - c, - height: 2 * c, - width: 2 * c - }) - } - } - } - } -}); -Ext.define("Ext.draw.TextMeasurer", { - singleton: true, - requires: ["Ext.util.TextMetrics"], - measureDiv: null, - measureCache: {}, - precise: Ext.isIE8, - measureDivTpl: { - tag: "div", - style: { - overflow: "hidden", - position: "relative", - "float": "left", - width: 0, - height: 0 - }, - children: { - tag: "div", - style: { - display: "block", - position: "absolute", - x: -100000, - y: -100000, - padding: 0, - margin: 0, - "z-index": -100000, - "white-space": "nowrap" - } - } - }, - actualMeasureText: function(g, b) { - var e = Ext.draw.TextMeasurer, - f = e.measureDiv, - a = 100000, - c; - if (!f) { - var d = Ext.Element.create({ - style: { - overflow: "hidden", - position: "relative", - "float": "left", - width: 0, - height: 0 - } - }); - e.measureDiv = f = Ext.Element.create({ - style: { - position: "absolute", - x: a, - y: a, - "z-index": -a, - "white-space": "nowrap", - display: "block", - padding: 0, - margin: 0 - } - }); - Ext.getBody().appendChild(d); - d.appendChild(f) - } - if (b) { - f.setStyle({ - font: b, - lineHeight: "normal" - }) - } - f.setText("(" + g + ")"); - c = f.getSize(); - f.setText("()"); - c.width -= f.getSize().width; - return c - }, - measureTextSingleLine: function(h, d) { - if (this.precise) { - return this.preciseMeasureTextSingleLine(h, d) - } - h = h.toString(); - var a = this.measureCache, - g = h.split(""), - c = 0, - j = 0, - l, b, e, f, k; - if (!a[d]) { - a[d] = {} - } - a = a[d]; - if (a[h]) { - return a[h] - } - for (e = 0, f = g.length; e < f; e++) { - b = g[e]; - if (!(l = a[b])) { - k = this.actualMeasureText(b, d); - l = a[b] = k - } - c += l.width; - j = Math.max(j, l.height) - } - return a[h] = { - width: c, - height: j - } - }, - preciseMeasureTextSingleLine: function(c, a) { - c = c.toString(); - var b = this.measureDiv || (this.measureDiv = Ext.getBody().createChild(this.measureDivTpl).down("div")); - b.setStyle({ - font: a || "" - }); - return Ext.util.TextMetrics.measure(b, c) - }, - measureText: function(e, b) { - var h = e.split("\n"), - d = h.length, - f = 0, - a = 0, - j, c, g; - if (d === 1) { - return this.measureTextSingleLine(e, b) - } - g = []; - for (c = 0; c < d; c++) { - j = this.measureTextSingleLine(h[c], b); - g.push(j); - f += j.height; - a = Math.max(a, j.width) - } - return { - width: a, - height: f, - sizes: g - } - } -}); -Ext.define("Ext.draw.sprite.Text", function() { - var d = { - "xx-small": true, - "x-small": true, - small: true, - medium: true, - large: true, - "x-large": true, - "xx-large": true - }; - var b = { - normal: true, - bold: true, - bolder: true, - lighter: true, - 100: true, - 200: true, - 300: true, - 400: true, - 500: true, - 600: true, - 700: true, - 800: true, - 900: true - }; - var a = { - start: "start", - left: "start", - center: "center", - middle: "center", - end: "end", - right: "end" - }; - var c = { - top: "top", - hanging: "hanging", - middle: "middle", - center: "middle", - alphabetic: "alphabetic", - ideographic: "ideographic", - bottom: "bottom" - }; - return { - extend: "Ext.draw.sprite.Sprite", - requires: ["Ext.draw.TextMeasurer", "Ext.draw.Color"], - alias: "sprite.text", - type: "text", - lineBreakRe: /\r?\n/g, - inheritableStatics: { - def: { - animationProcessors: { - text: "text" - }, - processors: { - x: "number", - y: "number", - text: "string", - fontSize: function(e) { - if (Ext.isNumber(+e)) { - return e + "px" - } else { - if (e.match(Ext.dom.Element.unitRe)) { - return e - } else { - if (e in d) { - return e - } - } - } - }, - fontStyle: "enums(,italic,oblique)", - fontVariant: "enums(,small-caps)", - fontWeight: function(e) { - if (e in b) { - return String(e) - } else { - return "" - } - }, - fontFamily: "string", - textAlign: function(e) { - return a[e] || "center" - }, - textBaseline: function(e) { - return c[e] || "alphabetic" - }, - font: "string" - }, - aliases: { - "font-size": "fontSize", - "font-family": "fontFamily", - "font-weight": "fontWeight", - "font-variant": "fontVariant", - "text-anchor": "textAlign" - }, - defaults: { - fontStyle: "", - fontVariant: "", - fontWeight: "", - fontSize: "10px", - fontFamily: "sans-serif", - font: "10px sans-serif", - textBaseline: "alphabetic", - textAlign: "start", - strokeStyle: "rgba(0, 0, 0, 0)", - fillStyle: "#000", - x: 0, - y: 0, - text: "" - }, - triggers: { - fontStyle: "fontX,bbox", - fontVariant: "fontX,bbox", - fontWeight: "fontX,bbox", - fontSize: "fontX,bbox", - fontFamily: "fontX,bbox", - font: "font,bbox,canvas", - textBaseline: "bbox", - textAlign: "bbox", - x: "bbox", - y: "bbox", - text: "bbox" - }, - updaters: { - fontX: "makeFontShorthand", - font: "parseFontShorthand" - } - } - }, - constructor: function(e) { - if (e && e.font) { - e = Ext.clone(e); - for (var f in e) { - if (f !== "font" && f.indexOf("font") === 0) { - delete e[f] - } - } - } - Ext.draw.sprite.Sprite.prototype.constructor.call(this, e) - }, - fontValuesMap: { - italic: "fontStyle", - oblique: "fontStyle", - "small-caps": "fontVariant", - bold: "fontWeight", - bolder: "fontWeight", - lighter: "fontWeight", - "100": "fontWeight", - "200": "fontWeight", - "300": "fontWeight", - "400": "fontWeight", - "500": "fontWeight", - "600": "fontWeight", - "700": "fontWeight", - "800": "fontWeight", - "900": "fontWeight", - "xx-small": "fontSize", - "x-small": "fontSize", - small: "fontSize", - medium: "fontSize", - large: "fontSize", - "x-large": "fontSize", - "xx-large": "fontSize" - }, - makeFontShorthand: function(e) { - var f = []; - if (e.fontStyle) { - f.push(e.fontStyle) - } - if (e.fontVariant) { - f.push(e.fontVariant) - } - if (e.fontWeight) { - f.push(e.fontWeight) - } - if (e.fontSize) { - f.push(e.fontSize) - } - if (e.fontFamily) { - f.push(e.fontFamily) - } - this.setAttributes({ - font: f.join(" ") - }, true) - }, - parseFontShorthand: function(j) { - var m = j.font, - k = m.length, - l = {}, - n = this.fontValuesMap, - e = 0, - i, g, f, h; - while (e < k && i !== -1) { - i = m.indexOf(" ", e); - if (i < 0) { - f = m.substr(e) - } else { - if (i > e) { - f = m.substr(e, i - e) - } else { - continue - } - } - g = f.indexOf("/"); - if (g > 0) { - f = f.substr(0, g) - } else { - if (g === 0) { - continue - } - } - if (f !== "normal" && f !== "inherit") { - h = n[f]; - if (h) { - l[h] = f - } else { - if (f.match(Ext.dom.Element.unitRe)) { - l.fontSize = f - } else { - l.fontFamily = m.substr(e); - break - } - } - } - e = i + 1 - } - if (!l.fontStyle) { - l.fontStyle = "" - } - if (!l.fontVariant) { - l.fontVariant = "" - } - if (!l.fontWeight) { - l.fontWeight = "" - } - this.setAttributes(l, true) - }, - fontProperties: { - fontStyle: true, - fontVariant: true, - fontWeight: true, - fontSize: true, - fontFamily: true - }, - setAttributes: function(g, i, e) { - var f, h; - if (g && g.font) { - h = {}; - for (f in g) { - if (!(f in this.fontProperties)) { - h[f] = g[f] - } - } - g = h - } - this.callParent([g, i, e]) - }, - getBBox: function(g) { - var h = this, - f = h.attr.bbox.plain, - e = h.getSurface(); - if (f.dirty) { - h.updatePlainBBox(f); - f.dirty = false - } - if (e.getInherited().rtl && e.getFlipRtlText()) { - h.updatePlainBBox(f, true) - } - return h.callParent([g]) - }, - rtlAlignments: { - start: "end", - center: "center", - end: "start" - }, - updatePlainBBox: function(k, B) { - var C = this, - w = C.attr, - o = w.x, - n = w.y, - q = [], - t = w.font, - r = w.text, - s = w.textBaseline, - l = w.textAlign, - u = (B && C.oldSize) ? C.oldSize : (C.oldSize = Ext.draw.TextMeasurer.measureText(r, t)), - z = C.getSurface(), - p = z.getInherited().rtl, - v = p && z.getFlipRtlText(), - h = z.getRect(), - f = u.sizes, - g = u.height, - j = u.width, - m = f ? f.length : 0, - e, A = 0; - switch (s) { - case "hanging": - case "top": - break; - case "ideographic": - case "bottom": - n -= g; - break; - case "alphabetic": - n -= g * 0.8; - break; - case "middle": - n -= g * 0.5; - break - } - if (v) { - o = h[2] - h[0] - o; - l = C.rtlAlignments[l] - } - switch (l) { - case "start": - if (p) { - for (; A < m; A++) { - e = f[A].width; - q.push(-(j - e)) - } - } - break; - case "end": - o -= j; - if (p) { - break - } - for (; A < m; A++) { - e = f[A].width; - q.push(j - e) - } - break; - case "center": - o -= j * 0.5; - for (; A < m; A++) { - e = f[A].width; - q.push((p ? -1 : 1) * (j - e) * 0.5) - } - break - } - w.textAlignOffsets = q; - k.x = o; - k.y = n; - k.width = j; - k.height = g - }, - setText: function(e) { - this.setAttributes({ - text: e - }, true) - }, - render: function(e, q, k) { - var h = this, - g = h.attr, - p = Ext.draw.Matrix.fly(g.matrix.elements.slice(0)), - o = h.getBBox(true), - s = g.textAlignOffsets, - m = Ext.draw.Color.RGBA_NONE, - l, j, f, r, n; - if (g.text.length === 0) { - return - } - r = g.text.split(h.lineBreakRe); - n = o.height / r.length; - l = g.bbox.plain.x; - j = g.bbox.plain.y + n * 0.78; - p.toContext(q); - if (e.getInherited().rtl) { - l += g.bbox.plain.width - } - for (f = 0; f < r.length; f++) { - if (q.fillStyle !== m) { - q.fillText(r[f], l + (s[f] || 0), j + n * f) - } - if (q.strokeStyle !== m) { - q.strokeText(r[f], l + (s[f] || 0), j + n * f) - } - } - } - } -}); -Ext.define("Ext.draw.sprite.Tick", { - extend: "Ext.draw.sprite.Line", - alias: "sprite.tick", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "tick", - y: "tick", - size: "tick" - }, - updaters: { - tick: function(b) { - var d = b.size * 1.5, - c = b.lineWidth / 2, - a = b.x, - e = b.y; - this.setAttributes({ - fromX: a - c, - fromY: e - d, - toX: a - c, - toY: e + d - }) - } - } - } - } -}); -Ext.define("Ext.draw.sprite.Triangle", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.triangle", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 2.2, - a = b.x, - e = b.y; - d.fromSvgString("M".concat(a, ",", e, "m0-", c * 0.58, "l", c * 0.5, ",", c * 0.87, "-", c, ",0z")) - } -}); -Ext.define("Ext.draw.gradient.Linear", { - extend: "Ext.draw.gradient.Gradient", - requires: ["Ext.draw.Color"], - type: "linear", - config: { - degrees: 0, - radians: 0 - }, - applyRadians: function(b, a) { - if (Ext.isNumber(b)) { - return b - } - return a - }, - applyDegrees: function(b, a) { - if (Ext.isNumber(b)) { - return b - } - return a - }, - updateRadians: function(a) { - this.setDegrees(Ext.draw.Draw.degrees(a)) - }, - updateDegrees: function(a) { - this.setRadians(Ext.draw.Draw.rad(a)) - }, - generateGradient: function(q, o) { - var c = this.getRadians(), - p = Math.cos(c), - j = Math.sin(c), - m = o.width, - f = o.height, - d = o.x + m * 0.5, - b = o.y + f * 0.5, - n = this.getStops(), - g = n.length, - k, a, e; - if (Ext.isNumber(d + b) && f > 0 && m > 0) { - a = (Math.sqrt(f * f + m * m) * Math.abs(Math.cos(c - Math.atan(f / m)))) / 2; - k = q.createLinearGradient(d + p * a, b + j * a, d - p * a, b - j * a); - for (e = 0; e < g; e++) { - k.addColorStop(n[e].offset, n[e].color) - } - return k - } - return Ext.draw.Color.NONE - } -}); -Ext.define("Ext.draw.gradient.Radial", { - extend: "Ext.draw.gradient.Gradient", - type: "radial", - config: { - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - } - }, - applyStart: function(a, b) { - if (!b) { - return a - } - var c = { - x: b.x, - y: b.y, - r: b.r - }; - if ("x" in a) { - c.x = a.x - } else { - if ("centerX" in a) { - c.x = a.centerX - } - } - if ("y" in a) { - c.y = a.y - } else { - if ("centerY" in a) { - c.y = a.centerY - } - } - if ("r" in a) { - c.r = a.r - } else { - if ("radius" in a) { - c.r = a.radius - } - } - return c - }, - applyEnd: function(b, a) { - if (!a) { - return b - } - var c = { - x: a.x, - y: a.y, - r: a.r - }; - if ("x" in b) { - c.x = b.x - } else { - if ("centerX" in b) { - c.x = b.centerX - } - } - if ("y" in b) { - c.y = b.y - } else { - if ("centerY" in b) { - c.y = b.centerY - } - } - if ("r" in b) { - c.r = b.r - } else { - if ("radius" in b) { - c.r = b.radius - } - } - return c - }, - generateGradient: function(n, m) { - var a = this.getStart(), - b = this.getEnd(), - k = m.width * 0.5, - d = m.height * 0.5, - j = m.x + k, - f = m.y + d, - g = n.createRadialGradient(j + a.x * k, f + a.y * d, a.r * Math.max(k, d), j + b.x * k, f + b.y * d, b.r * Math.max(k, d)), - l = this.getStops(), - e = l.length, - c; - for (c = 0; c < e; c++) { - g.addColorStop(l[c].offset, l[c].color) - } - return g - } -}); -Ext.define("Ext.draw.Surface", { - extend: "Ext.draw.SurfaceBase", - xtype: "surface", - requires: ["Ext.draw.sprite.*", "Ext.draw.gradient.*", "Ext.draw.sprite.AttributeDefinition", "Ext.draw.Matrix", "Ext.draw.Draw"], - uses: ["Ext.draw.engine.Canvas"], - devicePixelRatio: window.devicePixelRatio || window.screen.deviceXDPI / window.screen.logicalXDPI, - deprecated: { - "5.1.0": { - statics: { - methods: { - stableSort: function(a) { - return Ext.Array.sort(a, function(d, c) { - return d.attr.zIndex - c.attr.zIndex - }) - } - } - } - } - }, - config: { - cls: Ext.baseCSSPrefix + "surface", - rect: null, - background: null, - items: [], - dirty: false, - flipRtlText: false - }, - isSurface: true, - isPendingRenderFrame: false, - dirtyPredecessorCount: 0, - constructor: function(a) { - var b = this; - b.predecessors = []; - b.successors = []; - b.map = {}; - b.callParent([a]); - b.matrix = new Ext.draw.Matrix(); - b.inverseMatrix = b.matrix.inverse() - }, - roundPixel: function(a) { - return Math.round(this.devicePixelRatio * a) / this.devicePixelRatio - }, - waitFor: function(a) { - var b = this, - c = b.predecessors; - if (!Ext.Array.contains(c, a)) { - c.push(a); - a.successors.push(b); - if (a.getDirty()) { - b.dirtyPredecessorCount++ - } - } - }, - updateDirty: function(d) { - var c = this.successors, - e = c.length, - b = 0, - a; - for (; b < e; b++) { - a = c[b]; - if (d) { - a.dirtyPredecessorCount++; - a.setDirty(true) - } else { - a.dirtyPredecessorCount--; - if (a.dirtyPredecessorCount === 0 && a.isPendingRenderFrame) { - a.renderFrame() - } - } - } - }, - applyBackground: function(a, b) { - this.setDirty(true); - if (Ext.isString(a)) { - a = { - fillStyle: a - } - } - return Ext.factory(a, Ext.draw.sprite.Rect, b) - }, - applyRect: function(a, b) { - if (b && a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]) { - return - } - if (Ext.isArray(a)) { - return [a[0], a[1], a[2], a[3]] - } else { - if (Ext.isObject(a)) { - return [a.x || a.left, a.y || a.top, a.width || (a.right - a.left), a.height || (a.bottom - a.top)] - } - } - }, - updateRect: function(i) { - var h = this, - c = i[0], - f = i[1], - g = c + i[2], - a = f + i[3], - e = h.getBackground(), - d = h.element; - d.setLocalXY(Math.floor(c), Math.floor(f)); - d.setSize(Math.ceil(g - Math.floor(c)), Math.ceil(a - Math.floor(f))); - if (e) { - e.setAttributes({ - x: 0, - y: 0, - width: Math.ceil(g - Math.floor(c)), - height: Math.ceil(a - Math.floor(f)) - }) - } - h.setDirty(true) - }, - resetTransform: function() { - this.matrix.set(1, 0, 0, 1, 0, 0); - this.inverseMatrix.set(1, 0, 0, 1, 0, 0); - this.setDirty(true) - }, - get: function(a) { - return this.map[a] || this.getItems()[a] - }, - add: function() { - var g = this, - e = Array.prototype.slice.call(arguments), - j = Ext.isArray(e[0]), - a = g.map, - c = [], - f, k, h, b, d; - f = Ext.Array.clean(j ? e[0] : e); - if (!f.length) { - return c - } - for (b = 0, d = f.length; b < d; b++) { - k = f[b]; - h = null; - if (k.isSprite && !a[k.getId()]) { - h = k - } else { - if (!a[k.id]) { - h = this.createItem(k) - } - } - if (h) { - a[h.getId()] = h; - c.push(h); - h.setParent(g); - h.setSurface(g); - g.onAdd(h) - } - } - f = g.getItems(); - if (f) { - f.push.apply(f, c) - } - g.dirtyZIndex = true; - g.setDirty(true); - if (!j && c.length === 1) { - return c[0] - } else { - return c - } - }, - onAdd: Ext.emptyFn, - remove: function(a, c) { - var b = this, - e, d; - if (a) { - if (a.charAt) { - a = b.map[a] - } - if (!a || !a.isSprite) { - return null - } - if (a.isDestroyed || a.isDestroying) { - return a - } - e = a.getId(); - d = b.map[e]; - delete b.map[e]; - if (c) { - a.destroy() - } - if (!d) { - return a - } - a.setParent(null); - a.setSurface(null); - Ext.Array.remove(b.getItems(), a); - b.dirtyZIndex = true; - b.setDirty(true) - } - return a || null - }, - removeAll: function(d) { - var a = this.getItems(), - b = a.length - 1, - c; - if (d) { - for (; b >= 0; b--) { - a[b].destroy() - } - } else { - for (; b >= 0; b--) { - c = a[b]; - c.setParent(null); - c.setSurface(null) - } - } - a.length = 0; - this.map = {}; - this.dirtyZIndex = true - }, - applyItems: function(a) { - if (this.getItems()) { - this.removeAll(true) - } - return Ext.Array.from(this.add(a)) - }, - createItem: function(a) { - return Ext.create(a.xclass || "sprite." + a.type, a) - }, - getBBox: function(f, b) { - var f = Ext.Array.from(f), - c = Infinity, - h = -Infinity, - g = Infinity, - a = -Infinity, - j, k, d, e; - for (d = 0, e = f.length; d < e; d++) { - j = f[d]; - k = j.getBBox(b); - if (c > k.x) { - c = k.x - } - if (h < k.x + k.width) { - h = k.x + k.width - } - if (g > k.y) { - g = k.y - } - if (a < k.y + k.height) { - a = k.y + k.height - } - } - return { - x: c, - y: g, - width: h - c, - height: a - g - } - }, - emptyRect: [0, 0, 0, 0], - getEventXY: function(d) { - var g = this, - f = g.getInherited().rtl, - c = d.getXY(), - a = g.getOwnerBody(), - i = a.getXY(), - h = g.getRect() || g.emptyRect, - j = [], - b; - if (f) { - b = a.getWidth(); - j[0] = i[0] - c[0] - h[0] + b - } else { - j[0] = c[0] - i[0] - h[0] - } - j[1] = c[1] - i[1] - h[1]; - return j - }, - clear: Ext.emptyFn, - orderByZIndex: function() { - var d = this, - a = d.getItems(), - e = false, - b, c; - if (d.getDirty()) { - for (b = 0, c = a.length; b < c; b++) { - if (a[b].attr.dirtyZIndex) { - e = true; - break - } - } - if (e) { - Ext.Array.sort(a, function(g, f) { - return g.attr.zIndex - f.attr.zIndex - }); - this.setDirty(true) - } - for (b = 0, c = a.length; b < c; b++) { - a[b].attr.dirtyZIndex = false - } - } - }, - repaint: function() { - var a = this; - a.repaint = Ext.emptyFn; - Ext.defer(function() { - delete a.repaint; - a.element.repaint() - }, 1) - }, - renderFrame: function() { - var g = this; - if (!g.element) { - return - } - if (g.dirtyPredecessorCount > 0) { - g.isPendingRenderFrame = true; - return - } - var f = g.getRect(), - c = g.getBackground(), - a = g.getItems(), - e, b, d; - if (!f) { - return - } - g.orderByZIndex(); - if (g.getDirty()) { - g.clear(); - g.clearTransform(); - if (c) { - g.renderSprite(c) - } - for (b = 0, d = a.length; b < d; b++) { - e = a[b]; - if (g.renderSprite(e) === false) { - return - } - e.attr.textPositionCount = g.textPosition - } - g.setDirty(false) - } - }, - renderSprite: Ext.emptyFn, - clearTransform: Ext.emptyFn, - destroy: function() { - var a = this; - a.removeAll(true); - a.predecessors = null; - a.successors = null; - a.callParent() - } -}); -Ext.define("Ext.draw.overrides.Surface", { - override: "Ext.draw.Surface", - hitTest: function(b, c) { - var f = this, - g = f.getItems(), - e, d, a; - c = c || Ext.draw.sprite.Sprite.defaultHitTestOptions; - for (e = g.length - 1; e >= 0; e--) { - d = g[e]; - if (d.hitTest) { - a = d.hitTest(b, c); - if (a) { - return a - } - } - } - return null - }, - hitTestEvent: function(b, a) { - var c = this.getEventXY(b); - return this.hitTest(c, a) - } -}); -Ext.define("Ext.draw.engine.SvgContext", { - requires: ["Ext.draw.Color"], - toSave: ["strokeOpacity", "strokeStyle", "fillOpacity", "fillStyle", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "lineDash", "lineDashOffset", "miterLimit", "shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor", "globalCompositeOperation", "position", "fillGradient", "strokeGradient"], - strokeOpacity: 1, - strokeStyle: "none", - fillOpacity: 1, - fillStyle: "none", - lineDash: [], - lineDashOffset: 0, - globalAlpha: 1, - lineWidth: 1, - lineCap: "butt", - lineJoin: "miter", - miterLimit: 10, - shadowOffsetX: 0, - shadowOffsetY: 0, - shadowBlur: 0, - shadowColor: "none", - globalCompositeOperation: "src", - urlStringRe: /^url\(#([\w\-]+)\)$/, - constructor: function(a) { - this.surface = a; - this.state = []; - this.matrix = new Ext.draw.Matrix(); - this.path = null; - this.clear() - }, - clear: function() { - this.group = this.surface.mainGroup; - this.position = 0; - this.path = null - }, - getElement: function(a) { - return this.surface.getSvgElement(this.group, a, this.position++) - }, - removeElement: function(d) { - var d = Ext.fly(d), - h, g, b, f, a, e, c; - if (!d) { - return - } - if (d.dom.tagName === "g") { - a = d.dom.gradients; - for (c in a) { - a[c].destroy() - } - } else { - h = d.getAttribute("fill"); - g = d.getAttribute("stroke"); - b = h && h.match(this.urlStringRe); - f = g && g.match(this.urlStringRe); - if (b && b[1]) { - e = Ext.fly(b[1]); - if (e) { - e.destroy() - } - } - if (f && f[1]) { - e = Ext.fly(f[1]); - if (e) { - e.destroy() - } - } - } - d.destroy() - }, - save: function() { - var c = this.toSave, - e = {}, - d = this.getElement("g"), - b, a; - for (a = 0; a < c.length; a++) { - b = c[a]; - if (b in this) { - e[b] = this[b] - } - } - this.position = 0; - e.matrix = this.matrix.clone(); - this.state.push(e); - this.group = d; - return d - }, - restore: function() { - var d = this.toSave, - e = this.state.pop(), - c = this.group.dom.childNodes, - b, a; - while (c.length > this.position) { - this.removeElement(c[c.length - 1]) - } - for (a = 0; a < d.length; a++) { - b = d[a]; - if (b in e) { - this[b] = e[b] - } else { - delete this[b] - } - } - this.setTransform.apply(this, e.matrix.elements); - this.group = this.group.getParent() - }, - transform: function(f, b, e, g, d, c) { - if (this.path) { - var a = Ext.draw.Matrix.fly([f, b, e, g, d, c]).inverse(); - this.path.transform(a) - } - this.matrix.append(f, b, e, g, d, c) - }, - setTransform: function(e, a, d, f, c, b) { - if (this.path) { - this.path.transform(this.matrix) - } - this.matrix.reset(); - this.transform(e, a, d, f, c, b) - }, - scale: function(a, b) { - this.transform(a, 0, 0, b, 0, 0) - }, - rotate: function(d) { - var c = Math.cos(d), - a = Math.sin(d), - b = -Math.sin(d), - e = Math.cos(d); - this.transform(c, a, b, e, 0, 0) - }, - translate: function(a, b) { - this.transform(1, 0, 0, 1, a, b) - }, - setGradientBBox: function(a) { - this.bbox = a - }, - beginPath: function() { - this.path = new Ext.draw.Path() - }, - moveTo: function(a, b) { - if (!this.path) { - this.beginPath() - } - this.path.moveTo(a, b); - this.path.element = null - }, - lineTo: function(a, b) { - if (!this.path) { - this.beginPath() - } - this.path.lineTo(a, b); - this.path.element = null - }, - rect: function(b, d, c, a) { - this.moveTo(b, d); - this.lineTo(b + c, d); - this.lineTo(b + c, d + a); - this.lineTo(b, d + a); - this.closePath() - }, - strokeRect: function(b, d, c, a) { - this.beginPath(); - this.rect(b, d, c, a); - this.stroke() - }, - fillRect: function(b, d, c, a) { - this.beginPath(); - this.rect(b, d, c, a); - this.fill() - }, - closePath: function() { - if (!this.path) { - this.beginPath() - } - this.path.closePath(); - this.path.element = null - }, - arcSvg: function(d, a, f, g, c, b, e) { - if (!this.path) { - this.beginPath() - } - this.path.arcSvg(d, a, f, g, c, b, e); - this.path.element = null - }, - arc: function(b, f, a, d, c, e) { - if (!this.path) { - this.beginPath() - } - this.path.arc(b, f, a, d, c, e); - this.path.element = null - }, - ellipse: function(a, h, g, f, d, c, b, e) { - if (!this.path) { - this.beginPath() - } - this.path.ellipse(a, h, g, f, d, c, b, e); - this.path.element = null - }, - arcTo: function(b, e, a, d, g, f, c) { - if (!this.path) { - this.beginPath() - } - this.path.arcTo(b, e, a, d, g, f, c); - this.path.element = null - }, - bezierCurveTo: function(d, f, b, e, a, c) { - if (!this.path) { - this.beginPath() - } - this.path.bezierCurveTo(d, f, b, e, a, c); - this.path.element = null - }, - strokeText: function(d, a, e) { - d = String(d); - if (this.strokeStyle) { - var b = this.getElement("text"), - c = this.surface.getSvgElement(b, "tspan", 0); - this.surface.setElementAttributes(b, { - x: a, - y: e, - transform: this.matrix.toSvg(), - stroke: this.strokeStyle, - fill: "none", - opacity: this.globalAlpha, - "stroke-opacity": this.strokeOpacity, - style: "font: " + this.font, - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }); - if (this.lineDash.length) { - this.surface.setElementAttributes(b, { - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }) - } - if (c.dom.firstChild) { - c.dom.removeChild(c.dom.firstChild) - } - this.surface.setElementAttributes(c, { - "alignment-baseline": "alphabetic" - }); - c.dom.appendChild(document.createTextNode(Ext.String.htmlDecode(d))) - } - }, - fillText: function(d, a, e) { - d = String(d); - if (this.fillStyle) { - var b = this.getElement("text"), - c = this.surface.getSvgElement(b, "tspan", 0); - this.surface.setElementAttributes(b, { - x: a, - y: e, - transform: this.matrix.toSvg(), - fill: this.fillStyle, - opacity: this.globalAlpha, - "fill-opacity": this.fillOpacity, - style: "font: " + this.font - }); - if (c.dom.firstChild) { - c.dom.removeChild(c.dom.firstChild) - } - this.surface.setElementAttributes(c, { - "alignment-baseline": "alphabetic" - }); - c.dom.appendChild(document.createTextNode(Ext.String.htmlDecode(d))) - } - }, - drawImage: function(c, k, i, l, e, p, n, a, g) { - var f = this, - d = f.getElement("image"), - j = k, - h = i, - b = typeof l === "undefined" ? c.width : l, - m = typeof e === "undefined" ? c.height : e, - o = null; - if (typeof g !== "undefined") { - o = k + " " + i + " " + l + " " + e; - j = p; - h = n; - b = a; - m = g - } - d.dom.setAttributeNS("http://www.w3.org/1999/xlink", "href", c.src); - f.surface.setElementAttributes(d, { - viewBox: o, - x: j, - y: h, - width: b, - height: m, - opacity: f.globalAlpha, - transform: f.matrix.toSvg() - }) - }, - fill: function() { - if (!this.path) { - return - } - if (this.fillStyle) { - var c, a = this.fillGradient, - d = this.bbox, - b = this.path.element; - if (!b) { - c = this.path.toString(); - b = this.path.element = this.getElement("path"); - this.surface.setElementAttributes(b, { - d: c, - transform: this.matrix.toSvg() - }) - } - this.surface.setElementAttributes(b, { - fill: a && d ? a.generateGradient(this, d) : this.fillStyle, - "fill-opacity": this.fillOpacity * this.globalAlpha - }) - } - }, - stroke: function() { - if (!this.path) { - return - } - if (this.strokeStyle) { - var c, b = this.strokeGradient, - d = this.bbox, - a = this.path.element; - if (!a || !this.path.svgString) { - c = this.path.toString(); - if (!c) { - return - } - a = this.path.element = this.getElement("path"); - this.surface.setElementAttributes(a, { - fill: "none", - d: c, - transform: this.matrix.toSvg() - }) - } - this.surface.setElementAttributes(a, { - stroke: b && d ? b.generateGradient(this, d) : this.strokeStyle, - "stroke-linecap": this.lineCap, - "stroke-linejoin": this.lineJoin, - "stroke-width": this.lineWidth, - "stroke-opacity": this.strokeOpacity * this.globalAlpha, - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }); - if (this.lineDash.length) { - this.surface.setElementAttributes(a, { - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }) - } - } - }, - fillStroke: function(a, e) { - var b = this, - d = b.fillStyle, - g = b.strokeStyle, - c = b.fillOpacity, - f = b.strokeOpacity; - if (e === undefined) { - e = a.transformFillStroke - } - if (!e) { - a.inverseMatrix.toContext(b) - } - if (d && c !== 0) { - b.fill() - } - if (g && f !== 0) { - b.stroke() - } - }, - appendPath: function(a) { - this.path = a.clone() - }, - setLineDash: function(a) { - this.lineDash = a - }, - getLineDash: function() { - return this.lineDash - }, - createLinearGradient: function(d, g, b, e) { - var f = this, - c = f.surface.getNextDef("linearGradient"), - a = f.group.dom.gradients || (f.group.dom.gradients = {}), - h; - f.surface.setElementAttributes(c, { - x1: d, - y1: g, - x2: b, - y2: e, - gradientUnits: "userSpaceOnUse" - }); - h = new Ext.draw.engine.SvgContext.Gradient(f, f.surface, c); - a[c.dom.id] = h; - return h - }, - createRadialGradient: function(b, j, d, a, i, c) { - var g = this, - e = g.surface.getNextDef("radialGradient"), - f = g.group.dom.gradients || (g.group.dom.gradients = {}), - h; - g.surface.setElementAttributes(e, { - fx: b, - fy: j, - cx: a, - cy: i, - r: c, - gradientUnits: "userSpaceOnUse" - }); - h = new Ext.draw.engine.SvgContext.Gradient(g, g.surface, e, d / c); - f[e.dom.id] = h; - return h - } -}); -Ext.define("Ext.draw.engine.SvgContext.Gradient", { - statics: { - map: {} - }, - constructor: function(c, a, d, b) { - var f = this.statics().map, - e; - e = f[d.dom.id]; - if (e) { - e.element = null - } - f[d.dom.id] = this; - this.ctx = c; - this.surface = a; - this.element = d; - this.position = 0; - this.compression = b || 0 - }, - addColorStop: function(d, b) { - var c = this.surface.getSvgElement(this.element, "stop", this.position++), - a = this.compression; - this.surface.setElementAttributes(c, { - offset: (((1 - a) * d + a) * 100).toFixed(2) + "%", - "stop-color": b, - "stop-opacity": Ext.draw.Color.fly(b).a.toFixed(15) - }) - }, - toString: function() { - var a = this.element.dom.childNodes; - while (a.length > this.position) { - Ext.fly(a[a.length - 1]).destroy() - } - return "url(#" + this.element.getId() + ")" - }, - destroy: function() { - var b = this.statics().map, - a = this.element; - if (a && a.dom) { - delete b[a.dom.id]; - a.destroy() - } - this.callParent() - } -}); -Ext.define("Ext.draw.engine.Svg", { - extend: "Ext.draw.Surface", - requires: ["Ext.draw.engine.SvgContext"], - statics: { - BBoxTextCache: {} - }, - config: { - highPrecision: false - }, - getElementConfig: function() { - return { - reference: "element", - style: { - position: "absolute" - }, - children: [{ - reference: "innerElement", - style: { - width: "100%", - height: "100%", - position: "relative" - }, - children: [{ - tag: "svg", - reference: "svgElement", - namespace: "http://www.w3.org/2000/svg", - width: "100%", - height: "100%", - version: 1.1 - }] - }] - } - }, - constructor: function(a) { - var b = this; - b.callParent([a]); - b.mainGroup = b.createSvgNode("g"); - b.defElement = b.createSvgNode("defs"); - b.svgElement.appendChild(b.mainGroup); - b.svgElement.appendChild(b.defElement); - b.ctx = new Ext.draw.engine.SvgContext(b) - }, - createSvgNode: function(a) { - var b = document.createElementNS("http://www.w3.org/2000/svg", a); - return Ext.get(b) - }, - getSvgElement: function(d, b, a) { - var c; - if (d.dom.childNodes.length > a) { - c = d.dom.childNodes[a]; - if (c.tagName === b) { - return Ext.get(c) - } else { - Ext.destroy(c) - } - } - c = Ext.get(this.createSvgNode(b)); - if (a === 0) { - d.insertFirst(c) - } else { - c.insertAfter(Ext.fly(d.dom.childNodes[a - 1])) - } - c.cache = {}; - return c - }, - setElementAttributes: function(d, b) { - var f = d.dom, - a = d.cache, - c, e; - for (c in b) { - e = b[c]; - if (a[c] !== e) { - a[c] = e; - f.setAttribute(c, e) - } - } - }, - getNextDef: function(a) { - return this.getSvgElement(this.defElement, a, this.defPosition++) - }, - clearTransform: function() { - var a = this; - a.mainGroup.set({ - transform: a.matrix.toSvg() - }) - }, - clear: function() { - this.ctx.clear(); - this.defPosition = 0 - }, - renderSprite: function(b) { - var d = this, - c = d.getRect(), - a = d.ctx; - if (b.attr.hidden || b.attr.globalAlpha === 0) { - a.save(); - a.restore(); - return - } - b.element = a.save(); - b.preRender(this); - b.useAttributes(a, c); - if (false === b.render(this, a, [0, 0, c[2], c[3]])) { - return false - } - b.setDirty(false); - a.restore() - }, - flatten: function(e, b) { - var c = '', - f = Ext.getClassName(this), - a, g, d; - c += ''; - for (d = 0; d < b.length; d++) { - a = b[d]; - if (Ext.getClassName(a) !== f) { - continue - } - g = a.getRect(); - c += ''; - c += this.serializeNode(a.svgElement.dom); - c += "" - } - c += ""; - return { - data: "data:image/svg+xml;utf8," + encodeURIComponent(c), - type: "svg" - } - }, - serializeNode: function(d) { - var b = "", - c, f, a, e; - if (d.nodeType === document.TEXT_NODE) { - return d.nodeValue - } - b += "<" + d.nodeName; - if (d.attributes.length) { - for (c = 0, f = d.attributes.length; c < f; c++) { - a = d.attributes[c]; - b += " " + a.name + '="' + a.value + '"' - } - } - b += ">"; - if (d.childNodes && d.childNodes.length) { - for (c = 0, f = d.childNodes.length; c < f; c++) { - e = d.childNodes[c]; - b += this.serializeNode(e) - } - } - b += ""; - return b - }, - destroy: function() { - var a = this; - a.ctx.destroy(); - a.mainGroup.destroy(); - delete a.mainGroup; - delete a.ctx; - a.callParent() - }, - remove: function(a, b) { - if (a && a.element) { - if (this.ctx) { - this.ctx.removeElement(a.element) - } else { - a.element.destroy() - } - a.element = null - } - this.callParent(arguments) - } -}); -Ext.draw || (Ext.draw = {}); -Ext.draw.engine || (Ext.draw.engine = {}); -Ext.draw.engine.excanvas = true; -if (!document.createElement("canvas").getContext) { - (function() { - var ab = Math; - var n = ab.round; - var l = ab.sin; - var A = ab.cos; - var H = ab.abs; - var N = ab.sqrt; - var d = 10; - var f = d / 2; - var z = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1]; - - function y() { - return this.context_ || (this.context_ = new D(this)) - } - var t = Array.prototype.slice; - - function g(j, m, p) { - var i = t.call(arguments, 2); - return function() { - return j.apply(m, i.concat(t.call(arguments))) - } - } - - function af(i) { - return String(i).replace(/&/g, "&").replace(/"/g, """) - } - - function Y(m, j, i) { - Ext.onReady(function() { - if (!m.namespaces[j]) { - m.namespaces.add(j, i, "#default#VML") - } - }) - } - - function R(j) { - Y(j, "g_vml_", "urn:schemas-microsoft-com:vml"); - Y(j, "g_o_", "urn:schemas-microsoft-com:office:office"); - if (!j.styleSheets.ex_canvas_) { - var i = j.createStyleSheet(); - i.owningElement.id = "ex_canvas_"; - i.cssText = "canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}" - } - } - R(document); - var e = { - init: function(i) { - var j = i || document; - j.createElement("canvas"); - j.attachEvent("onreadystatechange", g(this.init_, this, j)) - }, - init_: function(p) { - var m = p.getElementsByTagName("canvas"); - for (var j = 0; j < m.length; j++) { - this.initElement(m[j]) - } - }, - initElement: function(j) { - if (!j.getContext) { - j.getContext = y; - R(j.ownerDocument); - j.innerHTML = ""; - j.attachEvent("onpropertychange", x); - j.attachEvent("onresize", W); - var i = j.attributes; - if (i.width && i.width.specified) { - j.style.width = i.width.nodeValue + "px" - } else { - j.width = j.clientWidth - } - if (i.height && i.height.specified) { - j.style.height = i.height.nodeValue + "px" - } else { - j.height = j.clientHeight - } - } - return j - } - }; - - function x(j) { - var i = j.srcElement; - switch (j.propertyName) { - case "width": - i.getContext().clearRect(); - i.style.width = i.attributes.width.nodeValue + "px"; - i.firstChild.style.width = i.clientWidth + "px"; - break; - case "height": - i.getContext().clearRect(); - i.style.height = i.attributes.height.nodeValue + "px"; - i.firstChild.style.height = i.clientHeight + "px"; - break - } - } - - function W(j) { - var i = j.srcElement; - if (i.firstChild) { - i.firstChild.style.width = i.clientWidth + "px"; - i.firstChild.style.height = i.clientHeight + "px" - } - } - e.init(); - var k = []; - for (var ae = 0; ae < 16; ae++) { - for (var ad = 0; ad < 16; ad++) { - k[ae * 16 + ad] = ae.toString(16) + ad.toString(16) - } - } - - function B() { - return [ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1] - ] - } - - function J(p, m) { - var j = B(); - for (var i = 0; i < 3; i++) { - for (var ah = 0; ah < 3; ah++) { - var Z = 0; - for (var ag = 0; ag < 3; ag++) { - Z += p[i][ag] * m[ag][ah] - } - j[i][ah] = Z - } - } - return j - } - - function v(j, i) { - i.fillStyle = j.fillStyle; - i.lineCap = j.lineCap; - i.lineJoin = j.lineJoin; - i.lineDash = j.lineDash; - i.lineWidth = j.lineWidth; - i.miterLimit = j.miterLimit; - i.shadowBlur = j.shadowBlur; - i.shadowColor = j.shadowColor; - i.shadowOffsetX = j.shadowOffsetX; - i.shadowOffsetY = j.shadowOffsetY; - i.strokeStyle = j.strokeStyle; - i.globalAlpha = j.globalAlpha; - i.font = j.font; - i.textAlign = j.textAlign; - i.textBaseline = j.textBaseline; - i.arcScaleX_ = j.arcScaleX_; - i.arcScaleY_ = j.arcScaleY_; - i.lineScale_ = j.lineScale_ - } - var b = { - aliceblue: "#F0F8FF", - antiquewhite: "#FAEBD7", - aquamarine: "#7FFFD4", - azure: "#F0FFFF", - beige: "#F5F5DC", - bisque: "#FFE4C4", - black: "#000000", - blanchedalmond: "#FFEBCD", - blueviolet: "#8A2BE2", - brown: "#A52A2A", - burlywood: "#DEB887", - cadetblue: "#5F9EA0", - chartreuse: "#7FFF00", - chocolate: "#D2691E", - coral: "#FF7F50", - cornflowerblue: "#6495ED", - cornsilk: "#FFF8DC", - crimson: "#DC143C", - cyan: "#00FFFF", - darkblue: "#00008B", - darkcyan: "#008B8B", - darkgoldenrod: "#B8860B", - darkgray: "#A9A9A9", - darkgreen: "#006400", - darkgrey: "#A9A9A9", - darkkhaki: "#BDB76B", - darkmagenta: "#8B008B", - darkolivegreen: "#556B2F", - darkorange: "#FF8C00", - darkorchid: "#9932CC", - darkred: "#8B0000", - darksalmon: "#E9967A", - darkseagreen: "#8FBC8F", - darkslateblue: "#483D8B", - darkslategray: "#2F4F4F", - darkslategrey: "#2F4F4F", - darkturquoise: "#00CED1", - darkviolet: "#9400D3", - deeppink: "#FF1493", - deepskyblue: "#00BFFF", - dimgray: "#696969", - dimgrey: "#696969", - dodgerblue: "#1E90FF", - firebrick: "#B22222", - floralwhite: "#FFFAF0", - forestgreen: "#228B22", - gainsboro: "#DCDCDC", - ghostwhite: "#F8F8FF", - gold: "#FFD700", - goldenrod: "#DAA520", - grey: "#808080", - greenyellow: "#ADFF2F", - honeydew: "#F0FFF0", - hotpink: "#FF69B4", - indianred: "#CD5C5C", - indigo: "#4B0082", - ivory: "#FFFFF0", - khaki: "#F0E68C", - lavender: "#E6E6FA", - lavenderblush: "#FFF0F5", - lawngreen: "#7CFC00", - lemonchiffon: "#FFFACD", - lightblue: "#ADD8E6", - lightcoral: "#F08080", - lightcyan: "#E0FFFF", - lightgoldenrodyellow: "#FAFAD2", - lightgreen: "#90EE90", - lightgrey: "#D3D3D3", - lightpink: "#FFB6C1", - lightsalmon: "#FFA07A", - lightseagreen: "#20B2AA", - lightskyblue: "#87CEFA", - lightslategray: "#778899", - lightslategrey: "#778899", - lightsteelblue: "#B0C4DE", - lightyellow: "#FFFFE0", - limegreen: "#32CD32", - linen: "#FAF0E6", - magenta: "#FF00FF", - mediumaquamarine: "#66CDAA", - mediumblue: "#0000CD", - mediumorchid: "#BA55D3", - mediumpurple: "#9370DB", - mediumseagreen: "#3CB371", - mediumslateblue: "#7B68EE", - mediumspringgreen: "#00FA9A", - mediumturquoise: "#48D1CC", - mediumvioletred: "#C71585", - midnightblue: "#191970", - mintcream: "#F5FFFA", - mistyrose: "#FFE4E1", - moccasin: "#FFE4B5", - navajowhite: "#FFDEAD", - oldlace: "#FDF5E6", - olivedrab: "#6B8E23", - orange: "#FFA500", - orangered: "#FF4500", - orchid: "#DA70D6", - palegoldenrod: "#EEE8AA", - palegreen: "#98FB98", - paleturquoise: "#AFEEEE", - palevioletred: "#DB7093", - papayawhip: "#FFEFD5", - peachpuff: "#FFDAB9", - peru: "#CD853F", - pink: "#FFC0CB", - plum: "#DDA0DD", - powderblue: "#B0E0E6", - rosybrown: "#BC8F8F", - royalblue: "#4169E1", - saddlebrown: "#8B4513", - salmon: "#FA8072", - sandybrown: "#F4A460", - seagreen: "#2E8B57", - seashell: "#FFF5EE", - sienna: "#A0522D", - skyblue: "#87CEEB", - slateblue: "#6A5ACD", - slategray: "#708090", - slategrey: "#708090", - snow: "#FFFAFA", - springgreen: "#00FF7F", - steelblue: "#4682B4", - tan: "#D2B48C", - thistle: "#D8BFD8", - tomato: "#FF6347", - turquoise: "#40E0D0", - violet: "#EE82EE", - wheat: "#F5DEB3", - whitesmoke: "#F5F5F5", - yellowgreen: "#9ACD32" - }; - - function M(j) { - var p = j.indexOf("(", 3); - var i = j.indexOf(")", p + 1); - var m = j.substring(p + 1, i).split(","); - if (m.length != 4 || j.charAt(3) != "a") { - m[3] = 1 - } - return m - } - - function c(i) { - return parseFloat(i) / 100 - } - - function r(j, m, i) { - return Math.min(i, Math.max(m, j)) - } - - function I(ag) { - var i, ai, aj, ah, ak, Z; - ah = parseFloat(ag[0]) / 360 % 360; - if (ah < 0) { - ah++ - } - ak = r(c(ag[1]), 0, 1); - Z = r(c(ag[2]), 0, 1); - if (ak == 0) { - i = ai = aj = Z - } else { - var j = Z < 0.5 ? Z * (1 + ak) : Z + ak - Z * ak; - var m = 2 * Z - j; - i = a(m, j, ah + 1 / 3); - ai = a(m, j, ah); - aj = a(m, j, ah - 1 / 3) - } - return "#" + k[Math.floor(i * 255)] + k[Math.floor(ai * 255)] + k[Math.floor(aj * 255)] - } - - function a(j, i, m) { - if (m < 0) { - m++ - } - if (m > 1) { - m-- - } - if (6 * m < 1) { - return j + (i - j) * 6 * m - } else { - if (2 * m < 1) { - return i - } else { - if (3 * m < 2) { - return j + (i - j) * (2 / 3 - m) * 6 - } else { - return j - } - } - } - } - var C = {}; - - function F(j) { - if (j in C) { - return C[j] - } - var ag, Z = 1; - j = String(j); - if (j.charAt(0) == "#") { - ag = j - } else { - if (/^rgb/.test(j)) { - var p = M(j); - var ag = "#", - ah; - for (var m = 0; m < 3; m++) { - if (p[m].indexOf("%") != -1) { - ah = Math.floor(c(p[m]) * 255) - } else { - ah = +p[m] - } - ag += k[r(ah, 0, 255)] - } - Z = +p[3] - } else { - if (/^hsl/.test(j)) { - var p = M(j); - ag = I(p); - Z = p[3] - } else { - ag = b[j] || j - } - } - } - return C[j] = { - color: ag, - alpha: Z - } - } - var o = { - style: "normal", - variant: "normal", - weight: "normal", - size: 10, - family: "sans-serif" - }; - var L = {}; - - function E(i) { - if (L[i]) { - return L[i] - } - var p = document.createElement("div"); - var m = p.style; - try { - m.font = i - } catch (j) {} - return L[i] = { - style: m.fontStyle || o.style, - variant: m.fontVariant || o.variant, - weight: m.fontWeight || o.weight, - size: m.fontSize || o.size, - family: m.fontFamily || o.family - } - } - - function u(m, j) { - var i = {}; - for (var ah in m) { - i[ah] = m[ah] - } - var ag = parseFloat(j.currentStyle.fontSize), - Z = parseFloat(m.size); - if (typeof m.size == "number") { - i.size = m.size - } else { - if (m.size.indexOf("px") != -1) { - i.size = Z - } else { - if (m.size.indexOf("em") != -1) { - i.size = ag * Z - } else { - if (m.size.indexOf("%") != -1) { - i.size = (ag / 100) * Z - } else { - if (m.size.indexOf("pt") != -1) { - i.size = Z / 0.75 - } else { - i.size = ag - } - } - } - } - } - i.size *= 0.981; - return i - } - - function ac(i) { - return i.style + " " + i.variant + " " + i.weight + " " + i.size + "px " + i.family - } - var s = { - butt: "flat", - round: "round" - }; - - function S(i) { - return s[i] || "square" - } - - function D(i) { - this.m_ = B(); - this.mStack_ = []; - this.aStack_ = []; - this.currentPath_ = []; - this.strokeStyle = "#000"; - this.fillStyle = "#000"; - this.lineWidth = 1; - this.lineJoin = "miter"; - this.lineDash = []; - this.lineCap = "butt"; - this.miterLimit = d * 1; - this.globalAlpha = 1; - this.font = "10px sans-serif"; - this.textAlign = "left"; - this.textBaseline = "alphabetic"; - this.canvas = i; - var m = "width:" + i.clientWidth + "px;height:" + i.clientHeight + "px;overflow:hidden;position:absolute"; - var j = i.ownerDocument.createElement("div"); - j.style.cssText = m; - i.appendChild(j); - var p = j.cloneNode(false); - p.style.backgroundColor = "red"; - p.style.filter = "alpha(opacity=0)"; - i.appendChild(p); - this.element_ = j; - this.arcScaleX_ = 1; - this.arcScaleY_ = 1; - this.lineScale_ = 1 - } - var q = D.prototype; - q.clearRect = function() { - if (this.textMeasureEl_) { - this.textMeasureEl_.removeNode(true); - this.textMeasureEl_ = null - } - this.element_.innerHTML = "" - }; - q.beginPath = function() { - this.currentPath_ = [] - }; - q.moveTo = function(j, i) { - var m = V(this, j, i); - this.currentPath_.push({ - type: "moveTo", - x: m.x, - y: m.y - }); - this.currentX_ = m.x; - this.currentY_ = m.y - }; - q.lineTo = function(j, i) { - var m = V(this, j, i); - this.currentPath_.push({ - type: "lineTo", - x: m.x, - y: m.y - }); - this.currentX_ = m.x; - this.currentY_ = m.y - }; - q.bezierCurveTo = function(m, j, ak, aj, ai, ag) { - var i = V(this, ai, ag); - var ah = V(this, m, j); - var Z = V(this, ak, aj); - K(this, ah, Z, i) - }; - - function K(i, Z, m, j) { - i.currentPath_.push({ - type: "bezierCurveTo", - cp1x: Z.x, - cp1y: Z.y, - cp2x: m.x, - cp2y: m.y, - x: j.x, - y: j.y - }); - i.currentX_ = j.x; - i.currentY_ = j.y - } - q.quadraticCurveTo = function(ai, m, j, i) { - var ah = V(this, ai, m); - var ag = V(this, j, i); - var aj = { - x: this.currentX_ + 2 / 3 * (ah.x - this.currentX_), - y: this.currentY_ + 2 / 3 * (ah.y - this.currentY_) - }; - var Z = { - x: aj.x + (ag.x - this.currentX_) / 3, - y: aj.y + (ag.y - this.currentY_) / 3 - }; - K(this, aj, Z, ag) - }; - q.arc = function(al, aj, ak, ag, j, m) { - ak *= d; - var ap = m ? "at" : "wa"; - var am = al + A(ag) * ak - f; - var ao = aj + l(ag) * ak - f; - var i = al + A(j) * ak - f; - var an = aj + l(j) * ak - f; - if (am == i && !m) { - am += 0.125 - } - var Z = V(this, al, aj); - var ai = V(this, am, ao); - var ah = V(this, i, an); - this.currentPath_.push({ - type: ap, - x: Z.x, - y: Z.y, - radius: ak, - xStart: ai.x, - yStart: ai.y, - xEnd: ah.x, - yEnd: ah.y - }) - }; - q.rect = function(m, j, i, p) { - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath() - }; - q.strokeRect = function(m, j, i, p) { - var Z = this.currentPath_; - this.beginPath(); - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath(); - this.stroke(); - this.currentPath_ = Z - }; - q.fillRect = function(m, j, i, p) { - var Z = this.currentPath_; - this.beginPath(); - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath(); - this.fill(); - this.currentPath_ = Z - }; - q.createLinearGradient = function(j, p, i, m) { - var Z = new U("gradient"); - Z.x0_ = j; - Z.y0_ = p; - Z.x1_ = i; - Z.y1_ = m; - return Z - }; - q.createRadialGradient = function(p, ag, m, j, Z, i) { - var ah = new U("gradientradial"); - ah.x0_ = p; - ah.y0_ = ag; - ah.r0_ = m; - ah.x1_ = j; - ah.y1_ = Z; - ah.r1_ = i; - return ah - }; - q.drawImage = function(an, j) { - var ah, Z, aj, ar, al, ak, ao, av; - var ai = an.runtimeStyle.width; - var am = an.runtimeStyle.height; - an.runtimeStyle.width = "auto"; - an.runtimeStyle.height = "auto"; - var ag = an.width; - var aq = an.height; - an.runtimeStyle.width = ai; - an.runtimeStyle.height = am; - if (arguments.length == 3) { - ah = arguments[1]; - Z = arguments[2]; - al = ak = 0; - ao = aj = ag; - av = ar = aq - } else { - if (arguments.length == 5) { - ah = arguments[1]; - Z = arguments[2]; - aj = arguments[3]; - ar = arguments[4]; - al = ak = 0; - ao = ag; - av = aq - } else { - if (arguments.length == 9) { - al = arguments[1]; - ak = arguments[2]; - ao = arguments[3]; - av = arguments[4]; - ah = arguments[5]; - Z = arguments[6]; - aj = arguments[7]; - ar = arguments[8] - } else { - throw Error("Invalid number of arguments") - } - } - } - var au = V(this, ah, Z); - var at = []; - var i = 10; - var p = 10; - var ap = this.m_; - at.push(" ', '", ""); - this.element_.insertAdjacentHTML("BeforeEnd", at.join("")) - }; - q.setLineDash = function(i) { - if (i.length === 1) { - i = i.slice(); - i[1] = i[0] - } - this.lineDash = i - }; - q.getLineDash = function() { - return this.lineDash - }; - q.stroke = function(ak) { - var ai = []; - var m = 10; - var al = 10; - ai.push(" aj.x) { - aj.x = j.x - } - if (Z.y == null || j.y < Z.y) { - Z.y = j.y - } - if (aj.y == null || j.y > aj.y) { - aj.y = j.y - } - } - } - ai.push(' ">'); - if (!ak) { - w(this, ai) - } else { - G(this, ai, Z, aj) - } - ai.push(""); - this.element_.insertAdjacentHTML("beforeEnd", ai.join("")) - }; - - function w(m, ag) { - var j = F(m.strokeStyle); - var p = j.color; - var Z = j.alpha * m.globalAlpha; - var i = m.lineScale_ * m.lineWidth; - if (i < 1) { - Z *= i - } - ag.push("') - } - - function G(aq, ai, aK, ar) { - var aj = aq.fillStyle; - var aB = aq.arcScaleX_; - var aA = aq.arcScaleY_; - var j = ar.x - aK.x; - var p = ar.y - aK.y; - if (aj instanceof U) { - var an = 0; - var aF = { - x: 0, - y: 0 - }; - var ax = 0; - var am = 1; - if (aj.type_ == "gradient") { - var al = aj.x0_ / aB; - var m = aj.y0_ / aA; - var ak = aj.x1_ / aB; - var aM = aj.y1_ / aA; - var aJ = V(aq, al, m); - var aI = V(aq, ak, aM); - var ag = aI.x - aJ.x; - var Z = aI.y - aJ.y; - an = Math.atan2(ag, Z) * 180 / Math.PI; - if (an < 0) { - an += 360 - } - if (an < 0.000001) { - an = 0 - } - } else { - var aJ = V(aq, aj.x0_, aj.y0_); - aF = { - x: (aJ.x - aK.x) / j, - y: (aJ.y - aK.y) / p - }; - j /= aB * d; - p /= aA * d; - var aD = ab.max(j, p); - ax = 2 * aj.r0_ / aD; - am = 2 * aj.r1_ / aD - ax - } - var av = aj.colors_; - av.sort(function(aN, i) { - return aN.offset - i.offset - }); - var ap = av.length; - var au = av[0].color; - var at = av[ap - 1].color; - var az = av[0].alpha * aq.globalAlpha; - var ay = av[ap - 1].alpha * aq.globalAlpha; - var aE = []; - for (var aH = 0; aH < ap; aH++) { - var ao = av[aH]; - aE.push(ao.offset * am + ax + " " + ao.color) - } - ai.push('') - } else { - if (aj instanceof T) { - if (j && p) { - var ah = -aK.x; - var aC = -aK.y; - ai.push("') - } - } else { - var aL = F(aq.fillStyle); - var aw = aL.color; - var aG = aL.alpha * aq.globalAlpha; - ai.push('') - } - } - } - q.fill = function() { - this.$stroke(true) - }; - q.closePath = function() { - this.currentPath_.push({ - type: "close" - }) - }; - - function V(j, Z, p) { - var i = j.m_; - return { - x: d * (Z * i[0][0] + p * i[1][0] + i[2][0]) - f, - y: d * (Z * i[0][1] + p * i[1][1] + i[2][1]) - f - } - } - q.save = function() { - var i = {}; - v(this, i); - this.aStack_.push(i); - this.mStack_.push(this.m_); - this.m_ = J(B(), this.m_) - }; - q.restore = function() { - if (this.aStack_.length) { - v(this.aStack_.pop(), this); - this.m_ = this.mStack_.pop() - } - }; - - function h(i) { - return isFinite(i[0][0]) && isFinite(i[0][1]) && isFinite(i[1][0]) && isFinite(i[1][1]) && isFinite(i[2][0]) && isFinite(i[2][1]) - } - - function aa(j, i, p) { - if (!h(i)) { - return - } - j.m_ = i; - if (p) { - var Z = i[0][0] * i[1][1] - i[0][1] * i[1][0]; - j.lineScale_ = N(H(Z)) - } - } - q.translate = function(m, j) { - var i = [ - [1, 0, 0], - [0, 1, 0], - [m, j, 1] - ]; - aa(this, J(i, this.m_), false) - }; - q.rotate = function(j) { - var p = A(j); - var m = l(j); - var i = [ - [p, m, 0], - [-m, p, 0], - [0, 0, 1] - ]; - aa(this, J(i, this.m_), false) - }; - q.scale = function(m, j) { - this.arcScaleX_ *= m; - this.arcScaleY_ *= j; - var i = [ - [m, 0, 0], - [0, j, 0], - [0, 0, 1] - ]; - aa(this, J(i, this.m_), true) - }; - q.transform = function(Z, p, ah, ag, j, i) { - var m = [ - [Z, p, 0], - [ah, ag, 0], - [j, i, 1] - ]; - aa(this, J(m, this.m_), true) - }; - q.setTransform = function(ag, Z, ai, ah, p, j) { - var i = [ - [ag, Z, 0], - [ai, ah, 0], - [p, j, 1] - ]; - aa(this, i, true) - }; - q.drawText_ = function(am, ak, aj, ap, ai) { - var ao = this.m_, - at = 1000, - j = 0, - ar = at, - ah = { - x: 0, - y: 0 - }, - ag = []; - var i = u(E(this.font), this.element_); - var p = ac(i); - var au = this.element_.currentStyle; - var Z = this.textAlign.toLowerCase(); - switch (Z) { - case "left": - case "center": - case "right": - break; - case "end": - Z = au.direction == "ltr" ? "right" : "left"; - break; - case "start": - Z = au.direction == "rtl" ? "right" : "left"; - break; - default: - Z = "left" - } - switch (this.textBaseline) { - case "hanging": - case "top": - ah.y = i.size / 1.75; - break; - case "middle": - break; - default: - case null: - case "alphabetic": - case "ideographic": - case "bottom": - ah.y = -i.size / 3; - break - } - switch (Z) { - case "right": - j = at; - ar = 0.05; - break; - case "center": - j = ar = at / 2; - break - } - var aq = V(this, ak + ah.x, aj + ah.y); - ag.push(''); - if (ai) { - w(this, ag) - } else { - G(this, ag, { - x: -j, - y: 0 - }, { - x: ar, - y: i.size - }) - } - var an = ao[0][0].toFixed(3) + "," + ao[1][0].toFixed(3) + "," + ao[0][1].toFixed(3) + "," + ao[1][1].toFixed(3) + ",0,0"; - var al = n(aq.x / d) + "," + n(aq.y / d); - ag.push('', '', ''); - this.element_.insertAdjacentHTML("beforeEnd", ag.join("")) - }; - q.fillText = function(m, i, p, j) { - this.drawText_(m, i, p, j, false) - }; - q.strokeText = function(m, i, p, j) { - this.drawText_(m, i, p, j, true) - }; - q.measureText = function(m) { - if (!this.textMeasureEl_) { - var i = ''; - this.element_.insertAdjacentHTML("beforeEnd", i); - this.textMeasureEl_ = this.element_.lastChild - } - var j = this.element_.ownerDocument; - this.textMeasureEl_.innerHTML = ""; - this.textMeasureEl_.style.font = this.font; - this.textMeasureEl_.appendChild(j.createTextNode(m)); - return { - width: this.textMeasureEl_.offsetWidth - } - }; - q.clip = function() {}; - q.arcTo = function() {}; - q.createPattern = function(j, i) { - return new T(j, i) - }; - - function U(i) { - this.type_ = i; - this.x0_ = 0; - this.y0_ = 0; - this.r0_ = 0; - this.x1_ = 0; - this.y1_ = 0; - this.r1_ = 0; - this.colors_ = [] - } - U.prototype.addColorStop = function(j, i) { - i = F(i); - this.colors_.push({ - offset: j, - color: i.color, - alpha: i.alpha - }) - }; - - function T(j, i) { - Q(j); - switch (i) { - case "repeat": - case null: - case "": - this.repetition_ = "repeat"; - break; - case "repeat-x": - case "repeat-y": - case "no-repeat": - this.repetition_ = i; - break; - default: - O("SYNTAX_ERR") - } - this.src_ = j.src; - this.width_ = j.width; - this.height_ = j.height - } - - function O(i) { - throw new P(i) - } - - function Q(i) { - if (!i || i.nodeType != 1 || i.tagName != "IMG") { - O("TYPE_MISMATCH_ERR") - } - if (i.readyState != "complete") { - O("INVALID_STATE_ERR") - } - } - - function P(i) { - this.code = this[i]; - this.message = i + ": DOM Exception " + this.code - } - var X = P.prototype = new Error(); - X.INDEX_SIZE_ERR = 1; - X.DOMSTRING_SIZE_ERR = 2; - X.HIERARCHY_REQUEST_ERR = 3; - X.WRONG_DOCUMENT_ERR = 4; - X.INVALID_CHARACTER_ERR = 5; - X.NO_DATA_ALLOWED_ERR = 6; - X.NO_MODIFICATION_ALLOWED_ERR = 7; - X.NOT_FOUND_ERR = 8; - X.NOT_SUPPORTED_ERR = 9; - X.INUSE_ATTRIBUTE_ERR = 10; - X.INVALID_STATE_ERR = 11; - X.SYNTAX_ERR = 12; - X.INVALID_MODIFICATION_ERR = 13; - X.NAMESPACE_ERR = 14; - X.INVALID_ACCESS_ERR = 15; - X.VALIDATION_ERR = 16; - X.TYPE_MISMATCH_ERR = 17; - G_vmlCanvasManager = e; - CanvasRenderingContext2D = D; - CanvasGradient = U; - CanvasPattern = T; - DOMException = P - })() -} -Ext.define("Ext.draw.engine.Canvas", { - extend: "Ext.draw.Surface", - requires: ["Ext.draw.engine.excanvas", "Ext.draw.Animator", "Ext.draw.Color"], - config: { - highPrecision: false - }, - statics: { - contextOverrides: { - setGradientBBox: function(a) { - this.bbox = a - }, - fill: function() { - var c = this.fillStyle, - a = this.fillGradient, - b = this.fillOpacity, - d = this.globalAlpha, - e = this.bbox; - if (c !== Ext.draw.Color.RGBA_NONE && b !== 0) { - if (a && e) { - this.fillStyle = a.generateGradient(this, e) - } - if (b !== 1) { - this.globalAlpha = d * b - } - this.$fill(); - if (b !== 1) { - this.globalAlpha = d - } - if (a && e) { - this.fillStyle = c - } - } - }, - stroke: function() { - var e = this.strokeStyle, - c = this.strokeGradient, - a = this.strokeOpacity, - b = this.globalAlpha, - d = this.bbox; - if (e !== Ext.draw.Color.RGBA_NONE && a !== 0) { - if (c && d) { - this.strokeStyle = c.generateGradient(this, d) - } - if (a !== 1) { - this.globalAlpha = b * a - } - this.$stroke(); - if (a !== 1) { - this.globalAlpha = b - } - if (c && d) { - this.strokeStyle = e - } - } - }, - fillStroke: function(d, e) { - var j = this, - i = this.fillStyle, - h = this.fillOpacity, - f = this.strokeStyle, - c = this.strokeOpacity, - b = j.shadowColor, - a = j.shadowBlur, - g = Ext.draw.Color.RGBA_NONE; - if (e === undefined) { - e = d.transformFillStroke - } - if (!e) { - d.inverseMatrix.toContext(j) - } - if (i !== g && h !== 0) { - j.fill(); - j.shadowColor = g; - j.shadowBlur = 0 - } - if (f !== g && c !== 0) { - j.stroke() - } - j.shadowColor = b; - j.shadowBlur = a - }, - setLineDash: function(a) { - if (this.$setLineDash) { - this.$setLineDash(a) - } - }, - getLineDash: function() { - if (this.$getLineDash) { - return this.$getLineDash() - } - }, - ellipse: function(g, e, c, a, j, b, f, d) { - var i = Math.cos(j), - h = Math.sin(j); - this.transform(i * c, h * c, -h * a, i * a, g, e); - this.arc(0, 0, 1, b, f, d); - this.transform(i / c, -h / a, h / c, i / a, -(i * g + h * e) / c, (h * g - i * e) / a) - }, - appendPath: function(f) { - var e = this, - c = 0, - b = 0, - a = f.commands, - g = f.params, - d = a.length; - e.beginPath(); - for (; c < d; c++) { - switch (a[c]) { - case "M": - e.moveTo(g[b], g[b + 1]); - b += 2; - break; - case "L": - e.lineTo(g[b], g[b + 1]); - b += 2; - break; - case "C": - e.bezierCurveTo(g[b], g[b + 1], g[b + 2], g[b + 3], g[b + 4], g[b + 5]); - b += 6; - break; - case "Z": - e.closePath(); - break - } - } - }, - save: function() { - var c = this.toSave, - d = c.length, - e = d && {}, - b = 0, - a; - for (; b < d; b++) { - a = c[b]; - if (a in this) { - e[a] = this[a] - } - } - this.state.push(e); - this.$save() - }, - restore: function() { - var b = this.state.pop(), - a; - if (b) { - for (a in b) { - this[a] = b[a] - } - } - this.$restore() - } - } - }, - splitThreshold: 3000, - toSave: ["fillGradient", "strokeGradient"], - element: { - reference: "element", - style: { - position: "absolute" - }, - children: [{ - reference: "innerElement", - style: { - width: "100%", - height: "100%", - position: "relative" - } - }] - }, - createCanvas: function() { - var c = Ext.Element.create({ - tag: "canvas", - cls: Ext.baseCSSPrefix + "surface-canvas" - }); - window.G_vmlCanvasManager && G_vmlCanvasManager.initElement(c.dom); - var d = Ext.draw.engine.Canvas.contextOverrides, - a = c.dom.getContext("2d"), - b; - if (a.ellipse) { - delete d.ellipse - } - a.state = []; - a.toSave = this.toSave; - for (b in d) { - a["$" + b] = a[b] - } - Ext.apply(a, d); - if (this.getHighPrecision()) { - this.enablePrecisionCompensation(a) - } else { - this.disablePrecisionCompensation(a) - } - this.innerElement.appendChild(c); - this.canvases.push(c); - this.contexts.push(a) - }, - updateHighPrecision: function(d) { - var e = this.contexts, - c = e.length, - b, a; - for (b = 0; b < c; b++) { - a = e[b]; - if (d) { - this.enablePrecisionCompensation(a) - } else { - this.disablePrecisionCompensation(a) - } - } - }, - precisionNames: ["rect", "fillRect", "strokeRect", "clearRect", "moveTo", "lineTo", "arc", "arcTo", "save", "restore", "updatePrecisionCompensate", "setTransform", "transform", "scale", "translate", "rotate", "quadraticCurveTo", "bezierCurveTo", "createLinearGradient", "createRadialGradient", "fillText", "strokeText", "drawImage"], - disablePrecisionCompensation: function(b) { - var a = Ext.draw.engine.Canvas.contextOverrides, - f = this.precisionNames, - e = f.length, - d, c; - for (d = 0; d < e; d++) { - c = f[d]; - if (!(c in a)) { - delete b[c] - } - } - this.setDirty(true) - }, - enablePrecisionCompensation: function(j) { - var c = this, - a = 1, - g = 1, - l = 0, - k = 0, - i = new Ext.draw.Matrix(), - b = [], - e = {}, - d = Ext.draw.engine.Canvas.contextOverrides, - h = j.constructor.prototype; - var f = { - toSave: c.toSave, - rect: function(m, p, n, o) { - return h.rect.call(this, m * a + l, p * g + k, n * a, o * g) - }, - fillRect: function(m, p, n, o) { - this.updatePrecisionCompensateRect(); - h.fillRect.call(this, m * a + l, p * g + k, n * a, o * g); - this.updatePrecisionCompensate() - }, - strokeRect: function(m, p, n, o) { - this.updatePrecisionCompensateRect(); - h.strokeRect.call(this, m * a + l, p * g + k, n * a, o * g); - this.updatePrecisionCompensate() - }, - clearRect: function(m, p, n, o) { - return h.clearRect.call(this, m * a + l, p * g + k, n * a, o * g) - }, - moveTo: function(m, n) { - return h.moveTo.call(this, m * a + l, n * g + k) - }, - lineTo: function(m, n) { - return h.lineTo.call(this, m * a + l, n * g + k) - }, - arc: function(n, r, m, p, o, q) { - this.updatePrecisionCompensateRect(); - h.arc.call(this, n * a + l, r * a + k, m * a, p, o, q); - this.updatePrecisionCompensate() - }, - arcTo: function(o, q, n, p, m) { - this.updatePrecisionCompensateRect(); - h.arcTo.call(this, o * a + l, q * g + k, n * a + l, p * g + k, m * a); - this.updatePrecisionCompensate() - }, - save: function() { - b.push(i); - i = i.clone(); - d.save.call(this); - h.save.call(this) - }, - restore: function() { - i = b.pop(); - d.restore.call(this); - h.restore.call(this); - this.updatePrecisionCompensate() - }, - updatePrecisionCompensate: function() { - i.precisionCompensate(c.devicePixelRatio, e); - a = e.xx; - g = e.yy; - l = e.dx; - k = e.dy; - h.setTransform.call(this, c.devicePixelRatio, e.b, e.c, e.d, 0, 0) - }, - updatePrecisionCompensateRect: function() { - i.precisionCompensateRect(c.devicePixelRatio, e); - a = e.xx; - g = e.yy; - l = e.dx; - k = e.dy; - h.setTransform.call(this, c.devicePixelRatio, e.b, e.c, e.d, 0, 0) - }, - setTransform: function(q, o, n, m, r, p) { - i.set(q, o, n, m, r, p); - this.updatePrecisionCompensate() - }, - transform: function(q, o, n, m, r, p) { - i.append(q, o, n, m, r, p); - this.updatePrecisionCompensate() - }, - scale: function(n, m) { - this.transform(n, 0, 0, m, 0, 0) - }, - translate: function(n, m) { - this.transform(1, 0, 0, 1, n, m) - }, - rotate: function(o) { - var n = Math.cos(o), - m = Math.sin(o); - this.transform(n, m, -m, n, 0, 0) - }, - quadraticCurveTo: function(n, p, m, o) { - h.quadraticCurveTo.call(this, n * a + l, p * g + k, m * a + l, o * g + k) - }, - bezierCurveTo: function(r, p, o, n, m, q) { - h.bezierCurveTo.call(this, r * a + l, p * g + k, o * a + l, n * g + k, m * a + l, q * g + k) - }, - createLinearGradient: function(n, p, m, o) { - this.updatePrecisionCompensateRect(); - var q = h.createLinearGradient.call(this, n * a + l, p * g + k, m * a + l, o * g + k); - this.updatePrecisionCompensate(); - return q - }, - createRadialGradient: function(p, r, o, n, q, m) { - this.updatePrecisionCompensateRect(); - var s = h.createLinearGradient.call(this, p * a + l, r * a + k, o * a, n * a + l, q * a + k, m * a); - this.updatePrecisionCompensate(); - return s - }, - fillText: function(o, m, p, n) { - h.setTransform.apply(this, i.elements); - if (typeof n === "undefined") { - h.fillText.call(this, o, m, p) - } else { - h.fillText.call(this, o, m, p, n) - } - this.updatePrecisionCompensate() - }, - strokeText: function(o, m, p, n) { - h.setTransform.apply(this, i.elements); - if (typeof n === "undefined") { - h.strokeText.call(this, o, m, p) - } else { - h.strokeText.call(this, o, m, p, n) - } - this.updatePrecisionCompensate() - }, - fill: function() { - var m = this.fillGradient, - n = this.bbox; - this.updatePrecisionCompensateRect(); - if (m && n) { - this.fillStyle = m.generateGradient(this, n) - } - h.fill.call(this); - this.updatePrecisionCompensate() - }, - stroke: function() { - var m = this.strokeGradient, - n = this.bbox; - this.updatePrecisionCompensateRect(); - if (m && n) { - this.strokeStyle = m.generateGradient(this, n) - } - h.stroke.call(this); - this.updatePrecisionCompensate() - }, - drawImage: function(u, s, r, q, p, o, n, m, t) { - switch (arguments.length) { - case 3: - return h.drawImage.call(this, u, s * a + l, r * g + k); - case 5: - return h.drawImage.call(this, u, s * a + l, r * g + k, q * a, p * g); - case 9: - return h.drawImage.call(this, u, s, r, q, p, o * a + l, n * g * k, m * a, t * g) - } - } - }; - Ext.apply(j, f); - this.setDirty(true) - }, - updateRect: function(a) { - this.callParent([a]); - var C = this, - p = Math.floor(a[0]), - e = Math.floor(a[1]), - g = Math.ceil(a[0] + a[2]), - B = Math.ceil(a[1] + a[3]), - u = C.devicePixelRatio, - D = C.canvases, - d = g - p, - y = B - e, - n = Math.round(C.splitThreshold / u), - c = C.xSplits = Math.ceil(d / n), - f = C.ySplits = Math.ceil(y / n), - v, s, q, A, z, x, o, m; - for (s = 0, z = 0; s < f; s++, z += n) { - for (v = 0, A = 0; v < c; v++, A += n) { - q = s * c + v; - if (q >= D.length) { - C.createCanvas() - } - x = D[q].dom; - x.style.left = A + "px"; - x.style.top = z + "px"; - m = Math.min(n, y - z); - if (m * u !== x.height) { - x.height = m * u; - x.style.height = m + "px" - } - o = Math.min(n, d - A); - if (o * u !== x.width) { - x.width = o * u; - x.style.width = o + "px" - } - C.applyDefaults(C.contexts[q]) - } - } - for (q += 1; q < D.length; q++) { - D[q].destroy() - } - C.activeCanvases = c * f; - D.length = C.activeCanvases; - C.clear() - }, - clearTransform: function() { - var f = this, - a = f.xSplits, - g = f.ySplits, - d = f.contexts, - h = f.splitThreshold, - l = f.devicePixelRatio, - e, c, b, m; - for (e = 0; e < a; e++) { - for (c = 0; c < g; c++) { - b = c * a + e; - m = d[b]; - m.translate(-h * e, -h * c); - m.scale(l, l); - f.matrix.toContext(m) - } - } - }, - renderSprite: function(q) { - var C = this, - b = C.getRect(), - e = C.matrix, - g = q.getParent(), - v = Ext.draw.Matrix.fly([1, 0, 0, 1, 0, 0]), - p = C.splitThreshold / C.devicePixelRatio, - c = C.xSplits, - m = C.ySplits, - A, z, s, a, r, o, d = 0, - B, n = 0, - f, l = b[2], - y = b[3], - x, u, t; - while (g && (g !== C)) { - v.prependMatrix(g.matrix || g.attr && g.attr.matrix); - g = g.getParent() - } - v.prependMatrix(e); - a = q.getBBox(); - if (a) { - a = v.transformBBox(a) - } - q.preRender(C); - if (q.attr.hidden || q.attr.globalAlpha === 0) { - q.setDirty(false); - return - } - for (u = 0, z = 0; u < m; u++, z += p) { - for (x = 0, A = 0; x < c; x++, A += p) { - t = u * c + x; - s = C.contexts[t]; - r = Math.min(p, l - A); - o = Math.min(p, y - z); - d = A; - B = d + r; - n = z; - f = n + o; - if (a) { - if (a.x > B || a.x + a.width < d || a.y > f || a.y + a.height < n) { - continue - } - } - s.save(); - q.useAttributes(s, b); - if (false === q.render(C, s, [d, n, r, o], b)) { - return false - } - s.restore() - } - } - q.setDirty(false) - }, - flatten: function(n, a) { - var k = document.createElement("canvas"), - f = Ext.getClassName(this), - g = this.devicePixelRatio, - l = k.getContext("2d"), - b, c, h, e, d, m; - k.width = Math.ceil(n.width * g); - k.height = Math.ceil(n.height * g); - for (e = 0; e < a.length; e++) { - b = a[e]; - if (Ext.getClassName(b) !== f) { - continue - } - h = b.getRect(); - for (d = 0; d < b.canvases.length; d++) { - c = b.canvases[d]; - m = c.getOffsetsTo(c.getParent()); - l.drawImage(c.dom, (h[0] + m[0]) * g, (h[1] + m[1]) * g) - } - } - return { - data: k.toDataURL(), - type: "png" - } - }, - applyDefaults: function(a) { - var b = Ext.draw.Color.RGBA_NONE; - a.strokeStyle = b; - a.fillStyle = b; - a.textAlign = "start"; - a.textBaseline = "alphabetic"; - a.miterLimit = 1 - }, - clear: function() { - var d = this, - e = d.activeCanvases, - c, b, a; - for (c = 0; c < e; c++) { - b = d.canvases[c].dom; - a = d.contexts[c]; - a.setTransform(1, 0, 0, 1, 0, 0); - a.clearRect(0, 0, b.width, b.height) - } - d.setDirty(true) - }, - destroy: function() { - var c = this, - a, b = c.canvases.length; - for (a = 0; a < b; a++) { - c.contexts[a] = null; - c.canvases[a].destroy(); - c.canvases[a] = null - } - delete c.contexts; - delete c.canvases; - c.callParent() - }, - privates: { - initElement: function() { - var a = this; - a.callParent(); - a.canvases = []; - a.contexts = []; - a.activeCanvases = (a.xSplits = 0) * (a.ySplits = 0) - } - } -}, function() { - var c = this, - b = c.prototype, - a = 10000000000; - if (Ext.os.is.Android4 && Ext.browser.is.Chrome) { - a = 3000 - } else { - if (Ext.is.iOS) { - a = 2200 - } - } - b.splitThreshold = a -}); -Ext.define("Ext.draw.Container", { - extend: "Ext.draw.ContainerBase", - alternateClassName: "Ext.draw.Component", - xtype: "draw", - defaultType: "surface", - isDrawContainer: true, - requires: ["Ext.draw.Surface", "Ext.draw.engine.Svg", "Ext.draw.engine.Canvas", "Ext.draw.gradient.GradientDefinition"], - engine: "Ext.draw.engine.Canvas", - config: { - cls: Ext.baseCSSPrefix + "draw-container", - resizeHandler: null, - sprites: null, - gradients: [] - }, - defaultDownloadServerUrl: "http://svg.sencha.io", - supportedFormats: ["png", "pdf", "jpeg", "gif"], - supportedOptions: { - version: Ext.isNumber, - data: Ext.isString, - format: function(a) { - return Ext.Array.indexOf(this.supportedFormats, a) >= 0 - }, - filename: Ext.isString, - width: Ext.isNumber, - height: Ext.isNumber, - scale: Ext.isNumber, - pdf: Ext.isObject, - jpeg: Ext.isObject - }, - initAnimator: function() { - this.frameCallbackId = Ext.draw.Animator.addFrameCallback("renderFrame", this) - }, - applyGradients: function(b) { - var a = [], - c, f, d, e; - if (!Ext.isArray(b)) { - return a - } - for (c = 0, f = b.length; c < f; c++) { - d = b[c]; - if (!Ext.isObject(d)) { - continue - } - if (typeof d.type !== "string") { - d.type = "linear" - } - if (d.angle) { - d.degrees = d.angle; - delete d.angle - } - if (Ext.isObject(d.stops)) { - d.stops = (function(i) { - var g = [], - h; - for (e in i) { - h = i[e]; - h.offset = e / 100; - g.push(h) - } - return g - })(d.stops) - } - a.push(d) - } - Ext.draw.gradient.GradientDefinition.add(a); - return a - }, - applySprites: function(f) { - if (!f) { - return - } - f = Ext.Array.from(f); - var e = f.length, - b = [], - d, a, c; - for (d = 0; d < e; d++) { - c = f[d]; - a = c.surface; - if (!(a && a.isSurface)) { - if (Ext.isString(a)) { - a = this.getSurface(a) - } else { - a = this.getSurface("main") - } - } - c = a.add(c); - b.push(c) - } - return b - }, - onBodyResize: function() { - var b = this.element, - a; - if (!b) { - return - } - a = b.getSize(); - if (a.width && a.height) { - this.setBodySize(a) - } - }, - setBodySize: function(c) { - var d = this, - b = d.getResizeHandler() || d.defaultResizeHandler, - a; - d.fireEvent("bodyresize", d, c); - a = b.call(d, c); - if (a !== false) { - d.renderFrame() - } - }, - defaultResizeHandler: function(a) { - this.getItems().each(function(b) { - b.setRect([0, 0, a.width, a.height]) - }) - }, - getSurface: function(d) { - d = this.getId() + "-" + (d || "main"); - var c = this, - b = c.getItems(), - a = b.get(d); - if (!a) { - a = c.add({ - xclass: c.engine, - id: d - }); - c.onBodyResize() - } - return a - }, - renderFrame: function() { - var e = this, - a = e.getItems(), - b, d, c; - for (b = 0, d = a.length; b < d; b++) { - c = a.items[b]; - if (c.isSurface) { - c.renderFrame() - } - } - }, - getImage: function(k) { - var l = this.innerElement.getSize(), - a = Array.prototype.slice.call(this.items.items), - d, g, c = this.surfaceZIndexes, - f, e, b, h; - for (e = 1; e < a.length; e++) { - b = a[e]; - h = c[b.type]; - f = e - 1; - while (f >= 0 && c[a[f].type] > h) { - a[f + 1] = a[f]; - f-- - } - a[f + 1] = b - } - d = a[0].flatten(l, a); - if (k === "image") { - g = new Image(); - g.src = d.data; - d.data = g; - return d - } - if (k === "stream") { - d.data = d.data.replace(/^data:image\/[^;]+/, "data:application/octet-stream"); - return d - } - return d - }, - download: function(d) { - var e = this, - a = [], - b, c, f; - d = Ext.apply({ - version: 2, - data: e.getImage().data - }, d); - for (c in d) { - if (d.hasOwnProperty(c)) { - f = d[c]; - if (c in e.supportedOptions) { - if (e.supportedOptions[c].call(e, f)) { - a.push({ - tag: "input", - type: "hidden", - name: c, - value: Ext.String.htmlEncode(Ext.isObject(f) ? Ext.JSON.encode(f) : f) - }) - } - } - } - } - b = Ext.dom.Helper.markup({ - tag: "html", - children: [{ - tag: "head" - }, { - tag: "body", - children: [{ - tag: "form", - method: "POST", - action: d.url || e.defaultDownloadServerUrl, - children: a - }, { - tag: "script", - type: "text/javascript", - children: 'document.getElementsByTagName("form")[0].submit();' - }] - }] - }); - window.open("", "ImageDownload_" + Date.now()).document.write(b) - }, - destroy: function() { - var a = this.frameCallbackId; - if (a) { - Ext.draw.Animator.removeFrameCallback(a) - } - this.callParent() - } -}, function() { - if (location.search.match("svg")) { - Ext.draw.Container.prototype.engine = "Ext.draw.engine.Svg" - } else { - if ((Ext.os.is.BlackBerry && Ext.os.version.getMajor() === 10) || (Ext.browser.is.AndroidStock4 && (Ext.os.version.getMinor() === 1 || Ext.os.version.getMinor() === 2 || Ext.os.version.getMinor() === 3))) { - Ext.draw.Container.prototype.engine = "Ext.draw.engine.Svg" - } - } -}); -Ext.define("Ext.chart.theme.Base", { - mixins: { - factoryable: "Ext.mixin.Factoryable" - }, - requires: ["Ext.draw.Color"], - factoryConfig: { - type: "chart.theme" - }, - isTheme: true, - config: { - baseColor: null, - colors: undefined, - gradients: null, - chart: { - defaults: { - background: "#23272a" - } - }, - axis: { - defaults: { - label: { - x: 0, - y: 0, - textBaseline: "middle", - textAlign: "center", - fontSize: "default", - fontFamily: "default", - fontWeight: "default", - fillStyle: "black", - color: "white" - }, - title: { - fillStyle: "black", - fontSize: "default*1.23", - fontFamily: "default", - fontWeight: "default", - color: "white" - }, - style: { - strokeStyle: "black" - }, - grid: { - strokeStyle: "rgba(44, 47, 51, 1)" - } - }, - top: { - style: { - textPadding: 5 - } - }, - bottom: { - style: { - textPadding: 5 - } - } - }, - series: { - defaults: { - label: { - fillStyle: "black", - strokeStyle: "none", - fontFamily: "default", - fontWeight: "default", - fontSize: "default*1.077", - textBaseline: "middle", - textAlign: "center" - }, - labelOverflowPadding: 5 - } - }, - sprites: { - text: { - fontSize: "default", - fontWeight: "default", - fontFamily: "default", - fillStyle: "black", - color: "white" - } - }, - seriesThemes: undefined, - markerThemes: { - type: ["circle", "cross", "plus", "square", "triangle", "diamond"] - }, - useGradients: false, - background: null - }, - colorDefaults: ["#94ae0a", "#115fa6", "#a61120", "#ff8809", "#ffd13e", "#a61187", "#24ad9a", "#7c7474", "#a66111"], - constructor: function(a) { - this.initConfig(a); - this.resolveDefaults() - }, - defaultRegEx: /^default([+\-/\*]\d+(?:\.\d+)?)?$/, - defaultOperators: { - "*": function(b, a) { - return b * a - }, - "+": function(b, a) { - return b + a - }, - "-": function(b, a) { - return b - a - } - }, - resolveDefaults: function() { - var a = this; - Ext.onReady(function() { - var f = Ext.clone(a.getSprites()), - e = Ext.clone(a.getAxis()), - d = Ext.clone(a.getSeries()), - g, c, b; - if (!a.superclass.defaults) { - g = Ext.getBody().createChild({ - tag: "div", - cls: "x-component" - }); - a.superclass.defaults = { - fontFamily: g.getStyle("fontFamily"), - fontWeight: g.getStyle("fontWeight"), - fontSize: parseFloat(g.getStyle("fontSize")), - fontVariant: g.getStyle("fontVariant"), - fontStyle: g.getStyle("fontStyle") - }; - g.destroy() - } - a.replaceDefaults(f.text); - a.setSprites(f); - for (c in e) { - b = e[c]; - a.replaceDefaults(b.label); - a.replaceDefaults(b.title) - } - a.setAxis(e); - for (c in d) { - b = d[c]; - a.replaceDefaults(b.label) - } - a.setSeries(d) - }) - }, - replaceDefaults: function(h) { - var e = this, - g = e.superclass.defaults, - a = e.defaultRegEx, - d, f, c, b; - if (Ext.isObject(h)) { - for (d in g) { - c = a.exec(h[d]); - if (c) { - f = g[d]; - c = c[1]; - if (c) { - b = e.defaultOperators[c.charAt(0)]; - f = Math.round(b(f, parseFloat(c.substr(1)))) - } - h[d] = f - } - } - } - }, - applyBaseColor: function(c) { - var a, b; - if (c) { - a = c.isColor ? c : Ext.draw.Color.fromString(c); - b = a.getHSL()[2]; - if (b < 0.15) { - a = a.createLighter(0.3) - } else { - if (b < 0.3) { - a = a.createLighter(0.15) - } else { - if (b > 0.85) { - a = a.createDarker(0.3) - } else { - if (b > 0.7) { - a = a.createDarker(0.15) - } - } - } - } - this.setColors([a.createDarker(0.3).toString(), a.createDarker(0.15).toString(), a.toString(), a.createLighter(0.12).toString(), a.createLighter(0.24).toString(), a.createLighter(0.31).toString()]) - } - return c - }, - applyColors: function(a) { - return a || this.colorDefaults - }, - updateUseGradients: function(a) { - if (a) { - this.updateGradients({ - type: "linear", - degrees: 90 - }) - } - }, - updateBackground: function(a) { - if (a) { - var b = this.getChart(); - b.defaults.background = a; - this.setChart(b) - } - }, - updateGradients: function(a) { - var c = this.getColors(), - e = [], - h, b, d, f, g; - if (Ext.isObject(a)) { - for (f = 0, g = c && c.length || 0; f < g; f++) { - b = Ext.draw.Color.fromString(c[f]); - if (b) { - d = b.createLighter(0.15).toString(); - h = Ext.apply(Ext.Object.chain(a), { - stops: [{ - offset: 1, - color: b.toString() - }, { - offset: 0, - color: d.toString() - }] - }); - e.push(h) - } - } - this.setColors(e) - } - }, - applySeriesThemes: function(a) { - this.getBaseColor(); - this.getUseGradients(); - this.getGradients(); - var b = this.getColors(); - if (!a) { - a = { - fillStyle: Ext.Array.clone(b), - strokeStyle: Ext.Array.map(b, function(d) { - var c = Ext.draw.Color.fromString(d.stops ? d.stops[0].color : d); - return c.createDarker(0.15).toString() - }) - } - } - return a - } -}); -Ext.define("Ext.chart.theme.Default", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.default", "chart.theme.Base"] -}); -Ext.define("Ext.chart.Markers", { - extend: "Ext.draw.sprite.Instancing", - isMarkers: true, - defaultCategory: "default", - constructor: function() { - this.callParent(arguments); - this.categories = {}; - this.revisions = {} - }, - destroy: function() { - this.categories = null; - this.revisions = null; - this.callParent() - }, - getMarkerFor: function(b, a) { - if (b in this.categories) { - var c = this.categories[b]; - if (a in c) { - return this.get(c[a]) - } - } - }, - clear: function(a) { - a = a || this.defaultCategory; - if (!(a in this.revisions)) { - this.revisions[a] = 1 - } else { - this.revisions[a]++ - } - }, - putMarkerFor: function(e, b, c, h, f) { - e = e || this.defaultCategory; - var d = this, - g = d.categories[e] || (d.categories[e] = {}), - a; - if (c in g) { - d.setAttributesFor(g[c], b, h) - } else { - g[c] = d.getCount(); - d.createInstance(b, h) - } - a = d.get(g[c]); - if (a) { - a.category = e; - if (!f) { - a.revision = d.revisions[e] || (d.revisions[e] = 1) - } - } - }, - getMarkerBBoxFor: function(c, a, b) { - if (c in this.categories) { - var d = this.categories[c]; - if (a in d) { - return this.getBBoxFor(d[a], b) - } - } - }, - getBBox: function() { - return null - }, - render: function(a, l, b) { - var f = this, - k = f.revisions, - j = f.attr.matrix, - h = f.getTemplate(), - d = h.attr, - g, c, e; - j.toContext(l); - h.preRender(a, l, b); - h.useAttributes(l, b); - for (c = 0, e = f.instances.length; c < e; c++) { - g = f.get(c); - if (g.hidden || g.revision !== k[g.category]) { - continue - } - l.save(); - h.attr = g; - h.useAttributes(l, b); - h.render(a, l, b); - l.restore() - } - h.attr = d - } -}); -Ext.define("Ext.chart.label.Callout", { - extend: "Ext.draw.modifier.Modifier", - prepareAttributes: function(a) { - if (!a.hasOwnProperty("calloutOriginal")) { - a.calloutOriginal = Ext.Object.chain(a); - a.calloutOriginal.prototype = a - } - if (this._previous) { - this._previous.prepareAttributes(a.calloutOriginal) - } - }, - setAttrs: function(e, h) { - var d = e.callout, - i = e.calloutOriginal, - l = e.bbox.plain, - c = (l.width || 0) + e.labelOverflowPadding, - m = (l.height || 0) + e.labelOverflowPadding, - p, o; - if ("callout" in h) { - d = h.callout - } - if ("callout" in h || "calloutPlaceX" in h || "calloutPlaceY" in h || "x" in h || "y" in h) { - var n = "rotationRads" in h ? i.rotationRads = h.rotationRads : i.rotationRads, - g = "x" in h ? (i.x = h.x) : i.x, - f = "y" in h ? (i.y = h.y) : i.y, - b = "calloutPlaceX" in h ? h.calloutPlaceX : e.calloutPlaceX, - a = "calloutPlaceY" in h ? h.calloutPlaceY : e.calloutPlaceY, - k = "calloutVertical" in h ? h.calloutVertical : e.calloutVertical, - j; - n %= Math.PI * 2; - if (Math.cos(n) < 0) { - n = (n + Math.PI) % (Math.PI * 2) - } - if (n > Math.PI) { - n -= Math.PI * 2 - } - if (k) { - n = n * (1 - d) - Math.PI / 2 * d; - j = c; - c = m; - m = j - } else { - n = n * (1 - d) - } - h.rotationRads = n; - h.x = g * (1 - d) + b * d; - h.y = f * (1 - d) + a * d; - p = b - g; - o = a - f; - if (Math.abs(o * c) > Math.abs(p * m)) { - if (o > 0) { - h.calloutEndX = h.x - (m / 2) * (p / o) * d; - h.calloutEndY = h.y - (m / 2) * d - } else { - h.calloutEndX = h.x + (m / 2) * (p / o) * d; - h.calloutEndY = h.y + (m / 2) * d - } - } else { - if (p > 0) { - h.calloutEndX = h.x - c / 2; - h.calloutEndY = h.y - (c / 2) * (o / p) * d - } else { - h.calloutEndX = h.x + c / 2; - h.calloutEndY = h.y + (c / 2) * (o / p) * d - } - } - if (h.calloutStartX && h.calloutStartY) { - h.calloutHasLine = (p > 0 && h.calloutStartX < h.calloutEndX) || (p <= 0 && h.calloutStartX > h.calloutEndX) || (o > 0 && h.calloutStartY < h.calloutEndY) || (o <= 0 && h.calloutStartY > h.calloutEndY) - } else { - h.calloutHasLine = true - } - } - return h - }, - pushDown: function(a, b) { - b = this.callParent([a.calloutOriginal, b]); - return this.setAttrs(a, b) - }, - popUp: function(a, b) { - a = a.prototype; - b = this.setAttrs(a, b); - if (this._next) { - return this._next.popUp(a, b) - } else { - return Ext.apply(a, b) - } - } -}); -Ext.define("Ext.chart.label.Label", { - extend: "Ext.draw.sprite.Text", - requires: ["Ext.chart.label.Callout"], - inheritableStatics: { - def: { - processors: { - callout: "limited01", - calloutHasLine: "bool", - calloutPlaceX: "number", - calloutPlaceY: "number", - calloutStartX: "number", - calloutStartY: "number", - calloutEndX: "number", - calloutEndY: "number", - calloutColor: "color", - calloutWidth: "number", - calloutVertical: "bool", - labelOverflowPadding: "number", - display: "enums(none,under,over,rotate,insideStart,insideEnd,inside,outside)", - orientation: "enums(horizontal,vertical)", - renderer: "default" - }, - defaults: { - callout: 0, - calloutHasLine: true, - calloutPlaceX: 0, - calloutPlaceY: 0, - calloutStartX: 0, - calloutStartY: 0, - calloutEndX: 0, - calloutEndY: 0, - calloutWidth: 1, - calloutVertical: false, - calloutColor: "black", - labelOverflowPadding: 5, - display: "none", - orientation: "", - renderer: null - }, - triggers: { - callout: "transform", - calloutPlaceX: "transform", - calloutPlaceY: "transform", - labelOverflowPadding: "transform", - calloutRotation: "transform", - display: "hidden" - }, - updaters: { - hidden: function(a) { - a.hidden = a.display === "none" - } - } - } - }, - config: { - fx: { - customDurations: { - callout: 200 - } - }, - field: null, - calloutLine: true - }, - applyCalloutLine: function(a) { - if (a) { - return Ext.apply({}, a) - } - }, - prepareModifiers: function() { - this.callParent(arguments); - this.calloutModifier = new Ext.chart.label.Callout({ - sprite: this - }); - this.fx.setNext(this.calloutModifier); - this.calloutModifier.setNext(this.topModifier) - }, - render: function(b, c) { - var e = this, - a = e.attr, - d = a.calloutColor; - c.save(); - c.globalAlpha *= a.callout; - if (c.globalAlpha > 0 && a.calloutHasLine) { - if (d && d.isGradient) { - d = d.getStops()[0].color - } - c.strokeStyle = d; - c.fillStyle = d; - c.lineWidth = a.calloutWidth; - c.beginPath(); - c.moveTo(e.attr.calloutStartX, e.attr.calloutStartY); - c.lineTo(e.attr.calloutEndX, e.attr.calloutEndY); - c.stroke(); - c.beginPath(); - c.arc(e.attr.calloutStartX, e.attr.calloutStartY, 1 * a.calloutWidth, 0, 2 * Math.PI, true); - c.fill(); - c.beginPath(); - c.arc(e.attr.calloutEndX, e.attr.calloutEndY, 1 * a.calloutWidth, 0, 2 * Math.PI, true); - c.fill() - } - c.restore(); - Ext.draw.sprite.Text.prototype.render.apply(e, arguments) - } -}); -Ext.define("Ext.chart.series.Series", { - requires: ["Ext.chart.Markers", "Ext.chart.label.Label", "Ext.tip.ToolTip"], - mixins: ["Ext.mixin.Observable", "Ext.mixin.Bindable"], - isSeries: true, - defaultBindProperty: "store", - type: null, - seriesType: "sprite", - identifiablePrefix: "ext-line-", - observableType: "series", - darkerStrokeRatio: 0.15, - config: { - chart: null, - title: null, - renderer: null, - showInLegend: true, - triggerAfterDraw: false, - style: {}, - subStyle: {}, - themeStyle: {}, - colors: null, - useDarkerStrokeColor: true, - store: null, - label: {}, - labelOverflowPadding: null, - showMarkers: true, - marker: null, - markerSubStyle: null, - itemInstancing: null, - background: null, - highlightItem: null, - surface: null, - overlaySurface: null, - hidden: false, - highlight: false, - highlightCfg: { - merge: function(a) { - return a - }, - $value: { - fillStyle: "yellow", - strokeStyle: "red" - } - }, - animation: null, - tooltip: null - }, - directions: [], - sprites: null, - themeColorCount: function() { - return 1 - }, - isStoreDependantColorCount: false, - themeMarkerCount: function() { - return 0 - }, - getFields: function(f) { - var e = this, - a = [], - c, b, d; - for (b = 0, d = f.length; b < d; b++) { - c = e["get" + f[b] + "Field"](); - if (Ext.isArray(c)) { - a.push.apply(a, c) - } else { - a.push(c) - } - } - return a - }, - applyAnimation: function(a, b) { - if (!a) { - a = { - duration: 0 - } - } else { - if (a === true) { - a = { - easing: "easeInOut", - duration: 500 - } - } - } - return b ? Ext.apply({}, a, b) : a - }, - getAnimation: function() { - var a = this.getChart(); - if (a && a.animationSuspendCount) { - return { - duration: 0 - } - } else { - return this.callParent() - } - }, - updateTitle: function(a) { - var j = this, - g = j.getChart(); - if (!g || g.isInitializing) { - return - } - a = Ext.Array.from(a); - var c = g.getSeries(), - b = Ext.Array.indexOf(c, j), - e = g.getLegendStore(), - h = j.getYField(), - d, l, k, f; - if (e.getCount() && b !== -1) { - f = h ? Math.min(a.length, h.length) : a.length; - for (d = 0; d < f; d++) { - k = a[d]; - l = e.getAt(b + d); - if (k && l) { - l.set("name", k) - } - } - } - }, - applyHighlight: function(a, b) { - if (Ext.isObject(a)) { - a = Ext.merge({}, this.config.highlightCfg, a) - } else { - if (a === true) { - a = this.config.highlightCfg - } - } - return Ext.apply(b || {}, a) - }, - updateHighlight: function(a) { - this.getStyle(); - if (!Ext.Object.isEmpty(a)) { - this.addItemHighlight() - } - }, - updateHighlightCfg: function(a) { - if (!Ext.Object.equals(a, this.defaultConfig.highlightCfg)) { - this.addItemHighlight() - } - }, - applyItemInstancing: function(a, b) { - return Ext.merge(b || {}, a) - }, - setAttributesForItem: function(c, d) { - var b = c && c.sprite, - a; - if (b) { - if (b.itemsMarker && c.category === "items") { - b.putMarker(c.category, d, c.index, false, true) - } - if (b.isMarkerHolder && c.category === "markers") { - b.putMarker(c.category, d, c.index, false, true) - } else { - if (b.isInstancing) { - b.setAttributesFor(c.index, d) - } else { - if (Ext.isArray(b)) { - for (a = 0; a < b.length; a++) { - b[a].setAttributes(d) - } - } else { - b.setAttributes(d) - } - } - } - } - }, - getBBoxForItem: function(a) { - if (a && a.sprite) { - if (a.sprite.itemsMarker && a.category === "items") { - return a.sprite.getMarkerBBox(a.category, a.index) - } else { - if (a.sprite instanceof Ext.draw.sprite.Instancing) { - return a.sprite.getBBoxFor(a.index) - } else { - return a.sprite.getBBox() - } - } - } - return null - }, - applyHighlightItem: function(d, a) { - if (d === a) { - return - } - if (Ext.isObject(d) && Ext.isObject(a)) { - var c = d.sprite === a.sprite, - b = d.index === a.index; - if (c && b) { - return - } - } - return d - }, - updateHighlightItem: function(b, a) { - this.setAttributesForItem(a, { - highlighted: false - }); - this.setAttributesForItem(b, { - highlighted: true - }) - }, - constructor: function(a) { - var b = this, - c; - a = a || {}; - if (a.tips) { - a = Ext.apply({ - tooltip: a.tips - }, a) - } - if (a.highlightCfg) { - a = Ext.apply({ - highlight: a.highlightCfg - }, a) - } - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.sprites = []; - b.dataRange = []; - b.mixins.observable.constructor.call(b, a); - b.initBindable() - }, - lookupViewModel: function(a) { - var b = this.getChart(); - return b ? b.lookupViewModel(a) : null - }, - applyTooltip: function(c, b) { - var a = Ext.apply({ - xtype: "tooltip", - renderer: Ext.emptyFn, - constrainPosition: true, - shrinkWrapDock: true, - autoHide: true, - offsetX: 10, - offsetY: 10 - }, c); - return Ext.create(a) - }, - updateTooltip: function() { - this.addItemHighlight() - }, - addItemHighlight: function() { - var d = this.getChart(); - if (!d) { - return - } - var e = d.getInteractions(), - c, a, b; - for (c = 0; c < e.length; c++) { - a = e[c]; - if (a.isItemHighlight || a.isItemEdit) { - b = true; - break - } - } - if (!b) { - e.push("itemhighlight"); - d.setInteractions(e) - } - }, - showTooltip: function(l, m) { - var d = this, - n = d.getTooltip(), - j, a, i, f, h, k, g, e, b, c; - if (!n) { - return - } - clearTimeout(d.tooltipTimeout); - b = n.config; - if (n.trackMouse) { - m[0] += b.offsetX; - m[1] += b.offsetY - } else { - j = l.sprite; - a = j.getSurface(); - i = Ext.get(a.getId()); - if (i) { - k = l.series.getBBoxForItem(l); - g = k.x + k.width / 2; - e = k.y + k.height / 2; - h = a.matrix.transformPoint([g, e]); - f = i.getXY(); - c = a.getInherited().rtl; - g = c ? f[0] + i.getWidth() - h[0] : f[0] + h[0]; - e = f[1] + h[1]; - m = [g, e] - } - } - Ext.callback(n.renderer, n.scope, [n, l.record, l], 0, d); - n.show(m) - }, - hideTooltip: function(b) { - var a = this, - c = a.getTooltip(); - if (!c) { - return - } - clearTimeout(a.tooltipTimeout); - a.tooltipTimeout = Ext.defer(function() { - c.hide() - }, 1) - }, - applyStore: function(a) { - return a && Ext.StoreManager.lookup(a) - }, - getStore: function() { - return this._store || this.getChart() && this.getChart().getStore() - }, - updateStore: function(b, a) { - var h = this, - g = h.getChart(), - c = g && g.getStore(), - f, j, e, d; - a = a || c; - if (a && a !== b) { - a.un({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: h - }) - } - if (b) { - b.on({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: h - }); - f = h.getSprites(); - for (d = 0, e = f.length; d < e; d++) { - j = f[d]; - if (j.setStore) { - j.setStore(b) - } - } - h.onDataChanged() - } - h.fireEvent("storechange", h, b, a) - }, - onStoreChange: function(b, a, c) { - if (!this._store) { - this.updateStore(a, c) - } - }, - coordinate: function(o, m, e) { - var l = this, - p = l.getStore(), - h = l.getHidden(), - k = p.getData().items, - b = l["get" + o + "Axis"](), - f = { - min: Infinity, - max: -Infinity - }, - q = l["fieldCategory" + o] || [o], - g = l.getFields(q), - d, n, c, a = {}, - j = l.getSprites(); - if (j.length > 0) { - if (!Ext.isBoolean(h) || !h) { - for (d = 0; d < q.length; d++) { - n = g[d]; - c = l.coordinateData(k, n, b); - l.getRangeOfData(c, f); - a["data" + q[d]] = c - } - } - l.dataRange[m] = f.min; - l.dataRange[m + e] = f.max; - a["dataMin" + o] = f.min; - a["dataMax" + o] = f.max; - if (b) { - b.range = null; - a["range" + o] = b.getRange() - } - for (d = 0; d < j.length; d++) { - j[d].setAttributes(a) - } - } - }, - coordinateData: function(b, h, d) { - var g = [], - f = b.length, - e = d && d.getLayout(), - c, a; - for (c = 0; c < f; c++) { - a = b[c].data[h]; - if (!Ext.isEmpty(a, true)) { - if (e) { - g[c] = e.getCoordFor(a, h, c, b) - } else { - g[c] = +a - } - } else { - g[c] = a - } - } - return g - }, - getRangeOfData: function(g, b) { - var e = g.length, - d = b.min, - a = b.max, - c, f; - for (c = 0; c < e; c++) { - f = g[c]; - if (f < d) { - d = f - } - if (f > a) { - a = f - } - } - b.min = d; - b.max = a - }, - updateLabelData: function() { - var h = this, - l = h.getStore(), - g = l.getData().items, - f = h.getSprites(), - a = h.getLabel().getTemplate(), - n = Ext.Array.from(a.getField()), - c, b, e, d, m, k; - if (!f.length || !n.length) { - return - } - for (c = 0; c < f.length; c++) { - d = []; - m = f[c]; - k = m.getField(); - if (Ext.Array.indexOf(n, k) < 0) { - k = n[c] - } - for (b = 0, e = g.length; b < e; b++) { - d.push(g[b].get(k)) - } - m.setAttributes({ - labels: d - }) - } - }, - processData: function() { - if (!this.getStore()) { - return - } - var d = this, - f = this.directions, - a, c = f.length, - e, b; - for (a = 0; a < c; a++) { - e = f[a]; - b = d["get" + e + "Axis"](); - if (b) { - b.processData(d); - continue - } - if (d["coordinate" + e]) { - d["coordinate" + e]() - } - } - d.updateLabelData() - }, - applyBackground: function(a) { - if (this.getChart()) { - this.getSurface().setBackground(a); - return this.getSurface().getBackground() - } else { - return a - } - }, - updateChart: function(d, a) { - var c = this, - b = c._store; - if (a) { - a.un("axeschange", "onAxesChange", c); - c.clearSprites(); - c.setSurface(null); - c.setOverlaySurface(null); - a.unregister(c); - c.onChartDetached(a); - if (!b) { - c.updateStore(null) - } - } - if (d) { - c.setSurface(d.getSurface("series")); - c.setOverlaySurface(d.getSurface("overlay")); - d.on("axeschange", "onAxesChange", c); - if (d.getAxes()) { - c.onAxesChange(d) - } - c.onChartAttached(d); - d.register(c); - if (!b) { - c.updateStore(d.getStore()) - } - } - }, - onAxesChange: function(h) { - var k = this, - g = h.getAxes(), - c, a = {}, - b = {}, - e = false, - j = this.directions, - l, d, f; - for (d = 0, f = j.length; d < f; d++) { - l = j[d]; - b[l] = k.getFields(k["fieldCategory" + l]) - } - for (d = 0, f = g.length; d < f; d++) { - c = g[d]; - if (!a[c.getDirection()]) { - a[c.getDirection()] = [c] - } else { - a[c.getDirection()].push(c) - } - } - for (d = 0, f = j.length; d < f; d++) { - l = j[d]; - if (k["get" + l + "Axis"]()) { - continue - } - if (a[l]) { - c = k.findMatchingAxis(a[l], b[l]); - if (c) { - k["set" + l + "Axis"](c); - if (c.getNeedHighPrecision()) { - e = true - } - } - } - } - this.getSurface().setHighPrecision(e) - }, - findMatchingAxis: function(f, e) { - var d, c, b, a; - for (b = 0; b < f.length; b++) { - d = f[b]; - c = d.getFields(); - if (!c.length) { - return d - } else { - if (e) { - for (a = 0; a < e.length; a++) { - if (Ext.Array.indexOf(c, e[a]) >= 0) { - return d - } - } - } - } - } - }, - onChartDetached: function(a) { - var b = this; - b.fireEvent("chartdetached", a, b); - a.un("storechange", "onStoreChange", b) - }, - onChartAttached: function(a) { - var b = this; - b.setBackground(b.getBackground()); - b.fireEvent("chartattached", a, b); - a.on("storechange", "onStoreChange", b); - b.processData() - }, - updateOverlaySurface: function(a) { - var b = this; - if (a) { - if (b.getLabel()) { - b.getOverlaySurface().add(b.getLabel()) - } - } - }, - applyLabel: function(a, b) { - if (!b) { - b = new Ext.chart.Markers({ - zIndex: 10 - }); - b.setTemplate(new Ext.chart.label.Label(a)) - } else { - b.getTemplate().setAttributes(a) - } - return b - }, - createItemInstancingSprite: function(c, b) { - var e = this, - f = new Ext.chart.Markers(), - a, d; - f.setAttributes({ - zIndex: Number.MAX_VALUE - }); - a = Ext.apply({}, b); - if (e.getHighlight()) { - a.highlight = e.getHighlight(); - a.modifiers = ["highlight"] - } - f.setTemplate(a); - d = f.getTemplate(); - d.setAttributes(e.getStyle()); - d.fx.on("animationstart", "onSpriteAnimationStart", this); - d.fx.on("animationend", "onSpriteAnimationEnd", this); - c.bindMarker("items", f); - e.getSurface().add(f); - return f - }, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer() - } - }, - updateRenderer: function(c) { - var b = this, - a = b.getChart(), - d; - if (a && a.isInitializing) { - return - } - d = b.getSprites(); - if (d.length) { - d[0].setAttributes({ - renderer: c || null - }); - if (a && !a.isInitializing) { - a.redraw() - } - } - }, - updateShowMarkers: function(a) { - var d = this.getSprites(), - b = d && d[0], - c = b && b.getMarker("markers"); - if (c) { - c.getTemplate().setAttributes({ - hidden: !a - }) - } - }, - createSprite: function() { - var f = this, - a = f.getSurface(), - e = f.getItemInstancing(), - d = a.add(f.getDefaultSpriteConfig()), - b = f.getMarker(), - g, c; - d.setAttributes(f.getStyle()); - d.setSeries(f); - if (e) { - d.itemsMarker = f.createItemInstancingSprite(d, e) - } - if (d.bindMarker) { - if (b) { - g = new Ext.chart.Markers(); - c = Ext.Object.merge({}, b); - if (f.getHighlight()) { - c.highlight = f.getHighlight(); - c.modifiers = ["highlight"] - } - g.setTemplate(c); - g.getTemplate().fx.setCustomDurations({ - translationX: 0, - translationY: 0 - }); - d.dataMarker = g; - d.bindMarker("markers", g); - f.getOverlaySurface().add(g) - } - if (f.getLabel().getTemplate().getField()) { - d.bindMarker("labels", f.getLabel()) - } - } - if (d.setStore) { - d.setStore(f.getStore()) - } - d.fx.on("animationstart", "onSpriteAnimationStart", f); - d.fx.on("animationend", "onSpriteAnimationEnd", f); - f.sprites.push(d); - return d - }, - getSprites: Ext.emptyFn, - onDataChanged: function() { - var d = this, - c = d.getChart(), - b = c && c.getStore(), - a = d.getStore(); - if (a !== b) { - d.processData() - } - }, - isXType: function(a) { - return a === "series" - }, - getItemId: function() { - return this.getId() - }, - applyThemeStyle: function(e, a) { - var b = this, - d, c; - d = e && e.subStyle && e.subStyle.fillStyle; - c = d && e.subStyle.strokeStyle; - if (d && !c) { - e.subStyle.strokeStyle = b.getStrokeColorsFromFillColors(d) - } - d = e && e.markerSubStyle && e.markerSubStyle.fillStyle; - c = d && e.markerSubStyle.strokeStyle; - if (d && !c) { - e.markerSubStyle.strokeStyle = b.getStrokeColorsFromFillColors(d) - } - return Ext.apply(a || {}, e) - }, - applyStyle: function(c, b) { - var a = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + this.seriesType)); - if (a && a.def) { - c = a.def.normalize(c) - } - return Ext.apply({}, c, b) - }, - applySubStyle: function(b, c) { - var a = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + this.seriesType)); - if (a && a.def) { - b = a.def.batchedNormalize(b, true) - } - return Ext.merge({}, c, b) - }, - applyMarker: function(c, a) { - var d = (c && c.type) || (a && a.type) || "circle", - b = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + d)); - if (b && b.def) { - c = b.def.normalize(Ext.isObject(c) ? c : {}, true); - c.type = d - } - return Ext.merge(a || {}, c) - }, - applyMarkerSubStyle: function(c, a) { - var d = (c && c.type) || (a && a.type) || "circle", - b = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + d)); - if (b && b.def) { - c = b.def.batchedNormalize(c, true) - } - return Ext.merge(a || {}, c) - }, - updateHidden: function(b) { - var a = this; - a.getColors(); - a.getSubStyle(); - a.setSubStyle({ - hidden: b - }); - a.processData(); - a.doUpdateStyles(); - if (!Ext.isArray(b)) { - a.updateLegendStore(b) - } - }, - updateLegendStore: function(f, b) { - var e = this, - d = e.getChart(), - c = d.getLegendStore(), - g = e.getId(), - a; - if (c) { - if (arguments.length > 1) { - a = c.findBy(function(h) { - return h.get("series") === g && h.get("index") === b - }); - if (a !== -1) { - a = c.getAt(a) - } - } else { - a = c.findRecord("series", g) - } - if (a && a.get("disabled") !== f) { - a.set("disabled", f) - } - } - }, - setHiddenByIndex: function(a, c) { - var b = this; - if (Ext.isArray(b.getHidden())) { - b.getHidden()[a] = c; - b.updateHidden(b.getHidden()); - b.updateLegendStore(c, a) - } else { - b.setHidden(c) - } - }, - getStrokeColorsFromFillColors: function(a) { - var c = this, - e = c.getUseDarkerStrokeColor(), - b = (Ext.isNumber(e) ? e : c.darkerStrokeRatio), - d; - if (e) { - d = Ext.Array.map(a, function(f) { - f = Ext.isString(f) ? f : f.stops[0].color; - f = Ext.draw.Color.fromString(f); - return f.createDarker(b).toString() - }) - } else { - d = Ext.Array.clone(a) - } - return d - }, - updateThemeColors: function(b) { - var c = this, - d = c.getThemeStyle(), - a = Ext.Array.clone(b), - f = c.getStrokeColorsFromFillColors(b), - e = { - fillStyle: a, - strokeStyle: f - }; - d.subStyle = Ext.apply(d.subStyle || {}, e); - d.markerSubStyle = Ext.apply(d.markerSubStyle || {}, e); - c.doUpdateStyles() - }, - themeOnlyIfConfigured: {}, - updateTheme: function(d) { - var h = this, - a = d.getSeries(), - n = h.getInitialConfig(), - c = h.defaultConfig, - f = h.getConfigurator().configs, - j = a.defaults, - k = a[h.type], - g = h.themeOnlyIfConfigured, - l, i, o, b, m, e; - a = Ext.merge({}, j, k); - for (l in a) { - i = a[l]; - e = f[l]; - if (i !== null && i !== undefined && e) { - m = n[l]; - o = Ext.isObject(i); - b = m === c[l]; - if (o) { - if (b && g[l]) { - continue - } - i = Ext.merge({}, i, m) - } - if (b || o) { - h[e.names.set](i) - } - } - } - }, - updateChartColors: function(a) { - var b = this; - if (!b.getColors()) { - b.updateThemeColors(a) - } - }, - updateColors: function(a) { - this.updateThemeColors(a) - }, - updateStyle: function() { - this.doUpdateStyles() - }, - updateSubStyle: function() { - this.doUpdateStyles() - }, - updateThemeStyle: function() { - this.doUpdateStyles() - }, - doUpdateStyles: function() { - var g = this, - h = g.sprites, - d = g.getItemInstancing(), - c = 0, - f = h && h.length, - a = g.getConfig("showMarkers", true), - b = g.getMarker(), - e; - for (; c < f; c++) { - e = g.getStyleByIndex(c); - if (d) { - h[c].itemsMarker.getTemplate().setAttributes(e) - } - h[c].setAttributes(e); - if (b && h[c].dataMarker) { - h[c].dataMarker.getTemplate().setAttributes(g.getMarkerStyleByIndex(c)) - } - } - }, - getStyleWithTheme: function() { - var b = this, - c = b.getThemeStyle(), - d = (c && c.style) || {}, - a = Ext.applyIf(Ext.apply({}, b.getStyle()), d); - return a - }, - getSubStyleWithTheme: function() { - var c = this, - d = c.getThemeStyle(), - a = (d && d.subStyle) || {}, - b = Ext.applyIf(Ext.apply({}, c.getSubStyle()), a); - return b - }, - getStyleByIndex: function(b) { - var e = this, - h = e.getThemeStyle(), - d, g, c, f, a = {}; - d = e.getStyle(); - g = (h && h.style) || {}; - c = e.styleDataForIndex(e.getSubStyle(), b); - f = e.styleDataForIndex((h && h.subStyle), b); - Ext.apply(a, g); - Ext.apply(a, f); - Ext.apply(a, d); - Ext.apply(a, c); - return a - }, - getMarkerStyleByIndex: function(d) { - var g = this, - c = g.getThemeStyle(), - a, e, k, j, b, l, h, f, m = {}; - a = g.getStyle(); - e = (c && c.style) || {}; - k = g.styleDataForIndex(g.getSubStyle(), d); - if (k.hasOwnProperty("hidden")) { - k.hidden = k.hidden || !this.getConfig("showMarkers", true) - } - j = g.styleDataForIndex((c && c.subStyle), d); - b = g.getMarker(); - l = (c && c.marker) || {}; - h = g.getMarkerSubStyle(); - f = g.styleDataForIndex((c && c.markerSubStyle), d); - Ext.apply(m, e); - Ext.apply(m, j); - Ext.apply(m, l); - Ext.apply(m, f); - Ext.apply(m, a); - Ext.apply(m, k); - Ext.apply(m, b); - Ext.apply(m, h); - return m - }, - styleDataForIndex: function(d, c) { - var e, b, a = {}; - if (d) { - for (b in d) { - e = d[b]; - if (Ext.isArray(e)) { - a[b] = e[c % e.length] - } else { - a[b] = e - } - } - } - return a - }, - getItemForPoint: Ext.emptyFn, - getItemByIndex: function(a, e) { - var d = this, - f = d.getSprites(), - b = f && f[0], - c; - if (!b) { - return - } - if (e === undefined && b.isMarkerHolder) { - e = d.getItemInstancing() ? "items" : "markers" - } else { - if (!e || e === "" || e === "sprites") { - b = f[a] - } - } - if (b) { - c = { - series: d, - category: e, - index: a, - record: d.getStore().getData().items[a], - field: d.getYField(), - sprite: b - }; - return c - } - }, - onSpriteAnimationStart: function(a) { - this.fireEvent("animationstart", this, a) - }, - onSpriteAnimationEnd: function(a) { - this.fireEvent("animationend", this, a) - }, - resolveListenerScope: function(e) { - var d = this, - a = Ext._namedScopes[e], - c = d.getChart(), - b; - if (!a) { - b = c ? c.resolveListenerScope(e, false) : (e || d) - } else { - if (a.isThis) { - b = d - } else { - if (a.isController) { - b = c ? c.resolveListenerScope(e, false) : d - } else { - if (a.isSelf) { - b = c ? c.resolveListenerScope(e, false) : d; - if (b === c && !c.getInheritedConfig("defaultListenerScope")) { - b = d - } - } - } - } - } - return b - }, - provideLegendInfo: function(a) { - a.push({ - name: this.getTitle() || this.getId(), - mark: "black", - disabled: this.getHidden(), - series: this.getId(), - index: 0 - }) - }, - clearSprites: function() { - var d = this.sprites, - b, a, c; - for (a = 0, c = d.length; a < c; a++) { - b = d[a]; - if (b && b.isSprite) { - b.destroy() - } - } - this.sprites = [] - }, - destroy: function() { - var b = this, - a = b._store, - c = b.getConfig("tooltip", true); - if (a && a.getAutoDestroy()) { - Ext.destroy(a) - } - b.setChart(null); - b.clearListeners(); - if (c) { - Ext.destroy(c); - clearTimeout(b.tooltipTimeout) - } - b.callParent() - } -}); -Ext.define("Ext.chart.interactions.Abstract", { - xtype: "interaction", - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - gestures: { - tap: "onGesture" - }, - chart: null, - enabled: true - }, - throttleGap: 0, - stopAnimationBeforeSync: false, - constructor: function(a) { - var b = this, - c; - a = a || {}; - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.mixins.observable.constructor.call(b, a) - }, - initialize: Ext.emptyFn, - updateChart: function(c, a) { - var b = this; - if (a === c) { - return - } - if (a) { - a.unregister(b); - b.removeChartListener(a) - } - if (c) { - c.register(b); - b.addChartListener() - } - }, - updateEnabled: function(a) { - var c = this, - b = c.getChart(); - if (b) { - if (a) { - c.addChartListener() - } else { - c.removeChartListener(b) - } - } - }, - onGesture: Ext.emptyFn, - getItemForEvent: function(d) { - var b = this, - a = b.getChart(), - c = a.getEventXY(d); - return a.getItemForPoint(c[0], c[1]) - }, - getItemsForEvent: function(d) { - var b = this, - a = b.getChart(), - c = a.getEventXY(d); - return a.getItemsForPoint(c[0], c[1]) - }, - addChartListener: function() { - var c = this, - b = c.getChart(), - e = c.getGestures(), - a; - if (!c.getEnabled()) { - return - } - - function d(f, g) { - b.addElementListener(f, c.listeners[f] = function(j) { - var i = c.getLocks(), - h; - if (c.getEnabled() && (!(f in i) || i[f] === c)) { - h = (Ext.isFunction(g) ? g : c[g]).apply(this, arguments); - if (h === false && j && j.stopPropagation) { - j.stopPropagation() - } - return h - } - }, c) - } - c.listeners = c.listeners || {}; - for (a in e) { - d(a, e[a]) - } - }, - removeChartListener: function(c) { - var d = this, - e = d.getGestures(), - b; - - function a(f) { - var g = d.listeners[f]; - if (g) { - c.removeElementListener(f, g); - delete d.listeners[f] - } - } - if (d.listeners) { - for (b in e) { - a(b) - } - } - }, - lockEvents: function() { - var d = this, - c = d.getLocks(), - a = Array.prototype.slice.call(arguments), - b = a.length; - while (b--) { - c[a[b]] = d - } - }, - unlockEvents: function() { - var c = this.getLocks(), - a = Array.prototype.slice.call(arguments), - b = a.length; - while (b--) { - delete c[a[b]] - } - }, - getLocks: function() { - var a = this.getChart(); - return a.lockedEvents || (a.lockedEvents = {}) - }, - isMultiTouch: function() { - if (Ext.browser.is.IE10) { - return true - } - return !Ext.os.is.Desktop - }, - initializeDefaults: Ext.emptyFn, - doSync: function() { - var b = this, - a = b.getChart(); - if (b.syncTimer) { - clearTimeout(b.syncTimer); - b.syncTimer = null - } - if (b.stopAnimationBeforeSync) { - a.animationSuspendCount++ - } - a.redraw(); - if (b.stopAnimationBeforeSync) { - a.animationSuspendCount-- - } - b.syncThrottle = Date.now() + b.throttleGap - }, - sync: function() { - var a = this; - if (a.throttleGap && Ext.frameStartTime < a.syncThrottle) { - if (a.syncTimer) { - return - } - a.syncTimer = Ext.defer(function() { - a.doSync() - }, a.throttleGap) - } else { - a.doSync() - } - }, - getItemId: function() { - return this.getId() - }, - isXType: function(a) { - return a === "interaction" - }, - destroy: function() { - var a = this; - a.setChart(null); - delete a.listeners; - a.callParent() - } -}, function() { - if (Ext.os.is.Android4) { - this.prototype.throttleGap = 40 - } -}); -Ext.define("Ext.chart.MarkerHolder", { - extend: "Ext.Mixin", - mixinConfig: { - id: "markerHolder", - after: { - constructor: "constructor", - preRender: "preRender" - }, - before: { - destroy: "destroy" - } - }, - isMarkerHolder: true, - surfaceMatrix: null, - inverseSurfaceMatrix: null, - deprecated: { - 6: { - methods: { - getBoundMarker: { - message: "Please use the 'getMarker' method instead.", - fn: function(b) { - var a = this.boundMarkers[b]; - return a ? [a] : a - } - } - } - } - }, - constructor: function() { - this.boundMarkers = {}; - this.cleanRedraw = false - }, - bindMarker: function(b, a) { - var c = this, - d = c.boundMarkers; - if (a && a.isMarkers) { - c.releaseMarker(b); - d[b] = a; - a.on("destroy", c.onMarkerDestroy, c) - } - }, - onMarkerDestroy: function(a) { - this.releaseMarker(a) - }, - releaseMarker: function(a) { - var c = this.boundMarkers, - b; - if (a && a.isMarkers) { - for (b in c) { - if (c[b] === a) { - delete c[b]; - break - } - } - } else { - b = a; - a = c[b]; - delete c[b] - } - return a || null - }, - getMarker: function(a) { - return this.boundMarkers[a] || null - }, - preRender: function() { - var f = this, - g = f.getId(), - d = f.boundMarkers, - e = f.getParent(), - c, a, b; - if (f.surfaceMatrix) { - b = f.surfaceMatrix.set(1, 0, 0, 1, 0, 0) - } else { - b = f.surfaceMatrix = new Ext.draw.Matrix() - } - f.cleanRedraw = !f.attr.dirty; - if (!f.cleanRedraw) { - for (c in d) { - a = d[c]; - if (a) { - a.clear(g) - } - } - } - while (e && e.attr && e.attr.matrix) { - b.prependMatrix(e.attr.matrix); - e = e.getParent() - } - b.prependMatrix(e.matrix); - f.surfaceMatrix = b; - f.inverseSurfaceMatrix = b.inverse(f.inverseSurfaceMatrix) - }, - putMarker: function(d, a, c, g, e) { - var b = this.boundMarkers[d], - f = this.getId(); - if (b) { - b.putMarkerFor(f, a, c, g, e) - } - }, - getMarkerBBox: function(c, b, d) { - var a = this.boundMarkers[c], - e = this.getId(); - if (a) { - return a.getMarkerBBoxFor(e, b, d) - } - }, - destroy: function() { - var c = this.boundMarkers, - b, a; - for (b in c) { - a = c[b]; - a.destroy() - } - } -}); -Ext.define("Ext.chart.axis.sprite.Axis", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.axis", - type: "axis", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - requires: ["Ext.draw.sprite.Text"], - inheritableStatics: { - def: { - processors: { - grid: "bool", - axisLine: "bool", - minorTicks: "bool", - minorTickSize: "number", - majorTicks: "bool", - majorTickSize: "number", - length: "number", - startGap: "number", - endGap: "number", - dataMin: "number", - dataMax: "number", - visibleMin: "number", - visibleMax: "number", - position: "enums(left,right,top,bottom,angular,radial,gauge)", - minStepSize: "number", - estStepSize: "number", - titleOffset: "number", - textPadding: "number", - min: "number", - max: "number", - centerX: "number", - centerY: "number", - radius: "number", - totalAngle: "number", - baseRotation: "number", - data: "default", - enlargeEstStepSizeByText: "bool" - }, - defaults: { - grid: false, - axisLine: true, - minorTicks: false, - minorTickSize: 3, - majorTicks: true, - majorTickSize: 5, - length: 0, - startGap: 0, - endGap: 0, - visibleMin: 0, - visibleMax: 1, - dataMin: 0, - dataMax: 1, - position: "", - minStepSize: 0, - estStepSize: 20, - min: 0, - max: 1, - centerX: 0, - centerY: 0, - radius: 1, - baseRotation: 0, - data: null, - titleOffset: 0, - textPadding: 0, - scalingCenterY: 0, - scalingCenterX: 0, - strokeStyle: "black", - enlargeEstStepSizeByText: false - }, - triggers: { - minorTickSize: "bbox", - majorTickSize: "bbox", - position: "bbox,layout", - axisLine: "bbox,layout", - min: "layout", - max: "layout", - length: "layout", - minStepSize: "layout", - estStepSize: "layout", - data: "layout", - dataMin: "layout", - dataMax: "layout", - visibleMin: "layout", - visibleMax: "layout", - enlargeEstStepSizeByText: "layout" - }, - updaters: { - layout: "layoutUpdater" - } - } - }, - config: { - label: null, - layout: null, - segmenter: null, - renderer: null, - layoutContext: null, - axis: null - }, - thickness: 0, - stepSize: 0, - getBBox: function() { - return null - }, - defaultRenderer: function(a) { - return this.segmenter.renderer(a, this) - }, - layoutUpdater: function() { - var h = this, - f = h.getAxis().getChart(); - if (f.isInitializing) { - return - } - var e = h.attr, - d = h.getLayout(), - g = f.getInherited().rtl, - b = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMin, - i = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMax, - c = e.position, - a = { - attr: e, - segmenter: h.getSegmenter(), - renderer: h.defaultRenderer - }; - if (c === "left" || c === "right") { - e.translationX = 0; - e.translationY = i * e.length / (i - b); - e.scalingX = 1; - e.scalingY = -e.length / (i - b); - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } else { - if (c === "top" || c === "bottom") { - if (g) { - e.translationX = e.length + b * e.length / (i - b) + 1 - } else { - e.translationX = -b * e.length / (i - b) - } - e.translationY = 0; - e.scalingX = (g ? -1 : 1) * e.length / (i - b); - e.scalingY = 1; - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } - } - if (d) { - d.calculateLayout(a); - h.setLayoutContext(a) - } - }, - iterate: function(e, j) { - var c, g, a, b, h, d, k = Ext.Array.some, - m = Math.abs, - f; - if (e.getLabel) { - if (e.min < e.from) { - j.call(this, e.min, e.getLabel(e.min), -1, e) - } - for (c = 0; c <= e.steps; c++) { - j.call(this, e.get(c), e.getLabel(c), c, e) - } - if (e.max > e.to) { - j.call(this, e.max, e.getLabel(e.max), e.steps + 1, e) - } - } else { - b = this.getAxis(); - h = b.floatingAxes; - d = []; - f = (e.to - e.from) / (e.steps + 1); - if (b.getFloating()) { - for (a in h) { - d.push(h[a]) - } - } - - function l(i) { - return !d.length || k(d, function(n) { - return m(n - i) > f - }) - } - if (e.min < e.from && l(e.min)) { - j.call(this, e.min, e.min, -1, e) - } - for (c = 0; c <= e.steps; c++) { - g = e.get(c); - if (l(g)) { - j.call(this, g, g, c, e) - } - } - if (e.max > e.to && l(e.max)) { - j.call(this, e.max, e.max, e.steps + 1, e) - } - } - }, - renderTicks: function(l, m, s, p) { - var v = this, - k = v.attr, - u = k.position, - n = k.matrix, - e = 0.5 * k.lineWidth, - f = n.getXX(), - i = n.getDX(), - j = n.getYY(), - h = n.getDY(), - o = s.majorTicks, - d = k.majorTickSize, - a = s.minorTicks, - r = k.minorTickSize; - if (o) { - switch (u) { - case "right": - function q(w) { - return function(x, z, y) { - x = l.roundPixel(x * j + h) + e; - m.moveTo(0, x); - m.lineTo(w, x) - } - } - v.iterate(o, q(d)); - a && v.iterate(a, q(r)); - break; - case "left": - function t(w) { - return function(x, z, y) { - x = l.roundPixel(x * j + h) + e; - m.moveTo(p[2] - w, x); - m.lineTo(p[2], x) - } - } - v.iterate(o, t(d)); - a && v.iterate(a, t(r)); - break; - case "bottom": - function c(w) { - return function(x, z, y) { - x = l.roundPixel(x * f + i) - e; - m.moveTo(x, 0); - m.lineTo(x, w) - } - } - v.iterate(o, c(d)); - a && v.iterate(a, c(r)); - break; - case "top": - function b(w) { - return function(x, z, y) { - x = l.roundPixel(x * f + i) - e; - m.moveTo(x, p[3]); - m.lineTo(x, p[3] - w) - } - } - v.iterate(o, b(d)); - a && v.iterate(a, b(r)); - break; - case "angular": - v.iterate(o, function(w, y, x) { - w = w / (k.max + 1) * Math.PI * 2 + k.baseRotation; - m.moveTo(k.centerX + (k.length) * Math.cos(w), k.centerY + (k.length) * Math.sin(w)); - m.lineTo(k.centerX + (k.length + d) * Math.cos(w), k.centerY + (k.length + d) * Math.sin(w)) - }); - break; - case "gauge": - var g = v.getGaugeAngles(); - v.iterate(o, function(w, y, x) { - w = (w - k.min) / (k.max - k.min + 1) * k.totalAngle - k.totalAngle + g.start; - m.moveTo(k.centerX + (k.length) * Math.cos(w), k.centerY + (k.length) * Math.sin(w)); - m.lineTo(k.centerX + (k.length + d) * Math.cos(w), k.centerY + (k.length + d) * Math.sin(w)) - }); - break - } - } - }, - renderLabels: function(E, q, D, K) { - var o = this, - k = o.attr, - i = 0.5 * k.lineWidth, - u = k.position, - y = k.matrix, - A = k.textPadding, - x = y.getXX(), - d = y.getDX(), - g = y.getYY(), - c = y.getDY(), - n = 0, - I = D.majorTicks, - G = Math.max(k.majorTickSize, k.minorTickSize) + k.lineWidth, - f = Ext.draw.Draw.isBBoxIntersect, - F = o.getLabel(), - J, s, r = null, - w = 0, - b = 0, - m = D.segmenter, - B = o.getRenderer(), - t = o.getAxis(), - z = t.getTitle(), - a = z && z.attr.text !== "" && z.getBBox(), - l, h = null, - p, C, v, e, H; - if (I && F && !F.attr.hidden) { - J = F.attr.font; - if (q.font !== J) { - q.font = J - } - F.setAttributes({ - translationX: 0, - translationY: 0 - }, true); - F.applyTransformations(); - l = F.attr.inverseMatrix.elements.slice(0); - switch (u) { - case "left": - e = a ? a.x + a.width : 0; - switch (F.attr.textAlign) { - case "start": - H = E.roundPixel(e + d) - i; - break; - case "end": - H = E.roundPixel(K[2] - G + d) - i; - break; - default: - H = E.roundPixel(e + (K[2] - e - G) / 2 + d) - i - } - F.setAttributes({ - translationX: H - }, true); - break; - case "right": - e = a ? K[2] - a.x : 0; - switch (F.attr.textAlign) { - case "start": - H = E.roundPixel(G + d) + i; - break; - case "end": - H = E.roundPixel(K[2] - e + d) + i; - break; - default: - H = E.roundPixel(G + (K[2] - G - e) / 2 + d) + i - } - F.setAttributes({ - translationX: H - }, true); - break; - case "top": - e = a ? a.y + a.height : 0; - F.setAttributes({ - translationY: E.roundPixel(e + (K[3] - e - G) / 2) - i - }, true); - break; - case "bottom": - e = a ? K[3] - a.y : 0; - F.setAttributes({ - translationY: E.roundPixel(G + (K[3] - G - e) / 2) + i - }, true); - break; - case "radial": - F.setAttributes({ - translationX: k.centerX - }, true); - break; - case "angular": - F.setAttributes({ - translationY: k.centerY - }, true); - break; - case "gauge": - F.setAttributes({ - translationY: k.centerY - }, true); - break - } - if (u === "left" || u === "right") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - F.setAttributes({ - text: String(v), - translationY: E.roundPixel(L * g + c) - }, true); - F.applyTransformations(); - n = Math.max(n, F.getBBox().width + G); - if (n <= o.thickness) { - C = Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0)); - p = C.prepend.apply(C, l).transformBBox(F.getBBox(true)); - if (h && !f(p, h, A)) { - return - } - E.renderSprite(F); - h = p; - w += p.height; - b++ - } - }) - } else { - if (u === "top" || u === "bottom") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - F.setAttributes({ - text: String(v), - translationX: E.roundPixel(L * x + d) - }, true); - F.applyTransformations(); - n = Math.max(n, F.getBBox().height + G); - if (n <= o.thickness) { - C = Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0)); - p = C.prepend.apply(C, l).transformBBox(F.getBBox(true)); - if (h && !f(p, h, A)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "radial") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - if (typeof v !== "undefined") { - F.setAttributes({ - text: String(v), - translationX: k.centerX - E.roundPixel(L) / k.max * k.length * Math.cos(k.baseRotation + Math.PI / 2), - translationY: k.centerY - E.roundPixel(L) / k.max * k.length * Math.sin(k.baseRotation + Math.PI / 2) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "angular") { - s = k.majorTickSize + k.lineWidth * 0.5 + (parseInt(F.attr.fontSize, 10) || 10) / 2; - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - n = Math.max(n, Math.max(k.majorTickSize, k.minorTickSize) + (k.lineCap !== "butt" ? k.lineWidth * 0.5 : 0)); - if (typeof v !== "undefined") { - var O = L / (k.max + 1) * Math.PI * 2 + k.baseRotation; - F.setAttributes({ - text: String(v), - translationX: k.centerX + (k.length + s) * Math.cos(O), - translationY: k.centerY + (k.length + s) * Math.sin(O) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "gauge") { - var j = o.getGaugeAngles(); - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - if (typeof v !== "undefined") { - var O = (L - k.min) / (k.max - k.min + 1) * k.totalAngle - k.totalAngle + j.start; - F.setAttributes({ - text: String(v), - translationX: k.centerX + (k.length + 10) * Math.cos(O), - translationY: k.centerY + (k.length + 10) * Math.sin(O) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } - } - } - } - } - if (k.enlargeEstStepSizeByText && b) { - w /= b; - w += G; - w *= 2; - if (k.estStepSize < w) { - k.estStepSize = w - } - } - if (Math.abs(o.thickness - (n)) > 1) { - o.thickness = n; - k.bbox.plain.dirty = true; - k.bbox.transform.dirty = true; - o.doThicknessChanged(); - return false - } - } - }, - renderAxisLine: function(a, i, e, c) { - var h = this, - g = h.attr, - b = g.lineWidth * 0.5, - j = g.position, - d, f; - if (g.axisLine && g.length) { - switch (j) { - case "left": - d = a.roundPixel(c[2]) - b; - i.moveTo(d, -g.endGap); - i.lineTo(d, g.length + g.startGap + 1); - break; - case "right": - i.moveTo(b, -g.endGap); - i.lineTo(b, g.length + g.startGap + 1); - break; - case "bottom": - i.moveTo(-g.startGap, b); - i.lineTo(g.length + g.endGap, b); - break; - case "top": - d = a.roundPixel(c[3]) - b; - i.moveTo(-g.startGap, d); - i.lineTo(g.length + g.endGap, d); - break; - case "angular": - i.moveTo(g.centerX + g.length, g.centerY); - i.arc(g.centerX, g.centerY, g.length, 0, Math.PI * 2, true); - break; - case "gauge": - f = h.getGaugeAngles(); - i.moveTo(g.centerX + Math.cos(f.start) * g.length, g.centerY + Math.sin(f.start) * g.length); - i.arc(g.centerX, g.centerY, g.length, f.start, f.end, true); - break - } - } - }, - getGaugeAngles: function() { - var a = this, - c = a.attr.totalAngle, - b; - if (c <= Math.PI) { - b = (Math.PI - c) * 0.5 - } else { - b = -(Math.PI * 2 - c) * 0.5 - } - b = Math.PI * 2 - b; - return { - start: b, - end: b - c - } - }, - renderGridLines: function(m, n, s, r) { - var t = this, - b = t.getAxis(), - l = t.attr, - p = l.matrix, - d = l.startGap, - a = l.endGap, - c = p.getXX(), - k = p.getYY(), - h = p.getDX(), - g = p.getDY(), - u = l.position, - f = b.getGridAlignment(), - q = s.majorTicks, - e, o, i; - if (l.grid) { - if (q) { - if (u === "left" || u === "right") { - i = l.min * k + g + a + d; - t.iterate(q, function(j, w, v) { - e = j * k + g + a; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - y: e, - height: i - e - }, o = v, true); - i = e - }); - o++; - e = 0; - t.putMarker(f + "-" + (o % 2 ? "odd" : "even"), { - y: e, - height: i - e - }, o, true) - } else { - if (u === "top" || u === "bottom") { - i = l.min * c + h + d; - if (d) { - t.putMarker(f + "-even", { - x: 0, - width: i - }, -1, true) - } - t.iterate(q, function(j, w, v) { - e = j * c + h + d; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - x: e, - width: i - e - }, o = v, true); - i = e - }); - o++; - e = l.length + l.startGap + l.endGap; - t.putMarker(f + "-" + (o % 2 ? "odd" : "even"), { - x: e, - width: i - e - }, o, true) - } else { - if (u === "radial") { - t.iterate(q, function(j, w, v) { - if (!j) { - return - } - e = j / l.max * l.length; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - scalingX: e, - scalingY: e - }, v, true); - i = e - }) - } else { - if (u === "angular") { - t.iterate(q, function(j, w, v) { - if (!l.length) { - return - } - e = j / (l.max + 1) * Math.PI * 2 + l.baseRotation; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - rotationRads: e, - rotationCenterX: 0, - rotationCenterY: 0, - scalingX: l.length, - scalingY: l.length - }, v, true); - i = e - }) - } - } - } - } - } - } - }, - renderLimits: function(o) { - var t = this, - a = t.getAxis(), - h = a.getChart(), - p = h.getInnerPadding(), - d = Ext.Array.from(a.getLimits()); - if (!d.length) { - return - } - var r = a.limits.surface.getRect(), - m = t.attr, - n = m.matrix, - u = m.position, - k = Ext.Object.chain, - v = a.limits.titles, - c, j, b, s, l, q, f, g, e; - v.instances = []; - v.position = 0; - if (u === "left" || u === "right") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l * n.getYY() + n.getDY(); - s.line.y = l + p.top; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("horizontal-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - j = s.line.title.position || (u === "left" ? "start" : "end"); - switch (j) { - case "start": - g = 10; - break; - case "end": - g = r[2] - 10; - break; - case "middle": - g = r[2] / 2; - break - } - v.setAttributesFor(v.position - 1, { - x: g, - y: s.line.y - c.height / 2, - textAlign: j, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "top" || u === "bottom") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l * n.getXX() + n.getDX(); - s.line.x = l + p.left; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("vertical-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - j = s.line.title.position || (u === "top" ? "end" : "start"); - switch (j) { - case "start": - e = r[3] - c.width / 2 - 10; - break; - case "end": - e = c.width / 2 + 10; - break; - case "middle": - e = r[3] / 2; - break - } - v.setAttributesFor(v.position - 1, { - x: s.line.x + c.height / 2, - y: e, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle, - rotationRads: Math.PI / 2 - }) - } - } - } else { - if (u === "radial") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - if (l > m.max) { - continue - } - l = l / m.max * m.length; - s.line.cx = m.centerX; - s.line.cy = m.centerY; - s.line.scalingX = l; - s.line.scalingY = l; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("circular-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - v.setAttributesFor(v.position - 1, { - x: m.centerX, - y: m.centerY - l - c.height / 2, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "angular") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l / (m.max + 1) * Math.PI * 2 + m.baseRotation; - s.line.translationX = m.centerX; - s.line.translationY = m.centerY; - s.line.rotationRads = l; - s.line.rotationCenterX = 0; - s.line.rotationCenterY = 0; - s.line.scalingX = m.length; - s.line.scalingY = m.length; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("radial-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - b = ((l > -0.5 * Math.PI && l < 0.5 * Math.PI) || (l > 1.5 * Math.PI && l < 2 * Math.PI)) ? 1 : -1; - v.setAttributesFor(v.position - 1, { - x: m.centerX + 0.5 * m.length * Math.cos(l) + b * c.height / 2 * Math.sin(l), - y: m.centerY + 0.5 * m.length * Math.sin(l) - b * c.height / 2 * Math.cos(l), - rotationRads: b === 1 ? l : l - Math.PI, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "gauge") {} - } - } - } - } - }, - doThicknessChanged: function() { - var a = this.getAxis(); - if (a) { - a.onThicknessChanged() - } - }, - render: function(a, c, d) { - var e = this, - b = e.getLayoutContext(); - if (b) { - if (false === e.renderLabels(a, c, b, d)) { - return false - } - c.beginPath(); - e.renderTicks(a, c, b, d); - e.renderAxisLine(a, c, b, d); - e.renderGridLines(a, c, b, d); - e.renderLimits(d); - c.stroke() - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Segmenter", { - config: { - axis: null - }, - constructor: function(a) { - this.initConfig(a) - }, - renderer: function(b, a) { - return String(b) - }, - from: function(a) { - return a - }, - diff: Ext.emptyFn, - align: Ext.emptyFn, - add: Ext.emptyFn, - preferredStep: Ext.emptyFn -}); -Ext.define("Ext.chart.axis.segmenter.Names", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.names", - renderer: function(b, a) { - return b - }, - diff: function(b, a, c) { - return Math.floor(a - b) - }, - align: function(c, b, a) { - return Math.floor(c) - }, - add: function(c, b, a) { - return c + b - }, - preferredStep: function(c, a, b, d) { - return { - unit: 1, - step: 1 - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Numeric", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.numeric", - isNumeric: true, - renderer: function(b, a) { - return b.toFixed(Math.max(0, a.majorTicks.unit.fixes)) - }, - diff: function(b, a, c) { - return Math.floor((a - b) / c.scale) - }, - align: function(c, b, a) { - return Math.floor(c / (a.scale * b)) * a.scale * b - }, - add: function(c, b, a) { - return c + b * a.scale - }, - preferredStep: function(c, b) { - var a = Math.floor(Math.log(b) * Math.LOG10E), - d = Math.pow(10, a); - b /= d; - if (b < 2) { - b = 2 - } else { - if (b < 5) { - b = 5 - } else { - if (b < 10) { - b = 10; - a++ - } - } - } - return { - unit: { - fixes: -a, - scale: d - }, - step: b - } - }, - exactStep: function(c, b) { - var a = Math.floor(Math.log(b) * Math.LOG10E), - d = Math.pow(10, a); - return { - unit: { - fixes: -a + (b % d === 0 ? 0 : 1), - scale: 1 - }, - step: b - } - }, - adjustByMajorUnit: function(e, g, c) { - var d = c[0], - b = c[1], - a = e * g, - f = d % a; - if (f !== 0) { - c[0] = d - f + (d < 0 ? -a : 0) - } - f = b % a; - if (f !== 0) { - c[1] = b - f + (b > 0 ? a : 0) - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Time", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.time", - config: { - step: null - }, - renderer: function(c, b) { - var a = Ext.Date; - switch (b.majorTicks.unit) { - case "y": - return a.format(c, "Y"); - case "mo": - return a.format(c, "Y-m"); - case "d": - return a.format(c, "Y-m-d") - } - return a.format(c, "Y-m-d\nH:i:s") - }, - from: function(a) { - return new Date(a) - }, - diff: function(b, a, c) { - if (isFinite(b)) { - b = new Date(b) - } - if (isFinite(a)) { - a = new Date(a) - } - return Ext.Date.diff(b, a, c) - }, - align: function(a, c, b) { - if (b === "d" && c >= 7) { - a = Ext.Date.align(a, "d", c); - a.setDate(a.getDate() - a.getDay() + 1); - return a - } else { - return Ext.Date.align(a, b, c) - } - }, - add: function(c, b, a) { - return Ext.Date.add(new Date(c), a, b) - }, - stepUnits: [ - [Ext.Date.YEAR, 1, 2, 5, 10, 20, 50, 100, 200, 500], - [Ext.Date.MONTH, 1, 3, 6], - [Ext.Date.DAY, 1, 7, 14], - [Ext.Date.HOUR, 1, 6, 12], - [Ext.Date.MINUTE, 1, 5, 15, 30], - [Ext.Date.SECOND, 1, 5, 15, 30], - [Ext.Date.MILLI, 1, 2, 5, 10, 20, 50, 100, 200, 500] - ], - preferredStep: function(b, e) { - if (this.getStep()) { - return this.getStep() - } - var f = new Date(+b), - g = new Date(+b + Math.ceil(e)), - d = this.stepUnits, - l, k, h, c, a; - for (c = 0; c < d.length; c++) { - k = d[c][0]; - h = this.diff(f, g, k); - if (h > 0) { - for (a = 1; a < d[c].length; a++) { - if (h <= d[c][a]) { - l = { - unit: k, - step: d[c][a] - }; - break - } - } - if (!l) { - c--; - l = { - unit: d[c][0], - step: 1 - } - } - break - } - } - if (!l) { - l = { - unit: Ext.Date.DAY, - step: 1 - } - } - return l - } -}); -Ext.define("Ext.chart.axis.layout.Layout", { - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - axis: null - }, - constructor: function(a) { - this.mixins.observable.constructor.call(this, a) - }, - processData: function(b) { - var e = this, - c = e.getAxis(), - f = c.getDirection(), - g = c.boundSeries, - a, d; - if (b) { - b["coordinate" + f]() - } else { - for (a = 0, d = g.length; a < d; a++) { - g[a]["coordinate" + f]() - } - } - }, - calculateMajorTicks: function(a) { - var f = this, - e = a.attr, - d = e.max - e.min, - i = d / Math.max(1, e.length) * (e.visibleMax - e.visibleMin), - h = e.min + d * e.visibleMin, - b = e.min + d * e.visibleMax, - g = e.estStepSize * i, - c = f.snapEnds(a, e.min, e.max, g); - if (c) { - f.trimByRange(a, c, h, b); - a.majorTicks = c - } - }, - calculateMinorTicks: function(a) { - if (this.snapMinorEnds) { - a.minorTicks = this.snapMinorEnds(a) - } - }, - calculateLayout: function(b) { - var c = this, - a = b.attr; - if (a.length === 0) { - return null - } - if (a.majorTicks) { - c.calculateMajorTicks(b); - if (a.minorTicks) { - c.calculateMinorTicks(b) - } - } - }, - snapEnds: Ext.emptyFn, - trimByRange: function(b, f, i, a) { - var g = b.segmenter, - j = f.unit, - h = g.diff(f.from, i, j), - d = g.diff(f.from, a, j), - c = Math.max(0, Math.ceil(h / f.step)), - e = Math.min(f.steps, Math.floor(d / f.step)); - if (e < f.steps) { - f.to = g.add(f.from, e * f.step, j) - } - if (f.max > a) { - f.max = f.to - } - if (f.from < i) { - f.from = g.add(f.from, c * f.step, j); - while (f.from < i) { - c++; - f.from = g.add(f.from, f.step, j) - } - } - if (f.min < i) { - f.min = f.from - } - f.steps = e - c - } -}); -Ext.define("Ext.chart.axis.layout.Discrete", { - extend: "Ext.chart.axis.layout.Layout", - alias: "axisLayout.discrete", - isDiscrete: true, - processData: function() { - var f = this, - d = f.getAxis(), - c = d.boundSeries, - g = d.getDirection(), - b, e, a; - f.labels = []; - f.labelMap = {}; - for (b = 0, e = c.length; b < e; b++) { - a = c[b]; - if (a["get" + g + "Axis"]() === d) { - a["coordinate" + g]() - } - } - d.getSprites()[0].setAttributes({ - data: f.labels - }); - f.fireEvent("datachange", f.labels) - }, - calculateLayout: function(a) { - a.data = this.labels; - this.callParent([a]) - }, - calculateMajorTicks: function(a) { - var g = this, - f = a.attr, - d = a.data, - e = f.max - f.min, - j = e / Math.max(1, f.length) * (f.visibleMax - f.visibleMin), - i = f.min + e * f.visibleMin, - b = f.min + e * f.visibleMax, - h = f.estStepSize * j; - var c = g.snapEnds(a, Math.max(0, f.min), Math.min(f.max, d.length - 1), h); - if (c) { - g.trimByRange(a, c, i, b); - a.majorTicks = c - } - }, - snapEnds: function(e, d, a, b) { - b = Math.ceil(b); - var c = Math.floor((a - d) / b), - f = e.data; - return { - min: d, - max: a, - from: d, - to: c * b + d, - step: b, - steps: c, - unit: 1, - getLabel: function(g) { - return f[this.from + this.step * g] - }, - get: function(g) { - return this.from + this.step * g - } - } - }, - trimByRange: function(b, f, h, a) { - var i = f.unit, - g = Math.ceil((h - f.from) / i) * i, - d = Math.floor((a - f.from) / i) * i, - c = Math.max(0, Math.ceil(g / f.step)), - e = Math.min(f.steps, Math.floor(d / f.step)); - if (e < f.steps) { - f.to = e - } - if (f.max > a) { - f.max = f.to - } - if (f.from < h && f.step > 0) { - f.from = f.from + c * f.step * i; - while (f.from < h) { - c++; - f.from += f.step * i - } - } - if (f.min < h) { - f.min = f.from - } - f.steps = e - c - }, - getCoordFor: function(c, d, a, b) { - this.labels.push(c); - return this.labels.length - 1 - } -}); -Ext.define("Ext.chart.axis.layout.CombineDuplicate", { - extend: "Ext.chart.axis.layout.Discrete", - alias: "axisLayout.combineDuplicate", - getCoordFor: function(d, e, b, c) { - if (!(d in this.labelMap)) { - var a = this.labelMap[d] = this.labels.length; - this.labels.push(d); - return a - } - return this.labelMap[d] - } -}); -Ext.define("Ext.chart.axis.layout.Continuous", { - extend: "Ext.chart.axis.layout.Layout", - alias: "axisLayout.continuous", - isContinuous: true, - config: { - adjustMinimumByMajorUnit: false, - adjustMaximumByMajorUnit: false - }, - getCoordFor: function(c, d, a, b) { - return +c - }, - snapEnds: function(a, d, i, h) { - var f = a.segmenter, - c = this.getAxis(), - l = c.getMajorTickSteps(), - e = l && f.exactStep ? f.exactStep(d, (i - d) / l) : f.preferredStep(d, h), - k = e.unit, - b = e.step, - j = f.align(d, b, k), - g = (l || f.diff(d, i, k)) + 1; - return { - min: f.from(d), - max: f.from(i), - from: j, - to: f.add(j, g * b, k), - step: b, - steps: g, - unit: k, - get: function(m) { - return f.add(this.from, this.step * m, k) - } - } - }, - snapMinorEnds: function(a) { - var e = a.majorTicks, - m = this.getAxis().getMinorTickSteps(), - f = a.segmenter, - d = e.min, - i = e.max, - k = e.from, - l = e.unit, - b = e.step / m, - n = b * l.scale, - j = k - d, - c = Math.floor(j / n), - h = c + Math.floor((i - e.to) / n) + 1, - g = e.steps * m + h; - return { - min: d, - max: i, - from: d + j % n, - to: f.add(k, g * b, l), - step: b, - steps: g, - unit: l, - get: function(o) { - return (o % m + c + 1 !== 0) ? f.add(this.from, this.step * o, l) : null - } - } - } -}); -Ext.define("Ext.chart.axis.Axis", { - xtype: "axis", - mixins: { - observable: "Ext.mixin.Observable" - }, - requires: ["Ext.chart.axis.sprite.Axis", "Ext.chart.axis.segmenter.*", "Ext.chart.axis.layout.*"], - isAxis: true, - config: { - position: "bottom", - fields: [], - label: undefined, - grid: false, - limits: null, - renderer: null, - chart: null, - style: null, - margin: 0, - titleMargin: 4, - background: null, - minimum: NaN, - maximum: NaN, - reconcileRange: false, - minZoom: 1, - maxZoom: 10000, - layout: "continuous", - segmenter: "numeric", - hidden: false, - majorTickSteps: 0, - minorTickSteps: 0, - adjustByMajorUnit: true, - title: null, - increment: 0.5, - length: 0, - center: null, - radius: null, - totalAngle: Math.PI, - rotation: null, - labelInSpan: null, - visibleRange: [0, 1], - needHighPrecision: false, - linkedTo: null, - floating: null - }, - titleOffset: 0, - spriteAnimationCount: 0, - prevMin: 0, - prevMax: 1, - boundSeries: [], - sprites: null, - surface: null, - range: null, - xValues: [], - yValues: [], - masterAxis: null, - applyRotation: function(b) { - var a = Math.PI * 2; - return (b % a + Math.PI) % a - Math.PI - }, - updateRotation: function(b) { - var c = this.getSprites(), - a = this.getPosition(); - if (!this.getHidden() && a === "angular" && c[0]) { - c[0].setAttributes({ - baseRotation: b - }) - } - }, - applyTitle: function(c, b) { - var a; - if (Ext.isString(c)) { - c = { - text: c - } - } - if (!b) { - b = Ext.create("sprite.text", c); - if ((a = this.getSurface())) { - a.add(b) - } - } else { - b.setAttributes(c) - } - return b - }, - applyFloating: function(b, a) { - if (b === null) { - b = { - value: null, - alongAxis: null - } - } else { - if (Ext.isNumber(b)) { - b = { - value: b, - alongAxis: null - } - } - } - if (Ext.isObject(b)) { - if (a && a.alongAxis) { - delete this.getChart().getAxis(a.alongAxis).floatingAxes[this.getId()] - } - return b - } - return a - }, - constructor: function(a) { - var b = this, - c; - b.sprites = []; - b.labels = []; - b.floatingAxes = {}; - a = a || {}; - if (a.position === "angular") { - a.style = a.style || {}; - a.style.estStepSize = 1 - } - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.mixins.observable.constructor.apply(b, arguments) - }, - getAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "vertical"; - case "top": - case "bottom": - return "horizontal"; - case "radial": - return "radial"; - case "angular": - return "angular" - } - }, - getGridAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "horizontal"; - case "top": - case "bottom": - return "vertical"; - case "radial": - return "circular"; - case "angular": - return "radial" - } - }, - getSurface: function() { - var e = this, - d = e.getChart(); - if (d && !e.surface) { - var b = e.surface = d.getSurface(e.getId(), "axis"), - c = e.gridSurface = d.getSurface("main"), - a = e.getSprites()[0], - f = e.getGridAlignment(); - c.waitFor(b); - e.getGrid(); - if (e.getLimits() && f) { - f = f.replace("3d", ""); - e.limits = { - surface: d.getSurface("overlay"), - lines: new Ext.chart.Markers(), - titles: new Ext.draw.sprite.Instancing() - }; - e.limits.lines.setTemplate({ - xclass: "grid." + f - }); - e.limits.lines.getTemplate().setAttributes({ - strokeStyle: "black" - }, true); - e.limits.surface.add(e.limits.lines); - a.bindMarker(f + "-limit-lines", e.limits.lines); - e.limitTitleTpl = new Ext.draw.sprite.Text(); - e.limits.titles.setTemplate(e.limitTitleTpl); - e.limits.surface.add(e.limits.titles); - d.on("redraw", e.renderLimits, e) - } - } - return e.surface - }, - applyGrid: function(a) { - if (a === true) { - return {} - } - return a - }, - updateGrid: function(b) { - var e = this, - d = e.getChart(); - if (!d) { - e.on({ - chartattached: Ext.bind(e.updateGrid, e, [b]), - single: true - }); - return - } - var c = e.gridSurface, - a = e.getSprites()[0], - f = e.getGridAlignment(), - g; - if (b) { - g = e.gridSpriteEven; - if (!g) { - g = e.gridSpriteEven = new Ext.chart.Markers(); - g.setTemplate({ - xclass: "grid." + f - }); - c.add(g); - a.bindMarker(f + "-even", g) - } - if (Ext.isObject(b)) { - g.getTemplate().setAttributes(b); - if (Ext.isObject(b.even)) { - g.getTemplate().setAttributes(b.even) - } - } - g = e.gridSpriteOdd; - if (!g) { - g = e.gridSpriteOdd = new Ext.chart.Markers(); - g.setTemplate({ - xclass: "grid." + f - }); - c.add(g); - a.bindMarker(f + "-odd", g) - } - if (Ext.isObject(b)) { - g.getTemplate().setAttributes(b); - if (Ext.isObject(b.odd)) { - g.getTemplate().setAttributes(b.odd) - } - } - } - }, - renderLimits: function() { - this.getSprites()[0].renderLimits() - }, - getCoordFor: function(c, d, a, b) { - return this.getLayout().getCoordFor(c, d, a, b) - }, - applyPosition: function(a) { - return a.toLowerCase() - }, - applyLength: function(b, a) { - return b > 0 ? b : a - }, - applyLabel: function(b, a) { - if (!a) { - a = new Ext.draw.sprite.Text({}) - } - if (this.limitTitleTpl) { - this.limitTitleTpl.setAttributes(b) - } - a.setAttributes(b); - return a - }, - applyLayout: function(b, a) { - b = Ext.factory(b, null, a, "axisLayout"); - b.setAxis(this); - return b - }, - applySegmenter: function(a, b) { - a = Ext.factory(a, null, b, "segmenter"); - a.setAxis(this); - return a - }, - updateMinimum: function() { - this.range = null - }, - updateMaximum: function() { - this.range = null - }, - hideLabels: function() { - this.getSprites()[0].setDirty(true); - this.setLabel({ - hidden: true - }) - }, - showLabels: function() { - this.getSprites()[0].setDirty(true); - this.setLabel({ - hidden: false - }) - }, - renderFrame: function() { - this.getSurface().renderFrame() - }, - updateChart: function(d, b) { - var c = this, - a; - if (b) { - b.unregister(c); - b.un("serieschange", c.onSeriesChange, c); - b.un("redraw", c.renderLimits, c); - c.linkAxis(); - c.fireEvent("chartdetached", b, c) - } - if (d) { - d.on("serieschange", c.onSeriesChange, c); - c.surface = null; - a = c.getSurface(); - c.getLabel().setSurface(a); - a.add(c.getSprites()); - a.add(c.getTitle()); - d.register(c); - c.fireEvent("chartattached", d, c) - } - }, - applyBackground: function(a) { - var b = Ext.ClassManager.getByAlias("sprite.rect"); - return b.def.normalize(a) - }, - processData: function() { - this.getLayout().processData(); - this.range = null - }, - getDirection: function() { - return this.getChart().getDirectionForAxis(this.getPosition()) - }, - isSide: function() { - var a = this.getPosition(); - return a === "left" || a === "right" - }, - applyFields: function(a) { - return Ext.Array.from(a) - }, - applyVisibleRange: function(a, c) { - this.getChart(); - if (a[0] > a[1]) { - var b = a[0]; - a[0] = a[1]; - a[0] = b - } - if (a[1] === a[0]) { - a[1] += 1 / this.getMaxZoom() - } - if (a[1] > a[0] + 1) { - a[0] = 0; - a[1] = 1 - } else { - if (a[0] < 0) { - a[1] -= a[0]; - a[0] = 0 - } else { - if (a[1] > 1) { - a[0] -= a[1] - 1; - a[1] = 1 - } - } - } - if (c && a[0] === c[0] && a[1] === c[1]) { - return undefined - } - return a - }, - updateVisibleRange: function(a) { - this.fireEvent("visiblerangechange", this, a) - }, - onSeriesChange: function(e) { - var f = this, - b = e.getSeries(), - j = "get" + f.getDirection() + "Axis", - g = [], - c, d = b.length, - a, h; - for (c = 0; c < d; c++) { - if (this === b[c][j]()) { - g.push(b[c]) - } - } - f.boundSeries = g; - a = f.getLinkedTo(); - h = !Ext.isEmpty(a) && e.getAxis(a); - if (h) { - f.linkAxis(h) - } else { - f.getLayout().processData() - } - }, - linkAxis: function(a) { - var c = this; - - function b(f, d, e) { - e.getLayout()[f]("datachange", "onDataChange", d); - e[f]("rangechange", "onMasterAxisRangeChange", d) - } - if (c.masterAxis) { - b("un", c, c.masterAxis); - c.masterAxis = null - } - if (a) { - if (a.type !== this.type) { - Ext.Error.raise("Linked axes must be of the same type.") - } - b("on", c, a); - c.onDataChange(a.getLayout().labels); - c.onMasterAxisRangeChange(a, a.range); - c.setStyle(Ext.apply({}, c.config.style, a.config.style)); - c.setTitle(Ext.apply({}, c.config.title, a.config.title)); - c.setLabel(Ext.apply({}, c.config.label, a.config.label)); - c.masterAxis = a - } - }, - onDataChange: function(a) { - this.getLayout().labels = a - }, - onMasterAxisRangeChange: function(b, a) { - this.range = a - }, - applyRange: function(a) { - if (!a) { - return this.dataRange.slice(0) - } else { - return [a[0] === null ? this.dataRange[0] : a[0], a[1] === null ? this.dataRange[1] : a[1]] - } - }, - getRange: function() { - var m = this; - if (m.range) { - return m.range - } else { - if (m.masterAxis) { - return m.masterAxis.range - } - } - if (Ext.isNumber(m.getMinimum() + m.getMaximum())) { - return m.range = [m.getMinimum(), m.getMaximum()] - } - var d = Infinity, - n = -Infinity, - o = m.boundSeries, - h = m.getLayout(), - l = m.getSegmenter(), - p = m.getVisibleRange(), - b = "get" + m.getDirection() + "Range", - a, j, g, f, e, k; - for (e = 0, k = o.length; e < k; e++) { - f = o[e]; - var c = f[b](); - if (c) { - if (c[0] < d) { - d = c[0] - } - if (c[1] > n) { - n = c[1] - } - } - } - if (!isFinite(n)) { - n = m.prevMax - } - if (!isFinite(d)) { - d = m.prevMin - } - if (m.getLabelInSpan() || d === n) { - n += m.getIncrement(); - d -= m.getIncrement() - } - if (Ext.isNumber(m.getMinimum())) { - d = m.getMinimum() - } else { - m.prevMin = d - } - if (Ext.isNumber(m.getMaximum())) { - n = m.getMaximum() - } else { - m.prevMax = n - } - m.range = [Ext.Number.correctFloat(d), Ext.Number.correctFloat(n)]; - if (m.getReconcileRange()) { - m.reconcileRange() - } - if (m.getAdjustByMajorUnit() && l.adjustByMajorUnit && !m.getMajorTickSteps()) { - j = Ext.Object.chain(m.getSprites()[0].attr); - j.min = m.range[0]; - j.max = m.range[1]; - j.visibleMin = p[0]; - j.visibleMax = p[1]; - a = { - attr: j, - segmenter: l - }; - h.calculateLayout(a); - g = a.majorTicks; - if (g) { - l.adjustByMajorUnit(g.step, g.unit.scale, m.range); - j.min = m.range[0]; - j.max = m.range[1]; - delete a.majorTicks; - h.calculateLayout(a); - g = a.majorTicks; - l.adjustByMajorUnit(g.step, g.unit.scale, m.range) - } else { - if (!m.hasClearRangePending) { - m.hasClearRangePending = true; - m.getChart().on("layout", "clearRange", m) - } - } - } - if (!Ext.Array.equals(m.range, m.oldRange || [])) { - m.fireEvent("rangechange", m, m.range); - m.oldRange = m.range - } - return m.range - }, - clearRange: function() { - delete this.hasClearRangePending; - this.range = null - }, - reconcileRange: function() { - var e = this, - g = e.getChart().getAxes(), - f = e.getDirection(), - b, d, c, a; - if (!g) { - return - } - for (b = 0, d = g.length; b < d; b++) { - c = g[b]; - a = c.getRange(); - if (c === e || c.getDirection() !== f || !a || !c.getReconcileRange()) { - continue - } - if (a[0] < e.range[0]) { - e.range[0] = a[0] - } - if (a[1] > e.range[1]) { - e.range[1] = a[1] - } - } - }, - applyStyle: function(c, b) { - var a = Ext.ClassManager.getByAlias("sprite." + this.seriesType); - if (a && a.def) { - c = a.def.normalize(c) - } - b = Ext.apply(b || {}, c); - return b - }, - themeOnlyIfConfigured: { - grid: true - }, - updateTheme: function(d) { - var i = this, - k = d.getAxis(), - e = i.getPosition(), - o = i.getInitialConfig(), - c = i.defaultConfig, - g = i.getConfigurator().configs, - a = k.defaults, - n = k[e], - h = i.themeOnlyIfConfigured, - l, j, p, b, m, f; - k = Ext.merge({}, a, n); - for (l in k) { - j = k[l]; - f = g[l]; - if (j !== null && j !== undefined && f) { - m = o[l]; - p = Ext.isObject(j); - b = m === c[l]; - if (p) { - if (b && h[l]) { - continue - } - j = Ext.merge({}, j, m) - } - if (b || p) { - i[f.names.set](j) - } - } - } - }, - updateCenter: function(b) { - var e = this.getSprites(), - a = e[0], - d = b[0], - c = b[1]; - if (a) { - a.setAttributes({ - centerX: d, - centerY: c - }) - } - if (this.gridSpriteEven) { - this.gridSpriteEven.getTemplate().setAttributes({ - translationX: d, - translationY: c, - rotationCenterX: d, - rotationCenterY: c - }) - } - if (this.gridSpriteOdd) { - this.gridSpriteOdd.getTemplate().setAttributes({ - translationX: d, - translationY: c, - rotationCenterX: d, - rotationCenterY: c - }) - } - }, - getSprites: function() { - if (!this.getChart()) { - return - } - var i = this, - e = i.getRange(), - f = i.getPosition(), - g = i.getChart(), - c = g.getAnimation(), - d, a, b = i.getLength(), - h = i.superclass; - if (c === false) { - c = { - duration: 0 - } - } - if (e) { - a = Ext.applyIf({ - position: f, - axis: i, - min: e[0], - max: e[1], - length: b, - grid: i.getGrid(), - hidden: i.getHidden(), - titleOffset: i.titleOffset, - layout: i.getLayout(), - segmenter: i.getSegmenter(), - totalAngle: i.getTotalAngle(), - label: i.getLabel() - }, i.getStyle()); - if (!i.sprites.length) { - while (!h.xtype) { - h = h.superclass - } - d = Ext.create("sprite." + h.xtype, a); - d.fx.setCustomDurations({ - baseRotation: 0 - }); - d.fx.on("animationstart", "onAnimationStart", i); - d.fx.on("animationend", "onAnimationEnd", i); - d.setLayout(i.getLayout()); - d.setSegmenter(i.getSegmenter()); - d.setLabel(i.getLabel()); - i.sprites.push(d); - i.updateTitleSprite() - } else { - d = i.sprites[0]; - d.setAnimation(c); - d.setAttributes(a) - } - if (i.getRenderer()) { - d.setRenderer(i.getRenderer()) - } - } - return i.sprites - }, - updateTitleSprite: function() { - var f = this, - b = f.getLength(); - if (!f.sprites[0] || !Ext.isNumber(b)) { - return - } - var h = this.sprites[0].thickness, - a = f.getSurface(), - g = f.getTitle(), - e = f.getPosition(), - c = f.getMargin(), - i = f.getTitleMargin(), - d = a.roundPixel(b / 2); - if (g) { - switch (e) { - case "top": - g.setAttributes({ - x: d, - y: c + i / 2, - textBaseline: "top", - textAlign: "center" - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().height + i; - break; - case "bottom": - g.setAttributes({ - x: d, - y: h + i / 2, - textBaseline: "top", - textAlign: "center" - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().height + i; - break; - case "left": - g.setAttributes({ - x: c + i / 2, - y: d, - textBaseline: "top", - textAlign: "center", - rotationCenterX: c + i / 2, - rotationCenterY: d, - rotationRads: -Math.PI / 2 - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().width + i; - break; - case "right": - g.setAttributes({ - x: h - c + i / 2, - y: d, - textBaseline: "bottom", - textAlign: "center", - rotationCenterX: h + i / 2, - rotationCenterY: d, - rotationRads: Math.PI / 2 - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().width + i; - break - } - } - }, - onThicknessChanged: function() { - this.getChart().onThicknessChanged() - }, - getThickness: function() { - if (this.getHidden()) { - return 0 - } - return (this.sprites[0] && this.sprites[0].thickness || 1) + this.titleOffset + this.getMargin() - }, - onAnimationStart: function() { - this.spriteAnimationCount++; - if (this.spriteAnimationCount === 1) { - this.fireEvent("animationstart", this) - } - }, - onAnimationEnd: function() { - this.spriteAnimationCount--; - if (this.spriteAnimationCount === 0) { - this.fireEvent("animationend", this) - } - }, - getItemId: function() { - return this.getId() - }, - getAncestorIds: function() { - return [this.getChart().getId()] - }, - isXType: function(a) { - return a === "axis" - }, - resolveListenerScope: function(e) { - var d = this, - a = Ext._namedScopes[e], - c = d.getChart(), - b; - if (!a) { - b = c ? c.resolveListenerScope(e, false) : (e || d) - } else { - if (a.isThis) { - b = d - } else { - if (a.isController) { - b = c ? c.resolveListenerScope(e, false) : d - } else { - if (a.isSelf) { - b = c ? c.resolveListenerScope(e, false) : d; - if (b === c && !c.getInheritedConfig("defaultListenerScope")) { - b = d - } - } - } - } - } - return b - }, - destroy: function() { - var a = this; - a.setChart(null); - a.surface.destroy(); - a.surface = null; - a.callParent() - } -}); -Ext.define("Ext.chart.LegendBase", { - extend: "Ext.view.View", - config: { - tpl: ['
', '', '
', "', "{name}", "
", "
", "
"], - nodeContainerSelector: "div." + Ext.baseCSSPrefix + "legend-container", - itemSelector: "div." + Ext.baseCSSPrefix + "legend-item", - docked: "bottom" - }, - setDocked: function(d) { - var c = this, - a = c.ownerCt, - b; - c.docked = d; - switch (d) { - case "top": - case "bottom": - c.addCls(Ext.baseCSSPrefix + "horizontal"); - b = "hbox"; - break; - case "left": - case "right": - c.removeCls(Ext.baseCSSPrefix + "horizontal"); - b = "vbox"; - break - } - if (a) { - a.setDocked(d) - } - }, - setStore: function(a) { - this.bindStore(a) - }, - clearViewEl: function() { - this.callParent(arguments); - Ext.removeNode(this.getNodeContainer()) - }, - onItemClick: function(a, c, b, d) { - this.callParent(arguments); - this.toggleItem(b) - } -}); -Ext.define("Ext.chart.Legend", { - xtype: "legend", - extend: "Ext.chart.LegendBase", - config: { - baseCls: Ext.baseCSSPrefix + "legend", - padding: 5, - rect: null, - disableSelection: true, - toggleable: true - }, - toggleItem: function(c) { - if (!this.getToggleable()) { - return - } - var b = this.getStore(), - h = 0, - e, g = true, - d, f, a; - if (b) { - f = b.getCount(); - for (d = 0; d < f; d++) { - a = b.getAt(d); - if (a.get("disabled")) { - h++ - } - } - g = f - h > 1; - a = b.getAt(c); - if (a) { - e = a.get("disabled"); - if (e || g) { - a.set("disabled", !e) - } - } - } - } -}); -Ext.define("Ext.chart.AbstractChart", { - extend: "Ext.draw.Container", - requires: ["Ext.chart.theme.Default", "Ext.chart.series.Series", "Ext.chart.interactions.Abstract", "Ext.chart.axis.Axis", "Ext.data.StoreManager", "Ext.chart.Legend", "Ext.data.Store"], - isChart: true, - defaultBindProperty: "store", - config: { - store: "ext-empty-store", - theme: "default", - style: null, - animation: !Ext.isIE8, - series: [], - axes: [], - legend: null, - colors: null, - insetPadding: { - top: 10, - left: 10, - right: 10, - bottom: 10 - }, - background: null, - interactions: [], - mainRect: null, - resizeHandler: null, - highlightItem: null - }, - animationSuspendCount: 0, - chartLayoutSuspendCount: 0, - axisThicknessSuspendCount: 0, - isThicknessChanged: false, - surfaceZIndexes: { - background: 0, - main: 1, - grid: 2, - series: 3, - axis: 4, - chart: 5, - overlay: 6, - events: 7 - }, - constructor: function(a) { - var b = this; - b.itemListeners = {}; - b.surfaceMap = {}; - b.chartComponents = {}; - b.isInitializing = true; - b.suspendChartLayout(); - b.animationSuspendCount++; - b.callParent(arguments); - delete b.isInitializing; - b.getSurface("main"); - b.getSurface("chart").setFlipRtlText(b.getInherited().rtl); - b.getSurface("overlay").waitFor(b.getSurface("series")); - b.animationSuspendCount--; - b.resumeChartLayout() - }, - applyAnimation: function(a, b) { - if (!a) { - a = { - duration: 0 - } - } else { - if (a === true) { - a = { - easing: "easeInOut", - duration: 500 - } - } - } - return b ? Ext.apply({}, a, b) : a - }, - getAnimation: function() { - if (this.animationSuspendCount) { - return { - duration: 0 - } - } else { - return this.callParent() - } - }, - applyInsetPadding: function(b, a) { - if (!Ext.isObject(b)) { - return Ext.util.Format.parseBox(b) - } else { - if (!a) { - return b - } else { - return Ext.apply(a, b) - } - } - }, - suspendAnimation: function() { - var d = this, - c = d.getSeries(), - e = c.length, - b = -1, - a; - d.animationSuspendCount++; - if (d.animationSuspendCount === 1) { - while (++b < e) { - a = c[b]; - a.setAnimation(a.getAnimation()) - } - } - }, - resumeAnimation: function() { - var d = this, - c = d.getSeries(), - f = c.length, - b = -1, - a, e; - d.animationSuspendCount--; - if (d.animationSuspendCount === 0) { - while (++b < f) { - a = c[b]; - e = a.getAnimation(); - a.setAnimation(e.duration && e || d.getAnimation()) - } - } - }, - suspendChartLayout: function() { - this.chartLayoutSuspendCount++; - if (this.chartLayoutSuspendCount === 1) { - if (this.scheduledLayoutId) { - this.layoutInSuspension = true; - this.cancelChartLayout() - } else { - this.layoutInSuspension = false - } - } - }, - resumeChartLayout: function() { - this.chartLayoutSuspendCount--; - if (this.chartLayoutSuspendCount === 0) { - if (this.layoutInSuspension) { - this.scheduleLayout() - } - } - }, - cancelChartLayout: function() { - if (this.scheduledLayoutId) { - Ext.draw.Animator.cancel(this.scheduledLayoutId); - this.scheduledLayoutId = null - } - }, - scheduleLayout: function() { - var a = this; - if (a.allowSchedule() && !a.scheduledLayoutId) { - a.scheduledLayoutId = Ext.draw.Animator.schedule("doScheduleLayout", a) - } - }, - allowSchedule: function() { - return true - }, - doScheduleLayout: function() { - if (this.chartLayoutSuspendCount) { - this.layoutInSuspension = true - } else { - this.performLayout() - } - }, - suspendThicknessChanged: function() { - this.axisThicknessSuspendCount++ - }, - resumeThicknessChanged: function() { - if (this.axisThicknessSuspendCount > 0) { - this.axisThicknessSuspendCount--; - if (this.axisThicknessSuspendCount === 0 && this.isThicknessChanged) { - this.onThicknessChanged() - } - } - }, - onThicknessChanged: function() { - if (this.axisThicknessSuspendCount === 0) { - this.isThicknessChanged = false; - this.performLayout() - } else { - this.isThicknessChanged = true - } - }, - applySprites: function(b) { - var a = this.getSurface("chart"); - b = Ext.Array.from(b); - a.removeAll(true); - a.add(b); - return b - }, - initItems: function() { - var a = this.items, - b, d, c; - if (a && !a.isMixedCollection) { - this.items = []; - a = Ext.Array.from(a); - for (b = 0, d = a.length; b < d; b++) { - c = a[b]; - if (c.type) { - Ext.raise("To add custom sprites to the chart use the 'sprites' config.") - } else { - this.items.push(c) - } - } - } - this.callParent() - }, - applyBackground: function(c, e) { - var b = this.getSurface("background"), - d, a, f; - if (c) { - if (e) { - d = e.attr.width; - a = e.attr.height; - f = e.type === (c.type || "rect") - } - if (c.isSprite) { - e = c - } else { - if (c.type === "image" && Ext.isString(c.src)) { - if (f) { - e.setAttributes({ - src: c.src - }) - } else { - b.remove(e, true); - e = b.add(c) - } - } else { - if (f) { - e.setAttributes({ - fillStyle: c - }) - } else { - b.remove(e, true); - e = b.add({ - type: "rect", - fillStyle: c, - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0 - } - } - }) - } - } - } - } - if (d && a) { - e.setAttributes({ - width: d, - height: a - }) - } - e.setAnimation(this.getAnimation()); - return e - }, - getLegendStore: function() { - return this.legendStore - }, - refreshLegendStore: function() { - if (this.getLegendStore()) { - var d, e, c = this.getSeries(), - b, a = []; - if (c) { - for (d = 0, e = c.length; d < e; d++) { - b = c[d]; - if (b.getShowInLegend()) { - b.provideLegendInfo(a) - } - } - } - this.getLegendStore().setData(a) - } - }, - resetLegendStore: function() { - var c = this.getLegendStore(), - e, d, a, b; - if (c) { - e = this.getLegendStore().getData().items; - for (d = 0, a = e.length; d < a; d++) { - b = e[d]; - b.beginEdit(); - b.set("disabled", false); - b.commit() - } - } - }, - onUpdateLegendStore: function(b, a) { - var d = this.getSeries(), - c; - if (a && d) { - c = d.map[a.get("series")]; - if (c) { - c.setHiddenByIndex(a.get("index"), a.get("disabled")); - this.redraw() - } - } - }, - defaultResizeHandler: function(a) { - this.scheduleLayout(); - return false - }, - applyMainRect: function(a, b) { - if (!b) { - return a - } - this.getSeries(); - this.getAxes(); - if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]) { - return b - } else { - return a - } - }, - register: function(a) { - var b = this.chartComponents, - c = a.getId(); - b[c] = a - }, - unregister: function(a) { - var b = this.chartComponents, - c = a.getId(); - delete b[c] - }, - get: function(a) { - return this.chartComponents[a] - }, - getAxis: function(a) { - if (a instanceof Ext.chart.axis.Axis) { - return a - } else { - if (Ext.isNumber(a)) { - return this.getAxes()[a] - } else { - if (Ext.isString(a)) { - return this.get(a) - } - } - } - }, - getSurface: function(b, c) { - b = b || "main"; - c = c || b; - var d = this, - a = this.callParent([b]), - f = d.surfaceZIndexes, - e = d.surfaceMap; - if (c in f) { - a.element.setStyle("zIndex", f[c]) - } - if (!e[c]) { - e[c] = [] - } - if (Ext.Array.indexOf(e[c], a) < 0) { - a.type = c; - e[c].push(a); - a.on("destroy", d.forgetSurface, d) - } - return a - }, - forgetSurface: function(a) { - var d = this.surfaceMap; - if (!d || this.isDestroying) { - return - } - var c = d[a.type], - b = c ? Ext.Array.indexOf(c, a) : -1; - if (b >= 0) { - c.splice(b, 1) - } - }, - applyAxes: function(b, k) { - var l = this, - g = { - left: "right", - right: "left" - }, - m = [], - c, d, e, a, f, h, j; - l.animationSuspendCount++; - l.getStore(); - if (!k) { - k = []; - k.map = {} - } - j = k.map; - m.map = {}; - b = Ext.Array.from(b, true); - for (f = 0, h = b.length; f < h; f++) { - c = b[f]; - if (!c) { - continue - } - if (c instanceof Ext.chart.axis.Axis) { - d = j[c.getId()]; - c.setChart(l) - } else { - c = Ext.Object.chain(c); - e = c.linkedTo; - a = c.id; - if (Ext.isNumber(e)) { - c = Ext.merge({}, b[e], c) - } else { - if (Ext.isString(e)) { - Ext.Array.each(b, function(i) { - if (i.id === c.linkedTo) { - c = Ext.merge({}, i, c); - return false - } - }) - } - } - c.id = a; - c.chart = l; - if (l.getInherited().rtl) { - c.position = g[c.position] || c.position - } - a = c.getId && c.getId() || c.id; - c = Ext.factory(c, null, d = j[a], "axis") - } - if (c) { - m.push(c); - m.map[c.getId()] = c; - if (!d) { - c.on("animationstart", "onAnimationStart", l); - c.on("animationend", "onAnimationEnd", l) - } - } - } - for (f in j) { - if (!m.map[f]) { - j[f].destroy() - } - } - l.animationSuspendCount--; - return m - }, - updateAxes: function() { - if (!this.isDestroying) { - this.scheduleLayout() - } - }, - circularCopyArray: function(e, f, d) { - var c = [], - b, a = e && e.length; - if (a) { - for (b = 0; b < d; b++) { - c.push(e[(f + b) % a]) - } - } - return c - }, - circularCopyObject: function(f, g, d) { - var c = this, - b, e, a = {}; - if (d) { - for (b in f) { - if (f.hasOwnProperty(b)) { - e = f[b]; - if (Ext.isArray(e)) { - a[b] = c.circularCopyArray(e, g, d) - } else { - a[b] = e - } - } - } - } - return a - }, - getColors: function() { - var b = this, - a = b.config.colors, - c = b.getTheme(); - if (Ext.isArray(a) && a.length > 0) { - a = b.applyColors(a) - } - return a || (c && c.getColors()) - }, - applyColors: function(a) { - a = Ext.Array.map(a, function(b) { - if (Ext.isString(b)) { - return b - } else { - return b.toString() - } - }); - return a - }, - updateColors: function(c) { - var k = this, - e = k.getTheme(), - a = c || (e && e.getColors()), - l = 0, - f = k.getSeries(), - d = f && f.length, - g, j, b, h; - if (a.length) { - for (g = 0; g < d; g++) { - j = f[g]; - h = j.themeColorCount(); - b = k.circularCopyArray(a, l, h); - l += h; - j.updateChartColors(b) - } - } - k.refreshLegendStore() - }, - applyTheme: function(a) { - if (a && a.isTheme) { - return a - } - return Ext.Factory.chartTheme(a) - }, - updateTheme: function(g) { - var e = this, - f = e.getAxes(), - d = e.getSeries(), - a = e.getColors(), - c, b; - e.updateChartTheme(g); - for (b = 0; b < f.length; b++) { - f[b].updateTheme(g) - } - for (b = 0; b < d.length; b++) { - c = d[b]; - c.updateTheme(g) - } - e.updateSpriteTheme(g); - e.updateColors(a); - e.redraw() - }, - themeOnlyIfConfigured: {}, - updateChartTheme: function(c) { - var i = this, - k = c.getChart(), - n = i.getInitialConfig(), - b = i.defaultConfig, - e = i.getConfigurator().configs, - f = k.defaults, - g = k[i.xtype], - h = i.themeOnlyIfConfigured, - l, j, o, a, m, d; - k = Ext.merge({}, f, g); - for (l in k) { - j = k[l]; - d = e[l]; - if (j !== null && j !== undefined && d) { - m = n[l]; - o = Ext.isObject(j); - a = m === b[l]; - if (o) { - if (a && h[l]) { - continue - } - j = Ext.merge({}, j, m) - } - if (a || o) { - i[d.names.set](j) - } - } - } - }, - updateSpriteTheme: function(c) { - this.getSprites(); - var j = this, - e = j.getSurface("chart"), - h = e.getItems(), - m = c.getSprites(), - k, a, l, f, d, b, g; - for (b = 0, g = h.length; b < g; b++) { - k = h[b]; - a = m[k.type]; - if (a) { - f = {}; - d = k.type === "text"; - for (l in a) { - if (!(l in k.config)) { - if (!(d && l.indexOf("font") === 0 && k.config.font)) { - f[l] = a[l] - } - } - } - k.setAttributes(f) - } - } - }, - addSeries: function(b) { - var a = this.getSeries(); - Ext.Array.push(a, b); - this.setSeries(a) - }, - removeSeries: function(d) { - d = Ext.Array.from(d); - var b = this.getSeries(), - f = [], - a = d.length, - g = {}, - c, e; - for (c = 0; c < a; c++) { - e = d[c]; - if (typeof e !== "string") { - e = e.getId() - } - g[e] = true - } - for (c = 0, a = b.length; c < a; c++) { - if (!g[b[c].getId()]) { - f.push(b[c]) - } - } - this.setSeries(f) - }, - applySeries: function(e, d) { - var g = this, - j = [], - h, a, c, f, b; - g.animationSuspendCount++; - g.getAxes(); - if (d) { - h = d.map - } else { - d = []; - h = d.map = {} - } - j.map = {}; - e = Ext.Array.from(e, true); - for (c = 0, f = e.length; c < f; c++) { - b = e[c]; - if (!b) { - continue - } - a = h[b.getId && b.getId() || b.id]; - if (b instanceof Ext.chart.series.Series) { - if (a && a !== b) { - a.destroy() - } - b.setChart(g) - } else { - if (Ext.isObject(b)) { - if (a) { - a.setConfig(b); - b = a - } else { - if (Ext.isString(b)) { - b = { - type: b - } - } - b.chart = g; - b = Ext.create(b.xclass || ("series." + b.type), b); - b.on("animationstart", "onAnimationStart", g); - b.on("animationend", "onAnimationEnd", g) - } - } - } - j.push(b); - j.map[b.getId()] = b - } - for (c in h) { - if (!j.map[h[c].getId()]) { - h[c].destroy() - } - } - g.animationSuspendCount--; - return j - }, - applyLegend: function(b, a) { - return Ext.factory(b, Ext.chart.Legend, a) - }, - updateLegend: function(b, a) { - if (a) { - a.destroy() - } - if (b) { - this.getItems(); - this.legendStore = new Ext.data.Store({ - autoDestroy: true, - fields: ["id", "name", "mark", "disabled", "series", "index"] - }); - b.setStore(this.legendStore); - this.refreshLegendStore(); - this.legendStore.on("update", "onUpdateLegendStore", this) - } - }, - updateSeries: function(b, a) { - var c = this; - if (c.isDestroying) { - return - } - c.animationSuspendCount++; - c.fireEvent("serieschange", c, b, a); - c.refreshLegendStore(); - if (!Ext.isEmpty(b)) { - c.updateTheme(c.getTheme()) - } - c.scheduleLayout(); - c.animationSuspendCount-- - }, - applyInteractions: function(h, d) { - if (!d) { - d = []; - d.map = {} - } - var g = this, - a = [], - c = d.map, - e, f, b; - a.map = {}; - h = Ext.Array.from(h, true); - for (e = 0, f = h.length; e < f; e++) { - b = h[e]; - if (!b) { - continue - } - b = Ext.factory(b, null, c[b.getId && b.getId() || b.id], "interaction"); - if (b) { - b.setChart(g); - a.push(b); - a.map[b.getId()] = b - } - } - for (e in c) { - if (!a.map[e]) { - c[e].destroy() - } - } - return a - }, - getInteraction: function(e) { - var f = this.getInteractions(), - a = f && f.length, - c = null, - b, d; - if (a) { - for (d = 0; d < a; ++d) { - b = f[d]; - if (b.type === e) { - c = b; - break - } - } - } - return c - }, - applyStore: function(a) { - return a && Ext.StoreManager.lookup(a) - }, - updateStore: function(a, c) { - var b = this; - if (c) { - c.un({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: b, - order: "after" - }); - if (c.autoDestroy) { - c.destroy() - } - } - if (a) { - a.on({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: b, - order: "after" - }) - } - b.fireEvent("storechange", b, a, c); - b.onDataChanged() - }, - redraw: function() { - this.fireEvent("redraw", this) - }, - performLayout: function() { - var d = this, - b = d.getChartSize(true), - c = [0, 0, b.width, b.height], - a = d.getBackground(); - d.hasFirstLayout = true; - d.fireEvent("layout", d); - d.cancelChartLayout(); - d.getSurface("background").setRect(c); - d.getSurface("chart").setRect(c); - a.setAttributes({ - width: b.width, - height: b.height - }) - }, - getChartSize: function(b) { - var a = this; - if (b) { - a.chartSize = null - } - return a.chartSize || (a.chartSize = a.innerElement.getSize()) - }, - getEventXY: function(a) { - return this.getSurface().getEventXY(a) - }, - getItemForPoint: function(h, g) { - var f = this, - a = f.getSeries(), - e = f.getMainRect(), - d = a.length, - b = f.hasFirstLayout ? d - 1 : -1, - c, j; - if (!(e && h >= 0 && h <= e[2] && g >= 0 && g <= e[3])) { - return null - } - for (; b >= 0; b--) { - c = a[b]; - j = c.getItemForPoint(h, g); - if (j) { - return j - } - } - return null - }, - getItemsForPoint: function(h, g) { - var f = this, - a = f.getSeries(), - d = a.length, - b = f.hasFirstLayout ? d - 1 : -1, - e = [], - c, j; - for (; b >= 0; b--) { - c = a[b]; - j = c.getItemForPoint(h, g); - if (j) { - e.push(j) - } - } - return e - }, - onAnimationStart: function() { - this.fireEvent("animationstart", this) - }, - onAnimationEnd: function() { - this.fireEvent("animationend", this) - }, - onDataChanged: function() { - var d = this; - if (d.isInitializing) { - return - } - var c = d.getMainRect(), - a = d.getStore(), - b = d.getSeries(), - e = d.getAxes(); - if (!a || !e || !b) { - return - } - if (!c) { - d.on({ - redraw: d.onDataChanged, - scope: d, - single: true - }); - return - } - d.processData(); - d.redraw() - }, - recordCount: 0, - processData: function() { - var g = this, - e = g.getStore().getCount(), - c = g.getSeries(), - f = c.length, - d = false, - b = 0, - a; - for (; b < f; b++) { - a = c[b]; - a.processData(); - if (!d && a.isStoreDependantColorCount) { - d = true - } - } - if (d && e > g.recordCount) { - g.updateColors(g.getColors()); - g.recordCount = e - } - }, - bindStore: function(a) { - this.setStore(a) - }, - applyHighlightItem: function(f, a) { - if (f === a) { - return - } - if (Ext.isObject(f) && Ext.isObject(a)) { - var e = f, - d = a, - c = e.sprite && (e.sprite[0] || e.sprite), - b = d.sprite && (d.sprite[0] || d.sprite); - if (c === b && e.index === d.index) { - return - } - } - return f - }, - updateHighlightItem: function(b, a) { - if (a) { - a.series.setAttributesForItem(a, { - highlighted: false - }) - } - if (b) { - b.series.setAttributesForItem(b, { - highlighted: true - }); - this.fireEvent("itemhighlight", this, b, a) - } - this.fireEvent("itemhighlightchange", this, b, a) - }, - destroyChart: function() { - var f = this, - d = f.getLegend(), - g = f.getAxes(), - c = f.getSeries(), - h = f.getInteractions(), - b = [], - a, e; - f.surfaceMap = null; - for (a = 0, e = h.length; a < e; a++) { - h[a].destroy() - } - for (a = 0, e = g.length; a < e; a++) { - g[a].destroy() - } - for (a = 0, e = c.length; a < e; a++) { - c[a].destroy() - } - f.setInteractions(b); - f.setAxes(b); - f.setSeries(b); - if (d) { - d.destroy(); - f.setLegend(null) - } - f.legendStore = null; - f.setStore(null); - f.cancelChartLayout() - }, - getRefItems: function(b) { - var g = this, - e = g.getSeries(), - h = g.getAxes(), - a = g.getInteractions(), - c = [], - d, f; - for (d = 0, f = e.length; d < f; d++) { - c.push(e[d]); - if (e[d].getRefItems) { - c.push.apply(c, e[d].getRefItems(b)) - } - } - for (d = 0, f = h.length; d < f; d++) { - c.push(h[d]); - if (h[d].getRefItems) { - c.push.apply(c, h[d].getRefItems(b)) - } - } - for (d = 0, f = a.length; d < f; d++) { - c.push(a[d]); - if (a[d].getRefItems) { - c.push.apply(c, a[d].getRefItems(b)) - } - } - return c - } -}); -Ext.define("Ext.chart.overrides.AbstractChart", { - override: "Ext.chart.AbstractChart", - updateLegend: function(b, a) { - var c; - this.callParent([b, a]); - if (b) { - c = b.docked; - this.addDocked({ - dock: c, - xtype: "panel", - shrinkWrap: true, - scrollable: true, - layout: { - type: c === "top" || c === "bottom" ? "hbox" : "vbox", - pack: "center" - }, - items: b, - cls: Ext.baseCSSPrefix + "legend-panel" - }) - } - }, - performLayout: function() { - if (this.isVisible(true)) { - return this.callParent() - } - this.cancelChartLayout(); - return false - }, - afterComponentLayout: function(c, a, b, d) { - this.callParent([c, a, b, d]); - this.scheduleLayout() - }, - allowSchedule: function() { - return this.rendered - }, - onDestroy: function() { - this.destroyChart(); - this.callParent(arguments) - } -}); -Ext.define("Ext.chart.grid.HorizontalGrid", { - extend: "Ext.draw.sprite.Sprite", - alias: "grid.horizontal", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number" - }, - defaults: { - x: 0, - y: 0, - width: 1, - height: 1, - strokeStyle: "#DDD" - } - } - }, - render: function(b, c, e) { - var a = this.attr, - f = b.roundPixel(a.y), - d = c.lineWidth * 0.5; - c.beginPath(); - c.rect(e[0] - b.matrix.getDX(), f + d, +e[2], a.height); - c.fill(); - c.beginPath(); - c.moveTo(e[0] - b.matrix.getDX(), f + d); - c.lineTo(e[0] + e[2] - b.matrix.getDX(), f + d); - c.stroke() - } -}); -Ext.define("Ext.chart.grid.VerticalGrid", { - extend: "Ext.draw.sprite.Sprite", - alias: "grid.vertical", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number" - }, - defaults: { - x: 0, - y: 0, - width: 1, - height: 1, - strokeStyle: "#DDD" - } - } - }, - render: function(c, d, f) { - var b = this.attr, - a = c.roundPixel(b.x), - e = d.lineWidth * 0.5; - d.beginPath(); - d.rect(a - e, f[1] - c.matrix.getDY(), b.width, f[3]); - d.fill(); - d.beginPath(); - d.moveTo(a - e, f[1] - c.matrix.getDY()); - d.lineTo(a - e, f[1] + f[3] - c.matrix.getDY()); - d.stroke() - } -}); -Ext.define("Ext.chart.CartesianChart", { - extend: "Ext.chart.AbstractChart", - alternateClassName: "Ext.chart.Chart", - requires: ["Ext.chart.grid.HorizontalGrid", "Ext.chart.grid.VerticalGrid"], - xtype: ["cartesian", "chart"], - isCartesian: true, - config: { - flipXY: false, - innerRect: [0, 0, 1, 1], - innerPadding: { - top: 0, - left: 0, - right: 0, - bottom: 0 - } - }, - applyInnerPadding: function(b, a) { - if (!Ext.isObject(b)) { - return Ext.util.Format.parseBox(b) - } else { - if (!a) { - return b - } else { - return Ext.apply(a, b) - } - } - }, - getDirectionForAxis: function(a) { - var b = this.getFlipXY(); - if (a === "left" || a === "right") { - if (b) { - return "X" - } else { - return "Y" - } - } else { - if (b) { - return "Y" - } else { - return "X" - } - } - }, - performLayout: function() { - var A = this; - A.animationSuspendCount++; - if (A.callParent() === false) { - --A.animationSuspendCount; - return - } - A.suspendThicknessChanged(); - var d = A.getSurface("chart").getRect(), - o = d[2], - n = d[3], - z = A.getAxes(), - b, q = A.getSeries(), - h, l, a, f = A.getInsetPadding(), - v = A.getInnerPadding(), - r, c, e = Ext.apply({}, f), - u, p, s, k, m, y, t, x, g, j = A.getInherited().rtl, - w = A.getFlipXY(); - if (o <= 0 || n <= 0) { - return - } - for (x = 0; x < z.length; x++) { - b = z[x]; - l = b.getSurface(); - m = b.getFloating(); - y = m ? m.value : null; - a = b.getThickness(); - switch (b.getPosition()) { - case "top": - l.setRect([0, e.top + 1, o, a]); - break; - case "bottom": - l.setRect([0, n - (e.bottom + a), o, a]); - break; - case "left": - l.setRect([e.left, 0, a, n]); - break; - case "right": - l.setRect([o - (e.right + a), 0, a, n]); - break - } - if (y === null) { - e[b.getPosition()] += a - } - } - o -= e.left + e.right; - n -= e.top + e.bottom; - u = [e.left, e.top, o, n]; - e.left += v.left; - e.top += v.top; - e.right += v.right; - e.bottom += v.bottom; - p = o - v.left - v.right; - s = n - v.top - v.bottom; - A.setInnerRect([e.left, e.top, p, s]); - if (p <= 0 || s <= 0) { - return - } - A.setMainRect(u); - A.getSurface().setRect(u); - for (x = 0, g = A.surfaceMap.grid && A.surfaceMap.grid.length; x < g; x++) { - c = A.surfaceMap.grid[x]; - c.setRect(u); - c.matrix.set(1, 0, 0, 1, v.left, v.top); - c.matrix.inverse(c.inverseMatrix) - } - for (x = 0; x < z.length; x++) { - b = z[x]; - l = b.getSurface(); - t = l.matrix; - k = t.elements; - switch (b.getPosition()) { - case "top": - case "bottom": - k[4] = e.left; - b.setLength(p); - break; - case "left": - case "right": - k[5] = e.top; - b.setLength(s); - break - } - b.updateTitleSprite(); - t.inverse(l.inverseMatrix) - } - for (x = 0, g = q.length; x < g; x++) { - h = q[x]; - r = h.getSurface(); - r.setRect(u); - if (w) { - if (j) { - r.matrix.set(0, -1, -1, 0, v.left + p, v.top + s) - } else { - r.matrix.set(0, -1, 1, 0, v.left, v.top + s) - } - } else { - r.matrix.set(1, 0, 0, -1, v.left, v.top + s) - } - r.matrix.inverse(r.inverseMatrix); - h.getOverlaySurface().setRect(u) - } - A.redraw(); - A.animationSuspendCount--; - A.resumeThicknessChanged() - }, - refloatAxes: function() { - var h = this, - g = h.getAxes(), - o = (g && g.length) || 0, - c, d, n, f, l, b, k, r = h.getChartSize(), - q = h.getInsetPadding(), - p = h.getInnerPadding(), - a = r.width - q.left - q.right, - m = r.height - q.top - q.bottom, - j, e; - for (e = 0; e < o; e++) { - c = g[e]; - f = c.getFloating(); - l = f ? f.value : null; - if (l === null) { - delete c.floatingAtCoord; - continue - } - d = c.getSurface(); - n = d.getRect(); - if (!n) { - continue - } - n = n.slice(); - b = h.getAxis(f.alongAxis); - if (b) { - j = b.getAlignment() === "horizontal"; - if (Ext.isString(l)) { - l = b.getCoordFor(l) - } - b.floatingAxes[c.getId()] = l; - k = b.getSprites()[0].attr.matrix; - if (j) { - l = l * k.getXX() + k.getDX(); - c.floatingAtCoord = l + p.left + p.right - } else { - l = l * k.getYY() + k.getDY(); - c.floatingAtCoord = l + p.top + p.bottom - } - } else { - j = c.getAlignment() === "horizontal"; - if (j) { - c.floatingAtCoord = l + p.top + p.bottom - } else { - c.floatingAtCoord = l + p.left + p.right - } - l = d.roundPixel(0.01 * l * (j ? m : a)) - } - switch (c.getPosition()) { - case "top": - n[1] = q.top + p.top + l - n[3] + 1; - break; - case "bottom": - n[1] = q.top + p.top + (b ? l : m - l); - break; - case "left": - n[0] = q.left + p.left + l - n[2]; - break; - case "right": - n[0] = q.left + p.left + (b ? l : a - l) - 1; - break - } - d.setRect(n) - } - }, - redraw: function() { - var C = this, - r = C.getSeries(), - z = C.getAxes(), - b = C.getMainRect(), - p, t, w = C.getInnerPadding(), - f, l, s, e, q, A, v, g, d, c, a, k, n, y = C.getFlipXY(), - x = 1000, - m, u, h, o, B; - if (!b) { - return - } - p = b[2] - w.left - w.right; - t = b[3] - w.top - w.bottom; - for (A = 0; A < r.length; A++) { - h = r[A]; - if ((c = h.getXAxis())) { - n = c.getVisibleRange(); - l = c.getRange(); - l = [l[0] + (l[1] - l[0]) * n[0], l[0] + (l[1] - l[0]) * n[1]] - } else { - l = h.getXRange() - } - if ((a = h.getYAxis())) { - n = a.getVisibleRange(); - s = a.getRange(); - s = [s[0] + (s[1] - s[0]) * n[0], s[0] + (s[1] - s[0]) * n[1]] - } else { - s = h.getYRange() - } - q = { - visibleMinX: l[0], - visibleMaxX: l[1], - visibleMinY: s[0], - visibleMaxY: s[1], - innerWidth: p, - innerHeight: t, - flipXY: y - }; - f = h.getSprites(); - for (v = 0, g = f.length; v < g; v++) { - o = f[v]; - m = o.attr.zIndex; - if (m < x) { - m += (A + 1) * 100 + x; - o.attr.zIndex = m; - B = o.getMarker("items"); - if (B) { - u = B.attr.zIndex; - if (u === Number.MAX_VALUE) { - B.attr.zIndex = m - } else { - if (u < x) { - B.attr.zIndex = m + u - } - } - } - } - o.setAttributes(q, true) - } - } - for (A = 0; A < z.length; A++) { - d = z[A]; - e = d.isSide(); - f = d.getSprites(); - k = d.getRange(); - n = d.getVisibleRange(); - q = { - dataMin: k[0], - dataMax: k[1], - visibleMin: n[0], - visibleMax: n[1] - }; - if (e) { - q.length = t; - q.startGap = w.bottom; - q.endGap = w.top - } else { - q.length = p; - q.startGap = w.left; - q.endGap = w.right - } - for (v = 0, g = f.length; v < g; v++) { - f[v].setAttributes(q, true) - } - } - C.renderFrame(); - C.callParent(arguments) - }, - renderFrame: function() { - this.refloatAxes(); - this.callParent() - } -}); -Ext.define("Ext.chart.grid.CircularGrid", { - extend: "Ext.draw.sprite.Circle", - alias: "grid.circular", - inheritableStatics: { - def: { - defaults: { - r: 1, - strokeStyle: "#DDD" - } - } - } -}); -Ext.define("Ext.chart.grid.RadialGrid", { - extend: "Ext.draw.sprite.Path", - alias: "grid.radial", - inheritableStatics: { - def: { - processors: { - startRadius: "number", - endRadius: "number" - }, - defaults: { - startRadius: 0, - endRadius: 1, - scalingCenterX: 0, - scalingCenterY: 0, - strokeStyle: "#DDD" - }, - triggers: { - startRadius: "path,bbox", - endRadius: "path,bbox" - } - } - }, - render: function() { - this.callParent(arguments) - }, - updatePath: function(d, a) { - var b = a.startRadius, - c = a.endRadius; - d.moveTo(b, 0); - d.lineTo(c, 0) - } -}); -Ext.define("Ext.chart.PolarChart", { - extend: "Ext.chart.AbstractChart", - requires: ["Ext.chart.grid.CircularGrid", "Ext.chart.grid.RadialGrid"], - xtype: "polar", - isPolar: true, - config: { - center: [0, 0], - radius: 0, - innerPadding: 0 - }, - getDirectionForAxis: function(a) { - return a === "radial" ? "Y" : "X" - }, - applyCenter: function(a, b) { - if (b && a[0] === b[0] && a[1] === b[1]) { - return - } - return [+a[0], +a[1]] - }, - updateCenter: function(a) { - var g = this, - h = g.getAxes(), - d = g.getSeries(), - c, f, e, b; - for (c = 0, f = h.length; c < f; c++) { - e = h[c]; - e.setCenter(a) - } - for (c = 0, f = d.length; c < f; c++) { - b = d[c]; - b.setCenter(a) - } - }, - applyInnerPadding: function(b, a) { - return Ext.isNumber(b) ? b : a - }, - doSetSurfaceRect: function(b, c) { - var a = this.getMainRect(); - b.setRect(c); - b.matrix.set(1, 0, 0, 1, a[0] - c[0], a[1] - c[1]); - b.inverseMatrix.set(1, 0, 0, 1, c[0] - a[0], c[1] - a[1]) - }, - applyAxes: function(f, h) { - var e = this, - g = Ext.Array.from(e.config.series)[0], - b, d, c, a; - if (g.type === "radar" && f && f.length) { - for (b = 0, d = f.length; b < d; b++) { - c = f[b]; - if (c.position === "angular") { - a = true; - break - } - } - if (!a) { - f.push({ - type: "category", - position: "angular", - fields: g.xField || g.angleField, - style: { - estStepSize: 1 - }, - grid: true - }) - } - } - return this.callParent(arguments) - }, - performLayout: function() { - var F = this, - g = true; - try { - F.animationSuspendCount++; - if (this.callParent() === false) { - g = false; - return - } - F.suspendThicknessChanged(); - var h = F.getSurface("chart").getRect(), - v = F.getInsetPadding(), - G = F.getInnerPadding(), - l = Ext.apply({}, v), - d, s = h[2] - v.left - v.right, - r = h[3] - v.top - v.bottom, - x = [v.left, v.top, s, r], - u = F.getSeries(), - p, t = s - G * 2, - w = r - G * 2, - D = [t * 0.5 + G, w * 0.5 + G], - j = Math.min(t, w) * 0.5, - A = F.getAxes(), - f, a, k, m = [], - o = [], - E = j - G, - z, n, b, q, y, c, C; - F.setMainRect(x); - F.doSetSurfaceRect(F.getSurface(), x); - for (z = 0, n = F.surfaceMap.grid && F.surfaceMap.grid.length; z < n; z++) { - F.doSetSurfaceRect(F.surfaceMap.grid[z], h) - } - for (z = 0, n = A.length; z < n; z++) { - f = A[z]; - switch (f.getPosition()) { - case "angular": - m.push(f); - break; - case "radial": - o.push(f); - break - } - } - for (z = 0, n = m.length; z < n; z++) { - f = m[z]; - q = f.getFloating(); - y = q ? q.value : null; - F.doSetSurfaceRect(f.getSurface(), h); - a = f.getThickness(); - for (d in l) { - l[d] += a - } - s = h[2] - l.left - l.right; - r = h[3] - l.top - l.bottom; - b = Math.min(s, r) * 0.5; - if (z === 0) { - E = b - G - } - f.setMinimum(0); - f.setLength(b); - f.getSprites(); - k = f.sprites[0].attr.lineWidth * 0.5; - for (d in l) { - l[d] += k - } - } - for (z = 0, n = o.length; z < n; z++) { - f = o[z]; - F.doSetSurfaceRect(f.getSurface(), h); - f.setMinimum(0); - f.setLength(E); - f.getSprites() - } - for (z = 0, n = u.length; z < n; z++) { - p = u[z]; - if (p.type === "gauge" && !c) { - c = p - } else { - p.setRadius(E) - } - F.doSetSurfaceRect(p.getSurface(), x) - } - F.doSetSurfaceRect(F.getSurface("overlay"), h); - if (c) { - c.setRect(x); - C = c.getRadius() - G; - F.setRadius(C); - F.setCenter(c.getCenter()); - c.setRadius(C); - if (A.length && A[0].getPosition() === "gauge") { - f = A[0]; - F.doSetSurfaceRect(f.getSurface(), h); - f.setTotalAngle(c.getTotalAngle()); - f.setLength(C) - } - } else { - F.setRadius(j); - F.setCenter(D) - } - F.redraw() - } catch (B) { - throw B - } finally { - F.animationSuspendCount--; - if (g) { - F.resumeThicknessChanged() - } - } - }, - refloatAxes: function() { - var j = this, - g = j.getAxes(), - h = j.getMainRect(), - f, k, b, d, a, c, e; - if (!h) { - return - } - e = 0.5 * Math.min(h[2], h[3]); - for (d = 0, a = g.length; d < a; d++) { - c = g[d]; - f = c.getFloating(); - k = f ? f.value : null; - if (k !== null) { - b = j.getAxis(f.alongAxis); - if (c.getPosition() === "angular") { - if (b) { - k = b.getLength() * k / b.getRange()[1] - } else { - k = 0.01 * k * e - } - c.sprites[0].setAttributes({ - length: k - }, true) - } else { - if (b) { - if (Ext.isString(k)) { - k = b.getCoordFor(k) - } - k = k / (b.getRange()[1] + 1) * Math.PI * 2 - Math.PI * 1.5 + c.getRotation() - } else { - k = Ext.draw.Draw.rad(k) - } - c.sprites[0].setAttributes({ - baseRotation: k - }, true) - } - } - } - }, - redraw: function() { - var f = this, - g = f.getAxes(), - d, c = f.getSeries(), - b, a, e; - for (a = 0, e = g.length; a < e; a++) { - d = g[a]; - d.getSprites() - } - for (a = 0, e = c.length; a < e; a++) { - b = c[a]; - b.getSprites() - } - f.renderFrame(); - f.callParent(arguments) - }, - renderFrame: function() { - this.refloatAxes(); - this.callParent() - } -}); -Ext.define("Ext.chart.SpaceFillingChart", { - extend: "Ext.chart.AbstractChart", - xtype: "spacefilling", - config: {}, - performLayout: function() { - var j = this; - try { - j.animationSuspendCount++; - if (j.callParent() === false) { - return - } - var k = j.getSurface("chart").getRect(), - l = j.getInsetPadding(), - a = k[2] - l.left - l.right, - m = k[3] - l.top - l.bottom, - h = [l.left, l.top, a, m], - b = j.getSeries(), - d, c, g; - j.getSurface().setRect(h); - j.setMainRect(h); - for (c = 0, g = b.length; c < g; c++) { - d = b[c]; - d.getSurface().setRect(h); - if (d.setRect) { - d.setRect(h) - } - d.getOverlaySurface().setRect(k) - } - j.redraw() - } catch (f) { - throw f - } finally { - j.animationSuspendCount-- - } - }, - redraw: function() { - var e = this, - c = e.getSeries(), - b, a, d; - for (a = 0, d = c.length; a < d; a++) { - b = c[a]; - b.getSprites() - } - e.renderFrame(); - e.callParent(arguments) - } -}); -Ext.define("Ext.chart.axis.sprite.Axis3D", { - extend: "Ext.chart.axis.sprite.Axis", - alias: "sprite.axis3d", - type: "axis3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - }, - triggers: { - depth: "layout" - } - } - }, - config: { - fx: { - customDurations: { - depth: 0 - } - } - }, - layoutUpdater: function() { - var h = this, - f = h.getAxis().getChart(); - if (f.isInitializing) { - return - } - var e = h.attr, - d = h.getLayout(), - c = d.isDiscrete ? 0 : e.depth, - g = f.getInherited().rtl, - b = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMin, - i = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMax, - a = { - attr: e, - segmenter: h.getSegmenter(), - renderer: h.defaultRenderer - }; - if (e.position === "left" || e.position === "right") { - e.translationX = 0; - e.translationY = i * (e.length - c) / (i - b) + c; - e.scalingX = 1; - e.scalingY = (-e.length + c) / (i - b); - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } else { - if (e.position === "top" || e.position === "bottom") { - if (g) { - e.translationX = e.length + b * e.length / (i - b) + 1 - } else { - e.translationX = -b * e.length / (i - b) - } - e.translationY = 0; - e.scalingX = (g ? -1 : 1) * (e.length - c) / (i - b); - e.scalingY = 1; - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } - } - if (d) { - d.calculateLayout(a); - h.setLayoutContext(a) - } - }, - renderAxisLine: function(a, j, f, c) { - var i = this, - h = i.attr, - b = h.lineWidth * 0.5, - f = i.getLayout(), - d = f.isDiscrete ? 0 : h.depth, - k = h.position, - e, g; - if (h.axisLine && h.length) { - switch (k) { - case "left": - e = a.roundPixel(c[2]) - b; - j.moveTo(e, -h.endGap + d); - j.lineTo(e, h.length + h.startGap); - break; - case "right": - j.moveTo(b, -h.endGap); - j.lineTo(b, h.length + h.startGap); - break; - case "bottom": - j.moveTo(-h.startGap, b); - j.lineTo(h.length - d + h.endGap, b); - break; - case "top": - e = a.roundPixel(c[3]) - b; - j.moveTo(-h.startGap, e); - j.lineTo(h.length + h.endGap, e); - break; - case "angular": - j.moveTo(h.centerX + h.length, h.centerY); - j.arc(h.centerX, h.centerY, h.length, 0, Math.PI * 2, true); - break; - case "gauge": - g = i.getGaugeAngles(); - j.moveTo(h.centerX + Math.cos(g.start) * h.length, h.centerY + Math.sin(g.start) * h.length); - j.arc(h.centerX, h.centerY, h.length, g.start, g.end, true); - break - } - } - } -}); -Ext.define("Ext.chart.axis.Axis3D", { - extend: "Ext.chart.axis.Axis", - xtype: "axis3d", - requires: ["Ext.chart.axis.sprite.Axis3D"], - config: { - depth: 0 - }, - onSeriesChange: function(e) { - var g = this, - b = "depthchange", - f = "onSeriesDepthChange", - d, c; - - function a(h) { - var i = g.boundSeries; - for (d = 0; d < i.length; d++) { - c = i[d]; - c[h](b, f, g) - } - } - a("un"); - g.callParent(arguments); - a("on") - }, - onSeriesDepthChange: function(b, f) { - var d = this, - g = f, - e = d.boundSeries, - a, c; - if (f > d.getDepth()) { - g = f - } else { - for (a = 0; a < e.length; a++) { - c = e[a]; - if (c !== b && c.getDepth) { - f = c.getDepth(); - if (f > g) { - g = f - } - } - } - } - d.setDepth(g) - }, - updateDepth: function(d) { - var b = this, - c = b.getSprites(), - a = { - depth: d - }; - if (c && c.length) { - c[0].setAttributes(a) - } - if (b.gridSpriteEven && b.gridSpriteOdd) { - b.gridSpriteEven.getTemplate().setAttributes(a); - b.gridSpriteOdd.getTemplate().setAttributes(a) - } - }, - getGridAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "horizontal3d"; - case "top": - case "bottom": - return "vertical3d" - } - } -}); -Ext.define("Ext.chart.axis.Category", { - requires: ["Ext.chart.axis.layout.CombineDuplicate", "Ext.chart.axis.segmenter.Names"], - extend: "Ext.chart.axis.Axis", - alias: "axis.category", - type: "category", - config: { - layout: "combineDuplicate", - segmenter: "names" - } -}); -Ext.define("Ext.chart.axis.Category3D", { - requires: ["Ext.chart.axis.layout.CombineDuplicate", "Ext.chart.axis.segmenter.Names"], - extend: "Ext.chart.axis.Axis3D", - alias: "axis.category3d", - type: "category3d", - config: { - layout: "combineDuplicate", - segmenter: "names" - } -}); -Ext.define("Ext.chart.axis.Numeric", { - extend: "Ext.chart.axis.Axis", - type: "numeric", - alias: ["axis.numeric", "axis.radial"], - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Numeric"], - config: { - layout: "continuous", - segmenter: "numeric", - aggregator: "double" - } -}); -Ext.define("Ext.chart.axis.Numeric3D", { - extend: "Ext.chart.axis.Axis3D", - alias: ["axis.numeric3d"], - type: "numeric3d", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Numeric"], - config: { - layout: "continuous", - segmenter: "numeric", - aggregator: "double" - } -}); -Ext.define("Ext.chart.axis.Time", { - extend: "Ext.chart.axis.Numeric", - alias: "axis.time", - type: "time", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Time"], - config: { - calculateByLabelSize: true, - dateFormat: null, - fromDate: null, - toDate: null, - step: [Ext.Date.DAY, 1], - layout: "continuous", - segmenter: "time", - aggregator: "time" - }, - updateDateFormat: function(a) { - this.setRenderer(function(c, b) { - return Ext.Date.format(new Date(b), a) - }) - }, - updateFromDate: function(a) { - this.setMinimum(+a) - }, - updateToDate: function(a) { - this.setMaximum(+a) - }, - getCoordFor: function(a) { - if (Ext.isString(a)) { - a = new Date(a) - } - return +a - } -}); -Ext.define("Ext.chart.axis.Time3D", { - extend: "Ext.chart.axis.Numeric3D", - alias: "axis.time3d", - type: "time3d", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Time"], - config: { - calculateByLabelSize: true, - dateFormat: null, - fromDate: null, - toDate: null, - step: [Ext.Date.DAY, 1], - layout: "continuous", - segmenter: "time", - aggregator: "time" - }, - updateDateFormat: function(a) { - this.setRenderer(function(c, b) { - return Ext.Date.format(new Date(b), a) - }) - }, - updateFromDate: function(a) { - this.setMinimum(+a) - }, - updateToDate: function(a) { - this.setMaximum(+a) - }, - getCoordFor: function(a) { - if (Ext.isString(a)) { - a = new Date(a) - } - return +a - } -}); -Ext.define("Ext.chart.grid.HorizontalGrid3D", { - extend: "Ext.chart.grid.HorizontalGrid", - alias: "grid.horizontal3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - } - } - }, - render: function(a, k, d) { - var f = this.attr, - i = a.roundPixel(f.x), - h = a.roundPixel(f.y), - l = a.matrix.getDX(), - c = k.lineWidth * 0.5, - j = f.height, - e = f.depth, - b, g; - if (h <= d[1]) { - return - } - b = d[0] + e - l; - g = h + c - e; - k.beginPath(); - k.rect(b, g, d[2], j); - k.fill(); - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + d[2], g); - k.stroke(); - b = d[0] + i - l; - g = h + c; - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + e, g - e); - k.lineTo(b + e, g - e + j); - k.lineTo(b, g + j); - k.closePath(); - k.fill(); - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + e, g - e); - k.stroke() - } -}); -Ext.define("Ext.chart.grid.VerticalGrid3D", { - extend: "Ext.chart.grid.VerticalGrid", - alias: "grid.vertical3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - } - } - }, - render_: function(c, d, f) { - var b = this.attr, - a = c.roundPixel(b.x), - e = d.lineWidth * 0.5; - d.beginPath(); - d.rect(a - e, f[1] - c.matrix.getDY(), b.width, f[3]); - d.fill(); - d.beginPath(); - d.moveTo(a - e, f[1] - c.matrix.getDY()); - d.lineTo(a - e, f[1] + f[3] - c.matrix.getDY()); - d.stroke() - }, - render: function(b, j, e) { - var g = this.attr, - i = b.roundPixel(g.x), - k = b.matrix.getDY(), - d = j.lineWidth * 0.5, - a = g.width, - f = g.depth, - c, h; - if (i >= e[2]) { - return - } - c = i - d + f; - h = e[1] - f - k; - j.beginPath(); - j.rect(c, h, a, e[3]); - j.fill(); - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c, h + e[3]); - j.stroke(); - c = i - d; - h = e[3]; - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c + f, h - f); - j.lineTo(c + f + a, h - f); - j.lineTo(c + a, h); - j.closePath(); - j.fill(); - c = i - d; - h = e[3]; - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c + f, h - f); - j.stroke() - } -}); -Ext.define("Ext.chart.interactions.CrossZoom", { - extend: "Ext.chart.interactions.Abstract", - type: "crosszoom", - alias: "interaction.crosszoom", - isCrossZoom: true, - config: { - axes: true, - gestures: { - dragstart: "onGestureStart", - drag: "onGesture", - dragend: "onGestureEnd", - dblclick: "onDoubleTap" - }, - undoButton: {} - }, - stopAnimationBeforeSync: false, - zoomAnimationInProgress: false, - constructor: function() { - this.callParent(arguments); - this.zoomHistory = [] - }, - applyAxes: function(b) { - var a = {}; - if (b === true) { - return { - top: {}, - right: {}, - bottom: {}, - left: {} - } - } else { - if (Ext.isArray(b)) { - a = {}; - Ext.each(b, function(c) { - a[c] = {} - }) - } else { - if (Ext.isObject(b)) { - Ext.iterate(b, function(c, d) { - if (d === true) { - a[c] = {} - } else { - if (d !== false) { - a[c] = d - } - } - }) - } - } - } - return a - }, - applyUndoButton: function(b, a) { - var c = this; - if (a) { - a.destroy() - } - if (b) { - return Ext.create("Ext.Button", Ext.apply({ - cls: [], - text: "Undo Zoom", - disabled: true, - handler: function() { - c.undoZoom() - } - }, b)) - } - }, - getSurface: function() { - return this.getChart() && this.getChart().getSurface("main") - }, - setSeriesOpacity: function(b) { - var a = this.getChart() && this.getChart().getSurface("series"); - if (a) { - a.element.setStyle("opacity", b) - } - }, - onGestureStart: function(h) { - var j = this, - i = j.getChart(), - d = j.getSurface(), - l = i.getInnerRect(), - c = i.getInnerPadding(), - g = c.left, - b = g + l[2], - f = c.top, - a = f + l[3], - n = i.getEventXY(h), - m = n[0], - k = n[1]; - if (j.zoomAnimationInProgress) { - return - } - if (m > g && m < b && k > f && k < a) { - j.gestureEvent = "drag"; - j.lockEvents(j.gestureEvent); - j.startX = m; - j.startY = k; - j.selectionRect = d.add({ - type: "rect", - globalAlpha: 0.5, - fillStyle: "rgba(80,80,140,0.5)", - strokeStyle: "rgba(80,80,140,1)", - lineWidth: 2, - x: m, - y: k, - width: 0, - height: 0, - zIndex: 10000 - }); - j.setSeriesOpacity(0.8); - return false - } - }, - onGesture: function(h) { - var j = this; - if (j.zoomAnimationInProgress) { - return - } - if (j.getLocks()[j.gestureEvent] === j) { - var i = j.getChart(), - d = j.getSurface(), - l = i.getInnerRect(), - c = i.getInnerPadding(), - g = c.left, - b = g + l[2], - f = c.top, - a = f + l[3], - n = i.getEventXY(h), - m = n[0], - k = n[1]; - if (m < g) { - m = g - } else { - if (m > b) { - m = b - } - } - if (k < f) { - k = f - } else { - if (k > a) { - k = a - } - } - j.selectionRect.setAttributes({ - width: m - j.startX, - height: k - j.startY - }); - if (Math.abs(j.startX - m) < 11 || Math.abs(j.startY - k) < 11) { - j.selectionRect.setAttributes({ - globalAlpha: 0.5 - }) - } else { - j.selectionRect.setAttributes({ - globalAlpha: 1 - }) - } - d.renderFrame(); - return false - } - }, - onGestureEnd: function(i) { - var l = this; - if (l.zoomAnimationInProgress) { - return - } - if (l.getLocks()[l.gestureEvent] === l) { - var k = l.getChart(), - d = l.getSurface(), - n = k.getInnerRect(), - c = k.getInnerPadding(), - g = c.left, - b = g + n[2], - f = c.top, - a = f + n[3], - h = n[2], - j = n[3], - p = k.getEventXY(i), - o = p[0], - m = p[1]; - if (o < g) { - o = g - } else { - if (o > b) { - o = b - } - } - if (m < f) { - m = f - } else { - if (m > a) { - m = a - } - } - if (Math.abs(l.startX - o) < 11 || Math.abs(l.startY - m) < 11) { - d.remove(l.selectionRect) - } else { - l.zoomBy([Math.min(l.startX, o) / h, 1 - Math.max(l.startY, m) / j, Math.max(l.startX, o) / h, 1 - Math.min(l.startY, m) / j]); - l.selectionRect.setAttributes({ - x: Math.min(l.startX, o), - y: Math.min(l.startY, m), - width: Math.abs(l.startX - o), - height: Math.abs(l.startY - m) - }); - l.selectionRect.setAnimation(k.getAnimation() || { - duration: 0 - }); - l.selectionRect.setAttributes({ - globalAlpha: 0, - x: 0, - y: 0, - width: h, - height: j - }); - l.zoomAnimationInProgress = true; - k.suspendThicknessChanged(); - l.selectionRect.fx.on("animationend", function() { - k.resumeThicknessChanged(); - d.remove(l.selectionRect); - l.selectionRect = null; - l.zoomAnimationInProgress = false - }) - } - d.renderFrame(); - l.sync(); - l.unlockEvents(l.gestureEvent); - l.setSeriesOpacity(1); - if (!l.zoomAnimationInProgress) { - d.remove(l.selectionRect); - l.selectionRect = null - } - } - }, - zoomBy: function(o) { - var n = this, - a = n.getAxes(), - k = n.getChart(), - j = k.getAxes(), - l = k.getInherited().rtl, - f, d = {}, - c, b; - if (l) { - o = o.slice(); - c = 1 - o[0]; - b = 1 - o[2]; - o[0] = Math.min(c, b); - o[2] = Math.max(c, b) - } - for (var h = 0; h < j.length; h++) { - var g = j[h]; - f = a[g.getPosition()]; - if (f && f.allowZoom !== false) { - var e = g.isSide(), - m = g.getVisibleRange(); - d[g.getId()] = m.slice(0); - if (!e) { - g.setVisibleRange([(m[1] - m[0]) * o[0] + m[0], (m[1] - m[0]) * o[2] + m[0]]) - } else { - g.setVisibleRange([(m[1] - m[0]) * o[1] + m[0], (m[1] - m[0]) * o[3] + m[0]]) - } - } - } - n.zoomHistory.push(d); - n.getUndoButton().setDisabled(false) - }, - undoZoom: function() { - var c = this.zoomHistory.pop(), - d = this.getChart().getAxes(); - if (c) { - for (var a = 0; a < d.length; a++) { - var b = d[a]; - if (c[b.getId()]) { - b.setVisibleRange(c[b.getId()]) - } - } - } - this.getUndoButton().setDisabled(this.zoomHistory.length === 0); - this.sync() - }, - onDoubleTap: function(a) { - this.undoZoom() - }, - destroy: function() { - this.setUndoButton(null); - this.callParent(arguments) - } -}); -Ext.define("Ext.chart.interactions.Crosshair", { - extend: "Ext.chart.interactions.Abstract", - requires: ["Ext.chart.grid.HorizontalGrid", "Ext.chart.grid.VerticalGrid", "Ext.chart.CartesianChart", "Ext.chart.axis.layout.Discrete"], - type: "crosshair", - alias: "interaction.crosshair", - config: { - axes: { - top: { - label: {}, - rect: {} - }, - right: { - label: {}, - rect: {} - }, - bottom: { - label: {}, - rect: {} - }, - left: { - label: {}, - rect: {} - } - }, - lines: { - horizontal: { - strokeStyle: "black", - lineDash: [5, 5] - }, - vertical: { - strokeStyle: "black", - lineDash: [5, 5] - } - }, - gesture: "drag" - }, - applyAxes: function(b, a) { - return Ext.merge(a || {}, b) - }, - applyLines: function(a, b) { - return Ext.merge(b || {}, a) - }, - updateChart: function(a) { - if (a && !a.isCartesian) { - Ext.raise("Crosshair interaction can only be used on cartesian charts.") - } - this.callParent(arguments) - }, - getGestures: function() { - var a = this, - b = {}; - b[a.getGesture()] = "onGesture"; - b[a.getGesture() + "start"] = "onGestureStart"; - b[a.getGesture() + "end"] = "onGestureEnd"; - return b - }, - onGestureStart: function(N) { - var m = this, - O = m.getChart(), - B = O.getTheme().getAxis(), - A, F = O.getSurface("overlay"), - s = O.getInnerRect(), - n = s[2], - M = s[3], - r = O.getEventXY(N), - D = r[0], - C = r[1], - E = O.getAxes(), - u = m.getAxes(), - h = m.getLines(), - q, v, b, d, k, z, G, L, J, o, I, w, l, f, p, j, t, a, g, H, c, K; - if (D > 0 && D < n && C > 0 && C < M) { - m.lockEvents(m.getGesture()); - H = Ext.apply({ - xclass: "Ext.chart.grid.HorizontalGrid", - x: 0, - y: C, - width: n - }, h.horizontal); - c = Ext.apply({ - xclass: "Ext.chart.grid.VerticalGrid", - x: D, - y: 0, - height: M - }, h.vertical); - m.axesLabels = m.axesLabels || {}; - for (K = 0; K < E.length; K++) { - q = E[K]; - v = q.getSurface(); - b = v.getRect(); - w = q.getSprites()[0]; - d = b[2]; - k = b[3]; - z = q.getPosition(); - G = q.getAlignment(); - t = q.getTitle(); - a = t && t.attr.text !== "" && t.getBBox(); - l = w.attr; - f = w.thickness; - p = l.axisLine ? l.lineWidth : 0; - j = p / 2; - I = Math.max(l.majorTickSize, l.minorTickSize) + p; - L = m.axesLabels[z] = v.add({ - type: "composite" - }); - L.labelRect = L.add(Ext.apply({ - type: "rect", - fillStyle: "white", - x: z === "right" ? p : 0, - y: z === "bottom" ? p : 0, - width: d - p - (G === "vertical" && a ? a.width : 0), - height: k - p - (G === "horizontal" && a ? a.height : 0), - translationX: z === "left" && a ? a.width : 0, - translationY: z === "top" && a ? a.height : 0 - }, u.rect || u[z].rect)); - if (G === "vertical" && !c.strokeStyle) { - c.strokeStyle = l.strokeStyle - } - if (G === "horizontal" && !H.strokeStyle) { - H.strokeStyle = l.strokeStyle - } - A = Ext.merge({}, B.defaults, B[z]); - J = Ext.apply({}, q.config.label, A.label); - o = u.label || u[z].label; - L.labelText = L.add(Ext.apply(J, o, { - type: "text", - x: (function() { - switch (z) { - case "left": - g = a ? a.x + a.width : 0; - return g + (d - g - I) / 2 - j; - case "right": - g = a ? d - a.x : 0; - return I + (d - I - g) / 2 + j; - default: - return 0 - } - })(), - y: (function() { - switch (z) { - case "top": - g = a ? a.y + a.height : 0; - return g + (k - g - I) / 2 - j; - case "bottom": - g = a ? k - a.y : 0; - return I + (k - I - g) / 2 + j; - default: - return 0 - } - })() - })) - } - m.horizontalLine = F.add(H); - m.verticalLine = F.add(c); - return false - } - }, - onGesture: function(G) { - var K = this; - if (K.getLocks()[K.getGesture()] !== K) { - return - } - var u = K.getChart(), - z = u.getSurface("overlay"), - a = Ext.Array.slice(u.getInnerRect()), - r = u.getInnerPadding(), - t = r.left, - q = r.top, - E = a[2], - f = a[3], - d = u.getEventXY(G), - k = d[0], - j = d[1], - D = u.getAxes(), - c, h, m, p, J, w, I, H, s, b, C, g, v, n, l, A, F, o, B; - if (k < 0) { - k = 0 - } else { - if (k > E) { - k = E - } - } - if (j < 0) { - j = 0 - } else { - if (j > f) { - j = f - } - } - k += t; - j += q; - for (B = 0; B < D.length; B++) { - c = D[B]; - h = c.getPosition(); - m = c.getAlignment(); - p = c.getSurface(); - J = c.getSprites()[0]; - w = J.attr.matrix; - C = J.attr.textPadding * 2; - s = K.axesLabels[h]; - I = J.getLayoutContext(); - H = c.getSegmenter(); - if (s) { - if (m === "vertical") { - v = w.getYY(); - l = w.getDY(); - F = (j - l - q) / v; - if (c.getLayout() instanceof Ext.chart.axis.layout.Discrete) { - j = Math.round(F) * v + l + q; - F = H.from(Math.round(F)); - F = J.attr.data[F] - } else { - F = H.from(F) - } - o = H.renderer(F, I); - s.setAttributes({ - translationY: j - q - }); - s.labelText.setAttributes({ - text: o - }); - b = s.labelText.getBBox(); - s.labelRect.setAttributes({ - height: b.height + C, - y: -(b.height + C) / 2 - }); - p.renderFrame() - } else { - g = w.getXX(); - n = w.getDX(); - A = (k - n - t) / g; - if (c.getLayout() instanceof Ext.chart.axis.layout.Discrete) { - k = Math.round(A) * g + n + t; - A = H.from(Math.round(A)); - A = J.attr.data[A] - } else { - A = H.from(A) - } - o = H.renderer(A, I); - s.setAttributes({ - translationX: k - t - }); - s.labelText.setAttributes({ - text: o - }); - b = s.labelText.getBBox(); - s.labelRect.setAttributes({ - width: b.width + C, - x: -(b.width + C) / 2 - }); - p.renderFrame() - } - } - } - K.horizontalLine.setAttributes({ - y: j, - strokeStyle: J.attr.strokeStyle - }); - K.verticalLine.setAttributes({ - x: k, - strokeStyle: J.attr.strokeStyle - }); - z.renderFrame(); - return false - }, - onGestureEnd: function(h) { - var l = this, - k = l.getChart(), - a = k.getSurface("overlay"), - j = k.getAxes(), - c, g, d, b, f; - a.remove(l.verticalLine); - a.remove(l.horizontalLine); - for (f = 0; f < j.length; f++) { - c = j[f]; - g = c.getPosition(); - d = c.getSurface(); - b = l.axesLabels[g]; - if (b) { - delete l.axesLabels[g]; - d.remove(b) - } - d.renderFrame() - } - a.renderFrame(); - l.unlockEvents(l.getGesture()) - } -}); -Ext.define("Ext.chart.interactions.ItemHighlight", { - extend: "Ext.chart.interactions.Abstract", - type: "itemhighlight", - alias: "interaction.itemhighlight", - isItemHighlight: true, - config: { - gestures: { - tap: "onTapGesture", - mousemove: "onMouseMoveGesture", - mousedown: "onMouseDownGesture", - mouseup: "onMouseUpGesture", - mouseleave: "onMouseUpGesture" - }, - sticky: false - }, - stickyHighlightItem: null, - onMouseMoveGesture: function(g) { - var d = this, - h = d.tipItem, - a = g.pointerType === "mouse", - c, f, b; - if (d.getSticky()) { - return true - } - if (d.isDragging) { - if (h && a) { - h.series.hideTooltip(h); - d.tipItem = null - } - } else { - if (!d.stickyHighlightItem) { - c = d.getItemForEvent(g); - b = d.getChart(); - if (c !== b.getHighlightItem()) { - d.highlight(c); - d.sync() - } - if (a) { - if (h && (!c || h.field !== c.field || h.record !== c.record)) { - h.series.hideTooltip(h); - d.tipItem = h = null - } - if (c && (f = c.series.getTooltip())) { - if (f.trackMouse || !h) { - c.series.showTooltip(c, g.getXY()) - } - d.tipItem = c - } - } - return false - } - } - }, - highlight: function(a) { - this.getChart().setHighlightItem(a) - }, - showTooltip: function(b, a) { - a.series.showTooltip(a, b.getXY()); - this.tipItem = a - }, - onMouseDownGesture: function() { - this.isDragging = true - }, - onMouseUpGesture: function() { - this.isDragging = false - }, - onTapGesture: function(c) { - var b = this; - if (c.pointerType === "mouse" && !b.getSticky()) { - return - } - var a = b.getItemForEvent(c); - if (b.stickyHighlightItem && a && (b.stickyHighlightItem.index === a.index)) { - a = null - } - b.stickyHighlightItem = a; - b.highlight(a) - } -}); -Ext.define("Ext.chart.interactions.ItemEdit", { - extend: "Ext.chart.interactions.ItemHighlight", - requires: ["Ext.tip.ToolTip"], - type: "itemedit", - alias: "interaction.itemedit", - isItemEdit: true, - config: { - style: null, - renderer: null, - tooltip: true, - gestures: { - dragstart: "onDragStart", - drag: "onDrag", - dragend: "onDragEnd" - }, - cursors: { - ewResize: "ew-resize", - nsResize: "ns-resize", - move: "move" - } - }, - item: null, - applyTooltip: function(b) { - if (b) { - var a = Ext.apply({}, b, { - renderer: this.defaultTooltipRenderer, - constrainPosition: true, - shrinkWrapDock: true, - autoHide: true, - offsetX: 10, - offsetY: 10 - }); - b = new Ext.tip.ToolTip(a) - } - return b - }, - defaultTooltipRenderer: function(b, a, f, d) { - var c = []; - if (f.xField) { - c.push(f.xField + ": " + f.xValue) - } - if (f.yField) { - c.push(f.yField + ": " + f.yValue) - } - b.setHtml(c.join("
")) - }, - onDragStart: function(d) { - var c = this, - a = c.getChart(), - b = a.getHighlightItem(); - if (b) { - a.fireEvent("beginitemedit", a, c, c.item = b); - return false - } - }, - onDrag: function(f) { - var d = this, - b = d.getChart(), - c = b.getHighlightItem(), - a = c && c.sprite.type; - if (c) { - switch (a) { - case "barSeries": - return d.onDragBar(f); - break; - case "scatterSeries": - return d.onDragScatter(f); - break - } - } - }, - highlight: function(f) { - var e = this, - d = e.getChart(), - a = d.getFlipXY(), - g = e.getCursors(), - c = f && f.sprite.type, - b = d.el.dom.style; - e.callParent([f]); - if (f) { - switch (c) { - case "barSeries": - if (a) { - b.cursor = g.ewResize - } else { - b.cursor = g.nsResize - } - break; - case "scatterSeries": - b.cursor = g.move; - break - } - } else { - d.el.dom.style.cursor = "default" - } - }, - onDragBar: function(i) { - var m = this, - k = m.getChart(), - l = k.getInherited().rtl, - f = k.isCartesian && k.getFlipXY(), - q = k.getHighlightItem(), - g = q.sprite.getMarker("items"), - p = g.getMarkerFor(q.sprite.getId(), q.index), - b = q.sprite.getSurface(), - c = b.getRect(), - r = b.getEventXY(i), - o = q.sprite.attr.matrix, - j = m.getRenderer(), - a, n, d, h; - if (f) { - h = l ? c[2] - r[0] : r[0] - } else { - h = c[3] - r[1] - } - a = { - x: p.x, - y: h, - width: p.width, - height: p.height + (p.y - h), - radius: p.radius, - fillStyle: "none", - lineDash: [4, 4], - zIndex: 100 - }; - Ext.apply(a, m.getStyle()); - if (Ext.isArray(q.series.getYField())) { - h = h - p.y - p.height - } - m.target = { - index: q.index, - yField: q.field, - yValue: (h - o.getDY()) / o.getYY() - }; - d = [k, { - target: m.target, - style: a, - item: q - }]; - n = Ext.callback(j, null, d, 0, k); - if (n) { - Ext.apply(a, n) - } - q.sprite.putMarker("items", a, "itemedit"); - m.showTooltip(i, m.target, q); - b.renderFrame() - }, - onDragScatter: function(n) { - var t = this, - g = t.getChart(), - d = g.getInherited().rtl, - l = g.isCartesian && g.getFlipXY(), - o = g.getHighlightItem(), - b = o.sprite.getMarker("items"), - p = b.getMarkerFor(o.sprite.getId(), o.index), - j = o.sprite.getSurface(), - h = j.getRect(), - a = j.getEventXY(n), - k = o.sprite.attr.matrix, - c = o.series.getXAxis(), - f = c && c.getLayout().isContinuous, - i = t.getRenderer(), - m, u, q, s, r; - if (l) { - r = d ? h[2] - a[0] : a[0] - } else { - r = h[3] - a[1] - } - if (f) { - if (l) { - s = h[3] - a[1] - } else { - s = a[0] - } - } else { - s = p.translationX - } - m = { - translationX: s, - translationY: r, - scalingX: p.scalingX, - scalingY: p.scalingY, - r: p.r, - fillStyle: "none", - lineDash: [4, 4], - zIndex: 100 - }; - Ext.apply(m, t.getStyle()); - t.target = { - index: o.index, - yField: o.field, - yValue: (r - k.getDY()) / k.getYY() - }; - if (f) { - Ext.apply(t.target, { - xField: o.series.getXField(), - xValue: (s - k.getDX()) / k.getXX() - }) - } - q = [g, { - target: t.target, - style: m, - item: o - }]; - u = Ext.callback(i, null, q, 0, g); - if (u) { - Ext.apply(m, u) - } - o.sprite.putMarker("items", m, "itemedit"); - t.showTooltip(n, t.target, o); - j.renderFrame() - }, - showTooltip: function(g, f, c) { - var d = this.getTooltip(), - a, b; - if (d && Ext.toolkit !== "modern") { - a = d.config; - b = this.getChart(); - Ext.callback(a.renderer, null, [d, c, f, g], 0, b); - d.show([g.x + a.offsetX, g.y + a.offsetY]) - } - }, - hideTooltip: function() { - var a = this.getTooltip(); - if (a && Ext.toolkit !== "modern") { - a.hide() - } - }, - onDragEnd: function(g) { - var d = this, - f = d.target, - c = d.getChart(), - b = c.getStore(), - a; - if (f) { - a = b.getAt(f.index); - if (f.yField) { - a.set(f.yField, f.yValue, { - convert: false - }) - } - if (f.xField) { - a.set(f.xField, f.xValue, { - convert: false - }) - } - if (f.yField || f.xField) { - d.getChart().onDataChanged() - } - d.target = null - } - d.hideTooltip(); - if (d.item) { - c.fireEvent("enditemedit", c, d, d.item, f) - } - d.highlight(d.item = null) - }, - destroy: function() { - var a = this.getConfig("tooltip", true); - Ext.destroy(a); - this.callParent() - } -}); -Ext.define("Ext.chart.interactions.PanZoom", { - extend: "Ext.chart.interactions.Abstract", - type: "panzoom", - alias: "interaction.panzoom", - requires: ["Ext.draw.Animator"], - config: { - axes: { - top: {}, - right: {}, - bottom: {}, - left: {} - }, - minZoom: null, - maxZoom: null, - showOverflowArrows: true, - panGesture: "drag", - zoomGesture: "pinch", - zoomOnPanGesture: false, - modeToggleButton: { - xtype: "segmentedbutton", - width: 200, - defaults: { - ui: "default-toolbar" - }, - cls: Ext.baseCSSPrefix + "panzoom-toggle", - items: [{ - text: "Pan" - }, { - text: "Zoom" - }] - }, - hideLabelInGesture: false - }, - stopAnimationBeforeSync: true, - applyAxes: function(b, a) { - return Ext.merge(a || {}, b) - }, - applyZoomOnPanGesture: function(a) { - this.getChart(); - if (this.isMultiTouch()) { - return false - } - return a - }, - updateZoomOnPanGesture: function(b) { - var a = this.getModeToggleButton(); - if (!this.isMultiTouch()) { - a.show(); - a.setValue(b ? 1 : 0) - } else { - a.hide() - } - }, - toggleMode: function() { - var a = this; - if (!a.isMultiTouch()) { - a.setZoomOnPanGesture(!a.getZoomOnPanGesture()) - } - }, - applyModeToggleButton: function(c, b) { - var d = this, - a = Ext.factory(c, "Ext.button.Segmented", b); - if (!a && b) { - b.destroy() - } - if (a && !b) { - a.addListener("toggle", function(e) { - d.setZoomOnPanGesture(e.getValue() === 1) - }) - } - return a - }, - getGestures: function() { - var c = this, - e = {}, - d = c.getPanGesture(), - b = c.getZoomGesture(), - a = Ext.supports.Touch; - e[b] = "onZoomGestureMove"; - e[b + "start"] = "onZoomGestureStart"; - e[b + "end"] = "onZoomGestureEnd"; - e[d] = "onPanGestureMove"; - e[d + "start"] = "onPanGestureStart"; - e[d + "end"] = "onPanGestureEnd"; - e.doubletap = "onDoubleTap"; - return e - }, - onDoubleTap: function(h) { - var f = this, - c = f.getChart(), - g = c.getAxes(), - b, a, d; - for (a = 0, d = g.length; a < d; a++) { - b = g[a]; - b.setVisibleRange([0, 1]) - } - c.redraw() - }, - onPanGestureStart: function(d) { - if (!d || !d.touches || d.touches.length < 2) { - var b = this, - a = b.getChart().getInnerRect(), - c = b.getChart().element.getXY(); - b.startX = d.getX() - c[0] - a[0]; - b.startY = d.getY() - c[1] - a[1]; - b.oldVisibleRanges = null; - b.hideLabels(); - b.getChart().suspendThicknessChanged(); - b.lockEvents(b.getPanGesture()); - return false - } - }, - onPanGestureMove: function(d) { - var b = this; - if (b.getLocks()[b.getPanGesture()] === b) { - var a = b.getChart().getInnerRect(), - c = b.getChart().element.getXY(); - if (b.getZoomOnPanGesture()) { - b.transformAxesBy(b.getZoomableAxes(d), 0, 0, (d.getX() - c[0] - a[0]) / b.startX, b.startY / (d.getY() - c[1] - a[1])) - } else { - b.transformAxesBy(b.getPannableAxes(d), d.getX() - c[0] - a[0] - b.startX, d.getY() - c[1] - a[1] - b.startY, 1, 1) - } - b.sync(); - return false - } - }, - onPanGestureEnd: function(b) { - var a = this, - c = a.getPanGesture(); - if (a.getLocks()[c] === a) { - a.getChart().resumeThicknessChanged(); - a.showLabels(); - a.sync(); - a.unlockEvents(c); - return false - } - }, - onZoomGestureStart: function(b) { - if (b.touches && b.touches.length === 2) { - var c = this, - i = c.getChart().element.getXY(), - f = c.getChart().getInnerRect(), - h = i[0] + f[0], - d = i[1] + f[1], - j = [b.touches[0].point.x - h, b.touches[0].point.y - d, b.touches[1].point.x - h, b.touches[1].point.y - d], - g = Math.max(44, Math.abs(j[2] - j[0])), - a = Math.max(44, Math.abs(j[3] - j[1])); - c.getChart().suspendThicknessChanged(); - c.lastZoomDistances = [g, a]; - c.lastPoints = j; - c.oldVisibleRanges = null; - c.hideLabels(); - c.lockEvents(c.getZoomGesture()); - return false - } - }, - onZoomGestureMove: function(d) { - var f = this; - if (f.getLocks()[f.getZoomGesture()] === f) { - var i = f.getChart().getInnerRect(), - n = f.getChart().element.getXY(), - k = n[0] + i[0], - h = n[1] + i[1], - o = Math.abs, - c = f.lastPoints, - m = [d.touches[0].point.x - k, d.touches[0].point.y - h, d.touches[1].point.x - k, d.touches[1].point.y - h], - g = Math.max(44, o(m[2] - m[0])), - b = Math.max(44, o(m[3] - m[1])), - a = this.lastZoomDistances || [g, b], - l = g / a[0], - j = b / a[1]; - f.transformAxesBy(f.getZoomableAxes(d), i[2] * (l - 1) / 2 + m[2] - c[2] * l, i[3] * (j - 1) / 2 + m[3] - c[3] * j, l, j); - f.sync(); - return false - } - }, - onZoomGestureEnd: function(c) { - var b = this, - a = b.getZoomGesture(); - if (b.getLocks()[a] === b) { - b.getChart().resumeThicknessChanged(); - b.showLabels(); - b.sync(); - b.unlockEvents(a); - return false - } - }, - hideLabels: function() { - if (this.getHideLabelInGesture()) { - this.eachInteractiveAxes(function(a) { - a.hideLabels() - }) - } - }, - showLabels: function() { - if (this.getHideLabelInGesture()) { - this.eachInteractiveAxes(function(a) { - a.showLabels() - }) - } - }, - isEventOnAxis: function(c, a) { - var b = a.getSurface().getRect(); - return b[0] <= c.getX() && c.getX() <= b[0] + b[2] && b[1] <= c.getY() && c.getY() <= b[1] + b[3] - }, - getPannableAxes: function(d) { - var h = this, - a = h.getAxes(), - f = h.getChart().getAxes(), - c, g = f.length, - k = [], - j = false, - b; - if (d) { - for (c = 0; c < g; c++) { - if (this.isEventOnAxis(d, f[c])) { - j = true; - break - } - } - } - for (c = 0; c < g; c++) { - b = a[f[c].getPosition()]; - if (b && b.allowPan !== false && (!j || this.isEventOnAxis(d, f[c]))) { - k.push(f[c]) - } - } - return k - }, - getZoomableAxes: function(f) { - var j = this, - a = j.getAxes(), - g = j.getChart().getAxes(), - l = [], - d, h = g.length, - c, k = false, - b; - if (f) { - for (d = 0; d < h; d++) { - if (this.isEventOnAxis(f, g[d])) { - k = true; - break - } - } - } - for (d = 0; d < h; d++) { - c = g[d]; - b = a[c.getPosition()]; - if (b && b.allowZoom !== false && (!k || this.isEventOnAxis(f, c))) { - l.push(c) - } - } - return l - }, - eachInteractiveAxes: function(c) { - var d = this, - b = d.getAxes(), - e = d.getChart().getAxes(); - for (var a = 0; a < e.length; a++) { - if (b[e[a].getPosition()]) { - if (false === c.call(this, e[a])) { - return - } - } - } - }, - transformAxesBy: function(d, j, g, h, e) { - var f = this.getChart().getInnerRect(), - a = this.getAxes(), - k, b = this.oldVisibleRanges, - l = false; - if (!b) { - this.oldVisibleRanges = b = {}; - this.eachInteractiveAxes(function(i) { - b[i.getId()] = i.getVisibleRange() - }) - } - if (!f) { - return - } - for (var c = 0; c < d.length; c++) { - k = a[d[c].getPosition()]; - l = this.transformAxisBy(d[c], b[d[c].getId()], j, g, h, e, this.minZoom || k.minZoom, this.maxZoom || k.maxZoom) || l - } - return l - }, - transformAxisBy: function(c, o, r, q, k, i, h, m) { - var s = this, - b = o[1] - o[0], - l = c.getVisibleRange(), - g = h || s.getMinZoom() || c.config.minZoom, - j = m || s.getMaxZoom() || c.config.maxZoom, - a = s.getChart().getInnerRect(), - f, p; - if (!a) { - return - } - var d = c.isSide(), - e = d ? a[3] : a[2], - n = d ? -q : r; - b /= d ? i : k; - if (b < 0) { - b = -b - } - if (b * g > 1) { - b = 1 - } - if (b * j < 1) { - b = 1 / j - } - f = o[0]; - p = o[1]; - l = l[1] - l[0]; - if (b === l && l === 1) { - return - } - c.setVisibleRange([(o[0] + o[1] - b) * 0.5 - n / e * b, (o[0] + o[1] + b) * 0.5 - n / e * b]); - return (Math.abs(f - c.getVisibleRange()[0]) > 1e-10 || Math.abs(p - c.getVisibleRange()[1]) > 1e-10) - }, - destroy: function() { - this.setModeToggleButton(null); - this.callParent() - } -}); -Ext.define("Ext.chart.interactions.Rotate", { - extend: "Ext.chart.interactions.Abstract", - type: "rotate", - alias: "interaction.rotate", - config: { - gesture: "rotate", - gestures: { - rotate: "onRotate", - rotateend: "onRotate", - dragstart: "onGestureStart", - drag: "onGesture", - dragend: "onGestureEnd" - }, - rotation: 0 - }, - oldRotations: null, - getAngle: function(f) { - var c = this, - b = c.getChart(), - d = b.getEventXY(f), - a = b.getCenter(); - return Math.atan2(d[1] - a[1], d[0] - a[0]) - }, - getRadius: function(a) { - return this.getChart().getRadius() - }, - getEventRadius: function(h) { - var f = this, - d = f.getChart(), - g = d.getEventXY(h), - a = d.getCenter(), - c = g[0] - a[0], - b = g[1] - a[1]; - return Math.sqrt(c * c + b * b) - }, - onGestureStart: function(d) { - var c = this, - b = c.getRadius(d), - a = c.getEventRadius(d); - if (b >= a) { - c.lockEvents("drag"); - c.angle = c.getAngle(d); - c.oldRotations = {}; - return false - } - }, - onGesture: function(b) { - var a = this, - c = a.getAngle(b) - a.angle; - if (a.getLocks().drag === a) { - a.doRotateTo(c, true); - return false - } - }, - doRotateTo: function(d, a, b) { - var n = this, - l = n.getChart(), - k = l.getAxes(), - f = l.getSeries(), - m = n.oldRotations, - c, j, g, e, h; - if (!b) { - l.suspendAnimation() - } - for (e = 0, h = k.length; e < h; e++) { - c = k[e]; - g = m[c.getId()] || (m[c.getId()] = c.getRotation()); - c.setRotation(d + (a ? g : 0)) - } - for (e = 0, h = f.length; e < h; e++) { - j = f[e]; - g = m[j.getId()] || (m[j.getId()] = j.getRotation()); - j.setRotation(d + (a ? g : 0)) - } - n.setRotation(d + (a ? g : 0)); - n.fireEvent("rotate", n, n.getRotation()); - n.sync(); - if (!b) { - l.resumeAnimation() - } - }, - rotateTo: function(c, b, a) { - this.doRotateTo(c, b, a); - this.oldRotations = {} - }, - onGestureEnd: function(b) { - var a = this; - if (a.getLocks().drag === a) { - a.onGesture(b); - a.unlockEvents("drag"); - a.fireEvent("rotationEnd", a, a.getRotation()); - return false - } - }, - onRotate: function(a) {} -}); -Ext.define("Ext.chart.interactions.RotatePie3D", { - extend: "Ext.chart.interactions.Rotate", - type: "rotatePie3d", - alias: "interaction.rotatePie3d", - getAngle: function(g) { - var a = this.getChart(), - f = a.getInherited().rtl, - d = f ? -1 : 1, - h = g.getXY(), - c = a.element.getXY(), - b = a.getMainRect(); - return d * Math.atan2(h[1] - c[1] - b[3] * 0.5, h[0] - c[0] - b[2] * 0.5) - }, - getRadius: function(j) { - var f = this.getChart(), - a = f.getRadius(), - d = f.getSeries(), - h = d.length, - c = 0, - b, g; - for (; c < h; c++) { - b = d[c]; - if (b.isPie3D) { - g = b.getRadius(); - if (g > a) { - a = g - } - } - } - return a - } -}); -Ext.define("Ext.chart.plugin.ItemEvents", { - extend: "Ext.plugin.Abstract", - alias: "plugin.chartitemevents", - moveEvents: false, - mouseMoveEvents: { - mousemove: true, - mouseover: true, - mouseout: true - }, - itemMouseMoveEvents: { - itemmousemove: true, - itemmouseover: true, - itemmouseout: true - }, - init: function(b) { - var a = "handleEvent"; - this.chart = b; - b.addElementListener({ - click: a, - dblclick: a, - mousedown: a, - mousemove: a, - mouseup: a, - mouseover: a, - mouseout: a, - priority: 1001, - scope: this - }) - }, - hasItemMouseMoveListeners: function() { - var b = this.chart.hasListeners, - a; - for (a in this.itemMouseMoveEvents) { - if (a in b) { - return true - } - } - return false - }, - handleEvent: function(g) { - var d = this, - a = d.chart, - h = g.type in d.mouseMoveEvents, - c = d.lastItem, - f, b; - if (h && !d.hasItemMouseMoveListeners() && !d.moveEvents) { - return - } - f = a.getEventXY(g); - b = a.getItemForPoint(f[0], f[1]); - if (h && !Ext.Object.equals(b, c)) { - if (c) { - a.fireEvent("itemmouseout", a, c, g); - c.series.fireEvent("itemmouseout", c.series, c, g) - } - if (b) { - a.fireEvent("itemmouseover", a, b, g); - b.series.fireEvent("itemmouseover", b.series, b, g) - } - } - if (b) { - a.fireEvent("item" + g.type, a, b, g); - b.series.fireEvent("item" + g.type, b.series, b, g) - } - d.lastItem = b - } -}); -Ext.define("Ext.chart.series.Cartesian", { - extend: "Ext.chart.series.Series", - config: { - xField: null, - yField: null, - xAxis: null, - yAxis: null - }, - directions: ["X", "Y"], - fieldCategoryX: ["X"], - fieldCategoryY: ["Y"], - applyXAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - applyYAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - updateXAxis: function(a) { - a.processData(this) - }, - updateYAxis: function(a) { - a.processData(this) - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - getItemForPoint: function(a, g) { - if (this.getSprites()) { - var f = this, - d = f.getSprites()[0], - b = f.getStore(), - e, c; - if (f.getHidden()) { - return null - } - if (d) { - c = d.getIndexNearPoint(a, g); - if (c !== -1) { - e = { - series: f, - category: f.getItemInstancing() ? "items" : "markers", - index: c, - record: b.getData().items[c], - field: f.getYField(), - sprite: d - }; - return e - } - } - } - }, - createSprite: function() { - var c = this, - a = c.callParent(), - b = c.getChart(), - d = c.getXAxis(); - a.setAttributes({ - flipXY: b.getFlipXY(), - xAxis: d - }); - if (a.setAggregator && d && d.getAggregator) { - if (d.getAggregator) { - a.setAggregator({ - strategy: d.getAggregator() - }) - } else { - a.setAggregator({}) - } - } - return a - }, - getSprites: function() { - var d = this, - c = this.getChart(), - e = d.getAnimation() || c && c.getAnimation(), - b = d.getItemInstancing(), - f = d.sprites, - a; - if (!c) { - return [] - } - if (!f.length) { - a = d.createSprite() - } else { - a = f[0] - } - if (e) { - if (b) { - a.itemsMarker.getTemplate().setAnimation(e) - } - a.setAnimation(e) - } - return f - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getSubStyleWithTheme(), - c = a.fillStyle; - if (Ext.isArray(c)) { - c = c[0] - } - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - }, - getXRange: function() { - return [this.dataRange[0], this.dataRange[2]] - }, - getYRange: function() { - return [this.dataRange[1], this.dataRange[3]] - } -}); -Ext.define("Ext.chart.series.StackedCartesian", { - extend: "Ext.chart.series.Cartesian", - config: { - stacked: true, - splitStacks: true, - fullStack: false, - fullStackTotal: 100, - hidden: [] - }, - spriteAnimationCount: 0, - themeColorCount: function() { - var b = this, - a = b.getYField(); - return Ext.isArray(a) ? a.length : 1 - }, - updateStacked: function() { - this.processData() - }, - updateSplitStacks: function() { - this.processData() - }, - coordinateY: function() { - return this.coordinateStacked("Y", 1, 2) - }, - coordinateStacked: function(D, e, m) { - var F = this, - f = F.getStore(), - r = f.getData().items, - B = r.length, - c = F["get" + D + "Axis"](), - x = F.getHidden(), - a = F.getSplitStacks(), - z = F.getFullStack(), - l = F.getFullStackTotal(), - p = { - min: 0, - max: 0 - }, - n = F["fieldCategory" + D], - C = [], - o = [], - E = [], - h, A = F.getStacked(), - g = F.getSprites(), - q = [], - w, v, u, s, H, y, b, d, G, t; - if (!g.length) { - return - } - for (w = 0; w < n.length; w++) { - d = n[w]; - s = F.getFields([d]); - H = s.length; - for (v = 0; v < B; v++) { - C[v] = 0; - o[v] = 0; - E[v] = 0 - } - for (v = 0; v < H; v++) { - if (!x[v]) { - q[v] = F.coordinateData(r, s[v], c) - } - } - if (A && z) { - y = []; - if (a) { - b = [] - } - for (v = 0; v < B; v++) { - y[v] = 0; - if (a) { - b[v] = 0 - } - for (u = 0; u < H; u++) { - G = q[u]; - if (!G) { - continue - } - G = G[v]; - if (G >= 0 || !a) { - y[v] += G - } else { - if (G < 0) { - b[v] += G - } - } - } - } - } - for (v = 0; v < H; v++) { - t = {}; - if (x[v]) { - t["dataStart" + d] = C; - t["data" + d] = C; - g[v].setAttributes(t); - continue - } - G = q[v]; - if (A) { - h = []; - for (u = 0; u < B; u++) { - if (!G[u]) { - G[u] = 0 - } - if (G[u] >= 0 || !a) { - if (z && y[u]) { - G[u] *= l / y[u] - } - C[u] = o[u]; - o[u] += G[u]; - h[u] = o[u] - } else { - if (z && b[u]) { - G[u] *= l / b[u] - } - C[u] = E[u]; - E[u] += G[u]; - h[u] = E[u] - } - } - t["dataStart" + d] = C; - t["data" + d] = h; - F.getRangeOfData(C, p); - F.getRangeOfData(h, p) - } else { - t["dataStart" + d] = C; - t["data" + d] = G; - F.getRangeOfData(G, p) - } - g[v].setAttributes(t) - } - } - F.dataRange[e] = p.min; - F.dataRange[e + m] = p.max; - t = {}; - t["dataMin" + D] = p.min; - t["dataMax" + D] = p.max; - for (w = 0; w < g.length; w++) { - g[w].setAttributes(t) - } - }, - getFields: function(f) { - var e = this, - a = [], - c, b, d; - for (b = 0, d = f.length; b < d; b++) { - c = e["get" + f[b] + "Field"](); - if (Ext.isArray(c)) { - a.push.apply(a, c) - } else { - a.push(c) - } - } - return a - }, - updateLabelOverflowPadding: function(a) { - this.getLabel().setAttributes({ - labelOverflowPadding: a - }) - }, - getSprites: function() { - var k = this, - j = k.getChart(), - c = k.getAnimation() || j && j.getAnimation(), - f = k.getFields(k.fieldCategoryY), - b = k.getItemInstancing(), - h = k.sprites, - l, e = k.getHidden(), - g = false, - d, a = f.length; - if (!j) { - return [] - } - for (d = 0; d < a; d++) { - l = h[d]; - if (!l) { - l = k.createSprite(); - l.setAttributes({ - zIndex: -d - }); - l.setField(f[d]); - g = true; - e.push(false); - if (b) { - l.itemsMarker.getTemplate().setAttributes(k.getStyleByIndex(d)) - } else { - l.setAttributes(k.getStyleByIndex(d)) - } - } - if (c) { - if (b) { - l.itemsMarker.getTemplate().setAnimation(c) - } - l.setAnimation(c) - } - } - if (g) { - k.updateHidden(e) - } - return h - }, - getItemForPoint: function(k, j) { - if (this.getSprites()) { - var h = this, - b, g, m, a = h.getItemInstancing(), - f = h.getSprites(), - l = h.getStore(), - c = h.getHidden(), - n, d, e; - for (b = 0, g = f.length; b < g; b++) { - if (!c[b]) { - m = f[b]; - d = m.getIndexNearPoint(k, j); - if (d !== -1) { - e = h.getYField(); - n = { - series: h, - index: d, - category: a ? "items" : "markers", - record: l.getData().items[d], - field: typeof e === "string" ? e : e[b], - sprite: m - }; - return n - } - } - } - return null - } - }, - provideLegendInfo: function(e) { - var g = this, - f = g.getSprites(), - h = g.getTitle(), - j = g.getYField(), - d = g.getHidden(), - k = f.length === 1, - b, l, c, a; - for (c = 0; c < f.length; c++) { - b = g.getStyleByIndex(c); - l = b.fillStyle; - if (h) { - if (Ext.isArray(h)) { - a = h[c] - } else { - if (k) { - a = h - } - } - } else { - if (Ext.isArray(j)) { - a = j[c] - } else { - a = g.getId() - } - } - e.push({ - name: a, - mark: (Ext.isObject(l) ? l.stops && l.stops[0].color : l) || b.strokeStyle || "black", - disabled: d[c], - series: g.getId(), - index: c - }) - } - }, - onSpriteAnimationStart: function(a) { - this.spriteAnimationCount++; - if (this.spriteAnimationCount === 1) { - this.fireEvent("animationstart") - } - }, - onSpriteAnimationEnd: function(a) { - this.spriteAnimationCount--; - if (this.spriteAnimationCount === 0) { - this.fireEvent("animationend") - } - } -}); -Ext.define("Ext.chart.series.sprite.Series", { - extend: "Ext.draw.sprite.Sprite", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - inheritableStatics: { - def: { - processors: { - dataMinX: "number", - dataMaxX: "number", - dataMinY: "number", - dataMaxY: "number", - rangeX: "data", - rangeY: "data", - dataX: "data", - dataY: "data" - }, - defaults: { - dataMinX: 0, - dataMaxX: 1, - dataMinY: 0, - dataMaxY: 1, - rangeX: null, - rangeY: null, - dataX: null, - dataY: null - }, - triggers: { - dataX: "bbox", - dataY: "bbox", - dataMinX: "bbox", - dataMaxX: "bbox", - dataMinY: "bbox", - dataMaxY: "bbox" - } - } - }, - config: { - store: null, - series: null, - field: null - } -}); -Ext.define("Ext.chart.series.sprite.Cartesian", { - extend: "Ext.chart.series.sprite.Series", - inheritableStatics: { - def: { - processors: { - labels: "default", - labelOverflowPadding: "number", - selectionTolerance: "number", - flipXY: "bool", - renderer: "default", - visibleMinX: "number", - visibleMinY: "number", - visibleMaxX: "number", - visibleMaxY: "number", - innerWidth: "number", - innerHeight: "number" - }, - defaults: { - labels: null, - labelOverflowPadding: 10, - selectionTolerance: 20, - flipXY: false, - renderer: null, - transformFillStroke: false, - visibleMinX: 0, - visibleMinY: 0, - visibleMaxX: 1, - visibleMaxY: 1, - innerWidth: 1, - innerHeight: 1 - }, - triggers: { - dataX: "dataX,bbox", - dataY: "dataY,bbox", - visibleMinX: "panzoom", - visibleMinY: "panzoom", - visibleMaxX: "panzoom", - visibleMaxY: "panzoom", - innerWidth: "panzoom", - innerHeight: "panzoom" - }, - updaters: { - dataX: function(a) { - this.processDataX(); - this.scheduleUpdater(a, "dataY", ["dataY"]) - }, - dataY: function() { - this.processDataY() - }, - panzoom: function(c) { - var e = c.visibleMaxX - c.visibleMinX, - d = c.visibleMaxY - c.visibleMinY, - b = c.flipXY ? c.innerHeight : c.innerWidth, - g = !c.flipXY ? c.innerHeight : c.innerWidth, - a = this.getSurface(), - f = a ? a.getInherited().rtl : false; - if (f && !c.flipXY) { - c.translationX = b + c.visibleMinX * b / e - } else { - c.translationX = -c.visibleMinX * b / e - } - c.translationY = -c.visibleMinY * g / d; - c.scalingX = (f && !c.flipXY ? -1 : 1) * b / e; - c.scalingY = g / d; - c.scalingCenterX = 0; - c.scalingCenterY = 0; - this.applyTransformations(true) - } - } - } - }, - processDataY: Ext.emptyFn, - processDataX: Ext.emptyFn, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.dataMinX; - b.y = a.dataMinY; - b.width = a.dataMaxX - a.dataMinX; - b.height = a.dataMaxY - a.dataMinY - }, - binarySearch: function(d) { - var b = this.attr.dataX, - f = 0, - a = b.length; - if (d <= b[0]) { - return f - } - if (d >= b[a - 1]) { - return a - 1 - } - while (f + 1 < a) { - var c = (f + a) >> 1, - e = b[c]; - if (e === d) { - return c - } else { - if (e < d) { - f = c - } else { - a = c - } - } - } - return f - }, - render: function(b, c, g) { - var f = this, - a = f.attr, - e = a.inverseMatrix.clone(); - e.appendMatrix(b.inverseMatrix); - if (a.dataX === null || a.dataX === undefined) { - return - } - if (a.dataY === null || a.dataY === undefined) { - return - } - if (e.getXX() * e.getYX() || e.getXY() * e.getYY()) { - console.log("Cartesian Series sprite does not support rotation/sheering"); - return - } - var d = e.transformList([ - [g[0] - 1, g[3] + 1], - [g[0] + g[2] + 1, -1] - ]); - d = d[0].concat(d[1]); - f.renderClipped(b, c, d, g) - }, - renderClipped: Ext.emptyFn, - getIndexNearPoint: function(f, e) { - var w = this, - q = w.attr.matrix, - h = w.attr.dataX, - g = w.attr.dataY, - k = w.attr.selectionTolerance, - t, r, c = -1, - j = q.clone().prependMatrix(w.surfaceMatrix).inverse(), - u = j.transformPoint([f, e]), - b = j.transformPoint([f - k, e - k]), - n = j.transformPoint([f + k, e + k]), - a = Math.min(b[0], n[0]), - s = Math.max(b[0], n[0]), - l = Math.min(b[1], n[1]), - d = Math.max(b[1], n[1]), - m, v, o, p; - for (o = 0, p = h.length; o < p; o++) { - m = h[o]; - v = g[o]; - if (m > a && m < s && v > l && v < d) { - if (c === -1 || (Math.abs(m - u[0]) < t) && (Math.abs(v - u[1]) < r)) { - t = Math.abs(m - u[0]); - r = Math.abs(v - u[1]); - c = o - } - } - } - return c - } -}); -Ext.define("Ext.chart.series.sprite.StackedCartesian", { - extend: "Ext.chart.series.sprite.Cartesian", - inheritableStatics: { - def: { - processors: { - groupCount: "number", - groupOffset: "number", - dataStartY: "data" - }, - defaults: { - selectionTolerance: 20, - groupCount: 1, - groupOffset: 0, - dataStartY: null - }, - triggers: { - dataStartY: "dataY,bbox" - } - } - }, - getIndexNearPoint: function(e, d) { - var o = this, - q = o.attr.matrix, - h = o.attr.dataX, - f = o.attr.dataY, - u = o.attr.dataStartY, - l = o.attr.selectionTolerance, - s = 0.5, - r = Infinity, - b = -1, - k = q.clone().prependMatrix(this.surfaceMatrix).inverse(), - t = k.transformPoint([e, d]), - a = k.transformPoint([e - l, d - l]), - n = k.transformPoint([e + l, d + l]), - m = Math.min(a[1], n[1]), - c = Math.max(a[1], n[1]), - j, g; - for (var p = 0; p < h.length; p++) { - if (Math.min(u[p], f[p]) <= c && m <= Math.max(u[p], f[p])) { - j = Math.abs(h[p] - t[0]); - g = Math.max(-Math.min(f[p] - t[1], t[1] - u[p]), 0); - if (j < s && g <= r) { - s = j; - r = g; - b = p - } - } - } - return b - } -}); -Ext.define("Ext.chart.series.sprite.Area", { - alias: "sprite.areaSeries", - extend: "Ext.chart.series.sprite.StackedCartesian", - inheritableStatics: { - def: { - processors: { - step: "bool" - }, - defaults: { - step: false - } - } - }, - renderClipped: function(q, s, A) { - var B = this, - p = B.attr, - l = p.dataX, - j = p.dataY, - C = p.dataStartY, - t = p.matrix, - h, g, v, f, d, z, w, e = t.elements[0], - m = t.elements[4], - o = t.elements[3], - k = t.elements[5], - c = B.surfaceMatrix, - n = {}, - r = Math.min(A[0], A[2]), - u = Math.max(A[0], A[2]), - b = Math.max(0, this.binarySearch(r)), - a = Math.min(l.length - 1, this.binarySearch(u) + 1); - s.beginPath(); - z = l[b] * e + m; - w = j[b] * o + k; - s.moveTo(z, w); - if (p.step) { - d = w; - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, d); - s.lineTo(h, d = g) - } - } else { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, g) - } - } - if (C) { - if (p.step) { - f = l[a] * e + m; - for (v = a; v >= b; v--) { - h = l[v] * e + m; - g = C[v] * o + k; - s.lineTo(f, g); - s.lineTo(f = h, g) - } - } else { - for (v = a; v >= b; v--) { - h = l[v] * e + m; - g = C[v] * o + k; - s.lineTo(h, g) - } - } - } else { - s.lineTo(l[a] * e + m, g); - s.lineTo(l[a] * e + m, k); - s.lineTo(z, k); - s.lineTo(z, j[v] * o + k) - } - if (p.transformFillStroke) { - p.matrix.toContext(s) - } - s.fill(); - if (p.transformFillStroke) { - p.inverseMatrix.toContext(s) - } - s.beginPath(); - s.moveTo(z, w); - if (p.step) { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, d); - s.lineTo(h, d = g); - n.translationX = c.x(h, g); - n.translationY = c.y(h, g); - B.putMarker("markers", n, v, !p.renderer) - } - } else { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, g); - n.translationX = c.x(h, g); - n.translationY = c.y(h, g); - B.putMarker("markers", n, v, !p.renderer) - } - } - if (p.transformFillStroke) { - p.matrix.toContext(s) - } - s.stroke() - } -}); -Ext.define("Ext.chart.series.Area", { - extend: "Ext.chart.series.StackedCartesian", - alias: "series.area", - type: "area", - seriesType: "areaSeries", - requires: ["Ext.chart.series.sprite.Area"], - config: { - splitStacks: false - } -}); -Ext.define("Ext.chart.series.sprite.Bar", { - alias: "sprite.barSeries", - extend: "Ext.chart.series.sprite.StackedCartesian", - inheritableStatics: { - def: { - processors: { - minBarWidth: "number", - maxBarWidth: "number", - minGapWidth: "number", - radius: "number", - inGroupGapWidth: "number" - }, - defaults: { - minBarWidth: 2, - maxBarWidth: 100, - minGapWidth: 5, - inGroupGapWidth: 3, - radius: 0 - } - } - }, - drawLabel: function(k, i, s, h, o) { - var q = this, - n = q.attr, - f = q.getMarker("labels"), - d = f.getTemplate(), - l = q.labelCfg || (q.labelCfg = {}), - c = q.surfaceMatrix, - j = n.labelOverflowPadding, - b = d.attr.display, - m = d.attr.orientation, - g, e, a, r, t, p; - l.x = c.x(i, h); - l.y = c.y(i, h); - if (!n.flipXY) { - l.rotationRads = -Math.PI * 0.5 - } else { - l.rotationRads = 0 - } - l.calloutVertical = !n.flipXY; - switch (m) { - case "horizontal": - l.rotationRads = 0; - l.calloutVertical = false; - break; - case "vertical": - l.rotationRads = -Math.PI * 0.5; - l.calloutVertical = true; - break - } - l.text = k; - if (d.attr.renderer) { - p = [k, f, l, { - store: q.getStore() - }, o]; - r = Ext.callback(d.attr.renderer, null, p, 0, q.getSeries()); - if (typeof r === "string") { - l.text = r - } else { - if (typeof r === "object") { - if ("text" in r) { - l.text = r.text - } - t = true - } - } - } - a = q.getMarkerBBox("labels", o, true); - if (!a) { - q.putMarker("labels", l, o); - a = q.getMarkerBBox("labels", o, true) - } - e = (a.width / 2 + j); - if (s > h) { - e = -e - } - if ((m === "horizontal" && n.flipXY) || (m === "vertical" && !n.flipXY) || !m) { - g = (b === "insideStart") ? s + e : h - e - } else { - g = (b === "insideStart") ? s + j * 2 : h - j * 2 - } - l.x = c.x(i, g); - l.y = c.y(i, g); - g = (b === "insideStart") ? s - e : h + e; - l.calloutPlaceX = c.x(i, g); - l.calloutPlaceY = c.y(i, g); - g = (b === "insideStart") ? s : h; - l.calloutStartX = c.x(i, g); - l.calloutStartY = c.y(i, g); - if (s > h) { - e = -e - } - if (Math.abs(h - s) <= e * 2 || b === "outside") { - l.callout = 1 - } else { - l.callout = 0 - } - if (t) { - Ext.apply(l, r) - } - q.putMarker("labels", l, o) - }, - drawBar: function(l, b, d, c, h, k, a, e) { - var g = this, - j = {}, - f = g.attr.renderer, - i; - j.x = c; - j.y = h; - j.width = k - c; - j.height = a - h; - j.radius = g.attr.radius; - if (f) { - i = Ext.callback(f, null, [g, j, { - store: g.getStore() - }, e], 0, g.getSeries()); - Ext.apply(j, i) - } - g.putMarker("items", j, e, !f) - }, - renderClipped: function(G, u, F, C) { - if (this.cleanRedraw) { - return - } - var q = this, - o = q.attr, - w = o.dataX, - v = o.dataY, - H = o.labels, - n = o.dataStartY, - m = o.groupCount, - E = o.groupOffset - (m - 1) * 0.5, - z = o.inGroupGapWidth, - t = u.lineWidth, - D = o.matrix, - B = D.elements[0], - j = D.elements[3], - e = D.elements[4], - d = G.roundPixel(D.elements[5]) - 1, - J = (B < 0 ? -1 : 1) * B - o.minGapWidth, - k = (Math.min(J, o.maxBarWidth) - z * (m - 1)) / m, - A = G.roundPixel(Math.max(o.minBarWidth, k)), - c = q.surfaceMatrix, - g, I, b, h, K, a, l = 0.5 * o.lineWidth, - L = Math.min(F[0], F[2]), - x = Math.max(F[0], F[2]), - y = Math.max(0, Math.floor(L)), - p = Math.min(w.length - 1, Math.ceil(x)), - f = H && q.getMarker("labels"), - s, r; - for (K = y; K <= p; K++) { - s = n ? n[K] : 0; - r = v[K]; - a = w[K] * B + e + E * (A + z); - g = G.roundPixel(a - A / 2) + l; - h = G.roundPixel(r * j + d + t); - I = G.roundPixel(a + A / 2) - l; - b = G.roundPixel(s * j + d + t); - q.drawBar(u, G, F, g, h - l, I, b - l, K); - if (f && H[K] != null) { - q.drawLabel(H[K], a, b, h, K) - } - q.putMarker("markers", { - translationX: c.x(a, h), - translationY: c.y(a, h) - }, K, true) - } - }, - getIndexNearPoint: function(l, k) { - var m = this, - g = m.attr, - h = g.dataX, - a = m.getSurface(), - b = a.getRect() || [0, 0, 0, 0], - j = b[3], - e, d, c, n, f = -1; - if (g.flipXY) { - e = j - k; - if (a.getInherited().rtl) { - d = b[2] - l - } else { - d = l - } - } else { - e = l; - d = j - k - } - for (c = 0; c < h.length; c++) { - n = m.getMarkerBBox("items", c); - if (Ext.draw.Draw.isPointInBBox(e, d, n)) { - f = c; - break - } - } - return f - } -}); -Ext.define("Ext.chart.series.Bar", { - extend: "Ext.chart.series.StackedCartesian", - alias: "series.bar", - type: "bar", - seriesType: "barSeries", - requires: ["Ext.chart.series.sprite.Bar", "Ext.draw.sprite.Rect"], - config: { - itemInstancing: { - type: "rect", - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0, - radius: 0 - } - } - } - }, - getItemForPoint: function(a, f) { - if (this.getSprites()) { - var d = this, - c = d.getChart(), - e = c.getInnerPadding(), - b = c.getInherited().rtl; - arguments[0] = a + (b ? e.right : -e.left); - arguments[1] = f + e.bottom; - return d.callParent(arguments) - } - }, - updateXAxis: function(a) { - a.setLabelInSpan(true); - this.callParent(arguments) - }, - updateHidden: function(a) { - this.callParent(arguments); - this.updateStacked() - }, - updateStacked: function(c) { - var e = this, - g = e.getSprites(), - d = g.length, - f = [], - a = {}, - b; - for (b = 0; b < d; b++) { - if (!g[b].attr.hidden) { - f.push(g[b]) - } - } - d = f.length; - if (e.getStacked()) { - a.groupCount = 1; - a.groupOffset = 0; - for (b = 0; b < d; b++) { - f[b].setAttributes(a) - } - } else { - a.groupCount = f.length; - for (b = 0; b < d; b++) { - a.groupOffset = b; - f[b].setAttributes(a) - } - } - e.callParent(arguments) - } -}); -Ext.define("Ext.chart.series.sprite.Bar3D", { - extend: "Ext.chart.series.sprite.Bar", - alias: "sprite.bar3dSeries", - requires: ["Ext.draw.gradient.Linear"], - inheritableStatics: { - def: { - processors: { - depthWidthRatio: "number", - saturationFactor: "number", - brightnessFactor: "number", - colorSpread: "number" - }, - defaults: { - depthWidthRatio: 1 / 3, - saturationFactor: 1, - brightnessFactor: 1, - colorSpread: 1, - transformFillStroke: true - }, - triggers: { - groupCount: "panzoom" - }, - updaters: { - panzoom: function(c) { - var g = this, - e = c.visibleMaxX - c.visibleMinX, - d = c.visibleMaxY - c.visibleMinY, - b = c.flipXY ? c.innerHeight : c.innerWidth, - h = !c.flipXY ? c.innerHeight : c.innerWidth, - a = g.getSurface(), - f = a ? a.getInherited().rtl : false; - if (f && !c.flipXY) { - c.translationX = b + c.visibleMinX * b / e - } else { - c.translationX = -c.visibleMinX * b / e - } - c.translationY = -c.visibleMinY * (h - g.depth) / d; - c.scalingX = (f && !c.flipXY ? -1 : 1) * b / e; - c.scalingY = (h - g.depth) / d; - c.scalingCenterX = 0; - c.scalingCenterY = 0; - g.applyTransformations(true) - } - } - } - }, - config: { - showStroke: false - }, - depth: 0, - drawBar: function(p, b, d, c, l, o, a, h) { - var k = this, - i = k.attr, - n = {}, - j = i.renderer, - m, g, f, e; - n.x = (c + o) * 0.5; - n.y = l; - n.width = (o - c) * 0.75; - n.height = a - l; - n.depth = g = n.width * i.depthWidthRatio; - n.orientation = i.flipXY ? "horizontal" : "vertical"; - n.saturationFactor = i.saturationFactor; - n.brightnessFactor = i.brightnessFactor; - n.colorSpread = i.colorSpread; - if (g !== k.depth) { - k.depth = g; - f = k.getSeries(); - f.fireEvent("depthchange", f, g) - } - if (j) { - e = [k, n, { - store: k.getStore() - }, h]; - m = Ext.callback(j, null, e, 0, k.getSeries()); - Ext.apply(n, m) - } - k.putMarker("items", n, h, !j) - } -}); -Ext.define("Ext.chart.series.sprite.Box", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.box", - type: "box", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number", - depth: "number", - orientation: "enums(vertical,horizontal)", - showStroke: "bool", - saturationFactor: "number", - brightnessFactor: "number", - colorSpread: "number" - }, - triggers: { - x: "bbox", - y: "bbox", - width: "bbox", - height: "bbox", - depth: "bbox", - orientation: "bbox" - }, - defaults: { - x: 0, - y: 0, - width: 8, - height: 8, - depth: 8, - orientation: "vertical", - showStroke: false, - saturationFactor: 1, - brightnessFactor: 1, - colorSpread: 1, - lineJoin: "bevel" - } - } - }, - constructor: function(a) { - this.callParent([a]); - this.topGradient = new Ext.draw.gradient.Linear({}); - this.rightGradient = new Ext.draw.gradient.Linear({}); - this.frontGradient = new Ext.draw.gradient.Linear({}) - }, - updatePlainBBox: function(d) { - var c = this.attr, - b = c.x, - g = c.y, - e = c.width, - a = c.height, - f = c.depth; - d.x = b - e * 0.5; - d.width = e + f; - if (a > 0) { - d.y = g; - d.height = a + f - } else { - d.y = g + f; - d.height = a - f - } - }, - render: function(l, m) { - var u = this, - k = u.attr, - r = k.x, - j = k.y, - f = j + k.height, - i = j < f, - e = k.width * 0.5, - v = k.depth, - d = k.orientation === "horizontal", - g = k.globalAlpha < 1, - c = k.fillStyle, - n = Ext.draw.Color.create(c.isGradient ? c.getStops()[0].color : c), - h = k.saturationFactor, - o = k.brightnessFactor, - t = k.colorSpread, - b = n.getHSV(), - a = {}, - s, q, p; - if (!k.showStroke) { - m.strokeStyle = Ext.draw.Color.RGBA_NONE - } - if (i) { - p = j; - j = f; - f = p - } - u.topGradient.setDegrees(d ? 0 : 80); - u.topGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 + t * 0.1) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.11) * o, 0, 1)) - }]); - u.rightGradient.setDegrees(d ? 45 : 90); - u.rightGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.14) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 + t * 0.4) * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.32) * o, 0, 1)) - }]); - if (d) { - u.frontGradient.setDegrees(0) - } else { - u.frontGradient.setRadians(Math.atan2(j - f, e * 2)) - } - u.frontGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 - t * 0.1) * h, 0, 1), Ext.Number.constrain((0.5 + t * 0.1) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 + t * 0.1) * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.23) * o, 0, 1)) - }]); - if (g || i) { - m.beginPath(); - m.moveTo(r - e, f); - m.lineTo(r - e + v, f + v); - m.lineTo(r + e + v, f + v); - m.lineTo(r + e, f); - m.closePath(); - a.x = r - e; - a.y = j; - a.width = e + v; - a.height = v; - m.fillStyle = (d ? u.rightGradient : u.topGradient).generateGradient(m, a); - m.fillStroke(k) - } - if (g) { - m.beginPath(); - m.moveTo(r - e, j); - m.lineTo(r - e + v, j + v); - m.lineTo(r - e + v, f + v); - m.lineTo(r - e, f); - m.closePath(); - a.x = r + e; - a.y = f; - a.width = v; - a.height = j + v - f; - m.fillStyle = (d ? u.topGradient : u.rightGradient).generateGradient(m, a); - m.fillStroke(k) - } - q = l.roundPixel(j); - m.beginPath(); - m.moveTo(r - e, q); - m.lineTo(r - e + v, j + v); - m.lineTo(r + e + v, j + v); - m.lineTo(r + e, q); - m.closePath(); - a.x = r - e; - a.y = j; - a.width = e + v; - a.height = v; - m.fillStyle = (d ? u.rightGradient : u.topGradient).generateGradient(m, a); - m.fillStroke(k); - s = l.roundPixel(r + e); - m.beginPath(); - m.moveTo(s, l.roundPixel(j)); - m.lineTo(r + e + v, j + v); - m.lineTo(r + e + v, f + v); - m.lineTo(s, f); - m.closePath(); - a.x = r + e; - a.y = f; - a.width = v; - a.height = j + v - f; - m.fillStyle = (d ? u.topGradient : u.rightGradient).generateGradient(m, a); - m.fillStroke(k); - s = l.roundPixel(r + e); - q = l.roundPixel(j); - m.beginPath(); - m.moveTo(r - e, f); - m.lineTo(r - e, q); - m.lineTo(s, q); - m.lineTo(s, f); - m.closePath(); - a.x = r - e; - a.y = f; - a.width = e * 2; - a.height = j - f; - m.fillStyle = u.frontGradient.generateGradient(m, a); - m.fillStroke(k) - } -}); -Ext.define("Ext.chart.series.Bar3D", { - extend: "Ext.chart.series.Bar", - requires: ["Ext.chart.series.sprite.Bar3D", "Ext.chart.series.sprite.Box"], - alias: "series.bar3d", - type: "bar3d", - seriesType: "bar3dSeries", - config: { - itemInstancing: { - type: "box", - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0, - depth: 0 - } - } - }, - highlightCfg: { - opacity: 0.8 - } - }, - getSprites: function() { - var c = this.callParent(arguments), - b, d, a; - for (a = 0; a < c.length; a++) { - b = c[a]; - d = b.attr.zIndex; - if (d < 0) { - b.setAttributes({ - zIndex: -d - }) - } - if (b.setSeries) { - b.setSeries(this) - } - } - return c - }, - getDepth: function() { - var a = this.getSprites()[0]; - return a ? (a.depth || 0) : 0 - }, - getItemForPoint: function(m, k) { - if (this.getSprites()) { - var j = this, - b, o, a = j.getItemInstancing(), - h = j.getSprites(), - n = j.getStore(), - c = j.getHidden(), - g = j.getChart(), - l = g.getInnerPadding(), - f = g.getInherited().rtl, - p, d, e; - m = m + (f ? l.right : -l.left); - k = k + l.bottom; - for (b = h.length - 1; b >= 0; b--) { - if (!c[b]) { - o = h[b]; - d = o.getIndexNearPoint(m, k); - if (d !== -1) { - e = j.getYField(); - p = { - series: j, - index: d, - category: a ? "items" : "markers", - record: n.getData().items[d], - field: typeof e === "string" ? e : e[b], - sprite: o - }; - return p - } - } - } - return null - } - } -}); -Ext.define("Ext.draw.LimitedCache", { - config: { - limit: 40, - feeder: function() { - return 0 - }, - scope: null - }, - cache: null, - constructor: function(a) { - this.cache = {}; - this.cache.list = []; - this.cache.tail = 0; - this.initConfig(a) - }, - get: function(e) { - var c = this.cache, - b = this.getLimit(), - a = this.getFeeder(), - d = this.getScope() || this; - if (c[e]) { - return c[e].value - } - if (c.list[c.tail]) { - delete c[c.list[c.tail].cacheId] - } - c[e] = c.list[c.tail] = { - value: a.apply(d, Array.prototype.slice.call(arguments, 1)), - cacheId: e - }; - c.tail++; - if (c.tail === b) { - c.tail = 0 - } - return c[e].value - }, - clear: function() { - this.cache = {}; - this.cache.list = []; - this.cache.tail = 0 - } -}); -Ext.define("Ext.draw.SegmentTree", { - config: { - strategy: "double" - }, - time: function(m, l, n, c, E, d, e) { - var f = 0, - o, A, s = new Date(n[m.startIdx[0]]), - x = new Date(n[m.endIdx[l - 1]]), - D = Ext.Date, - u = [ - [D.MILLI, 1, "ms1", null], - [D.MILLI, 2, "ms2", "ms1"], - [D.MILLI, 5, "ms5", "ms1"], - [D.MILLI, 10, "ms10", "ms5"], - [D.MILLI, 50, "ms50", "ms10"], - [D.MILLI, 100, "ms100", "ms50"], - [D.MILLI, 500, "ms500", "ms100"], - [D.SECOND, 1, "s1", "ms500"], - [D.SECOND, 10, "s10", "s1"], - [D.SECOND, 30, "s30", "s10"], - [D.MINUTE, 1, "mi1", "s10"], - [D.MINUTE, 5, "mi5", "mi1"], - [D.MINUTE, 10, "mi10", "mi5"], - [D.MINUTE, 30, "mi30", "mi10"], - [D.HOUR, 1, "h1", "mi30"], - [D.HOUR, 6, "h6", "h1"], - [D.HOUR, 12, "h12", "h6"], - [D.DAY, 1, "d1", "h12"], - [D.DAY, 7, "d7", "d1"], - [D.MONTH, 1, "mo1", "d1"], - [D.MONTH, 3, "mo3", "mo1"], - [D.MONTH, 6, "mo6", "mo3"], - [D.YEAR, 1, "y1", "mo3"], - [D.YEAR, 5, "y5", "y1"], - [D.YEAR, 10, "y10", "y5"], - [D.YEAR, 100, "y100", "y10"] - ], - z, b, k = f, - F = l, - j = false, - r = m.startIdx, - h = m.endIdx, - w = m.minIdx, - C = m.maxIdx, - a = m.open, - y = m.close, - g = m.minX, - q = m.minY, - p = m.maxX, - B = m.maxY, - v, t; - for (z = 0; l > f + 1 && z < u.length; z++) { - s = new Date(n[r[0]]); - b = u[z]; - s = D.align(s, b[0], b[1]); - if (D.diff(s, x, b[0]) > n.length * 2 * b[1]) { - continue - } - if (b[3] && m.map["time_" + b[3]]) { - o = m.map["time_" + b[3]][0]; - A = m.map["time_" + b[3]][1] - } else { - o = k; - A = F - } - f = l; - t = s; - j = true; - r[l] = r[o]; - h[l] = h[o]; - w[l] = w[o]; - C[l] = C[o]; - a[l] = a[o]; - y[l] = y[o]; - g[l] = g[o]; - q[l] = q[o]; - p[l] = p[o]; - B[l] = B[o]; - t = Ext.Date.add(t, b[0], b[1]); - for (v = o + 1; v < A; v++) { - if (n[h[v]] < +t) { - h[l] = h[v]; - y[l] = y[v]; - if (B[v] > B[l]) { - B[l] = B[v]; - p[l] = p[v]; - C[l] = C[v] - } - if (q[v] < q[l]) { - q[l] = q[v]; - g[l] = g[v]; - w[l] = w[v] - } - } else { - l++; - r[l] = r[v]; - h[l] = h[v]; - w[l] = w[v]; - C[l] = C[v]; - a[l] = a[v]; - y[l] = y[v]; - g[l] = g[v]; - q[l] = q[v]; - p[l] = p[v]; - B[l] = B[v]; - t = Ext.Date.add(t, b[0], b[1]) - } - } - if (l > f) { - m.map["time_" + b[2]] = [f, l] - } - } - }, - "double": function(h, u, j, a, t, b, c) { - var e = 0, - k, f = 1, - n, d, v, g, s, l, m, r, q, p, o; - while (u > e + 1) { - k = e; - e = u; - f += f; - for (n = k; n < e; n += 2) { - if (n === e - 1) { - d = h.startIdx[n]; - v = h.endIdx[n]; - g = h.minIdx[n]; - s = h.maxIdx[n]; - l = h.open[n]; - m = h.close[n]; - r = h.minX[n]; - q = h.minY[n]; - p = h.maxX[n]; - o = h.maxY[n] - } else { - d = h.startIdx[n]; - v = h.endIdx[n + 1]; - l = h.open[n]; - m = h.close[n]; - if (h.minY[n] <= h.minY[n + 1]) { - g = h.minIdx[n]; - r = h.minX[n]; - q = h.minY[n] - } else { - g = h.minIdx[n + 1]; - r = h.minX[n + 1]; - q = h.minY[n + 1] - } - if (h.maxY[n] >= h.maxY[n + 1]) { - s = h.maxIdx[n]; - p = h.maxX[n]; - o = h.maxY[n] - } else { - s = h.maxIdx[n + 1]; - p = h.maxX[n + 1]; - o = h.maxY[n + 1] - } - } - h.startIdx[u] = d; - h.endIdx[u] = v; - h.minIdx[u] = g; - h.maxIdx[u] = s; - h.open[u] = l; - h.close[u] = m; - h.minX[u] = r; - h.minY[u] = q; - h.maxX[u] = p; - h.maxY[u] = o; - u++ - } - h.map["double_" + f] = [e, u] - } - }, - none: Ext.emptyFn, - aggregateData: function(h, a, r, c, d) { - var b = h.length, - e = [], - s = [], - f = [], - q = [], - j = [], - p = [], - n = [], - o = [], - m = [], - k = [], - g = { - startIdx: e, - endIdx: s, - minIdx: f, - maxIdx: q, - open: j, - minX: p, - minY: n, - maxX: o, - maxY: m, - close: k - }, - l; - for (l = 0; l < b; l++) { - e[l] = l; - s[l] = l; - f[l] = l; - q[l] = l; - j[l] = a[l]; - p[l] = h[l]; - n[l] = c[l]; - o[l] = h[l]; - m[l] = r[l]; - k[l] = d[l] - } - g.map = { - original: [0, b] - }; - if (b) { - this[this.getStrategy()](g, b, h, a, r, c, d) - } - return g - }, - binarySearchMin: function(c, g, a, e) { - var b = this.dataX; - if (e <= b[c.startIdx[0]]) { - return g - } - if (e >= b[c.startIdx[a - 1]]) { - return a - 1 - } - while (g + 1 < a) { - var d = (g + a) >> 1, - f = b[c.startIdx[d]]; - if (f === e) { - return d - } else { - if (f < e) { - g = d - } else { - a = d - } - } - } - return g - }, - binarySearchMax: function(c, g, a, e) { - var b = this.dataX; - if (e <= b[c.endIdx[0]]) { - return g - } - if (e >= b[c.endIdx[a - 1]]) { - return a - 1 - } - while (g + 1 < a) { - var d = (g + a) >> 1, - f = b[c.endIdx[d]]; - if (f === e) { - return d - } else { - if (f < e) { - g = d - } else { - a = d - } - } - } - return a - }, - constructor: function(a) { - this.initConfig(a) - }, - setData: function(d, a, b, c, e) { - if (!b) { - e = c = b = a - } - this.dataX = d; - this.dataOpen = a; - this.dataHigh = b; - this.dataLow = c; - this.dataClose = e; - if (d.length === b.length && d.length === c.length) { - this.cache = this.aggregateData(d, a, b, c, e) - } - }, - getAggregation: function(d, k, i) { - if (!this.cache) { - return null - } - var c = Infinity, - g = this.dataX[this.dataX.length - 1] - this.dataX[0], - l = this.cache.map, - m = l.original, - a, e, j, b, f, h; - for (a in l) { - e = l[a]; - j = e[1] - e[0] - 1; - b = g / j; - if (i <= b && b < c) { - m = e; - c = b - } - } - f = Math.max(this.binarySearchMin(this.cache, m[0], m[1], d), m[0]); - h = Math.min(this.binarySearchMax(this.cache, m[0], m[1], k) + 1, m[1]); - return { - data: this.cache, - start: f, - end: h - } - } -}); -Ext.define("Ext.chart.series.sprite.Aggregative", { - extend: "Ext.chart.series.sprite.Cartesian", - requires: ["Ext.draw.LimitedCache", "Ext.draw.SegmentTree"], - inheritableStatics: { - def: { - processors: { - dataHigh: "data", - dataLow: "data", - dataClose: "data" - }, - aliases: { - dataOpen: "dataY" - }, - defaults: { - dataHigh: null, - dataLow: null, - dataClose: null - } - } - }, - config: { - aggregator: {} - }, - applyAggregator: function(b, a) { - return Ext.factory(b, Ext.draw.SegmentTree, a) - }, - constructor: function() { - this.callParent(arguments) - }, - processDataY: function() { - var d = this, - b = d.attr, - e = b.dataHigh, - a = b.dataLow, - f = b.dataClose, - c = b.dataY; - d.callParent(arguments); - if (b.dataX && c && c.length > 0) { - if (e) { - d.getAggregator().setData(b.dataX, b.dataY, e, a, f) - } else { - d.getAggregator().setData(b.dataX, b.dataY) - } - } - }, - getGapWidth: function() { - return 1 - }, - renderClipped: function(b, c, g, f) { - var e = this, - d = Math.min(g[0], g[2]), - a = Math.max(g[0], g[2]), - h = e.getAggregator() && e.getAggregator().getAggregation(d, a, (a - d) / f[2] * e.getGapWidth()); - if (h) { - e.dataStart = h.data.startIdx[h.start]; - e.dataEnd = h.data.endIdx[h.end - 1]; - e.renderAggregates(h.data, h.start, h.end, b, c, g, f) - } - } -}); -Ext.define("Ext.chart.series.sprite.CandleStick", { - alias: "sprite.candlestickSeries", - extend: "Ext.chart.series.sprite.Aggregative", - inheritableStatics: { - def: { - processors: { - raiseStyle: function(b, a) { - return Ext.merge({}, a || {}, b) - }, - dropStyle: function(b, a) { - return Ext.merge({}, a || {}, b) - }, - barWidth: "number", - padding: "number", - ohlcType: "enums(candlestick,ohlc)" - }, - defaults: { - raiseStyle: { - strokeStyle: "green", - fillStyle: "green" - }, - dropStyle: { - strokeStyle: "red", - fillStyle: "red" - }, - planar: false, - barWidth: 15, - padding: 3, - lineJoin: "miter", - miterLimit: 5, - ohlcType: "candlestick" - }, - triggers: { - raiseStyle: "raiseStyle", - dropStyle: "dropStyle" - }, - updaters: { - raiseStyle: function() { - this.raiseTemplate && this.raiseTemplate.setAttributes(this.attr.raiseStyle) - }, - dropStyle: function() { - this.dropTemplate && this.dropTemplate.setAttributes(this.attr.dropStyle) - } - } - } - }, - candlestick: function(i, c, a, e, h, f, b) { - var d = Math.min(c, h), - g = Math.max(c, h); - i.moveTo(f, e); - i.lineTo(f, g); - i.moveTo(f + b, g); - i.lineTo(f + b, d); - i.lineTo(f - b, d); - i.lineTo(f - b, g); - i.closePath(); - i.moveTo(f, a); - i.lineTo(f, d) - }, - ohlc: function(b, d, e, a, f, c, g) { - b.moveTo(c, e); - b.lineTo(c, a); - b.moveTo(c, d); - b.lineTo(c - g, d); - b.moveTo(c, f); - b.lineTo(c + g, f) - }, - constructor: function() { - this.callParent(arguments); - this.raiseTemplate = new Ext.draw.sprite.Rect({ - parent: this - }); - this.dropTemplate = new Ext.draw.sprite.Rect({ - parent: this - }) - }, - getGapWidth: function() { - var a = this.attr, - b = a.barWidth, - c = a.padding; - return b + c - }, - renderAggregates: function(d, c, b, t, u, z) { - var D = this, - s = this.attr, - j = s.dataX, - v = s.matrix, - e = v.getXX(), - r = v.getYY(), - l = v.getDX(), - h = v.getDY(), - o = s.barWidth / e, - C, k = s.ohlcType, - f = Math.round(o * 0.5 * e), - a = d.open, - y = d.close, - B = d.maxY, - p = d.minY, - q = d.startIdx, - m, g, E, n, A, x, w = s.lineWidth * t.devicePixelRatio / 2; - w -= Math.floor(w); - u.save(); - C = this.raiseTemplate; - C.useAttributes(u, z); - u.beginPath(); - for (x = c; x < b; x++) { - if (a[x] <= y[x]) { - m = Math.round(a[x] * r + h) + w; - g = Math.round(B[x] * r + h) + w; - E = Math.round(p[x] * r + h) + w; - n = Math.round(y[x] * r + h) + w; - A = Math.round(j[q[x]] * e + l) + w; - D[k](u, m, g, E, n, A, f) - } - } - u.fillStroke(C.attr); - u.restore(); - u.save(); - C = this.dropTemplate; - C.useAttributes(u, z); - u.beginPath(); - for (x = c; x < b; x++) { - if (a[x] > y[x]) { - m = Math.round(a[x] * r + h) + w; - g = Math.round(B[x] * r + h) + w; - E = Math.round(p[x] * r + h) + w; - n = Math.round(y[x] * r + h) + w; - A = Math.round(j[q[x]] * e + l) + w; - D[k](u, m, g, E, n, A, f) - } - } - u.fillStroke(C.attr); - u.restore() - } -}); -Ext.define("Ext.chart.series.CandleStick", { - extend: "Ext.chart.series.Cartesian", - requires: ["Ext.chart.series.sprite.CandleStick"], - alias: "series.candlestick", - type: "candlestick", - seriesType: "candlestickSeries", - config: { - openField: null, - highField: null, - lowField: null, - closeField: null - }, - fieldCategoryY: ["Open", "High", "Low", "Close"], - themeColorCount: function() { - return 2 - } -}); -Ext.define("Ext.chart.series.Polar", { - extend: "Ext.chart.series.Series", - config: { - rotation: 0, - radius: null, - center: [0, 0], - offsetX: 0, - offsetY: 0, - showInLegend: true, - xField: null, - yField: null, - angleField: null, - radiusField: null, - xAxis: null, - yAxis: null - }, - directions: ["X", "Y"], - fieldCategoryX: ["X"], - fieldCategoryY: ["Y"], - deprecatedConfigs: { - field: "angleField", - lengthField: "radiusField" - }, - constructor: function(b) { - var c = this, - a = c.getConfigurator(), - e = a.configs, - d; - if (b) { - for (d in c.deprecatedConfigs) { - if (d in b && !(b in e)) { - Ext.raise("'" + d + "' config has been deprecated. Please use the '" + c.deprecatedConfigs[d] + "' config instead.") - } - } - } - c.callParent([b]) - }, - getXField: function() { - return this.getAngleField() - }, - updateXField: function(a) { - this.setAngleField(a) - }, - getYField: function() { - return this.getRadiusField() - }, - updateYField: function(a) { - this.setRadiusField(a) - }, - applyXAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - applyYAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - getXRange: function() { - return [this.dataRange[0], this.dataRange[2]] - }, - getYRange: function() { - return [this.dataRange[1], this.dataRange[3]] - }, - themeColorCount: function() { - var c = this, - a = c.getStore(), - b = a && a.getCount() || 0; - return b - }, - isStoreDependantColorCount: true, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer(), - centerX: 0, - centerY: 0, - rotationCenterX: 0, - rotationCenterY: 0 - } - }, - applyRotation: function(a) { - return Ext.draw.sprite.AttributeParser.angle(a) - }, - updateRotation: function(a) { - var b = this.getSprites(); - if (b && b[0]) { - b[0].setAttributes({ - baseRotation: a - }) - } - } -}); -Ext.define("Ext.chart.series.Gauge", { - alias: "series.gauge", - extend: "Ext.chart.series.Polar", - type: "gauge", - seriesType: "pieslice", - requires: ["Ext.draw.sprite.Sector"], - config: { - needle: false, - needleLength: 90, - needleWidth: 4, - donut: 30, - showInLegend: false, - value: null, - colors: null, - sectors: null, - minimum: 0, - maximum: 100, - rotation: 0, - totalAngle: Math.PI / 2, - rect: [0, 0, 1, 1], - center: [0.5, 0.75], - radius: 0.5, - wholeDisk: false - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - updateNeedle: function(b) { - var a = this, - d = a.getSprites(), - c = a.valueToAngle(a.getValue()); - if (d && d.length) { - d[0].setAttributes({ - startAngle: (b ? c : 0), - endAngle: c, - strokeOpacity: (b ? 1 : 0), - lineWidth: (b ? a.getNeedleWidth() : 0) - }); - a.doUpdateStyles() - } - }, - themeColorCount: function() { - var c = this, - a = c.getStore(), - b = a && a.getCount() || 0; - return b + (c.getNeedle() ? 0 : 1) - }, - updateColors: function(a, b) { - var f = this, - h = f.getSectors(), - j = h && h.length, - e = f.getSprites(), - c = Ext.Array.clone(a), - g = a && a.length, - d; - if (!g || !a[0]) { - return - } - for (d = 0; d < j; d++) { - c[d + 1] = h[d].color || c[d + 1] || a[d % g] - } - if (e.length) { - e[0].setAttributes({ - strokeStyle: c[0] - }) - } - this.setSubStyle({ - fillStyle: c, - strokeStyle: c - }); - this.doUpdateStyles() - }, - updateRect: function(f) { - var d = this.getWholeDisk(), - c = d ? Math.PI : this.getTotalAngle() / 2, - g = this.getDonut() / 100, - e, b, a; - if (c <= Math.PI / 2) { - e = 2 * Math.sin(c); - b = 1 - g * Math.cos(c) - } else { - e = 2; - b = 1 - Math.cos(c) - } - a = Math.min(f[2] / e, f[3] / b); - this.setRadius(a); - this.setCenter([f[2] / 2, a + (f[3] - b * a) / 2]) - }, - updateCenter: function(a) { - this.setStyle({ - centerX: a[0], - centerY: a[1], - rotationCenterX: a[0], - rotationCenterY: a[1] - }); - this.doUpdateStyles() - }, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a - (this.getTotalAngle() + Math.PI) / 2 - }); - this.doUpdateStyles() - }, - doUpdateShape: function(b, f) { - var a, d = this.getSectors(), - c = (d && d.length) || 0, - e = this.getNeedleLength() / 100; - a = [b * e, b]; - while (c--) { - a.push(b) - } - this.setSubStyle({ - endRho: a, - startRho: b / 100 * f - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - var b = this.getDonut(); - this.doUpdateShape(a, b) - }, - updateDonut: function(b) { - var a = this.getRadius(); - this.doUpdateShape(a, b) - }, - valueToAngle: function(a) { - a = this.applyValue(a); - return this.getTotalAngle() * (a - this.getMinimum()) / (this.getMaximum() - this.getMinimum()) - }, - applyValue: function(a) { - return Math.min(this.getMaximum(), Math.max(a, this.getMinimum())) - }, - updateValue: function(b) { - var a = this, - c = a.getNeedle(), - e = a.valueToAngle(b), - d = a.getSprites(); - d[0].rendererData.value = b; - d[0].setAttributes({ - startAngle: (c ? e : 0), - endAngle: e - }); - a.doUpdateStyles() - }, - processData: function() { - var f = this, - j = f.getStore(), - a, d, h, b, g, e = j && j.first(), - c, i; - if (e) { - c = f.getXField(); - if (c) { - i = e.get(c) - } - } - if (a = f.getXAxis()) { - d = a.getMinimum(); - h = a.getMaximum(); - b = a.getSprites()[0].fx; - g = b.getDuration(); - b.setDuration(0); - if (Ext.isNumber(d)) { - f.setMinimum(d) - } else { - a.setMinimum(f.getMinimum()) - } - if (Ext.isNumber(h)) { - f.setMaximum(h) - } else { - a.setMaximum(f.getMaximum()) - } - b.setDuration(g) - } - if (!Ext.isNumber(i)) { - i = f.getMinimum() - } - f.setValue(i) - }, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer(), - fx: { - customDurations: { - translationX: 0, - translationY: 0, - rotationCenterX: 0, - rotationCenterY: 0, - centerX: 0, - centerY: 0, - startRho: 0, - endRho: 0, - baseRotation: 0 - } - } - } - }, - normalizeSectors: function(f) { - var d = this, - c = (f && f.length) || 0, - b, e, g, a; - if (c) { - for (b = 0; b < c; b++) { - e = f[b]; - if (typeof e === "number") { - f[b] = { - start: (b > 0 ? f[b - 1].end : d.getMinimum()), - end: Math.min(e, d.getMaximum()) - }; - if (b == (c - 1) && f[b].end < d.getMaximum()) { - f[b + 1] = { - start: f[b].end, - end: d.getMaximum() - } - } - } else { - if (typeof e.start === "number") { - g = Math.max(e.start, d.getMinimum()) - } else { - g = (b > 0 ? f[b - 1].end : d.getMinimum()) - } - if (typeof e.end === "number") { - a = Math.min(e.end, d.getMaximum()) - } else { - a = d.getMaximum() - } - f[b].start = g; - f[b].end = a - } - } - } else { - f = [{ - start: d.getMinimum(), - end: d.getMaximum() - }] - } - return f - }, - getSprites: function() { - var j = this, - m = j.getStore(), - l = j.getValue(), - c, g; - if (!m && !Ext.isNumber(l)) { - return [] - } - var h = j.getChart(), - b = j.getAnimation() || h && h.getAnimation(), - f = j.sprites, - k = 0, - o, n, e, d, a = []; - if (f && f.length) { - f[0].setAnimation(b); - return f - } - d = { - store: m, - field: j.getXField(), - angleField: j.getXField(), - value: l, - series: j - }; - o = j.createSprite(); - o.setAttributes({ - zIndex: 10 - }, true); - o.rendererData = d; - o.rendererIndex = k++; - a.push(j.getNeedleWidth()); - j.getLabel().getTemplate().setField(true); - n = j.normalizeSectors(j.getSectors()); - for (c = 0, g = n.length; c < g; c++) { - e = { - startAngle: j.valueToAngle(n[c].start), - endAngle: j.valueToAngle(n[c].end), - label: n[c].label, - fillStyle: n[c].color, - strokeOpacity: 0, - doCallout: false, - labelOverflowPadding: -1 - }; - Ext.apply(e, n[c].style); - o = j.createSprite(); - o.rendererData = d; - o.rendererIndex = k++; - o.setAttributes(e, true); - a.push(e.lineWidth) - } - j.setSubStyle({ - lineWidth: a - }); - j.doUpdateStyles(); - return f - } -}); -Ext.define("Ext.chart.series.sprite.Line", { - alias: "sprite.lineSeries", - extend: "Ext.chart.series.sprite.Aggregative", - inheritableStatics: { - def: { - processors: { - smooth: "bool", - fillArea: "bool", - step: "bool", - preciseStroke: "bool", - xAxis: "default", - yCap: "default" - }, - defaults: { - smooth: false, - fillArea: false, - step: false, - preciseStroke: true, - xAxis: null, - yCap: Math.pow(2, 20), - yJump: 50 - }, - triggers: { - dataX: "dataX,bbox,smooth", - dataY: "dataY,bbox,smooth", - smooth: "smooth" - }, - updaters: { - smooth: function(a) { - var c = a.dataX, - b = a.dataY; - if (a.smooth && c && b && c.length > 2 && b.length > 2) { - this.smoothX = Ext.draw.Draw.spline(c); - this.smoothY = Ext.draw.Draw.spline(b) - } else { - delete this.smoothX; - delete this.smoothY - } - } - } - } - }, - list: null, - updatePlainBBox: function(d) { - var b = this.attr, - c = Math.min(0, b.dataMinY), - a = Math.max(0, b.dataMaxY); - d.x = b.dataMinX; - d.y = c; - d.width = b.dataMaxX - b.dataMinX; - d.height = a - c - }, - drawStrip: function(a, c) { - a.moveTo(c[0], c[1]); - for (var b = 2, d = c.length; b < d; b += 2) { - a.lineTo(c[b], c[b + 1]) - } - }, - drawStraightStroke: function(p, q, e, d, u, h) { - var w = this, - o = w.attr, - n = o.renderer, - g = o.step, - a = true, - l = { - type: "line", - smooth: false, - step: g - }, - m = [], - l, z, v, f, k, j, t, c, s, b, r; - for (r = 3; r < u.length; r += 3) { - t = u[r - 3]; - c = u[r - 2]; - k = u[r]; - j = u[r + 1]; - s = u[r + 3]; - b = u[r + 4]; - if (n) { - l.x = k; - l.y = j; - l.x0 = t; - l.y0 = c; - v = [w, l, w.rendererData, e + r / 3]; - z = Ext.callback(n, null, v, 0, w.getSeries()) - } - if (Ext.isNumber(k + j + t + c)) { - if (a) { - q.beginPath(); - q.moveTo(t, c); - m.push(t, c); - f = t; - a = false - } - } else { - continue - } - if (g) { - q.lineTo(k, c); - m.push(k, c) - } - q.lineTo(k, j); - m.push(k, j); - if (z || !(Ext.isNumber(s + b))) { - q.save(); - Ext.apply(q, z); - if (o.fillArea) { - q.lineTo(k, h); - q.lineTo(f, h); - q.closePath(); - q.fill() - } - q.beginPath(); - w.drawStrip(q, m); - m = []; - q.stroke(); - q.restore(); - q.beginPath(); - a = true - } - } - }, - calculateScale: function(c, a) { - var b = 0, - d = c; - while (d < a && c > 0) { - b++; - d += c >> b - } - return Math.pow(2, b > 0 ? b - 1 : b) - }, - drawSmoothStroke: function(u, v, c, b, C, f) { - var G = this, - t = G.attr, - d = t.step, - z = t.matrix, - s = t.renderer, - e = z.getXX(), - p = z.getYY(), - m = z.getDX(), - k = z.getDY(), - r = G.smoothX, - q = G.smoothY, - I = G.calculateScale(t.dataX.length, b), - o, F, n, E, h, g, B, a, A, w, H, D, l = { - type: "line", - smooth: true, - step: d - }; - v.beginPath(); - v.moveTo(r[c * 3] * e + m, q[c * 3] * p + k); - for (A = 0, w = c * 3 + 1; A < C.length - 3; A += 3, w += 3 * I) { - o = r[w] * e + m; - F = q[w] * p + k; - n = r[w + 1] * e + m; - E = q[w + 1] * p + k; - h = u.roundPixel(C[A + 3]); - g = C[A + 4]; - B = u.roundPixel(C[A]); - a = C[A + 1]; - if (s) { - l.x0 = B; - l.y0 = a; - l.cx1 = o; - l.cy1 = F; - l.cx2 = n; - l.cy2 = E; - l.x = h; - l.y = g; - D = [G, l, G.rendererData, c + A / 3 + 1]; - H = Ext.callback(s, null, D, 0, G.getSeries()); - v.save(); - Ext.apply(v, H) - } - if (t.fillArea) { - v.moveTo(B, a); - v.bezierCurveTo(o, F, n, E, h, g); - v.lineTo(h, f); - v.lineTo(B, f); - v.lineTo(B, a); - v.closePath(); - v.fill(); - v.beginPath() - } - v.moveTo(B, a); - v.bezierCurveTo(o, F, n, E, h, g); - v.stroke(); - v.moveTo(B, a); - v.closePath(); - if (s) { - v.restore() - } - v.beginPath(); - v.moveTo(h, g) - } - v.beginPath() - }, - drawLabel: function(k, i, h, o, a) { - var q = this, - n = q.attr, - e = q.getMarker("labels"), - d = e.getTemplate(), - m = q.labelCfg || (q.labelCfg = {}), - c = q.surfaceMatrix, - g, f, j = n.labelOverflowPadding, - l, b, r, p, s; - m.x = c.x(i, h); - m.y = c.y(i, h); - if (n.flipXY) { - m.rotationRads = Math.PI * 0.5 - } else { - m.rotationRads = 0 - } - m.text = k; - if (d.attr.renderer) { - p = [k, e, m, q.rendererData, o]; - r = Ext.callback(d.attr.renderer, null, p, 0, q.getSeries()); - if (typeof r === "string") { - m.text = r - } else { - if (typeof r === "object") { - if ("text" in r) { - m.text = r.text - } - s = true - } - } - } - b = q.getMarkerBBox("labels", o, true); - if (!b) { - q.putMarker("labels", m, o); - b = q.getMarkerBBox("labels", o, true) - } - l = b.height / 2; - g = i; - switch (d.attr.display) { - case "under": - f = h - l - j; - break; - case "rotate": - g += j; - f = h - j; - m.rotationRads = -Math.PI / 4; - break; - default: - f = h + l + j - } - m.x = c.x(g, f); - m.y = c.y(g, f); - if (s) { - Ext.apply(m, r) - } - q.putMarker("labels", m, o) - }, - drawMarker: function(j, h, d) { - var g = this, - e = g.attr, - f = e.renderer, - c = g.surfaceMatrix, - b = {}, - i, a; - if (f && g.getMarker("markers")) { - b.type = "marker"; - b.x = j; - b.y = h; - a = [g, b, g.rendererData, d]; - i = Ext.callback(f, null, a, 0, g.getSeries()); - if (i) { - Ext.apply(b, i) - } - } - b.translationX = c.x(j, h); - b.translationY = c.y(j, h); - delete b.x; - delete b.y; - g.putMarker("markers", b, d, !f) - }, - drawStroke: function(a, c, h, b, f, e) { - var d = this, - g = d.attr.smooth && d.smoothX && d.smoothY; - if (g) { - d.drawSmoothStroke(a, c, h, b, f, e) - } else { - d.drawStraightStroke(a, c, h, b, f, e) - } - }, - renderAggregates: function(B, w, l, N, o, I, D) { - var m = this, - k = m.attr, - s = k.dataX, - r = k.dataY, - h = k.labels, - v = k.xAxis, - a = k.yCap, - g = k.smooth && m.smoothX && m.smoothY, - d = h && m.getMarker("labels"), - t = m.getMarker("markers"), - E = k.matrix, - u = N.devicePixelRatio, - C = E.getXX(), - f = E.getYY(), - c = E.getDX(), - b = E.getDY(), - q = m.list || (m.list = []), - F = B.minX, - e = B.maxX, - j = B.minY, - P = B.maxY, - U = B.startIdx, - S = true, - Q, T, L, K, R, G; - m.rendererData = { - store: m.getStore() - }; - q.length = 0; - for (R = w; R < l; R++) { - var O = F[R], - p = e[R], - M = j[R], - n = P[R]; - if (O < p) { - q.push(O * C + c, M * f + b, U[R]); - q.push(p * C + c, n * f + b, U[R]) - } else { - if (O > p) { - q.push(p * C + c, n * f + b, U[R]); - q.push(O * C + c, M * f + b, U[R]) - } else { - q.push(p * C + c, n * f + b, U[R]) - } - } - } - if (q.length) { - for (R = 0; R < q.length; R += 3) { - L = q[R]; - K = q[R + 1]; - if (Ext.isNumber(L + K)) { - if (K > a) { - K = a - } else { - if (K < -a) { - K = -a - } - } - q[R + 1] = K - } else { - S = false; - continue - } - G = q[R + 2]; - if (t) { - m.drawMarker(L, K, G) - } - if (d && h[G]) { - m.drawLabel(h[G], L, K, G, D) - } - } - m.isContinuousLine = S; - if (g && !S) { - Ext.raise("Line smoothing in only supported for gapless data, where all data points are finite numbers.") - } - if (v) { - T = v.getAlignment() === "vertical"; - if (Ext.isNumber(v.floatingAtCoord)) { - Q = (T ? D[2] : D[3]) - v.floatingAtCoord - } else { - Q = T ? D[0] : D[1] - } - } else { - Q = k.flipXY ? D[0] : D[1] - } - if (k.preciseStroke) { - if (k.fillArea) { - o.fill() - } - if (k.transformFillStroke) { - k.inverseMatrix.toContext(o) - } - m.drawStroke(N, o, w, l, q, Q); - if (k.transformFillStroke) { - k.matrix.toContext(o) - } - o.stroke() - } else { - m.drawStroke(N, o, w, l, q, Q); - if (S && g && k.fillArea && !k.renderer) { - var A = s[s.length - 1] * C + c + u, - z = r[r.length - 1] * f + b, - J = s[0] * C + c - u, - H = r[0] * f + b; - o.lineTo(A, z); - o.lineTo(A, Q - k.lineWidth); - o.lineTo(J, Q - k.lineWidth); - o.lineTo(J, H) - } - if (k.transformFillStroke) { - k.matrix.toContext(o) - } - if (k.fillArea) { - o.fillStroke(k, true) - } else { - o.stroke(true) - } - } - } - } -}); -Ext.define("Ext.chart.series.Line", { - extend: "Ext.chart.series.Cartesian", - alias: "series.line", - type: "line", - seriesType: "lineSeries", - requires: ["Ext.chart.series.sprite.Line"], - config: { - selectionTolerance: 20, - smooth: false, - step: false, - fill: undefined, - aggregator: { - strategy: "double" - } - }, - defaultSmoothness: 3, - overflowBuffer: 1, - themeMarkerCount: function() { - return 1 - }, - getDefaultSpriteConfig: function() { - var d = this, - e = d.callParent(arguments), - c = Ext.apply({}, d.getStyle()), - b, a = false; - if (typeof d.config.fill != "undefined") { - if (d.config.fill) { - a = true; - if (typeof c.fillStyle == "undefined") { - if (typeof c.strokeStyle == "undefined") { - b = d.getStyleWithTheme(); - c.fillStyle = b.fillStyle; - c.strokeStyle = b.strokeStyle - } else { - c.fillStyle = c.strokeStyle - } - } - } - } else { - if (c.fillStyle) { - a = true - } - } - if (!a) { - delete c.fillStyle - } - c = Ext.apply(e || {}, c); - return Ext.apply(c, { - fillArea: a, - step: d.config.step, - smooth: d.config.smooth, - selectionTolerance: d.config.selectionTolerance - }) - }, - updateStep: function(b) { - var a = this.getSprites()[0]; - if (a && a.attr.step !== b) { - a.setAttributes({ - step: b - }) - } - }, - updateFill: function(b) { - var a = this.getSprites()[0]; - if (a && a.attr.fillArea !== b) { - a.setAttributes({ - fillArea: b - }) - } - }, - updateSmooth: function(a) { - var b = this.getSprites()[0]; - if (b && b.attr.smooth !== a) { - b.setAttributes({ - smooth: a - }) - } - } -}); -Ext.define("Ext.chart.series.sprite.PieSlice", { - extend: "Ext.draw.sprite.Sector", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - alias: "sprite.pieslice", - inheritableStatics: { - def: { - processors: { - doCallout: "bool", - label: "string", - rotateLabels: "bool", - labelOverflowPadding: "number", - renderer: "default" - }, - defaults: { - doCallout: true, - rotateLabels: true, - label: "", - labelOverflowPadding: 10, - renderer: null - } - } - }, - config: { - rendererData: null, - rendererIndex: 0, - series: null - }, - setGradientBBox: function(q, k) { - var j = this, - i = j.attr, - g = (i.fillStyle && i.fillStyle.isGradient) || (i.strokeStyle && i.strokeStyle.isGradient); - if (g && !i.constrainGradients) { - var b = j.getMidAngle(), - d = i.margin, - e = i.centerX, - c = i.centerY, - a = i.endRho, - l = i.matrix, - o = l.getScaleX(), - n = l.getScaleY(), - m = o * a, - f = n * a, - p = { - width: m + m, - height: f + f - }; - if (d) { - e += d * Math.cos(b); - c += d * Math.sin(b) - } - p.x = l.x(e, c) - m; - p.y = l.y(e, c) - f; - q.setGradientBBox(p) - } else { - j.callParent([q, k]) - } - }, - render: function(b, c, g, f) { - var e = this, - a = e.attr, - h = {}, - d; - if (a.renderer) { - h = { - type: "sector", - text: a.text, - centerX: a.centerX, - centerY: a.centerY, - margin: a.margin, - startAngle: Math.min(a.startAngle, a.endAngle), - endAngle: Math.max(a.startAngle, a.endAngle), - startRho: Math.min(a.startRho, a.endRho), - endRho: Math.max(a.startRho, a.endRho) - }; - d = Ext.callback(a.renderer, null, [e, h, e.rendererData, e.rendererIndex], 0, e.getSeries()); - e.setAttributes(d); - e.useAttributes(c, g) - } - e.callParent([b, c, g, f]); - if (a.label && e.getMarker("labels")) { - e.placeLabel() - } - }, - placeLabel: function() { - var z = this, - s = z.attr, - r = s.attributeId, - t = Math.min(s.startAngle, s.endAngle), - p = Math.max(s.startAngle, s.endAngle), - k = (t + p) * 0.5, - n = s.margin, - h = s.centerX, - g = s.centerY, - f = Math.sin(k), - c = Math.cos(k), - v = Math.min(s.startRho, s.endRho) + n, - m = Math.max(s.startRho, s.endRho) + n, - l = (v + m) * 0.5, - b = z.surfaceMatrix, - o = z.labelCfg || (z.labelCfg = {}), - e = z.getMarker("labels"), - d = e.getTemplate(), - a = d.getCalloutLine(), - q = a && a.length || 40, - u, j, i, A, w; - b.appendMatrix(s.matrix); - o.text = s.label; - j = h + c * l; - i = g + f * l; - o.x = b.x(j, i); - o.y = b.y(j, i); - j = h + c * m; - i = g + f * m; - o.calloutStartX = b.x(j, i); - o.calloutStartY = b.y(j, i); - j = h + c * (m + q); - i = g + f * (m + q); - o.calloutPlaceX = b.x(j, i); - o.calloutPlaceY = b.y(j, i); - if (!s.rotateLabels) { - o.rotationRads = 0 - } else { - switch (d.attr.orientation) { - case "horizontal": - o.rotationRads = k + Math.atan2(b.y(1, 0) - b.y(0, 0), b.x(1, 0) - b.x(0, 0)) + Math.PI / 2; - break; - case "vertical": - o.rotationRads = k + Math.atan2(b.y(1, 0) - b.y(0, 0), b.x(1, 0) - b.x(0, 0)); - break - } - } - o.calloutColor = (a && a.color) || z.attr.fillStyle; - if (a) { - if (a.width) { - o.calloutWidth = a.width - } - } else { - o.calloutHasLine = false - } - o.globalAlpha = s.globalAlpha * s.fillOpacity; - o.hidden = (s.startAngle == s.endAngle); - if (d.attr.renderer) { - w = [z.attr.label, e, o, z.rendererData, z.rendererIndex]; - A = Ext.callback(d.attr.renderer, null, w, 0, z.getSeries()); - if (typeof A === "string") { - o.text = A - } else { - Ext.apply(o, A) - } - } - z.putMarker("labels", o, r); - u = z.getMarkerBBox("labels", r, true); - if (u) { - if (s.doCallout) { - if (d.attr.display === "outside") { - z.putMarker("labels", { - callout: 1 - }, r) - } else { - if (d.attr.display === "inside") { - z.putMarker("labels", { - callout: 0 - }, r) - } else { - z.putMarker("labels", { - callout: 1 - z.sliceContainsLabel(s, u) - }, r) - } - } - } else { - z.putMarker("labels", { - globalAlpha: z.sliceContainsLabel(s, u) - }, r) - } - } - }, - sliceContainsLabel: function(d, f) { - var e = d.labelOverflowPadding, - h = (d.endRho + d.startRho) / 2, - g = h + (f.width + e) / 2, - i = h - (f.width + e) / 2, - j, c, b, a; - if (e < 0) { - return 1 - } - if (f.width + e * 2 > (d.endRho - d.startRho)) { - return 0 - } - c = Math.sqrt(d.endRho * d.endRho - g * g); - b = Math.sqrt(d.endRho * d.endRho - i * i); - j = Math.abs(d.endAngle - d.startAngle); - a = (j > Math.PI / 2 ? i : Math.abs(Math.tan(j / 2)) * i); - if (f.height + e * 2 > Math.min(c, b, a) * 2) { - return 0 - } - return 1 - } -}); -Ext.define("Ext.chart.series.Pie", { - extend: "Ext.chart.series.Polar", - requires: ["Ext.chart.series.sprite.PieSlice"], - type: "pie", - alias: "series.pie", - seriesType: "pieslice", - config: { - donut: 0, - rotation: 0, - clockwise: true, - totalAngle: 2 * Math.PI, - hidden: [], - radiusFactor: 100, - highlightCfg: { - margin: 20 - }, - style: {} - }, - directions: ["X"], - applyLabel: function(a, b) { - if (Ext.isObject(a) && !Ext.isString(a.orientation)) { - Ext.apply(a = Ext.Object.chain(a), { - orientation: "vertical" - }) - } - return this.callParent([a, b]) - }, - updateLabelData: function() { - var h = this, - j = h.getStore(), - g = j.getData().items, - e = h.getSprites(), - a = h.getLabel().getTemplate().getField(), - d = h.getHidden(), - b, f, c, k; - if (e.length && a) { - c = []; - for (b = 0, f = g.length; b < f; b++) { - c.push(g[b].get(a)) - } - for (b = 0, f = e.length; b < f; b++) { - k = e[b]; - k.setAttributes({ - label: c[b] - }); - k.putMarker("labels", { - hidden: d[b] - }, k.attr.attributeId) - } - } - }, - coordinateX: function() { - var t = this, - f = t.getStore(), - q = f.getData().items, - c = q.length, - b = t.getXField(), - e = t.getYField(), - l, a = 0, - m, k, s = 0, - o = t.getHidden(), - d = [], - p, g = 0, - h = t.getTotalAngle(), - r = t.getClockwise() ? 1 : -1, - j = t.getSprites(), - n; - if (!j) { - return - } - for (p = 0; p < c; p++) { - l = Math.abs(Number(q[p].get(b))) || 0; - k = e && Math.abs(Number(q[p].get(e))) || 0; - if (!o[p]) { - a += l; - if (k > s) { - s = k - } - } - d[p] = a; - if (p >= o.length) { - o[p] = false - } - } - o.length = c; - t.maxY = s; - if (a !== 0) { - m = h / a - } - for (p = 0; p < c; p++) { - j[p].setAttributes({ - startAngle: g, - endAngle: g = (m ? r * d[p] * m : 0), - globalAlpha: 1 - }) - } - if (c < t.sprites.length) { - for (p = c; p < t.sprites.length; p++) { - n = t.sprites[p]; - n.getMarker("labels").clear(n.getId()); - n.releaseMarker("labels"); - n.destroy() - } - t.sprites.length = c - } - for (p = c; p < t.sprites.length; p++) { - j[p].setAttributes({ - startAngle: h, - endAngle: h, - globalAlpha: 0 - }) - } - t.getChart().refreshLegendStore() - }, - updateCenter: function(a) { - this.setStyle({ - translationX: a[0] + this.getOffsetX(), - translationY: a[1] + this.getOffsetY() - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - this.setStyle({ - startRho: a * this.getDonut() * 0.01, - endRho: a * this.getRadiusFactor() * 0.01 - }); - this.doUpdateStyles() - }, - getStyleByIndex: function(c) { - var g = this, - j = g.getStore(), - k = j.getAt(c), - f = g.getYField(), - d = g.getRadius(), - a = {}, - e, b, h; - if (k) { - h = f && Math.abs(Number(k.get(f))) || 0; - e = d * g.getDonut() * 0.01; - b = d * g.getRadiusFactor() * 0.01; - a = g.callParent([c]); - a.startRho = e; - a.endRho = g.maxY ? (e + (b - e) * h / g.maxY) : b - } - return a - }, - updateDonut: function(b) { - var a = this.getRadius(); - this.setStyle({ - startRho: a * b * 0.01, - endRho: a * this.getRadiusFactor() * 0.01 - }); - this.doUpdateStyles() - }, - rotationOffset: -Math.PI / 2, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a + this.rotationOffset - }); - this.doUpdateStyles() - }, - updateTotalAngle: function(a) { - this.processData() - }, - getSprites: function() { - var k = this, - h = k.getChart(), - n = k.getStore(); - if (!h || !n) { - return [] - } - k.getColors(); - k.getSubStyle(); - var j = n.getData().items, - b = j.length, - d = k.getAnimation() || h && h.getAnimation(), - g = k.sprites, - o, l = 0, - f, e, c = false, - m = k.getLabel(), - a = m.getTemplate(); - f = { - store: n, - field: k.getXField(), - angleField: k.getXField(), - radiusField: k.getYField(), - series: k - }; - for (e = 0; e < b; e++) { - o = g[e]; - if (!o) { - o = k.createSprite(); - if (k.getHighlight()) { - o.config.highlight = k.getHighlight(); - o.addModifier("highlight", true) - } - if (a.getField()) { - a.setAttributes({ - labelOverflowPadding: k.getLabelOverflowPadding() - }); - a.fx.setCustomDurations({ - callout: 200 - }) - } - o.setAttributes(k.getStyleByIndex(e)); - o.rendererData = f; - o.rendererIndex = l++; - c = true - } - o.setAnimation(d) - } - if (c) { - k.doUpdateStyles() - } - return k.sprites - }, - betweenAngle: function(d, f, c) { - var e = Math.PI * 2, - g = this.rotationOffset; - if (!this.getClockwise()) { - d *= -1; - f *= -1; - c *= -1; - f -= g; - c -= g - } else { - f += g; - c += g - } - d -= f; - c -= f; - d %= e; - c %= e; - d += e; - c += e; - d %= e; - c %= e; - return d < c || c === 0 - }, - getItemForAngle: function(a) { - var h = this, - f = h.getSprites(), - d; - a %= Math.PI * 2; - while (a < 0) { - a += Math.PI * 2 - } - if (f) { - var j = h.getStore(), - g = j.getData().items, - c = h.getHidden(), - b = 0, - e = j.getCount(); - for (; b < e; b++) { - if (!c[b]) { - d = f[b].attr; - if (d.startAngle <= a && d.endAngle >= a) { - return { - series: h, - sprite: f[b], - index: b, - record: g[b], - field: h.getXField() - } - } - } - } - } - return null - }, - getItemForPoint: function(f, e) { - var t = this, - c = t.getSprites(); - if (c) { - var s = t.getCenter(), - q = t.getOffsetX(), - p = t.getOffsetY(), - j = f - s[0] + q, - h = e - s[1] + p, - b = t.getStore(), - g = t.getDonut(), - o = b.getData().items, - r = Math.atan2(h, j) - t.getRotation(), - a = Math.sqrt(j * j + h * h), - l = t.getRadius() * g * 0.01, - m = t.getHidden(), - n, d, k; - for (n = 0, d = o.length; n < d; n++) { - if (!m[n]) { - k = c[n].attr; - if (a >= l + k.margin && a <= k.endRho + k.margin) { - if (t.betweenAngle(r, k.startAngle, k.endAngle)) { - return { - series: t, - sprite: c[n], - index: n, - record: o[n], - field: t.getXField() - } - } - } - } - } - return null - } - }, - provideLegendInfo: function(f) { - var h = this, - j = h.getStore(); - if (j) { - var g = j.getData().items, - b = h.getLabel().getTemplate().getField(), - c = h.getXField(), - e = h.getHidden(), - d, a, k; - for (d = 0; d < g.length; d++) { - a = h.getStyleByIndex(d); - k = a.fillStyle; - if (Ext.isObject(k)) { - k = k.stops && k.stops[0].color - } - f.push({ - name: b ? String(g[d].get(b)) : c + " " + d, - mark: k || a.strokeStyle || "black", - disabled: e[d], - series: h.getId(), - index: d - }) - } - } - } -}); -Ext.define("Ext.chart.series.sprite.Pie3DPart", { - extend: "Ext.draw.sprite.Path", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - alias: "sprite.pie3dPart", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - margin: "number", - thickness: "number", - bevelWidth: "number", - distortion: "number", - baseColor: "color", - colorSpread: "number", - baseRotation: "number", - part: "enums(top,bottom,start,end,innerFront,innerBack,outerFront,outerBack)", - label: "string" - }, - aliases: { - rho: "endRho" - }, - triggers: { - centerX: "path,bbox", - centerY: "path,bbox", - startAngle: "path,partZIndex", - endAngle: "path,partZIndex", - startRho: "path", - endRho: "path,bbox", - margin: "path,bbox", - thickness: "path", - distortion: "path", - baseRotation: "path,partZIndex", - baseColor: "partZIndex,partColor", - colorSpread: "partColor", - part: "path,partZIndex", - globalAlpha: "canvas,alpha" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: Math.PI * 2, - endAngle: Math.PI * 2, - startRho: 0, - endRho: 150, - margin: 0, - thickness: 35, - distortion: 0.5, - baseRotation: 0, - baseColor: "white", - colorSpread: 1, - miterLimit: 1, - bevelWidth: 5, - strokeOpacity: 0, - part: "top", - label: "" - }, - updaters: { - alpha: "alphaUpdater", - partColor: "partColorUpdater", - partZIndex: "partZIndexUpdater" - } - } - }, - bevelParams: [], - constructor: function(a) { - this.callParent([a]); - this.bevelGradient = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: "rgba(255,255,255,0)" - }, { - offset: 0.7, - color: "rgba(255,255,255,0.6)" - }, { - offset: 1, - color: "rgba(255,255,255,0)" - }] - }) - }, - alphaUpdater: function(a) { - var d = this, - c = a.globalAlpha, - b = d.oldOpacity; - if (c !== b && (c === 1 || b === 1)) { - d.scheduleUpdater(a, "path", ["globalAlpha"]); - d.oldOpacity = c - } - }, - partColorUpdater: function(a) { - var d = Ext.draw.Color.fly(a.baseColor), - b = d.toString(), - e = a.colorSpread, - c; - switch (a.part) { - case "top": - c = new Ext.draw.gradient.Radial({ - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - }, - stops: [{ - offset: 0, - color: d.createLighter(0.1 * e) - }, { - offset: 1, - color: d.createDarker(0.1 * e) - }] - }); - break; - case "bottom": - c = new Ext.draw.gradient.Radial({ - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - }, - stops: [{ - offset: 0, - color: d.createDarker(0.2 * e) - }, { - offset: 1, - color: d.toString() - }] - }); - break; - case "outerFront": - case "outerBack": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.15 * e).toString() - }, { - offset: 0.3, - color: b - }, { - offset: 0.8, - color: d.createLighter(0.2 * e).toString() - }, { - offset: 1, - color: d.createDarker(0.25 * e).toString() - }] - }); - break; - case "start": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 1, - color: d.createLighter(0.2 * e).toString() - }] - }); - break; - case "end": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 1, - color: d.createLighter(0.2 * e).toString() - }] - }); - break; - case "innerFront": - case "innerBack": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 0.2, - color: d.createLighter(0.2 * e).toString() - }, { - offset: 0.7, - color: b - }, { - offset: 1, - color: d.createDarker(0.1 * e).toString() - }] - }); - break - } - a.fillStyle = c; - a.canvasAttributes.fillStyle = c - }, - partZIndexUpdater: function(a) { - var c = Ext.draw.sprite.AttributeParser.angle, - e = a.baseRotation, - d = a.startAngle, - b = a.endAngle, - f; - switch (a.part) { - case "top": - a.zIndex = 5; - break; - case "outerFront": - d = c(d + e); - b = c(b + e); - if (d >= 0 && b < 0) { - f = Math.sin(d) - } else { - if (d <= 0 && b > 0) { - f = Math.sin(b) - } else { - if (d >= 0 && b > 0) { - if (d > b) { - f = 0 - } else { - f = Math.max(Math.sin(d), Math.sin(b)) - } - } else { - f = 1 - } - } - } - a.zIndex = 4 + f; - break; - case "outerBack": - a.zIndex = 1; - break; - case "start": - a.zIndex = 4 + Math.sin(c(d + e)); - break; - case "end": - a.zIndex = 4 + Math.sin(c(b + e)); - break; - case "innerFront": - a.zIndex = 2; - break; - case "innerBack": - a.zIndex = 4 + Math.sin(c((d + b) / 2 + e)); - break; - case "bottom": - a.zIndex = 0; - break - } - a.dirtyZIndex = true - }, - updatePlainBBox: function(k) { - var f = this.attr, - a = f.part, - b = f.baseRotation, - e = f.centerX, - d = f.centerY, - j, c, i, h, g, l; - if (a === "start") { - c = f.startAngle + b - } else { - if (a === "end") { - c = f.endAngle + b - } - } - if (Ext.isNumber(c)) { - g = Math.sin(c); - l = Math.cos(c); - i = Math.min(e + l * f.startRho, e + l * f.endRho); - h = d + g * f.startRho * f.distortion; - k.x = i; - k.y = h; - k.width = l * (f.endRho - f.startRho); - k.height = f.thickness + g * (f.endRho - f.startRho) * 2; - return - } - if (a === "innerFront" || a === "innerBack") { - j = f.startRho - } else { - j = f.endRho - } - k.width = j * 2; - k.height = j * f.distortion * 2 + f.thickness; - k.x = f.centerX - j; - k.y = f.centerY - j * f.distortion - }, - updateTransformedBBox: function(a) { - if (this.attr.part === "start" || this.attr.part === "end") { - return this.callParent(arguments) - } - return this.updatePlainBBox(a) - }, - updatePath: function(a) { - if (!this.attr.globalAlpha) { - return - } - if (this.attr.endAngle < this.attr.startAngle) { - return - } - this[this.attr.part + "Renderer"](a) - }, - render: function(b, c) { - var d = this, - a = d.attr; - if (!a.globalAlpha) { - return - } - d.callParent([b, c]); - d.bevelRenderer(b, c); - if (a.label && d.getMarker("labels")) { - d.placeLabel() - } - }, - placeLabel: function() { - var z = this, - u = z.attr, - t = u.attributeId, - p = u.margin, - c = u.distortion, - i = u.centerX, - h = u.centerY, - j = u.baseRotation, - v = u.startAngle + j, - r = u.endAngle + j, - m = (v + r) / 2, - w = u.startRho + p, - o = u.endRho + p, - n = (w + o) / 2, - a = Math.sin(m), - b = Math.cos(m), - e = z.surfaceMatrix, - g = z.getMarker("labels"), - f = g.getTemplate(), - d = f.getCalloutLine(), - s = d && d.length || 40, - q = {}, - l, k; - e.appendMatrix(u.matrix); - q.text = u.label; - l = i + b * n; - k = h + a * n * c; - q.x = e.x(l, k); - q.y = e.y(l, k); - l = i + b * o; - k = h + a * o * c; - q.calloutStartX = e.x(l, k); - q.calloutStartY = e.y(l, k); - l = i + b * (o + s); - k = h + a * (o + s) * c; - q.calloutPlaceX = e.x(l, k); - q.calloutPlaceY = e.y(l, k); - q.calloutWidth = 2; - z.putMarker("labels", q, t); - z.putMarker("labels", { - callout: 1 - }, t) - }, - bevelRenderer: function(b, c) { - var f = this, - a = f.attr, - e = a.bevelWidth, - g = f.bevelParams, - d; - for (d = 0; d < g.length; d++) { - c.beginPath(); - c.ellipse.apply(c, g[d]); - c.save(); - c.lineWidth = e; - c.strokeOpacity = e ? 1 : 0; - c.strokeGradient = f.bevelGradient; - c.stroke(a); - c.restore() - } - }, - lidRenderer: function(o, m) { - var k = this.attr, - g = k.margin, - c = k.distortion, - i = k.centerX, - h = k.centerY, - f = k.baseRotation, - j = k.startAngle + f, - e = k.endAngle + f, - d = (j + e) / 2, - l = k.startRho, - b = k.endRho, - n = Math.sin(e), - a = Math.cos(e); - i += Math.cos(d) * g; - h += Math.sin(d) * g * c; - o.ellipse(i, h + m, l, l * c, 0, j, e, false); - o.lineTo(i + a * b, h + m + n * b * c); - o.ellipse(i, h + m, b, b * c, 0, e, j, true); - o.closePath() - }, - topRenderer: function(a) { - this.lidRenderer(a, 0) - }, - bottomRenderer: function(b) { - var a = this.attr; - if (a.globalAlpha < 1 || a.shadowColor !== Ext.draw.Color.RGBA_NONE) { - this.lidRenderer(b, a.thickness) - } - }, - sideRenderer: function(l, s) { - var o = this.attr, - k = o.margin, - g = o.centerX, - f = o.centerY, - e = o.distortion, - h = o.baseRotation, - p = o.startAngle + h, - m = o.endAngle + h, - a = o.thickness, - q = o.startRho, - j = o.endRho, - r = (s === "start" && p) || (s === "end" && m), - b = Math.sin(r), - d = Math.cos(r), - c = o.globalAlpha < 1, - n = s === "start" && d < 0 || s === "end" && d > 0 || c, - i; - if (n) { - i = (p + m) / 2; - g += Math.cos(i) * k; - f += Math.sin(i) * k * e; - l.moveTo(g + d * q, f + b * q * e); - l.lineTo(g + d * j, f + b * j * e); - l.lineTo(g + d * j, f + b * j * e + a); - l.lineTo(g + d * q, f + b * q * e + a); - l.closePath() - } - }, - startRenderer: function(a) { - this.sideRenderer(a, "start") - }, - endRenderer: function(a) { - this.sideRenderer(a, "end") - }, - rimRenderer: function(q, e, o, j) { - var w = this, - s = w.attr, - p = s.margin, - h = s.centerX, - g = s.centerY, - d = s.distortion, - i = s.baseRotation, - t = Ext.draw.sprite.AttributeParser.angle, - u = s.startAngle + i, - r = s.endAngle + i, - k = t((u + r) / 2), - a = s.thickness, - b = s.globalAlpha < 1, - c, n, v; - w.bevelParams = []; - u = t(u); - r = t(r); - h += Math.cos(k) * p; - g += Math.sin(k) * p * d; - c = u >= 0 && r >= 0; - n = u <= 0 && r <= 0; - - function l() { - q.ellipse(h, g + a, e, e * d, 0, Math.PI, u, true); - q.lineTo(h + Math.cos(u) * e, g + Math.sin(u) * e * d); - v = [h, g, e, e * d, 0, u, Math.PI, false]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function f() { - q.ellipse(h, g + a, e, e * d, 0, 0, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, 0, true]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function x() { - q.ellipse(h, g + a, e, e * d, 0, Math.PI, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, Math.PI, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function m() { - q.ellipse(h, g + a, e, e * d, 0, u, 0, false); - q.lineTo(h + e, g); - v = [h, g, e, e * d, 0, 0, u, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - if (j) { - if (!o || b) { - if (u >= 0 && r < 0) { - l() - } else { - if (u <= 0 && r > 0) { - f() - } else { - if (u <= 0 && r < 0) { - if (u > r) { - q.ellipse(h, g + a, e, e * d, 0, 0, Math.PI, false); - q.lineTo(h - e, g); - v = [h, g, e, e * d, 0, Math.PI, 0, true]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } else { - if (u > r) { - l(); - f() - } else { - v = [h, g, e, e * d, 0, u, r, false]; - if (c && !o || n && o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d + a); - q.ellipse(h, g + a, e, e * d, 0, r, u, true); - q.closePath() - } - } - } - } - } - } else { - if (o || b) { - if (u >= 0 && r < 0) { - x() - } else { - if (u <= 0 && r > 0) { - m() - } else { - if (u <= 0 && r < 0) { - if (u > r) { - x(); - m() - } else { - q.ellipse(h, g + a, e, e * d, 0, u, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, u, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } else { - if (u > r) { - q.ellipse(h, g + a, e, e * d, 0, -Math.PI, 0, false); - q.lineTo(h + e, g); - v = [h, g, e, e * d, 0, 0, -Math.PI, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } - } - } - } - } - }, - innerFrontRenderer: function(a) { - this.rimRenderer(a, this.attr.startRho, true, true) - }, - innerBackRenderer: function(a) { - this.rimRenderer(a, this.attr.startRho, true, false) - }, - outerFrontRenderer: function(a) { - this.rimRenderer(a, this.attr.endRho, false, true) - }, - outerBackRenderer: function(a) { - this.rimRenderer(a, this.attr.endRho, false, false) - } -}); -Ext.define("Ext.draw.PathUtil", function() { - var a = Math.abs, - c = Math.pow, - e = Math.cos, - b = Math.acos, - d = Math.sqrt, - f = Math.PI; - return { - singleton: true, - requires: ["Ext.draw.overrides.Path", "Ext.draw.overrides.sprite.Path", "Ext.draw.overrides.sprite.Instancing", "Ext.draw.overrides.Surface"], - cubicRoots: function(m) { - var z = m[0], - x = m[1], - w = m[2], - v = m[3]; - if (z === 0) { - return this.quadraticRoots(x, w, v) - } - var s = x / z, - r = w / z, - q = v / z, - k = (3 * r - c(s, 2)) / 9, - j = (9 * s * r - 27 * q - 2 * c(s, 3)) / 54, - p = c(k, 3) + c(j, 2), - n = [], - h, g, o, l, u, y = Ext.Number.sign; - if (p >= 0) { - h = y(j + d(p)) * c(a(j + d(p)), 1 / 3); - g = y(j - d(p)) * c(a(j - d(p)), 1 / 3); - n[0] = -s / 3 + (h + g); - n[1] = -s / 3 - (h + g) / 2; - n[2] = n[1]; - o = a(d(3) * (h - g) / 2); - if (o !== 0) { - n[1] = -1; - n[2] = -1 - } - } else { - l = b(j / d(-c(k, 3))); - n[0] = 2 * d(-k) * e(l / 3) - s / 3; - n[1] = 2 * d(-k) * e((l + 2 * f) / 3) - s / 3; - n[2] = 2 * d(-k) * e((l + 4 * f) / 3) - s / 3 - } - for (u = 0; u < 3; u++) { - if (n[u] < 0 || n[u] > 1) { - n[u] = -1 - } - } - return n - }, - quadraticRoots: function(h, g, n) { - var m, l, k, j; - if (h === 0) { - return this.linearRoot(g, n) - } - m = g * g - 4 * h * n; - if (m === 0) { - k = [-g / (2 * h)] - } else { - if (m > 0) { - l = d(m); - k = [(-g - l) / (2 * h), (-g + l) / (2 * h)] - } else { - return [] - } - } - for (j = 0; j < k.length; j++) { - if (k[j] < 0 || k[j] > 1) { - k[j] = -1 - } - } - return k - }, - linearRoot: function(h, g) { - var i = -g / h; - if (h === 0 || i < 0 || i > 1) { - return [] - } - return [i] - }, - bezierCoeffs: function(h, g, k, j) { - var i = []; - i[0] = -h + 3 * g - 3 * k + j; - i[1] = 3 * h - 6 * g + 3 * k; - i[2] = -3 * h + 3 * g; - i[3] = h; - return i - }, - cubicLineIntersections: function(I, G, F, E, l, k, j, h, M, p, K, n) { - var u = [], - N = [], - D = p - n, - z = K - M, - y = M * (n - p) - p * (K - M), - L = this.bezierCoeffs(I, G, F, E), - J = this.bezierCoeffs(l, k, j, h), - H, x, w, v, g, q, o, m; - u[0] = D * L[0] + z * J[0]; - u[1] = D * L[1] + z * J[1]; - u[2] = D * L[2] + z * J[2]; - u[3] = D * L[3] + z * J[3] + y; - x = this.cubicRoots(u); - for (H = 0; H < x.length; H++) { - v = x[H]; - if (v < 0 || v > 1) { - continue - } - g = v * v; - q = g * v; - o = L[0] * q + L[1] * g + L[2] * v + L[3]; - m = J[0] * q + J[1] * g + J[2] * v + J[3]; - if ((K - M) !== 0) { - w = (o - M) / (K - M) - } else { - w = (m - p) / (n - p) - } - if (!(w < 0 || w > 1)) { - N.push([o, m]) - } - } - return N - }, - splitCubic: function(g, q, p, o, m) { - var j = m * m, - n = m * j, - i = m - 1, - h = i * i, - k = i * h, - l = n * o - 3 * j * i * p + 3 * m * h * q - k * g; - return [ - [g, m * q - i * g, j * p - 2 * m * i * q + h * g, l], - [l, j * o - 2 * m * i * p + h * q, m * o - i * p, o] - ] - }, - cubicDimension: function(p, o, l, k) { - var j = 3 * (-p + 3 * (o - l) + k), - i = 6 * (p - 2 * o + l), - h = -3 * (p - o), - q, n, g = Math.min(p, k), - m = Math.max(p, k), - r; - if (j === 0) { - if (i === 0) { - return [g, m] - } else { - q = -h / i; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - } - } else { - r = i * i - 4 * j * h; - if (r >= 0) { - r = d(r); - q = (r - i) / 2 / j; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - if (r > 0) { - q -= r / j; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - } - } - } - return [g, m] - }, - interpolateCubic: function(h, g, l, k, i) { - if (i === 0) { - return h - } - if (i === 1) { - return k - } - var j = (1 - i) / i; - return i * i * i * (k + j * (3 * l + j * (3 * g + j * h))) - }, - cubicsIntersections: function(r, q, p, o, A, z, y, v, g, F, E, D, m, l, k, i) { - var C = this, - x = C.cubicDimension(r, q, p, o), - B = C.cubicDimension(A, z, y, v), - n = C.cubicDimension(g, F, E, D), - s = C.cubicDimension(m, l, k, i), - j, h, u, t, w = []; - if (x[0] > n[1] || x[1] < n[0] || B[0] > s[1] || B[1] < s[0]) { - return [] - } - if (a(A - z) < 1 && a(y - v) < 1 && a(r - o) < 1 && a(q - p) < 1 && a(m - l) < 1 && a(k - i) < 1 && a(g - D) < 1 && a(F - E) < 1) { - return [ - [(r + o) * 0.5, (A + z) * 0.5] - ] - } - j = C.splitCubic(r, q, p, o, 0.5); - h = C.splitCubic(A, z, y, v, 0.5); - u = C.splitCubic(g, F, E, D, 0.5); - t = C.splitCubic(m, l, k, i, 0.5); - w.push.apply(w, C.cubicsIntersections.apply(C, j[0].concat(h[0], u[0], t[0]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[0].concat(h[0], u[1], t[1]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[1].concat(h[1], u[0], t[0]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[1].concat(h[1], u[1], t[1]))); - return w - }, - linesIntersection: function(k, p, j, o, h, n, q, m) { - var l = (j - k) * (m - n) - (o - p) * (q - h), - i, g; - if (l === 0) { - return null - } - i = ((q - h) * (p - n) - (k - h) * (m - n)) / l; - g = ((j - k) * (p - n) - (o - p) * (k - h)) / l; - if (i >= 0 && i <= 1 && g >= 0 && g <= 1) { - return [k + i * (j - k), p + i * (o - p)] - } - return null - }, - pointOnLine: function(j, m, h, l, g, n) { - var k, i; - if (a(h - j) < a(l - m)) { - i = j; - j = m; - m = i; - i = h; - h = l; - l = i; - i = g; - g = n; - n = i - } - k = (g - j) / (h - j); - if (k < 0 || k > 1) { - return false - } - return a(m + k * (l - m) - n) < 4 - }, - pointOnCubic: function(w, u, s, r, l, k, h, g, p, o) { - var C = this, - B = C.bezierCoeffs(w, u, s, r), - A = C.bezierCoeffs(l, k, h, g), - z, v, n, m, q; - B[3] -= p; - A[3] -= o; - n = C.cubicRoots(B); - m = C.cubicRoots(A); - for (z = 0; z < n.length; z++) { - q = n[z]; - for (v = 0; v < m.length; v++) { - if (q >= 0 && q <= 1 && a(q - m[v]) < 0.05) { - return true - } - } - } - return false - } - } -}); -Ext.define("Ext.chart.series.Pie3D", { - extend: "Ext.chart.series.Polar", - requires: ["Ext.chart.series.sprite.Pie3DPart", "Ext.draw.PathUtil"], - type: "pie3d", - seriesType: "pie3d", - alias: "series.pie3d", - isPie3D: true, - config: { - rect: [0, 0, 0, 0], - thickness: 35, - distortion: 0.5, - donut: false, - hidden: [], - highlightCfg: { - margin: 20 - }, - shadow: false - }, - rotationOffset: -Math.PI / 2, - setField: function(a) { - return this.setXField(a) - }, - getField: function() { - return this.getXField() - }, - updateRotation: function(a) { - this.setStyle({ - baseRotation: a + this.rotationOffset - }); - this.doUpdateStyles() - }, - updateDistortion: function() { - this.setRadius() - }, - updateThickness: function() { - this.setRadius() - }, - updateColors: function(a) { - this.setSubStyle({ - baseColor: a - }) - }, - applyShadow: function(a) { - if (a === true) { - a = { - shadowColor: "rgba(0,0,0,0.8)", - shadowBlur: 30 - } - } else { - if (!Ext.isObject(a)) { - a = { - shadowColor: Ext.draw.Color.RGBA_NONE - } - } - } - return a - }, - updateShadow: function(g) { - var e = this, - f = e.getSprites(), - d = e.spritesPerSlice, - c = f && f.length, - b, a; - for (b = 1; b < c; b += d) { - a = f[b]; - if (a.attr.part = "bottom") { - a.setAttributes(g) - } - } - }, - getStyleByIndex: function(b) { - var d = this.callParent([b]), - c = this.getStyle(), - a = d.fillStyle || d.fill || d.color, - e = c.strokeStyle || c.stroke; - if (a) { - d.baseColor = a; - delete d.fillStyle; - delete d.fill; - delete d.color - } - if (e) { - d.strokeStyle = e - } - return d - }, - doUpdateStyles: function() { - var g = this, - h = g.getSprites(), - f = g.spritesPerSlice, - e = h && h.length, - c = 0, - b = 0, - a, d; - for (; c < e; c += f, b++) { - d = g.getStyleByIndex(b); - for (a = 0; a < f; a++) { - h[c + a].setAttributes(d) - } - } - }, - coordinateX: function() { - var w = this, - m = w.getChart(), - u = m && m.getAnimation(), - f = w.getStore(), - t = f.getData().items, - d = t.length, - b = w.getXField(), - p = w.getRotation(), - s = w.getHidden(), - n, c = 0, - h, e = [], - k = w.getSprites(), - a = k.length, - l = w.spritesPerSlice, - g = 0, - o = Math.PI * 2, - v = 1e-10, - r, q; - for (r = 0; r < d; r++) { - n = Math.abs(Number(t[r].get(b))) || 0; - if (!s[r]) { - c += n - } - e[r] = c; - if (r >= s.length) { - s[r] = false - } - } - s.length = d; - if (c === 0) { - return - } - h = 2 * Math.PI / c; - for (r = 0; r < d; r++) { - e[r] *= h - } - for (r = 0; r < a; r++) { - k[r].setAnimation(u) - } - for (r = 0; r < d; r++) { - for (q = 0; q < l; q++) { - k[r * l + q].setAttributes({ - startAngle: g, - endAngle: e[r] - v, - globalAlpha: 1, - baseRotation: p - }) - } - g = e[r] - } - for (r *= l; r < a; r++) { - k[r].setAnimation(u); - k[r].setAttributes({ - startAngle: o, - endAngle: o, - globalAlpha: 0, - baseRotation: p - }) - } - }, - updateLabelData: function() { - var l = this, - m = l.getStore(), - k = m.getData().items, - h = l.getSprites(), - b = l.getLabel().getTemplate().getField(), - f = l.getHidden(), - a = l.spritesPerSlice, - d, c, g, e, n; - if (h.length && b) { - e = []; - for (d = 0, g = k.length; d < g; d++) { - e.push(k[d].get(b)) - } - for (d = 0, c = 0, g = h.length; d < g; d += a, c++) { - n = h[d]; - n.setAttributes({ - label: e[c] - }); - n.putMarker("labels", { - hidden: f[c] - }, n.attr.attributeId) - } - } - }, - applyRadius: function() { - var f = this, - d = f.getChart(), - h = d.getInnerPadding(), - e = d.getMainRect() || [0, 0, 1, 1], - c = e[2] - h * 2, - a = e[3] - h * 2 - f.getThickness(), - g = c / 2, - b = g * f.getDistortion(); - if (b > a / 2) { - return a / (f.getDistortion() * 2) - } else { - return g - } - }, - getSprites: function() { - var y = this, - e = y.getStore(); - if (!e) { - return [] - } - var n = y.getChart(), - p = y.getSurface(), - t = e.getData().items, - l = y.spritesPerSlice, - a = t.length, - v = y.getAnimation() || n && n.getAnimation(), - x = y.getCenter(), - w = y.getOffsetX(), - u = y.getOffsetY(), - b = y.getRadius(), - q = y.getRotation(), - d = y.getHighlight(), - c = { - centerX: x[0] + w, - centerY: x[1] + u - y.getThickness() / 2, - endRho: b, - startRho: b * y.getDonut() / 100, - thickness: y.getThickness(), - distortion: y.getDistortion() - }, - k = y.sprites, - h = y.getLabel(), - f = h.getTemplate(), - m, g, o, s, r; - for (s = 0; s < a; s++) { - g = Ext.apply({}, this.getStyleByIndex(s), c); - if (!k[s * l]) { - for (r = 0; r < y.partNames.length; r++) { - o = p.add({ - type: "pie3dPart", - part: y.partNames[r] - }); - if (r === 0 && f.getField()) { - o.bindMarker("labels", h) - } - o.fx.setDurationOn("baseRotation", q); - if (d) { - o.config.highlight = d; - o.addModifier("highlight", true) - } - o.setAttributes(g); - k.push(o) - } - } else { - m = k.slice(s * l, (s + 1) * l); - for (r = 0; r < m.length; r++) { - o = m[r]; - if (v) { - o.setAnimation(v) - } - o.setAttributes(g) - } - } - } - return k - }, - betweenAngle: function(d, f, c) { - var e = Math.PI * 2, - g = this.rotationOffset; - f += g; - c += g; - d -= f; - c -= f; - d %= e; - c %= e; - d += e; - c += e; - d %= e; - c %= e; - return d < c || c === 0 - }, - getItemForPoint: function(k, j) { - var h = this, - g = h.getSprites(); - if (g) { - var l = h.getStore(), - b = l.getData().items, - a = h.spritesPerSlice, - e = h.getHidden(), - c, f, m, d; - for (c = 0, f = b.length; c < f; c++) { - if (!e[c]) { - d = c * a; - m = g[d]; - if (m.hitTest([k, j])) { - return { - series: h, - sprite: g.slice(d, d + a), - index: c, - record: b[c], - category: "sprites", - field: h.getXField() - } - } - } - } - return null - } - }, - provideLegendInfo: function(f) { - var h = this, - k = h.getStore(); - if (k) { - var g = k.getData().items, - b = h.getLabel().getTemplate().getField(), - j = h.getField(), - e = h.getHidden(), - d, a, c; - for (d = 0; d < g.length; d++) { - a = h.getStyleByIndex(d); - c = a.baseColor; - f.push({ - name: b ? String(g[d].get(b)) : j + " " + d, - mark: c || "black", - disabled: e[d], - series: h.getId(), - index: d - }) - } - } - } -}, function() { - var b = this.prototype, - a = Ext.chart.series.sprite.Pie3DPart.def.getInitialConfig().processors.part; - b.partNames = a.replace(/^enums\(|\)/g, "").split(","); - b.spritesPerSlice = b.partNames.length -}); -Ext.define("Ext.chart.series.sprite.Polar", { - extend: "Ext.chart.series.sprite.Series", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - baseRotation: "number", - labels: "default", - labelOverflowPadding: "number" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: 0, - endAngle: Math.PI, - startRho: 0, - endRho: 150, - baseRotation: 0, - labels: null, - labelOverflowPadding: 10 - }, - triggers: { - centerX: "bbox", - centerY: "bbox", - startAngle: "bbox", - endAngle: "bbox", - startRho: "bbox", - endRho: "bbox", - baseRotation: "bbox" - } - } - }, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.centerX - a.endRho; - b.y = a.centerY + a.endRho; - b.width = a.endRho * 2; - b.height = a.endRho * 2 - } -}); -Ext.define("Ext.chart.series.sprite.Radar", { - alias: "sprite.radar", - extend: "Ext.chart.series.sprite.Polar", - getDataPointXY: function(d) { - var u = this, - n = u.attr, - f = n.centerX, - e = n.centerY, - o = n.matrix, - t = n.dataMinX, - s = n.dataMaxX, - k = n.dataX, - j = n.dataY, - l = n.endRho, - p = n.startRho, - g = n.baseRotation, - i, h, m, c, b, a, q; - if (n.rangeY) { - q = n.rangeY[1] - } else { - q = n.dataMaxY - } - c = (k[d] - t) / (s - t + 1) * 2 * Math.PI + g; - m = j[d] / q * (l - p) + p; - b = f + Math.cos(c) * m; - a = e + Math.sin(c) * m; - i = o.x(b, a); - h = o.y(b, a); - return [i, h] - }, - render: function(a, l) { - var h = this, - f = h.attr, - g = f.dataX, - b = g.length, - e = h.surfaceMatrix, - d = {}, - c, k, j, m; - l.beginPath(); - for (c = 0; c < b; c++) { - m = h.getDataPointXY(c); - k = m[0]; - j = m[1]; - if (c === 0) { - l.moveTo(k, j) - } - l.lineTo(k, j); - d.translationX = e.x(k, j); - d.translationY = e.y(k, j); - h.putMarker("markers", d, c, true) - } - l.closePath(); - l.fillStroke(f) - } -}); -Ext.define("Ext.chart.series.Radar", { - extend: "Ext.chart.series.Polar", - type: "radar", - seriesType: "radar", - alias: "series.radar", - requires: ["Ext.chart.series.sprite.Radar"], - themeColorCount: function() { - return 1 - }, - isStoreDependantColorCount: false, - themeMarkerCount: function() { - return 1 - }, - updateAngularAxis: function(a) { - a.processData(this) - }, - updateRadialAxis: function(a) { - a.processData(this) - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - updateCenter: function(a) { - this.setStyle({ - translationX: a[0] + this.getOffsetX(), - translationY: a[1] + this.getOffsetY() - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - this.setStyle({ - endRho: a - }); - this.doUpdateStyles() - }, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a - }); - this.doUpdateStyles() - }, - updateTotalAngle: function(a) { - this.processData() - }, - getItemForPoint: function(k, j) { - var h = this, - m = h.sprites && h.sprites[0], - f = m.attr, - g = f.dataX, - a = g.length, - l = h.getStore(), - e = h.getMarker(), - b, o, p, d, n, c; - if (h.getHidden()) { - return null - } - if (m && e) { - c = m.getMarker("markers"); - for (d = 0; d < a; d++) { - n = c.getBBoxFor(d); - b = (n.width + n.height) * 0.25; - p = m.getDataPointXY(d); - if (Math.abs(p[0] - k) < b && Math.abs(p[1] - j) < b) { - o = { - series: h, - sprite: m, - index: d, - category: "markers", - record: l.getData().items[d], - field: h.getYField() - }; - return o - } - } - } - return h.callParent(arguments) - }, - getDefaultSpriteConfig: function() { - var a = this.callParent(), - b = { - customDurations: { - translationX: 0, - translationY: 0, - rotationRads: 0, - dataMinX: 0, - dataMaxX: 0 - } - }; - if (a.fx) { - Ext.apply(a.fx, b) - } else { - a.fx = b - } - return a - }, - getSprites: function() { - var d = this, - c = d.getChart(), - e = d.getAnimation() || c && c.getAnimation(), - b = d.sprites[0], - a; - if (!c) { - return [] - } - if (!b) { - b = d.createSprite() - } - if (e) { - a = b.getMarker("markers"); - if (a) { - a.getTemplate().setAnimation(e) - } - b.setAnimation(e) - } - return d.sprites - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getSubStyleWithTheme(), - c = a.fillStyle; - if (Ext.isArray(c)) { - c = c[0] - } - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - } -}); -Ext.define("Ext.chart.series.sprite.Scatter", { - alias: "sprite.scatterSeries", - extend: "Ext.chart.series.sprite.Cartesian", - renderClipped: function(r, s, w, u) { - if (this.cleanRedraw) { - return - } - var C = this, - q = C.attr, - l = q.dataX, - h = q.dataY, - z = q.labels, - j = C.getSeries(), - b = z && C.getMarker("labels"), - t = C.attr.matrix, - c = t.getXX(), - p = t.getYY(), - m = t.getDX(), - k = t.getDY(), - n = {}, - D, B, d = r.getInherited().rtl && !q.flipXY ? -1 : 1, - a, A, o, e, g, f, v; - if (q.flipXY) { - a = u[1] - c * d; - A = u[1] + u[3] + c * d; - o = u[0] - p; - e = u[0] + u[2] + p - } else { - a = u[0] - c * d; - A = u[0] + u[2] + c * d; - o = u[1] - p; - e = u[1] + u[3] + p - } - for (v = 0; v < l.length; v++) { - g = l[v]; - f = h[v]; - g = g * c + m; - f = f * p + k; - if (a <= g && g <= A && o <= f && f <= e) { - if (q.renderer) { - n = { - type: "items", - translationX: g, - translationY: f - }; - B = [C, n, { - store: C.getStore() - }, v]; - D = Ext.callback(q.renderer, null, B, 0, j); - n = Ext.apply(n, D) - } else { - n.translationX = g; - n.translationY = f - } - C.putMarker("items", n, v, !q.renderer); - if (b && z[v]) { - C.drawLabel(z[v], g, f, v, u) - } - } - } - }, - drawLabel: function(j, h, g, p, a) { - var r = this, - m = r.attr, - d = r.getMarker("labels"), - c = d.getTemplate(), - l = r.labelCfg || (r.labelCfg = {}), - b = r.surfaceMatrix, - f, e, i = m.labelOverflowPadding, - o = m.flipXY, - k, n, s, q; - l.text = j; - n = r.getMarkerBBox("labels", p, true); - if (!n) { - r.putMarker("labels", l, p); - n = r.getMarkerBBox("labels", p, true) - } - if (o) { - l.rotationRads = Math.PI * 0.5 - } else { - l.rotationRads = 0 - } - k = n.height / 2; - f = h; - switch (c.attr.display) { - case "under": - e = g - k - i; - break; - case "rotate": - f += i; - e = g - i; - l.rotationRads = -Math.PI / 4; - break; - default: - e = g + k + i - } - l.x = b.x(f, e); - l.y = b.y(f, e); - if (c.attr.renderer) { - q = [j, d, l, { - store: r.getStore() - }, p]; - s = Ext.callback(c.attr.renderer, null, q, 0, r.getSeries()); - if (typeof s === "string") { - l.text = s - } else { - Ext.apply(l, s) - } - } - r.putMarker("labels", l, p) - } -}); -Ext.define("Ext.chart.series.Scatter", { - extend: "Ext.chart.series.Cartesian", - alias: "series.scatter", - type: "scatter", - seriesType: "scatterSeries", - requires: ["Ext.chart.series.sprite.Scatter"], - config: { - itemInstancing: { - fx: { - customDurations: { - translationX: 0, - translationY: 0 - } - } - } - }, - themeMarkerCount: function() { - return 1 - }, - applyMarker: function(b, a) { - this.getItemInstancing(); - this.setItemInstancing(b); - return this.callParent(arguments) - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getMarkerStyleByIndex(0), - c = a.fillStyle; - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - } -}); -Ext.define("Ext.chart.theme.Blue", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.blue", "chart.theme.Blue"], - config: { - baseColor: "#4d7fe6" - } -}); -Ext.define("Ext.chart.theme.BlueGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.blue-gradients", "chart.theme.Blue:gradients"], - config: { - baseColor: "#4d7fe6", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category1", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category1", "chart.theme.Category1"], - config: { - colors: ["#f0a50a", "#c20024", "#2044ba", "#810065", "#7eae29"] - } -}); -Ext.define("Ext.chart.theme.Category1Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category1-gradients", "chart.theme.Category1:gradients"], - config: { - colors: ["#f0a50a", "#c20024", "#2044ba", "#810065", "#7eae29"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category2", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category2", "chart.theme.Category2"], - config: { - colors: ["#6d9824", "#87146e", "#2a9196", "#d39006", "#1e40ac"] - } -}); -Ext.define("Ext.chart.theme.Category2Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category2-gradients", "chart.theme.Category2:gradients"], - config: { - colors: ["#6d9824", "#87146e", "#2a9196", "#d39006", "#1e40ac"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category3", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category3", "chart.theme.Category3"], - config: { - colors: ["#fbbc29", "#ce2e4e", "#7e0062", "#158b90", "#57880e"] - } -}); -Ext.define("Ext.chart.theme.Category3Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category3-gradients", "chart.theme.Category3:gradients"], - config: { - colors: ["#fbbc29", "#ce2e4e", "#7e0062", "#158b90", "#57880e"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category4", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category4", "chart.theme.Category4"], - config: { - colors: ["#ef5773", "#fcbd2a", "#4f770d", "#1d3eaa", "#9b001f"] - } -}); -Ext.define("Ext.chart.theme.Category4Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category4-gradients", "chart.theme.Category4:gradients"], - config: { - colors: ["#ef5773", "#fcbd2a", "#4f770d", "#1d3eaa", "#9b001f"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category5", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category5", "chart.theme.Category5"], - config: { - colors: ["#7eae29", "#fdbe2a", "#910019", "#27b4bc", "#d74dbc"] - } -}); -Ext.define("Ext.chart.theme.Category5Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category5-gradients", "chart.theme.Category5:gradients"], - config: { - colors: ["#7eae29", "#fdbe2a", "#910019", "#27b4bc", "#d74dbc"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category6", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category6", "chart.theme.Category6"], - config: { - colors: ["#44dce1", "#0b2592", "#996e05", "#7fb325", "#b821a1"] - } -}); -Ext.define("Ext.chart.theme.Category6Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category6-gradients", "chart.theme.Category6:gradients"], - config: { - colors: ["#44dce1", "#0b2592", "#996e05", "#7fb325", "#b821a1"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.DefaultGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.default-gradients", "chart.theme.Base:gradients"], - config: { - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Green", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.green", "chart.theme.Green"], - config: { - baseColor: "#b1da5a" - } -}); -Ext.define("Ext.chart.theme.GreenGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.green-gradients", "chart.theme.Green:gradients"], - config: { - baseColor: "#b1da5a", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Midnight", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.midnight", "chart.theme.Midnight"], - config: { - colors: ["#A837FF", "#4AC0F2", "#FF4D35", "#FF8809", "#61C102", "#FF37EA"], - chart: { - defaults: { - background: "rgb(52, 52, 53)" - } - }, - axis: { - defaults: { - style: { - strokeStyle: "rgb(224, 224, 227)" - }, - label: { - fillStyle: "rgb(224, 224, 227)" - }, - title: { - fillStyle: "rgb(224, 224, 227)" - }, - grid: { - strokeStyle: "rgb(112, 112, 115)" - } - } - }, - series: { - defaults: { - label: { - fillStyle: "rgb(224, 224, 227)" - } - } - }, - sprites: { - text: { - fillStyle: "rgb(224, 224, 227)" - } - } - } -}); -Ext.define("Ext.chart.theme.Muted", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.muted", "chart.theme.Muted"], - config: { - colors: ["#8ca640", "#974144", "#4091ba", "#8e658e", "#3b8d8b", "#b86465", "#d2af69", "#6e8852", "#3dcc7e", "#a6bed1", "#cbaa4b", "#998baa"] - } -}); -Ext.define("Ext.chart.theme.Purple", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.purple", "chart.theme.Purple"], - config: { - baseColor: "#da5abd" - } -}); -Ext.define("Ext.chart.theme.PurpleGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.purple-gradients", "chart.theme.Purple:gradients"], - config: { - baseColor: "#da5abd", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Red", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.red", "chart.theme.Red"], - config: { - baseColor: "#e84b67" - } -}); -Ext.define("Ext.chart.theme.RedGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.red-gradients", "chart.theme.Red:gradients"], - config: { - baseColor: "#e84b67", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Sky", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.sky", "chart.theme.Sky"], - config: { - baseColor: "#4ce0e7" - } -}); -Ext.define("Ext.chart.theme.SkyGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.sky-gradients", "chart.theme.Sky:gradients"], - config: { - baseColor: "#4ce0e7", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Yellow", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.yellow", "chart.theme.Yellow"], - config: { - baseColor: "#fec935" - } -}); -Ext.define("Ext.chart.theme.YellowGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.yellow-gradients", "chart.theme.Yellow:gradients"], - config: { - baseColor: "#fec935", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.draw.Point", { - requires: ["Ext.draw.Draw", "Ext.draw.Matrix"], - isPoint: true, - x: 0, - y: 0, - length: 0, - angle: 0, - angleUnits: "degrees", - statics: { - fly: (function() { - var a = null; - return function(b, c) { - if (!a) { - a = new Ext.draw.Point() - } - a.constructor(b, c); - return a - } - })() - }, - constructor: function(a, c) { - var b = this; - if (typeof a === "number") { - b.x = a; - if (typeof c === "number") { - b.y = c - } else { - b.y = a - } - } else { - if (Ext.isArray(a)) { - b.x = a[0]; - b.y = a[1] - } else { - if (a) { - b.x = a.x; - b.y = a.y - } - } - } - b.calculatePolar() - }, - calculateCartesian: function() { - var b = this, - a = b.length, - c = b.angle; - if (b.angleUnits === "degrees") { - c = Ext.draw.Draw.rad(c) - } - b.x = Math.cos(c) * a; - b.y = Math.sin(c) * a - }, - calculatePolar: function() { - var b = this, - a = b.x, - c = b.y; - b.length = Math.sqrt(a * a + c * c); - b.angle = Math.atan2(c, a); - if (b.angleUnits === "degrees") { - b.angle = Ext.draw.Draw.degrees(b.angle) - } - }, - setX: function(a) { - this.x = a; - this.calculatePolar() - }, - setY: function(a) { - this.y = a; - this.calculatePolar() - }, - set: function(a, b) { - this.constructor(a, b) - }, - setAngle: function(a) { - this.angle = a; - this.calculateCartesian() - }, - setLength: function(a) { - this.length = a; - this.calculateCartesian() - }, - setPolar: function(b, a) { - this.angle = b; - this.length = a; - this.calculateCartesian() - }, - clone: function() { - return new Ext.draw.Point(this.x, this.y) - }, - add: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return new Ext.draw.Point(this.x + b.x, this.y + b.y) - }, - sub: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return new Ext.draw.Point(this.x - b.x, this.y - b.y) - }, - mul: function(a) { - return new Ext.draw.Point(this.x * a, this.y * a) - }, - div: function(a) { - return new Ext.draw.Point(this.x / a, this.y / a) - }, - dot: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return this.x * b.x + this.y * b.y - }, - equals: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return this.x === b.x && this.y === b.y - }, - rotate: function(f, c) { - var d, e, b, g, a; - if (this.angleUnits === "degrees") { - f = Ext.draw.Draw.rad(f); - d = Math.sin(f); - e = Math.cos(f) - } - if (c) { - b = c.x; - g = c.y - } else { - b = 0; - g = 0 - } - a = Ext.draw.Matrix.fly([e, d, -d, e, b - e * b + g * d, g - e * g + b * -d]).transformPoint(this); - return new Ext.draw.Point(a) - }, - transform: function(a) { - if (a && a.isMatrix) { - return new Ext.draw.Point(a.transformPoint(this)) - } else { - if (arguments.length === 6) { - return new Ext.draw.Point(Ext.draw.Matrix.fly(arguments).transformPoint(this)) - } else { - Ext.raise("Invalid parameters.") - } - } - }, - round: function() { - return new Ext.draw.Point(Math.round(this.x), Math.round(this.y)) - }, - ceil: function() { - return new Ext.draw.Point(Math.ceil(this.x), Math.ceil(this.y)) - }, - floor: function() { - return new Ext.draw.Point(Math.floor(this.x), Math.floor(this.y)) - }, - abs: function(a, b) { - return new Ext.draw.Point(Math.abs(this.x), Math.abs(this.y)) - }, - normalize: function(c) { - var b = this.x, - f = this.y, - a, e, d; - c = c || 1; - if (b === 0) { - a = 0; - e = c * Ext.Number.sign(f) - } else { - d = f / b; - a = c / Math.sqrt(1 + d * d); - e = a * d - } - return new Ext.draw.Point(a, e) - }, - getDistanceToLine: function(c, b) { - if (arguments.length === 4) { - c = new Ext.draw.Point(arguments[0], arguments[1]); - b = new Ext.draw.Point(arguments[2], arguments[3]) - } - var d = b.sub(c).normalize(), - a = c.sub(this); - return a.sub(d.mul(a.dot(d))) - }, - isZero: function() { - return this.x === 0 && this.y === 0 - }, - isNumber: function() { - return Ext.isNumber(this.x + this.y) - } -}); -Ext.define("Ext.draw.plugin.SpriteEvents", { - extend: "Ext.plugin.Abstract", - alias: "plugin.spriteevents", - requires: ["Ext.draw.PathUtil"], - mouseMoveEvents: { - mousemove: true, - mouseover: true, - mouseout: true - }, - spriteMouseMoveEvents: { - spritemousemove: true, - spritemouseover: true, - spritemouseout: true - }, - init: function(a) { - var b = "handleEvent"; - this.drawContainer = a; - a.addElementListener({ - click: b, - dblclick: b, - mousedown: b, - mousemove: b, - mouseup: b, - mouseover: b, - mouseout: b, - priority: 1001, - scope: this - }) - }, - hasSpriteMouseMoveListeners: function() { - var b = this.drawContainer.hasListeners, - a; - for (a in this.spriteMouseMoveEvents) { - if (a in b) { - return true - } - } - return false - }, - hitTestEvent: function(f) { - var b = this.drawContainer.getItems(), - a, d, c; - for (c = b.length - 1; c >= 0; c--) { - a = b.get(c); - d = a.hitTestEvent(f); - if (d) { - return d - } - } - return null - }, - handleEvent: function(f) { - var d = this, - b = d.drawContainer, - g = f.type in d.mouseMoveEvents, - a = d.lastSprite, - c; - if (g && !d.hasSpriteMouseMoveListeners()) { - return - } - c = d.hitTestEvent(f); - if (g && !Ext.Object.equals(c, a)) { - if (a) { - b.fireEvent("spritemouseout", a, f) - } - if (c) { - b.fireEvent("spritemouseover", c, f) - } - } - if (c) { - b.fireEvent("sprite" + f.type, c, f) - } - d.lastSprite = c - } -}); -Ext.define("Ext.chart.TipSurface", { - extend: "Ext.draw.Container", - spriteArray: false, - renderFirst: true, - constructor: function(a) { - this.callParent([a]); - if (a.sprites) { - this.spriteArray = [].concat(a.sprites); - delete a.sprites - } - }, - onRender: function() { - var c = this, - b = 0, - a = 0, - d, e; - this.callParent(arguments); - e = c.spriteArray; - if (c.renderFirst && e) { - c.renderFirst = false; - for (a = e.length; b < a; b++) { - d = c.surface.add(e[b]); - d.setAttributes({ - hidden: false - }, true) - } - } - } -}); -Ext.define("Ext.chart.interactions.ItemInfo", { - extend: "Ext.chart.interactions.Abstract", - type: "iteminfo", - alias: "interaction.iteminfo", - config: { - extjsGestures: { - start: { - event: "click", - handler: "onInfoGesture" - }, - move: { - event: "mousemove", - handler: "onInfoGesture" - }, - end: { - event: "mouseleave", - handler: "onInfoGesture" - } - } - }, - item: null, - onInfoGesture: function(f, a) { - var c = this, - b = c.getItemForEvent(f), - d = b && b.series.tooltip; - if (d) { - d.onMouseMove.call(d, f) - } - if (b !== c.item) { - if (b) { - b.series.showTip(b) - } else { - c.item.series.hideTip(c.item) - } - c.item = b - } - return false - } -}); \ No newline at end of file diff --git a/serverside/jsmod/6.0-4/charts.js.original b/serverside/jsmod/6.0-4/charts.js.original deleted file mode 100644 index 2b8dd71..0000000 --- a/serverside/jsmod/6.0-4/charts.js.original +++ /dev/null @@ -1 +0,0 @@ -Ext.define("Ext.draw.ContainerBase",{extend:"Ext.panel.Panel",requires:["Ext.window.Window"],previewTitleText:"Chart Preview",previewAltText:"Chart preview",layout:"container",addElementListener:function(){var b=this,a=arguments;if(b.rendered){b.el.on.apply(b.el,a)}else{b.on("render",function(){b.el.on.apply(b.el,a)})}},removeElementListener:function(){var b=this,a=arguments;if(b.rendered){b.el.un.apply(b.el,a)}},afterRender:function(){this.callParent(arguments);this.initAnimator()},getItems:function(){var b=this,a=b.items;if(!a||!a.isMixedCollection){b.initItems()}return b.items},onRender:function(){this.callParent(arguments);this.element=this.el;this.innerElement=this.body},setItems:function(a){this.items=a;return a},setSurfaceSize:function(b,a){this.resizeHandler({width:b,height:a});this.renderFrame()},onResize:function(c,a,b,e){var d=this;d.callParent([c,a,b,e]);d.setBodySize({width:c,height:a})},preview:function(){var a=this.getImage();new Ext.window.Window({title:this.previewTitleText,closeable:true,renderTo:Ext.getBody(),autoShow:true,maximizeable:true,maximized:true,border:true,layout:{type:"hbox",pack:"center",align:"middle"},items:{xtype:"container",items:{xtype:"image",mode:"img",cls:Ext.baseCSSPrefix+"chart-image",alt:this.previewAltText,src:a.data,listeners:{afterrender:function(){var e=this,b=e.imgEl.dom,d=a.type==="svg"?1:(window.devicePixelRatio||1),c;if(!b.naturalWidth||!b.naturalHeight){b.onload=function(){var g=b.naturalWidth,f=b.naturalHeight;e.setWidth(Math.floor(g/d));e.setHeight(Math.floor(f/d))}}else{c=e.getSize();e.setWidth(Math.floor(c.width/d));e.setHeight(Math.floor(c.height/d))}}}}}})},privates:{getTargetEl:function(){return this.innerElement},reattachToBody:function(){var a=this;if(a.pendingDetachSize){a.onBodyResize()}a.pendingDetachSize=false;a.callParent()}}});Ext.define("Ext.draw.SurfaceBase",{extend:"Ext.Widget",getOwnerBody:function(){return this.ownerCt.body},destroy:function(){var a=this;if(a.hasListeners.destroy){a.fireEvent("destroy",a)}a.callParent()}});Ext.define("Ext.draw.Color",{statics:{colorToHexRe:/(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,rgbToHexRe:/\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/,rgbaToHexRe:/\s*rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\.\d]+)\)/,hexRe:/\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/,NONE:"none",RGBA_NONE:"rgba(0, 0, 0, 0)"},isColor:true,lightnessFactor:0.2,constructor:function(d,b,a,c){this.setRGB(d,b,a,c)},setRGB:function(e,c,a,d){var b=this;b.r=Math.min(255,Math.max(0,e));b.g=Math.min(255,Math.max(0,c));b.b=Math.min(255,Math.max(0,a));if(d===undefined){b.a=1}else{b.a=Math.min(1,Math.max(0,d))}},getGrayscale:function(){return this.r*0.3+this.g*0.59+this.b*0.11},getHSL:function(){var i=this,a=i.r/255,f=i.g/255,j=i.b/255,k=Math.max(a,f,j),d=Math.min(a,f,j),m=k-d,e,n=0,c=0.5*(k+d);if(d!==k){n=(c<=0.5)?m/(k+d):m/(2-k-d);if(a===k){e=60*(f-j)/m}else{if(f===k){e=120+60*(j-a)/m}else{e=240+60*(a-f)/m}}if(e<0){e+=360}if(e>=360){e-=360}}return[e,n,c]},getHSV:function(){var i=this,a=i.r/255,f=i.g/255,j=i.b/255,k=Math.max(a,f,j),d=Math.min(a,f,j),c=k-d,e,m=0,l=k;if(d!=k){m=l?c/l:0;if(a===k){e=60*(f-j)/c}else{if(f===k){e=60*(j-a)/c+120}else{e=60*(a-f)/c+240}}if(e<0){e+=360}if(e>=360){e-=360}}return[e,m,l]},setHSL:function(g,f,e){var i=this,d=Math.abs,j,b,a;g=(g%360+360)%360;f=f>1?1:f<0?0:f;e=e>1?1:e<0?0:e;if(f===0||g===null){e*=255;i.setRGB(e,e,e)}else{g/=60;j=f*(1-d(2*e-1));b=j*(1-d(g%2-1));a=e-j/2;a*=255;j*=255;b*=255;switch(Math.floor(g)){case 0:i.setRGB(j+a,b+a,a);break;case 1:i.setRGB(b+a,j+a,a);break;case 2:i.setRGB(a,j+a,b+a);break;case 3:i.setRGB(a,b+a,j+a);break;case 4:i.setRGB(b+a,a,j+a);break;case 5:i.setRGB(j+a,a,b+a);break}}return i},setHSV:function(f,e,d){var g=this,i,b,a;f=(f%360+360)%360;e=e>1?1:e<0?0:e;d=d>1?1:d<0?0:d;if(e===0||f===null){d*=255;g.setRGB(d,d,d)}else{f/=60;i=d*e;b=i*(1-Math.abs(f%2-1));a=d-i;a*=255;i*=255;b*=255;switch(Math.floor(f)){case 0:g.setRGB(i+a,b+a,a);break;case 1:g.setRGB(b+a,i+a,a);break;case 2:g.setRGB(a,i+a,b+a);break;case 3:g.setRGB(a,b+a,i+a);break;case 4:g.setRGB(b+a,a,i+a);break;case 5:g.setRGB(i+a,a,b+a);break}}return g},createLighter:function(b){if(!b&&b!==0){b=this.lightnessFactor}var a=this.getHSL();a[2]=Ext.Number.constrain(a[2]+b,0,1);return Ext.draw.Color.fromHSL(a[0],a[1],a[2])},createDarker:function(a){if(!a&&a!==0){a=this.lightnessFactor}return this.createLighter(-a)},toString:function(){var f=this,c=Math.round;if(f.a===1){var e=c(f.r).toString(16),d=c(f.g).toString(16),a=c(f.b).toString(16);e=(e.length===1)?"0"+e:e;d=(d.length===1)?"0"+d:d;a=(a.length===1)?"0"+a:a;return["#",e,d,a].join("")}else{return"rgba("+[c(f.r),c(f.g),c(f.b),f.a===0?0:f.a.toFixed(15)].join(", ")+")"}},toHex:function(b){if(Ext.isArray(b)){b=b[0]}if(!Ext.isString(b)){return""}if(b.substr(0,1)==="#"){return b}var e=Ext.draw.Color.colorToHexRe.exec(b);if(Ext.isArray(e)){var f=parseInt(e[2],10),d=parseInt(e[3],10),a=parseInt(e[4],10),c=a|(d<<8)|(f<<16);return e[1]+"#"+("000000"+c.toString(16)).slice(-6)}else{return""}},setFromString:function(j){var e,h,f,c,d=1,i=parseInt;if(j===Ext.draw.Color.NONE){this.r=this.g=this.b=this.a=0;return this}if((j.length===4||j.length===7)&&j.substr(0,1)==="#"){e=j.match(Ext.draw.Color.hexRe);if(e){h=i(e[1],16)>>0;f=i(e[2],16)>>0;c=i(e[3],16)>>0;if(j.length===4){h+=(h*16);f+=(f*16);c+=(c*16)}}}else{if((e=j.match(Ext.draw.Color.rgbToHexRe))){h=+e[1];f=+e[2];c=+e[3]}else{if((e=j.match(Ext.draw.Color.rgbaToHexRe))){h=+e[1];f=+e[2];c=+e[3];d=+e[4]}else{if(Ext.draw.Color.ColorList.hasOwnProperty(j.toLowerCase())){return this.setFromString(Ext.draw.Color.ColorList[j.toLowerCase()])}}}}if(typeof h==="undefined"){return this}this.r=h;this.g=f;this.b=c;this.a=d;return this}},function(){var a=new this();this.addStatics({fly:function(f,e,c,d){switch(arguments.length){case 1:a.setFromString(f);break;case 3:case 4:a.setRGB(f,e,c,d);break;default:return null}return a},ColorList:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},fromHSL:function(d,c,b){return(new this(0,0,0,0)).setHSL(d,c,b)},fromHSV:function(d,c,b){return(new this(0,0,0,0)).setHSL(d,c,b)},fromString:function(b){return(new this(0,0,0,0)).setFromString(b)},create:function(b){if(b instanceof this){return b}else{if(Ext.isArray(b)){return new Ext.draw.Color(b[0],b[1],b[2],b[3])}else{if(Ext.isString(b)){return Ext.draw.Color.fromString(b)}else{if(arguments.length>2){return new Ext.draw.Color(arguments[0],arguments[1],arguments[2],arguments[3])}else{return new Ext.draw.Color(0,0,0,0)}}}}}})});Ext.define("Ext.draw.sprite.AnimationParser",function(){function a(d,c,b){return d+(c-d)*b}return{singleton:true,attributeRe:/^url\(#([a-zA-Z\-]+)\)$/,requires:["Ext.draw.Color"],color:{parseInitial:function(c,b){if(Ext.isString(c)){c=Ext.draw.Color.create(c)}if(Ext.isString(b)){b=Ext.draw.Color.create(b)}if((c instanceof Ext.draw.Color)&&(b instanceof Ext.draw.Color)){return[[c.r,c.g,c.b,c.a],[b.r,b.g,b.b,b.a]]}else{return[c||b,b||c]}},compute:function(d,c,b){if(!Ext.isArray(d)||!Ext.isArray(c)){return c||d}else{return[a(d[0],c[0],b),a(d[1],c[1],b),a(d[2],c[2],b),a(d[3],c[3],b)]}},serve:function(c){var b=Ext.draw.Color.fly(c[0],c[1],c[2],c[3]);return b.toString()}},number:{parse:function(b){return b===null?null:+b},compute:function(d,c,b){if(!Ext.isNumber(d)||!Ext.isNumber(c)){return c||d}else{return a(d,c,b)}}},angle:{parseInitial:function(c,b){if(b-c>Math.PI){b-=Math.PI*2}else{if(b-c<-Math.PI){b+=Math.PI*2}}return[c,b]},compute:function(d,c,b){if(!Ext.isNumber(d)||!Ext.isNumber(c)){return c||d}else{return a(d,c,b)}}},path:{parseInitial:function(m,n){var c=m.toStripes(),o=n.toStripes(),e,d,k=c.length,p=o.length,h,f,b,g=o[p-1],l=[g[g.length-2],g[g.length-1]];for(e=k;e=1){return l.path}var e=0,f=c.length,d=0,b,k,h,n=l.temp.params,g=0;for(;eMath.min(b.x+b.width,a.x+a.width))||(Math.max(b.y,a.y)-c>Math.min(b.y+b.height,a.y+a.height))},isPointInBBox:function(a,c,b){return !!b&&a>=b.x&&a<=(b.x+b.width)&&c>=b.y&&c<=(b.y+b.height)},spline:function(m){var e,c,k=m.length,b,h,l,f,a=0,g=new Float32Array(m.length),n=new Float32Array(m.length*3-2);g[0]=0;g[k-1]=0;for(e=1;e0;e--){a=3.732050807568877+48.248711305964385/(-13.928203230275537+Math.pow(0.07179676972449123,e));g[e]-=g[e+1]*a}f=m[0];b=f-g[0];for(e=0,c=0;e=d&&h>=s)||(h<=d&&h<=s)){g=j=p}else{g=f((i-e)/k(h-d));if(dp){c-=n}g+=c;j+=c;m=i-r*a(g);l=h+r*b(g);v=i+q*a(j);u=h+q*b(j);if((h>d&&ld)){m+=k(d-l)*(m-i)/(l-h);l=d}if((h>s&&us)){v-=k(s-u)*(v-i)/(u-h);u=s}return{x1:m,y1:l,x2:v,y2:u}},smooth:function(l,j,o){var k=l.length,h,g,c,b,q,p,n,m,f=[],e=[],d,a;for(d=0;d=Math.PI){a-=Math.PI*2}}return a}},data:function(a){if(Ext.isArray(a)){return a.slice()}else{if(a instanceof Float32Array){return new Float32Array(a)}}},bool:function(a){return !!a},color:function(a){if(a instanceof Ext.draw.Color){return a.toString()}else{if(a instanceof Ext.draw.gradient.Gradient){return a}else{if(!a){return Ext.draw.Color.NONE}else{if(Ext.isString(a)){if(a.substr(0,3)==="url"){a=Ext.draw.gradient.GradientDefinition.get(a);if(Ext.isString(a)){return a}}else{return Ext.draw.Color.fly(a).toString()}}}}}if(a.type==="linear"){return Ext.create("Ext.draw.gradient.Linear",a)}else{if(a.type==="radial"){return Ext.create("Ext.draw.gradient.Radial",a)}else{if(a.type==="pattern"){return Ext.create("Ext.draw.gradient.Pattern",a)}else{return Ext.draw.Color.NONE}}}},limited:function(a,b){return function(c){c=+c;return Ext.isNumber(c)?Math.min(Math.max(c,a),b):undefined}},limited01:function(a){a=+a;return Ext.isNumber(a)?Math.min(Math.max(a,0),1):undefined},enums:function(){var d={},a=Array.prototype.slice.call(arguments,0),b,c;for(b=0,c=a.length;b=(7-4*o)/11){return i*i-g((11-6*o-11*q)/4,2)}}},elastic:function(o,i){return g(2,10*--o)*m(20*o*e*(i||1)/3)}},k={},a,f,d;function h(i){return function(o){return g(o,i)}}function n(i,o){k[i+"In"]=function(p){return o(p)};k[i+"Out"]=function(p){return 1-o(1-p)};k[i+"InOut"]=function(p){return(p<=0.5)?o(2*p)/2:(2-o(2*(1-p)))/2}}for(d=0,f=b.length;d-1},empty:function(){return this.animations.length===0},step:function(d){var c=this,f=c.animations,e,a=0,b=f.length;for(;a0},applyEasing:function(a){if(typeof a==="string"){a=Ext.draw.TimingFunctions.easingMap[a]}return a},applyCustomEasings:function(a,e){e=e||{};var g,d,b,h,c,f;for(d in a){g=true;h=a[d];b=d.split(",");if(typeof h==="string"){h=Ext.draw.TimingFunctions.easingMap[h]}for(c=0,f=b.length;c=1){h[a]=f[a];delete f[a];if(d[a].remove){h.removeFromInstance=h.removeFromInstance||{};h.removeFromInstance[a]=true}delete d[a]}else{h[a]=b.serve(b.compute(b.source,b.target,b.easing(i),g[a]));e=true}}g.lastUpdate=c;this.setAnimating(g,e);return h},pushDown:function(a,b){b=this.callParent([a.animationOriginal,b]);return this.setAttrs(a,b)},popUp:function(a,b){a=a.prototype;b=this.setAttrs(a,b);if(this._next){return this._next.popUp(a,b)}else{return Ext.apply(a,b)}},step:function(g){var f=this,c=f.animatingPool.slice(),e=c.length,b=0,a,d;for(;b=e.x&&a<=(e.x+e.width)&&f>=e.y&&f<=(e.y+e.height);if(d){return{sprite:this}}}return null},isVisible:function(){var e=this.attr,f=this.getParent(),g=f&&(f.isSurface||f.isVisible()),d=g&&!e.hidden&&e.globalAlpha,b=Ext.draw.Color.NONE,a=Ext.draw.Color.RGBA_NONE,c=e.fillOpacity&&e.fillStyle!==b&&e.fillStyle!==a,i=e.strokeOpacity&&e.strokeStyle!==b&&e.strokeStyle!==a,h=d&&(c||i);return !!h},repaint:function(){var a=this.getSurface();if(a){a.renderFrame()}},remove:function(){var a=this.getSurface();if(a&&a.isSurface){return a.remove(this)}return null},destroy:function(){var b=this,a=b.topModifier,c;while(a){c=a;a=a.getPrevious();c.destroy()}delete b.attr;b.remove();if(b.fireEvent("beforedestroy",b)!==false){b.fireEvent("destroy",b)}b.callParent()}},function(){this.def=new Ext.draw.sprite.AttributeDefinition(this.def);this.def.spriteClass=this});Ext.define("Ext.draw.Path",{requires:["Ext.draw.Draw"],statics:{pathRe:/,?([achlmqrstvxz]),?/gi,pathRe2:/-/gi,pathSplitRe:/\s|,/g},svgString:"",constructor:function(a){var b=this;b.commands=[];b.params=[];b.cursor=null;b.startX=0;b.startY=0;if(a){b.fromSvgString(a)}},clear:function(){var a=this;a.params.length=0;a.commands.length=0;a.cursor=null;a.startX=0;a.startY=0;a.dirt()},dirt:function(){this.svgString=""},moveTo:function(a,c){var b=this;if(!b.cursor){b.cursor=[a,c]}b.params.push(a,c);b.commands.push("M");b.startX=a;b.startY=c;b.cursor[0]=a;b.cursor[1]=c;b.dirt()},lineTo:function(a,c){var b=this;if(!b.cursor){b.cursor=[a,c];b.params.push(a,c);b.commands.push("M")}else{b.params.push(a,c);b.commands.push("L")}b.cursor[0]=a;b.cursor[1]=c;b.dirt()},bezierCurveTo:function(c,e,b,d,a,g){var f=this;if(!f.cursor){f.moveTo(c,e)}f.params.push(c,e,b,d,a,g);f.commands.push("C");f.cursor[0]=a;f.cursor[1]=g;f.dirt()},quadraticCurveTo:function(b,e,a,d){var c=this;if(!c.cursor){c.moveTo(b,e)}c.bezierCurveTo((2*b+c.cursor[0])/3,(2*e+c.cursor[1])/3,(2*b+a)/3,(2*e+d)/3,a,d)},closePath:function(){var a=this;if(a.cursor){a.cursor=null;a.commands.push("Z");a.dirt()}},arcTo:function(A,f,z,d,j,i,v){var E=this;if(i===undefined){i=j}if(v===undefined){v=0}if(!E.cursor){E.moveTo(A,f);return}if(j===0||i===0){E.lineTo(A,f);return}z-=A;d-=f;var B=E.cursor[0]-A,g=E.cursor[1]-f,C=z*g-d*B,b,a,l,r,k,q,x=Math.sqrt(B*B+g*g),u=Math.sqrt(z*z+d*d),t,e,c;if(C===0){E.lineTo(A,f);return}if(i!==j){b=Math.cos(v);a=Math.sin(v);l=b/j;r=a/i;k=-a/j;q=b/i;var D=l*B+r*g;g=k*B+q*g;B=D;D=l*z+r*d;d=k*z+q*d;z=D}else{B/=j;g/=i;z/=j;d/=i}e=B*u+z*x;c=g*u+d*x;t=1/(Math.sin(Math.asin(Math.abs(C)/(x*u))*0.5)*Math.sqrt(e*e+c*c));e*=t;c*=t;var o=(e*B+c*g)/(B*B+g*g),m=(e*z+c*d)/(z*z+d*d);var n=B*o-e,p=g*o-c,h=z*m-e,y=d*m-c,w=Math.atan2(p,n),s=Math.atan2(y,h);if(C>0){if(s=Math.PI*2){o.ellipse(h,f,c,a,q,n,n+Math.PI,e);o.ellipse(h,f,c,a,q,n+Math.PI,d,e);return}if(!e){if(d=m){s.push(j+u*b+i,h+t*b+f,j*b+u+i,h*b+t+f,u+i,t+f);r+=6;v-=m;C=j;j=u;u=-C;C=h;h=t;t=-C}if(v){g=(0.3294738052815987+0.012120855841304373*v)*v;A=Math.cos(v);a=Math.sin(v);B=A+g*a;c=a-g*A;s.push(j+u*g+i,h+t*g+f,j*B+u*c+i,h*B+t*c+f,j*A+u*a+i,h*A+t*a+f);r+=6}return r},arcSvg:function(j,h,r,m,w,t,c){if(j<0){j=-j}if(h<0){h=-h}var x=this,u=x.cursor[0],f=x.cursor[1],a=(u-t)/2,y=(f-c)/2,d=Math.cos(r),s=Math.sin(r),o=a*d+y*s,v=-a*s+y*d,i=o/j,g=v/h,p=i*i+g*g,e=(u+t)*0.5,b=(f+c)*0.5,l=0,k=0;if(p>=1){p=Math.sqrt(p);j*=p;h*=p}else{p=Math.sqrt(1/p-1);if(m===w){p=-p}l=p*j*g;k=-p*h*i;e+=d*l-s*k;b+=s*l+d*k}var q=Math.atan2((v-k)/h,(o-l)/j),n=Math.atan2((-v-k)/h,(-o-l)/j)-q;if(w){if(n<=0){n+=Math.PI*2}}else{if(n>=0){n-=Math.PI*2}}x.ellipse(e,b,j,h,r,q,q+n,1-w)},fromSvgString:function(e){if(!e){return}var m=this,h,l={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0,A:7,C:6,H:1,L:2,M:2,Q:4,S:4,T:2,V:1,Z:0},k="",g,f,c=0,b=0,d=false,j,n,a;if(Ext.isString(e)){h=e.replace(Ext.draw.Path.pathRe," $1 ").replace(Ext.draw.Path.pathRe2," -").split(Ext.draw.Path.pathSplitRe)}else{if(Ext.isArray(e)){h=e.join(",").split(Ext.draw.Path.pathSplitRe)}}for(j=0,n=0;j=0){q=Math.sqrt(q);o=(q-g)/2/i;if(00){o-=q/i;if(0k.x){b=k.x}if(hk.y){f=k.y}if(a=f.length||!e.isVisible()){return a}d.attr=f[c];a=d.isVisible(point,options);d.attr=b;return a},render:function(b,l,d,h){var g=this,j=g.getTemplate(),k=g.attr.matrix,c=j.attr,a=g.instances,e,f=g.position;k.toContext(l);j.preRender(b,l,d,h);j.useAttributes(l,h);for(e=0;ee){f=m.substr(e,i-e)}else{continue}}g=f.indexOf("/");if(g>0){f=f.substr(0,g)}else{if(g===0){continue}}if(f!=="normal"&&f!=="inherit"){h=n[f];if(h){l[h]=f}else{if(f.match(Ext.dom.Element.unitRe)){l.fontSize=f}else{l.fontFamily=m.substr(e);break}}}e=i+1}if(!l.fontStyle){l.fontStyle=""}if(!l.fontVariant){l.fontVariant=""}if(!l.fontWeight){l.fontWeight=""}this.setAttributes(l,true)},fontProperties:{fontStyle:true,fontVariant:true,fontWeight:true,fontSize:true,fontFamily:true},setAttributes:function(g,i,e){var f,h;if(g&&g.font){h={};for(f in g){if(!(f in this.fontProperties)){h[f]=g[f]}}g=h}this.callParent([g,i,e])},getBBox:function(g){var h=this,f=h.attr.bbox.plain,e=h.getSurface();if(f.dirty){h.updatePlainBBox(f);f.dirty=false}if(e.getInherited().rtl&&e.getFlipRtlText()){h.updatePlainBBox(f,true)}return h.callParent([g])},rtlAlignments:{start:"end",center:"center",end:"start"},updatePlainBBox:function(k,B){var C=this,w=C.attr,o=w.x,n=w.y,q=[],t=w.font,r=w.text,s=w.textBaseline,l=w.textAlign,u=(B&&C.oldSize)?C.oldSize:(C.oldSize=Ext.draw.TextMeasurer.measureText(r,t)),z=C.getSurface(),p=z.getInherited().rtl,v=p&&z.getFlipRtlText(),h=z.getRect(),f=u.sizes,g=u.height,j=u.width,m=f?f.length:0,e,A=0;switch(s){case"hanging":case"top":break;case"ideographic":case"bottom":n-=g;break;case"alphabetic":n-=g*0.8;break;case"middle":n-=g*0.5;break}if(v){o=h[2]-h[0]-o;l=C.rtlAlignments[l]}switch(l){case"start":if(p){for(;A0&&m>0){a=(Math.sqrt(f*f+m*m)*Math.abs(Math.cos(c-Math.atan(f/m))))/2;k=q.createLinearGradient(d+p*a,b+j*a,d-p*a,b-j*a);for(e=0;e=0;b--){a[b].destroy()}}else{for(;b>=0;b--){c=a[b];c.setParent(null);c.setSurface(null)}}a.length=0;this.map={};this.dirtyZIndex=true},applyItems:function(a){if(this.getItems()){this.removeAll(true)}return Ext.Array.from(this.add(a))},createItem:function(a){return Ext.create(a.xclass||"sprite."+a.type,a)},getBBox:function(f,b){var f=Ext.Array.from(f),c=Infinity,h=-Infinity,g=Infinity,a=-Infinity,j,k,d,e;for(d=0,e=f.length;dk.x){c=k.x}if(hk.y){g=k.y}if(a0){g.isPendingRenderFrame=true;return}var f=g.getRect(),c=g.getBackground(),a=g.getItems(),e,b,d;if(!f){return}g.orderByZIndex();if(g.getDirty()){g.clear();g.clearTransform();if(c){g.renderSprite(c)}for(b=0,d=a.length;b=0;e--){d=g[e];if(d.hitTest){a=d.hitTest(b,c);if(a){return a}}}return null},hitTestEvent:function(b,a){var c=this.getEventXY(b);return this.hitTest(c,a)}});Ext.define("Ext.draw.engine.SvgContext",{requires:["Ext.draw.Color"],toSave:["strokeOpacity","strokeStyle","fillOpacity","fillStyle","globalAlpha","lineWidth","lineCap","lineJoin","lineDash","lineDashOffset","miterLimit","shadowOffsetX","shadowOffsetY","shadowBlur","shadowColor","globalCompositeOperation","position","fillGradient","strokeGradient"],strokeOpacity:1,strokeStyle:"none",fillOpacity:1,fillStyle:"none",lineDash:[],lineDashOffset:0,globalAlpha:1,lineWidth:1,lineCap:"butt",lineJoin:"miter",miterLimit:10,shadowOffsetX:0,shadowOffsetY:0,shadowBlur:0,shadowColor:"none",globalCompositeOperation:"src",urlStringRe:/^url\(#([\w\-]+)\)$/,constructor:function(a){this.surface=a;this.state=[];this.matrix=new Ext.draw.Matrix();this.path=null;this.clear()},clear:function(){this.group=this.surface.mainGroup;this.position=0;this.path=null},getElement:function(a){return this.surface.getSvgElement(this.group,a,this.position++)},removeElement:function(d){var d=Ext.fly(d),h,g,b,f,a,e,c;if(!d){return}if(d.dom.tagName==="g"){a=d.dom.gradients;for(c in a){a[c].destroy()}}else{h=d.getAttribute("fill");g=d.getAttribute("stroke");b=h&&h.match(this.urlStringRe);f=g&&g.match(this.urlStringRe);if(b&&b[1]){e=Ext.fly(b[1]);if(e){e.destroy()}}if(f&&f[1]){e=Ext.fly(f[1]);if(e){e.destroy()}}}d.destroy()},save:function(){var c=this.toSave,e={},d=this.getElement("g"),b,a;for(a=0;athis.position){this.removeElement(c[c.length-1])}for(a=0;athis.position){Ext.fly(a[a.length-1]).destroy()}return"url(#"+this.element.getId()+")"},destroy:function(){var b=this.statics().map,a=this.element;if(a&&a.dom){delete b[a.dom.id];a.destroy()}this.callParent()}});Ext.define("Ext.draw.engine.Svg",{extend:"Ext.draw.Surface",requires:["Ext.draw.engine.SvgContext"],statics:{BBoxTextCache:{}},config:{highPrecision:false},getElementConfig:function(){return{reference:"element",style:{position:"absolute"},children:[{reference:"innerElement",style:{width:"100%",height:"100%",position:"relative"},children:[{tag:"svg",reference:"svgElement",namespace:"http://www.w3.org/2000/svg",width:"100%",height:"100%",version:1.1}]}]}},constructor:function(a){var b=this;b.callParent([a]);b.mainGroup=b.createSvgNode("g");b.defElement=b.createSvgNode("defs");b.svgElement.appendChild(b.mainGroup);b.svgElement.appendChild(b.defElement);b.ctx=new Ext.draw.engine.SvgContext(b)},createSvgNode:function(a){var b=document.createElementNS("http://www.w3.org/2000/svg",a);return Ext.get(b)},getSvgElement:function(d,b,a){var c;if(d.dom.childNodes.length>a){c=d.dom.childNodes[a];if(c.tagName===b){return Ext.get(c)}else{Ext.destroy(c)}}c=Ext.get(this.createSvgNode(b));if(a===0){d.insertFirst(c)}else{c.insertAfter(Ext.fly(d.dom.childNodes[a-1]))}c.cache={};return c},setElementAttributes:function(d,b){var f=d.dom,a=d.cache,c,e;for(c in b){e=b[c];if(a[c]!==e){a[c]=e;f.setAttribute(c,e)}}},getNextDef:function(a){return this.getSvgElement(this.defElement,a,this.defPosition++)},clearTransform:function(){var a=this;a.mainGroup.set({transform:a.matrix.toSvg()})},clear:function(){this.ctx.clear();this.defPosition=0},renderSprite:function(b){var d=this,c=d.getRect(),a=d.ctx;if(b.attr.hidden||b.attr.globalAlpha===0){a.save();a.restore();return}b.element=a.save();b.preRender(this);b.useAttributes(a,c);if(false===b.render(this,a,[0,0,c[2],c[3]])){return false}b.setDirty(false);a.restore()},flatten:function(e,b){var c='',f=Ext.getClassName(this),a,g,d;c+='';for(d=0;d';c+=this.serializeNode(a.svgElement.dom);c+=""}c+="";return{data:"data:image/svg+xml;utf8,"+encodeURIComponent(c),type:"svg"}},serializeNode:function(d){var b="",c,f,a,e;if(d.nodeType===document.TEXT_NODE){return d.nodeValue}b+="<"+d.nodeName;if(d.attributes.length){for(c=0,f=d.attributes.length;c";return b},destroy:function(){var a=this;a.ctx.destroy();a.mainGroup.destroy();delete a.mainGroup;delete a.ctx;a.callParent()},remove:function(a,b){if(a&&a.element){if(this.ctx){this.ctx.removeElement(a.element)}else{a.element.destroy()}a.element=null}this.callParent(arguments)}});Ext.draw||(Ext.draw={});Ext.draw.engine||(Ext.draw.engine={});Ext.draw.engine.excanvas=true;if(!document.createElement("canvas").getContext){(function(){var ab=Math;var n=ab.round;var l=ab.sin;var A=ab.cos;var H=ab.abs;var N=ab.sqrt;var d=10;var f=d/2;var z=+navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];function y(){return this.context_||(this.context_=new D(this))}var t=Array.prototype.slice;function g(j,m,p){var i=t.call(arguments,2);return function(){return j.apply(m,i.concat(t.call(arguments)))}}function af(i){return String(i).replace(/&/g,"&").replace(/"/g,""")}function Y(m,j,i){Ext.onReady(function(){if(!m.namespaces[j]){m.namespaces.add(j,i,"#default#VML")}})}function R(j){Y(j,"g_vml_","urn:schemas-microsoft-com:vml");Y(j,"g_o_","urn:schemas-microsoft-com:office:office");if(!j.styleSheets.ex_canvas_){var i=j.createStyleSheet();i.owningElement.id="ex_canvas_";i.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}R(document);var e={init:function(i){var j=i||document;j.createElement("canvas");j.attachEvent("onreadystatechange",g(this.init_,this,j))},init_:function(p){var m=p.getElementsByTagName("canvas");for(var j=0;j1){m--}if(6*m<1){return j+(i-j)*6*m}else{if(2*m<1){return i}else{if(3*m<2){return j+(i-j)*(2/3-m)*6}else{return j}}}}var C={};function F(j){if(j in C){return C[j]}var ag,Z=1;j=String(j);if(j.charAt(0)=="#"){ag=j}else{if(/^rgb/.test(j)){var p=M(j);var ag="#",ah;for(var m=0;m<3;m++){if(p[m].indexOf("%")!=-1){ah=Math.floor(c(p[m])*255)}else{ah=+p[m]}ag+=k[r(ah,0,255)]}Z=+p[3]}else{if(/^hsl/.test(j)){var p=M(j);ag=I(p);Z=p[3]}else{ag=b[j]||j}}}return C[j]={color:ag,alpha:Z}}var o={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var L={};function E(i){if(L[i]){return L[i]}var p=document.createElement("div");var m=p.style;try{m.font=i}catch(j){}return L[i]={style:m.fontStyle||o.style,variant:m.fontVariant||o.variant,weight:m.fontWeight||o.weight,size:m.fontSize||o.size,family:m.fontFamily||o.family}}function u(m,j){var i={};for(var ah in m){i[ah]=m[ah]}var ag=parseFloat(j.currentStyle.fontSize),Z=parseFloat(m.size);if(typeof m.size=="number"){i.size=m.size}else{if(m.size.indexOf("px")!=-1){i.size=Z}else{if(m.size.indexOf("em")!=-1){i.size=ag*Z}else{if(m.size.indexOf("%")!=-1){i.size=(ag/100)*Z}else{if(m.size.indexOf("pt")!=-1){i.size=Z/0.75}else{i.size=ag}}}}}i.size*=0.981;return i}function ac(i){return i.style+" "+i.variant+" "+i.weight+" "+i.size+"px "+i.family}var s={butt:"flat",round:"round"};function S(i){return s[i]||"square"}function D(i){this.m_=B();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineDash=[];this.lineCap="butt";this.miterLimit=d*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var m="width:"+i.clientWidth+"px;height:"+i.clientHeight+"px;overflow:hidden;position:absolute";var j=i.ownerDocument.createElement("div");j.style.cssText=m;i.appendChild(j);var p=j.cloneNode(false);p.style.backgroundColor="red";p.style.filter="alpha(opacity=0)";i.appendChild(p);this.element_=j;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var q=D.prototype;q.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};q.beginPath=function(){this.currentPath_=[]};q.moveTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"moveTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.lineTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"lineTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.bezierCurveTo=function(m,j,ak,aj,ai,ag){var i=V(this,ai,ag);var ah=V(this,m,j);var Z=V(this,ak,aj);K(this,ah,Z,i)};function K(i,Z,m,j){i.currentPath_.push({type:"bezierCurveTo",cp1x:Z.x,cp1y:Z.y,cp2x:m.x,cp2y:m.y,x:j.x,y:j.y});i.currentX_=j.x;i.currentY_=j.y}q.quadraticCurveTo=function(ai,m,j,i){var ah=V(this,ai,m);var ag=V(this,j,i);var aj={x:this.currentX_+2/3*(ah.x-this.currentX_),y:this.currentY_+2/3*(ah.y-this.currentY_)};var Z={x:aj.x+(ag.x-this.currentX_)/3,y:aj.y+(ag.y-this.currentY_)/3};K(this,aj,Z,ag)};q.arc=function(al,aj,ak,ag,j,m){ak*=d;var ap=m?"at":"wa";var am=al+A(ag)*ak-f;var ao=aj+l(ag)*ak-f;var i=al+A(j)*ak-f;var an=aj+l(j)*ak-f;if(am==i&&!m){am+=0.125}var Z=V(this,al,aj);var ai=V(this,am,ao);var ah=V(this,i,an);this.currentPath_.push({type:ap,x:Z.x,y:Z.y,radius:ak,xStart:ai.x,yStart:ai.y,xEnd:ah.x,yEnd:ah.y})};q.rect=function(m,j,i,p){this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath()};q.strokeRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.stroke();this.currentPath_=Z};q.fillRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.fill();this.currentPath_=Z};q.createLinearGradient=function(j,p,i,m){var Z=new U("gradient");Z.x0_=j;Z.y0_=p;Z.x1_=i;Z.y1_=m;return Z};q.createRadialGradient=function(p,ag,m,j,Z,i){var ah=new U("gradientradial");ah.x0_=p;ah.y0_=ag;ah.r0_=m;ah.x1_=j;ah.y1_=Z;ah.r1_=i;return ah};q.drawImage=function(an,j){var ah,Z,aj,ar,al,ak,ao,av;var ai=an.runtimeStyle.width;var am=an.runtimeStyle.height;an.runtimeStyle.width="auto";an.runtimeStyle.height="auto";var ag=an.width;var aq=an.height;an.runtimeStyle.width=ai;an.runtimeStyle.height=am;if(arguments.length==3){ah=arguments[1];Z=arguments[2];al=ak=0;ao=aj=ag;av=ar=aq}else{if(arguments.length==5){ah=arguments[1];Z=arguments[2];aj=arguments[3];ar=arguments[4];al=ak=0;ao=ag;av=aq}else{if(arguments.length==9){al=arguments[1];ak=arguments[2];ao=arguments[3];av=arguments[4];ah=arguments[5];Z=arguments[6];aj=arguments[7];ar=arguments[8]}else{throw Error("Invalid number of arguments")}}}var au=V(this,ah,Z);var at=[];var i=10;var p=10;var ap=this.m_;at.push(" ','","");this.element_.insertAdjacentHTML("BeforeEnd",at.join(""))};q.setLineDash=function(i){if(i.length===1){i=i.slice();i[1]=i[0]}this.lineDash=i};q.getLineDash=function(){return this.lineDash};q.stroke=function(ak){var ai=[];var m=10;var al=10;ai.push("aj.x){aj.x=j.x}if(Z.y==null||j.yaj.y){aj.y=j.y}}}ai.push(' ">');if(!ak){w(this,ai)}else{G(this,ai,Z,aj)}ai.push("");this.element_.insertAdjacentHTML("beforeEnd",ai.join(""))};function w(m,ag){var j=F(m.strokeStyle);var p=j.color;var Z=j.alpha*m.globalAlpha;var i=m.lineScale_*m.lineWidth;if(i<1){Z*=i}ag.push("')}function G(aq,ai,aK,ar){var aj=aq.fillStyle;var aB=aq.arcScaleX_;var aA=aq.arcScaleY_;var j=ar.x-aK.x;var p=ar.y-aK.y;if(aj instanceof U){var an=0;var aF={x:0,y:0};var ax=0;var am=1;if(aj.type_=="gradient"){var al=aj.x0_/aB;var m=aj.y0_/aA;var ak=aj.x1_/aB;var aM=aj.y1_/aA;var aJ=V(aq,al,m);var aI=V(aq,ak,aM);var ag=aI.x-aJ.x;var Z=aI.y-aJ.y;an=Math.atan2(ag,Z)*180/Math.PI;if(an<0){an+=360}if(an<0.000001){an=0}}else{var aJ=V(aq,aj.x0_,aj.y0_);aF={x:(aJ.x-aK.x)/j,y:(aJ.y-aK.y)/p};j/=aB*d;p/=aA*d;var aD=ab.max(j,p);ax=2*aj.r0_/aD;am=2*aj.r1_/aD-ax}var av=aj.colors_;av.sort(function(aN,i){return aN.offset-i.offset});var ap=av.length;var au=av[0].color;var at=av[ap-1].color;var az=av[0].alpha*aq.globalAlpha;var ay=av[ap-1].alpha*aq.globalAlpha;var aE=[];for(var aH=0;aH')}else{if(aj instanceof T){if(j&&p){var ah=-aK.x;var aC=-aK.y;ai.push("')}}else{var aL=F(aq.fillStyle);var aw=aL.color;var aG=aL.alpha*aq.globalAlpha;ai.push('')}}}q.fill=function(){this.$stroke(true)};q.closePath=function(){this.currentPath_.push({type:"close"})};function V(j,Z,p){var i=j.m_;return{x:d*(Z*i[0][0]+p*i[1][0]+i[2][0])-f,y:d*(Z*i[0][1]+p*i[1][1]+i[2][1])-f}}q.save=function(){var i={};v(this,i);this.aStack_.push(i);this.mStack_.push(this.m_);this.m_=J(B(),this.m_)};q.restore=function(){if(this.aStack_.length){v(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function h(i){return isFinite(i[0][0])&&isFinite(i[0][1])&&isFinite(i[1][0])&&isFinite(i[1][1])&&isFinite(i[2][0])&&isFinite(i[2][1])}function aa(j,i,p){if(!h(i)){return}j.m_=i;if(p){var Z=i[0][0]*i[1][1]-i[0][1]*i[1][0];j.lineScale_=N(H(Z))}}q.translate=function(m,j){var i=[[1,0,0],[0,1,0],[m,j,1]];aa(this,J(i,this.m_),false)};q.rotate=function(j){var p=A(j);var m=l(j);var i=[[p,m,0],[-m,p,0],[0,0,1]];aa(this,J(i,this.m_),false)};q.scale=function(m,j){this.arcScaleX_*=m;this.arcScaleY_*=j;var i=[[m,0,0],[0,j,0],[0,0,1]];aa(this,J(i,this.m_),true)};q.transform=function(Z,p,ah,ag,j,i){var m=[[Z,p,0],[ah,ag,0],[j,i,1]];aa(this,J(m,this.m_),true)};q.setTransform=function(ag,Z,ai,ah,p,j){var i=[[ag,Z,0],[ai,ah,0],[p,j,1]];aa(this,i,true)};q.drawText_=function(am,ak,aj,ap,ai){var ao=this.m_,at=1000,j=0,ar=at,ah={x:0,y:0},ag=[];var i=u(E(this.font),this.element_);var p=ac(i);var au=this.element_.currentStyle;var Z=this.textAlign.toLowerCase();switch(Z){case"left":case"center":case"right":break;case"end":Z=au.direction=="ltr"?"right":"left";break;case"start":Z=au.direction=="rtl"?"right":"left";break;default:Z="left"}switch(this.textBaseline){case"hanging":case"top":ah.y=i.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":ah.y=-i.size/3;break}switch(Z){case"right":j=at;ar=0.05;break;case"center":j=ar=at/2;break}var aq=V(this,ak+ah.x,aj+ah.y);ag.push('');if(ai){w(this,ag)}else{G(this,ag,{x:-j,y:0},{x:ar,y:i.size})}var an=ao[0][0].toFixed(3)+","+ao[1][0].toFixed(3)+","+ao[0][1].toFixed(3)+","+ao[1][1].toFixed(3)+",0,0";var al=n(aq.x/d)+","+n(aq.y/d);ag.push('','','');this.element_.insertAdjacentHTML("beforeEnd",ag.join(""))};q.fillText=function(m,i,p,j){this.drawText_(m,i,p,j,false)};q.strokeText=function(m,i,p,j){this.drawText_(m,i,p,j,true)};q.measureText=function(m){if(!this.textMeasureEl_){var i='';this.element_.insertAdjacentHTML("beforeEnd",i);this.textMeasureEl_=this.element_.lastChild}var j=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(j.createTextNode(m));return{width:this.textMeasureEl_.offsetWidth}};q.clip=function(){};q.arcTo=function(){};q.createPattern=function(j,i){return new T(j,i)};function U(i){this.type_=i;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}U.prototype.addColorStop=function(j,i){i=F(i);this.colors_.push({offset:j,color:i.color,alpha:i.alpha})};function T(j,i){Q(j);switch(i){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=i;break;default:O("SYNTAX_ERR")}this.src_=j.src;this.width_=j.width;this.height_=j.height}function O(i){throw new P(i)}function Q(i){if(!i||i.nodeType!=1||i.tagName!="IMG"){O("TYPE_MISMATCH_ERR")}if(i.readyState!="complete"){O("INVALID_STATE_ERR")}}function P(i){this.code=this[i];this.message=i+": DOM Exception "+this.code}var X=P.prototype=new Error();X.INDEX_SIZE_ERR=1;X.DOMSTRING_SIZE_ERR=2;X.HIERARCHY_REQUEST_ERR=3;X.WRONG_DOCUMENT_ERR=4;X.INVALID_CHARACTER_ERR=5;X.NO_DATA_ALLOWED_ERR=6;X.NO_MODIFICATION_ALLOWED_ERR=7;X.NOT_FOUND_ERR=8;X.NOT_SUPPORTED_ERR=9;X.INUSE_ATTRIBUTE_ERR=10;X.INVALID_STATE_ERR=11;X.SYNTAX_ERR=12;X.INVALID_MODIFICATION_ERR=13;X.NAMESPACE_ERR=14;X.INVALID_ACCESS_ERR=15;X.VALIDATION_ERR=16;X.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=e;CanvasRenderingContext2D=D;CanvasGradient=U;CanvasPattern=T;DOMException=P})()}Ext.define("Ext.draw.engine.Canvas",{extend:"Ext.draw.Surface",requires:["Ext.draw.engine.excanvas","Ext.draw.Animator","Ext.draw.Color"],config:{highPrecision:false},statics:{contextOverrides:{setGradientBBox:function(a){this.bbox=a},fill:function(){var c=this.fillStyle,a=this.fillGradient,b=this.fillOpacity,d=this.globalAlpha,e=this.bbox;if(c!==Ext.draw.Color.RGBA_NONE&&b!==0){if(a&&e){this.fillStyle=a.generateGradient(this,e)}if(b!==1){this.globalAlpha=d*b}this.$fill();if(b!==1){this.globalAlpha=d}if(a&&e){this.fillStyle=c}}},stroke:function(){var e=this.strokeStyle,c=this.strokeGradient,a=this.strokeOpacity,b=this.globalAlpha,d=this.bbox;if(e!==Ext.draw.Color.RGBA_NONE&&a!==0){if(c&&d){this.strokeStyle=c.generateGradient(this,d)}if(a!==1){this.globalAlpha=b*a}this.$stroke();if(a!==1){this.globalAlpha=b}if(c&&d){this.strokeStyle=e}}},fillStroke:function(d,e){var j=this,i=this.fillStyle,h=this.fillOpacity,f=this.strokeStyle,c=this.strokeOpacity,b=j.shadowColor,a=j.shadowBlur,g=Ext.draw.Color.RGBA_NONE;if(e===undefined){e=d.transformFillStroke}if(!e){d.inverseMatrix.toContext(j)}if(i!==g&&h!==0){j.fill();j.shadowColor=g;j.shadowBlur=0}if(f!==g&&c!==0){j.stroke()}j.shadowColor=b;j.shadowBlur=a},setLineDash:function(a){if(this.$setLineDash){this.$setLineDash(a)}},getLineDash:function(){if(this.$getLineDash){return this.$getLineDash()}},ellipse:function(g,e,c,a,j,b,f,d){var i=Math.cos(j),h=Math.sin(j);this.transform(i*c,h*c,-h*a,i*a,g,e);this.arc(0,0,1,b,f,d);this.transform(i/c,-h/a,h/c,i/a,-(i*g+h*e)/c,(h*g-i*e)/a)},appendPath:function(f){var e=this,c=0,b=0,a=f.commands,g=f.params,d=a.length;e.beginPath();for(;c=D.length){C.createCanvas()}x=D[q].dom;x.style.left=A+"px";x.style.top=z+"px";m=Math.min(n,y-z);if(m*u!==x.height){x.height=m*u;x.style.height=m+"px"}o=Math.min(n,d-A);if(o*u!==x.width){x.width=o*u;x.style.width=o+"px"}C.applyDefaults(C.contexts[q])}}for(q+=1;qB||a.x+a.widthf||a.y+a.height=0},filename:Ext.isString,width:Ext.isNumber,height:Ext.isNumber,scale:Ext.isNumber,pdf:Ext.isObject,jpeg:Ext.isObject},initAnimator:function(){this.frameCallbackId=Ext.draw.Animator.addFrameCallback("renderFrame",this)},applyGradients:function(b){var a=[],c,f,d,e;if(!Ext.isArray(b)){return a}for(c=0,f=b.length;c=0&&c[a[f].type]>h){a[f+1]=a[f];f--}a[f+1]=b}d=a[0].flatten(l,a);if(k==="image"){g=new Image();g.src=d.data;d.data=g;return d}if(k==="stream"){d.data=d.data.replace(/^data:image\/[^;]+/,"data:application/octet-stream");return d}return d},download:function(d){var e=this,a=[],b,c,f;d=Ext.apply({version:2,data:e.getImage().data},d);for(c in d){if(d.hasOwnProperty(c)){f=d[c];if(c in e.supportedOptions){if(e.supportedOptions[c].call(e,f)){a.push({tag:"input",type:"hidden",name:c,value:Ext.String.htmlEncode(Ext.isObject(f)?Ext.JSON.encode(f):f)})}}}}b=Ext.dom.Helper.markup({tag:"html",children:[{tag:"head"},{tag:"body",children:[{tag:"form",method:"POST",action:d.url||e.defaultDownloadServerUrl,children:a},{tag:"script",type:"text/javascript",children:'document.getElementsByTagName("form")[0].submit();'}]}]});window.open("","ImageDownload_"+Date.now()).document.write(b)},destroy:function(){var a=this.frameCallbackId;if(a){Ext.draw.Animator.removeFrameCallback(a)}this.callParent()}},function(){if(location.search.match("svg")){Ext.draw.Container.prototype.engine="Ext.draw.engine.Svg"}else{if((Ext.os.is.BlackBerry&&Ext.os.version.getMajor()===10)||(Ext.browser.is.AndroidStock4&&(Ext.os.version.getMinor()===1||Ext.os.version.getMinor()===2||Ext.os.version.getMinor()===3))){Ext.draw.Container.prototype.engine="Ext.draw.engine.Svg"}}});Ext.define("Ext.chart.theme.Base",{mixins:{factoryable:"Ext.mixin.Factoryable"},requires:["Ext.draw.Color"],factoryConfig:{type:"chart.theme"},isTheme:true,config:{baseColor:null,colors:undefined,gradients:null,chart:{defaults:{background:"white"}},axis:{defaults:{label:{x:0,y:0,textBaseline:"middle",textAlign:"center",fontSize:"default",fontFamily:"default",fontWeight:"default",fillStyle:"black"},title:{fillStyle:"black",fontSize:"default*1.23",fontFamily:"default",fontWeight:"default"},style:{strokeStyle:"black"},grid:{strokeStyle:"rgb(221, 221, 221)"}},top:{style:{textPadding:5}},bottom:{style:{textPadding:5}}},series:{defaults:{label:{fillStyle:"black",strokeStyle:"none",fontFamily:"default",fontWeight:"default",fontSize:"default*1.077",textBaseline:"middle",textAlign:"center"},labelOverflowPadding:5}},sprites:{text:{fontSize:"default",fontWeight:"default",fontFamily:"default",fillStyle:"black"}},seriesThemes:undefined,markerThemes:{type:["circle","cross","plus","square","triangle","diamond"]},useGradients:false,background:null},colorDefaults:["#94ae0a","#115fa6","#a61120","#ff8809","#ffd13e","#a61187","#24ad9a","#7c7474","#a66111"],constructor:function(a){this.initConfig(a);this.resolveDefaults()},defaultRegEx:/^default([+\-/\*]\d+(?:\.\d+)?)?$/,defaultOperators:{"*":function(b,a){return b*a},"+":function(b,a){return b+a},"-":function(b,a){return b-a}},resolveDefaults:function(){var a=this;Ext.onReady(function(){var f=Ext.clone(a.getSprites()),e=Ext.clone(a.getAxis()),d=Ext.clone(a.getSeries()),g,c,b;if(!a.superclass.defaults){g=Ext.getBody().createChild({tag:"div",cls:"x-component"});a.superclass.defaults={fontFamily:g.getStyle("fontFamily"),fontWeight:g.getStyle("fontWeight"),fontSize:parseFloat(g.getStyle("fontSize")),fontVariant:g.getStyle("fontVariant"),fontStyle:g.getStyle("fontStyle")};g.destroy()}a.replaceDefaults(f.text);a.setSprites(f);for(c in e){b=e[c];a.replaceDefaults(b.label);a.replaceDefaults(b.title)}a.setAxis(e);for(c in d){b=d[c];a.replaceDefaults(b.label)}a.setSeries(d)})},replaceDefaults:function(h){var e=this,g=e.superclass.defaults,a=e.defaultRegEx,d,f,c,b;if(Ext.isObject(h)){for(d in g){c=a.exec(h[d]);if(c){f=g[d];c=c[1];if(c){b=e.defaultOperators[c.charAt(0)];f=Math.round(b(f,parseFloat(c.substr(1))))}h[d]=f}}}},applyBaseColor:function(c){var a,b;if(c){a=c.isColor?c:Ext.draw.Color.fromString(c);b=a.getHSL()[2];if(b<0.15){a=a.createLighter(0.3)}else{if(b<0.3){a=a.createLighter(0.15)}else{if(b>0.85){a=a.createDarker(0.3)}else{if(b>0.7){a=a.createDarker(0.15)}}}}this.setColors([a.createDarker(0.3).toString(),a.createDarker(0.15).toString(),a.toString(),a.createLighter(0.12).toString(),a.createLighter(0.24).toString(),a.createLighter(0.31).toString()])}return c},applyColors:function(a){return a||this.colorDefaults},updateUseGradients:function(a){if(a){this.updateGradients({type:"linear",degrees:90})}},updateBackground:function(a){if(a){var b=this.getChart();b.defaults.background=a;this.setChart(b)}},updateGradients:function(a){var c=this.getColors(),e=[],h,b,d,f,g;if(Ext.isObject(a)){for(f=0,g=c&&c.length||0;fMath.PI){n-=Math.PI*2}if(k){n=n*(1-d)-Math.PI/2*d;j=c;c=m;m=j}else{n=n*(1-d)}h.rotationRads=n;h.x=g*(1-d)+b*d;h.y=f*(1-d)+a*d;p=b-g;o=a-f;if(Math.abs(o*c)>Math.abs(p*m)){if(o>0){h.calloutEndX=h.x-(m/2)*(p/o)*d;h.calloutEndY=h.y-(m/2)*d}else{h.calloutEndX=h.x+(m/2)*(p/o)*d;h.calloutEndY=h.y+(m/2)*d}}else{if(p>0){h.calloutEndX=h.x-c/2;h.calloutEndY=h.y-(c/2)*(o/p)*d}else{h.calloutEndX=h.x+c/2;h.calloutEndY=h.y+(c/2)*(o/p)*d}}if(h.calloutStartX&&h.calloutStartY){h.calloutHasLine=(p>0&&h.calloutStartXh.calloutEndX)||(o>0&&h.calloutStartYh.calloutEndY)}else{h.calloutHasLine=true}}return h},pushDown:function(a,b){b=this.callParent([a.calloutOriginal,b]);return this.setAttrs(a,b)},popUp:function(a,b){a=a.prototype;b=this.setAttrs(a,b);if(this._next){return this._next.popUp(a,b)}else{return Ext.apply(a,b)}}});Ext.define("Ext.chart.label.Label",{extend:"Ext.draw.sprite.Text",requires:["Ext.chart.label.Callout"],inheritableStatics:{def:{processors:{callout:"limited01",calloutHasLine:"bool",calloutPlaceX:"number",calloutPlaceY:"number",calloutStartX:"number",calloutStartY:"number",calloutEndX:"number",calloutEndY:"number",calloutColor:"color",calloutWidth:"number",calloutVertical:"bool",labelOverflowPadding:"number",display:"enums(none,under,over,rotate,insideStart,insideEnd,inside,outside)",orientation:"enums(horizontal,vertical)",renderer:"default"},defaults:{callout:0,calloutHasLine:true,calloutPlaceX:0,calloutPlaceY:0,calloutStartX:0,calloutStartY:0,calloutEndX:0,calloutEndY:0,calloutWidth:1,calloutVertical:false,calloutColor:"black",labelOverflowPadding:5,display:"none",orientation:"",renderer:null},triggers:{callout:"transform",calloutPlaceX:"transform",calloutPlaceY:"transform",labelOverflowPadding:"transform",calloutRotation:"transform",display:"hidden"},updaters:{hidden:function(a){a.hidden=a.display==="none"}}}},config:{fx:{customDurations:{callout:200}},field:null,calloutLine:true},applyCalloutLine:function(a){if(a){return Ext.apply({},a)}},prepareModifiers:function(){this.callParent(arguments);this.calloutModifier=new Ext.chart.label.Callout({sprite:this});this.fx.setNext(this.calloutModifier);this.calloutModifier.setNext(this.topModifier)},render:function(b,c){var e=this,a=e.attr,d=a.calloutColor;c.save();c.globalAlpha*=a.callout;if(c.globalAlpha>0&&a.calloutHasLine){if(d&&d.isGradient){d=d.getStops()[0].color}c.strokeStyle=d;c.fillStyle=d;c.lineWidth=a.calloutWidth;c.beginPath();c.moveTo(e.attr.calloutStartX,e.attr.calloutStartY);c.lineTo(e.attr.calloutEndX,e.attr.calloutEndY);c.stroke();c.beginPath();c.arc(e.attr.calloutStartX,e.attr.calloutStartY,1*a.calloutWidth,0,2*Math.PI,true);c.fill();c.beginPath();c.arc(e.attr.calloutEndX,e.attr.calloutEndY,1*a.calloutWidth,0,2*Math.PI,true);c.fill()}c.restore();Ext.draw.sprite.Text.prototype.render.apply(e,arguments)}});Ext.define("Ext.chart.series.Series",{requires:["Ext.chart.Markers","Ext.chart.label.Label","Ext.tip.ToolTip"],mixins:["Ext.mixin.Observable","Ext.mixin.Bindable"],isSeries:true,defaultBindProperty:"store",type:null,seriesType:"sprite",identifiablePrefix:"ext-line-",observableType:"series",darkerStrokeRatio:0.15,config:{chart:null,title:null,renderer:null,showInLegend:true,triggerAfterDraw:false,style:{},subStyle:{},themeStyle:{},colors:null,useDarkerStrokeColor:true,store:null,label:{},labelOverflowPadding:null,showMarkers:true,marker:null,markerSubStyle:null,itemInstancing:null,background:null,highlightItem:null,surface:null,overlaySurface:null,hidden:false,highlight:false,highlightCfg:{merge:function(a){return a},$value:{fillStyle:"yellow",strokeStyle:"red"}},animation:null,tooltip:null},directions:[],sprites:null,themeColorCount:function(){return 1},isStoreDependantColorCount:false,themeMarkerCount:function(){return 0},getFields:function(f){var e=this,a=[],c,b,d;for(b=0,d=f.length;b0){if(!Ext.isBoolean(h)||!h){for(d=0;da){a=f}}b.min=d;b.max=a},updateLabelData:function(){var h=this,l=h.getStore(),g=l.getData().items,f=h.getSprites(),a=h.getLabel().getTemplate(),n=Ext.Array.from(a.getField()),c,b,e,d,m,k;if(!f.length||!n.length){return}for(c=0;c=0){return d}}}}}},onChartDetached:function(a){var b=this;b.fireEvent("chartdetached",a,b);a.un("storechange","onStoreChange",b)},onChartAttached:function(a){var b=this;b.setBackground(b.getBackground());b.fireEvent("chartattached",a,b);a.on("storechange","onStoreChange",b);b.processData()},updateOverlaySurface:function(a){var b=this;if(a){if(b.getLabel()){b.getOverlaySurface().add(b.getLabel())}}},applyLabel:function(a,b){if(!b){b=new Ext.chart.Markers({zIndex:10});b.setTemplate(new Ext.chart.label.Label(a))}else{b.getTemplate().setAttributes(a)}return b},createItemInstancingSprite:function(c,b){var e=this,f=new Ext.chart.Markers(),a,d;f.setAttributes({zIndex:Number.MAX_VALUE});a=Ext.apply({},b);if(e.getHighlight()){a.highlight=e.getHighlight();a.modifiers=["highlight"]}f.setTemplate(a);d=f.getTemplate();d.setAttributes(e.getStyle());d.fx.on("animationstart","onSpriteAnimationStart",this);d.fx.on("animationend","onSpriteAnimationEnd",this);c.bindMarker("items",f);e.getSurface().add(f);return f},getDefaultSpriteConfig:function(){return{type:this.seriesType,renderer:this.getRenderer()}},updateRenderer:function(c){var b=this,a=b.getChart(),d;if(a&&a.isInitializing){return}d=b.getSprites();if(d.length){d[0].setAttributes({renderer:c||null});if(a&&!a.isInitializing){a.redraw()}}},updateShowMarkers:function(a){var d=this.getSprites(),b=d&&d[0],c=b&&b.getMarker("markers");if(c){c.getTemplate().setAttributes({hidden:!a})}},createSprite:function(){var f=this,a=f.getSurface(),e=f.getItemInstancing(),d=a.add(f.getDefaultSpriteConfig()),b=f.getMarker(),g,c;d.setAttributes(f.getStyle());d.setSeries(f);if(e){d.itemsMarker=f.createItemInstancingSprite(d,e)}if(d.bindMarker){if(b){g=new Ext.chart.Markers();c=Ext.Object.merge({},b);if(f.getHighlight()){c.highlight=f.getHighlight();c.modifiers=["highlight"]}g.setTemplate(c);g.getTemplate().fx.setCustomDurations({translationX:0,translationY:0});d.dataMarker=g;d.bindMarker("markers",g);f.getOverlaySurface().add(g)}if(f.getLabel().getTemplate().getField()){d.bindMarker("labels",f.getLabel())}}if(d.setStore){d.setStore(f.getStore())}d.fx.on("animationstart","onSpriteAnimationStart",f);d.fx.on("animationend","onSpriteAnimationEnd",f);f.sprites.push(d);return d},getSprites:Ext.emptyFn,onDataChanged:function(){var d=this,c=d.getChart(),b=c&&c.getStore(),a=d.getStore();if(a!==b){d.processData()}},isXType:function(a){return a==="series"},getItemId:function(){return this.getId()},applyThemeStyle:function(e,a){var b=this,d,c;d=e&&e.subStyle&&e.subStyle.fillStyle;c=d&&e.subStyle.strokeStyle;if(d&&!c){e.subStyle.strokeStyle=b.getStrokeColorsFromFillColors(d)}d=e&&e.markerSubStyle&&e.markerSubStyle.fillStyle;c=d&&e.markerSubStyle.strokeStyle;if(d&&!c){e.markerSubStyle.strokeStyle=b.getStrokeColorsFromFillColors(d)}return Ext.apply(a||{},e)},applyStyle:function(c,b){var a=Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite."+this.seriesType));if(a&&a.def){c=a.def.normalize(c)}return Ext.apply({},c,b)},applySubStyle:function(b,c){var a=Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite."+this.seriesType));if(a&&a.def){b=a.def.batchedNormalize(b,true)}return Ext.merge({},c,b)},applyMarker:function(c,a){var d=(c&&c.type)||(a&&a.type)||"circle",b=Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite."+d));if(b&&b.def){c=b.def.normalize(Ext.isObject(c)?c:{},true);c.type=d}return Ext.merge(a||{},c)},applyMarkerSubStyle:function(c,a){var d=(c&&c.type)||(a&&a.type)||"circle",b=Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite."+d));if(b&&b.def){c=b.def.batchedNormalize(c,true)}return Ext.merge(a||{},c)},updateHidden:function(b){var a=this;a.getColors();a.getSubStyle();a.setSubStyle({hidden:b});a.processData();a.doUpdateStyles();if(!Ext.isArray(b)){a.updateLegendStore(b)}},updateLegendStore:function(f,b){var e=this,d=e.getChart(),c=d.getLegendStore(),g=e.getId(),a;if(c){if(arguments.length>1){a=c.findBy(function(h){return h.get("series")===g&&h.get("index")===b});if(a!==-1){a=c.getAt(a)}}else{a=c.findRecord("series",g)}if(a&&a.get("disabled")!==f){a.set("disabled",f)}}},setHiddenByIndex:function(a,c){var b=this;if(Ext.isArray(b.getHidden())){b.getHidden()[a]=c;b.updateHidden(b.getHidden());b.updateLegendStore(c,a)}else{b.setHidden(c)}},getStrokeColorsFromFillColors:function(a){var c=this,e=c.getUseDarkerStrokeColor(),b=(Ext.isNumber(e)?e:c.darkerStrokeRatio),d;if(e){d=Ext.Array.map(a,function(f){f=Ext.isString(f)?f:f.stops[0].color;f=Ext.draw.Color.fromString(f);return f.createDarker(b).toString()})}else{d=Ext.Array.clone(a)}return d},updateThemeColors:function(b){var c=this,d=c.getThemeStyle(),a=Ext.Array.clone(b),f=c.getStrokeColorsFromFillColors(b),e={fillStyle:a,strokeStyle:f};d.subStyle=Ext.apply(d.subStyle||{},e);d.markerSubStyle=Ext.apply(d.markerSubStyle||{},e);c.doUpdateStyles()},themeOnlyIfConfigured:{},updateTheme:function(d){var h=this,a=d.getSeries(),n=h.getInitialConfig(),c=h.defaultConfig,f=h.getConfigurator().configs,j=a.defaults,k=a[h.type],g=h.themeOnlyIfConfigured,l,i,o,b,m,e;a=Ext.merge({},j,k);for(l in a){i=a[l];e=f[l];if(i!==null&&i!==undefined&&e){m=n[l];o=Ext.isObject(i);b=m===c[l];if(o){if(b&&g[l]){continue}i=Ext.merge({},i,m)}if(b||o){h[e.names.set](i)}}}},updateChartColors:function(a){var b=this;if(!b.getColors()){b.updateThemeColors(a)}},updateColors:function(a){this.updateThemeColors(a)},updateStyle:function(){this.doUpdateStyles()},updateSubStyle:function(){this.doUpdateStyles()},updateThemeStyle:function(){this.doUpdateStyles()},doUpdateStyles:function(){var g=this,h=g.sprites,d=g.getItemInstancing(),c=0,f=h&&h.length,a=g.getConfig("showMarkers",true),b=g.getMarker(),e;for(;ce.to){j.call(this,e.max,e.getLabel(e.max),e.steps+1,e)}}else{b=this.getAxis();h=b.floatingAxes;d=[];f=(e.to-e.from)/(e.steps+1);if(b.getFloating()){for(a in h){d.push(h[a])}}function l(i){return !d.length||k(d,function(n){return m(n-i)>f})}if(e.mine.to&&l(e.max)){j.call(this,e.max,e.max,e.steps+1,e)}}},renderTicks:function(l,m,s,p){var v=this,k=v.attr,u=k.position,n=k.matrix,e=0.5*k.lineWidth,f=n.getXX(),i=n.getDX(),j=n.getYY(),h=n.getDY(),o=s.majorTicks,d=k.majorTickSize,a=s.minorTicks,r=k.minorTickSize;if(o){switch(u){case"right":function q(w){return function(x,z,y){x=l.roundPixel(x*j+h)+e;m.moveTo(0,x);m.lineTo(w,x)}}v.iterate(o,q(d));a&&v.iterate(a,q(r));break;case"left":function t(w){return function(x,z,y){x=l.roundPixel(x*j+h)+e;m.moveTo(p[2]-w,x);m.lineTo(p[2],x)}}v.iterate(o,t(d));a&&v.iterate(a,t(r));break;case"bottom":function c(w){return function(x,z,y){x=l.roundPixel(x*f+i)-e;m.moveTo(x,0);m.lineTo(x,w)}}v.iterate(o,c(d));a&&v.iterate(a,c(r));break;case"top":function b(w){return function(x,z,y){x=l.roundPixel(x*f+i)-e;m.moveTo(x,p[3]);m.lineTo(x,p[3]-w)}}v.iterate(o,b(d));a&&v.iterate(a,b(r));break;case"angular":v.iterate(o,function(w,y,x){w=w/(k.max+1)*Math.PI*2+k.baseRotation;m.moveTo(k.centerX+(k.length)*Math.cos(w),k.centerY+(k.length)*Math.sin(w));m.lineTo(k.centerX+(k.length+d)*Math.cos(w),k.centerY+(k.length+d)*Math.sin(w))});break;case"gauge":var g=v.getGaugeAngles();v.iterate(o,function(w,y,x){w=(w-k.min)/(k.max-k.min+1)*k.totalAngle-k.totalAngle+g.start;m.moveTo(k.centerX+(k.length)*Math.cos(w),k.centerY+(k.length)*Math.sin(w));m.lineTo(k.centerX+(k.length+d)*Math.cos(w),k.centerY+(k.length+d)*Math.sin(w))});break}}},renderLabels:function(E,q,D,K){var o=this,k=o.attr,i=0.5*k.lineWidth,u=k.position,y=k.matrix,A=k.textPadding,x=y.getXX(),d=y.getDX(),g=y.getYY(),c=y.getDY(),n=0,I=D.majorTicks,G=Math.max(k.majorTickSize,k.minorTickSize)+k.lineWidth,f=Ext.draw.Draw.isBBoxIntersect,F=o.getLabel(),J,s,r=null,w=0,b=0,m=D.segmenter,B=o.getRenderer(),t=o.getAxis(),z=t.getTitle(),a=z&&z.attr.text!==""&&z.getBBox(),l,h=null,p,C,v,e,H;if(I&&F&&!F.attr.hidden){J=F.attr.font;if(q.font!==J){q.font=J}F.setAttributes({translationX:0,translationY:0},true);F.applyTransformations();l=F.attr.inverseMatrix.elements.slice(0);switch(u){case"left":e=a?a.x+a.width:0;switch(F.attr.textAlign){case"start":H=E.roundPixel(e+d)-i;break;case"end":H=E.roundPixel(K[2]-G+d)-i;break;default:H=E.roundPixel(e+(K[2]-e-G)/2+d)-i}F.setAttributes({translationX:H},true);break;case"right":e=a?K[2]-a.x:0;switch(F.attr.textAlign){case"start":H=E.roundPixel(G+d)+i;break;case"end":H=E.roundPixel(K[2]-e+d)+i;break;default:H=E.roundPixel(G+(K[2]-G-e)/2+d)+i}F.setAttributes({translationX:H},true);break;case"top":e=a?a.y+a.height:0;F.setAttributes({translationY:E.roundPixel(e+(K[3]-e-G)/2)-i},true);break;case"bottom":e=a?K[3]-a.y:0;F.setAttributes({translationY:E.roundPixel(G+(K[3]-G-e)/2)+i},true);break;case"radial":F.setAttributes({translationX:k.centerX},true);break;case"angular":F.setAttributes({translationY:k.centerY},true);break;case"gauge":F.setAttributes({translationY:k.centerY},true);break}if(u==="left"||u==="right"){o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;F.setAttributes({text:String(v),translationY:E.roundPixel(L*g+c)},true);F.applyTransformations();n=Math.max(n,F.getBBox().width+G);if(n<=o.thickness){C=Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0));p=C.prepend.apply(C,l).transformBBox(F.getBBox(true));if(h&&!f(p,h,A)){return}E.renderSprite(F);h=p;w+=p.height;b++}})}else{if(u==="top"||u==="bottom"){o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;F.setAttributes({text:String(v),translationX:E.roundPixel(L*x+d)},true);F.applyTransformations();n=Math.max(n,F.getBBox().height+G);if(n<=o.thickness){C=Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0));p=C.prepend.apply(C,l).transformBBox(F.getBBox(true));if(h&&!f(p,h,A)){return}E.renderSprite(F);h=p;w+=p.width;b++}})}else{if(u==="radial"){o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;if(typeof v!=="undefined"){F.setAttributes({text:String(v),translationX:k.centerX-E.roundPixel(L)/k.max*k.length*Math.cos(k.baseRotation+Math.PI/2),translationY:k.centerY-E.roundPixel(L)/k.max*k.length*Math.sin(k.baseRotation+Math.PI/2)},true);F.applyTransformations();p=F.attr.matrix.transformBBox(F.getBBox(true));if(h&&!f(p,h)){return}E.renderSprite(F);h=p;w+=p.width;b++}})}else{if(u==="angular"){s=k.majorTickSize+k.lineWidth*0.5+(parseInt(F.attr.fontSize,10)||10)/2;o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;n=Math.max(n,Math.max(k.majorTickSize,k.minorTickSize)+(k.lineCap!=="butt"?k.lineWidth*0.5:0));if(typeof v!=="undefined"){var O=L/(k.max+1)*Math.PI*2+k.baseRotation;F.setAttributes({text:String(v),translationX:k.centerX+(k.length+s)*Math.cos(O),translationY:k.centerY+(k.length+s)*Math.sin(O)},true);F.applyTransformations();p=F.attr.matrix.transformBBox(F.getBBox(true));if(h&&!f(p,h)){return}E.renderSprite(F);h=p;w+=p.width;b++}})}else{if(u==="gauge"){var j=o.getGaugeAngles();o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;if(typeof v!=="undefined"){var O=(L-k.min)/(k.max-k.min+1)*k.totalAngle-k.totalAngle+j.start;F.setAttributes({text:String(v),translationX:k.centerX+(k.length+10)*Math.cos(O),translationY:k.centerY+(k.length+10)*Math.sin(O)},true);F.applyTransformations();p=F.attr.matrix.transformBBox(F.getBBox(true));if(h&&!f(p,h)){return}E.renderSprite(F);h=p;w+=p.width;b++}})}}}}}if(k.enlargeEstStepSizeByText&&b){w/=b;w+=G;w*=2;if(k.estStepSize1){o.thickness=n;k.bbox.plain.dirty=true;k.bbox.transform.dirty=true;o.doThicknessChanged();return false}}},renderAxisLine:function(a,i,e,c){var h=this,g=h.attr,b=g.lineWidth*0.5,j=g.position,d,f;if(g.axisLine&&g.length){switch(j){case"left":d=a.roundPixel(c[2])-b;i.moveTo(d,-g.endGap);i.lineTo(d,g.length+g.startGap+1);break;case"right":i.moveTo(b,-g.endGap);i.lineTo(b,g.length+g.startGap+1);break;case"bottom":i.moveTo(-g.startGap,b);i.lineTo(g.length+g.endGap,b);break;case"top":d=a.roundPixel(c[3])-b;i.moveTo(-g.startGap,d);i.lineTo(g.length+g.endGap,d);break;case"angular":i.moveTo(g.centerX+g.length,g.centerY);i.arc(g.centerX,g.centerY,g.length,0,Math.PI*2,true);break;case"gauge":f=h.getGaugeAngles();i.moveTo(g.centerX+Math.cos(f.start)*g.length,g.centerY+Math.sin(f.start)*g.length);i.arc(g.centerX,g.centerY,g.length,f.start,f.end,true);break}}},getGaugeAngles:function(){var a=this,c=a.attr.totalAngle,b;if(c<=Math.PI){b=(Math.PI-c)*0.5}else{b=-(Math.PI*2-c)*0.5}b=Math.PI*2-b;return{start:b,end:b-c}},renderGridLines:function(m,n,s,r){var t=this,b=t.getAxis(),l=t.attr,p=l.matrix,d=l.startGap,a=l.endGap,c=p.getXX(),k=p.getYY(),h=p.getDX(),g=p.getDY(),u=l.position,f=b.getGridAlignment(),q=s.majorTicks,e,o,i;if(l.grid){if(q){if(u==="left"||u==="right"){i=l.min*k+g+a+d;t.iterate(q,function(j,w,v){e=j*k+g+a;t.putMarker(f+"-"+(v%2?"odd":"even"),{y:e,height:i-e},o=v,true);i=e});o++;e=0;t.putMarker(f+"-"+(o%2?"odd":"even"),{y:e,height:i-e},o,true)}else{if(u==="top"||u==="bottom"){i=l.min*c+h+d;if(d){t.putMarker(f+"-even",{x:0,width:i},-1,true)}t.iterate(q,function(j,w,v){e=j*c+h+d;t.putMarker(f+"-"+(v%2?"odd":"even"),{x:e,width:i-e},o=v,true);i=e});o++;e=l.length+l.startGap+l.endGap;t.putMarker(f+"-"+(o%2?"odd":"even"),{x:e,width:i-e},o,true)}else{if(u==="radial"){t.iterate(q,function(j,w,v){if(!j){return}e=j/l.max*l.length;t.putMarker(f+"-"+(v%2?"odd":"even"),{scalingX:e,scalingY:e},v,true);i=e})}else{if(u==="angular"){t.iterate(q,function(j,w,v){if(!l.length){return}e=j/(l.max+1)*Math.PI*2+l.baseRotation;t.putMarker(f+"-"+(v%2?"odd":"even"),{rotationRads:e,rotationCenterX:0,rotationCenterY:0,scalingX:l.length,scalingY:l.length},v,true);i=e})}}}}}}},renderLimits:function(o){var t=this,a=t.getAxis(),h=a.getChart(),p=h.getInnerPadding(),d=Ext.Array.from(a.getLimits());if(!d.length){return}var r=a.limits.surface.getRect(),m=t.attr,n=m.matrix,u=m.position,k=Ext.Object.chain,v=a.limits.titles,c,j,b,s,l,q,f,g,e;v.instances=[];v.position=0;if(u==="left"||u==="right"){for(q=0,f=d.length;qm.max){continue}l=l/m.max*m.length;s.line.cx=m.centerX;s.line.cy=m.centerY;s.line.scalingX=l;s.line.scalingY=l;s.line.strokeStyle=s.line.strokeStyle||m.strokeStyle;t.putMarker("circular-limit-lines",s.line,q,true);if(s.line.title){v.createInstance(s.line.title);c=v.getBBoxFor(v.position-1);v.setAttributesFor(v.position-1,{x:m.centerX,y:m.centerY-l-c.height/2,fillStyle:s.line.title.fillStyle||s.line.strokeStyle})}}}else{if(u==="angular"){for(q=0,f=d.length;q-0.5*Math.PI&&l<0.5*Math.PI)||(l>1.5*Math.PI&&l<2*Math.PI))?1:-1;v.setAttributesFor(v.position-1,{x:m.centerX+0.5*m.length*Math.cos(l)+b*c.height/2*Math.sin(l),y:m.centerY+0.5*m.length*Math.sin(l)-b*c.height/2*Math.cos(l),rotationRads:b===1?l:l-Math.PI,fillStyle:s.line.title.fillStyle||s.line.strokeStyle})}}}else{if(u==="gauge"){}}}}}},doThicknessChanged:function(){var a=this.getAxis();if(a){a.onThicknessChanged()}},render:function(a,c,d){var e=this,b=e.getLayoutContext();if(b){if(false===e.renderLabels(a,c,b,d)){return false}c.beginPath();e.renderTicks(a,c,b,d);e.renderAxisLine(a,c,b,d);e.renderGridLines(a,c,b,d);e.renderLimits(d);c.stroke()}}});Ext.define("Ext.chart.axis.segmenter.Segmenter",{config:{axis:null},constructor:function(a){this.initConfig(a)},renderer:function(b,a){return String(b)},from:function(a){return a},diff:Ext.emptyFn,align:Ext.emptyFn,add:Ext.emptyFn,preferredStep:Ext.emptyFn});Ext.define("Ext.chart.axis.segmenter.Names",{extend:"Ext.chart.axis.segmenter.Segmenter",alias:"segmenter.names",renderer:function(b,a){return b},diff:function(b,a,c){return Math.floor(a-b)},align:function(c,b,a){return Math.floor(c)},add:function(c,b,a){return c+b},preferredStep:function(c,a,b,d){return{unit:1,step:1}}});Ext.define("Ext.chart.axis.segmenter.Numeric",{extend:"Ext.chart.axis.segmenter.Segmenter",alias:"segmenter.numeric",isNumeric:true,renderer:function(b,a){return b.toFixed(Math.max(0,a.majorTicks.unit.fixes))},diff:function(b,a,c){return Math.floor((a-b)/c.scale)},align:function(c,b,a){return Math.floor(c/(a.scale*b))*a.scale*b},add:function(c,b,a){return c+b*a.scale},preferredStep:function(c,b){var a=Math.floor(Math.log(b)*Math.LOG10E),d=Math.pow(10,a);b/=d;if(b<2){b=2}else{if(b<5){b=5}else{if(b<10){b=10;a++}}}return{unit:{fixes:-a,scale:d},step:b}},exactStep:function(c,b){var a=Math.floor(Math.log(b)*Math.LOG10E),d=Math.pow(10,a);return{unit:{fixes:-a+(b%d===0?0:1),scale:1},step:b}},adjustByMajorUnit:function(e,g,c){var d=c[0],b=c[1],a=e*g,f=d%a;if(f!==0){c[0]=d-f+(d<0?-a:0)}f=b%a;if(f!==0){c[1]=b-f+(b>0?a:0)}}});Ext.define("Ext.chart.axis.segmenter.Time",{extend:"Ext.chart.axis.segmenter.Segmenter",alias:"segmenter.time",config:{step:null},renderer:function(c,b){var a=Ext.Date;switch(b.majorTicks.unit){case"y":return a.format(c,"Y");case"mo":return a.format(c,"Y-m");case"d":return a.format(c,"Y-m-d")}return a.format(c,"Y-m-d\nH:i:s")},from:function(a){return new Date(a)},diff:function(b,a,c){if(isFinite(b)){b=new Date(b)}if(isFinite(a)){a=new Date(a)}return Ext.Date.diff(b,a,c)},align:function(a,c,b){if(b==="d"&&c>=7){a=Ext.Date.align(a,"d",c);a.setDate(a.getDate()-a.getDay()+1);return a}else{return Ext.Date.align(a,b,c)}},add:function(c,b,a){return Ext.Date.add(new Date(c),a,b)},stepUnits:[[Ext.Date.YEAR,1,2,5,10,20,50,100,200,500],[Ext.Date.MONTH,1,3,6],[Ext.Date.DAY,1,7,14],[Ext.Date.HOUR,1,6,12],[Ext.Date.MINUTE,1,5,15,30],[Ext.Date.SECOND,1,5,15,30],[Ext.Date.MILLI,1,2,5,10,20,50,100,200,500]],preferredStep:function(b,e){if(this.getStep()){return this.getStep()}var f=new Date(+b),g=new Date(+b+Math.ceil(e)),d=this.stepUnits,l,k,h,c,a;for(c=0;c0){for(a=1;aa){f.max=f.to}if(f.froma){f.max=f.to}if(f.from0){f.from=f.from+c*f.step*i;while(f.from0?b:a},applyLabel:function(b,a){if(!a){a=new Ext.draw.sprite.Text({})}if(this.limitTitleTpl){this.limitTitleTpl.setAttributes(b)}a.setAttributes(b);return a},applyLayout:function(b,a){b=Ext.factory(b,null,a,"axisLayout");b.setAxis(this);return b},applySegmenter:function(a,b){a=Ext.factory(a,null,b,"segmenter");a.setAxis(this);return a},updateMinimum:function(){this.range=null},updateMaximum:function(){this.range=null},hideLabels:function(){this.getSprites()[0].setDirty(true);this.setLabel({hidden:true})},showLabels:function(){this.getSprites()[0].setDirty(true);this.setLabel({hidden:false})},renderFrame:function(){this.getSurface().renderFrame()},updateChart:function(d,b){var c=this,a;if(b){b.unregister(c);b.un("serieschange",c.onSeriesChange,c);b.un("redraw",c.renderLimits,c);c.linkAxis();c.fireEvent("chartdetached",b,c)}if(d){d.on("serieschange",c.onSeriesChange,c);c.surface=null;a=c.getSurface();c.getLabel().setSurface(a);a.add(c.getSprites());a.add(c.getTitle());d.register(c);c.fireEvent("chartattached",d,c)}},applyBackground:function(a){var b=Ext.ClassManager.getByAlias("sprite.rect");return b.def.normalize(a)},processData:function(){this.getLayout().processData();this.range=null},getDirection:function(){return this.getChart().getDirectionForAxis(this.getPosition())},isSide:function(){var a=this.getPosition();return a==="left"||a==="right"},applyFields:function(a){return Ext.Array.from(a)},applyVisibleRange:function(a,c){this.getChart();if(a[0]>a[1]){var b=a[0];a[0]=a[1];a[0]=b}if(a[1]===a[0]){a[1]+=1/this.getMaxZoom()}if(a[1]>a[0]+1){a[0]=0;a[1]=1}else{if(a[0]<0){a[1]-=a[0];a[0]=0}else{if(a[1]>1){a[0]-=a[1]-1;a[1]=1}}}if(c&&a[0]===c[0]&&a[1]===c[1]){return undefined}return a},updateVisibleRange:function(a){this.fireEvent("visiblerangechange",this,a)},onSeriesChange:function(e){var f=this,b=e.getSeries(),j="get"+f.getDirection()+"Axis",g=[],c,d=b.length,a,h;for(c=0;cn){n=c[1]}}}if(!isFinite(n)){n=m.prevMax}if(!isFinite(d)){d=m.prevMin}if(m.getLabelInSpan()||d===n){n+=m.getIncrement();d-=m.getIncrement()}if(Ext.isNumber(m.getMinimum())){d=m.getMinimum()}else{m.prevMin=d}if(Ext.isNumber(m.getMaximum())){n=m.getMaximum()}else{m.prevMax=n}m.range=[Ext.Number.correctFloat(d),Ext.Number.correctFloat(n)];if(m.getReconcileRange()){m.reconcileRange()}if(m.getAdjustByMajorUnit()&&l.adjustByMajorUnit&&!m.getMajorTickSteps()){j=Ext.Object.chain(m.getSprites()[0].attr);j.min=m.range[0];j.max=m.range[1];j.visibleMin=p[0];j.visibleMax=p[1];a={attr:j,segmenter:l};h.calculateLayout(a);g=a.majorTicks;if(g){l.adjustByMajorUnit(g.step,g.unit.scale,m.range);j.min=m.range[0];j.max=m.range[1];delete a.majorTicks;h.calculateLayout(a);g=a.majorTicks;l.adjustByMajorUnit(g.step,g.unit.scale,m.range)}else{if(!m.hasClearRangePending){m.hasClearRangePending=true;m.getChart().on("layout","clearRange",m)}}}if(!Ext.Array.equals(m.range,m.oldRange||[])){m.fireEvent("rangechange",m,m.range);m.oldRange=m.range}return m.range},clearRange:function(){delete this.hasClearRangePending;this.range=null},reconcileRange:function(){var e=this,g=e.getChart().getAxes(),f=e.getDirection(),b,d,c,a;if(!g){return}for(b=0,d=g.length;be.range[1]){e.range[1]=a[1]}}},applyStyle:function(c,b){var a=Ext.ClassManager.getByAlias("sprite."+this.seriesType);if(a&&a.def){c=a.def.normalize(c)}b=Ext.apply(b||{},c);return b},themeOnlyIfConfigured:{grid:true},updateTheme:function(d){var i=this,k=d.getAxis(),e=i.getPosition(),o=i.getInitialConfig(),c=i.defaultConfig,g=i.getConfigurator().configs,a=k.defaults,n=k[e],h=i.themeOnlyIfConfigured,l,j,p,b,m,f;k=Ext.merge({},a,n);for(l in k){j=k[l];f=g[l];if(j!==null&&j!==undefined&&f){m=o[l];p=Ext.isObject(j);b=m===c[l];if(p){if(b&&h[l]){continue}j=Ext.merge({},j,m)}if(b||p){i[f.names.set](j)}}}},updateCenter:function(b){var e=this.getSprites(),a=e[0],d=b[0],c=b[1];if(a){a.setAttributes({centerX:d,centerY:c})}if(this.gridSpriteEven){this.gridSpriteEven.getTemplate().setAttributes({translationX:d,translationY:c,rotationCenterX:d,rotationCenterY:c})}if(this.gridSpriteOdd){this.gridSpriteOdd.getTemplate().setAttributes({translationX:d,translationY:c,rotationCenterX:d,rotationCenterY:c})}},getSprites:function(){if(!this.getChart()){return}var i=this,e=i.getRange(),f=i.getPosition(),g=i.getChart(),c=g.getAnimation(),d,a,b=i.getLength(),h=i.superclass;if(c===false){c={duration:0}}if(e){a=Ext.applyIf({position:f,axis:i,min:e[0],max:e[1],length:b,grid:i.getGrid(),hidden:i.getHidden(),titleOffset:i.titleOffset,layout:i.getLayout(),segmenter:i.getSegmenter(),totalAngle:i.getTotalAngle(),label:i.getLabel()},i.getStyle());if(!i.sprites.length){while(!h.xtype){h=h.superclass}d=Ext.create("sprite."+h.xtype,a);d.fx.setCustomDurations({baseRotation:0});d.fx.on("animationstart","onAnimationStart",i);d.fx.on("animationend","onAnimationEnd",i);d.setLayout(i.getLayout());d.setSegmenter(i.getSegmenter());d.setLabel(i.getLabel());i.sprites.push(d);i.updateTitleSprite()}else{d=i.sprites[0];d.setAnimation(c);d.setAttributes(a)}if(i.getRenderer()){d.setRenderer(i.getRenderer())}}return i.sprites},updateTitleSprite:function(){var f=this,b=f.getLength();if(!f.sprites[0]||!Ext.isNumber(b)){return}var h=this.sprites[0].thickness,a=f.getSurface(),g=f.getTitle(),e=f.getPosition(),c=f.getMargin(),i=f.getTitleMargin(),d=a.roundPixel(b/2);if(g){switch(e){case"top":g.setAttributes({x:d,y:c+i/2,textBaseline:"top",textAlign:"center"},true);g.applyTransformations();f.titleOffset=g.getBBox().height+i;break;case"bottom":g.setAttributes({x:d,y:h+i/2,textBaseline:"top",textAlign:"center"},true);g.applyTransformations();f.titleOffset=g.getBBox().height+i;break;case"left":g.setAttributes({x:c+i/2,y:d,textBaseline:"top",textAlign:"center",rotationCenterX:c+i/2,rotationCenterY:d,rotationRads:-Math.PI/2},true);g.applyTransformations();f.titleOffset=g.getBBox().width+i;break;case"right":g.setAttributes({x:h-c+i/2,y:d,textBaseline:"bottom",textAlign:"center",rotationCenterX:h+i/2,rotationCenterY:d,rotationRads:Math.PI/2},true);g.applyTransformations();f.titleOffset=g.getBBox().width+i;break}}},onThicknessChanged:function(){this.getChart().onThicknessChanged()},getThickness:function(){if(this.getHidden()){return 0}return(this.sprites[0]&&this.sprites[0].thickness||1)+this.titleOffset+this.getMargin()},onAnimationStart:function(){this.spriteAnimationCount++;if(this.spriteAnimationCount===1){this.fireEvent("animationstart",this)}},onAnimationEnd:function(){this.spriteAnimationCount--;if(this.spriteAnimationCount===0){this.fireEvent("animationend",this)}},getItemId:function(){return this.getId()},getAncestorIds:function(){return[this.getChart().getId()]},isXType:function(a){return a==="axis"},resolveListenerScope:function(e){var d=this,a=Ext._namedScopes[e],c=d.getChart(),b;if(!a){b=c?c.resolveListenerScope(e,false):(e||d)}else{if(a.isThis){b=d}else{if(a.isController){b=c?c.resolveListenerScope(e,false):d}else{if(a.isSelf){b=c?c.resolveListenerScope(e,false):d;if(b===c&&!c.getInheritedConfig("defaultListenerScope")){b=d}}}}}return b},destroy:function(){var a=this;a.setChart(null);a.surface.destroy();a.surface=null;a.callParent()}});Ext.define("Ext.chart.LegendBase",{extend:"Ext.view.View",config:{tpl:['
','','
',"',"{name}","
","
","
"],nodeContainerSelector:"div."+Ext.baseCSSPrefix+"legend-container",itemSelector:"div."+Ext.baseCSSPrefix+"legend-item",docked:"bottom"},setDocked:function(d){var c=this,a=c.ownerCt,b;c.docked=d;switch(d){case"top":case"bottom":c.addCls(Ext.baseCSSPrefix+"horizontal");b="hbox";break;case"left":case"right":c.removeCls(Ext.baseCSSPrefix+"horizontal");b="vbox";break}if(a){a.setDocked(d)}},setStore:function(a){this.bindStore(a)},clearViewEl:function(){this.callParent(arguments);Ext.removeNode(this.getNodeContainer())},onItemClick:function(a,c,b,d){this.callParent(arguments);this.toggleItem(b)}});Ext.define("Ext.chart.Legend",{xtype:"legend",extend:"Ext.chart.LegendBase",config:{baseCls:Ext.baseCSSPrefix+"legend",padding:5,rect:null,disableSelection:true,toggleable:true},toggleItem:function(c){if(!this.getToggleable()){return}var b=this.getStore(),h=0,e,g=true,d,f,a;if(b){f=b.getCount();for(d=0;d1;a=b.getAt(c);if(a){e=a.get("disabled");if(e||g){a.set("disabled",!e)}}}}});Ext.define("Ext.chart.AbstractChart",{extend:"Ext.draw.Container",requires:["Ext.chart.theme.Default","Ext.chart.series.Series","Ext.chart.interactions.Abstract","Ext.chart.axis.Axis","Ext.data.StoreManager","Ext.chart.Legend","Ext.data.Store"],isChart:true,defaultBindProperty:"store",config:{store:"ext-empty-store",theme:"default",style:null,animation:!Ext.isIE8,series:[],axes:[],legend:null,colors:null,insetPadding:{top:10,left:10,right:10,bottom:10},background:null,interactions:[],mainRect:null,resizeHandler:null,highlightItem:null},animationSuspendCount:0,chartLayoutSuspendCount:0,axisThicknessSuspendCount:0,isThicknessChanged:false,surfaceZIndexes:{background:0,main:1,grid:2,series:3,axis:4,chart:5,overlay:6,events:7},constructor:function(a){var b=this;b.itemListeners={};b.surfaceMap={};b.chartComponents={};b.isInitializing=true;b.suspendChartLayout();b.animationSuspendCount++;b.callParent(arguments);delete b.isInitializing;b.getSurface("main");b.getSurface("chart").setFlipRtlText(b.getInherited().rtl);b.getSurface("overlay").waitFor(b.getSurface("series"));b.animationSuspendCount--;b.resumeChartLayout()},applyAnimation:function(a,b){if(!a){a={duration:0}}else{if(a===true){a={easing:"easeInOut",duration:500}}}return b?Ext.apply({},a,b):a},getAnimation:function(){if(this.animationSuspendCount){return{duration:0}}else{return this.callParent()}},applyInsetPadding:function(b,a){if(!Ext.isObject(b)){return Ext.util.Format.parseBox(b)}else{if(!a){return b}else{return Ext.apply(a,b)}}},suspendAnimation:function(){var d=this,c=d.getSeries(),e=c.length,b=-1,a;d.animationSuspendCount++;if(d.animationSuspendCount===1){while(++b0){this.axisThicknessSuspendCount--;if(this.axisThicknessSuspendCount===0&&this.isThicknessChanged){this.onThicknessChanged()}}},onThicknessChanged:function(){if(this.axisThicknessSuspendCount===0){this.isThicknessChanged=false;this.performLayout()}else{this.isThicknessChanged=true}},applySprites:function(b){var a=this.getSurface("chart");b=Ext.Array.from(b);a.removeAll(true);a.add(b);return b},initItems:function(){var a=this.items,b,d,c;if(a&&!a.isMixedCollection){this.items=[];a=Ext.Array.from(a);for(b=0,d=a.length;b=0){c.splice(b,1)}},applyAxes:function(b,k){var l=this,g={left:"right",right:"left"},m=[],c,d,e,a,f,h,j;l.animationSuspendCount++;l.getStore();if(!k){k=[];k.map={}}j=k.map;m.map={};b=Ext.Array.from(b,true);for(f=0,h=b.length;f0){a=b.applyColors(a)}return a||(c&&c.getColors())},applyColors:function(a){a=Ext.Array.map(a,function(b){if(Ext.isString(b)){return b}else{return b.toString()}});return a},updateColors:function(c){var k=this,e=k.getTheme(),a=c||(e&&e.getColors()),l=0,f=k.getSeries(),d=f&&f.length,g,j,b,h;if(a.length){for(g=0;g=0&&h<=e[2]&&g>=0&&g<=e[3])){return null}for(;b>=0;b--){c=a[b];j=c.getItemForPoint(h,g);if(j){return j}}return null},getItemsForPoint:function(h,g){var f=this,a=f.getSeries(),d=a.length,b=f.hasFirstLayout?d-1:-1,e=[],c,j;for(;b>=0;b--){c=a[b];j=c.getItemForPoint(h,g);if(j){e.push(j)}}return e},onAnimationStart:function(){this.fireEvent("animationstart",this)},onAnimationEnd:function(){this.fireEvent("animationend",this)},onDataChanged:function(){var d=this;if(d.isInitializing){return}var c=d.getMainRect(),a=d.getStore(),b=d.getSeries(),e=d.getAxes();if(!a||!e||!b){return}if(!c){d.on({redraw:d.onDataChanged,scope:d,single:true});return}d.processData();d.redraw()},recordCount:0,processData:function(){var g=this,e=g.getStore().getCount(),c=g.getSeries(),f=c.length,d=false,b=0,a;for(;bg.recordCount){g.updateColors(g.getColors());g.recordCount=e}},bindStore:function(a){this.setStore(a)},applyHighlightItem:function(f,a){if(f===a){return}if(Ext.isObject(f)&&Ext.isObject(a)){var e=f,d=a,c=e.sprite&&(e.sprite[0]||e.sprite),b=d.sprite&&(d.sprite[0]||d.sprite);if(c===b&&e.index===d.index){return}}return f},updateHighlightItem:function(b,a){if(a){a.series.setAttributesForItem(a,{highlighted:false})}if(b){b.series.setAttributesForItem(b,{highlighted:true});this.fireEvent("itemhighlight",this,b,a)}this.fireEvent("itemhighlightchange",this,b,a)},destroyChart:function(){var f=this,d=f.getLegend(),g=f.getAxes(),c=f.getSeries(),h=f.getInteractions(),b=[],a,e;f.surfaceMap=null;for(a=0,e=h.length;ad.getDepth()){g=f}else{for(a=0;ag){g=f}}}}d.setDepth(g)},updateDepth:function(d){var b=this,c=b.getSprites(),a={depth:d};if(c&&c.length){c[0].setAttributes(a)}if(b.gridSpriteEven&&b.gridSpriteOdd){b.gridSpriteEven.getTemplate().setAttributes(a);b.gridSpriteOdd.getTemplate().setAttributes(a)}},getGridAlignment:function(){switch(this.getPosition()){case"left":case"right":return"horizontal3d";case"top":case"bottom":return"vertical3d"}}});Ext.define("Ext.chart.axis.Category",{requires:["Ext.chart.axis.layout.CombineDuplicate","Ext.chart.axis.segmenter.Names"],extend:"Ext.chart.axis.Axis",alias:"axis.category",type:"category",config:{layout:"combineDuplicate",segmenter:"names"}});Ext.define("Ext.chart.axis.Category3D",{requires:["Ext.chart.axis.layout.CombineDuplicate","Ext.chart.axis.segmenter.Names"],extend:"Ext.chart.axis.Axis3D",alias:"axis.category3d",type:"category3d",config:{layout:"combineDuplicate",segmenter:"names"}});Ext.define("Ext.chart.axis.Numeric",{extend:"Ext.chart.axis.Axis",type:"numeric",alias:["axis.numeric","axis.radial"],requires:["Ext.chart.axis.layout.Continuous","Ext.chart.axis.segmenter.Numeric"],config:{layout:"continuous",segmenter:"numeric",aggregator:"double"}});Ext.define("Ext.chart.axis.Numeric3D",{extend:"Ext.chart.axis.Axis3D",alias:["axis.numeric3d"],type:"numeric3d",requires:["Ext.chart.axis.layout.Continuous","Ext.chart.axis.segmenter.Numeric"],config:{layout:"continuous",segmenter:"numeric",aggregator:"double"}});Ext.define("Ext.chart.axis.Time",{extend:"Ext.chart.axis.Numeric",alias:"axis.time",type:"time",requires:["Ext.chart.axis.layout.Continuous","Ext.chart.axis.segmenter.Time"],config:{calculateByLabelSize:true,dateFormat:null,fromDate:null,toDate:null,step:[Ext.Date.DAY,1],layout:"continuous",segmenter:"time",aggregator:"time"},updateDateFormat:function(a){this.setRenderer(function(c,b){return Ext.Date.format(new Date(b),a)})},updateFromDate:function(a){this.setMinimum(+a)},updateToDate:function(a){this.setMaximum(+a)},getCoordFor:function(a){if(Ext.isString(a)){a=new Date(a)}return +a}});Ext.define("Ext.chart.axis.Time3D",{extend:"Ext.chart.axis.Numeric3D",alias:"axis.time3d",type:"time3d",requires:["Ext.chart.axis.layout.Continuous","Ext.chart.axis.segmenter.Time"],config:{calculateByLabelSize:true,dateFormat:null,fromDate:null,toDate:null,step:[Ext.Date.DAY,1],layout:"continuous",segmenter:"time",aggregator:"time"},updateDateFormat:function(a){this.setRenderer(function(c,b){return Ext.Date.format(new Date(b),a)})},updateFromDate:function(a){this.setMinimum(+a)},updateToDate:function(a){this.setMaximum(+a)},getCoordFor:function(a){if(Ext.isString(a)){a=new Date(a)}return +a}});Ext.define("Ext.chart.grid.HorizontalGrid3D",{extend:"Ext.chart.grid.HorizontalGrid",alias:"grid.horizontal3d",inheritableStatics:{def:{processors:{depth:"number"},defaults:{depth:0}}},render:function(a,k,d){var f=this.attr,i=a.roundPixel(f.x),h=a.roundPixel(f.y),l=a.matrix.getDX(),c=k.lineWidth*0.5,j=f.height,e=f.depth,b,g;if(h<=d[1]){return}b=d[0]+e-l;g=h+c-e;k.beginPath();k.rect(b,g,d[2],j);k.fill();k.beginPath();k.moveTo(b,g);k.lineTo(b+d[2],g);k.stroke();b=d[0]+i-l;g=h+c;k.beginPath();k.moveTo(b,g);k.lineTo(b+e,g-e);k.lineTo(b+e,g-e+j);k.lineTo(b,g+j);k.closePath();k.fill();k.beginPath();k.moveTo(b,g);k.lineTo(b+e,g-e);k.stroke()}});Ext.define("Ext.chart.grid.VerticalGrid3D",{extend:"Ext.chart.grid.VerticalGrid",alias:"grid.vertical3d",inheritableStatics:{def:{processors:{depth:"number"},defaults:{depth:0}}},render_:function(c,d,f){var b=this.attr,a=c.roundPixel(b.x),e=d.lineWidth*0.5;d.beginPath();d.rect(a-e,f[1]-c.matrix.getDY(),b.width,f[3]);d.fill();d.beginPath();d.moveTo(a-e,f[1]-c.matrix.getDY());d.lineTo(a-e,f[1]+f[3]-c.matrix.getDY());d.stroke()},render:function(b,j,e){var g=this.attr,i=b.roundPixel(g.x),k=b.matrix.getDY(),d=j.lineWidth*0.5,a=g.width,f=g.depth,c,h;if(i>=e[2]){return}c=i-d+f;h=e[1]-f-k;j.beginPath();j.rect(c,h,a,e[3]);j.fill();j.beginPath();j.moveTo(c,h);j.lineTo(c,h+e[3]);j.stroke();c=i-d;h=e[3];j.beginPath();j.moveTo(c,h);j.lineTo(c+f,h-f);j.lineTo(c+f+a,h-f);j.lineTo(c+a,h);j.closePath();j.fill();c=i-d;h=e[3];j.beginPath();j.moveTo(c,h);j.lineTo(c+f,h-f);j.stroke()}});Ext.define("Ext.chart.interactions.CrossZoom",{extend:"Ext.chart.interactions.Abstract",type:"crosszoom",alias:"interaction.crosszoom",isCrossZoom:true,config:{axes:true,gestures:{dragstart:"onGestureStart",drag:"onGesture",dragend:"onGestureEnd",dblclick:"onDoubleTap"},undoButton:{}},stopAnimationBeforeSync:false,zoomAnimationInProgress:false,constructor:function(){this.callParent(arguments);this.zoomHistory=[]},applyAxes:function(b){var a={};if(b===true){return{top:{},right:{},bottom:{},left:{}}}else{if(Ext.isArray(b)){a={};Ext.each(b,function(c){a[c]={}})}else{if(Ext.isObject(b)){Ext.iterate(b,function(c,d){if(d===true){a[c]={}}else{if(d!==false){a[c]=d}}})}}}return a},applyUndoButton:function(b,a){var c=this;if(a){a.destroy()}if(b){return Ext.create("Ext.Button",Ext.apply({cls:[],text:"Undo Zoom",disabled:true,handler:function(){c.undoZoom()}},b))}},getSurface:function(){return this.getChart()&&this.getChart().getSurface("main")},setSeriesOpacity:function(b){var a=this.getChart()&&this.getChart().getSurface("series");if(a){a.element.setStyle("opacity",b)}},onGestureStart:function(h){var j=this,i=j.getChart(),d=j.getSurface(),l=i.getInnerRect(),c=i.getInnerPadding(),g=c.left,b=g+l[2],f=c.top,a=f+l[3],n=i.getEventXY(h),m=n[0],k=n[1];if(j.zoomAnimationInProgress){return}if(m>g&&mf&&kb){m=b}}if(ka){k=a}}j.selectionRect.setAttributes({width:m-j.startX,height:k-j.startY});if(Math.abs(j.startX-m)<11||Math.abs(j.startY-k)<11){j.selectionRect.setAttributes({globalAlpha:0.5})}else{j.selectionRect.setAttributes({globalAlpha:1})}d.renderFrame();return false}},onGestureEnd:function(i){var l=this;if(l.zoomAnimationInProgress){return}if(l.getLocks()[l.gestureEvent]===l){var k=l.getChart(),d=l.getSurface(),n=k.getInnerRect(),c=k.getInnerPadding(),g=c.left,b=g+n[2],f=c.top,a=f+n[3],h=n[2],j=n[3],p=k.getEventXY(i),o=p[0],m=p[1];if(ob){o=b}}if(ma){m=a}}if(Math.abs(l.startX-o)<11||Math.abs(l.startY-m)<11){d.remove(l.selectionRect)}else{l.zoomBy([Math.min(l.startX,o)/h,1-Math.max(l.startY,m)/j,Math.max(l.startX,o)/h,1-Math.min(l.startY,m)/j]);l.selectionRect.setAttributes({x:Math.min(l.startX,o),y:Math.min(l.startY,m),width:Math.abs(l.startX-o),height:Math.abs(l.startY-m)});l.selectionRect.setAnimation(k.getAnimation()||{duration:0});l.selectionRect.setAttributes({globalAlpha:0,x:0,y:0,width:h,height:j});l.zoomAnimationInProgress=true;k.suspendThicknessChanged();l.selectionRect.fx.on("animationend",function(){k.resumeThicknessChanged();d.remove(l.selectionRect);l.selectionRect=null;l.zoomAnimationInProgress=false})}d.renderFrame();l.sync();l.unlockEvents(l.gestureEvent);l.setSeriesOpacity(1);if(!l.zoomAnimationInProgress){d.remove(l.selectionRect);l.selectionRect=null}}},zoomBy:function(o){var n=this,a=n.getAxes(),k=n.getChart(),j=k.getAxes(),l=k.getInherited().rtl,f,d={},c,b;if(l){o=o.slice();c=1-o[0];b=1-o[2];o[0]=Math.min(c,b);o[2]=Math.max(c,b)}for(var h=0;h0&&D0&&CE){k=E}}if(j<0){j=0}else{if(j>f){j=f}}k+=t;j+=q;for(B=0;B"))},onDragStart:function(d){var c=this,a=c.getChart(),b=a.getHighlightItem();if(b){a.fireEvent("beginitemedit",a,c,c.item=b);return false}},onDrag:function(f){var d=this,b=d.getChart(),c=b.getHighlightItem(),a=c&&c.sprite.type;if(c){switch(a){case"barSeries":return d.onDragBar(f);break;case"scatterSeries":return d.onDragScatter(f);break}}},highlight:function(f){var e=this,d=e.getChart(),a=d.getFlipXY(),g=e.getCursors(),c=f&&f.sprite.type,b=d.el.dom.style;e.callParent([f]);if(f){switch(c){case"barSeries":if(a){b.cursor=g.ewResize}else{b.cursor=g.nsResize}break;case"scatterSeries":b.cursor=g.move;break}}else{d.el.dom.style.cursor="default"}},onDragBar:function(i){var m=this,k=m.getChart(),l=k.getInherited().rtl,f=k.isCartesian&&k.getFlipXY(),q=k.getHighlightItem(),g=q.sprite.getMarker("items"),p=g.getMarkerFor(q.sprite.getId(),q.index),b=q.sprite.getSurface(),c=b.getRect(),r=b.getEventXY(i),o=q.sprite.attr.matrix,j=m.getRenderer(),a,n,d,h;if(f){h=l?c[2]-r[0]:r[0]}else{h=c[3]-r[1]}a={x:p.x,y:h,width:p.width,height:p.height+(p.y-h),radius:p.radius,fillStyle:"none",lineDash:[4,4],zIndex:100};Ext.apply(a,m.getStyle());if(Ext.isArray(q.series.getYField())){h=h-p.y-p.height}m.target={index:q.index,yField:q.field,yValue:(h-o.getDY())/o.getYY()};d=[k,{target:m.target,style:a,item:q}];n=Ext.callback(j,null,d,0,k);if(n){Ext.apply(a,n)}q.sprite.putMarker("items",a,"itemedit");m.showTooltip(i,m.target,q);b.renderFrame()},onDragScatter:function(n){var t=this,g=t.getChart(),d=g.getInherited().rtl,l=g.isCartesian&&g.getFlipXY(),o=g.getHighlightItem(),b=o.sprite.getMarker("items"),p=b.getMarkerFor(o.sprite.getId(),o.index),j=o.sprite.getSurface(),h=j.getRect(),a=j.getEventXY(n),k=o.sprite.attr.matrix,c=o.series.getXAxis(),f=c&&c.getLayout().isContinuous,i=t.getRenderer(),m,u,q,s,r;if(l){r=d?h[2]-a[0]:a[0]}else{r=h[3]-a[1]}if(f){if(l){s=h[3]-a[1]}else{s=a[0]}}else{s=p.translationX}m={translationX:s,translationY:r,scalingX:p.scalingX,scalingY:p.scalingY,r:p.r,fillStyle:"none",lineDash:[4,4],zIndex:100};Ext.apply(m,t.getStyle());t.target={index:o.index,yField:o.field,yValue:(r-k.getDY())/k.getYY()};if(f){Ext.apply(t.target,{xField:o.series.getXField(),xValue:(s-k.getDX())/k.getXX()})}q=[g,{target:t.target,style:m,item:o}];u=Ext.callback(i,null,q,0,g);if(u){Ext.apply(m,u)}o.sprite.putMarker("items",m,"itemedit");t.showTooltip(n,t.target,o);j.renderFrame()},showTooltip:function(g,f,c){var d=this.getTooltip(),a,b;if(d&&Ext.toolkit!=="modern"){a=d.config;b=this.getChart();Ext.callback(a.renderer,null,[d,c,f,g],0,b);d.show([g.x+a.offsetX,g.y+a.offsetY])}},hideTooltip:function(){var a=this.getTooltip();if(a&&Ext.toolkit!=="modern"){a.hide()}},onDragEnd:function(g){var d=this,f=d.target,c=d.getChart(),b=c.getStore(),a;if(f){a=b.getAt(f.index);if(f.yField){a.set(f.yField,f.yValue,{convert:false})}if(f.xField){a.set(f.xField,f.xValue,{convert:false})}if(f.yField||f.xField){d.getChart().onDataChanged()}d.target=null}d.hideTooltip();if(d.item){c.fireEvent("enditemedit",c,d,d.item,f)}d.highlight(d.item=null)},destroy:function(){var a=this.getConfig("tooltip",true);Ext.destroy(a);this.callParent()}});Ext.define("Ext.chart.interactions.PanZoom",{extend:"Ext.chart.interactions.Abstract",type:"panzoom",alias:"interaction.panzoom",requires:["Ext.draw.Animator"],config:{axes:{top:{},right:{},bottom:{},left:{}},minZoom:null,maxZoom:null,showOverflowArrows:true,panGesture:"drag",zoomGesture:"pinch",zoomOnPanGesture:false,modeToggleButton:{xtype:"segmentedbutton",width:200,defaults:{ui:"default-toolbar"},cls:Ext.baseCSSPrefix+"panzoom-toggle",items:[{text:"Pan"},{text:"Zoom"}]},hideLabelInGesture:false},stopAnimationBeforeSync:true,applyAxes:function(b,a){return Ext.merge(a||{},b)},applyZoomOnPanGesture:function(a){this.getChart();if(this.isMultiTouch()){return false}return a},updateZoomOnPanGesture:function(b){var a=this.getModeToggleButton();if(!this.isMultiTouch()){a.show();a.setValue(b?1:0)}else{a.hide()}},toggleMode:function(){var a=this;if(!a.isMultiTouch()){a.setZoomOnPanGesture(!a.getZoomOnPanGesture())}},applyModeToggleButton:function(c,b){var d=this,a=Ext.factory(c,"Ext.button.Segmented",b);if(!a&&b){b.destroy()}if(a&&!b){a.addListener("toggle",function(e){d.setZoomOnPanGesture(e.getValue()===1)})}return a},getGestures:function(){var c=this,e={},d=c.getPanGesture(),b=c.getZoomGesture(),a=Ext.supports.Touch;e[b]="onZoomGestureMove";e[b+"start"]="onZoomGestureStart";e[b+"end"]="onZoomGestureEnd";e[d]="onPanGestureMove";e[d+"start"]="onPanGestureStart";e[d+"end"]="onPanGestureEnd";e.doubletap="onDoubleTap";return e},onDoubleTap:function(h){var f=this,c=f.getChart(),g=c.getAxes(),b,a,d;for(a=0,d=g.length;a1){b=1}if(b*j<1){b=1/j}f=o[0];p=o[1];l=l[1]-l[0];if(b===l&&l===1){return}c.setVisibleRange([(o[0]+o[1]-b)*0.5-n/e*b,(o[0]+o[1]+b)*0.5-n/e*b]);return(Math.abs(f-c.getVisibleRange()[0])>1e-10||Math.abs(p-c.getVisibleRange()[1])>1e-10)},destroy:function(){this.setModeToggleButton(null);this.callParent()}});Ext.define("Ext.chart.interactions.Rotate",{extend:"Ext.chart.interactions.Abstract",type:"rotate",alias:"interaction.rotate",config:{gesture:"rotate",gestures:{rotate:"onRotate",rotateend:"onRotate",dragstart:"onGestureStart",drag:"onGesture",dragend:"onGestureEnd"},rotation:0},oldRotations:null,getAngle:function(f){var c=this,b=c.getChart(),d=b.getEventXY(f),a=b.getCenter();return Math.atan2(d[1]-a[1],d[0]-a[0])},getRadius:function(a){return this.getChart().getRadius()},getEventRadius:function(h){var f=this,d=f.getChart(),g=d.getEventXY(h),a=d.getCenter(),c=g[0]-a[0],b=g[1]-a[1];return Math.sqrt(c*c+b*b)},onGestureStart:function(d){var c=this,b=c.getRadius(d),a=c.getEventRadius(d);if(b>=a){c.lockEvents("drag");c.angle=c.getAngle(d);c.oldRotations={};return false}},onGesture:function(b){var a=this,c=a.getAngle(b)-a.angle;if(a.getLocks().drag===a){a.doRotateTo(c,true);return false}},doRotateTo:function(d,a,b){var n=this,l=n.getChart(),k=l.getAxes(),f=l.getSeries(),m=n.oldRotations,c,j,g,e,h;if(!b){l.suspendAnimation()}for(e=0,h=k.length;ea){a=g}}}return a}});Ext.define("Ext.chart.plugin.ItemEvents",{extend:"Ext.plugin.Abstract",alias:"plugin.chartitemevents",moveEvents:false,mouseMoveEvents:{mousemove:true,mouseover:true,mouseout:true},itemMouseMoveEvents:{itemmousemove:true,itemmouseover:true,itemmouseout:true},init:function(b){var a="handleEvent";this.chart=b;b.addElementListener({click:a,dblclick:a,mousedown:a,mousemove:a,mouseup:a,mouseover:a,mouseout:a,priority:1001,scope:this})},hasItemMouseMoveListeners:function(){var b=this.chart.hasListeners,a;for(a in this.itemMouseMoveEvents){if(a in b){return true}}return false},handleEvent:function(g){var d=this,a=d.chart,h=g.type in d.mouseMoveEvents,c=d.lastItem,f,b;if(h&&!d.hasItemMouseMoveListeners()&&!d.moveEvents){return}f=a.getEventXY(g);b=a.getItemForPoint(f[0],f[1]);if(h&&!Ext.Object.equals(b,c)){if(c){a.fireEvent("itemmouseout",a,c,g);c.series.fireEvent("itemmouseout",c.series,c,g)}if(b){a.fireEvent("itemmouseover",a,b,g);b.series.fireEvent("itemmouseover",b.series,b,g)}}if(b){a.fireEvent("item"+g.type,a,b,g);b.series.fireEvent("item"+g.type,b.series,b,g)}d.lastItem=b}});Ext.define("Ext.chart.series.Cartesian",{extend:"Ext.chart.series.Series",config:{xField:null,yField:null,xAxis:null,yAxis:null},directions:["X","Y"],fieldCategoryX:["X"],fieldCategoryY:["Y"],applyXAxis:function(a,b){return this.getChart().getAxis(a)||b},applyYAxis:function(a,b){return this.getChart().getAxis(a)||b},updateXAxis:function(a){a.processData(this)},updateYAxis:function(a){a.processData(this)},coordinateX:function(){return this.coordinate("X",0,2)},coordinateY:function(){return this.coordinate("Y",1,2)},getItemForPoint:function(a,g){if(this.getSprites()){var f=this,d=f.getSprites()[0],b=f.getStore(),e,c;if(f.getHidden()){return null}if(d){c=d.getIndexNearPoint(a,g);if(c!==-1){e={series:f,category:f.getItemInstancing()?"items":"markers",index:c,record:b.getData().items[c],field:f.getYField(),sprite:d};return e}}}},createSprite:function(){var c=this,a=c.callParent(),b=c.getChart(),d=c.getXAxis();a.setAttributes({flipXY:b.getFlipXY(),xAxis:d});if(a.setAggregator&&d&&d.getAggregator){if(d.getAggregator){a.setAggregator({strategy:d.getAggregator()})}else{a.setAggregator({})}}return a},getSprites:function(){var d=this,c=this.getChart(),e=d.getAnimation()||c&&c.getAnimation(),b=d.getItemInstancing(),f=d.sprites,a;if(!c){return[]}if(!f.length){a=d.createSprite()}else{a=f[0]}if(e){if(b){a.itemsMarker.getTemplate().setAnimation(e)}a.setAnimation(e)}return f},provideLegendInfo:function(d){var b=this,a=b.getSubStyleWithTheme(),c=a.fillStyle;if(Ext.isArray(c)){c=c[0]}d.push({name:b.getTitle()||b.getYField()||b.getId(),mark:(Ext.isObject(c)?c.stops&&c.stops[0].color:c)||a.strokeStyle||"black",disabled:b.getHidden(),series:b.getId(),index:0})},getXRange:function(){return[this.dataRange[0],this.dataRange[2]]},getYRange:function(){return[this.dataRange[1],this.dataRange[3]]}});Ext.define("Ext.chart.series.StackedCartesian",{extend:"Ext.chart.series.Cartesian",config:{stacked:true,splitStacks:true,fullStack:false,fullStackTotal:100,hidden:[]},spriteAnimationCount:0,themeColorCount:function(){var b=this,a=b.getYField();return Ext.isArray(a)?a.length:1},updateStacked:function(){this.processData()},updateSplitStacks:function(){this.processData()},coordinateY:function(){return this.coordinateStacked("Y",1,2)},coordinateStacked:function(D,e,m){var F=this,f=F.getStore(),r=f.getData().items,B=r.length,c=F["get"+D+"Axis"](),x=F.getHidden(),a=F.getSplitStacks(),z=F.getFullStack(),l=F.getFullStackTotal(),p={min:0,max:0},n=F["fieldCategory"+D],C=[],o=[],E=[],h,A=F.getStacked(),g=F.getSprites(),q=[],w,v,u,s,H,y,b,d,G,t;if(!g.length){return}for(w=0;w=0||!a){y[v]+=G}else{if(G<0){b[v]+=G}}}}}for(v=0;v=0||!a){if(z&&y[u]){G[u]*=l/y[u]}C[u]=o[u];o[u]+=G[u];h[u]=o[u]}else{if(z&&b[u]){G[u]*=l/b[u]}C[u]=E[u];E[u]+=G[u];h[u]=E[u]}}t["dataStart"+d]=C;t["data"+d]=h;F.getRangeOfData(C,p);F.getRangeOfData(h,p)}else{t["dataStart"+d]=C;t["data"+d]=G;F.getRangeOfData(G,p)}g[v].setAttributes(t)}}F.dataRange[e]=p.min;F.dataRange[e+m]=p.max;t={};t["dataMin"+D]=p.min;t["dataMax"+D]=p.max;for(w=0;w=b[a-1]){return a-1}while(f+1>1,e=b[c];if(e===d){return c}else{if(ea&&ml&&v=b;v--){h=l[v]*e+m;g=C[v]*o+k;s.lineTo(f,g);s.lineTo(f=h,g)}}else{for(v=a;v>=b;v--){h=l[v]*e+m;g=C[v]*o+k;s.lineTo(h,g)}}}else{s.lineTo(l[a]*e+m,g);s.lineTo(l[a]*e+m,k);s.lineTo(z,k);s.lineTo(z,j[v]*o+k)}if(p.transformFillStroke){p.matrix.toContext(s)}s.fill();if(p.transformFillStroke){p.inverseMatrix.toContext(s)}s.beginPath();s.moveTo(z,w);if(p.step){for(v=b;v<=a;v++){h=l[v]*e+m;g=j[v]*o+k;s.lineTo(h,d);s.lineTo(h,d=g);n.translationX=c.x(h,g);n.translationY=c.y(h,g);B.putMarker("markers",n,v,!p.renderer)}}else{for(v=b;v<=a;v++){h=l[v]*e+m;g=j[v]*o+k;s.lineTo(h,g);n.translationX=c.x(h,g);n.translationY=c.y(h,g);B.putMarker("markers",n,v,!p.renderer)}}if(p.transformFillStroke){p.matrix.toContext(s)}s.stroke()}});Ext.define("Ext.chart.series.Area",{extend:"Ext.chart.series.StackedCartesian",alias:"series.area",type:"area",seriesType:"areaSeries",requires:["Ext.chart.series.sprite.Area"],config:{splitStacks:false}});Ext.define("Ext.chart.series.sprite.Bar",{alias:"sprite.barSeries",extend:"Ext.chart.series.sprite.StackedCartesian",inheritableStatics:{def:{processors:{minBarWidth:"number",maxBarWidth:"number",minGapWidth:"number",radius:"number",inGroupGapWidth:"number"},defaults:{minBarWidth:2,maxBarWidth:100,minGapWidth:5,inGroupGapWidth:3,radius:0}}},drawLabel:function(k,i,s,h,o){var q=this,n=q.attr,f=q.getMarker("labels"),d=f.getTemplate(),l=q.labelCfg||(q.labelCfg={}),c=q.surfaceMatrix,j=n.labelOverflowPadding,b=d.attr.display,m=d.attr.orientation,g,e,a,r,t,p;l.x=c.x(i,h);l.y=c.y(i,h);if(!n.flipXY){l.rotationRads=-Math.PI*0.5}else{l.rotationRads=0}l.calloutVertical=!n.flipXY;switch(m){case"horizontal":l.rotationRads=0;l.calloutVertical=false;break;case"vertical":l.rotationRads=-Math.PI*0.5;l.calloutVertical=true;break}l.text=k;if(d.attr.renderer){p=[k,f,l,{store:q.getStore()},o];r=Ext.callback(d.attr.renderer,null,p,0,q.getSeries());if(typeof r==="string"){l.text=r}else{if(typeof r==="object"){if("text" in r){l.text=r.text}t=true}}}a=q.getMarkerBBox("labels",o,true);if(!a){q.putMarker("labels",l,o);a=q.getMarkerBBox("labels",o,true)}e=(a.width/2+j);if(s>h){e=-e}if((m==="horizontal"&&n.flipXY)||(m==="vertical"&&!n.flipXY)||!m){g=(b==="insideStart")?s+e:h-e}else{g=(b==="insideStart")?s+j*2:h-j*2}l.x=c.x(i,g);l.y=c.y(i,g);g=(b==="insideStart")?s-e:h+e;l.calloutPlaceX=c.x(i,g);l.calloutPlaceY=c.y(i,g);g=(b==="insideStart")?s:h;l.calloutStartX=c.x(i,g);l.calloutStartY=c.y(i,g);if(s>h){e=-e}if(Math.abs(h-s)<=e*2||b==="outside"){l.callout=1}else{l.callout=0}if(t){Ext.apply(l,r)}q.putMarker("labels",l,o)},drawBar:function(l,b,d,c,h,k,a,e){var g=this,j={},f=g.attr.renderer,i;j.x=c;j.y=h;j.width=k-c;j.height=a-h;j.radius=g.attr.radius;if(f){i=Ext.callback(f,null,[g,j,{store:g.getStore()},e],0,g.getSeries());Ext.apply(j,i)}g.putMarker("items",j,e,!f)},renderClipped:function(G,u,F,C){if(this.cleanRedraw){return}var q=this,o=q.attr,w=o.dataX,v=o.dataY,H=o.labels,n=o.dataStartY,m=o.groupCount,E=o.groupOffset-(m-1)*0.5,z=o.inGroupGapWidth,t=u.lineWidth,D=o.matrix,B=D.elements[0],j=D.elements[3],e=D.elements[4],d=G.roundPixel(D.elements[5])-1,J=(B<0?-1:1)*B-o.minGapWidth,k=(Math.min(J,o.maxBarWidth)-z*(m-1))/m,A=G.roundPixel(Math.max(o.minBarWidth,k)),c=q.surfaceMatrix,g,I,b,h,K,a,l=0.5*o.lineWidth,L=Math.min(F[0],F[2]),x=Math.max(F[0],F[2]),y=Math.max(0,Math.floor(L)),p=Math.min(w.length-1,Math.ceil(x)),f=H&&q.getMarker("labels"),s,r;for(K=y;K<=p;K++){s=n?n[K]:0;r=v[K];a=w[K]*B+e+E*(A+z);g=G.roundPixel(a-A/2)+l;h=G.roundPixel(r*j+d+t);I=G.roundPixel(a+A/2)-l;b=G.roundPixel(s*j+d+t);q.drawBar(u,G,F,g,h-l,I,b-l,K);if(f&&H[K]!=null){q.drawLabel(H[K],a,b,h,K)}q.putMarker("markers",{translationX:c.x(a,h),translationY:c.y(a,h)},K,true)}},getIndexNearPoint:function(l,k){var m=this,g=m.attr,h=g.dataX,a=m.getSurface(),b=a.getRect()||[0,0,0,0],j=b[3],e,d,c,n,f=-1;if(g.flipXY){e=j-k;if(a.getInherited().rtl){d=b[2]-l}else{d=l}}else{e=l;d=j-k}for(c=0;c0){d.y=g;d.height=a+f}else{d.y=g+f;d.height=a-f}},render:function(l,m){var u=this,k=u.attr,r=k.x,j=k.y,f=j+k.height,i=j=0;b--){if(!c[b]){o=h[b];d=o.getIndexNearPoint(m,k);if(d!==-1){e=j.getYField();p={series:j,index:d,category:a?"items":"markers",record:n.getData().items[d],field:typeof e==="string"?e:e[b],sprite:o};return p}}}return null}}});Ext.define("Ext.draw.LimitedCache",{config:{limit:40,feeder:function(){return 0},scope:null},cache:null,constructor:function(a){this.cache={};this.cache.list=[];this.cache.tail=0;this.initConfig(a)},get:function(e){var c=this.cache,b=this.getLimit(),a=this.getFeeder(),d=this.getScope()||this;if(c[e]){return c[e].value}if(c.list[c.tail]){delete c[c.list[c.tail].cacheId]}c[e]=c.list[c.tail]={value:a.apply(d,Array.prototype.slice.call(arguments,1)),cacheId:e};c.tail++;if(c.tail===b){c.tail=0}return c[e].value},clear:function(){this.cache={};this.cache.list=[];this.cache.tail=0}});Ext.define("Ext.draw.SegmentTree",{config:{strategy:"double"},time:function(m,l,n,c,E,d,e){var f=0,o,A,s=new Date(n[m.startIdx[0]]),x=new Date(n[m.endIdx[l-1]]),D=Ext.Date,u=[[D.MILLI,1,"ms1",null],[D.MILLI,2,"ms2","ms1"],[D.MILLI,5,"ms5","ms1"],[D.MILLI,10,"ms10","ms5"],[D.MILLI,50,"ms50","ms10"],[D.MILLI,100,"ms100","ms50"],[D.MILLI,500,"ms500","ms100"],[D.SECOND,1,"s1","ms500"],[D.SECOND,10,"s10","s1"],[D.SECOND,30,"s30","s10"],[D.MINUTE,1,"mi1","s10"],[D.MINUTE,5,"mi5","mi1"],[D.MINUTE,10,"mi10","mi5"],[D.MINUTE,30,"mi30","mi10"],[D.HOUR,1,"h1","mi30"],[D.HOUR,6,"h6","h1"],[D.HOUR,12,"h12","h6"],[D.DAY,1,"d1","h12"],[D.DAY,7,"d7","d1"],[D.MONTH,1,"mo1","d1"],[D.MONTH,3,"mo3","mo1"],[D.MONTH,6,"mo6","mo3"],[D.YEAR,1,"y1","mo3"],[D.YEAR,5,"y5","y1"],[D.YEAR,10,"y10","y5"],[D.YEAR,100,"y100","y10"]],z,b,k=f,F=l,j=false,r=m.startIdx,h=m.endIdx,w=m.minIdx,C=m.maxIdx,a=m.open,y=m.close,g=m.minX,q=m.minY,p=m.maxX,B=m.maxY,v,t;for(z=0;l>f+1&&zn.length*2*b[1]){continue}if(b[3]&&m.map["time_"+b[3]]){o=m.map["time_"+b[3]][0];A=m.map["time_"+b[3]][1]}else{o=k;A=F}f=l;t=s;j=true;r[l]=r[o];h[l]=h[o];w[l]=w[o];C[l]=C[o];a[l]=a[o];y[l]=y[o];g[l]=g[o];q[l]=q[o];p[l]=p[o];B[l]=B[o];t=Ext.Date.add(t,b[0],b[1]);for(v=o+1;vB[l]){B[l]=B[v];p[l]=p[v];C[l]=C[v]}if(q[v]f){m.map["time_"+b[2]]=[f,l]}}},"double":function(h,u,j,a,t,b,c){var e=0,k,f=1,n,d,v,g,s,l,m,r,q,p,o;while(u>e+1){k=e;e=u;f+=f;for(n=k;n=h.maxY[n+1]){s=h.maxIdx[n];p=h.maxX[n];o=h.maxY[n]}else{s=h.maxIdx[n+1];p=h.maxX[n+1];o=h.maxY[n+1]}}h.startIdx[u]=d;h.endIdx[u]=v;h.minIdx[u]=g;h.maxIdx[u]=s;h.open[u]=l;h.close[u]=m;h.minX[u]=r;h.minY[u]=q;h.maxX[u]=p;h.maxY[u]=o;u++}h.map["double_"+f]=[e,u]}},none:Ext.emptyFn,aggregateData:function(h,a,r,c,d){var b=h.length,e=[],s=[],f=[],q=[],j=[],p=[],n=[],o=[],m=[],k=[],g={startIdx:e,endIdx:s,minIdx:f,maxIdx:q,open:j,minX:p,minY:n,maxX:o,maxY:m,close:k},l;for(l=0;l=b[c.startIdx[a-1]]){return a-1}while(g+1>1,f=b[c.startIdx[d]];if(f===e){return d}else{if(f=b[c.endIdx[a-1]]){return a-1}while(g+1>1,f=b[c.endIdx[d]];if(f===e){return d}else{if(f0){if(e){d.getAggregator().setData(b.dataX,b.dataY,e,a,f)}else{d.getAggregator().setData(b.dataX,b.dataY)}}},getGapWidth:function(){return 1},renderClipped:function(b,c,g,f){var e=this,d=Math.min(g[0],g[2]),a=Math.max(g[0],g[2]),h=e.getAggregator()&&e.getAggregator().getAggregation(d,a,(a-d)/f[2]*e.getGapWidth());if(h){e.dataStart=h.data.startIdx[h.start];e.dataEnd=h.data.endIdx[h.end-1];e.renderAggregates(h.data,h.start,h.end,b,c,g,f)}}});Ext.define("Ext.chart.series.sprite.CandleStick",{alias:"sprite.candlestickSeries",extend:"Ext.chart.series.sprite.Aggregative",inheritableStatics:{def:{processors:{raiseStyle:function(b,a){return Ext.merge({},a||{},b)},dropStyle:function(b,a){return Ext.merge({},a||{},b)},barWidth:"number",padding:"number",ohlcType:"enums(candlestick,ohlc)"},defaults:{raiseStyle:{strokeStyle:"green",fillStyle:"green"},dropStyle:{strokeStyle:"red",fillStyle:"red"},planar:false,barWidth:15,padding:3,lineJoin:"miter",miterLimit:5,ohlcType:"candlestick"},triggers:{raiseStyle:"raiseStyle",dropStyle:"dropStyle"},updaters:{raiseStyle:function(){this.raiseTemplate&&this.raiseTemplate.setAttributes(this.attr.raiseStyle)},dropStyle:function(){this.dropTemplate&&this.dropTemplate.setAttributes(this.attr.dropStyle)}}}},candlestick:function(i,c,a,e,h,f,b){var d=Math.min(c,h),g=Math.max(c,h);i.moveTo(f,e);i.lineTo(f,g);i.moveTo(f+b,g);i.lineTo(f+b,d);i.lineTo(f-b,d);i.lineTo(f-b,g);i.closePath();i.moveTo(f,a);i.lineTo(f,d)},ohlc:function(b,d,e,a,f,c,g){b.moveTo(c,e);b.lineTo(c,a);b.moveTo(c,d);b.lineTo(c-g,d);b.moveTo(c,f);b.lineTo(c+g,f)},constructor:function(){this.callParent(arguments);this.raiseTemplate=new Ext.draw.sprite.Rect({parent:this});this.dropTemplate=new Ext.draw.sprite.Rect({parent:this})},getGapWidth:function(){var a=this.attr,b=a.barWidth,c=a.padding;return b+c},renderAggregates:function(d,c,b,t,u,z){var D=this,s=this.attr,j=s.dataX,v=s.matrix,e=v.getXX(),r=v.getYY(),l=v.getDX(),h=v.getDY(),o=s.barWidth/e,C,k=s.ohlcType,f=Math.round(o*0.5*e),a=d.open,y=d.close,B=d.maxY,p=d.minY,q=d.startIdx,m,g,E,n,A,x,w=s.lineWidth*t.devicePixelRatio/2;w-=Math.floor(w);u.save();C=this.raiseTemplate;C.useAttributes(u,z);u.beginPath();for(x=c;xy[x]){m=Math.round(a[x]*r+h)+w;g=Math.round(B[x]*r+h)+w;E=Math.round(p[x]*r+h)+w;n=Math.round(y[x]*r+h)+w;A=Math.round(j[q[x]]*e+l)+w;D[k](u,m,g,E,n,A,f)}}u.fillStroke(C.attr);u.restore()}});Ext.define("Ext.chart.series.CandleStick",{extend:"Ext.chart.series.Cartesian",requires:["Ext.chart.series.sprite.CandleStick"],alias:"series.candlestick",type:"candlestick",seriesType:"candlestickSeries",config:{openField:null,highField:null,lowField:null,closeField:null},fieldCategoryY:["Open","High","Low","Close"],themeColorCount:function(){return 2}});Ext.define("Ext.chart.series.Polar",{extend:"Ext.chart.series.Series",config:{rotation:0,radius:null,center:[0,0],offsetX:0,offsetY:0,showInLegend:true,xField:null,yField:null,angleField:null,radiusField:null,xAxis:null,yAxis:null},directions:["X","Y"],fieldCategoryX:["X"],fieldCategoryY:["Y"],deprecatedConfigs:{field:"angleField",lengthField:"radiusField"},constructor:function(b){var c=this,a=c.getConfigurator(),e=a.configs,d;if(b){for(d in c.deprecatedConfigs){if(d in b&&!(b in e)){Ext.raise("'"+d+"' config has been deprecated. Please use the '"+c.deprecatedConfigs[d]+"' config instead.")}}}c.callParent([b])},getXField:function(){return this.getAngleField()},updateXField:function(a){this.setAngleField(a)},getYField:function(){return this.getRadiusField()},updateYField:function(a){this.setRadiusField(a)},applyXAxis:function(a,b){return this.getChart().getAxis(a)||b},applyYAxis:function(a,b){return this.getChart().getAxis(a)||b},getXRange:function(){return[this.dataRange[0],this.dataRange[2]]},getYRange:function(){return[this.dataRange[1],this.dataRange[3]]},themeColorCount:function(){var c=this,a=c.getStore(),b=a&&a.getCount()||0;return b},isStoreDependantColorCount:true,getDefaultSpriteConfig:function(){return{type:this.seriesType,renderer:this.getRenderer(),centerX:0,centerY:0,rotationCenterX:0,rotationCenterY:0}},applyRotation:function(a){return Ext.draw.sprite.AttributeParser.angle(a)},updateRotation:function(a){var b=this.getSprites();if(b&&b[0]){b[0].setAttributes({baseRotation:a})}}});Ext.define("Ext.chart.series.Gauge",{alias:"series.gauge",extend:"Ext.chart.series.Polar",type:"gauge",seriesType:"pieslice",requires:["Ext.draw.sprite.Sector"],config:{needle:false,needleLength:90,needleWidth:4,donut:30,showInLegend:false,value:null,colors:null,sectors:null,minimum:0,maximum:100,rotation:0,totalAngle:Math.PI/2,rect:[0,0,1,1],center:[0.5,0.75],radius:0.5,wholeDisk:false},coordinateX:function(){return this.coordinate("X",0,2)},coordinateY:function(){return this.coordinate("Y",1,2)},updateNeedle:function(b){var a=this,d=a.getSprites(),c=a.valueToAngle(a.getValue());if(d&&d.length){d[0].setAttributes({startAngle:(b?c:0),endAngle:c,strokeOpacity:(b?1:0),lineWidth:(b?a.getNeedleWidth():0)});a.doUpdateStyles()}},themeColorCount:function(){var c=this,a=c.getStore(),b=a&&a.getCount()||0;return b+(c.getNeedle()?0:1)},updateColors:function(a,b){var f=this,h=f.getSectors(),j=h&&h.length,e=f.getSprites(),c=Ext.Array.clone(a),g=a&&a.length,d;if(!g||!a[0]){return}for(d=0;d0?f[b-1].end:d.getMinimum()),end:Math.min(e,d.getMaximum())};if(b==(c-1)&&f[b].end0?f[b-1].end:d.getMinimum())}if(typeof e.end==="number"){a=Math.min(e.end,d.getMaximum())}else{a=d.getMaximum()}f[b].start=g;f[b].end=a}}}else{f=[{start:d.getMinimum(),end:d.getMaximum()}]}return f},getSprites:function(){var j=this,m=j.getStore(),l=j.getValue(),c,g;if(!m&&!Ext.isNumber(l)){return[]}var h=j.getChart(),b=j.getAnimation()||h&&h.getAnimation(),f=j.sprites,k=0,o,n,e,d,a=[];if(f&&f.length){f[0].setAnimation(b);return f}d={store:m,field:j.getXField(),angleField:j.getXField(),value:l,series:j};o=j.createSprite();o.setAttributes({zIndex:10},true);o.rendererData=d;o.rendererIndex=k++;a.push(j.getNeedleWidth());j.getLabel().getTemplate().setField(true);n=j.normalizeSectors(j.getSectors());for(c=0,g=n.length;c2&&b.length>2){this.smoothX=Ext.draw.Draw.spline(c);this.smoothY=Ext.draw.Draw.spline(b)}else{delete this.smoothX;delete this.smoothY}}}}},list:null,updatePlainBBox:function(d){var b=this.attr,c=Math.min(0,b.dataMinY),a=Math.max(0,b.dataMaxY);d.x=b.dataMinX;d.y=c;d.width=b.dataMaxX-b.dataMinX;d.height=a-c},drawStrip:function(a,c){a.moveTo(c[0],c[1]);for(var b=2,d=c.length;b0){b++;d+=c>>b}return Math.pow(2,b>0?b-1:b)},drawSmoothStroke:function(u,v,c,b,C,f){var G=this,t=G.attr,d=t.step,z=t.matrix,s=t.renderer,e=z.getXX(),p=z.getYY(),m=z.getDX(),k=z.getDY(),r=G.smoothX,q=G.smoothY,I=G.calculateScale(t.dataX.length,b),o,F,n,E,h,g,B,a,A,w,H,D,l={type:"line",smooth:true,step:d};v.beginPath();v.moveTo(r[c*3]*e+m,q[c*3]*p+k);for(A=0,w=c*3+1;Ap){q.push(p*C+c,n*f+b,U[R]);q.push(O*C+c,M*f+b,U[R])}else{q.push(p*C+c,n*f+b,U[R])}}}if(q.length){for(R=0;Ra){K=a}else{if(K<-a){K=-a}}q[R+1]=K}else{S=false;continue}G=q[R+2];if(t){m.drawMarker(L,K,G)}if(d&&h[G]){m.drawLabel(h[G],L,K,G,D)}}m.isContinuousLine=S;if(g&&!S){Ext.raise("Line smoothing in only supported for gapless data, where all data points are finite numbers.")}if(v){T=v.getAlignment()==="vertical";if(Ext.isNumber(v.floatingAtCoord)){Q=(T?D[2]:D[3])-v.floatingAtCoord}else{Q=T?D[0]:D[1]}}else{Q=k.flipXY?D[0]:D[1]}if(k.preciseStroke){if(k.fillArea){o.fill()}if(k.transformFillStroke){k.inverseMatrix.toContext(o)}m.drawStroke(N,o,w,l,q,Q);if(k.transformFillStroke){k.matrix.toContext(o)}o.stroke()}else{m.drawStroke(N,o,w,l,q,Q);if(S&&g&&k.fillArea&&!k.renderer){var A=s[s.length-1]*C+c+u,z=r[r.length-1]*f+b,J=s[0]*C+c-u,H=r[0]*f+b;o.lineTo(A,z);o.lineTo(A,Q-k.lineWidth);o.lineTo(J,Q-k.lineWidth);o.lineTo(J,H)}if(k.transformFillStroke){k.matrix.toContext(o)}if(k.fillArea){o.fillStroke(k,true)}else{o.stroke(true)}}}}});Ext.define("Ext.chart.series.Line",{extend:"Ext.chart.series.Cartesian",alias:"series.line",type:"line",seriesType:"lineSeries",requires:["Ext.chart.series.sprite.Line"],config:{selectionTolerance:20,smooth:false,step:false,fill:undefined,aggregator:{strategy:"double"}},defaultSmoothness:3,overflowBuffer:1,themeMarkerCount:function(){return 1},getDefaultSpriteConfig:function(){var d=this,e=d.callParent(arguments),c=Ext.apply({},d.getStyle()),b,a=false;if(typeof d.config.fill!="undefined"){if(d.config.fill){a=true;if(typeof c.fillStyle=="undefined"){if(typeof c.strokeStyle=="undefined"){b=d.getStyleWithTheme();c.fillStyle=b.fillStyle;c.strokeStyle=b.strokeStyle}else{c.fillStyle=c.strokeStyle}}}}else{if(c.fillStyle){a=true}}if(!a){delete c.fillStyle}c=Ext.apply(e||{},c);return Ext.apply(c,{fillArea:a,step:d.config.step,smooth:d.config.smooth,selectionTolerance:d.config.selectionTolerance})},updateStep:function(b){var a=this.getSprites()[0];if(a&&a.attr.step!==b){a.setAttributes({step:b})}},updateFill:function(b){var a=this.getSprites()[0];if(a&&a.attr.fillArea!==b){a.setAttributes({fillArea:b})}},updateSmooth:function(a){var b=this.getSprites()[0];if(b&&b.attr.smooth!==a){b.setAttributes({smooth:a})}}});Ext.define("Ext.chart.series.sprite.PieSlice",{extend:"Ext.draw.sprite.Sector",mixins:{markerHolder:"Ext.chart.MarkerHolder"},alias:"sprite.pieslice",inheritableStatics:{def:{processors:{doCallout:"bool",label:"string",rotateLabels:"bool",labelOverflowPadding:"number",renderer:"default"},defaults:{doCallout:true,rotateLabels:true,label:"",labelOverflowPadding:10,renderer:null}}},config:{rendererData:null,rendererIndex:0,series:null},setGradientBBox:function(q,k){var j=this,i=j.attr,g=(i.fillStyle&&i.fillStyle.isGradient)||(i.strokeStyle&&i.strokeStyle.isGradient);if(g&&!i.constrainGradients){var b=j.getMidAngle(),d=i.margin,e=i.centerX,c=i.centerY,a=i.endRho,l=i.matrix,o=l.getScaleX(),n=l.getScaleY(),m=o*a,f=n*a,p={width:m+m,height:f+f};if(d){e+=d*Math.cos(b);c+=d*Math.sin(b)}p.x=l.x(e,c)-m;p.y=l.y(e,c)-f;q.setGradientBBox(p)}else{j.callParent([q,k])}},render:function(b,c,g,f){var e=this,a=e.attr,h={},d;if(a.renderer){h={type:"sector",text:a.text,centerX:a.centerX,centerY:a.centerY,margin:a.margin,startAngle:Math.min(a.startAngle,a.endAngle),endAngle:Math.max(a.startAngle,a.endAngle),startRho:Math.min(a.startRho,a.endRho),endRho:Math.max(a.startRho,a.endRho)};d=Ext.callback(a.renderer,null,[e,h,e.rendererData,e.rendererIndex],0,e.getSeries());e.setAttributes(d);e.useAttributes(c,g)}e.callParent([b,c,g,f]);if(a.label&&e.getMarker("labels")){e.placeLabel()}},placeLabel:function(){var z=this,s=z.attr,r=s.attributeId,t=Math.min(s.startAngle,s.endAngle),p=Math.max(s.startAngle,s.endAngle),k=(t+p)*0.5,n=s.margin,h=s.centerX,g=s.centerY,f=Math.sin(k),c=Math.cos(k),v=Math.min(s.startRho,s.endRho)+n,m=Math.max(s.startRho,s.endRho)+n,l=(v+m)*0.5,b=z.surfaceMatrix,o=z.labelCfg||(z.labelCfg={}),e=z.getMarker("labels"),d=e.getTemplate(),a=d.getCalloutLine(),q=a&&a.length||40,u,j,i,A,w;b.appendMatrix(s.matrix);o.text=s.label;j=h+c*l;i=g+f*l;o.x=b.x(j,i);o.y=b.y(j,i);j=h+c*m;i=g+f*m;o.calloutStartX=b.x(j,i);o.calloutStartY=b.y(j,i);j=h+c*(m+q);i=g+f*(m+q);o.calloutPlaceX=b.x(j,i);o.calloutPlaceY=b.y(j,i);if(!s.rotateLabels){o.rotationRads=0}else{switch(d.attr.orientation){case"horizontal":o.rotationRads=k+Math.atan2(b.y(1,0)-b.y(0,0),b.x(1,0)-b.x(0,0))+Math.PI/2;break;case"vertical":o.rotationRads=k+Math.atan2(b.y(1,0)-b.y(0,0),b.x(1,0)-b.x(0,0));break}}o.calloutColor=(a&&a.color)||z.attr.fillStyle;if(a){if(a.width){o.calloutWidth=a.width}}else{o.calloutHasLine=false}o.globalAlpha=s.globalAlpha*s.fillOpacity;o.hidden=(s.startAngle==s.endAngle);if(d.attr.renderer){w=[z.attr.label,e,o,z.rendererData,z.rendererIndex];A=Ext.callback(d.attr.renderer,null,w,0,z.getSeries());if(typeof A==="string"){o.text=A}else{Ext.apply(o,A)}}z.putMarker("labels",o,r);u=z.getMarkerBBox("labels",r,true);if(u){if(s.doCallout){if(d.attr.display==="outside"){z.putMarker("labels",{callout:1},r)}else{if(d.attr.display==="inside"){z.putMarker("labels",{callout:0},r)}else{z.putMarker("labels",{callout:1-z.sliceContainsLabel(s,u)},r)}}}else{z.putMarker("labels",{globalAlpha:z.sliceContainsLabel(s,u)},r)}}},sliceContainsLabel:function(d,f){var e=d.labelOverflowPadding,h=(d.endRho+d.startRho)/2,g=h+(f.width+e)/2,i=h-(f.width+e)/2,j,c,b,a;if(e<0){return 1}if(f.width+e*2>(d.endRho-d.startRho)){return 0}c=Math.sqrt(d.endRho*d.endRho-g*g);b=Math.sqrt(d.endRho*d.endRho-i*i);j=Math.abs(d.endAngle-d.startAngle);a=(j>Math.PI/2?i:Math.abs(Math.tan(j/2))*i);if(f.height+e*2>Math.min(c,b,a)*2){return 0}return 1}});Ext.define("Ext.chart.series.Pie",{extend:"Ext.chart.series.Polar",requires:["Ext.chart.series.sprite.PieSlice"],type:"pie",alias:"series.pie",seriesType:"pieslice",config:{donut:0,rotation:0,clockwise:true,totalAngle:2*Math.PI,hidden:[],radiusFactor:100,highlightCfg:{margin:20},style:{}},directions:["X"],applyLabel:function(a,b){if(Ext.isObject(a)&&!Ext.isString(a.orientation)){Ext.apply(a=Ext.Object.chain(a),{orientation:"vertical"})}return this.callParent([a,b])},updateLabelData:function(){var h=this,j=h.getStore(),g=j.getData().items,e=h.getSprites(),a=h.getLabel().getTemplate().getField(),d=h.getHidden(),b,f,c,k;if(e.length&&a){c=[];for(b=0,f=g.length;bs){s=k}}d[p]=a;if(p>=o.length){o[p]=false}}o.length=c;t.maxY=s;if(a!==0){m=h/a}for(p=0;p=a){return{series:h,sprite:f[b],index:b,record:g[b],field:h.getXField()}}}}}return null},getItemForPoint:function(f,e){var t=this,c=t.getSprites();if(c){var s=t.getCenter(),q=t.getOffsetX(),p=t.getOffsetY(),j=f-s[0]+q,h=e-s[1]+p,b=t.getStore(),g=t.getDonut(),o=b.getData().items,r=Math.atan2(h,j)-t.getRotation(),a=Math.sqrt(j*j+h*h),l=t.getRadius()*g*0.01,m=t.getHidden(),n,d,k;for(n=0,d=o.length;n=l+k.margin&&a<=k.endRho+k.margin){if(t.betweenAngle(r,k.startAngle,k.endAngle)){return{series:t,sprite:c[n],index:n,record:o[n],field:t.getXField()}}}}}return null}},provideLegendInfo:function(f){var h=this,j=h.getStore();if(j){var g=j.getData().items,b=h.getLabel().getTemplate().getField(),c=h.getXField(),e=h.getHidden(),d,a,k;for(d=0;d=0&&b<0){f=Math.sin(d)}else{if(d<=0&&b>0){f=Math.sin(b)}else{if(d>=0&&b>0){if(d>b){f=0}else{f=Math.max(Math.sin(d),Math.sin(b))}}else{f=1}}}a.zIndex=4+f;break;case"outerBack":a.zIndex=1;break;case"start":a.zIndex=4+Math.sin(c(d+e));break;case"end":a.zIndex=4+Math.sin(c(b+e));break;case"innerFront":a.zIndex=2;break;case"innerBack":a.zIndex=4+Math.sin(c((d+b)/2+e));break;case"bottom":a.zIndex=0;break}a.dirtyZIndex=true},updatePlainBBox:function(k){var f=this.attr,a=f.part,b=f.baseRotation,e=f.centerX,d=f.centerY,j,c,i,h,g,l;if(a==="start"){c=f.startAngle+b}else{if(a==="end"){c=f.endAngle+b}}if(Ext.isNumber(c)){g=Math.sin(c);l=Math.cos(c);i=Math.min(e+l*f.startRho,e+l*f.endRho);h=d+g*f.startRho*f.distortion;k.x=i;k.y=h;k.width=l*(f.endRho-f.startRho);k.height=f.thickness+g*(f.endRho-f.startRho)*2;return}if(a==="innerFront"||a==="innerBack"){j=f.startRho}else{j=f.endRho}k.width=j*2;k.height=j*f.distortion*2+f.thickness;k.x=f.centerX-j;k.y=f.centerY-j*f.distortion},updateTransformedBBox:function(a){if(this.attr.part==="start"||this.attr.part==="end"){return this.callParent(arguments)}return this.updatePlainBBox(a)},updatePath:function(a){if(!this.attr.globalAlpha){return}if(this.attr.endAngle0||c,i;if(n){i=(p+m)/2;g+=Math.cos(i)*k;f+=Math.sin(i)*k*e;l.moveTo(g+d*q,f+b*q*e);l.lineTo(g+d*j,f+b*j*e);l.lineTo(g+d*j,f+b*j*e+a);l.lineTo(g+d*q,f+b*q*e+a);l.closePath()}},startRenderer:function(a){this.sideRenderer(a,"start")},endRenderer:function(a){this.sideRenderer(a,"end")},rimRenderer:function(q,e,o,j){var w=this,s=w.attr,p=s.margin,h=s.centerX,g=s.centerY,d=s.distortion,i=s.baseRotation,t=Ext.draw.sprite.AttributeParser.angle,u=s.startAngle+i,r=s.endAngle+i,k=t((u+r)/2),a=s.thickness,b=s.globalAlpha<1,c,n,v;w.bevelParams=[];u=t(u);r=t(r);h+=Math.cos(k)*p;g+=Math.sin(k)*p*d;c=u>=0&&r>=0;n=u<=0&&r<=0;function l(){q.ellipse(h,g+a,e,e*d,0,Math.PI,u,true);q.lineTo(h+Math.cos(u)*e,g+Math.sin(u)*e*d);v=[h,g,e,e*d,0,u,Math.PI,false];if(!o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}function f(){q.ellipse(h,g+a,e,e*d,0,0,r,false);q.lineTo(h+Math.cos(r)*e,g+Math.sin(r)*e*d);v=[h,g,e,e*d,0,r,0,true];if(!o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}function x(){q.ellipse(h,g+a,e,e*d,0,Math.PI,r,false);q.lineTo(h+Math.cos(r)*e,g+Math.sin(r)*e*d);v=[h,g,e,e*d,0,r,Math.PI,true];if(o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}function m(){q.ellipse(h,g+a,e,e*d,0,u,0,false);q.lineTo(h+e,g);v=[h,g,e,e*d,0,0,u,true];if(o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}if(j){if(!o||b){if(u>=0&&r<0){l()}else{if(u<=0&&r>0){f()}else{if(u<=0&&r<0){if(u>r){q.ellipse(h,g+a,e,e*d,0,0,Math.PI,false);q.lineTo(h-e,g);v=[h,g,e,e*d,0,Math.PI,0,true];if(!o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}}else{if(u>r){l();f()}else{v=[h,g,e,e*d,0,u,r,false];if(c&&!o||n&&o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.lineTo(h+Math.cos(r)*e,g+Math.sin(r)*e*d+a);q.ellipse(h,g+a,e,e*d,0,r,u,true);q.closePath()}}}}}}else{if(o||b){if(u>=0&&r<0){x()}else{if(u<=0&&r>0){m()}else{if(u<=0&&r<0){if(u>r){x();m()}else{q.ellipse(h,g+a,e,e*d,0,u,r,false);q.lineTo(h+Math.cos(r)*e,g+Math.sin(r)*e*d);v=[h,g,e,e*d,0,r,u,true];if(o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}}else{if(u>r){q.ellipse(h,g+a,e,e*d,0,-Math.PI,0,false);q.lineTo(h+e,g);v=[h,g,e,e*d,0,0,-Math.PI,true];if(o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}}}}}}},innerFrontRenderer:function(a){this.rimRenderer(a,this.attr.startRho,true,true)},innerBackRenderer:function(a){this.rimRenderer(a,this.attr.startRho,true,false)},outerFrontRenderer:function(a){this.rimRenderer(a,this.attr.endRho,false,true)},outerBackRenderer:function(a){this.rimRenderer(a,this.attr.endRho,false,false)}});Ext.define("Ext.draw.PathUtil",function(){var a=Math.abs,c=Math.pow,e=Math.cos,b=Math.acos,d=Math.sqrt,f=Math.PI;return{singleton:true,requires:["Ext.draw.overrides.Path","Ext.draw.overrides.sprite.Path","Ext.draw.overrides.sprite.Instancing","Ext.draw.overrides.Surface"],cubicRoots:function(m){var z=m[0],x=m[1],w=m[2],v=m[3];if(z===0){return this.quadraticRoots(x,w,v)}var s=x/z,r=w/z,q=v/z,k=(3*r-c(s,2))/9,j=(9*s*r-27*q-2*c(s,3))/54,p=c(k,3)+c(j,2),n=[],h,g,o,l,u,y=Ext.Number.sign;if(p>=0){h=y(j+d(p))*c(a(j+d(p)),1/3);g=y(j-d(p))*c(a(j-d(p)),1/3);n[0]=-s/3+(h+g);n[1]=-s/3-(h+g)/2;n[2]=n[1];o=a(d(3)*(h-g)/2);if(o!==0){n[1]=-1;n[2]=-1}}else{l=b(j/d(-c(k,3)));n[0]=2*d(-k)*e(l/3)-s/3;n[1]=2*d(-k)*e((l+2*f)/3)-s/3;n[2]=2*d(-k)*e((l+4*f)/3)-s/3}for(u=0;u<3;u++){if(n[u]<0||n[u]>1){n[u]=-1}}return n},quadraticRoots:function(h,g,n){var m,l,k,j;if(h===0){return this.linearRoot(g,n)}m=g*g-4*h*n;if(m===0){k=[-g/(2*h)]}else{if(m>0){l=d(m);k=[(-g-l)/(2*h),(-g+l)/(2*h)]}else{return[]}}for(j=0;j1){k[j]=-1}}return k},linearRoot:function(h,g){var i=-g/h;if(h===0||i<0||i>1){return[]}return[i]},bezierCoeffs:function(h,g,k,j){var i=[];i[0]=-h+3*g-3*k+j;i[1]=3*h-6*g+3*k;i[2]=-3*h+3*g;i[3]=h;return i},cubicLineIntersections:function(I,G,F,E,l,k,j,h,M,p,K,n){var u=[],N=[],D=p-n,z=K-M,y=M*(n-p)-p*(K-M),L=this.bezierCoeffs(I,G,F,E),J=this.bezierCoeffs(l,k,j,h),H,x,w,v,g,q,o,m;u[0]=D*L[0]+z*J[0];u[1]=D*L[1]+z*J[1];u[2]=D*L[2]+z*J[2];u[3]=D*L[3]+z*J[3]+y;x=this.cubicRoots(u);for(H=0;H1){continue}g=v*v;q=g*v;o=L[0]*q+L[1]*g+L[2]*v+L[3];m=J[0]*q+J[1]*g+J[2]*v+J[3];if((K-M)!==0){w=(o-M)/(K-M)}else{w=(m-p)/(n-p)}if(!(w<0||w>1)){N.push([o,m])}}return N},splitCubic:function(g,q,p,o,m){var j=m*m,n=m*j,i=m-1,h=i*i,k=i*h,l=n*o-3*j*i*p+3*m*h*q-k*g;return[[g,m*q-i*g,j*p-2*m*i*q+h*g,l],[l,j*o-2*m*i*p+h*q,m*o-i*p,o]]},cubicDimension:function(p,o,l,k){var j=3*(-p+3*(o-l)+k),i=6*(p-2*o+l),h=-3*(p-o),q,n,g=Math.min(p,k),m=Math.max(p,k),r;if(j===0){if(i===0){return[g,m]}else{q=-h/i;if(0=0){r=d(r);q=(r-i)/2/j;if(00){q-=r/j;if(0n[1]||x[1]s[1]||B[1]=0&&i<=1&&g>=0&&g<=1){return[k+i*(j-k),p+i*(o-p)]}return null},pointOnLine:function(j,m,h,l,g,n){var k,i;if(a(h-j)1){return false}return a(m+k*(l-m)-n)<4},pointOnCubic:function(w,u,s,r,l,k,h,g,p,o){var C=this,B=C.bezierCoeffs(w,u,s,r),A=C.bezierCoeffs(l,k,h,g),z,v,n,m,q;B[3]-=p;A[3]-=o;n=C.cubicRoots(B);m=C.cubicRoots(A);for(z=0;z=0&&q<=1&&a(q-m[v])<0.05){return true}}}return false}}});Ext.define("Ext.chart.series.Pie3D",{extend:"Ext.chart.series.Polar",requires:["Ext.chart.series.sprite.Pie3DPart","Ext.draw.PathUtil"],type:"pie3d",seriesType:"pie3d",alias:"series.pie3d",isPie3D:true,config:{rect:[0,0,0,0],thickness:35,distortion:0.5,donut:false,hidden:[],highlightCfg:{margin:20},shadow:false},rotationOffset:-Math.PI/2,setField:function(a){return this.setXField(a)},getField:function(){return this.getXField()},updateRotation:function(a){this.setStyle({baseRotation:a+this.rotationOffset});this.doUpdateStyles()},updateDistortion:function(){this.setRadius()},updateThickness:function(){this.setRadius()},updateColors:function(a){this.setSubStyle({baseColor:a})},applyShadow:function(a){if(a===true){a={shadowColor:"rgba(0,0,0,0.8)",shadowBlur:30}}else{if(!Ext.isObject(a)){a={shadowColor:Ext.draw.Color.RGBA_NONE}}}return a},updateShadow:function(g){var e=this,f=e.getSprites(),d=e.spritesPerSlice,c=f&&f.length,b,a;for(b=1;b=s.length){s[r]=false}}s.length=d;if(c===0){return}h=2*Math.PI/c;for(r=0;ra/2){return a/(f.getDistortion()*2)}else{return g}},getSprites:function(){var y=this,e=y.getStore();if(!e){return[]}var n=y.getChart(),p=y.getSurface(),t=e.getData().items,l=y.spritesPerSlice,a=t.length,v=y.getAnimation()||n&&n.getAnimation(),x=y.getCenter(),w=y.getOffsetX(),u=y.getOffsetY(),b=y.getRadius(),q=y.getRotation(),d=y.getHighlight(),c={centerX:x[0]+w,centerY:x[1]+u-y.getThickness()/2,endRho:b,startRho:b*y.getDonut()/100,thickness:y.getThickness(),distortion:y.getDistortion()},k=y.sprites,h=y.getLabel(),f=h.getTemplate(),m,g,o,s,r;for(s=0;s=0;c--){a=b.get(c);d=a.hitTestEvent(f);if(d){return d}}return null},handleEvent:function(f){var d=this,b=d.drawContainer,g=f.type in d.mouseMoveEvents,a=d.lastSprite,c;if(g&&!d.hasSpriteMouseMoveListeners()){return}c=d.hitTestEvent(f);if(g&&!Ext.Object.equals(c,a)){if(a){b.fireEvent("spritemouseout",a,f)}if(c){b.fireEvent("spritemouseover",c,f)}}if(c){b.fireEvent("sprite"+f.type,c,f)}d.lastSprite=c}});Ext.define("Ext.chart.TipSurface",{extend:"Ext.draw.Container",spriteArray:false,renderFirst:true,constructor:function(a){this.callParent([a]);if(a.sprites){this.spriteArray=[].concat(a.sprites);delete a.sprites}},onRender:function(){var c=this,b=0,a=0,d,e;this.callParent(arguments);e=c.spriteArray;if(c.renderFirst&&e){c.renderFirst=false;for(a=e.length;b 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"); - }, - - format_duration_long: function(ut) { - - var days = Math.floor(ut / 86400); - ut -= days*86400; - var hours = Math.floor(ut / 3600); - ut -= hours*3600; - var mins = Math.floor(ut / 60); - ut -= mins*60; - - var hours_str = '00' + hours.toString(); - hours_str = hours_str.substr(hours_str.length - 2); - var mins_str = "00" + mins.toString(); - mins_str = mins_str.substr(mins_str.length - 2); - var ut_str = "00" + ut.toString(); - ut_str = ut_str.substr(ut_str.length - 2); - - if (days) { - var 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; } - - var tm = new Ext.util.TextMetrics(); - var 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; - } - return (Proxmox.UserName !== '') && Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name); - }, - - authClear: function() { - if (Proxmox.LoggedOut) { - return undefined; - } - 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) { - var el = comp.el; - if (!el) { - return; - } - if (!msg) { - el.unmask(); - } else { - if (msg === true) { - el.mask(gettext("Loading...")); - } else { - el.mask(msg); - } - } - }, - - monStoreErrors: function(me, store, clearMaskBeforeLoad) { - if (clearMaskBeforeLoad) { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - Proxmox.Utils.setErrorMask(me, false); - }); - } else { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - if (!me.loadCount) { - me.loadCount = 0; // make sure it is numeric - Proxmox.Utils.setErrorMask(me, true); - } - }); - } - - // only works with 'proxmox' proxy - me.mon(store.proxy, 'afterload', function(proxy, request, success) { - me.loadCount++; - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - - var msg; - /*jslint nomen: true */ - var operation = request._operation; - var error = operation.getError(); - if (error.statusText) { - msg = error.statusText + ' (' + error.status + ')'; - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - extractRequestError: function(result, verbose) { - var 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) { - - var newopts = Ext.apply({ - waitMsg: gettext('Please wait...') - }, reqOpts); - - if (!newopts.url.match(/^\/api2/)) { - newopts.url = '/api2/extjs' + newopts.url; - } - delete newopts.callback; - - var 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); - } - } - var 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) {} - var 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); - - var 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', - //waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data; - - if (data.status !== 'Active') { - Ext.Msg.show({ - title: gettext('No valid subscription'), - icon: Ext.Msg.WARNING, - msg: 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)) { - Ext.Object.each(data, function(name, val) { - if (values.hasOwnProperty(name)) { - var 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; - } - }); - } - }, - - 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') ], - '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') ], - 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') ], - vzsuspend: [ 'CT', gettext('Suspend') ], - vzresume: [ 'CT', gettext('Resume') ], - hamigrate: [ 'HA', gettext('Migrate') ], - hastart: [ 'HA', gettext('Start') ], - hastop: [ 'HA', gettext('Stop') ], - 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: ['VM/CT', gettext('Backup') ], - 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') ] - }, - - format_task_description: function(type, id) { - var farray = Proxmox.Utils.task_desc_table[type]; - var text; - if (!farray) { - text = type; - if (id) { - type += ' ' + id; - } - return text; - } - var prefix = farray[0]; - text = farray[1]; - if (prefix) { - return prefix + ' ' + id + ' - ' + text; - } - return text; - }, - - format_size: function(size) { - /*jslint confusion: true */ - - var units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; - var num = 0; - - while (size >= 1024 && ((num++)+1) < units.length) { - size = size / 1024; - } - - return size.toFixed((num > 0)?2:0) + " " + units[num] + "B"; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - render_uptime: function(value) { - - var uptime = value; - - if (uptime === undefined) { - return ''; - } - - if (uptime <= 0) { - return '-'; - } - - return Proxmox.Utils.format_duration_long(uptime); - }, - - parse_task_upid: function(upid) { - var task = {}; - - var res = upid.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):([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); - task.starttime = parseInt(res[4], 16); - task.type = res[5]; - task.id = res[6]; - task.user = res[7]; - - task.desc = Proxmox.Utils.format_task_description(task.type, task.id); - - return task; - }, - - render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) { - var servertime = new Date(value * 1000); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }, - - get_help_info: function(section) { - var helpMap; - if (typeof proxmoxOnlineHelpInfo !== 'undefined') { - helpMap = proxmoxOnlineHelpInfo; - } else if (typeof pveOnlineHelpInfo !== 'undefined') { - // be backward compatible with older pve-doc-generators - helpMap = pveOnlineHelpInfo; - } else { - throw "no global OnlineHelpInfo map declared"; - } - - return helpMap[section]; - }, - - get_help_link: function(section) { - var info = Proxmox.Utils.get_help_info(section); - if (!info) { - return; - } - - return window.location.origin + info.link; - }, - - openXtermJsViewer: function(vmtype, vmid, nodename, vmname, cmd) { - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - xtermjs: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - cmd: cmd, - - }); - var 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() { - var me = this; - Ext.apply(me, me.utilities); - - var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])"; - var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")"; - var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})"; - var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")"; - var IPV4_CIDR_MASK = "([0-9]{1,2})"; - var 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 + "$"); - - var 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 + ")?::" + ")" + ")" + - ")"; - - 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 + ")$"); - - var 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+)?$"); - } -}); -// 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] <= 128); - }, - IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "
" + gettext('Valid CIDR Range') + ': 8-128', - 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\.:]/, - - IP64CIDRAddress: function(v) { - var result = Proxmox.Utils.IP64_cidr_match.exec(v); - if (result === null) { - return false; - } - if (result[1] !== undefined) { - return result[1] >= 8 && result[1] <= 128; - } else if (result[2] !== undefined) { - return result[2] >= 8 && result[2] <= 32; - } else { - return false; - } - }, - IP64CIDRAddressText: gettext('Example') + ': 192.168.1.1/24 2001:DB8::42/64', - IP64CIDRAddressMask: /[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', - - MacPrefix: function(v) { - return (/^[a-f0-9][02468ace](?::[a-f0-9]{2}){0,2}:?$/i).test(v); - }, - MacPrefixMask: /[a-fA-F0-9:]/, - MacPrefixText: gettext('Example') + ': 02:8f - ' + gettext('only unicast addresses are allowed'), - - 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'", - - 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", - - DnsOrIp: function(v) { - if (!Proxmox.Utils.DnsName_match.test(v) && - !Proxmox.Utils.IP64_match.test(v)) { - return false; - } - - return true; - }, - DnsOrIpText: gettext('Not a valid DNS name or IP address.'), - - 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'), - - password: function(val, field) { - if (field.initialPassField) { - var pwd = field.up('form').down( - '[name=' + field.initialPassField + ']'); - return (val == pwd.getValue()); - } - return true; - }, - - passwordText: gettext('Passwords do not match') -}); - -// Firefox 52+ Touchscreen bug -// see https://www.sencha.com/forum/showthread.php?336762-Examples-don-t-work-in-Firefox-52-touchscreen/page2 -// and https://bugzilla.proxmox.com/show_bug.cgi?id=1223 -Ext.define('EXTJS_23846.Element', { - override: 'Ext.dom.Element' -}, function(Element) { - var supports = Ext.supports, - proto = Element.prototype, - eventMap = proto.eventMap, - additiveEvents = proto.additiveEvents; - - if (Ext.os.is.Desktop && supports.TouchEvents && !supports.PointerEvents) { - eventMap.touchstart = 'mousedown'; - eventMap.touchmove = 'mousemove'; - eventMap.touchend = 'mouseup'; - eventMap.touchcancel = 'mouseup'; - - additiveEvents.mousedown = 'mousedown'; - additiveEvents.mousemove = 'mousemove'; - additiveEvents.mouseup = 'mouseup'; - additiveEvents.touchstart = 'touchstart'; - additiveEvents.touchmove = 'touchmove'; - additiveEvents.touchend = 'touchend'; - additiveEvents.touchcancel = 'touchcancel'; - - additiveEvents.pointerdown = 'mousedown'; - additiveEvents.pointermove = 'mousemove'; - additiveEvents.pointerup = 'mouseup'; - additiveEvents.pointercancel = 'mouseup'; - } -}); - -Ext.define('EXTJS_23846.Gesture', { - override: 'Ext.event.publisher.Gesture' -}, function(Gesture) { - var me = Gesture.instance; - - if (Ext.supports.TouchEvents && !Ext.isWebKit && Ext.os.is.Desktop) { - me.handledDomEvents.push('mousedown', 'mousemove', 'mouseup'); - me.registerEvents(); - } -}); - -Ext.define('EXTJS_18900.Pie', { - override: 'Ext.chart.series.Pie', - - // from 6.0.2 - betweenAngle: function (x, a, b) { - var pp = Math.PI * 2, - offset = this.rotationOffset; - - if (a === b) { - return false; - } - - if (!this.getClockwise()) { - x *= -1; - a *= -1; - b *= -1; - a -= offset; - b -= offset; - } else { - a += offset; - b += offset; - } - - x -= a; - b -= a; - - // Normalize, so that both x and b are in the [0,360) interval. - x %= pp; - b %= pp; - x += pp; - b += pp; - x %= pp; - b %= pp; - - // Because 360 * n angles will be normalized to 0, - // we need to treat b === 0 as a special case. - return x < b || b === 0; - }, -}); - -// 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 -// 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; - } -}); - -// '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('Proxmox.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 a grid/tree view, restoring the focus moves the view back to -// the previously focused item. Save scroll position before refocusing. -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('Proxmox.Datepicker', { - override: 'Ext.picker.Date', - hideMode: 'visibility' -}); - -// ExtJS 6.0.1 has no setSubmitValue() (although you find it in the docs). -// Note: this.submitValue is a boolean flag, whereas getSubmitValue() returns -// data to be submitted. -Ext.define('Proxmox.form.field.Text', { - override: 'Ext.form.field.Text', - - setSubmitValue: function(v) { - this.submitValue = v; - }, -}); - -// this should be fixed with ExtJS 6.0.2 -// make mousescrolling work in firefox in the containers overflowhandler -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. differs from the default onMouseWheel - // handler by using deltaY instead of wheelDeltaY and no normalizing, - // because it is already - onMouseWheelFirefox: function(e) { - e.stopEvent(); - var delta = e.browserEvent.deltaY || 0; - this.scrollBy(delta * this.wheelIncrement, false); - } - -}); - -// add '@' to the valid id -Ext.define('Proxmox.validIdReOverride', { - override: 'Ext.Component', - validIdRe: /^[a-z_][a-z0-9\-_\@]*$/i, -}); - -// 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); - } - } -}); -Ext.define('Proxmox.Mixin.CBind', { - extend: 'Ext.Mixin', - - mixinConfig: { - before: { - initComponent: 'cloneTemplates' - } - }, - - cloneTemplates: function() { - var me = this; - - if (typeof(me.cbindData) == "function") { - me.cbindData = me.cbindData(me.initialConfig) || {}; - } - - var getConfigValue = function(cname) { - - if (cname in me.initialConfig) { - return me.initialConfig[cname]; - } - if (cname in me.cbindData) { - return me.cbindData[cname]; - } - if (cname in me) { - return me[cname]; - } - throw "unable to get cbind data for '" + cname + "'"; - }; - - var applyCBind = function(obj) { - var cbind = obj.cbind, prop, cdata, cvalue, match, found; - if (!cbind) return; - - for (prop in cbind) { - cdata = cbind[prop]; - - found = false; - if (match = /^\{(!)?([a-z_][a-z0-9_]*)\}$/i.exec(cdata)) { - var cvalue = getConfigValue(match[2]); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else if (match = /^\{(!)?([a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)+)\}$/i.exec(cdata)) { - var keys = match[2].split('.'); - var cvalue = getConfigValue(keys.shift()); - keys.forEach(function(k) { - if (k in cvalue) { - cvalue = cvalue[k]; - } else { - throw "unable to get cbind data for '" + match[2] + "'"; - } - }); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else { - obj[prop] = cdata.replace(/{([a-z_][a-z0-9_]*)\}/ig, function(match, cname) { - var cvalue = getConfigValue(cname); - found = true; - return cvalue; - }); - } - if (!found) { - throw "unable to parse cbind template '" + cdata + "'"; - } - - } - }; - - if (me.cbind) { - applyCBind(me); - } - - var cloneTemplateArray = function(org) { - var copy, i, found, el, elcopy, arrayLength; - - arrayLength = org.length; - found = false; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - found = true; - break; - } - } - - if (!found) return org; // no need to copy - - copy = []; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - elcopy = cloneTemplateObject(el); - if (elcopy.cbind) { - applyCBind(elcopy); - } - copy.push(elcopy); - } else if (el.constructor == Array) { - elcopy = cloneTemplateArray(el); - copy.push(elcopy); - } else { - copy.push(el); - } - } - return copy; - }; - - var cloneTemplateObject = function(org) { - var res = {}, prop, el, copy; - for (prop in org) { - el = org[prop]; - if (el.constructor == Object && el.xtype) { - copy = cloneTemplateObject(el); - if (copy.cbind) { - applyCBind(copy); - } - res[prop] = copy; - } else if (el.constructor == Array) { - copy = cloneTemplateArray(el); - res[prop] = copy; - } else { - res[prop] = el; - } - } - return res; - }; - - var condCloneProperties = function() { - var prop, el, i, tmp; - - for (prop in me) { - el = me[prop]; - if (el === undefined || el === null) continue; - if (typeof(el) === 'object' && el.constructor == Object) { - if (el.xtype && prop != 'config') { - me[prop] = cloneTemplateObject(el); - } - } else if (el.constructor == Array) { - tmp = cloneTemplateArray(el); - me[prop] = tmp; - } - } - }; - - condCloneProperties(); - } -}); -/* A reader to store a single JSON Object (hash) into a storage. - * Also accepts an array containing a single hash. - * - * So it can read: - * - * example1: {data1: "xyz", data2: "abc"} - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * example2: [ {data1: "xyz", data2: "abc"} ] - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * If you set 'readArray', the reader expexts the object as array: - * - * example3: [ { key: "data1", value: "xyz", p2: "cde" }, { key: "data2", value: "abc", p2: "efg" }] - * returns [{key: "data1", value: "xyz", p2: "cde}, {key: "data2", value: "abc", p2: "efg"}] - * - * Note: The records can contain additional properties (like 'p2' above) when you use 'readArray' - * - * Additional feature: specify allowed properties with default values with 'rows' object - * - * var rows = { - * memory: { - * required: true, - * defaultValue: 512 - * } - * } - * - */ - -Ext.define('Proxmox.data.reader.JsonObject', { - extend: 'Ext.data.reader.Json', - alias : 'reader.jsonobject', - - readArray: false, - - rows: undefined, - - constructor: function(config) { - var me = this; - - Ext.apply(me, config || {}); - - me.callParent([config]); - }, - - getResponseData: function(response) { - var me = this; - - var data = []; - try { - var result = Ext.decode(response.responseText); - // get our data items inside the server response - var root = result[me.getRootProperty()]; - - if (me.readArray) { - - var rec_hash = {}; - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - rec_hash[rec.key] = rec; - } - }); - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - var rec = rec_hash[key]; - if (Ext.isDefined(rec)) { - if (!Ext.isDefined(rec.value)) { - rec.value = rowdef.defaultValue; - } - data.push(rec); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue} ); - } else if (rowdef.required) { - data.push({key: key, value: undefined }); - } - }); - } else { - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - data.push(rec); - } - }); - } - - } else { - - var org_root = root; - - if (Ext.isArray(org_root)) { - if (root.length == 1) { - root = org_root[0]; - } else { - root = {}; - } - } - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - if (Ext.isDefined(root[key])) { - data.push({key: key, value: root[key]}); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue}); - } else if (rowdef.required) { - data.push({key: key, value: undefined}); - } - }); - } else { - Ext.Object.each(root, function(key, value) { - data.push({key: key, value: value }); - }); - } - } - } - catch (ex) { - Ext.Error.raise({ - response: response, - json: response.responseText, - parseError: ex, - msg: 'Unable to parse the JSON returned by the server: ' + ex.toString() - }); - } - - return data; - } -}); - -Ext.define('Proxmox.RestProxy', { - extend: 'Ext.data.RestProxy', - alias : 'proxy.proxmox', - - pageParam : null, - startParam: null, - limitParam: null, - groupParam: null, - sortParam: null, - filterParam: null, - noCache : false, - - afterRequest: function(request, success) { - this.fireEvent('afterload', this, request, success); - return; - }, - - constructor: function(config) { - - Ext.applyIf(config, { - reader: { - type: 'json', - rootProperty: config.root || 'data' - } - }); - - this.callParent([config]); - } -}, function() { - - Ext.define('KeyValue', { - extend: "Ext.data.Model", - fields: [ 'key', 'value' ], - idProperty: 'key' - }); - - Ext.define('KeyValuePendingDelete', { - extend: "Ext.data.Model", - fields: [ 'key', 'value', 'pending', 'delete' ], - idProperty: 'key' - }); - - Ext.define('proxmox-tasks', { - extend: 'Ext.data.Model', - fields: [ - { name: 'starttime', type : 'date', dateFormat: 'timestamp' }, - { name: 'endtime', type : 'date', dateFormat: 'timestamp' }, - { name: 'pid', type: 'int' }, - 'node', 'upid', 'user', 'status', 'type', 'id' - ], - idProperty: 'upid' - }); - - Ext.define('proxmox-cluster-log', { - extend: 'Ext.data.Model', - fields: [ - { name: 'uid' , type: 'int' }, - { name: 'time', type : 'date', dateFormat: 'timestamp' }, - { name: 'pri', type: 'int' }, - { name: 'pid', type: 'int' }, - 'node', 'user', 'tag', 'msg', - { - name: 'id', - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - // compute unique ID - return info.uid + ':' + info.node; - } - } - ], - idProperty: 'id' - }); - -}); -/* Extends the Ext.data.Store type - * with startUpdate() and stopUpdate() methods - * to refresh the store data in the background - * Components using this store directly will flicker - * due to the redisplay of the element ater 'config.interval' ms - * - * Note that you have to call yourself startUpdate() for the background load - * to begin - */ -Ext.define('Proxmox.data.UpdateStore', { - extend: 'Ext.data.Store', - alias: 'store.update', - - isStopped: true, - - autoStart: false, - - destroy: function() { - var me = this; - me.stopUpdate(); - me.callParent(); - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.interval) { - config.interval = 3000; - } - - if (!config.storeid) { - throw "no storeid specified"; - } - - var load_task = new Ext.util.DelayedTask(); - - var run_load_task = function() { - if (me.isStopped) { - return; - } - - if (Proxmox.Utils.authOK()) { - var start = new Date(); - me.load(function() { - var runtime = (new Date()) - start; - var interval = config.interval + runtime*2; - load_task.delay(interval, run_load_task); - }); - } else { - load_task.delay(200, run_load_task); - } - }; - - Ext.apply(config, { - startUpdate: function() { - me.isStopped = false; - // run_load_task(); this makes problems with chrome - load_task.delay(1, run_load_task); - }, - stopUpdate: function() { - me.isStopped = true; - load_task.cancel(); - } - }); - - me.callParent([config]); - - me.load_task = load_task; - - if (me.autoStart) { - me.startUpdate(); - } - } -}); -/* - * The DiffStore is a in-memory store acting as proxy between a real store - * instance and a component. - * Its purpose is to redisplay the component *only* if the data has been changed - * inside the real store, to avoid the annoying visual flickering of using - * the real store directly. - * - * Implementation: - * The DiffStore monitors via mon() the 'load' events sent by the real store. - * On each 'load' event, the DiffStore compares its own content with the target - * store (call to cond_add_item()) and then fires a 'refresh' event. - * The 'refresh' event will automatically trigger a view refresh on the component - * who binds to this store. - */ - -/* Config properties: - * rstore: the realstore which will autorefresh its content from the API - * Only works if rstore has a model and use 'idProperty' - * sortAfterUpdate: sort the diffstore before rendering the view - */ -Ext.define('Proxmox.data.DiffStore', { - extend: 'Ext.data.Store', - alias: 'store.diff', - - sortAfterUpdate: false, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.rstore) { - throw "no rstore specified"; - } - - if (!config.rstore.model) { - throw "no rstore model specified"; - } - - var rstore = config.rstore; - - Ext.apply(config, { - model: rstore.model, - proxy: { type: 'memory' } - }); - - me.callParent([config]); - - var first_load = true; - - var cond_add_item = function(data, id) { - var olditem = me.getById(id); - if (olditem) { - olditem.beginEdit(); - Ext.Array.each(me.model.prototype.fields, function(field) { - if (olditem.data[field.name] !== data[field.name]) { - olditem.set(field.name, data[field.name]); - } - }); - olditem.endEdit(true); - olditem.commit(); - } else { - var newrec = Ext.create(me.model, data); - var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length; - me.insert(pos, newrec); - } - }; - - var loadFn = function(s, records, success) { - - if (!success) { - return; - } - - me.suspendEvents(); - - // getSource returns null if data is not filtered - // if it is filtered it returns all records - var allItems = me.getData().getSource() || me.getData(); - - // remove vanished items - allItems.each(function(olditem) { - var item = rstore.getById(olditem.getId()); - if (!item) { - me.remove(olditem); - } - }); - - rstore.each(function(item) { - cond_add_item(item.data, item.getId()); - }); - - me.filter(); - - if (me.sortAfterUpdate) { - me.sort(); - } - - first_load = false; - - me.resumeEvents(); - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - }; - - if (rstore.isLoaded()) { - // if store is already loaded, - // insert items instantly - loadFn(rstore, [], true); - } - - me.mon(rstore, 'load', loadFn); - } -}); -/* This store encapsulates data items which are organized as an Array of key-values Objects - * ie data[0] contains something like {key: "keyboard", value: "da"} -* -* Designed to work with the KeyValue model and the JsonObject data reader -*/ -Ext.define('Proxmox.data.ObjectStore', { - extend: 'Proxmox.data.UpdateStore', - - getRecord: function() { - var me = this; - var record = Ext.create('Ext.data.Model'); - me.getData().each(function(item) { - record.set(item.data.key, item.data.value); - }); - record.commit(true); - return record; - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.storeid) { - config.storeid = 'proxmox-store-' + (++Ext.idSeed); - } - - Ext.applyIf(config, { - model: 'KeyValue', - proxy: { - type: 'proxmox', - url: config.url, - extraParams: config.extraParams, - reader: { - type: 'jsonobject', - rows: config.rows, - readArray: config.readArray, - rootProperty: config.root || 'data' - } - } - }); - - me.callParent([config]); - } -}); -/* Extends the Proxmox.data.UpdateStore type - * - * - */ -Ext.define('Proxmox.data.RRDStore', { - extend: 'Proxmox.data.UpdateStore', - alias: 'store.proxmoxRRDStore', - - setRRDUrl: function(timeframe, cf) { - var me = this; - if (!timeframe) { - timeframe = me.timeframe; - } - - if (!cf) { - cf = me.cf; - } - - me.proxy.url = me.rrdurl + "?timeframe=" + timeframe + "&cf=" + cf; - }, - - proxy: { - type: 'proxmox' - }, - - timeframe: 'hour', - - cf: 'AVERAGE', - - constructor: function(config) { - var me = this; - - config = config || {}; - - // set default interval to 30seconds - if (!config.interval) { - config.interval = 30000; - } - - // set a new storeid - if (!config.storeid) { - config.storeid = 'rrdstore-' + (++Ext.idSeed); - } - - // rrdurl is required - if (!config.rrdurl) { - throw "no rrdurl specified"; - } - - var stateid = 'proxmoxRRDTypeSelection'; - var sp = Ext.state.Manager.getProvider(); - var stateinit = sp.get(stateid); - - if (stateinit) { - if(stateinit.timeframe !== me.timeframe || stateinit.cf !== me.rrdcffn){ - me.timeframe = stateinit.timeframe; - me.rrdcffn = stateinit.cf; - } - } - - me.callParent([config]); - - me.setRRDUrl(); - me.mon(sp, 'statechange', function(prov, key, state){ - if (key === stateid) { - if (state && state.id) { - if (state.timeframe !== me.timeframe || state.cf !== me.cf) { - me.timeframe = state.timeframe; - me.cf = state.cf; - me.setRRDUrl(); - me.reload(); - } - } - } - }); - } -}); -Ext.define('Timezone', { - extend: 'Ext.data.Model', - fields: ['zone'] -}); - -Ext.define('Proxmox.data.TimezoneStore', { - extend: 'Ext.data.Store', - model: 'Timezone', - data: [ - ['Africa/Abidjan'], - ['Africa/Accra'], - ['Africa/Addis_Ababa'], - ['Africa/Algiers'], - ['Africa/Asmara'], - ['Africa/Bamako'], - ['Africa/Bangui'], - ['Africa/Banjul'], - ['Africa/Bissau'], - ['Africa/Blantyre'], - ['Africa/Brazzaville'], - ['Africa/Bujumbura'], - ['Africa/Cairo'], - ['Africa/Casablanca'], - ['Africa/Ceuta'], - ['Africa/Conakry'], - ['Africa/Dakar'], - ['Africa/Dar_es_Salaam'], - ['Africa/Djibouti'], - ['Africa/Douala'], - ['Africa/El_Aaiun'], - ['Africa/Freetown'], - ['Africa/Gaborone'], - ['Africa/Harare'], - ['Africa/Johannesburg'], - ['Africa/Kampala'], - ['Africa/Khartoum'], - ['Africa/Kigali'], - ['Africa/Kinshasa'], - ['Africa/Lagos'], - ['Africa/Libreville'], - ['Africa/Lome'], - ['Africa/Luanda'], - ['Africa/Lubumbashi'], - ['Africa/Lusaka'], - ['Africa/Malabo'], - ['Africa/Maputo'], - ['Africa/Maseru'], - ['Africa/Mbabane'], - ['Africa/Mogadishu'], - ['Africa/Monrovia'], - ['Africa/Nairobi'], - ['Africa/Ndjamena'], - ['Africa/Niamey'], - ['Africa/Nouakchott'], - ['Africa/Ouagadougou'], - ['Africa/Porto-Novo'], - ['Africa/Sao_Tome'], - ['Africa/Tripoli'], - ['Africa/Tunis'], - ['Africa/Windhoek'], - ['America/Adak'], - ['America/Anchorage'], - ['America/Anguilla'], - ['America/Antigua'], - ['America/Araguaina'], - ['America/Argentina/Buenos_Aires'], - ['America/Argentina/Catamarca'], - ['America/Argentina/Cordoba'], - ['America/Argentina/Jujuy'], - ['America/Argentina/La_Rioja'], - ['America/Argentina/Mendoza'], - ['America/Argentina/Rio_Gallegos'], - ['America/Argentina/Salta'], - ['America/Argentina/San_Juan'], - ['America/Argentina/San_Luis'], - ['America/Argentina/Tucuman'], - ['America/Argentina/Ushuaia'], - ['America/Aruba'], - ['America/Asuncion'], - ['America/Atikokan'], - ['America/Bahia'], - ['America/Bahia_Banderas'], - ['America/Barbados'], - ['America/Belem'], - ['America/Belize'], - ['America/Blanc-Sablon'], - ['America/Boa_Vista'], - ['America/Bogota'], - ['America/Boise'], - ['America/Cambridge_Bay'], - ['America/Campo_Grande'], - ['America/Cancun'], - ['America/Caracas'], - ['America/Cayenne'], - ['America/Cayman'], - ['America/Chicago'], - ['America/Chihuahua'], - ['America/Costa_Rica'], - ['America/Cuiaba'], - ['America/Curacao'], - ['America/Danmarkshavn'], - ['America/Dawson'], - ['America/Dawson_Creek'], - ['America/Denver'], - ['America/Detroit'], - ['America/Dominica'], - ['America/Edmonton'], - ['America/Eirunepe'], - ['America/El_Salvador'], - ['America/Fortaleza'], - ['America/Glace_Bay'], - ['America/Godthab'], - ['America/Goose_Bay'], - ['America/Grand_Turk'], - ['America/Grenada'], - ['America/Guadeloupe'], - ['America/Guatemala'], - ['America/Guayaquil'], - ['America/Guyana'], - ['America/Halifax'], - ['America/Havana'], - ['America/Hermosillo'], - ['America/Indiana/Indianapolis'], - ['America/Indiana/Knox'], - ['America/Indiana/Marengo'], - ['America/Indiana/Petersburg'], - ['America/Indiana/Tell_City'], - ['America/Indiana/Vevay'], - ['America/Indiana/Vincennes'], - ['America/Indiana/Winamac'], - ['America/Inuvik'], - ['America/Iqaluit'], - ['America/Jamaica'], - ['America/Juneau'], - ['America/Kentucky/Louisville'], - ['America/Kentucky/Monticello'], - ['America/La_Paz'], - ['America/Lima'], - ['America/Los_Angeles'], - ['America/Maceio'], - ['America/Managua'], - ['America/Manaus'], - ['America/Marigot'], - ['America/Martinique'], - ['America/Matamoros'], - ['America/Mazatlan'], - ['America/Menominee'], - ['America/Merida'], - ['America/Mexico_City'], - ['America/Miquelon'], - ['America/Moncton'], - ['America/Monterrey'], - ['America/Montevideo'], - ['America/Montreal'], - ['America/Montserrat'], - ['America/Nassau'], - ['America/New_York'], - ['America/Nipigon'], - ['America/Nome'], - ['America/Noronha'], - ['America/North_Dakota/Center'], - ['America/North_Dakota/New_Salem'], - ['America/Ojinaga'], - ['America/Panama'], - ['America/Pangnirtung'], - ['America/Paramaribo'], - ['America/Phoenix'], - ['America/Port-au-Prince'], - ['America/Port_of_Spain'], - ['America/Porto_Velho'], - ['America/Puerto_Rico'], - ['America/Rainy_River'], - ['America/Rankin_Inlet'], - ['America/Recife'], - ['America/Regina'], - ['America/Resolute'], - ['America/Rio_Branco'], - ['America/Santa_Isabel'], - ['America/Santarem'], - ['America/Santiago'], - ['America/Santo_Domingo'], - ['America/Sao_Paulo'], - ['America/Scoresbysund'], - ['America/Shiprock'], - ['America/St_Barthelemy'], - ['America/St_Johns'], - ['America/St_Kitts'], - ['America/St_Lucia'], - ['America/St_Thomas'], - ['America/St_Vincent'], - ['America/Swift_Current'], - ['America/Tegucigalpa'], - ['America/Thule'], - ['America/Thunder_Bay'], - ['America/Tijuana'], - ['America/Toronto'], - ['America/Tortola'], - ['America/Vancouver'], - ['America/Whitehorse'], - ['America/Winnipeg'], - ['America/Yakutat'], - ['America/Yellowknife'], - ['Antarctica/Casey'], - ['Antarctica/Davis'], - ['Antarctica/DumontDUrville'], - ['Antarctica/Macquarie'], - ['Antarctica/Mawson'], - ['Antarctica/McMurdo'], - ['Antarctica/Palmer'], - ['Antarctica/Rothera'], - ['Antarctica/South_Pole'], - ['Antarctica/Syowa'], - ['Antarctica/Vostok'], - ['Arctic/Longyearbyen'], - ['Asia/Aden'], - ['Asia/Almaty'], - ['Asia/Amman'], - ['Asia/Anadyr'], - ['Asia/Aqtau'], - ['Asia/Aqtobe'], - ['Asia/Ashgabat'], - ['Asia/Baghdad'], - ['Asia/Bahrain'], - ['Asia/Baku'], - ['Asia/Bangkok'], - ['Asia/Beirut'], - ['Asia/Bishkek'], - ['Asia/Brunei'], - ['Asia/Choibalsan'], - ['Asia/Chongqing'], - ['Asia/Colombo'], - ['Asia/Damascus'], - ['Asia/Dhaka'], - ['Asia/Dili'], - ['Asia/Dubai'], - ['Asia/Dushanbe'], - ['Asia/Gaza'], - ['Asia/Harbin'], - ['Asia/Ho_Chi_Minh'], - ['Asia/Hong_Kong'], - ['Asia/Hovd'], - ['Asia/Irkutsk'], - ['Asia/Jakarta'], - ['Asia/Jayapura'], - ['Asia/Jerusalem'], - ['Asia/Kabul'], - ['Asia/Kamchatka'], - ['Asia/Karachi'], - ['Asia/Kashgar'], - ['Asia/Kathmandu'], - ['Asia/Kolkata'], - ['Asia/Krasnoyarsk'], - ['Asia/Kuala_Lumpur'], - ['Asia/Kuching'], - ['Asia/Kuwait'], - ['Asia/Macau'], - ['Asia/Magadan'], - ['Asia/Makassar'], - ['Asia/Manila'], - ['Asia/Muscat'], - ['Asia/Nicosia'], - ['Asia/Novokuznetsk'], - ['Asia/Novosibirsk'], - ['Asia/Omsk'], - ['Asia/Oral'], - ['Asia/Phnom_Penh'], - ['Asia/Pontianak'], - ['Asia/Pyongyang'], - ['Asia/Qatar'], - ['Asia/Qyzylorda'], - ['Asia/Rangoon'], - ['Asia/Riyadh'], - ['Asia/Sakhalin'], - ['Asia/Samarkand'], - ['Asia/Seoul'], - ['Asia/Shanghai'], - ['Asia/Singapore'], - ['Asia/Taipei'], - ['Asia/Tashkent'], - ['Asia/Tbilisi'], - ['Asia/Tehran'], - ['Asia/Thimphu'], - ['Asia/Tokyo'], - ['Asia/Ulaanbaatar'], - ['Asia/Urumqi'], - ['Asia/Vientiane'], - ['Asia/Vladivostok'], - ['Asia/Yakutsk'], - ['Asia/Yekaterinburg'], - ['Asia/Yerevan'], - ['Atlantic/Azores'], - ['Atlantic/Bermuda'], - ['Atlantic/Canary'], - ['Atlantic/Cape_Verde'], - ['Atlantic/Faroe'], - ['Atlantic/Madeira'], - ['Atlantic/Reykjavik'], - ['Atlantic/South_Georgia'], - ['Atlantic/St_Helena'], - ['Atlantic/Stanley'], - ['Australia/Adelaide'], - ['Australia/Brisbane'], - ['Australia/Broken_Hill'], - ['Australia/Currie'], - ['Australia/Darwin'], - ['Australia/Eucla'], - ['Australia/Hobart'], - ['Australia/Lindeman'], - ['Australia/Lord_Howe'], - ['Australia/Melbourne'], - ['Australia/Perth'], - ['Australia/Sydney'], - ['Europe/Amsterdam'], - ['Europe/Andorra'], - ['Europe/Athens'], - ['Europe/Belgrade'], - ['Europe/Berlin'], - ['Europe/Bratislava'], - ['Europe/Brussels'], - ['Europe/Bucharest'], - ['Europe/Budapest'], - ['Europe/Chisinau'], - ['Europe/Copenhagen'], - ['Europe/Dublin'], - ['Europe/Gibraltar'], - ['Europe/Guernsey'], - ['Europe/Helsinki'], - ['Europe/Isle_of_Man'], - ['Europe/Istanbul'], - ['Europe/Jersey'], - ['Europe/Kaliningrad'], - ['Europe/Kiev'], - ['Europe/Lisbon'], - ['Europe/Ljubljana'], - ['Europe/London'], - ['Europe/Luxembourg'], - ['Europe/Madrid'], - ['Europe/Malta'], - ['Europe/Mariehamn'], - ['Europe/Minsk'], - ['Europe/Monaco'], - ['Europe/Moscow'], - ['Europe/Oslo'], - ['Europe/Paris'], - ['Europe/Podgorica'], - ['Europe/Prague'], - ['Europe/Riga'], - ['Europe/Rome'], - ['Europe/Samara'], - ['Europe/San_Marino'], - ['Europe/Sarajevo'], - ['Europe/Simferopol'], - ['Europe/Skopje'], - ['Europe/Sofia'], - ['Europe/Stockholm'], - ['Europe/Tallinn'], - ['Europe/Tirane'], - ['Europe/Uzhgorod'], - ['Europe/Vaduz'], - ['Europe/Vatican'], - ['Europe/Vienna'], - ['Europe/Vilnius'], - ['Europe/Volgograd'], - ['Europe/Warsaw'], - ['Europe/Zagreb'], - ['Europe/Zaporozhye'], - ['Europe/Zurich'], - ['Indian/Antananarivo'], - ['Indian/Chagos'], - ['Indian/Christmas'], - ['Indian/Cocos'], - ['Indian/Comoro'], - ['Indian/Kerguelen'], - ['Indian/Mahe'], - ['Indian/Maldives'], - ['Indian/Mauritius'], - ['Indian/Mayotte'], - ['Indian/Reunion'], - ['Pacific/Apia'], - ['Pacific/Auckland'], - ['Pacific/Chatham'], - ['Pacific/Chuuk'], - ['Pacific/Easter'], - ['Pacific/Efate'], - ['Pacific/Enderbury'], - ['Pacific/Fakaofo'], - ['Pacific/Fiji'], - ['Pacific/Funafuti'], - ['Pacific/Galapagos'], - ['Pacific/Gambier'], - ['Pacific/Guadalcanal'], - ['Pacific/Guam'], - ['Pacific/Honolulu'], - ['Pacific/Johnston'], - ['Pacific/Kiritimati'], - ['Pacific/Kosrae'], - ['Pacific/Kwajalein'], - ['Pacific/Majuro'], - ['Pacific/Marquesas'], - ['Pacific/Midway'], - ['Pacific/Nauru'], - ['Pacific/Niue'], - ['Pacific/Norfolk'], - ['Pacific/Noumea'], - ['Pacific/Pago_Pago'], - ['Pacific/Palau'], - ['Pacific/Pitcairn'], - ['Pacific/Pohnpei'], - ['Pacific/Port_Moresby'], - ['Pacific/Rarotonga'], - ['Pacific/Saipan'], - ['Pacific/Tahiti'], - ['Pacific/Tarawa'], - ['Pacific/Tongatapu'], - ['Pacific/Wake'], - ['Pacific/Wallis'], - ['UTC'] - ] -}); -Ext.define('Proxmox.form.field.Integer',{ - extend: 'Ext.form.field.Number', - alias: 'widget.proxmoxintegerfield', - - config: { - deleteEmpty: false - }, - - allowDecimals: false, - allowExponential: false, - step: 1, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== undefined && val !== null && val !== '') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - } - -}); -Ext.define('Proxmox.form.field.Textfield', { - extend: 'Ext.form.field.Text', - alias: ['widget.proxmoxtextfield'], - - config: { - skipEmptyText: true, - - deleteEmpty: false, - }, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - getSubmitValue: function() { - var me = this; - - var value = this.processRawValue(this.getRawValue()); - if (value !== '') { - return value; - } - - return me.getSkipEmptyText() ? null: value; - }, - - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - this.validate(); - } -}); -Ext.define('Proxmox.DateTimeField', { - extend: 'Ext.form.FieldContainer', - xtype: 'promxoxDateTimeField', - - layout: 'hbox', - - referenceHolder: true, - - submitFormat: 'U', - - getValue: function() { - var me = this; - var d = me.lookupReference('dateentry').getValue(); - - if (d === undefined || d === null) { return null; } - - var t = me.lookupReference('timeentry').getValue(); - - if (t === undefined || t === null) { return null; } - - var offset = (t.getHours()*3600+t.getMinutes()*60)*1000; - - return new Date(d.getTime() + offset); - }, - - getSubmitValue: function() { - var me = this; - var format = me.submitFormat; - var value = me.getValue(); - - return value ? Ext.Date.format(value, format) : null; - }, - - items: [ - { - xtype: 'datefield', - editable: false, - reference: 'dateentry', - flex: 1, - format: 'Y-m-d' - }, - { - xtype: 'timefield', - reference: 'timeentry', - format: 'H:i', - width: 80, - value: '00:00', - increment: 60 - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - - var value = me.value || new Date(); - - me.lookupReference('dateentry').setValue(value); - me.lookupReference('timeentry').setValue(value); - - me.relayEvents(me.lookupReference('dateentry'), ['change']); - me.relayEvents(me.lookupReference('timeentry'), ['change']); - } -}); -Ext.define('Proxmox.form.Checkbox', { - extend: 'Ext.form.field.Checkbox', - alias: ['widget.proxmoxcheckbox'], - - config: { - defaultValue: undefined, - deleteDefaultValue: false, - deleteEmpty: false - }, - - inputValue: '1', - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - if ((val == me.getDefaultValue()) && me.getDeleteDefaultValue()) { - data['delete'] = me.getName(); - } else { - data[me.getName()] = val; - } - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - // also accept integer 1 as true - setRawValue: function(value) { - var me = this; - - if (value === 1) { - me.callParent([true]); - } else { - me.callParent([value]); - } - } - -}); -/* Key-Value ComboBox - * - * config properties: - * comboItems: an array of Key - Value pairs - * deleteEmpty: if set to true (default), an empty value received from the - * comboBox will reset the property to its default value - */ -Ext.define('Proxmox.form.KVComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.proxmoxKVComboBox', - - config: { - deleteEmpty: true - }, - - comboItems: undefined, - displayField: 'value', - valueField: 'key', - queryMode: 'local', - - // overide framework function to implement deleteEmpty behaviour - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null && val !== '' && val !== '__default__') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - validator: function(val) { - var me = this; - - if (me.editable || val === null || val === '') { - return true; - } - - if (me.store.getCount() > 0) { - var values = me.multiSelect ? val.split(me.delimiter) : [val]; - var items = me.store.getData().collect('value', 'data'); - if (Ext.Array.every(values, function(value) { - return Ext.Array.contains(items, value); - })) { - return true; - } - } - - // returns a boolean or string - /*jslint confusion: true */ - return "value '" + val + "' not allowed!"; - }, - - initComponent: function() { - var me = this; - - me.store = Ext.create('Ext.data.ArrayStore', { - model: 'KeyValue', - data : me.comboItems - }); - - if (me.initialConfig.editable === undefined) { - me.editable = false; - } - - me.callParent(); - }, - - setComboItems: function(items) { - var me = this; - - me.getStore().setData(items); - } - -}); -Ext.define('Proxmox.form.LanguageSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'proxmoxLanguageSelector', - - comboItems: Proxmox.Utils.language_array() -}); -/* - * ComboGrid component: a ComboBox where the dropdown menu (the - * "Picker") is a Grid with Rows and Columns expects a listConfig - * object with a columns property roughly based on the GridPicker from - * https://www.sencha.com/forum/showthread.php?299909 - * -*/ - -Ext.define('Proxmox.form.ComboGrid', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxComboGrid'], - - // this value is used as default value after load() - preferredValue: undefined, - - // hack: allow to select empty value - // seems extjs does not allow that when 'editable == false' - onKeyUp: function(e, t) { - var me = this; - var key = e.getKey(); - - if (!me.editable && me.allowBlank && !me.multiSelect && - (key == e.BACKSPACE || key == e.DELETE)) { - me.setValue(''); - } - - me.callParent(arguments); - }, - - config: { - skipEmptyText: false, - deleteEmpty: false, - }, - - // needed to trigger onKeyUp etc. - enableKeyEvents: true, - - editable: false, - - // override ExtJS method - // if the field has multiSelect enabled, the store is not loaded, and - // the displayfield == valuefield, it saves the rawvalue as an array - // but the getRawValue method is only defined in the textfield class - // (which has not to deal with arrays) an returns the string in the - // field (not an array) - // - // so if we have multiselect enabled, return the rawValue (which - // should be an array) and else we do callParent so - // it should not impact any other use of the class - getRawValue: function() { - var me = this; - if (me.multiSelect) { - return me.rawValue; - } else { - return me.callParent(); - } - }, - - getSubmitData: function() { - var me = this; - - let data = null; - if (!me.disabled && me.submitValue) { - let val = me.getSubmitValue(); - if (val !== null) { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - getSubmitValue: function() { - var me = this; - - var value = me.callParent(); - if (value !== '') { - return value; - } - - return me.getSkipEmptyText() ? null: value; - }, - - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - this.validate(); - }, - -// override ExtJS protected method - onBindStore: function(store, initial) { - var me = this, - picker = me.picker, - extraKeySpec, - valueCollectionConfig; - - // We're being bound, not unbound... - if (store) { - // If store was created from a 2 dimensional array with generated field names 'field1' and 'field2' - if (store.autoCreated) { - me.queryMode = 'local'; - me.valueField = me.displayField = 'field1'; - if (!store.expanded) { - me.displayField = 'field2'; - } - - // displayTpl config will need regenerating with the autogenerated displayField name 'field1' - me.setDisplayTpl(null); - } - if (!Ext.isDefined(me.valueField)) { - me.valueField = me.displayField; - } - - // Add a byValue index to the store so that we can efficiently look up records by the value field - // when setValue passes string value(s). - // The two indices (Ext.util.CollectionKeys) are configured unique: false, so that if duplicate keys - // are found, they are all returned by the get call. - // This is so that findByText and findByValue are able to return the *FIRST* matching value. By default, - // if unique is true, CollectionKey keeps the *last* matching value. - extraKeySpec = { - byValue: { - rootProperty: 'data', - unique: false - } - }; - extraKeySpec.byValue.property = me.valueField; - store.setExtraKeys(extraKeySpec); - - if (me.displayField === me.valueField) { - store.byText = store.byValue; - } else { - extraKeySpec.byText = { - rootProperty: 'data', - unique: false - }; - extraKeySpec.byText.property = me.displayField; - store.setExtraKeys(extraKeySpec); - } - - // We hold a collection of the values which have been selected, keyed by this field's valueField. - // This collection also functions as the selected items collection for the BoundList's selection model - valueCollectionConfig = { - rootProperty: 'data', - extraKeys: { - byInternalId: { - property: 'internalId' - }, - byValue: { - property: me.valueField, - rootProperty: 'data' - } - }, - // Whenever this collection is changed by anyone, whether by this field adding to it, - // or the BoundList operating, we must refresh our value. - listeners: { - beginupdate: me.onValueCollectionBeginUpdate, - endupdate: me.onValueCollectionEndUpdate, - scope: me - } - }; - - // This becomes our collection of selected records for the Field. - me.valueCollection = new Ext.util.Collection(valueCollectionConfig); - - // We use the selected Collection as our value collection and the basis - // for rendering the tag list. - - //proxmox override: since the picker is represented by a grid panel, - // we changed here the selection to RowModel - me.pickerSelectionModel = new Ext.selection.RowModel({ - mode: me.multiSelect ? 'SIMPLE' : 'SINGLE', - // There are situations when a row is selected on mousedown but then the mouse is dragged to another row - // and released. In these situations, the event target for the click event won't be the row where the mouse - // was released but the boundview. The view will then determine that it should fire a container click, and - // the DataViewModel will then deselect all prior selections. Setting `deselectOnContainerClick` here will - // prevent the model from deselecting. - deselectOnContainerClick: false, - enableInitialSelection: false, - pruneRemoved: false, - selected: me.valueCollection, - store: store, - listeners: { - scope: me, - lastselectedchanged: me.updateBindSelection - } - }); - - if (!initial) { - me.resetToDefault(); - } - - if (picker) { - picker.setSelectionModel(me.pickerSelectionModel); - if (picker.getStore() !== store) { - picker.bindStore(store); - } - } - } - }, - - // copied from ComboBox - createPicker: function() { - var me = this; - var picker; - - var pickerCfg = Ext.apply({ - // proxmox overrides: display a grid for selection - xtype: 'gridpanel', - id: me.pickerId, - pickerField: me, - floating: true, - hidden: true, - store: me.store, - displayField: me.displayField, - preserveScrollOnRefresh: true, - pageSize: me.pageSize, - tpl: me.tpl, - selModel: me.pickerSelectionModel, - focusOnToFront: false - }, me.listConfig, me.defaultListConfig); - - picker = me.picker || Ext.widget(pickerCfg); - - if (picker.getStore() !== me.store) { - picker.bindStore(me.store); - } - - if (me.pageSize) { - picker.pagingToolbar.on('beforechange', me.onPageChange, me); - } - - // proxmox overrides: pass missing method in gridPanel to its view - picker.refresh = function() { - picker.getSelectionModel().select(me.valueCollection.getRange()); - picker.getView().refresh(); - }; - picker.getNodeByRecord = function() { - picker.getView().getNodeByRecord(arguments); - }; - - // We limit the height of the picker to fit in the space above - // or below this field unless the picker has its own ideas about that. - if (!picker.initialConfig.maxHeight) { - picker.on({ - beforeshow: me.onBeforePickerShow, - scope: me - }); - } - picker.getSelectionModel().on({ - beforeselect: me.onBeforeSelect, - beforedeselect: me.onBeforeDeselect, - focuschange: me.onFocusChange, - selectionChange: function (sm, selectedRecords) { - var me = this; - if (selectedRecords.length) { - me.setValue(selectedRecords); - me.fireEvent('select', me, selectedRecords); - } - }, - scope: me - }); - - // hack for extjs6 - // when the clicked item is the same as the previously selected, - // it does not select the item - // instead we hide the picker - if (!me.multiSelect) { - picker.on('itemclick', function (sm,record) { - if (picker.getSelection()[0] === record) { - picker.hide(); - } - }); - } - - // when our store is not yet loaded, we increase - // the height of the gridpanel, so that we can see - // the loading mask - // - // we save the minheight to reset it after the load - picker.on('show', function() { - if (me.enableLoadMask) { - me.savedMinHeight = picker.getMinHeight(); - picker.setMinHeight(100); - } - }); - - picker.getNavigationModel().navigateOnSpace = false; - - return picker; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - queryMode: 'local', - matchFieldWidth: false - }); - - Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug - - Ext.applyIf(me.listConfig, { width: 400 }); - - me.callParent(); - - // Create the picker at an early stage, so it is available to store the previous selection - if (!me.picker) { - me.createPicker(); - } - - if (me.editable) { - // The trigger.picker causes first a focus event on the field then - // toggles the selection picker. Thus skip expanding in this case, - // else our focus listner expands and the picker.trigger then - // collapses it directly afterwards. - Ext.override(me.triggers.picker, { - onMouseDown : function (e) { - // copied "should we focus" check from Ext.form.trigger.Trigger - if (e.pointerType !== 'touch' && !this.field.owns(Ext.Element.getActiveElement())) { - me.skip_expand_on_focus = true; - } - this.callParent(arguments); - } - }); - - me.on("focus", function(me) { - if (!me.isExpanded && !me.skip_expand_on_focus) { - me.expand(); - } - me.skip_expand_on_focus = false; - }); - } - - me.mon(me.store, 'beforeload', function() { - if (!me.isDisabled()) { - me.enableLoadMask = true; - } - }); - - // hack: autoSelect does not work - me.mon(me.store, 'load', function(store, r, success, o) { - if (success) { - me.clearInvalid(); - - if (me.enableLoadMask) { - delete me.enableLoadMask; - - // if the picker exists, - // we reset its minheight to the saved var/0 - // we have to update the layout, otherwise the height - // gets not recalculated - if (me.picker) { - me.picker.setMinHeight(me.savedMinHeight || 0); - delete me.savedMinHeight; - me.picker.updateLayout(); - } - } - - var def = me.getValue() || me.preferredValue; - if (def) { - me.setValue(def, true); // sync with grid - } - var found = false; - if (def) { - if (Ext.isArray(def)) { - Ext.Array.each(def, function(v) { - if (store.findRecord(me.valueField, v)) { - found = true; - return false; // break - } - }); - } else { - found = store.findRecord(me.valueField, def); - } - } - - if (!found) { - var rec = me.store.first(); - if (me.autoSelect && rec && rec.data) { - def = rec.data[me.valueField]; - me.setValue(def, true); - } else { - me.setValue(me.editable ? def : '', true); - } - } - } - }); - } -}); -Ext.define('Proxmox.form.RRDTypeSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxRRDTypeSelector'], - - displayField: 'text', - valueField: 'id', - editable: false, - queryMode: 'local', - value: 'hour', - stateEvents: [ 'select' ], - stateful: true, - stateId: 'proxmoxRRDTypeSelection', - store: { - type: 'array', - fields: [ 'id', 'timeframe', 'cf', 'text' ], - data : [ - [ 'hour', 'hour', 'AVERAGE', - gettext('Hour') + ' (' + gettext('average') +')' ], - [ 'hourmax', 'hour', 'MAX', - gettext('Hour') + ' (' + gettext('maximum') + ')' ], - [ 'day', 'day', 'AVERAGE', - gettext('Day') + ' (' + gettext('average') + ')' ], - [ 'daymax', 'day', 'MAX', - gettext('Day') + ' (' + gettext('maximum') + ')' ], - [ 'week', 'week', 'AVERAGE', - gettext('Week') + ' (' + gettext('average') + ')' ], - [ 'weekmax', 'week', 'MAX', - gettext('Week') + ' (' + gettext('maximum') + ')' ], - [ 'month', 'month', 'AVERAGE', - gettext('Month') + ' (' + gettext('average') + ')' ], - [ 'monthmax', 'month', 'MAX', - gettext('Month') + ' (' + gettext('maximum') + ')' ], - [ 'year', 'year', 'AVERAGE', - gettext('Year') + ' (' + gettext('average') + ')' ], - [ 'yearmax', 'year', 'MAX', - gettext('Year') + ' (' + gettext('maximum') + ')' ] - ] - }, - // save current selection in the state Provider so RRDView can read it - getState: function() { - var ind = this.getStore().findExact('id', this.getValue()); - var rec = this.getStore().getAt(ind); - if (!rec) { - return; - } - return { - id: rec.data.id, - timeframe: rec.data.timeframe, - cf: rec.data.cf - }; - }, - // set selection based on last saved state - applyState : function(state) { - if (state && state.id) { - this.setValue(state.id); - } - } -}); -Ext.define('Proxmox.form.BondModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondModeSelector'], - - openvswitch: false, - - initComponent: function() { - var me = this; - - if (me.openvswitch) { - me.comboItems = Proxmox.Utils.bond_mode_array([ - 'active-backup', - 'balance-slb', - 'lacp-balance-slb', - 'lacp-balance-tcp', - ]); - } else { - me.comboItems = Proxmox.Utils.bond_mode_array([ - 'balance-rr', - 'active-backup', - 'balance-xor', - 'broadcast', - '802.3ad', - 'balance-tlb', - 'balance-alb', - ]); - } - - me.callParent(); - } -}); - -Ext.define('Proxmox.form.BondPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondPolicySelector'], - comboItems: [ - ['layer2', 'layer2'], - ['layer2+3', 'layer2+3'], - ['layer3+4', 'layer3+4'] - ] -}); - -Ext.define('Proxmox.form.NetworkSelectorController', { - extend: 'Ext.app.ViewController', - alias: 'controller.proxmoxNetworkSelectorController', - - init: function(view) { - var me = this; - - if (!view.nodename) { - throw "missing custom view config: nodename"; - } - view.getStore().getProxy().setUrl('/api2/json/nodes/'+ view.nodename + '/network'); - } -}); - -Ext.define('Proxmox.data.NetworkSelector', { - extend: 'Ext.data.Model', - fields: [ - {name: 'active'}, - {name: 'cidr'}, - {name: 'cidr6'}, - {name: 'address'}, - {name: 'address6'}, - {name: 'comments'}, - {name: 'iface'}, - {name: 'slaves'}, - {name: 'type'} - ] -}); - -Ext.define('Proxmox.form.NetworkSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.proxmoxNetworkSelector', - - controller: 'proxmoxNetworkSelectorController', - - nodename: 'localhost', - setNodename: function(nodename) { - this.nodename = nodename; - var networkSelectorStore = this.getStore(); - networkSelectorStore.removeAll(); - // because of manual local copy of data for ip4/6 - this.getPicker().refresh(); - if (networkSelectorStore && typeof networkSelectorStore.getProxy === 'function') { - networkSelectorStore.getProxy().setUrl('/api2/json/nodes/'+ nodename + '/network'); - networkSelectorStore.load(); - } - }, - // set default value to empty array, else it inits it with - // null and after the store load it is an empty array, - // triggering dirtychange - value: [], - valueField: 'cidr', - displayField: 'cidr', - store: { - autoLoad: true, - model: 'Proxmox.data.NetworkSelector', - proxy: { - type: 'proxmox' - }, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ], - filters: [ - function(item) { - return item.data.cidr; - } - ], - listeners: { - load: function(store, records, successfull) { - - if (successfull) { - records.forEach(function(record) { - if (record.data.cidr6) { - let dest = (record.data.cidr) ? record.copy(null) : record; - dest.data.cidr = record.data.cidr6; - dest.data.address = record.data.address6; - delete record.data.cidr6; - dest.data.comments = record.data.comments6; - delete record.data.comments6; - store.add(dest); - } - }); - } - } - } - }, - listConfig: { - width: 600, - columns: [ - { - - header: gettext('CIDR'), - dataIndex: 'cidr', - hideable: false, - flex: 1 - }, - { - - header: gettext('IP'), - dataIndex: 'address', - hidden: true, - }, - { - header: gettext('Interface'), - width: 90, - dataIndex: 'iface' - }, - { - header: gettext('Active'), - renderer: Proxmox.Utils.format_boolean, - width: 60, - dataIndex: 'active' - }, - { - header: gettext('Type'), - width: 80, - hidden: true, - dataIndex: 'type' - }, - { - header: gettext('Comment'), - flex: 2, - dataIndex: 'comments' - } - ] - } -}); -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - */ -Ext.define('Proxmox.button.Button', { - extend: 'Ext.button.Button', - alias: 'widget.proxmoxButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - - // Note: me.realHandler may be a string (see named scopes) - var realHandler = me.handler; - - me.handler = function(button, event) { - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn !== 'yes') { - return; - } - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }); - } else { - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }; - } - - me.callParent(); - - var grid; - if (!me.selModel && me.selModel !== null) { - grid = me.up('grid'); - if (grid && grid.selModel) { - me.selModel = grid.selModel; - } - } - - if (me.waitMsgTarget === true) { - grid = me.up('grid'); - if (grid) { - me.waitMsgTarget = grid; - } else { - throw "unable to find waitMsgTarget"; - } - } - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); - - -Ext.define('Proxmox.button.StdRemoveButton', { - extend: 'Proxmox.button.Button', - alias: 'widget.proxmoxStdRemoveButton', - - text: gettext('Remove'), - - disabled: true, - - config: { - baseurl: undefined - }, - - getUrl: function(rec) { - var me = this; - - return me.baseurl + '/' + rec.getId(); - }, - - // also works with names scopes - callback: function(options, success, response) {}, - - getRecordName: function(rec) { return rec.getId() }, - - confirmMsg: function (rec) { - var me = this; - - var name = me.getRecordName(rec); - return Ext.String.format( - gettext('Are you sure you want to remove entry {0}'), - "'" + name + "'"); - }, - - handler: function(btn, event, rec) { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.getUrl(rec), - method: 'DELETE', - waitMsgTarget: me.waitMsgTarget, - callback: function(options, success, response) { - Ext.callback(me.callback, me.scope, [options, success, response], 0, me); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -/* help button pointing to an online documentation - for components contained in a modal window -*/ -/*global - proxmoxOnlineHelpInfo -*/ -Ext.define('Proxmox.button.Help', { - extend: 'Ext.button.Button', - xtype: 'proxmoxHelpButton', - - text: gettext('Help'), - - // make help button less flashy by styling it like toolbar buttons - iconCls: ' x-btn-icon-el-default-toolbar-small fa fa-question-circle', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - - hidden: true, - - listenToGlobalEvent: true, - - controller: { - xclass: 'Ext.app.ViewController', - listen: { - global: { - proxmoxShowHelp: 'onProxmoxShowHelp', - proxmoxHideHelp: 'onProxmoxHideHelp' - } - }, - onProxmoxShowHelp: function(helpLink) { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.setOnlineHelp(helpLink); - me.show(); - } - }, - onProxmoxHideHelp: function() { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.hide(); - } - } - }, - - // this sets the link and the tooltip text - setOnlineHelp:function(blockid) { - var me = this; - - var info = Proxmox.Utils.get_help_info(blockid); - if (info) { - me.onlineHelp = blockid; - var title = info.title; - if (info.subtitle) { - title += ' - ' + info.subtitle; - } - me.setTooltip(title); - } - }, - - // helper to set the onlineHelp via a config object - setHelpConfig: function(config) { - var me = this; - me.setOnlineHelp(config.onlineHelp); - }, - - handler: function() { - var me = this; - var docsURI; - - if (me.onlineHelp) { - docsURI = Proxmox.Utils.get_help_link(me.onlineHelp); - } - - if (docsURI) { - window.open(docsURI); - } else { - Ext.Msg.alert(gettext('Help'), gettext('No Help available')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.callParent(); - - if (me.onlineHelp) { - me.setOnlineHelp(me.onlineHelp); // set tooltip - } - } -}); -/* Renders a list of key values objets - -mandatory config parameters: -rows: an object container where each propery is a key-value object we want to render - var rows = { - keyboard: { - header: gettext('Keyboard Layout'), - editor: 'Your.KeyboardEdit', - required: true - }, - -optional: -disabled: setting this parameter to true will disable selection and focus on the -proxmoxObjectGrid as well as greying out input elements. -Useful for a readonly tabular display - -*/ - -Ext.define('Proxmox.grid.ObjectGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.proxmoxObjectGrid'], - disabled: false, - hideHeaders: true, - - monStoreErrors: false, - - add_combobox_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxKVComboBox', - name: name, - comboItems: opts.comboItems, - value: opts.defaultValue, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_text_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxtextfield', - name: name, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - vtype: opts.vtype, - fieldLabel: text - } - } - }; - }, - - add_boolean_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue || 0, - header: text, - renderer: opts.renderer || Proxmox.Utils.format_boolean, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxcheckbox', - name: name, - uncheckedValue: 0, - defaultValue: opts.defaultValue || 0, - checked: opts.defaultValue ? true : false, - deleteDefaultValue: opts.deleteDefaultValue ? true : false, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_integer_row: function(name, text, opts) { - var me = this; - - opts = opts || {} - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxintegerfield', - name: name, - minValue: opts.minValue, - maxValue: opts.maxValue, - emptyText: gettext('Default'), - deleteEmpty: opts.deleteEmpty ? true : false, - value: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - editorConfig: {}, // default config passed to editor - - run_editor: function() { - var me = this; - - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rows = me.rows; - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - var config; - if (Ext.isString(rowdef.editor)) { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - win = Ext.create(rowdef.editor, config); - } else { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - Ext.apply(config, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', me.reload, me); - }, - - reload: function() { - var me = this; - me.rstore.load(); - }, - - getObjectValue: function(key, defaultValue) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }, - - renderKey: function(key, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - return rowdef.header || key; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - - var renderer = rowdef.renderer; - if (renderer) { - return renderer(value, metaData, record, rowIndex, colIndex, store); - } - - return value; - }, - - listeners: { - itemkeydown: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER) { - this.pressedIndex = index; - } - }, - itemkeyup: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER && index == this.pressedIndex) { - this.run_editor(); - } - - this.pressedIndex = undefined; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - var rstore = me.rstore; - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore, - sorters: [], - filters: [] - }); - - if (rows) { - Ext.Object.each(rows, function(key, rowdef) { - if (Ext.isDefined(rowdef.defaultValue)) { - store.add({ key: key, value: rowdef.defaultValue }); - } else if (rowdef.required) { - store.add({ key: key, value: undefined }); - } - }); - } - - if (me.sorterFn) { - store.sorters.add(Ext.create('Ext.util.Sorter', { - sorterFn: me.sorterFn - })); - } - - store.filters.add(Ext.create('Ext.util.Filter', { - filterFn: function(item) { - if (rows) { - var rowdef = rows[item.data.key]; - if (!rowdef || (rowdef.visible === false)) { - return false; - } - } - return true; - } - })); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.applyIf(me, { - store: store, - stateful: false, - columns: [ - { - header: gettext('Name'), - width: me.cwidth1 || 200, - dataIndex: 'key', - renderer: me.renderKey - }, - { - flex: 1, - header: gettext('Value'), - dataIndex: 'value', - renderer: me.renderValue - } - ] - }); - - me.callParent(); - - if (me.monStoreErrors) { - Proxmox.Utils.monStoreErrors(me, me.store); - } - } -}); -Ext.define('Proxmox.grid.PendingObjectGrid', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxPendingObjectGrid'], - - getObjectValue: function(key, defaultValue, pending) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - var value = rec.data.value; - if (pending) { - if (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') { - value = rec.data.pending; - } else if (rec.data['delete'] === 1) { - value = defaultValue; - } - } - - if (Ext.isDefined(value) && (value !== '')) { - return value; - } else { - return defaultValue; - } - } - return defaultValue; - }, - - hasPendingChanges: function(key) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var keys = rowdef.multiKey || [ key ]; - var pending = false; - - Ext.Array.each(keys, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && ( - (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') || - rec.data['delete'] === 1 - )) { - pending = true; - return false; // break - } - }); - - return pending; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var renderer = rowdef.renderer; - var current = ''; - var pendingdelete = ''; - var pending = ''; - - if (renderer) { - current = renderer(value, metaData, record, rowIndex, colIndex, store, false); - if (me.hasPendingChanges(key)) { - pending = renderer(record.data.pending, metaData, record, rowIndex, colIndex, store, true); - } - if (pending == current) { - pending = undefined; - } - } else { - current = value || ''; - pending = record.data.pending; - } - - if (record.data['delete']) { - var delete_all = true; - if (rowdef.multiKey) { - Ext.Array.each(rowdef.multiKey, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && rec.data['delete'] !== 1) { - delete_all = false; - return false; // break - } - }); - } - if (delete_all) { - pending = '
'+ current +'
'; - } - } - - if (pending) { - return current + '
' + pending + '
'; - } else { - return current; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - model: 'KeyValuePendingDelete', - readArray: true, - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - me.callParent(); - } -}); -Ext.define('Proxmox.panel.InputPanel', { - extend: 'Ext.panel.Panel', - alias: ['widget.inputpanel'], - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - border: false, - - // override this with an URL to a relevant chapter of the pve manual - // setting this will display a help button in our parent panel - onlineHelp: undefined, - - // will be set if the inputpanel has advanced items - hasAdvanced: false, - - // if the panel has advanced items, - // this will determine if they are shown by default - showAdvanced: false, - - // overwrite this to modify submit data - onGetValues: function(values) { - return values; - }, - - getValues: function(dirtyOnly) { - var me = this; - - if (Ext.isFunction(me.onGetValues)) { - dirtyOnly = false; - } - - var values = {}; - - Ext.Array.each(me.query('[isFormField]'), function(field) { - if (!dirtyOnly || field.isDirty()) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - return me.onGetValues(values); - }, - - setAdvancedVisible: function(visible) { - var me = this; - var advItems = me.getComponent('advancedContainer'); - if (advItems) { - advItems.setVisible(visible); - } - }, - - setValues: function(values) { - var me = this; - - var form = me.up('form'); - - Ext.iterate(values, function(fieldId, val) { - var field = me.query('[isFormField][name=' + fieldId + ']')[0]; - if (field) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - }, - - initComponent: function() { - var me = this; - - var items; - - if (me.items) { - me.columns = 1; - items = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.items - } - ]; - me.items = undefined; - } else if (me.column4) { - me.columns = 4; - items = [ - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column2 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column3 - }, - { - columnWidth: 0.25, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column4 - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else if (me.column1) { - me.columns = 2; - items = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column2 || [] // allow empty column - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else { - throw "unsupported config"; - } - - var advItems; - if (me.advancedItems) { - advItems = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.advancedItems - } - ]; - me.advancedItems = undefined; - } else if (me.advancedColumn1) { - advItems = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.advancedColumn1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.advancedColumn2 || [] // allow empty column - } - ]; - - me.advancedColumn1 = undefined; - me.advancedColumn2 = undefined; - - if (me.advancedColumnB) { - advItems.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.advancedColumnB - }); - me.advancedColumnB = undefined; - } - } - - if (advItems) { - me.hasAdvanced = true; - advItems.unshift({ - columnWidth: 1, - xtype: 'box', - hidden: false, - border: true, - autoEl: { - tag: 'hr' - } - }); - items.push({ - columnWidth: 1, - xtype: 'container', - itemId: 'advancedContainer', - hidden: !me.showAdvanced, - layout: 'column', - defaults: { - border: false - }, - items: advItems - }); - } - - if (me.useFieldContainer) { - Ext.apply(me, { - layout: 'fit', - items: Ext.apply(me.useFieldContainer, { - layout: 'column', - defaultType: 'container', - items: items - }) - }); - } else { - Ext.apply(me, { - layout: 'column', - defaultType: 'container', - items: items - }); - } - - me.callParent(); - } -}); -/* - * Display log entries in a panel with scrollbar - * The log entries are automatically refreshed via a background task, - * with newest entries comming at the bottom - */ -Ext.define('Proxmox.panel.LogView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxLogView', - - pageSize: 500, - viewBuffer: 50, - lineHeight: 16, - - scrollToEnd: true, - - // callback for load failure, used for ceph - failCallback: undefined, - - controller: { - xclass: 'Ext.app.ViewController', - - updateParams: function() { - var me = this; - var viewModel = me.getViewModel(); - var since = viewModel.get('since'); - var until = viewModel.get('until'); - if (viewModel.get('hide_timespan')) { - return; - } - - if (since > until) { - Ext.Msg.alert('Error', 'Since date must be less equal than Until date.'); - return; - } - - viewModel.set('params.since', Ext.Date.format(since, 'Y-m-d')); - viewModel.set('params.until', Ext.Date.format(until, 'Y-m-d') + ' 23:59:59'); - me.getView().loadTask.delay(200); - }, - - scrollPosBottom: function() { - var view = this.getView(); - var pos = view.getScrollY(); - var maxPos = view.getScrollable().getMaxPosition().y; - return maxPos - pos; - }, - - updateView: function(text, first, total) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - var content = me.lookup('content'); - var data = viewModel.get('data'); - - if (first === data.first && total === data.total && text.length === data.textlen) { - return; // same content, skip setting and scrolling - } - viewModel.set('data', { - first: first, - total: total, - textlen: text.length - }); - - var scrollPos = me.scrollPosBottom(); - - content.update(text); - - if (view.scrollToEnd && scrollPos <= 0) { - // we use setTimeout to work around scroll handling on touchscreens - setTimeout(function() { view.scrollTo(0, Infinity); }, 10); - } - }, - - doLoad: function() { - var me = this; - if (me.running) { - me.requested = true; - return; - } - me.running = true; - var view = me.getView(); - var viewModel = me.getViewModel(); - Proxmox.Utils.API2Request({ - url: me.getView().url, - params: viewModel.get('params'), - method: 'GET', - success: function(response) { - Proxmox.Utils.setErrorMask(me, false); - var total = response.result.total; - var lines = new Array(); - var first = Infinity; - - Ext.Array.each(response.result.data, function(line) { - if (first > line.n) { - first = line.n; - } - lines[line.n - 1] = Ext.htmlEncode(line.t); - }); - - lines.length = total; - me.updateView(lines.join('
'), first - 1, total); - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - }, - failure: function(response) { - if (view.failCallback) { - view.failCallback(response); - } else { - var msg = response.htmlStatus; - Proxmox.Utils.setErrorMask(me, msg); - } - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - } - }); - }, - - onScroll: function(x, y) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - - var lineHeight = view.lineHeight; - var line = view.getScrollY()/lineHeight; - var start = viewModel.get('params.start'); - var limit = viewModel.get('params.limit'); - var viewLines = view.getHeight()/lineHeight; - - var viewStart = Math.max(parseInt(line - 1 - view.viewBuffer, 10), 0); - var viewEnd = parseInt(line + viewLines + 1 + view.viewBuffer, 10); - - if (viewStart < start || viewEnd > (start+limit)) { - viewModel.set('params.start', - Math.max(parseInt(line - limit/2 + 10, 10), 0)); - view.loadTask.delay(200); - } - }, - - init: function(view) { - var me = this; - - if (!view.url) { - throw "no url specified"; - } - - var viewModel = this.getViewModel(); - var since = new Date(); - since.setDate(since.getDate() - 3); - viewModel.set('until', new Date()); - viewModel.set('since', since); - viewModel.set('params.limit', view.pageSize); - viewModel.set('hide_timespan', !view.log_select_timespan); - me.lookup('content').setStyle('line-height', view.lineHeight + 'px'); - - view.loadTask = new Ext.util.DelayedTask(me.doLoad, me); - - me.updateParams(); - view.task = Ext.TaskManager.start({ - run: function() { - if (!view.isVisible() || !view.scrollToEnd) { - return; - } - - if (me.scrollPosBottom() <= 1) { - view.loadTask.delay(200); - } - }, - interval: 1000 - }); - } - }, - - onDestroy: function() { - var me = this; - me.loadTask.cancel(); - Ext.TaskManager.stop(me.task); - }, - - // for user to initiate a load from outside - requestUpdate: function() { - var me = this; - me.loadTask.delay(200); - }, - - viewModel: { - data: { - until: null, - since: null, - hide_timespan: false, - data: { - start: 0, - total: 0, - textlen: 0 - }, - params: { - start: 0, - limit: 500, - } - } - }, - - layout: 'auto', - bodyPadding: 5, - scrollable: { - x: 'auto', - y: 'auto', - listeners: { - // we have to have this here, since we cannot listen to events - // of the scroller in the viewcontroller (extjs bug?), nor does - // the panel have a 'scroll' event' - scroll: { - fn: function(scroller, x, y) { - var controller = this.component.getController(); - if (controller) { // on destroy, controller can be gone - controller.onScroll(x,y); - } - }, - buffer: 200 - }, - } - }, - - tbar: { - bind: { - hidden: '{hide_timespan}' - }, - items: [ - '->', - 'Since: ', - { - xtype: 'datefield', - name: 'since_date', - reference: 'since', - format: 'Y-m-d', - bind: { - value: '{since}', - maxValue: '{until}' - } - }, - 'Until: ', - { - xtype: 'datefield', - name: 'until_date', - reference: 'until', - format: 'Y-m-d', - bind: { - value: '{until}', - minValue: '{since}' - } - }, - { - xtype: 'button', - text: 'Update', - handler: 'updateParams' - } - ], - }, - - items: [ - { - xtype: 'box', - reference: 'content', - style: { - font: 'normal 11px tahoma, arial, verdana, sans-serif', - 'white-space': 'pre' - }, - } - ] -}); -/* - * Display log entries in a panel with scrollbar - * The log entries are automatically refreshed via a background task, - * with newest entries comming at the bottom - */ -Ext.define('Proxmox.panel.JournalView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxJournalView', - - numEntries: 500, - lineHeight: 16, - - scrollToEnd: true, - - controller: { - xclass: 'Ext.app.ViewController', - - updateParams: function() { - var me = this; - var viewModel = me.getViewModel(); - var since = viewModel.get('since'); - var until = viewModel.get('until'); - - since.setHours(0, 0, 0, 0); - until.setHours(0, 0, 0, 0); - until.setDate(until.getDate()+1); - - me.getView().loadTask.delay(200, undefined, undefined, [ - false, - false, - Ext.Date.format(since, "U"), - Ext.Date.format(until, "U") - ]); - }, - - scrollPosBottom: function() { - var view = this.getView(); - var pos = view.getScrollY(); - var maxPos = view.getScrollable().getMaxPosition().y; - return maxPos - pos; - }, - - scrollPosTop: function() { - var view = this.getView(); - return view.getScrollY(); - }, - - updateScroll: function(livemode, num, scrollPos, scrollPosTop) { - var me = this; - var view = me.getView(); - - if (!livemode) { - setTimeout(function() { view.scrollTo(0, 0); }, 10); - } else if (view.scrollToEnd && scrollPos <= 0) { - setTimeout(function() { view.scrollTo(0, Infinity); }, 10); - } else if (!view.scrollToEnd && scrollPosTop < 20*view.lineHeight) { - setTimeout(function() { view.scrollTo(0, num*view.lineHeight + scrollPosTop); }, 10); - } - }, - - updateView: function(lines, livemode, top) { - var me = this; - var view = me.getView(); - var viewmodel = me.getViewModel(); - if (viewmodel.get('livemode') !== livemode) { - return; // we switched mode, do not update the content - } - var contentEl = me.lookup('content'); - - // save old scrollpositions - var scrollPos = me.scrollPosBottom(); - var scrollPosTop = me.scrollPosTop(); - - var newend = lines.shift(); - var newstart = lines.pop(); - - var num = lines.length; - var text = lines.map(Ext.htmlEncode).join('
'); - - if (!livemode) { - if (num) { - view.content = text; - } else { - view.content = 'nothing logged or no timespan selected'; - } - } else { - // update content - if (top && num) { - view.content = view.content ? text + '
' + view.content : text; - } else if (!top && num) { - view.content = view.content ? view.content + '
' + text : text; - } - - // update cursors - if (!top || !view.startcursor) { - view.startcursor = newstart; - } - - if (top || !view.endcursor) { - view.endcursor = newend; - } - } - - contentEl.update(view.content); - - me.updateScroll(livemode, num, scrollPos, scrollPosTop); - }, - - doLoad: function(livemode, top, since, until) { - var me = this; - if (me.running) { - me.requested = true; - return; - } - me.running = true; - var view = me.getView(); - var params = { - lastentries: view.numEntries || 500, - }; - if (livemode) { - if (!top && view.startcursor) { - params = { - startcursor: view.startcursor - }; - } else if (view.endcursor) { - params.endcursor = view.endcursor; - } - } else { - params = { - since: since, - until: until - }; - } - Proxmox.Utils.API2Request({ - url: view.url, - params: params, - waitMsgTarget: (!livemode) ? view : undefined, - method: 'GET', - success: function(response) { - Proxmox.Utils.setErrorMask(me, false); - var lines = response.result.data; - me.updateView(lines, livemode, top); - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - }, - failure: function(response) { - var msg = response.htmlStatus; - Proxmox.Utils.setErrorMask(me, msg); - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - } - }); - }, - - onScroll: function(x, y) { - var me = this; - var view = me.getView(); - var viewmodel = me.getViewModel(); - var livemode = viewmodel.get('livemode'); - if (!livemode) { - return; - } - - if (me.scrollPosTop() < 20*view.lineHeight) { - view.scrollToEnd = false; - view.loadTask.delay(200, undefined, undefined, [true, true]); - } else if (me.scrollPosBottom() <= 1) { - view.scrollToEnd = true; - } - }, - - init: function(view) { - var me = this; - - if (!view.url) { - throw "no url specified"; - } - - var viewmodel = me.getViewModel(); - var viewModel = this.getViewModel(); - var since = new Date(); - since.setDate(since.getDate() - 3); - viewModel.set('until', new Date()); - viewModel.set('since', since); - me.lookup('content').setStyle('line-height', view.lineHeight + 'px'); - - view.loadTask = new Ext.util.DelayedTask(me.doLoad, me, [true, false]); - - me.updateParams(); - view.task = Ext.TaskManager.start({ - run: function() { - if (!view.isVisible() || !view.scrollToEnd || !viewmodel.get('livemode')) { - return; - } - - if (me.scrollPosBottom() <= 1) { - view.loadTask.delay(200, undefined, undefined, [true, false]); - } - }, - interval: 1000 - }); - }, - - onLiveMode: function() { - var me = this; - var view = me.getView(); - delete view.startcursor; - delete view.endcursor; - delete view.content; - me.getViewModel().set('livemode', true); - view.scrollToEnd = true; - me.updateView([], true, false); - }, - - onTimespan: function() { - var me = this; - me.getViewModel().set('livemode', false); - me.updateView([], false); - } - }, - - onDestroy: function() { - var me = this; - me.loadTask.cancel(); - Ext.TaskManager.stop(me.task); - delete me.content; - }, - - // for user to initiate a load from outside - requestUpdate: function() { - var me = this; - me.loadTask.delay(200); - }, - - viewModel: { - data: { - livemode: true, - until: null, - since: null - } - }, - - layout: 'auto', - bodyPadding: 5, - scrollable: { - x: 'auto', - y: 'auto', - listeners: { - // we have to have this here, since we cannot listen to events - // of the scroller in the viewcontroller (extjs bug?), nor does - // the panel have a 'scroll' event' - scroll: { - fn: function(scroller, x, y) { - var controller = this.component.getController(); - if (controller) { // on destroy, controller can be gone - controller.onScroll(x,y); - } - }, - buffer: 200 - }, - } - }, - - tbar: { - - items: [ - '->', - { - xtype: 'segmentedbutton', - items: [ - { - text: gettext('Live Mode'), - bind: { - pressed: '{livemode}' - }, - handler: 'onLiveMode', - }, - { - text: gettext('Select Timespan'), - bind: { - pressed: '{!livemode}' - }, - handler: 'onTimespan', - } - ] - }, - { - xtype: 'box', - bind: { disabled: '{livemode}' }, - autoEl: { cn: gettext('Since') + ':' } - }, - { - xtype: 'datefield', - name: 'since_date', - reference: 'since', - format: 'Y-m-d', - bind: { - disabled: '{livemode}', - value: '{since}', - maxValue: '{until}' - } - }, - { - xtype: 'box', - bind: { disabled: '{livemode}' }, - autoEl: { cn: gettext('Until') + ':' } - }, - { - xtype: 'datefield', - name: 'until_date', - reference: 'until', - format: 'Y-m-d', - bind: { - disabled: '{livemode}', - value: '{until}', - minValue: '{since}' - } - }, - { - xtype: 'button', - text: 'Update', - reference: 'updateBtn', - handler: 'updateParams', - bind: { - disabled: '{livemode}' - } - } - ] - }, - - items: [ - { - xtype: 'box', - reference: 'content', - style: { - font: 'normal 11px tahoma, arial, verdana, sans-serif', - 'white-space': 'pre' - }, - } - ] -}); -Ext.define('Proxmox.widget.RRDChart', { - extend: 'Ext.chart.CartesianChart', - alias: 'widget.proxmoxRRDChart', - - unit: undefined, // bytes, bytespersecond, percent - - controller: { - xclass: 'Ext.app.ViewController', - - convertToUnits: function(value) { - var units = ['', 'k','M','G','T', 'P']; - var si = 0; - while(value >= 1000 && si < (units.length -1)){ - value = value / 1000; - si++; - } - - // javascript floating point weirdness - value = Ext.Number.correctFloat(value); - - // limit to 2 decimal points - value = Ext.util.Format.number(value, "0.##"); - - return value.toString() + " " + units[si]; - }, - - leftAxisRenderer: function(axis, label, layoutContext) { - var me = this; - - return me.convertToUnits(label); - }, - - onSeriesTooltipRender: function(tooltip, record, item) { - var me = this.getView(); - - var suffix = ''; - - if (me.unit === 'percent') { - suffix = '%'; - } else if (me.unit === 'bytes') { - suffix = 'B'; - } else if (me.unit === 'bytespersecond') { - suffix = 'B/s'; - } - - var prefix = item.field; - if (me.fieldTitles && me.fieldTitles[me.fields.indexOf(item.field)]) { - prefix = me.fieldTitles[me.fields.indexOf(item.field)]; - } - tooltip.setHtml(prefix + ': ' + this.convertToUnits(record.get(item.field)) + suffix + - '
' + new Date(record.get('time'))); - }, - - onAfterAnimation: function(chart, eopts) { - // if the undobuton is disabled, - // disable our tool - - var ourUndoZoomButton = chart.tools[0]; - var undoButton = chart.interactions[0].getUndoButton(); - ourUndoZoomButton.setDisabled(undoButton.isDisabled()); - } - }, - - width: 770, - height: 300, - animation: false, - interactions: [{ - type: 'crosszoom' - }], - axes: [{ - type: 'numeric', - position: 'left', - grid: true, - renderer: 'leftAxisRenderer', - //renderer: function(axis, label) { return label; }, - minimum: 0 - }, { - type: 'time', - position: 'bottom', - grid: true, - fields: ['time'] - }], - legend: { - docked: 'bottom' - }, - listeners: { - animationend: 'onAfterAnimation' - }, - - - initComponent: function() { - var me = this; - var series = {}; - - if (!me.store) { - throw "cannot work without store"; - } - - if (!me.fields) { - throw "cannot work without fields"; - } - - me.callParent(); - - // add correct label for left axis - var axisTitle = ""; - if (me.unit === 'percent') { - axisTitle = "%"; - } else if (me.unit === 'bytes') { - axisTitle = "Bytes"; - } else if (me.unit === 'bytespersecond') { - axisTitle = "Bytes/s"; - } else if (me.fieldTitles && me.fieldTitles.length === 1) { - axisTitle = me.fieldTitles[0]; - } else if (me.fields.length === 1) { - axisTitle = me.fields[0]; - } - - me.axes[0].setTitle(axisTitle); - - if (!me.noTool) { - me.addTool([{ - type: 'minus', - disabled: true, - tooltip: gettext('Undo Zoom'), - handler: function(){ - var undoButton = me.interactions[0].getUndoButton(); - if (undoButton.handler) { - undoButton.handler(); - } - } - },{ - type: 'restore', - tooltip: gettext('Toggle Legend'), - handler: function(){ - if (me.legend) { - me.legend.setVisible(!me.legend.isVisible()); - } - } - }]); - } - - // add a series for each field we get - me.fields.forEach(function(item, index){ - var title = item; - if (me.fieldTitles && me.fieldTitles[index]) { - title = me.fieldTitles[index]; - } - me.addSeries(Ext.apply( - { - type: 'line', - xField: 'time', - yField: item, - title: title, - fill: true, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - }, - tooltip: { - trackMouse: true, - renderer: 'onSeriesTooltipRender' - } - }, - me.seriesConfig - )); - }); - - // enable animation after the store is loaded - me.store.onAfter('load', function() { - me.setAnimation(true); - }, this, {single: true}); - } -}); -Ext.define('Proxmox.panel.GaugeWidget', { - extend: 'Ext.panel.Panel', - alias: 'widget.proxmoxGauge', - - defaults: { - style: { - 'text-align':'center' - } - }, - items: [ - { - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}

' - }, - { - xtype: 'polar', - height: 120, - border: false, - itemId: 'chart', - series: [{ - type: 'gauge', - value: 0, - colors: ['#f5f5f5'], - sectors: [0], - donut: 90, - needleLength: 100, - totalAngle: Math.PI - }], - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '', - textAlign: 'center', - textBaseline: 'bottom', - x: 125, - y: 110, - fontSize: 30 - }] - }, - { - xtype: 'box', - itemId: 'text' - } - ], - - header: false, - border: false, - - warningThreshold: 0.6, - criticalThreshold: 0.9, - warningColor: '#fc0', - criticalColor: '#FF6C59', - defaultColor: '#7289DA', - backgroundColor: '#2C2F33', - - initialValue: 0, - - - updateValue: function(value, text) { - var me = this; - var color = me.defaultColor; - var attr = {}; - - if (value >= me.criticalThreshold) { - color = me.criticalColor; - } else if (value >= me.warningThreshold) { - color = me.warningColor; - } - - me.chart.series[0].setColors([color, me.backgroundColor]); - me.chart.series[0].setValue(value*100); - - me.valueSprite.setText(' '+(value*100).toFixed(0) + '%'); - attr.x = me.chart.getWidth()/2; - attr.y = me.chart.getHeight()-20; - if (me.spriteFontSize) { - attr.fontSize = me.spriteFontSize; - } - me.valueSprite.setAttributes(attr, true); - - if (text !== undefined) { - me.text.setHtml(text); - } - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.text = me.getComponent('text'); - me.chart = me.getComponent('chart'); - me.valueSprite = me.chart.getSurface('chart').get('valueSprite'); - } -}); -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ -Ext.define('Proxmox.window.Edit', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxWindowEdit', - - // autoLoad trigger a load() after component creation - autoLoad: false, - - resizable: false, - - // use this tio atimatically generate a title like - // Create: - subject: undefined, - - // set isCreate to true if you want a Create button (instead - // OK and RESET) - isCreate: false, - - // set to true if you want an Add button (instead of Create) - isAdd: false, - - // set to true if you want an Remove button (instead of Create) - isRemove: false, - - // custom submitText - submitText: undefined, - - backgroundDelay: 0, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - - // finds the first form field - defaultFocus: 'field[disabled=false][hidden=false]', - - showProgress: false, - - showTaskViewer: false, - - // gets called if we have a progress bar or taskview and it detected that - // the task finished. function(success) - taskDone: Ext.emptyFn, - - // gets called when the api call is finished, right at the beginning - // function(success, response, options) - apiCallDone: Ext.emptyFn, - - // assign a reference from docs, to add a help button docked to the - // bottom of the window. If undefined we magically fall back to the - // onlineHelp of our first item, if set. - onlineHelp: undefined, - - isValid: function() { - var me = this; - - var form = me.formPanel.getForm(); - return form.isValid(); - }, - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.formPanel.getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - setValues: function(values) { - var me = this; - - var form = me.formPanel.getForm(); - - Ext.iterate(values, function(fieldId, val) { - var field = form.findField(fieldId); - if (field && !field.up('inputpanel')) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setValues(values); - }); - }, - - submit: function() { - var me = this; - - var form = me.formPanel.getForm(); - - var values = me.getValues(); - Ext.Object.each(values, function(name, val) { - if (values.hasOwnProperty(name)) { - if (Ext.isArray(val) && !val.length) { - values[name] = ''; - } - } - }); - - if (me.digest) { - values.digest = me.digest; - } - - if (me.backgroundDelay) { - values.background_delay = me.backgroundDelay; - } - - var url = me.url; - if (me.method === 'DELETE') { - url = url + "?" + Ext.Object.toQueryString(values); - values = undefined; - } - - Proxmox.Utils.API2Request({ - url: url, - waitMsgTarget: me, - method: me.method || (me.backgroundDelay ? 'POST' : 'PUT'), - params: values, - failure: function(response, options) { - me.apiCallDone(false, response, options); - - if (response.result && response.result.errors) { - form.markInvalid(response.result.errors); - } - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = (me.backgroundDelay || me.showProgress || me.showTaskViewer) && - response.result.data ? true : false; - - me.apiCallDone(true, response, options); - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - me.hide(); - - var upid = response.result.data; - var viewerClass = me.showTaskViewer ? 'Viewer' : 'Progress'; - var win = Ext.create('Proxmox.window.Task' + viewerClass, { - upid: upid, - taskDone: me.taskDone, - listeners: { - destroy: function () { - me.close(); - } - } - }); - win.show(); - } else { - me.close(); - } - } - }); - }, - - load: function(options) { - var me = this; - - var form = me.formPanel.getForm(); - - options = options || {}; - - var newopts = Ext.apply({ - waitMsgTarget: me - }, options); - - var createWrapper = function(successFn) { - Ext.apply(newopts, { - url: me.url, - method: 'GET', - success: function(response, opts) { - form.clearInvalid(); - me.digest = response.result.data.digest; - if (successFn) { - successFn(response, opts); - } else { - me.setValues(response.result.data); - } - // hack: fix ExtJS bug - Ext.Array.each(me.query('radiofield'), function(f) { - f.resetOriginalValue(); - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() { - me.close(); - }); - } - }); - }; - - createWrapper(options.success); - - Proxmox.Utils.API2Request(newopts); - }, - - initComponent : function() { - var me = this; - - if (!me.url) { - throw "no url specified"; - } - - if (me.create) {throw "deprecated parameter, use isCreate";} - - var items = Ext.isArray(me.items) ? me.items : [ me.items ]; - - me.items = undefined; - - me.formPanel = Ext.create('Ext.form.Panel', { - url: me.url, - method: me.method || 'PUT', - trackResetOnLoad: true, - bodyPadding: 10, - border: false, - defaults: Ext.apply({}, me.defaults, { - border: false - }), - fieldDefaults: Ext.apply({}, me.fieldDefaults, { - labelWidth: 100, - anchor: '100%' - }), - items: items - }); - - var inputPanel = me.formPanel.down('inputpanel'); - - var form = me.formPanel.getForm(); - - var submitText; - if (me.isCreate) { - if (me.submitText) { - submitText = me.submitText; - } else if (me.isAdd) { - submitText = gettext('Add'); - } else if (me.isRemove) { - submitText = gettext('Remove'); - } else { - submitText = gettext('Create'); - } - } else { - submitText = me.submitText || gettext('OK'); - } - - var submitBtn = Ext.create('Ext.Button', { - reference: 'submitbutton', - text: submitText, - disabled: !me.isCreate, - handler: function() { - me.submit(); - } - }); - - var resetBtn = Ext.create('Ext.Button', { - text: 'Reset', - disabled: true, - handler: function(){ - form.reset(); - } - }); - - var set_button_status = function() { - var valid = form.isValid(); - var dirty = form.isDirty(); - submitBtn.setDisabled(!valid || !(dirty || me.isCreate)); - resetBtn.setDisabled(!dirty); - - if (inputPanel && inputPanel.hasAdvanced) { - // we want to show the advanced options - // as soon as some of it is not valid - var advancedItems = me.down('#advancedContainer').query('field'); - var valid = true; - advancedItems.forEach(function(field) { - if (!field.isValid()) { - valid = false; - } - }); - - if (!valid) { - inputPanel.setAdvancedVisible(true); - me.down('#advancedcb').setValue(true); - } - } - }; - - form.on('dirtychange', set_button_status); - form.on('validitychange', set_button_status); - - var colwidth = 300; - if (me.fieldDefaults && me.fieldDefaults.labelWidth) { - colwidth += me.fieldDefaults.labelWidth - 100; - } - - var twoColumn = inputPanel && - (inputPanel.column1 || inputPanel.column2); - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, me.isCreate, me.isAdd); - } - - if (me.isCreate) { - me.buttons = [ submitBtn ] ; - } else { - me.buttons = [ submitBtn, resetBtn ]; - } - - if (inputPanel && inputPanel.hasAdvanced) { - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - inputPanel.setAdvancedVisible(advchecked); - me.buttons.unshift( - { - xtype: 'proxmoxcheckbox', - itemId: 'advancedcb', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - stateId: 'proxmox-advanced-cb', - value: advchecked, - listeners: { - change: function(cb, val) { - inputPanel.setAdvancedVisible(val); - sp.set('proxmox-advanced-cb', val); - } - } - } - ); - } - - var onlineHelp = me.onlineHelp; - if (!onlineHelp && inputPanel && inputPanel.onlineHelp) { - onlineHelp = inputPanel.onlineHelp; - } - - if (onlineHelp) { - var helpButton = Ext.create('Proxmox.button.Help'); - me.buttons.unshift(helpButton, '->'); - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', onlineHelp); - } - - Ext.applyIf(me, { - modal: true, - width: twoColumn ? colwidth*2 : colwidth, - border: false, - items: [ me.formPanel ] - }); - - me.callParent(); - - // always mark invalid fields - me.on('afterlayout', function() { - // on touch devices, the isValid function - // triggers a layout, which triggers an isValid - // and so on - // to prevent this we disable the layouting here - // and enable it afterwards - me.suspendLayout = true; - me.isValid(); - me.suspendLayout = false; - }); - - if (me.autoLoad) { - me.load(); - } - } -}); -Ext.define('Proxmox.window.PasswordEdit', { - extend: 'Proxmox.window.Edit', - alias: 'proxmoxWindowPasswordEdit', - - subject: gettext('Password'), - - url: '/api2/extjs/access/password', - - fieldDefaults: { - labelWidth: 120 - }, - - items: [ - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - allowBlank: false, - name: 'password', - listeners: { - change: function(field){ - field.next().validate(); - }, - blur: function(field){ - field.next().validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - allowBlank: false, - vtype: 'password', - initialPassField: 'password', - submitValue: false - }, - { - xtype: 'hiddenfield', - name: 'userid' - } - ], - - initComponent : function() { - var me = this; - - if (!me.userid) { - throw "no userid specified"; - } - - me.callParent(); - me.down('[name=userid]').setValue(me.userid); - } -}); -Ext.define('Proxmox.window.TaskProgress', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskProgress', - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: { - status: { defaultValue: 'unknown' }, - exitstatus: { defaultValue: 'unknown' } - } - }); - - me.on('destroy', statstore.stopUpdate); - - var getObjectValue = function(key, defaultValue) { - var rec = statstore.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }; - - var pbar = Ext.create('Ext.ProgressBar', { text: 'running...' }); - - me.mon(statstore, 'load', function() { - var status = getObjectValue('status'); - if (status === 'stopped') { - var exitstatus = getObjectValue('exitstatus'); - if (exitstatus == 'OK') { - pbar.reset(); - pbar.updateText("Done!"); - Ext.Function.defer(me.close, 1000, me); - } else { - me.close(); - Ext.Msg.alert('Task failed', exitstatus); - } - me.taskDone(exitstatus == 'OK'); - } - }); - - var descr = Proxmox.Utils.format_task_description(task.type, task.id); - - Ext.apply(me, { - title: gettext('Task') + ': ' + descr, - width: 300, - layout: 'auto', - modal: true, - bodyPadding: 5, - items: pbar, - buttons: [ - { - text: gettext('Details'), - handler: function() { - var win = Ext.create('Proxmox.window.TaskViewer', { - taskDone: me.taskDone, - upid: me.upid - }); - win.show(); - me.close(); - } - } - ] - }); - - me.callParent(); - - statstore.startUpdate(); - - pbar.wait(); - } -}); - -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ - -Ext.define('Proxmox.window.TaskViewer', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskViewer', - - extraTitle: '', // string to prepend after the generic task title - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statgrid; - - var rows = { - status: { - header: gettext('Status'), - defaultValue: 'unknown', - renderer: function(value) { - if (value != 'stopped') { - return value; - } - var es = statgrid.getObjectValue('exitstatus'); - if (es) { - return value + ': ' + es; - } - } - }, - exitstatus: { - visible: false - }, - type: { - header: gettext('Task type'), - required: true - }, - user: { - header: gettext('User name'), - required: true - }, - node: { - header: gettext('Node'), - required: true - }, - pid: { - header: gettext('Process ID'), - required: true - }, - starttime: { - header: gettext('Start Time'), - required: true, - renderer: Proxmox.Utils.render_timestamp - }, - upid: { - header: gettext('Unique task ID') - } - }; - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: rows - }); - - me.on('destroy', statstore.stopUpdate); - - var stop_task = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + task.node + "/tasks/" + me.upid, - waitMsgTarget: me, - method: 'DELETE', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var stop_btn1 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - var stop_btn2 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - statgrid = Ext.create('Proxmox.grid.ObjectGrid', { - title: gettext('Status'), - layout: 'fit', - tbar: [ stop_btn1 ], - rstore: statstore, - rows: rows, - border: false - }); - - var logView = Ext.create('Proxmox.panel.LogView', { - title: gettext('Output'), - tbar: [ stop_btn2 ], - border: false, - url: "/api2/extjs/nodes/" + task.node + "/tasks/" + me.upid + "/log" - }); - - me.mon(statstore, 'load', function() { - var status = statgrid.getObjectValue('status'); - - if (status === 'stopped') { - logView.scrollToEnd = false; - logView.requestUpdate(); - statstore.stopUpdate(); - me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK'); - } - - stop_btn1.setDisabled(status !== 'running'); - stop_btn2.setDisabled(status !== 'running'); - }); - - statstore.startUpdate(); - - Ext.apply(me, { - title: "Task viewer: " + task.desc + me.extraTitle, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [{ - xtype: 'tabpanel', - region: 'center', - items: [ logView, statgrid ] - }] - }); - - me.callParent(); - - logView.fireEvent('show', logView); - } -}); - -Ext.define('apt-pkglist', { - extend: 'Ext.data.Model', - fields: [ 'Package', 'Title', 'Description', 'Section', 'Arch', - 'Priority', 'Version', 'OldVersion', 'ChangeLogUrl', 'Origin' ], - idProperty: 'Package' -}); - -Ext.define('Proxmox.node.APT', { - extend: 'Ext.grid.GridPanel', - - xtype: 'proxmoxNodeAPT', - - upgradeBtn: undefined, - - columns: [ - { - header: gettext('Package'), - width: 200, - sortable: true, - dataIndex: 'Package' - }, - { - text: gettext('Version'), - columns: [ - { - header: gettext('current'), - width: 100, - sortable: false, - dataIndex: 'OldVersion' - }, - { - header: gettext('new'), - width: 100, - sortable: false, - dataIndex: 'Version' - } - ] - }, - { - header: gettext('Description'), - sortable: false, - dataIndex: 'Title', - flex: 1 - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'apt-pkglist', - groupField: 'Origin', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/apt/update" - }, - sorters: [ - { - property : 'Package', - direction: 'ASC' - } - ] - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl: '{[ "Origin: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})', - enableGroupingMenu: false - }); - - var rowBodyFeature = Ext.create('Ext.grid.feature.RowBody', { - getAdditionalData: function (data, rowIndex, record, orig) { - var headerCt = this.view.headerCt; - var colspan = headerCt.getColumnCount(); - return { - rowBody: '
' + - Ext.String.htmlEncode(data.Description) + - '
', - rowBodyCls: me.full_description ? '' : Ext.baseCSSPrefix + 'grid-row-body-hidden', - rowBodyColspan: colspan - }; - } - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store, true); - - var apt_command = function(cmd){ - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/apt/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.mon(win, 'close', reload); - } - }); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var update_btn = new Ext.Button({ - text: gettext('Refresh'), - handler: function() { - Proxmox.Utils.checked_command(function() { apt_command('update'); }); - } - }); - - var show_changelog = function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return; - } - - var view = Ext.createWidget('component', { - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Changelog') + ": " + rec.data.Package, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + me.nodename + "/apt/changelog", - params: { - name: rec.data.Package, - version: rec.data.Version - }, - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - view.update(Ext.htmlEncode(response.result.data)); - } - }); - - }; - - var changelog_btn = new Proxmox.button.Button({ - text: gettext('Changelog'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return false; - } - return true; - }, - handler: function(b, e, rec) { - show_changelog(rec); - } - }); - - var verbose_desc_checkbox = new Ext.form.field.Checkbox({ - boxLabel: gettext('Show details'), - value: false, - listeners: { - change: (f, val) => { - me.full_description = val; - me.getView().refresh(); - } - } - }); - - if (me.upgradeBtn) { - me.tbar = [ update_btn, me.upgradeBtn, changelog_btn, '->', verbose_desc_checkbox ]; - } else { - me.tbar = [ update_btn, changelog_btn, '->', verbose_desc_checkbox ]; - } - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-update', - selModel: sm, - viewConfig: { - stripeRows: false, - emptyText: '
' + gettext('No updates available.') + '
' - }, - features: [ groupingFeature, rowBodyFeature ], - listeners: { - activate: reload, - itemdblclick: function(v, rec) { - show_changelog(rec); - } - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.NetworkEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeNetworkEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.iftype) { - throw "no network device type specified"; - } - - me.isCreate = !me.iface; - - var iface_vtype; - - if (me.iftype === 'bridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'bond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'eth' && !me.isCreate) { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'vlan' && !me.isCreate) { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'OVSBridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'OVSBond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'OVSIntPort') { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'OVSPort') { - iface_vtype = 'InterfaceName'; - } else { - console.log(me.iftype); - throw "unknown network device type specified"; - } - - me.subject = Proxmox.Utils.render_network_iface_type(me.iftype); - - var column2 = []; - - if (!(me.iftype === 'OVSIntPort' || me.iftype === 'OVSPort' || - me.iftype === 'OVSBond')) { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Autostart'), - name: 'autostart', - uncheckedValue: 0, - checked: me.isCreate ? true : undefined - }); - } - - if (me.iftype === 'bridge') { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('VLAN aware'), - name: 'bridge_vlan_aware', - deleteEmpty: !me.isCreate - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'bridge_ports' - }); - } else if (me.iftype === 'OVSBridge') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'ovs_ports' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'OVSPort' || me.iftype === 'OVSIntPort') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'bond') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'slaves' - }); - - var policySelector = Ext.createWidget('bondPolicySelector', { - fieldLabel: gettext('Hash policy'), - name: 'bond_xmit_hash_policy', - deleteEmpty: !me.isCreate, - disabled: true - }); - - column2.push({ - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - value: me.isCreate ? 'balance-rr' : undefined, - listeners: { - change: function(f, value) { - if (value === 'balance-xor' || - value === '802.3ad') { - policySelector.setDisabled(false); - } else { - policySelector.setDisabled(true); - policySelector.setValue(''); - } - } - }, - allowBlank: false - }); - - column2.push(policySelector); - - } else if (me.iftype === 'OVSBond') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } - - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Comment'), - allowBlank: true, - nodename: me.nodename, - name: 'comments' - }); - - var url; - var method; - - if (me.isCreate) { - url = "/api2/extjs/nodes/" + me.nodename + "/network"; - method = 'POST'; - } else { - url = "/api2/extjs/nodes/" + me.nodename + "/network/" + me.iface; - method = 'PUT'; - } - - var column1 = [ - { - xtype: 'hiddenfield', - name: 'type', - value: me.iftype - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'iface', - value: me.iface, - vtype: iface_vtype, - allowBlank: false - } - ]; - - if (me.iftype === 'OVSBond') { - column1.push( - { - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - openvswitch: true, - value: me.isCreate ? 'active-backup' : undefined, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'ovs_bonds' - } - ); - } else { - - column1.push( - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: 'IPv4/CIDR', - vtype: 'IPCIDRAddress', - name: 'cidr' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway') + ' (IPv4)', - vtype: 'IPAddress', - name: 'gateway' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: 'IPv6/CIDR', - vtype: 'IP6CIDRAddress', - name: 'cidr6' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway') + ' (IPv6)', - vtype: 'IP6Address', - name: 'gateway6' - } - ); - } - - Ext.applyIf(me, { - url: url, - method: method, - items: { - xtype: 'inputpanel', - column1: column1, - column2: column2 - } - }); - - me.callParent(); - - if (me.isCreate) { - me.down('field[name=iface]').setValue(me.iface_default); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.type !== me.iftype) { - var msg = "Got unexpected device type"; - Ext.Msg.alert(gettext('Error'), msg, function() { - me.close(); - }); - return; - } - me.setValues(data); - me.isValid(); // trigger validation - } - }); - } - } -}); -Ext.define('proxmox-networks', { - extend: 'Ext.data.Model', - fields: [ - 'iface', 'type', 'active', 'autostart', - 'bridge_ports', 'slaves', - 'address', 'netmask', 'gateway', - 'address6', 'netmask6', 'gateway6', - 'cidr', 'cidr6', - 'comments' - ], - idProperty: 'iface' -}); - -Ext.define('Proxmox.node.NetworkView', { - extend: 'Ext.panel.Panel', - - alias: ['widget.proxmoxNodeNetworkView'], - - // defines what types of network devices we want to create - // order is always the same - types: ['bridge', 'bond', 'ovs'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseUrl = '/nodes/' + me.nodename + '/network'; - - var store = Ext.create('Ext.data.Store', { - model: 'proxmox-networks', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseUrl - }, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }); - - var reload = function() { - var changeitem = me.down('#changes'); - Proxmox.Utils.API2Request({ - url: baseUrl, - failure: function(response, opts) { - store.loadData({}); - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - changeitem.update(''); - changeitem.setHidden(true); - }, - success: function(response, opts) { - var result = Ext.decode(response.responseText); - store.loadData(result.data); - var changes = result.changes; - if (changes === undefined || changes === '') { - changes = gettext("No changes"); - changeitem.setHidden(true); - } else { - changeitem.update("
" + Ext.htmlEncode(changes) + "
"); - changeitem.setHidden(false); - } - } - }); - }; - - var run_editor = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iface: rec.data.iface, - iftype: rec.data.type - }); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: run_editor - }); - - var del_btn = new Ext.Button({ - text: gettext('Remove'), - disabled: true, - handler: function(){ - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var iface = rec.data.iface; - - Proxmox.Utils.API2Request({ - url: baseUrl + '/' + iface, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var set_button_status = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - - edit_btn.setDisabled(!rec); - del_btn.setDisabled(!rec); - }; - - var render_ports = function(value, metaData, record) { - if (value === 'bridge') { - return record.data.bridge_ports; - } else if (value === 'bond') { - return record.data.slaves; - } else if (value === 'OVSBridge') { - return record.data.ovs_ports; - } else if (value === 'OVSBond') { - return record.data.ovs_bonds; - } - }; - - var find_next_iface_id = function(prefix) { - var next; - for (next = 0; next <= 9999; next++) { - if (!store.getById(prefix + next.toString())) { - break; - } - } - return prefix + next.toString(); - }; - - var menu_items = []; - - if (me.types.indexOf('bridge') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bridge', - iface_default: find_next_iface_id('vmbr') - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('bond') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bond', - iface_default: find_next_iface_id('bond') - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('ovs') !== -1) { - if (menu_items.length > 0) { - menu_items.push({ xtype: 'menuseparator' }); - } - - menu_items.push( - { - text: Proxmox.Utils.render_network_iface_type('OVSBridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBridge', - iface_default: find_next_iface_id('vmbr') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSBond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBond', - iface_default: find_next_iface_id('bond') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSIntPort'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSIntPort' - }); - win.on('destroy', reload); - win.show(); - } - } - ); - } - - var renderer_generator = function(fieldname) { - return function(val, metaData, rec) { - var tmp = []; - if (rec.data[fieldname]) { - tmp.push(rec.data[fieldname]); - } - if (rec.data[fieldname + '6']) { - tmp.push(rec.data[fieldname + '6']); - } - return tmp.join('
') || ''; - }; - }; - - Ext.apply(me, { - layout: 'border', - tbar: [ - { - text: gettext('Create'), - menu: { - plain: true, - items: menu_items - } - }, ' ', - { - text: gettext('Revert'), - handler: function() { - Proxmox.Utils.API2Request({ - url: baseUrl, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - edit_btn, - del_btn - ], - items: [ - { - xtype: 'gridpanel', - stateful: true, - stateId: 'grid-node-network', - store: store, - region: 'center', - border: false, - columns: [ - { - header: gettext('Name'), - sortable: true, - dataIndex: 'iface' - }, - { - header: gettext('Type'), - sortable: true, - width: 120, - renderer: Proxmox.Utils.render_network_iface_type, - dataIndex: 'type' - }, - { - xtype: 'booleancolumn', - header: gettext('Active'), - width: 80, - sortable: true, - dataIndex: 'active', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText, - }, - { - xtype: 'booleancolumn', - header: gettext('Autostart'), - width: 80, - sortable: true, - dataIndex: 'autostart', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - xtype: 'booleancolumn', - header: gettext('VLAN aware'), - width: 80, - sortable: true, - dataIndex: 'bridge_vlan_aware', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - header: gettext('Ports/Slaves'), - dataIndex: 'type', - renderer: render_ports - }, - { - header: gettext('Bond Mode'), - dataIndex: 'bond_mode', - renderer: Proxmox.Utils.render_bond_mode, - }, - { - header: gettext('Hash Policy'), - hidden: true, - dataIndex: 'bond_xmit_hash_policy', - }, - { - header: gettext('IP address'), - sortable: true, - width: 120, - hidden: true, - dataIndex: 'address', - renderer: renderer_generator('address'), - }, - { - header: gettext('Subnet mask'), - width: 120, - sortable: true, - hidden: true, - dataIndex: 'netmask', - renderer: renderer_generator('netmask'), - }, - { - header: gettext('CIDR'), - width: 120, - sortable: true, - dataIndex: 'cidr', - renderer: renderer_generator('cidr'), - }, - { - header: gettext('Gateway'), - width: 120, - sortable: true, - dataIndex: 'gateway', - renderer: renderer_generator('gateway'), - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - flex: 1, - renderer: Ext.String.htmlEncode - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: run_editor - } - }, - { - border: false, - region: 'south', - autoScroll: true, - hidden: true, - itemId: 'changes', - tbar: [ - gettext('Pending changes') + ' (' + - gettext('Please reboot to activate changes') + ')' - ], - split: true, - bodyPadding: 5, - flex: 0.6, - html: gettext("No changes") - } - ], - }); - - me.callParent(); - reload(); - } -}); -Ext.define('Proxmox.node.DNSEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeDNSEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.items = [ - { - xtype: 'textfield', - fieldLabel: gettext('Search domain'), - name: 'search', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 1", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns1' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 2", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns2' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 3", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns3' - } - ]; - - Ext.applyIf(me, { - subject: gettext('DNS'), - url: "/api2/extjs/nodes/" + me.nodename + "/dns", - fieldDefaults: { - labelWidth: 120 - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('Proxmox.node.HostsView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxNodeHostsView', - - reload: function() { - var me = this; - me.store.load(); - }, - - tbar: [ - { - text: gettext('Save'), - disabled: true, - itemId: 'savebtn', - handler: function() { - var me = this.up('panel'); - Proxmox.Utils.API2Request({ - params: { - digest: me.digest, - data: me.down('#hostsfield').getValue() - }, - method: 'POST', - url: '/nodes/' + me.nodename + '/hosts', - waitMsgTarget: me, - success: function(response, opts) { - me.reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - text: gettext('Revert'), - disabled: true, - itemId: 'resetbtn', - handler: function() { - var me = this.up('panel'); - me.down('#hostsfield').reset(); - } - } - ], - - layout: 'fit', - - items: [ - { - xtype: 'textarea', - itemId: 'hostsfield', - fieldStyle: { - 'font-family': 'monospace', - 'white-space': 'pre' - }, - listeners: { - dirtychange: function(ta, dirty) { - var me = this.up('panel'); - me.down('#savebtn').setDisabled(!dirty); - me.down('#resetbtn').setDisabled(!dirty); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.store = Ext.create('Ext.data.Store', { - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/hosts", - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.store); - - me.mon(me.store, 'load', function(store, records, success) { - if (!success || records.length < 1) { - return; - } - me.digest = records[0].data.digest; - var data = records[0].data.data; - me.down('#hostsfield').setValue(data); - me.down('#hostsfield').resetOriginalValue(); - }); - - me.reload(); - } -}); -Ext.define('Proxmox.node.DNSView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeDNSView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var run_editor = function() { - var win = Ext.create('Proxmox.node.DNSEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/dns", - cwidth1: 130, - interval: 1000, - run_editor: run_editor, - rows: { - search: { - header: 'Search domain', - required: true, - renderer: Ext.htmlEncode - }, - dns1: { - header: gettext('DNS server') + " 1", - required: true, - renderer: Ext.htmlEncode - }, - dns2: { - header: gettext('DNS server') + " 2", - renderer: Ext.htmlEncode - }, - dns3: { - header: gettext('DNS server') + " 3", - renderer: Ext.htmlEncode - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); -Ext.define('Proxmox.node.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeTasks'], - stateful: true, - stateId: 'grid-node-tasks', - loadMask: true, - sortableColumns: false, - vmidFilter: 0, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.BufferedStore', { - pageSize: 500, - autoLoad: true, - remoteFilter: true, - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - startParam: 'start', - limitParam: 'limit', - url: "/api2/json/nodes/" + me.nodename + "/tasks" - } - }); - - var userfilter = ''; - var filter_errors = 0; - - var updateProxyParams = function() { - var params = { - errors: filter_errors - }; - if (userfilter) { - params.userfilter = userfilter; - } - if (me.vmidFilter) { - params.vmid = me.vmidFilter; - } - store.proxy.extraParams = params; - }; - - updateProxyParams(); - - var reload_task = Ext.create('Ext.util.DelayedTask',function() { - updateProxyParams(); - store.reload(); - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - var view_btn = new Ext.Button({ - text: gettext('View'), - disabled: true, - handler: run_task_viewer - }); - - Proxmox.Utils.monStoreErrors(me, store, true); - - Ext.apply(me, { - store: store, - viewConfig: { - trackOver: false, - stripeRows: false, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - tbar: [ - view_btn, '->', gettext('User name') +':', ' ', - { - xtype: 'textfield', - width: 200, - value: userfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - userfilter = field.getValue(); - reload_task.delay(500); - } - } - }, ' ', gettext('Only Errors') + ':', ' ', - { - xtype: 'checkbox', - hideLabel: true, - checked: filter_errors, - listeners: { - change: function(field, checked) { - filter_errors = checked ? 1 : 0; - reload_task.delay(10); - } - } - }, ' ' - ], - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 100, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 100, - renderer: function(value, metaData, record) { - return Ext.Date.format(value,"M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return "ERROR: " + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - selectionchange: function(v, selections) { - view_btn.setDisabled(!(selections && selections[0])); - }, - show: function() { reload_task.delay(10); }, - destroy: function() { reload_task.cancel(); } - } - }); - - me.callParent(); - - } -}); -Ext.define('proxmox-services', { - extend: 'Ext.data.Model', - fields: [ 'service', 'name', 'desc', 'state' ], - idProperty: 'service' -}); - -Ext.define('Proxmox.node.ServiceView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeServiceView'], - - startOnlyServices: {}, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 1000, - storeid: 'proxmox-services' + me.nodename, - model: 'proxmox-services', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/services" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - sortAfterUpdate: true, - sorters: [ - { - property : 'name', - direction: 'ASC' - } - ] - }); - - var view_service_log = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - var win = Ext.create('Ext.window.Window', { - title: gettext('Syslog') + ': ' + rec.data.service, - modal: true, - width: 800, - height: 400, - layout: 'fit', - items: { - xtype: 'proxmoxLogView', - url: "/api2/extjs/nodes/" + me.nodename + "/syslog?service=" + - rec.data.service, - log_select_timespan: 1 - } - }); - win.show(); - }; - - var service_cmd = function(cmd) { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/services/" + rec.data.service + "/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.loading = true; - }, - success: function(response, opts) { - rstore.startUpdate(); - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - }; - - var start_btn = new Ext.Button({ - text: gettext('Start'), - disabled: true, - handler: function(){ - service_cmd("start"); - } - }); - - var stop_btn = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: function(){ - service_cmd("stop"); - } - }); - - var restart_btn = new Ext.Button({ - text: gettext('Restart'), - disabled: true, - handler: function(){ - service_cmd("restart"); - } - }); - - var syslog_btn = new Ext.Button({ - text: gettext('Syslog'), - disabled: true, - handler: view_service_log - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - start_btn.disable(); - stop_btn.disable(); - restart_btn.disable(); - syslog_btn.disable(); - return; - } - var service = rec.data.service; - var state = rec.data.state; - - syslog_btn.enable(); - - if (me.startOnlyServices[service]) { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - } - stop_btn.disable(); - } else { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - stop_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - stop_btn.disable(); - } - } - }; - - me.mon(store, 'refresh', set_button_status); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.apply(me, { - store: store, - stateful: false, - tbar: [ start_btn, stop_btn, restart_btn, syslog_btn ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: gettext('Status'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - dataIndex: 'desc', - flex: 2 - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: view_service_log, - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeTimeEdit'], - - subject: gettext('Time zone'), - - width: 400, - - autoLoad: true, - - fieldDefaults: { - labelWidth: 70 - }, - - items: { - xtype: 'combo', - fieldLabel: gettext('Time zone'), - name: 'timezone', - queryMode: 'local', - store: Ext.create('Proxmox.data.TimezoneStore'), - displayField: 'zone', - editable: true, - anyMatch: true, - forceSelection: true, - allowBlank: false - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.url = "/api2/extjs/nodes/" + me.nodename + "/time"; - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeTimeView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var tzoffset = (new Date()).getTimezoneOffset()*60000; - var renderlocaltime = function(value) { - var servertime = new Date((value * 1000) + tzoffset); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }; - - var run_editor = function() { - var win = Ext.create('Proxmox.node.TimeEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/time", - cwidth1: 150, - interval: 1000, - run_editor: run_editor, - rows: { - timezone: { - header: gettext('Time zone'), - required: true - }, - localtime: { - header: gettext('Server time'), - required: true, - renderer: renderlocaltime - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); diff --git a/serverside/jsmod/6.0-4/proxmoxlib.js.original b/serverside/jsmod/6.0-4/proxmoxlib.js.original deleted file mode 100644 index e4e71b7..0000000 --- a/serverside/jsmod/6.0-4/proxmoxlib.js.original +++ /dev/null @@ -1,7357 +0,0 @@ -// 2.0-5 -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)) { - var console = { - dir: function() {}, - log: function() {} - }; -} - -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', { utilities: { - - // this singleton contains miscellaneous utilities - - yesText: gettext('Yes'), - noText: gettext('No'), - enabledText: gettext('Enabled'), - disabledText: gettext('Disabled'), - 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: { - zh_CN: 'Chinese (Simplified)', - zh_TW: 'Chinese (Traditional)', - ca: 'Catalan', - da: 'Danish', - en: 'English', - eu: 'Euskera (Basque)', - fr: 'French', - de: 'German', - it: 'Italian', - es: 'Spanish', - ja: 'Japanese', - nb: 'Norwegian (Bokmal)', - nn: 'Norwegian (Nynorsk)', - fa: 'Persian (Farsi)', - pl: 'Polish', - pt_BR: 'Portuguese (Brazil)', - ru: 'Russian', - sl: 'Slovenian', - sv: 'Swedish', - tr: 'Turkish' - }, - - render_language: function (value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (English)'; - } - var text = Proxmox.Utils.language_map[value]; - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - language_array: function() { - var 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"); - }, - - format_duration_long: function(ut) { - - var days = Math.floor(ut / 86400); - ut -= days*86400; - var hours = Math.floor(ut / 3600); - ut -= hours*3600; - var mins = Math.floor(ut / 60); - ut -= mins*60; - - var hours_str = '00' + hours.toString(); - hours_str = hours_str.substr(hours_str.length - 2); - var mins_str = "00" + mins.toString(); - mins_str = mins_str.substr(mins_str.length - 2); - var ut_str = "00" + ut.toString(); - ut_str = ut_str.substr(ut_str.length - 2); - - if (days) { - var 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; } - - var tm = new Ext.util.TextMetrics(); - var 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; - } - return (Proxmox.UserName !== '') && Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name); - }, - - authClear: function() { - if (Proxmox.LoggedOut) { - return undefined; - } - 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) { - var el = comp.el; - if (!el) { - return; - } - if (!msg) { - el.unmask(); - } else { - if (msg === true) { - el.mask(gettext("Loading...")); - } else { - el.mask(msg); - } - } - }, - - monStoreErrors: function(me, store, clearMaskBeforeLoad) { - if (clearMaskBeforeLoad) { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - Proxmox.Utils.setErrorMask(me, false); - }); - } else { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - if (!me.loadCount) { - me.loadCount = 0; // make sure it is numeric - Proxmox.Utils.setErrorMask(me, true); - } - }); - } - - // only works with 'proxmox' proxy - me.mon(store.proxy, 'afterload', function(proxy, request, success) { - me.loadCount++; - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - - var msg; - /*jslint nomen: true */ - var operation = request._operation; - var error = operation.getError(); - if (error.statusText) { - msg = error.statusText + ' (' + error.status + ')'; - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - extractRequestError: function(result, verbose) { - var 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) { - - var newopts = Ext.apply({ - waitMsg: gettext('Please wait...') - }, reqOpts); - - if (!newopts.url.match(/^\/api2/)) { - newopts.url = '/api2/extjs' + newopts.url; - } - delete newopts.callback; - - var 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); - } - } - var 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) {} - var 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); - - var 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', - //waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data; - - if (data.status !== 'Active') { - Ext.Msg.show({ - title: gettext('No valid subscription'), - icon: Ext.Msg.WARNING, - msg: 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)) { - Ext.Object.each(data, function(name, val) { - if (values.hasOwnProperty(name)) { - var 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; - } - }); - } - }, - - 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') ], - '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') ], - 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') ], - vzsuspend: [ 'CT', gettext('Suspend') ], - vzresume: [ 'CT', gettext('Resume') ], - hamigrate: [ 'HA', gettext('Migrate') ], - hastart: [ 'HA', gettext('Start') ], - hastop: [ 'HA', gettext('Stop') ], - 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: ['VM/CT', gettext('Backup') ], - 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') ] - }, - - format_task_description: function(type, id) { - var farray = Proxmox.Utils.task_desc_table[type]; - var text; - if (!farray) { - text = type; - if (id) { - type += ' ' + id; - } - return text; - } - var prefix = farray[0]; - text = farray[1]; - if (prefix) { - return prefix + ' ' + id + ' - ' + text; - } - return text; - }, - - format_size: function(size) { - /*jslint confusion: true */ - - var units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; - var num = 0; - - while (size >= 1024 && ((num++)+1) < units.length) { - size = size / 1024; - } - - return size.toFixed((num > 0)?2:0) + " " + units[num] + "B"; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - render_uptime: function(value) { - - var uptime = value; - - if (uptime === undefined) { - return ''; - } - - if (uptime <= 0) { - return '-'; - } - - return Proxmox.Utils.format_duration_long(uptime); - }, - - parse_task_upid: function(upid) { - var task = {}; - - var res = upid.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):([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); - task.starttime = parseInt(res[4], 16); - task.type = res[5]; - task.id = res[6]; - task.user = res[7]; - - task.desc = Proxmox.Utils.format_task_description(task.type, task.id); - - return task; - }, - - render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) { - var servertime = new Date(value * 1000); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }, - - get_help_info: function(section) { - var helpMap; - if (typeof proxmoxOnlineHelpInfo !== 'undefined') { - helpMap = proxmoxOnlineHelpInfo; - } else if (typeof pveOnlineHelpInfo !== 'undefined') { - // be backward compatible with older pve-doc-generators - helpMap = pveOnlineHelpInfo; - } else { - throw "no global OnlineHelpInfo map declared"; - } - - return helpMap[section]; - }, - - get_help_link: function(section) { - var info = Proxmox.Utils.get_help_info(section); - if (!info) { - return; - } - - return window.location.origin + info.link; - }, - - openXtermJsViewer: function(vmtype, vmid, nodename, vmname, cmd) { - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - xtermjs: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - cmd: cmd, - - }); - var 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() { - var me = this; - Ext.apply(me, me.utilities); - - var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])"; - var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")"; - var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})"; - var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")"; - var IPV4_CIDR_MASK = "([0-9]{1,2})"; - var 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 + "$"); - - var 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 + ")?::" + ")" + ")" + - ")"; - - 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 + ")$"); - - var 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+)?$"); - } -}); -// 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] <= 128); - }, - IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "
" + gettext('Valid CIDR Range') + ': 8-128', - 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\.:]/, - - IP64CIDRAddress: function(v) { - var result = Proxmox.Utils.IP64_cidr_match.exec(v); - if (result === null) { - return false; - } - if (result[1] !== undefined) { - return result[1] >= 8 && result[1] <= 128; - } else if (result[2] !== undefined) { - return result[2] >= 8 && result[2] <= 32; - } else { - return false; - } - }, - IP64CIDRAddressText: gettext('Example') + ': 192.168.1.1/24 2001:DB8::42/64', - IP64CIDRAddressMask: /[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', - - MacPrefix: function(v) { - return (/^[a-f0-9][02468ace](?::[a-f0-9]{2}){0,2}:?$/i).test(v); - }, - MacPrefixMask: /[a-fA-F0-9:]/, - MacPrefixText: gettext('Example') + ': 02:8f - ' + gettext('only unicast addresses are allowed'), - - 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'", - - 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", - - DnsOrIp: function(v) { - if (!Proxmox.Utils.DnsName_match.test(v) && - !Proxmox.Utils.IP64_match.test(v)) { - return false; - } - - return true; - }, - DnsOrIpText: gettext('Not a valid DNS name or IP address.'), - - 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'), - - password: function(val, field) { - if (field.initialPassField) { - var pwd = field.up('form').down( - '[name=' + field.initialPassField + ']'); - return (val == pwd.getValue()); - } - return true; - }, - - passwordText: gettext('Passwords do not match') -}); - -// Firefox 52+ Touchscreen bug -// see https://www.sencha.com/forum/showthread.php?336762-Examples-don-t-work-in-Firefox-52-touchscreen/page2 -// and https://bugzilla.proxmox.com/show_bug.cgi?id=1223 -Ext.define('EXTJS_23846.Element', { - override: 'Ext.dom.Element' -}, function(Element) { - var supports = Ext.supports, - proto = Element.prototype, - eventMap = proto.eventMap, - additiveEvents = proto.additiveEvents; - - if (Ext.os.is.Desktop && supports.TouchEvents && !supports.PointerEvents) { - eventMap.touchstart = 'mousedown'; - eventMap.touchmove = 'mousemove'; - eventMap.touchend = 'mouseup'; - eventMap.touchcancel = 'mouseup'; - - additiveEvents.mousedown = 'mousedown'; - additiveEvents.mousemove = 'mousemove'; - additiveEvents.mouseup = 'mouseup'; - additiveEvents.touchstart = 'touchstart'; - additiveEvents.touchmove = 'touchmove'; - additiveEvents.touchend = 'touchend'; - additiveEvents.touchcancel = 'touchcancel'; - - additiveEvents.pointerdown = 'mousedown'; - additiveEvents.pointermove = 'mousemove'; - additiveEvents.pointerup = 'mouseup'; - additiveEvents.pointercancel = 'mouseup'; - } -}); - -Ext.define('EXTJS_23846.Gesture', { - override: 'Ext.event.publisher.Gesture' -}, function(Gesture) { - var me = Gesture.instance; - - if (Ext.supports.TouchEvents && !Ext.isWebKit && Ext.os.is.Desktop) { - me.handledDomEvents.push('mousedown', 'mousemove', 'mouseup'); - me.registerEvents(); - } -}); - -Ext.define('EXTJS_18900.Pie', { - override: 'Ext.chart.series.Pie', - - // from 6.0.2 - betweenAngle: function (x, a, b) { - var pp = Math.PI * 2, - offset = this.rotationOffset; - - if (a === b) { - return false; - } - - if (!this.getClockwise()) { - x *= -1; - a *= -1; - b *= -1; - a -= offset; - b -= offset; - } else { - a += offset; - b += offset; - } - - x -= a; - b -= a; - - // Normalize, so that both x and b are in the [0,360) interval. - x %= pp; - b %= pp; - x += pp; - b += pp; - x %= pp; - b %= pp; - - // Because 360 * n angles will be normalized to 0, - // we need to treat b === 0 as a special case. - return x < b || b === 0; - }, -}); - -// 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 -// 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; - } -}); - -// '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('Proxmox.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 a grid/tree view, restoring the focus moves the view back to -// the previously focused item. Save scroll position before refocusing. -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('Proxmox.Datepicker', { - override: 'Ext.picker.Date', - hideMode: 'visibility' -}); - -// ExtJS 6.0.1 has no setSubmitValue() (although you find it in the docs). -// Note: this.submitValue is a boolean flag, whereas getSubmitValue() returns -// data to be submitted. -Ext.define('Proxmox.form.field.Text', { - override: 'Ext.form.field.Text', - - setSubmitValue: function(v) { - this.submitValue = v; - }, -}); - -// this should be fixed with ExtJS 6.0.2 -// make mousescrolling work in firefox in the containers overflowhandler -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. differs from the default onMouseWheel - // handler by using deltaY instead of wheelDeltaY and no normalizing, - // because it is already - onMouseWheelFirefox: function(e) { - e.stopEvent(); - var delta = e.browserEvent.deltaY || 0; - this.scrollBy(delta * this.wheelIncrement, false); - } - -}); - -// add '@' to the valid id -Ext.define('Proxmox.validIdReOverride', { - override: 'Ext.Component', - validIdRe: /^[a-z_][a-z0-9\-_\@]*$/i, -}); - -// 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); - } - } -}); -Ext.define('Proxmox.Mixin.CBind', { - extend: 'Ext.Mixin', - - mixinConfig: { - before: { - initComponent: 'cloneTemplates' - } - }, - - cloneTemplates: function() { - var me = this; - - if (typeof(me.cbindData) == "function") { - me.cbindData = me.cbindData(me.initialConfig) || {}; - } - - var getConfigValue = function(cname) { - - if (cname in me.initialConfig) { - return me.initialConfig[cname]; - } - if (cname in me.cbindData) { - return me.cbindData[cname]; - } - if (cname in me) { - return me[cname]; - } - throw "unable to get cbind data for '" + cname + "'"; - }; - - var applyCBind = function(obj) { - var cbind = obj.cbind, prop, cdata, cvalue, match, found; - if (!cbind) return; - - for (prop in cbind) { - cdata = cbind[prop]; - - found = false; - if (match = /^\{(!)?([a-z_][a-z0-9_]*)\}$/i.exec(cdata)) { - var cvalue = getConfigValue(match[2]); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else if (match = /^\{(!)?([a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)+)\}$/i.exec(cdata)) { - var keys = match[2].split('.'); - var cvalue = getConfigValue(keys.shift()); - keys.forEach(function(k) { - if (k in cvalue) { - cvalue = cvalue[k]; - } else { - throw "unable to get cbind data for '" + match[2] + "'"; - } - }); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else { - obj[prop] = cdata.replace(/{([a-z_][a-z0-9_]*)\}/ig, function(match, cname) { - var cvalue = getConfigValue(cname); - found = true; - return cvalue; - }); - } - if (!found) { - throw "unable to parse cbind template '" + cdata + "'"; - } - - } - }; - - if (me.cbind) { - applyCBind(me); - } - - var cloneTemplateArray = function(org) { - var copy, i, found, el, elcopy, arrayLength; - - arrayLength = org.length; - found = false; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - found = true; - break; - } - } - - if (!found) return org; // no need to copy - - copy = []; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - elcopy = cloneTemplateObject(el); - if (elcopy.cbind) { - applyCBind(elcopy); - } - copy.push(elcopy); - } else if (el.constructor == Array) { - elcopy = cloneTemplateArray(el); - copy.push(elcopy); - } else { - copy.push(el); - } - } - return copy; - }; - - var cloneTemplateObject = function(org) { - var res = {}, prop, el, copy; - for (prop in org) { - el = org[prop]; - if (el.constructor == Object && el.xtype) { - copy = cloneTemplateObject(el); - if (copy.cbind) { - applyCBind(copy); - } - res[prop] = copy; - } else if (el.constructor == Array) { - copy = cloneTemplateArray(el); - res[prop] = copy; - } else { - res[prop] = el; - } - } - return res; - }; - - var condCloneProperties = function() { - var prop, el, i, tmp; - - for (prop in me) { - el = me[prop]; - if (el === undefined || el === null) continue; - if (typeof(el) === 'object' && el.constructor == Object) { - if (el.xtype && prop != 'config') { - me[prop] = cloneTemplateObject(el); - } - } else if (el.constructor == Array) { - tmp = cloneTemplateArray(el); - me[prop] = tmp; - } - } - }; - - condCloneProperties(); - } -}); -/* A reader to store a single JSON Object (hash) into a storage. - * Also accepts an array containing a single hash. - * - * So it can read: - * - * example1: {data1: "xyz", data2: "abc"} - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * example2: [ {data1: "xyz", data2: "abc"} ] - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * If you set 'readArray', the reader expexts the object as array: - * - * example3: [ { key: "data1", value: "xyz", p2: "cde" }, { key: "data2", value: "abc", p2: "efg" }] - * returns [{key: "data1", value: "xyz", p2: "cde}, {key: "data2", value: "abc", p2: "efg"}] - * - * Note: The records can contain additional properties (like 'p2' above) when you use 'readArray' - * - * Additional feature: specify allowed properties with default values with 'rows' object - * - * var rows = { - * memory: { - * required: true, - * defaultValue: 512 - * } - * } - * - */ - -Ext.define('Proxmox.data.reader.JsonObject', { - extend: 'Ext.data.reader.Json', - alias : 'reader.jsonobject', - - readArray: false, - - rows: undefined, - - constructor: function(config) { - var me = this; - - Ext.apply(me, config || {}); - - me.callParent([config]); - }, - - getResponseData: function(response) { - var me = this; - - var data = []; - try { - var result = Ext.decode(response.responseText); - // get our data items inside the server response - var root = result[me.getRootProperty()]; - - if (me.readArray) { - - var rec_hash = {}; - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - rec_hash[rec.key] = rec; - } - }); - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - var rec = rec_hash[key]; - if (Ext.isDefined(rec)) { - if (!Ext.isDefined(rec.value)) { - rec.value = rowdef.defaultValue; - } - data.push(rec); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue} ); - } else if (rowdef.required) { - data.push({key: key, value: undefined }); - } - }); - } else { - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - data.push(rec); - } - }); - } - - } else { - - var org_root = root; - - if (Ext.isArray(org_root)) { - if (root.length == 1) { - root = org_root[0]; - } else { - root = {}; - } - } - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - if (Ext.isDefined(root[key])) { - data.push({key: key, value: root[key]}); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue}); - } else if (rowdef.required) { - data.push({key: key, value: undefined}); - } - }); - } else { - Ext.Object.each(root, function(key, value) { - data.push({key: key, value: value }); - }); - } - } - } - catch (ex) { - Ext.Error.raise({ - response: response, - json: response.responseText, - parseError: ex, - msg: 'Unable to parse the JSON returned by the server: ' + ex.toString() - }); - } - - return data; - } -}); - -Ext.define('Proxmox.RestProxy', { - extend: 'Ext.data.RestProxy', - alias : 'proxy.proxmox', - - pageParam : null, - startParam: null, - limitParam: null, - groupParam: null, - sortParam: null, - filterParam: null, - noCache : false, - - afterRequest: function(request, success) { - this.fireEvent('afterload', this, request, success); - return; - }, - - constructor: function(config) { - - Ext.applyIf(config, { - reader: { - type: 'json', - rootProperty: config.root || 'data' - } - }); - - this.callParent([config]); - } -}, function() { - - Ext.define('KeyValue', { - extend: "Ext.data.Model", - fields: [ 'key', 'value' ], - idProperty: 'key' - }); - - Ext.define('KeyValuePendingDelete', { - extend: "Ext.data.Model", - fields: [ 'key', 'value', 'pending', 'delete' ], - idProperty: 'key' - }); - - Ext.define('proxmox-tasks', { - extend: 'Ext.data.Model', - fields: [ - { name: 'starttime', type : 'date', dateFormat: 'timestamp' }, - { name: 'endtime', type : 'date', dateFormat: 'timestamp' }, - { name: 'pid', type: 'int' }, - 'node', 'upid', 'user', 'status', 'type', 'id' - ], - idProperty: 'upid' - }); - - Ext.define('proxmox-cluster-log', { - extend: 'Ext.data.Model', - fields: [ - { name: 'uid' , type: 'int' }, - { name: 'time', type : 'date', dateFormat: 'timestamp' }, - { name: 'pri', type: 'int' }, - { name: 'pid', type: 'int' }, - 'node', 'user', 'tag', 'msg', - { - name: 'id', - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - // compute unique ID - return info.uid + ':' + info.node; - } - } - ], - idProperty: 'id' - }); - -}); -/* Extends the Ext.data.Store type - * with startUpdate() and stopUpdate() methods - * to refresh the store data in the background - * Components using this store directly will flicker - * due to the redisplay of the element ater 'config.interval' ms - * - * Note that you have to call yourself startUpdate() for the background load - * to begin - */ -Ext.define('Proxmox.data.UpdateStore', { - extend: 'Ext.data.Store', - alias: 'store.update', - - isStopped: true, - - autoStart: false, - - destroy: function() { - var me = this; - me.stopUpdate(); - me.callParent(); - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.interval) { - config.interval = 3000; - } - - if (!config.storeid) { - throw "no storeid specified"; - } - - var load_task = new Ext.util.DelayedTask(); - - var run_load_task = function() { - if (me.isStopped) { - return; - } - - if (Proxmox.Utils.authOK()) { - var start = new Date(); - me.load(function() { - var runtime = (new Date()) - start; - var interval = config.interval + runtime*2; - load_task.delay(interval, run_load_task); - }); - } else { - load_task.delay(200, run_load_task); - } - }; - - Ext.apply(config, { - startUpdate: function() { - me.isStopped = false; - // run_load_task(); this makes problems with chrome - load_task.delay(1, run_load_task); - }, - stopUpdate: function() { - me.isStopped = true; - load_task.cancel(); - } - }); - - me.callParent([config]); - - me.load_task = load_task; - - if (me.autoStart) { - me.startUpdate(); - } - } -}); -/* - * The DiffStore is a in-memory store acting as proxy between a real store - * instance and a component. - * Its purpose is to redisplay the component *only* if the data has been changed - * inside the real store, to avoid the annoying visual flickering of using - * the real store directly. - * - * Implementation: - * The DiffStore monitors via mon() the 'load' events sent by the real store. - * On each 'load' event, the DiffStore compares its own content with the target - * store (call to cond_add_item()) and then fires a 'refresh' event. - * The 'refresh' event will automatically trigger a view refresh on the component - * who binds to this store. - */ - -/* Config properties: - * rstore: the realstore which will autorefresh its content from the API - * Only works if rstore has a model and use 'idProperty' - * sortAfterUpdate: sort the diffstore before rendering the view - */ -Ext.define('Proxmox.data.DiffStore', { - extend: 'Ext.data.Store', - alias: 'store.diff', - - sortAfterUpdate: false, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.rstore) { - throw "no rstore specified"; - } - - if (!config.rstore.model) { - throw "no rstore model specified"; - } - - var rstore = config.rstore; - - Ext.apply(config, { - model: rstore.model, - proxy: { type: 'memory' } - }); - - me.callParent([config]); - - var first_load = true; - - var cond_add_item = function(data, id) { - var olditem = me.getById(id); - if (olditem) { - olditem.beginEdit(); - Ext.Array.each(me.model.prototype.fields, function(field) { - if (olditem.data[field.name] !== data[field.name]) { - olditem.set(field.name, data[field.name]); - } - }); - olditem.endEdit(true); - olditem.commit(); - } else { - var newrec = Ext.create(me.model, data); - var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length; - me.insert(pos, newrec); - } - }; - - var loadFn = function(s, records, success) { - - if (!success) { - return; - } - - me.suspendEvents(); - - // getSource returns null if data is not filtered - // if it is filtered it returns all records - var allItems = me.getData().getSource() || me.getData(); - - // remove vanished items - allItems.each(function(olditem) { - var item = rstore.getById(olditem.getId()); - if (!item) { - me.remove(olditem); - } - }); - - rstore.each(function(item) { - cond_add_item(item.data, item.getId()); - }); - - me.filter(); - - if (me.sortAfterUpdate) { - me.sort(); - } - - first_load = false; - - me.resumeEvents(); - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - }; - - if (rstore.isLoaded()) { - // if store is already loaded, - // insert items instantly - loadFn(rstore, [], true); - } - - me.mon(rstore, 'load', loadFn); - } -}); -/* This store encapsulates data items which are organized as an Array of key-values Objects - * ie data[0] contains something like {key: "keyboard", value: "da"} -* -* Designed to work with the KeyValue model and the JsonObject data reader -*/ -Ext.define('Proxmox.data.ObjectStore', { - extend: 'Proxmox.data.UpdateStore', - - getRecord: function() { - var me = this; - var record = Ext.create('Ext.data.Model'); - me.getData().each(function(item) { - record.set(item.data.key, item.data.value); - }); - record.commit(true); - return record; - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.storeid) { - config.storeid = 'proxmox-store-' + (++Ext.idSeed); - } - - Ext.applyIf(config, { - model: 'KeyValue', - proxy: { - type: 'proxmox', - url: config.url, - extraParams: config.extraParams, - reader: { - type: 'jsonobject', - rows: config.rows, - readArray: config.readArray, - rootProperty: config.root || 'data' - } - } - }); - - me.callParent([config]); - } -}); -/* Extends the Proxmox.data.UpdateStore type - * - * - */ -Ext.define('Proxmox.data.RRDStore', { - extend: 'Proxmox.data.UpdateStore', - alias: 'store.proxmoxRRDStore', - - setRRDUrl: function(timeframe, cf) { - var me = this; - if (!timeframe) { - timeframe = me.timeframe; - } - - if (!cf) { - cf = me.cf; - } - - me.proxy.url = me.rrdurl + "?timeframe=" + timeframe + "&cf=" + cf; - }, - - proxy: { - type: 'proxmox' - }, - - timeframe: 'hour', - - cf: 'AVERAGE', - - constructor: function(config) { - var me = this; - - config = config || {}; - - // set default interval to 30seconds - if (!config.interval) { - config.interval = 30000; - } - - // set a new storeid - if (!config.storeid) { - config.storeid = 'rrdstore-' + (++Ext.idSeed); - } - - // rrdurl is required - if (!config.rrdurl) { - throw "no rrdurl specified"; - } - - var stateid = 'proxmoxRRDTypeSelection'; - var sp = Ext.state.Manager.getProvider(); - var stateinit = sp.get(stateid); - - if (stateinit) { - if(stateinit.timeframe !== me.timeframe || stateinit.cf !== me.rrdcffn){ - me.timeframe = stateinit.timeframe; - me.rrdcffn = stateinit.cf; - } - } - - me.callParent([config]); - - me.setRRDUrl(); - me.mon(sp, 'statechange', function(prov, key, state){ - if (key === stateid) { - if (state && state.id) { - if (state.timeframe !== me.timeframe || state.cf !== me.cf) { - me.timeframe = state.timeframe; - me.cf = state.cf; - me.setRRDUrl(); - me.reload(); - } - } - } - }); - } -}); -Ext.define('Timezone', { - extend: 'Ext.data.Model', - fields: ['zone'] -}); - -Ext.define('Proxmox.data.TimezoneStore', { - extend: 'Ext.data.Store', - model: 'Timezone', - data: [ - ['Africa/Abidjan'], - ['Africa/Accra'], - ['Africa/Addis_Ababa'], - ['Africa/Algiers'], - ['Africa/Asmara'], - ['Africa/Bamako'], - ['Africa/Bangui'], - ['Africa/Banjul'], - ['Africa/Bissau'], - ['Africa/Blantyre'], - ['Africa/Brazzaville'], - ['Africa/Bujumbura'], - ['Africa/Cairo'], - ['Africa/Casablanca'], - ['Africa/Ceuta'], - ['Africa/Conakry'], - ['Africa/Dakar'], - ['Africa/Dar_es_Salaam'], - ['Africa/Djibouti'], - ['Africa/Douala'], - ['Africa/El_Aaiun'], - ['Africa/Freetown'], - ['Africa/Gaborone'], - ['Africa/Harare'], - ['Africa/Johannesburg'], - ['Africa/Kampala'], - ['Africa/Khartoum'], - ['Africa/Kigali'], - ['Africa/Kinshasa'], - ['Africa/Lagos'], - ['Africa/Libreville'], - ['Africa/Lome'], - ['Africa/Luanda'], - ['Africa/Lubumbashi'], - ['Africa/Lusaka'], - ['Africa/Malabo'], - ['Africa/Maputo'], - ['Africa/Maseru'], - ['Africa/Mbabane'], - ['Africa/Mogadishu'], - ['Africa/Monrovia'], - ['Africa/Nairobi'], - ['Africa/Ndjamena'], - ['Africa/Niamey'], - ['Africa/Nouakchott'], - ['Africa/Ouagadougou'], - ['Africa/Porto-Novo'], - ['Africa/Sao_Tome'], - ['Africa/Tripoli'], - ['Africa/Tunis'], - ['Africa/Windhoek'], - ['America/Adak'], - ['America/Anchorage'], - ['America/Anguilla'], - ['America/Antigua'], - ['America/Araguaina'], - ['America/Argentina/Buenos_Aires'], - ['America/Argentina/Catamarca'], - ['America/Argentina/Cordoba'], - ['America/Argentina/Jujuy'], - ['America/Argentina/La_Rioja'], - ['America/Argentina/Mendoza'], - ['America/Argentina/Rio_Gallegos'], - ['America/Argentina/Salta'], - ['America/Argentina/San_Juan'], - ['America/Argentina/San_Luis'], - ['America/Argentina/Tucuman'], - ['America/Argentina/Ushuaia'], - ['America/Aruba'], - ['America/Asuncion'], - ['America/Atikokan'], - ['America/Bahia'], - ['America/Bahia_Banderas'], - ['America/Barbados'], - ['America/Belem'], - ['America/Belize'], - ['America/Blanc-Sablon'], - ['America/Boa_Vista'], - ['America/Bogota'], - ['America/Boise'], - ['America/Cambridge_Bay'], - ['America/Campo_Grande'], - ['America/Cancun'], - ['America/Caracas'], - ['America/Cayenne'], - ['America/Cayman'], - ['America/Chicago'], - ['America/Chihuahua'], - ['America/Costa_Rica'], - ['America/Cuiaba'], - ['America/Curacao'], - ['America/Danmarkshavn'], - ['America/Dawson'], - ['America/Dawson_Creek'], - ['America/Denver'], - ['America/Detroit'], - ['America/Dominica'], - ['America/Edmonton'], - ['America/Eirunepe'], - ['America/El_Salvador'], - ['America/Fortaleza'], - ['America/Glace_Bay'], - ['America/Godthab'], - ['America/Goose_Bay'], - ['America/Grand_Turk'], - ['America/Grenada'], - ['America/Guadeloupe'], - ['America/Guatemala'], - ['America/Guayaquil'], - ['America/Guyana'], - ['America/Halifax'], - ['America/Havana'], - ['America/Hermosillo'], - ['America/Indiana/Indianapolis'], - ['America/Indiana/Knox'], - ['America/Indiana/Marengo'], - ['America/Indiana/Petersburg'], - ['America/Indiana/Tell_City'], - ['America/Indiana/Vevay'], - ['America/Indiana/Vincennes'], - ['America/Indiana/Winamac'], - ['America/Inuvik'], - ['America/Iqaluit'], - ['America/Jamaica'], - ['America/Juneau'], - ['America/Kentucky/Louisville'], - ['America/Kentucky/Monticello'], - ['America/La_Paz'], - ['America/Lima'], - ['America/Los_Angeles'], - ['America/Maceio'], - ['America/Managua'], - ['America/Manaus'], - ['America/Marigot'], - ['America/Martinique'], - ['America/Matamoros'], - ['America/Mazatlan'], - ['America/Menominee'], - ['America/Merida'], - ['America/Mexico_City'], - ['America/Miquelon'], - ['America/Moncton'], - ['America/Monterrey'], - ['America/Montevideo'], - ['America/Montreal'], - ['America/Montserrat'], - ['America/Nassau'], - ['America/New_York'], - ['America/Nipigon'], - ['America/Nome'], - ['America/Noronha'], - ['America/North_Dakota/Center'], - ['America/North_Dakota/New_Salem'], - ['America/Ojinaga'], - ['America/Panama'], - ['America/Pangnirtung'], - ['America/Paramaribo'], - ['America/Phoenix'], - ['America/Port-au-Prince'], - ['America/Port_of_Spain'], - ['America/Porto_Velho'], - ['America/Puerto_Rico'], - ['America/Rainy_River'], - ['America/Rankin_Inlet'], - ['America/Recife'], - ['America/Regina'], - ['America/Resolute'], - ['America/Rio_Branco'], - ['America/Santa_Isabel'], - ['America/Santarem'], - ['America/Santiago'], - ['America/Santo_Domingo'], - ['America/Sao_Paulo'], - ['America/Scoresbysund'], - ['America/Shiprock'], - ['America/St_Barthelemy'], - ['America/St_Johns'], - ['America/St_Kitts'], - ['America/St_Lucia'], - ['America/St_Thomas'], - ['America/St_Vincent'], - ['America/Swift_Current'], - ['America/Tegucigalpa'], - ['America/Thule'], - ['America/Thunder_Bay'], - ['America/Tijuana'], - ['America/Toronto'], - ['America/Tortola'], - ['America/Vancouver'], - ['America/Whitehorse'], - ['America/Winnipeg'], - ['America/Yakutat'], - ['America/Yellowknife'], - ['Antarctica/Casey'], - ['Antarctica/Davis'], - ['Antarctica/DumontDUrville'], - ['Antarctica/Macquarie'], - ['Antarctica/Mawson'], - ['Antarctica/McMurdo'], - ['Antarctica/Palmer'], - ['Antarctica/Rothera'], - ['Antarctica/South_Pole'], - ['Antarctica/Syowa'], - ['Antarctica/Vostok'], - ['Arctic/Longyearbyen'], - ['Asia/Aden'], - ['Asia/Almaty'], - ['Asia/Amman'], - ['Asia/Anadyr'], - ['Asia/Aqtau'], - ['Asia/Aqtobe'], - ['Asia/Ashgabat'], - ['Asia/Baghdad'], - ['Asia/Bahrain'], - ['Asia/Baku'], - ['Asia/Bangkok'], - ['Asia/Beirut'], - ['Asia/Bishkek'], - ['Asia/Brunei'], - ['Asia/Choibalsan'], - ['Asia/Chongqing'], - ['Asia/Colombo'], - ['Asia/Damascus'], - ['Asia/Dhaka'], - ['Asia/Dili'], - ['Asia/Dubai'], - ['Asia/Dushanbe'], - ['Asia/Gaza'], - ['Asia/Harbin'], - ['Asia/Ho_Chi_Minh'], - ['Asia/Hong_Kong'], - ['Asia/Hovd'], - ['Asia/Irkutsk'], - ['Asia/Jakarta'], - ['Asia/Jayapura'], - ['Asia/Jerusalem'], - ['Asia/Kabul'], - ['Asia/Kamchatka'], - ['Asia/Karachi'], - ['Asia/Kashgar'], - ['Asia/Kathmandu'], - ['Asia/Kolkata'], - ['Asia/Krasnoyarsk'], - ['Asia/Kuala_Lumpur'], - ['Asia/Kuching'], - ['Asia/Kuwait'], - ['Asia/Macau'], - ['Asia/Magadan'], - ['Asia/Makassar'], - ['Asia/Manila'], - ['Asia/Muscat'], - ['Asia/Nicosia'], - ['Asia/Novokuznetsk'], - ['Asia/Novosibirsk'], - ['Asia/Omsk'], - ['Asia/Oral'], - ['Asia/Phnom_Penh'], - ['Asia/Pontianak'], - ['Asia/Pyongyang'], - ['Asia/Qatar'], - ['Asia/Qyzylorda'], - ['Asia/Rangoon'], - ['Asia/Riyadh'], - ['Asia/Sakhalin'], - ['Asia/Samarkand'], - ['Asia/Seoul'], - ['Asia/Shanghai'], - ['Asia/Singapore'], - ['Asia/Taipei'], - ['Asia/Tashkent'], - ['Asia/Tbilisi'], - ['Asia/Tehran'], - ['Asia/Thimphu'], - ['Asia/Tokyo'], - ['Asia/Ulaanbaatar'], - ['Asia/Urumqi'], - ['Asia/Vientiane'], - ['Asia/Vladivostok'], - ['Asia/Yakutsk'], - ['Asia/Yekaterinburg'], - ['Asia/Yerevan'], - ['Atlantic/Azores'], - ['Atlantic/Bermuda'], - ['Atlantic/Canary'], - ['Atlantic/Cape_Verde'], - ['Atlantic/Faroe'], - ['Atlantic/Madeira'], - ['Atlantic/Reykjavik'], - ['Atlantic/South_Georgia'], - ['Atlantic/St_Helena'], - ['Atlantic/Stanley'], - ['Australia/Adelaide'], - ['Australia/Brisbane'], - ['Australia/Broken_Hill'], - ['Australia/Currie'], - ['Australia/Darwin'], - ['Australia/Eucla'], - ['Australia/Hobart'], - ['Australia/Lindeman'], - ['Australia/Lord_Howe'], - ['Australia/Melbourne'], - ['Australia/Perth'], - ['Australia/Sydney'], - ['Europe/Amsterdam'], - ['Europe/Andorra'], - ['Europe/Athens'], - ['Europe/Belgrade'], - ['Europe/Berlin'], - ['Europe/Bratislava'], - ['Europe/Brussels'], - ['Europe/Bucharest'], - ['Europe/Budapest'], - ['Europe/Chisinau'], - ['Europe/Copenhagen'], - ['Europe/Dublin'], - ['Europe/Gibraltar'], - ['Europe/Guernsey'], - ['Europe/Helsinki'], - ['Europe/Isle_of_Man'], - ['Europe/Istanbul'], - ['Europe/Jersey'], - ['Europe/Kaliningrad'], - ['Europe/Kiev'], - ['Europe/Lisbon'], - ['Europe/Ljubljana'], - ['Europe/London'], - ['Europe/Luxembourg'], - ['Europe/Madrid'], - ['Europe/Malta'], - ['Europe/Mariehamn'], - ['Europe/Minsk'], - ['Europe/Monaco'], - ['Europe/Moscow'], - ['Europe/Oslo'], - ['Europe/Paris'], - ['Europe/Podgorica'], - ['Europe/Prague'], - ['Europe/Riga'], - ['Europe/Rome'], - ['Europe/Samara'], - ['Europe/San_Marino'], - ['Europe/Sarajevo'], - ['Europe/Simferopol'], - ['Europe/Skopje'], - ['Europe/Sofia'], - ['Europe/Stockholm'], - ['Europe/Tallinn'], - ['Europe/Tirane'], - ['Europe/Uzhgorod'], - ['Europe/Vaduz'], - ['Europe/Vatican'], - ['Europe/Vienna'], - ['Europe/Vilnius'], - ['Europe/Volgograd'], - ['Europe/Warsaw'], - ['Europe/Zagreb'], - ['Europe/Zaporozhye'], - ['Europe/Zurich'], - ['Indian/Antananarivo'], - ['Indian/Chagos'], - ['Indian/Christmas'], - ['Indian/Cocos'], - ['Indian/Comoro'], - ['Indian/Kerguelen'], - ['Indian/Mahe'], - ['Indian/Maldives'], - ['Indian/Mauritius'], - ['Indian/Mayotte'], - ['Indian/Reunion'], - ['Pacific/Apia'], - ['Pacific/Auckland'], - ['Pacific/Chatham'], - ['Pacific/Chuuk'], - ['Pacific/Easter'], - ['Pacific/Efate'], - ['Pacific/Enderbury'], - ['Pacific/Fakaofo'], - ['Pacific/Fiji'], - ['Pacific/Funafuti'], - ['Pacific/Galapagos'], - ['Pacific/Gambier'], - ['Pacific/Guadalcanal'], - ['Pacific/Guam'], - ['Pacific/Honolulu'], - ['Pacific/Johnston'], - ['Pacific/Kiritimati'], - ['Pacific/Kosrae'], - ['Pacific/Kwajalein'], - ['Pacific/Majuro'], - ['Pacific/Marquesas'], - ['Pacific/Midway'], - ['Pacific/Nauru'], - ['Pacific/Niue'], - ['Pacific/Norfolk'], - ['Pacific/Noumea'], - ['Pacific/Pago_Pago'], - ['Pacific/Palau'], - ['Pacific/Pitcairn'], - ['Pacific/Pohnpei'], - ['Pacific/Port_Moresby'], - ['Pacific/Rarotonga'], - ['Pacific/Saipan'], - ['Pacific/Tahiti'], - ['Pacific/Tarawa'], - ['Pacific/Tongatapu'], - ['Pacific/Wake'], - ['Pacific/Wallis'], - ['UTC'] - ] -}); -Ext.define('Proxmox.form.field.Integer',{ - extend: 'Ext.form.field.Number', - alias: 'widget.proxmoxintegerfield', - - config: { - deleteEmpty: false - }, - - allowDecimals: false, - allowExponential: false, - step: 1, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== undefined && val !== null && val !== '') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - } - -}); -Ext.define('Proxmox.form.field.Textfield', { - extend: 'Ext.form.field.Text', - alias: ['widget.proxmoxtextfield'], - - config: { - skipEmptyText: true, - - deleteEmpty: false, - }, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - getSubmitValue: function() { - var me = this; - - var value = this.processRawValue(this.getRawValue()); - if (value !== '') { - return value; - } - - return me.getSkipEmptyText() ? null: value; - }, - - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - this.validate(); - } -}); -Ext.define('Proxmox.DateTimeField', { - extend: 'Ext.form.FieldContainer', - xtype: 'promxoxDateTimeField', - - layout: 'hbox', - - referenceHolder: true, - - submitFormat: 'U', - - getValue: function() { - var me = this; - var d = me.lookupReference('dateentry').getValue(); - - if (d === undefined || d === null) { return null; } - - var t = me.lookupReference('timeentry').getValue(); - - if (t === undefined || t === null) { return null; } - - var offset = (t.getHours()*3600+t.getMinutes()*60)*1000; - - return new Date(d.getTime() + offset); - }, - - getSubmitValue: function() { - var me = this; - var format = me.submitFormat; - var value = me.getValue(); - - return value ? Ext.Date.format(value, format) : null; - }, - - items: [ - { - xtype: 'datefield', - editable: false, - reference: 'dateentry', - flex: 1, - format: 'Y-m-d' - }, - { - xtype: 'timefield', - reference: 'timeentry', - format: 'H:i', - width: 80, - value: '00:00', - increment: 60 - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - - var value = me.value || new Date(); - - me.lookupReference('dateentry').setValue(value); - me.lookupReference('timeentry').setValue(value); - - me.relayEvents(me.lookupReference('dateentry'), ['change']); - me.relayEvents(me.lookupReference('timeentry'), ['change']); - } -}); -Ext.define('Proxmox.form.Checkbox', { - extend: 'Ext.form.field.Checkbox', - alias: ['widget.proxmoxcheckbox'], - - config: { - defaultValue: undefined, - deleteDefaultValue: false, - deleteEmpty: false - }, - - inputValue: '1', - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - if ((val == me.getDefaultValue()) && me.getDeleteDefaultValue()) { - data['delete'] = me.getName(); - } else { - data[me.getName()] = val; - } - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - // also accept integer 1 as true - setRawValue: function(value) { - var me = this; - - if (value === 1) { - me.callParent([true]); - } else { - me.callParent([value]); - } - } - -}); -/* Key-Value ComboBox - * - * config properties: - * comboItems: an array of Key - Value pairs - * deleteEmpty: if set to true (default), an empty value received from the - * comboBox will reset the property to its default value - */ -Ext.define('Proxmox.form.KVComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.proxmoxKVComboBox', - - config: { - deleteEmpty: true - }, - - comboItems: undefined, - displayField: 'value', - valueField: 'key', - queryMode: 'local', - - // overide framework function to implement deleteEmpty behaviour - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null && val !== '' && val !== '__default__') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - validator: function(val) { - var me = this; - - if (me.editable || val === null || val === '') { - return true; - } - - if (me.store.getCount() > 0) { - var values = me.multiSelect ? val.split(me.delimiter) : [val]; - var items = me.store.getData().collect('value', 'data'); - if (Ext.Array.every(values, function(value) { - return Ext.Array.contains(items, value); - })) { - return true; - } - } - - // returns a boolean or string - /*jslint confusion: true */ - return "value '" + val + "' not allowed!"; - }, - - initComponent: function() { - var me = this; - - me.store = Ext.create('Ext.data.ArrayStore', { - model: 'KeyValue', - data : me.comboItems - }); - - if (me.initialConfig.editable === undefined) { - me.editable = false; - } - - me.callParent(); - }, - - setComboItems: function(items) { - var me = this; - - me.getStore().setData(items); - } - -}); -Ext.define('Proxmox.form.LanguageSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'proxmoxLanguageSelector', - - comboItems: Proxmox.Utils.language_array() -}); -/* - * ComboGrid component: a ComboBox where the dropdown menu (the - * "Picker") is a Grid with Rows and Columns expects a listConfig - * object with a columns property roughly based on the GridPicker from - * https://www.sencha.com/forum/showthread.php?299909 - * -*/ - -Ext.define('Proxmox.form.ComboGrid', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxComboGrid'], - - // this value is used as default value after load() - preferredValue: undefined, - - // hack: allow to select empty value - // seems extjs does not allow that when 'editable == false' - onKeyUp: function(e, t) { - var me = this; - var key = e.getKey(); - - if (!me.editable && me.allowBlank && !me.multiSelect && - (key == e.BACKSPACE || key == e.DELETE)) { - me.setValue(''); - } - - me.callParent(arguments); - }, - - config: { - skipEmptyText: false, - deleteEmpty: false, - }, - - // needed to trigger onKeyUp etc. - enableKeyEvents: true, - - editable: false, - - // override ExtJS method - // if the field has multiSelect enabled, the store is not loaded, and - // the displayfield == valuefield, it saves the rawvalue as an array - // but the getRawValue method is only defined in the textfield class - // (which has not to deal with arrays) an returns the string in the - // field (not an array) - // - // so if we have multiselect enabled, return the rawValue (which - // should be an array) and else we do callParent so - // it should not impact any other use of the class - getRawValue: function() { - var me = this; - if (me.multiSelect) { - return me.rawValue; - } else { - return me.callParent(); - } - }, - - getSubmitData: function() { - var me = this; - - let data = null; - if (!me.disabled && me.submitValue) { - let val = me.getSubmitValue(); - if (val !== null) { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - getSubmitValue: function() { - var me = this; - - var value = me.callParent(); - if (value !== '') { - return value; - } - - return me.getSkipEmptyText() ? null: value; - }, - - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - this.validate(); - }, - -// override ExtJS protected method - onBindStore: function(store, initial) { - var me = this, - picker = me.picker, - extraKeySpec, - valueCollectionConfig; - - // We're being bound, not unbound... - if (store) { - // If store was created from a 2 dimensional array with generated field names 'field1' and 'field2' - if (store.autoCreated) { - me.queryMode = 'local'; - me.valueField = me.displayField = 'field1'; - if (!store.expanded) { - me.displayField = 'field2'; - } - - // displayTpl config will need regenerating with the autogenerated displayField name 'field1' - me.setDisplayTpl(null); - } - if (!Ext.isDefined(me.valueField)) { - me.valueField = me.displayField; - } - - // Add a byValue index to the store so that we can efficiently look up records by the value field - // when setValue passes string value(s). - // The two indices (Ext.util.CollectionKeys) are configured unique: false, so that if duplicate keys - // are found, they are all returned by the get call. - // This is so that findByText and findByValue are able to return the *FIRST* matching value. By default, - // if unique is true, CollectionKey keeps the *last* matching value. - extraKeySpec = { - byValue: { - rootProperty: 'data', - unique: false - } - }; - extraKeySpec.byValue.property = me.valueField; - store.setExtraKeys(extraKeySpec); - - if (me.displayField === me.valueField) { - store.byText = store.byValue; - } else { - extraKeySpec.byText = { - rootProperty: 'data', - unique: false - }; - extraKeySpec.byText.property = me.displayField; - store.setExtraKeys(extraKeySpec); - } - - // We hold a collection of the values which have been selected, keyed by this field's valueField. - // This collection also functions as the selected items collection for the BoundList's selection model - valueCollectionConfig = { - rootProperty: 'data', - extraKeys: { - byInternalId: { - property: 'internalId' - }, - byValue: { - property: me.valueField, - rootProperty: 'data' - } - }, - // Whenever this collection is changed by anyone, whether by this field adding to it, - // or the BoundList operating, we must refresh our value. - listeners: { - beginupdate: me.onValueCollectionBeginUpdate, - endupdate: me.onValueCollectionEndUpdate, - scope: me - } - }; - - // This becomes our collection of selected records for the Field. - me.valueCollection = new Ext.util.Collection(valueCollectionConfig); - - // We use the selected Collection as our value collection and the basis - // for rendering the tag list. - - //proxmox override: since the picker is represented by a grid panel, - // we changed here the selection to RowModel - me.pickerSelectionModel = new Ext.selection.RowModel({ - mode: me.multiSelect ? 'SIMPLE' : 'SINGLE', - // There are situations when a row is selected on mousedown but then the mouse is dragged to another row - // and released. In these situations, the event target for the click event won't be the row where the mouse - // was released but the boundview. The view will then determine that it should fire a container click, and - // the DataViewModel will then deselect all prior selections. Setting `deselectOnContainerClick` here will - // prevent the model from deselecting. - deselectOnContainerClick: false, - enableInitialSelection: false, - pruneRemoved: false, - selected: me.valueCollection, - store: store, - listeners: { - scope: me, - lastselectedchanged: me.updateBindSelection - } - }); - - if (!initial) { - me.resetToDefault(); - } - - if (picker) { - picker.setSelectionModel(me.pickerSelectionModel); - if (picker.getStore() !== store) { - picker.bindStore(store); - } - } - } - }, - - // copied from ComboBox - createPicker: function() { - var me = this; - var picker; - - var pickerCfg = Ext.apply({ - // proxmox overrides: display a grid for selection - xtype: 'gridpanel', - id: me.pickerId, - pickerField: me, - floating: true, - hidden: true, - store: me.store, - displayField: me.displayField, - preserveScrollOnRefresh: true, - pageSize: me.pageSize, - tpl: me.tpl, - selModel: me.pickerSelectionModel, - focusOnToFront: false - }, me.listConfig, me.defaultListConfig); - - picker = me.picker || Ext.widget(pickerCfg); - - if (picker.getStore() !== me.store) { - picker.bindStore(me.store); - } - - if (me.pageSize) { - picker.pagingToolbar.on('beforechange', me.onPageChange, me); - } - - // proxmox overrides: pass missing method in gridPanel to its view - picker.refresh = function() { - picker.getSelectionModel().select(me.valueCollection.getRange()); - picker.getView().refresh(); - }; - picker.getNodeByRecord = function() { - picker.getView().getNodeByRecord(arguments); - }; - - // We limit the height of the picker to fit in the space above - // or below this field unless the picker has its own ideas about that. - if (!picker.initialConfig.maxHeight) { - picker.on({ - beforeshow: me.onBeforePickerShow, - scope: me - }); - } - picker.getSelectionModel().on({ - beforeselect: me.onBeforeSelect, - beforedeselect: me.onBeforeDeselect, - focuschange: me.onFocusChange, - selectionChange: function (sm, selectedRecords) { - var me = this; - if (selectedRecords.length) { - me.setValue(selectedRecords); - me.fireEvent('select', me, selectedRecords); - } - }, - scope: me - }); - - // hack for extjs6 - // when the clicked item is the same as the previously selected, - // it does not select the item - // instead we hide the picker - if (!me.multiSelect) { - picker.on('itemclick', function (sm,record) { - if (picker.getSelection()[0] === record) { - picker.hide(); - } - }); - } - - // when our store is not yet loaded, we increase - // the height of the gridpanel, so that we can see - // the loading mask - // - // we save the minheight to reset it after the load - picker.on('show', function() { - if (me.enableLoadMask) { - me.savedMinHeight = picker.getMinHeight(); - picker.setMinHeight(100); - } - }); - - picker.getNavigationModel().navigateOnSpace = false; - - return picker; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - queryMode: 'local', - matchFieldWidth: false - }); - - Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug - - Ext.applyIf(me.listConfig, { width: 400 }); - - me.callParent(); - - // Create the picker at an early stage, so it is available to store the previous selection - if (!me.picker) { - me.createPicker(); - } - - if (me.editable) { - // The trigger.picker causes first a focus event on the field then - // toggles the selection picker. Thus skip expanding in this case, - // else our focus listner expands and the picker.trigger then - // collapses it directly afterwards. - Ext.override(me.triggers.picker, { - onMouseDown : function (e) { - // copied "should we focus" check from Ext.form.trigger.Trigger - if (e.pointerType !== 'touch' && !this.field.owns(Ext.Element.getActiveElement())) { - me.skip_expand_on_focus = true; - } - this.callParent(arguments); - } - }); - - me.on("focus", function(me) { - if (!me.isExpanded && !me.skip_expand_on_focus) { - me.expand(); - } - me.skip_expand_on_focus = false; - }); - } - - me.mon(me.store, 'beforeload', function() { - if (!me.isDisabled()) { - me.enableLoadMask = true; - } - }); - - // hack: autoSelect does not work - me.mon(me.store, 'load', function(store, r, success, o) { - if (success) { - me.clearInvalid(); - - if (me.enableLoadMask) { - delete me.enableLoadMask; - - // if the picker exists, - // we reset its minheight to the saved var/0 - // we have to update the layout, otherwise the height - // gets not recalculated - if (me.picker) { - me.picker.setMinHeight(me.savedMinHeight || 0); - delete me.savedMinHeight; - me.picker.updateLayout(); - } - } - - var def = me.getValue() || me.preferredValue; - if (def) { - me.setValue(def, true); // sync with grid - } - var found = false; - if (def) { - if (Ext.isArray(def)) { - Ext.Array.each(def, function(v) { - if (store.findRecord(me.valueField, v)) { - found = true; - return false; // break - } - }); - } else { - found = store.findRecord(me.valueField, def); - } - } - - if (!found) { - var rec = me.store.first(); - if (me.autoSelect && rec && rec.data) { - def = rec.data[me.valueField]; - me.setValue(def, true); - } else { - me.setValue(me.editable ? def : '', true); - } - } - } - }); - } -}); -Ext.define('Proxmox.form.RRDTypeSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxRRDTypeSelector'], - - displayField: 'text', - valueField: 'id', - editable: false, - queryMode: 'local', - value: 'hour', - stateEvents: [ 'select' ], - stateful: true, - stateId: 'proxmoxRRDTypeSelection', - store: { - type: 'array', - fields: [ 'id', 'timeframe', 'cf', 'text' ], - data : [ - [ 'hour', 'hour', 'AVERAGE', - gettext('Hour') + ' (' + gettext('average') +')' ], - [ 'hourmax', 'hour', 'MAX', - gettext('Hour') + ' (' + gettext('maximum') + ')' ], - [ 'day', 'day', 'AVERAGE', - gettext('Day') + ' (' + gettext('average') + ')' ], - [ 'daymax', 'day', 'MAX', - gettext('Day') + ' (' + gettext('maximum') + ')' ], - [ 'week', 'week', 'AVERAGE', - gettext('Week') + ' (' + gettext('average') + ')' ], - [ 'weekmax', 'week', 'MAX', - gettext('Week') + ' (' + gettext('maximum') + ')' ], - [ 'month', 'month', 'AVERAGE', - gettext('Month') + ' (' + gettext('average') + ')' ], - [ 'monthmax', 'month', 'MAX', - gettext('Month') + ' (' + gettext('maximum') + ')' ], - [ 'year', 'year', 'AVERAGE', - gettext('Year') + ' (' + gettext('average') + ')' ], - [ 'yearmax', 'year', 'MAX', - gettext('Year') + ' (' + gettext('maximum') + ')' ] - ] - }, - // save current selection in the state Provider so RRDView can read it - getState: function() { - var ind = this.getStore().findExact('id', this.getValue()); - var rec = this.getStore().getAt(ind); - if (!rec) { - return; - } - return { - id: rec.data.id, - timeframe: rec.data.timeframe, - cf: rec.data.cf - }; - }, - // set selection based on last saved state - applyState : function(state) { - if (state && state.id) { - this.setValue(state.id); - } - } -}); -Ext.define('Proxmox.form.BondModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondModeSelector'], - - openvswitch: false, - - initComponent: function() { - var me = this; - - if (me.openvswitch) { - me.comboItems = Proxmox.Utils.bond_mode_array([ - 'active-backup', - 'balance-slb', - 'lacp-balance-slb', - 'lacp-balance-tcp', - ]); - } else { - me.comboItems = Proxmox.Utils.bond_mode_array([ - 'balance-rr', - 'active-backup', - 'balance-xor', - 'broadcast', - '802.3ad', - 'balance-tlb', - 'balance-alb', - ]); - } - - me.callParent(); - } -}); - -Ext.define('Proxmox.form.BondPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondPolicySelector'], - comboItems: [ - ['layer2', 'layer2'], - ['layer2+3', 'layer2+3'], - ['layer3+4', 'layer3+4'] - ] -}); - -Ext.define('Proxmox.form.NetworkSelectorController', { - extend: 'Ext.app.ViewController', - alias: 'controller.proxmoxNetworkSelectorController', - - init: function(view) { - var me = this; - - if (!view.nodename) { - throw "missing custom view config: nodename"; - } - view.getStore().getProxy().setUrl('/api2/json/nodes/'+ view.nodename + '/network'); - } -}); - -Ext.define('Proxmox.data.NetworkSelector', { - extend: 'Ext.data.Model', - fields: [ - {name: 'active'}, - {name: 'cidr'}, - {name: 'cidr6'}, - {name: 'address'}, - {name: 'address6'}, - {name: 'comments'}, - {name: 'iface'}, - {name: 'slaves'}, - {name: 'type'} - ] -}); - -Ext.define('Proxmox.form.NetworkSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.proxmoxNetworkSelector', - - controller: 'proxmoxNetworkSelectorController', - - nodename: 'localhost', - setNodename: function(nodename) { - this.nodename = nodename; - var networkSelectorStore = this.getStore(); - networkSelectorStore.removeAll(); - // because of manual local copy of data for ip4/6 - this.getPicker().refresh(); - if (networkSelectorStore && typeof networkSelectorStore.getProxy === 'function') { - networkSelectorStore.getProxy().setUrl('/api2/json/nodes/'+ nodename + '/network'); - networkSelectorStore.load(); - } - }, - // set default value to empty array, else it inits it with - // null and after the store load it is an empty array, - // triggering dirtychange - value: [], - valueField: 'cidr', - displayField: 'cidr', - store: { - autoLoad: true, - model: 'Proxmox.data.NetworkSelector', - proxy: { - type: 'proxmox' - }, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ], - filters: [ - function(item) { - return item.data.cidr; - } - ], - listeners: { - load: function(store, records, successfull) { - - if (successfull) { - records.forEach(function(record) { - if (record.data.cidr6) { - let dest = (record.data.cidr) ? record.copy(null) : record; - dest.data.cidr = record.data.cidr6; - dest.data.address = record.data.address6; - delete record.data.cidr6; - dest.data.comments = record.data.comments6; - delete record.data.comments6; - store.add(dest); - } - }); - } - } - } - }, - listConfig: { - width: 600, - columns: [ - { - - header: gettext('CIDR'), - dataIndex: 'cidr', - hideable: false, - flex: 1 - }, - { - - header: gettext('IP'), - dataIndex: 'address', - hidden: true, - }, - { - header: gettext('Interface'), - width: 90, - dataIndex: 'iface' - }, - { - header: gettext('Active'), - renderer: Proxmox.Utils.format_boolean, - width: 60, - dataIndex: 'active' - }, - { - header: gettext('Type'), - width: 80, - hidden: true, - dataIndex: 'type' - }, - { - header: gettext('Comment'), - flex: 2, - dataIndex: 'comments' - } - ] - } -}); -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - */ -Ext.define('Proxmox.button.Button', { - extend: 'Ext.button.Button', - alias: 'widget.proxmoxButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - - // Note: me.realHandler may be a string (see named scopes) - var realHandler = me.handler; - - me.handler = function(button, event) { - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn !== 'yes') { - return; - } - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }); - } else { - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }; - } - - me.callParent(); - - var grid; - if (!me.selModel && me.selModel !== null) { - grid = me.up('grid'); - if (grid && grid.selModel) { - me.selModel = grid.selModel; - } - } - - if (me.waitMsgTarget === true) { - grid = me.up('grid'); - if (grid) { - me.waitMsgTarget = grid; - } else { - throw "unable to find waitMsgTarget"; - } - } - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); - - -Ext.define('Proxmox.button.StdRemoveButton', { - extend: 'Proxmox.button.Button', - alias: 'widget.proxmoxStdRemoveButton', - - text: gettext('Remove'), - - disabled: true, - - config: { - baseurl: undefined - }, - - getUrl: function(rec) { - var me = this; - - return me.baseurl + '/' + rec.getId(); - }, - - // also works with names scopes - callback: function(options, success, response) {}, - - getRecordName: function(rec) { return rec.getId() }, - - confirmMsg: function (rec) { - var me = this; - - var name = me.getRecordName(rec); - return Ext.String.format( - gettext('Are you sure you want to remove entry {0}'), - "'" + name + "'"); - }, - - handler: function(btn, event, rec) { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.getUrl(rec), - method: 'DELETE', - waitMsgTarget: me.waitMsgTarget, - callback: function(options, success, response) { - Ext.callback(me.callback, me.scope, [options, success, response], 0, me); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -/* help button pointing to an online documentation - for components contained in a modal window -*/ -/*global - proxmoxOnlineHelpInfo -*/ -Ext.define('Proxmox.button.Help', { - extend: 'Ext.button.Button', - xtype: 'proxmoxHelpButton', - - text: gettext('Help'), - - // make help button less flashy by styling it like toolbar buttons - iconCls: ' x-btn-icon-el-default-toolbar-small fa fa-question-circle', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - - hidden: true, - - listenToGlobalEvent: true, - - controller: { - xclass: 'Ext.app.ViewController', - listen: { - global: { - proxmoxShowHelp: 'onProxmoxShowHelp', - proxmoxHideHelp: 'onProxmoxHideHelp' - } - }, - onProxmoxShowHelp: function(helpLink) { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.setOnlineHelp(helpLink); - me.show(); - } - }, - onProxmoxHideHelp: function() { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.hide(); - } - } - }, - - // this sets the link and the tooltip text - setOnlineHelp:function(blockid) { - var me = this; - - var info = Proxmox.Utils.get_help_info(blockid); - if (info) { - me.onlineHelp = blockid; - var title = info.title; - if (info.subtitle) { - title += ' - ' + info.subtitle; - } - me.setTooltip(title); - } - }, - - // helper to set the onlineHelp via a config object - setHelpConfig: function(config) { - var me = this; - me.setOnlineHelp(config.onlineHelp); - }, - - handler: function() { - var me = this; - var docsURI; - - if (me.onlineHelp) { - docsURI = Proxmox.Utils.get_help_link(me.onlineHelp); - } - - if (docsURI) { - window.open(docsURI); - } else { - Ext.Msg.alert(gettext('Help'), gettext('No Help available')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.callParent(); - - if (me.onlineHelp) { - me.setOnlineHelp(me.onlineHelp); // set tooltip - } - } -}); -/* Renders a list of key values objets - -mandatory config parameters: -rows: an object container where each propery is a key-value object we want to render - var rows = { - keyboard: { - header: gettext('Keyboard Layout'), - editor: 'Your.KeyboardEdit', - required: true - }, - -optional: -disabled: setting this parameter to true will disable selection and focus on the -proxmoxObjectGrid as well as greying out input elements. -Useful for a readonly tabular display - -*/ - -Ext.define('Proxmox.grid.ObjectGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.proxmoxObjectGrid'], - disabled: false, - hideHeaders: true, - - monStoreErrors: false, - - add_combobox_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxKVComboBox', - name: name, - comboItems: opts.comboItems, - value: opts.defaultValue, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_text_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxtextfield', - name: name, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - vtype: opts.vtype, - fieldLabel: text - } - } - }; - }, - - add_boolean_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue || 0, - header: text, - renderer: opts.renderer || Proxmox.Utils.format_boolean, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxcheckbox', - name: name, - uncheckedValue: 0, - defaultValue: opts.defaultValue || 0, - checked: opts.defaultValue ? true : false, - deleteDefaultValue: opts.deleteDefaultValue ? true : false, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_integer_row: function(name, text, opts) { - var me = this; - - opts = opts || {} - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxintegerfield', - name: name, - minValue: opts.minValue, - maxValue: opts.maxValue, - emptyText: gettext('Default'), - deleteEmpty: opts.deleteEmpty ? true : false, - value: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - editorConfig: {}, // default config passed to editor - - run_editor: function() { - var me = this; - - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rows = me.rows; - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - var config; - if (Ext.isString(rowdef.editor)) { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - win = Ext.create(rowdef.editor, config); - } else { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - Ext.apply(config, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', me.reload, me); - }, - - reload: function() { - var me = this; - me.rstore.load(); - }, - - getObjectValue: function(key, defaultValue) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }, - - renderKey: function(key, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - return rowdef.header || key; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - - var renderer = rowdef.renderer; - if (renderer) { - return renderer(value, metaData, record, rowIndex, colIndex, store); - } - - return value; - }, - - listeners: { - itemkeydown: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER) { - this.pressedIndex = index; - } - }, - itemkeyup: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER && index == this.pressedIndex) { - this.run_editor(); - } - - this.pressedIndex = undefined; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - var rstore = me.rstore; - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore, - sorters: [], - filters: [] - }); - - if (rows) { - Ext.Object.each(rows, function(key, rowdef) { - if (Ext.isDefined(rowdef.defaultValue)) { - store.add({ key: key, value: rowdef.defaultValue }); - } else if (rowdef.required) { - store.add({ key: key, value: undefined }); - } - }); - } - - if (me.sorterFn) { - store.sorters.add(Ext.create('Ext.util.Sorter', { - sorterFn: me.sorterFn - })); - } - - store.filters.add(Ext.create('Ext.util.Filter', { - filterFn: function(item) { - if (rows) { - var rowdef = rows[item.data.key]; - if (!rowdef || (rowdef.visible === false)) { - return false; - } - } - return true; - } - })); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.applyIf(me, { - store: store, - stateful: false, - columns: [ - { - header: gettext('Name'), - width: me.cwidth1 || 200, - dataIndex: 'key', - renderer: me.renderKey - }, - { - flex: 1, - header: gettext('Value'), - dataIndex: 'value', - renderer: me.renderValue - } - ] - }); - - me.callParent(); - - if (me.monStoreErrors) { - Proxmox.Utils.monStoreErrors(me, me.store); - } - } -}); -Ext.define('Proxmox.grid.PendingObjectGrid', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxPendingObjectGrid'], - - getObjectValue: function(key, defaultValue, pending) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - var value = rec.data.value; - if (pending) { - if (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') { - value = rec.data.pending; - } else if (rec.data['delete'] === 1) { - value = defaultValue; - } - } - - if (Ext.isDefined(value) && (value !== '')) { - return value; - } else { - return defaultValue; - } - } - return defaultValue; - }, - - hasPendingChanges: function(key) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var keys = rowdef.multiKey || [ key ]; - var pending = false; - - Ext.Array.each(keys, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && ( - (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') || - rec.data['delete'] === 1 - )) { - pending = true; - return false; // break - } - }); - - return pending; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var renderer = rowdef.renderer; - var current = ''; - var pendingdelete = ''; - var pending = ''; - - if (renderer) { - current = renderer(value, metaData, record, rowIndex, colIndex, store, false); - if (me.hasPendingChanges(key)) { - pending = renderer(record.data.pending, metaData, record, rowIndex, colIndex, store, true); - } - if (pending == current) { - pending = undefined; - } - } else { - current = value || ''; - pending = record.data.pending; - } - - if (record.data['delete']) { - var delete_all = true; - if (rowdef.multiKey) { - Ext.Array.each(rowdef.multiKey, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && rec.data['delete'] !== 1) { - delete_all = false; - return false; // break - } - }); - } - if (delete_all) { - pending = '
'+ current +'
'; - } - } - - if (pending) { - return current + '
' + pending + '
'; - } else { - return current; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - model: 'KeyValuePendingDelete', - readArray: true, - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - me.callParent(); - } -}); -Ext.define('Proxmox.panel.InputPanel', { - extend: 'Ext.panel.Panel', - alias: ['widget.inputpanel'], - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - border: false, - - // override this with an URL to a relevant chapter of the pve manual - // setting this will display a help button in our parent panel - onlineHelp: undefined, - - // will be set if the inputpanel has advanced items - hasAdvanced: false, - - // if the panel has advanced items, - // this will determine if they are shown by default - showAdvanced: false, - - // overwrite this to modify submit data - onGetValues: function(values) { - return values; - }, - - getValues: function(dirtyOnly) { - var me = this; - - if (Ext.isFunction(me.onGetValues)) { - dirtyOnly = false; - } - - var values = {}; - - Ext.Array.each(me.query('[isFormField]'), function(field) { - if (!dirtyOnly || field.isDirty()) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - return me.onGetValues(values); - }, - - setAdvancedVisible: function(visible) { - var me = this; - var advItems = me.getComponent('advancedContainer'); - if (advItems) { - advItems.setVisible(visible); - } - }, - - setValues: function(values) { - var me = this; - - var form = me.up('form'); - - Ext.iterate(values, function(fieldId, val) { - var field = me.query('[isFormField][name=' + fieldId + ']')[0]; - if (field) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - }, - - initComponent: function() { - var me = this; - - var items; - - if (me.items) { - me.columns = 1; - items = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.items - } - ]; - me.items = undefined; - } else if (me.column4) { - me.columns = 4; - items = [ - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column2 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column3 - }, - { - columnWidth: 0.25, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column4 - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else if (me.column1) { - me.columns = 2; - items = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column2 || [] // allow empty column - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else { - throw "unsupported config"; - } - - var advItems; - if (me.advancedItems) { - advItems = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.advancedItems - } - ]; - me.advancedItems = undefined; - } else if (me.advancedColumn1) { - advItems = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.advancedColumn1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.advancedColumn2 || [] // allow empty column - } - ]; - - me.advancedColumn1 = undefined; - me.advancedColumn2 = undefined; - - if (me.advancedColumnB) { - advItems.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.advancedColumnB - }); - me.advancedColumnB = undefined; - } - } - - if (advItems) { - me.hasAdvanced = true; - advItems.unshift({ - columnWidth: 1, - xtype: 'box', - hidden: false, - border: true, - autoEl: { - tag: 'hr' - } - }); - items.push({ - columnWidth: 1, - xtype: 'container', - itemId: 'advancedContainer', - hidden: !me.showAdvanced, - layout: 'column', - defaults: { - border: false - }, - items: advItems - }); - } - - if (me.useFieldContainer) { - Ext.apply(me, { - layout: 'fit', - items: Ext.apply(me.useFieldContainer, { - layout: 'column', - defaultType: 'container', - items: items - }) - }); - } else { - Ext.apply(me, { - layout: 'column', - defaultType: 'container', - items: items - }); - } - - me.callParent(); - } -}); -/* - * Display log entries in a panel with scrollbar - * The log entries are automatically refreshed via a background task, - * with newest entries comming at the bottom - */ -Ext.define('Proxmox.panel.LogView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxLogView', - - pageSize: 500, - viewBuffer: 50, - lineHeight: 16, - - scrollToEnd: true, - - // callback for load failure, used for ceph - failCallback: undefined, - - controller: { - xclass: 'Ext.app.ViewController', - - updateParams: function() { - var me = this; - var viewModel = me.getViewModel(); - var since = viewModel.get('since'); - var until = viewModel.get('until'); - if (viewModel.get('hide_timespan')) { - return; - } - - if (since > until) { - Ext.Msg.alert('Error', 'Since date must be less equal than Until date.'); - return; - } - - viewModel.set('params.since', Ext.Date.format(since, 'Y-m-d')); - viewModel.set('params.until', Ext.Date.format(until, 'Y-m-d') + ' 23:59:59'); - me.getView().loadTask.delay(200); - }, - - scrollPosBottom: function() { - var view = this.getView(); - var pos = view.getScrollY(); - var maxPos = view.getScrollable().getMaxPosition().y; - return maxPos - pos; - }, - - updateView: function(text, first, total) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - var content = me.lookup('content'); - var data = viewModel.get('data'); - - if (first === data.first && total === data.total && text.length === data.textlen) { - return; // same content, skip setting and scrolling - } - viewModel.set('data', { - first: first, - total: total, - textlen: text.length - }); - - var scrollPos = me.scrollPosBottom(); - - content.update(text); - - if (view.scrollToEnd && scrollPos <= 0) { - // we use setTimeout to work around scroll handling on touchscreens - setTimeout(function() { view.scrollTo(0, Infinity); }, 10); - } - }, - - doLoad: function() { - var me = this; - if (me.running) { - me.requested = true; - return; - } - me.running = true; - var view = me.getView(); - var viewModel = me.getViewModel(); - Proxmox.Utils.API2Request({ - url: me.getView().url, - params: viewModel.get('params'), - method: 'GET', - success: function(response) { - Proxmox.Utils.setErrorMask(me, false); - var total = response.result.total; - var lines = new Array(); - var first = Infinity; - - Ext.Array.each(response.result.data, function(line) { - if (first > line.n) { - first = line.n; - } - lines[line.n - 1] = Ext.htmlEncode(line.t); - }); - - lines.length = total; - me.updateView(lines.join('
'), first - 1, total); - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - }, - failure: function(response) { - if (view.failCallback) { - view.failCallback(response); - } else { - var msg = response.htmlStatus; - Proxmox.Utils.setErrorMask(me, msg); - } - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - } - }); - }, - - onScroll: function(x, y) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - - var lineHeight = view.lineHeight; - var line = view.getScrollY()/lineHeight; - var start = viewModel.get('params.start'); - var limit = viewModel.get('params.limit'); - var viewLines = view.getHeight()/lineHeight; - - var viewStart = Math.max(parseInt(line - 1 - view.viewBuffer, 10), 0); - var viewEnd = parseInt(line + viewLines + 1 + view.viewBuffer, 10); - - if (viewStart < start || viewEnd > (start+limit)) { - viewModel.set('params.start', - Math.max(parseInt(line - limit/2 + 10, 10), 0)); - view.loadTask.delay(200); - } - }, - - init: function(view) { - var me = this; - - if (!view.url) { - throw "no url specified"; - } - - var viewModel = this.getViewModel(); - var since = new Date(); - since.setDate(since.getDate() - 3); - viewModel.set('until', new Date()); - viewModel.set('since', since); - viewModel.set('params.limit', view.pageSize); - viewModel.set('hide_timespan', !view.log_select_timespan); - me.lookup('content').setStyle('line-height', view.lineHeight + 'px'); - - view.loadTask = new Ext.util.DelayedTask(me.doLoad, me); - - me.updateParams(); - view.task = Ext.TaskManager.start({ - run: function() { - if (!view.isVisible() || !view.scrollToEnd) { - return; - } - - if (me.scrollPosBottom() <= 1) { - view.loadTask.delay(200); - } - }, - interval: 1000 - }); - } - }, - - onDestroy: function() { - var me = this; - me.loadTask.cancel(); - Ext.TaskManager.stop(me.task); - }, - - // for user to initiate a load from outside - requestUpdate: function() { - var me = this; - me.loadTask.delay(200); - }, - - viewModel: { - data: { - until: null, - since: null, - hide_timespan: false, - data: { - start: 0, - total: 0, - textlen: 0 - }, - params: { - start: 0, - limit: 500, - } - } - }, - - layout: 'auto', - bodyPadding: 5, - scrollable: { - x: 'auto', - y: 'auto', - listeners: { - // we have to have this here, since we cannot listen to events - // of the scroller in the viewcontroller (extjs bug?), nor does - // the panel have a 'scroll' event' - scroll: { - fn: function(scroller, x, y) { - var controller = this.component.getController(); - if (controller) { // on destroy, controller can be gone - controller.onScroll(x,y); - } - }, - buffer: 200 - }, - } - }, - - tbar: { - bind: { - hidden: '{hide_timespan}' - }, - items: [ - '->', - 'Since: ', - { - xtype: 'datefield', - name: 'since_date', - reference: 'since', - format: 'Y-m-d', - bind: { - value: '{since}', - maxValue: '{until}' - } - }, - 'Until: ', - { - xtype: 'datefield', - name: 'until_date', - reference: 'until', - format: 'Y-m-d', - bind: { - value: '{until}', - minValue: '{since}' - } - }, - { - xtype: 'button', - text: 'Update', - handler: 'updateParams' - } - ], - }, - - items: [ - { - xtype: 'box', - reference: 'content', - style: { - font: 'normal 11px tahoma, arial, verdana, sans-serif', - 'white-space': 'pre' - }, - } - ] -}); -/* - * Display log entries in a panel with scrollbar - * The log entries are automatically refreshed via a background task, - * with newest entries comming at the bottom - */ -Ext.define('Proxmox.panel.JournalView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxJournalView', - - numEntries: 500, - lineHeight: 16, - - scrollToEnd: true, - - controller: { - xclass: 'Ext.app.ViewController', - - updateParams: function() { - var me = this; - var viewModel = me.getViewModel(); - var since = viewModel.get('since'); - var until = viewModel.get('until'); - - since.setHours(0, 0, 0, 0); - until.setHours(0, 0, 0, 0); - until.setDate(until.getDate()+1); - - me.getView().loadTask.delay(200, undefined, undefined, [ - false, - false, - Ext.Date.format(since, "U"), - Ext.Date.format(until, "U") - ]); - }, - - scrollPosBottom: function() { - var view = this.getView(); - var pos = view.getScrollY(); - var maxPos = view.getScrollable().getMaxPosition().y; - return maxPos - pos; - }, - - scrollPosTop: function() { - var view = this.getView(); - return view.getScrollY(); - }, - - updateScroll: function(livemode, num, scrollPos, scrollPosTop) { - var me = this; - var view = me.getView(); - - if (!livemode) { - setTimeout(function() { view.scrollTo(0, 0); }, 10); - } else if (view.scrollToEnd && scrollPos <= 0) { - setTimeout(function() { view.scrollTo(0, Infinity); }, 10); - } else if (!view.scrollToEnd && scrollPosTop < 20*view.lineHeight) { - setTimeout(function() { view.scrollTo(0, num*view.lineHeight + scrollPosTop); }, 10); - } - }, - - updateView: function(lines, livemode, top) { - var me = this; - var view = me.getView(); - var viewmodel = me.getViewModel(); - if (viewmodel.get('livemode') !== livemode) { - return; // we switched mode, do not update the content - } - var contentEl = me.lookup('content'); - - // save old scrollpositions - var scrollPos = me.scrollPosBottom(); - var scrollPosTop = me.scrollPosTop(); - - var newend = lines.shift(); - var newstart = lines.pop(); - - var num = lines.length; - var text = lines.map(Ext.htmlEncode).join('
'); - - if (!livemode) { - if (num) { - view.content = text; - } else { - view.content = 'nothing logged or no timespan selected'; - } - } else { - // update content - if (top && num) { - view.content = view.content ? text + '
' + view.content : text; - } else if (!top && num) { - view.content = view.content ? view.content + '
' + text : text; - } - - // update cursors - if (!top || !view.startcursor) { - view.startcursor = newstart; - } - - if (top || !view.endcursor) { - view.endcursor = newend; - } - } - - contentEl.update(view.content); - - me.updateScroll(livemode, num, scrollPos, scrollPosTop); - }, - - doLoad: function(livemode, top, since, until) { - var me = this; - if (me.running) { - me.requested = true; - return; - } - me.running = true; - var view = me.getView(); - var params = { - lastentries: view.numEntries || 500, - }; - if (livemode) { - if (!top && view.startcursor) { - params = { - startcursor: view.startcursor - }; - } else if (view.endcursor) { - params.endcursor = view.endcursor; - } - } else { - params = { - since: since, - until: until - }; - } - Proxmox.Utils.API2Request({ - url: view.url, - params: params, - waitMsgTarget: (!livemode) ? view : undefined, - method: 'GET', - success: function(response) { - Proxmox.Utils.setErrorMask(me, false); - var lines = response.result.data; - me.updateView(lines, livemode, top); - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - }, - failure: function(response) { - var msg = response.htmlStatus; - Proxmox.Utils.setErrorMask(me, msg); - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - } - }); - }, - - onScroll: function(x, y) { - var me = this; - var view = me.getView(); - var viewmodel = me.getViewModel(); - var livemode = viewmodel.get('livemode'); - if (!livemode) { - return; - } - - if (me.scrollPosTop() < 20*view.lineHeight) { - view.scrollToEnd = false; - view.loadTask.delay(200, undefined, undefined, [true, true]); - } else if (me.scrollPosBottom() <= 1) { - view.scrollToEnd = true; - } - }, - - init: function(view) { - var me = this; - - if (!view.url) { - throw "no url specified"; - } - - var viewmodel = me.getViewModel(); - var viewModel = this.getViewModel(); - var since = new Date(); - since.setDate(since.getDate() - 3); - viewModel.set('until', new Date()); - viewModel.set('since', since); - me.lookup('content').setStyle('line-height', view.lineHeight + 'px'); - - view.loadTask = new Ext.util.DelayedTask(me.doLoad, me, [true, false]); - - me.updateParams(); - view.task = Ext.TaskManager.start({ - run: function() { - if (!view.isVisible() || !view.scrollToEnd || !viewmodel.get('livemode')) { - return; - } - - if (me.scrollPosBottom() <= 1) { - view.loadTask.delay(200, undefined, undefined, [true, false]); - } - }, - interval: 1000 - }); - }, - - onLiveMode: function() { - var me = this; - var view = me.getView(); - delete view.startcursor; - delete view.endcursor; - delete view.content; - me.getViewModel().set('livemode', true); - view.scrollToEnd = true; - me.updateView([], true, false); - }, - - onTimespan: function() { - var me = this; - me.getViewModel().set('livemode', false); - me.updateView([], false); - } - }, - - onDestroy: function() { - var me = this; - me.loadTask.cancel(); - Ext.TaskManager.stop(me.task); - delete me.content; - }, - - // for user to initiate a load from outside - requestUpdate: function() { - var me = this; - me.loadTask.delay(200); - }, - - viewModel: { - data: { - livemode: true, - until: null, - since: null - } - }, - - layout: 'auto', - bodyPadding: 5, - scrollable: { - x: 'auto', - y: 'auto', - listeners: { - // we have to have this here, since we cannot listen to events - // of the scroller in the viewcontroller (extjs bug?), nor does - // the panel have a 'scroll' event' - scroll: { - fn: function(scroller, x, y) { - var controller = this.component.getController(); - if (controller) { // on destroy, controller can be gone - controller.onScroll(x,y); - } - }, - buffer: 200 - }, - } - }, - - tbar: { - - items: [ - '->', - { - xtype: 'segmentedbutton', - items: [ - { - text: gettext('Live Mode'), - bind: { - pressed: '{livemode}' - }, - handler: 'onLiveMode', - }, - { - text: gettext('Select Timespan'), - bind: { - pressed: '{!livemode}' - }, - handler: 'onTimespan', - } - ] - }, - { - xtype: 'box', - bind: { disabled: '{livemode}' }, - autoEl: { cn: gettext('Since') + ':' } - }, - { - xtype: 'datefield', - name: 'since_date', - reference: 'since', - format: 'Y-m-d', - bind: { - disabled: '{livemode}', - value: '{since}', - maxValue: '{until}' - } - }, - { - xtype: 'box', - bind: { disabled: '{livemode}' }, - autoEl: { cn: gettext('Until') + ':' } - }, - { - xtype: 'datefield', - name: 'until_date', - reference: 'until', - format: 'Y-m-d', - bind: { - disabled: '{livemode}', - value: '{until}', - minValue: '{since}' - } - }, - { - xtype: 'button', - text: 'Update', - reference: 'updateBtn', - handler: 'updateParams', - bind: { - disabled: '{livemode}' - } - } - ] - }, - - items: [ - { - xtype: 'box', - reference: 'content', - style: { - font: 'normal 11px tahoma, arial, verdana, sans-serif', - 'white-space': 'pre' - }, - } - ] -}); -Ext.define('Proxmox.widget.RRDChart', { - extend: 'Ext.chart.CartesianChart', - alias: 'widget.proxmoxRRDChart', - - unit: undefined, // bytes, bytespersecond, percent - - controller: { - xclass: 'Ext.app.ViewController', - - convertToUnits: function(value) { - var units = ['', 'k','M','G','T', 'P']; - var si = 0; - while(value >= 1000 && si < (units.length -1)){ - value = value / 1000; - si++; - } - - // javascript floating point weirdness - value = Ext.Number.correctFloat(value); - - // limit to 2 decimal points - value = Ext.util.Format.number(value, "0.##"); - - return value.toString() + " " + units[si]; - }, - - leftAxisRenderer: function(axis, label, layoutContext) { - var me = this; - - return me.convertToUnits(label); - }, - - onSeriesTooltipRender: function(tooltip, record, item) { - var me = this.getView(); - - var suffix = ''; - - if (me.unit === 'percent') { - suffix = '%'; - } else if (me.unit === 'bytes') { - suffix = 'B'; - } else if (me.unit === 'bytespersecond') { - suffix = 'B/s'; - } - - var prefix = item.field; - if (me.fieldTitles && me.fieldTitles[me.fields.indexOf(item.field)]) { - prefix = me.fieldTitles[me.fields.indexOf(item.field)]; - } - tooltip.setHtml(prefix + ': ' + this.convertToUnits(record.get(item.field)) + suffix + - '
' + new Date(record.get('time'))); - }, - - onAfterAnimation: function(chart, eopts) { - // if the undobuton is disabled, - // disable our tool - - var ourUndoZoomButton = chart.tools[0]; - var undoButton = chart.interactions[0].getUndoButton(); - ourUndoZoomButton.setDisabled(undoButton.isDisabled()); - } - }, - - width: 770, - height: 300, - animation: false, - interactions: [{ - type: 'crosszoom' - }], - axes: [{ - type: 'numeric', - position: 'left', - grid: true, - renderer: 'leftAxisRenderer', - //renderer: function(axis, label) { return label; }, - minimum: 0 - }, { - type: 'time', - position: 'bottom', - grid: true, - fields: ['time'] - }], - legend: { - docked: 'bottom' - }, - listeners: { - animationend: 'onAfterAnimation' - }, - - - initComponent: function() { - var me = this; - var series = {}; - - if (!me.store) { - throw "cannot work without store"; - } - - if (!me.fields) { - throw "cannot work without fields"; - } - - me.callParent(); - - // add correct label for left axis - var axisTitle = ""; - if (me.unit === 'percent') { - axisTitle = "%"; - } else if (me.unit === 'bytes') { - axisTitle = "Bytes"; - } else if (me.unit === 'bytespersecond') { - axisTitle = "Bytes/s"; - } else if (me.fieldTitles && me.fieldTitles.length === 1) { - axisTitle = me.fieldTitles[0]; - } else if (me.fields.length === 1) { - axisTitle = me.fields[0]; - } - - me.axes[0].setTitle(axisTitle); - - if (!me.noTool) { - me.addTool([{ - type: 'minus', - disabled: true, - tooltip: gettext('Undo Zoom'), - handler: function(){ - var undoButton = me.interactions[0].getUndoButton(); - if (undoButton.handler) { - undoButton.handler(); - } - } - },{ - type: 'restore', - tooltip: gettext('Toggle Legend'), - handler: function(){ - if (me.legend) { - me.legend.setVisible(!me.legend.isVisible()); - } - } - }]); - } - - // add a series for each field we get - me.fields.forEach(function(item, index){ - var title = item; - if (me.fieldTitles && me.fieldTitles[index]) { - title = me.fieldTitles[index]; - } - me.addSeries(Ext.apply( - { - type: 'line', - xField: 'time', - yField: item, - title: title, - fill: true, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - }, - tooltip: { - trackMouse: true, - renderer: 'onSeriesTooltipRender' - } - }, - me.seriesConfig - )); - }); - - // enable animation after the store is loaded - me.store.onAfter('load', function() { - me.setAnimation(true); - }, this, {single: true}); - } -}); -Ext.define('Proxmox.panel.GaugeWidget', { - extend: 'Ext.panel.Panel', - alias: 'widget.proxmoxGauge', - - defaults: { - style: { - 'text-align':'center' - } - }, - items: [ - { - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}

' - }, - { - xtype: 'polar', - height: 120, - border: false, - itemId: 'chart', - series: [{ - type: 'gauge', - value: 0, - colors: ['#f5f5f5'], - sectors: [0], - donut: 90, - needleLength: 100, - totalAngle: Math.PI - }], - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '', - textAlign: 'center', - textBaseline: 'bottom', - x: 125, - y: 110, - fontSize: 30 - }] - }, - { - xtype: 'box', - itemId: 'text' - } - ], - - header: false, - border: false, - - warningThreshold: 0.6, - criticalThreshold: 0.9, - warningColor: '#fc0', - criticalColor: '#FF6C59', - defaultColor: '#c2ddf2', - backgroundColor: '#f5f5f5', - - initialValue: 0, - - - updateValue: function(value, text) { - var me = this; - var color = me.defaultColor; - var attr = {}; - - if (value >= me.criticalThreshold) { - color = me.criticalColor; - } else if (value >= me.warningThreshold) { - color = me.warningColor; - } - - me.chart.series[0].setColors([color, me.backgroundColor]); - me.chart.series[0].setValue(value*100); - - me.valueSprite.setText(' '+(value*100).toFixed(0) + '%'); - attr.x = me.chart.getWidth()/2; - attr.y = me.chart.getHeight()-20; - if (me.spriteFontSize) { - attr.fontSize = me.spriteFontSize; - } - me.valueSprite.setAttributes(attr, true); - - if (text !== undefined) { - me.text.setHtml(text); - } - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.text = me.getComponent('text'); - me.chart = me.getComponent('chart'); - me.valueSprite = me.chart.getSurface('chart').get('valueSprite'); - } -}); -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ -Ext.define('Proxmox.window.Edit', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxWindowEdit', - - // autoLoad trigger a load() after component creation - autoLoad: false, - - resizable: false, - - // use this tio atimatically generate a title like - // Create: - subject: undefined, - - // set isCreate to true if you want a Create button (instead - // OK and RESET) - isCreate: false, - - // set to true if you want an Add button (instead of Create) - isAdd: false, - - // set to true if you want an Remove button (instead of Create) - isRemove: false, - - // custom submitText - submitText: undefined, - - backgroundDelay: 0, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - - // finds the first form field - defaultFocus: 'field[disabled=false][hidden=false]', - - showProgress: false, - - showTaskViewer: false, - - // gets called if we have a progress bar or taskview and it detected that - // the task finished. function(success) - taskDone: Ext.emptyFn, - - // gets called when the api call is finished, right at the beginning - // function(success, response, options) - apiCallDone: Ext.emptyFn, - - // assign a reference from docs, to add a help button docked to the - // bottom of the window. If undefined we magically fall back to the - // onlineHelp of our first item, if set. - onlineHelp: undefined, - - isValid: function() { - var me = this; - - var form = me.formPanel.getForm(); - return form.isValid(); - }, - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.formPanel.getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - setValues: function(values) { - var me = this; - - var form = me.formPanel.getForm(); - - Ext.iterate(values, function(fieldId, val) { - var field = form.findField(fieldId); - if (field && !field.up('inputpanel')) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setValues(values); - }); - }, - - submit: function() { - var me = this; - - var form = me.formPanel.getForm(); - - var values = me.getValues(); - Ext.Object.each(values, function(name, val) { - if (values.hasOwnProperty(name)) { - if (Ext.isArray(val) && !val.length) { - values[name] = ''; - } - } - }); - - if (me.digest) { - values.digest = me.digest; - } - - if (me.backgroundDelay) { - values.background_delay = me.backgroundDelay; - } - - var url = me.url; - if (me.method === 'DELETE') { - url = url + "?" + Ext.Object.toQueryString(values); - values = undefined; - } - - Proxmox.Utils.API2Request({ - url: url, - waitMsgTarget: me, - method: me.method || (me.backgroundDelay ? 'POST' : 'PUT'), - params: values, - failure: function(response, options) { - me.apiCallDone(false, response, options); - - if (response.result && response.result.errors) { - form.markInvalid(response.result.errors); - } - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = (me.backgroundDelay || me.showProgress || me.showTaskViewer) && - response.result.data ? true : false; - - me.apiCallDone(true, response, options); - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - me.hide(); - - var upid = response.result.data; - var viewerClass = me.showTaskViewer ? 'Viewer' : 'Progress'; - var win = Ext.create('Proxmox.window.Task' + viewerClass, { - upid: upid, - taskDone: me.taskDone, - listeners: { - destroy: function () { - me.close(); - } - } - }); - win.show(); - } else { - me.close(); - } - } - }); - }, - - load: function(options) { - var me = this; - - var form = me.formPanel.getForm(); - - options = options || {}; - - var newopts = Ext.apply({ - waitMsgTarget: me - }, options); - - var createWrapper = function(successFn) { - Ext.apply(newopts, { - url: me.url, - method: 'GET', - success: function(response, opts) { - form.clearInvalid(); - me.digest = response.result.data.digest; - if (successFn) { - successFn(response, opts); - } else { - me.setValues(response.result.data); - } - // hack: fix ExtJS bug - Ext.Array.each(me.query('radiofield'), function(f) { - f.resetOriginalValue(); - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() { - me.close(); - }); - } - }); - }; - - createWrapper(options.success); - - Proxmox.Utils.API2Request(newopts); - }, - - initComponent : function() { - var me = this; - - if (!me.url) { - throw "no url specified"; - } - - if (me.create) {throw "deprecated parameter, use isCreate";} - - var items = Ext.isArray(me.items) ? me.items : [ me.items ]; - - me.items = undefined; - - me.formPanel = Ext.create('Ext.form.Panel', { - url: me.url, - method: me.method || 'PUT', - trackResetOnLoad: true, - bodyPadding: 10, - border: false, - defaults: Ext.apply({}, me.defaults, { - border: false - }), - fieldDefaults: Ext.apply({}, me.fieldDefaults, { - labelWidth: 100, - anchor: '100%' - }), - items: items - }); - - var inputPanel = me.formPanel.down('inputpanel'); - - var form = me.formPanel.getForm(); - - var submitText; - if (me.isCreate) { - if (me.submitText) { - submitText = me.submitText; - } else if (me.isAdd) { - submitText = gettext('Add'); - } else if (me.isRemove) { - submitText = gettext('Remove'); - } else { - submitText = gettext('Create'); - } - } else { - submitText = me.submitText || gettext('OK'); - } - - var submitBtn = Ext.create('Ext.Button', { - reference: 'submitbutton', - text: submitText, - disabled: !me.isCreate, - handler: function() { - me.submit(); - } - }); - - var resetBtn = Ext.create('Ext.Button', { - text: 'Reset', - disabled: true, - handler: function(){ - form.reset(); - } - }); - - var set_button_status = function() { - var valid = form.isValid(); - var dirty = form.isDirty(); - submitBtn.setDisabled(!valid || !(dirty || me.isCreate)); - resetBtn.setDisabled(!dirty); - - if (inputPanel && inputPanel.hasAdvanced) { - // we want to show the advanced options - // as soon as some of it is not valid - var advancedItems = me.down('#advancedContainer').query('field'); - var valid = true; - advancedItems.forEach(function(field) { - if (!field.isValid()) { - valid = false; - } - }); - - if (!valid) { - inputPanel.setAdvancedVisible(true); - me.down('#advancedcb').setValue(true); - } - } - }; - - form.on('dirtychange', set_button_status); - form.on('validitychange', set_button_status); - - var colwidth = 300; - if (me.fieldDefaults && me.fieldDefaults.labelWidth) { - colwidth += me.fieldDefaults.labelWidth - 100; - } - - var twoColumn = inputPanel && - (inputPanel.column1 || inputPanel.column2); - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, me.isCreate, me.isAdd); - } - - if (me.isCreate) { - me.buttons = [ submitBtn ] ; - } else { - me.buttons = [ submitBtn, resetBtn ]; - } - - if (inputPanel && inputPanel.hasAdvanced) { - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - inputPanel.setAdvancedVisible(advchecked); - me.buttons.unshift( - { - xtype: 'proxmoxcheckbox', - itemId: 'advancedcb', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - stateId: 'proxmox-advanced-cb', - value: advchecked, - listeners: { - change: function(cb, val) { - inputPanel.setAdvancedVisible(val); - sp.set('proxmox-advanced-cb', val); - } - } - } - ); - } - - var onlineHelp = me.onlineHelp; - if (!onlineHelp && inputPanel && inputPanel.onlineHelp) { - onlineHelp = inputPanel.onlineHelp; - } - - if (onlineHelp) { - var helpButton = Ext.create('Proxmox.button.Help'); - me.buttons.unshift(helpButton, '->'); - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', onlineHelp); - } - - Ext.applyIf(me, { - modal: true, - width: twoColumn ? colwidth*2 : colwidth, - border: false, - items: [ me.formPanel ] - }); - - me.callParent(); - - // always mark invalid fields - me.on('afterlayout', function() { - // on touch devices, the isValid function - // triggers a layout, which triggers an isValid - // and so on - // to prevent this we disable the layouting here - // and enable it afterwards - me.suspendLayout = true; - me.isValid(); - me.suspendLayout = false; - }); - - if (me.autoLoad) { - me.load(); - } - } -}); -Ext.define('Proxmox.window.PasswordEdit', { - extend: 'Proxmox.window.Edit', - alias: 'proxmoxWindowPasswordEdit', - - subject: gettext('Password'), - - url: '/api2/extjs/access/password', - - fieldDefaults: { - labelWidth: 120 - }, - - items: [ - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - allowBlank: false, - name: 'password', - listeners: { - change: function(field){ - field.next().validate(); - }, - blur: function(field){ - field.next().validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - allowBlank: false, - vtype: 'password', - initialPassField: 'password', - submitValue: false - }, - { - xtype: 'hiddenfield', - name: 'userid' - } - ], - - initComponent : function() { - var me = this; - - if (!me.userid) { - throw "no userid specified"; - } - - me.callParent(); - me.down('[name=userid]').setValue(me.userid); - } -}); -Ext.define('Proxmox.window.TaskProgress', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskProgress', - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: { - status: { defaultValue: 'unknown' }, - exitstatus: { defaultValue: 'unknown' } - } - }); - - me.on('destroy', statstore.stopUpdate); - - var getObjectValue = function(key, defaultValue) { - var rec = statstore.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }; - - var pbar = Ext.create('Ext.ProgressBar', { text: 'running...' }); - - me.mon(statstore, 'load', function() { - var status = getObjectValue('status'); - if (status === 'stopped') { - var exitstatus = getObjectValue('exitstatus'); - if (exitstatus == 'OK') { - pbar.reset(); - pbar.updateText("Done!"); - Ext.Function.defer(me.close, 1000, me); - } else { - me.close(); - Ext.Msg.alert('Task failed', exitstatus); - } - me.taskDone(exitstatus == 'OK'); - } - }); - - var descr = Proxmox.Utils.format_task_description(task.type, task.id); - - Ext.apply(me, { - title: gettext('Task') + ': ' + descr, - width: 300, - layout: 'auto', - modal: true, - bodyPadding: 5, - items: pbar, - buttons: [ - { - text: gettext('Details'), - handler: function() { - var win = Ext.create('Proxmox.window.TaskViewer', { - taskDone: me.taskDone, - upid: me.upid - }); - win.show(); - me.close(); - } - } - ] - }); - - me.callParent(); - - statstore.startUpdate(); - - pbar.wait(); - } -}); - -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ - -Ext.define('Proxmox.window.TaskViewer', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskViewer', - - extraTitle: '', // string to prepend after the generic task title - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statgrid; - - var rows = { - status: { - header: gettext('Status'), - defaultValue: 'unknown', - renderer: function(value) { - if (value != 'stopped') { - return value; - } - var es = statgrid.getObjectValue('exitstatus'); - if (es) { - return value + ': ' + es; - } - } - }, - exitstatus: { - visible: false - }, - type: { - header: gettext('Task type'), - required: true - }, - user: { - header: gettext('User name'), - required: true - }, - node: { - header: gettext('Node'), - required: true - }, - pid: { - header: gettext('Process ID'), - required: true - }, - starttime: { - header: gettext('Start Time'), - required: true, - renderer: Proxmox.Utils.render_timestamp - }, - upid: { - header: gettext('Unique task ID') - } - }; - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: rows - }); - - me.on('destroy', statstore.stopUpdate); - - var stop_task = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + task.node + "/tasks/" + me.upid, - waitMsgTarget: me, - method: 'DELETE', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var stop_btn1 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - var stop_btn2 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - statgrid = Ext.create('Proxmox.grid.ObjectGrid', { - title: gettext('Status'), - layout: 'fit', - tbar: [ stop_btn1 ], - rstore: statstore, - rows: rows, - border: false - }); - - var logView = Ext.create('Proxmox.panel.LogView', { - title: gettext('Output'), - tbar: [ stop_btn2 ], - border: false, - url: "/api2/extjs/nodes/" + task.node + "/tasks/" + me.upid + "/log" - }); - - me.mon(statstore, 'load', function() { - var status = statgrid.getObjectValue('status'); - - if (status === 'stopped') { - logView.scrollToEnd = false; - logView.requestUpdate(); - statstore.stopUpdate(); - me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK'); - } - - stop_btn1.setDisabled(status !== 'running'); - stop_btn2.setDisabled(status !== 'running'); - }); - - statstore.startUpdate(); - - Ext.apply(me, { - title: "Task viewer: " + task.desc + me.extraTitle, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [{ - xtype: 'tabpanel', - region: 'center', - items: [ logView, statgrid ] - }] - }); - - me.callParent(); - - logView.fireEvent('show', logView); - } -}); - -Ext.define('apt-pkglist', { - extend: 'Ext.data.Model', - fields: [ 'Package', 'Title', 'Description', 'Section', 'Arch', - 'Priority', 'Version', 'OldVersion', 'ChangeLogUrl', 'Origin' ], - idProperty: 'Package' -}); - -Ext.define('Proxmox.node.APT', { - extend: 'Ext.grid.GridPanel', - - xtype: 'proxmoxNodeAPT', - - upgradeBtn: undefined, - - columns: [ - { - header: gettext('Package'), - width: 200, - sortable: true, - dataIndex: 'Package' - }, - { - text: gettext('Version'), - columns: [ - { - header: gettext('current'), - width: 100, - sortable: false, - dataIndex: 'OldVersion' - }, - { - header: gettext('new'), - width: 100, - sortable: false, - dataIndex: 'Version' - } - ] - }, - { - header: gettext('Description'), - sortable: false, - dataIndex: 'Title', - flex: 1 - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'apt-pkglist', - groupField: 'Origin', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/apt/update" - }, - sorters: [ - { - property : 'Package', - direction: 'ASC' - } - ] - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl: '{[ "Origin: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})', - enableGroupingMenu: false - }); - - var rowBodyFeature = Ext.create('Ext.grid.feature.RowBody', { - getAdditionalData: function (data, rowIndex, record, orig) { - var headerCt = this.view.headerCt; - var colspan = headerCt.getColumnCount(); - return { - rowBody: '
' + - Ext.String.htmlEncode(data.Description) + - '
', - rowBodyCls: me.full_description ? '' : Ext.baseCSSPrefix + 'grid-row-body-hidden', - rowBodyColspan: colspan - }; - } - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store, true); - - var apt_command = function(cmd){ - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/apt/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.mon(win, 'close', reload); - } - }); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var update_btn = new Ext.Button({ - text: gettext('Refresh'), - handler: function() { - Proxmox.Utils.checked_command(function() { apt_command('update'); }); - } - }); - - var show_changelog = function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return; - } - - var view = Ext.createWidget('component', { - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Changelog') + ": " + rec.data.Package, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + me.nodename + "/apt/changelog", - params: { - name: rec.data.Package, - version: rec.data.Version - }, - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - view.update(Ext.htmlEncode(response.result.data)); - } - }); - - }; - - var changelog_btn = new Proxmox.button.Button({ - text: gettext('Changelog'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return false; - } - return true; - }, - handler: function(b, e, rec) { - show_changelog(rec); - } - }); - - var verbose_desc_checkbox = new Ext.form.field.Checkbox({ - boxLabel: gettext('Show details'), - value: false, - listeners: { - change: (f, val) => { - me.full_description = val; - me.getView().refresh(); - } - } - }); - - if (me.upgradeBtn) { - me.tbar = [ update_btn, me.upgradeBtn, changelog_btn, '->', verbose_desc_checkbox ]; - } else { - me.tbar = [ update_btn, changelog_btn, '->', verbose_desc_checkbox ]; - } - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-update', - selModel: sm, - viewConfig: { - stripeRows: false, - emptyText: '
' + gettext('No updates available.') + '
' - }, - features: [ groupingFeature, rowBodyFeature ], - listeners: { - activate: reload, - itemdblclick: function(v, rec) { - show_changelog(rec); - } - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.NetworkEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeNetworkEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.iftype) { - throw "no network device type specified"; - } - - me.isCreate = !me.iface; - - var iface_vtype; - - if (me.iftype === 'bridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'bond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'eth' && !me.isCreate) { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'vlan' && !me.isCreate) { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'OVSBridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'OVSBond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'OVSIntPort') { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'OVSPort') { - iface_vtype = 'InterfaceName'; - } else { - console.log(me.iftype); - throw "unknown network device type specified"; - } - - me.subject = Proxmox.Utils.render_network_iface_type(me.iftype); - - var column2 = []; - - if (!(me.iftype === 'OVSIntPort' || me.iftype === 'OVSPort' || - me.iftype === 'OVSBond')) { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Autostart'), - name: 'autostart', - uncheckedValue: 0, - checked: me.isCreate ? true : undefined - }); - } - - if (me.iftype === 'bridge') { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('VLAN aware'), - name: 'bridge_vlan_aware', - deleteEmpty: !me.isCreate - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'bridge_ports' - }); - } else if (me.iftype === 'OVSBridge') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'ovs_ports' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'OVSPort' || me.iftype === 'OVSIntPort') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'bond') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'slaves' - }); - - var policySelector = Ext.createWidget('bondPolicySelector', { - fieldLabel: gettext('Hash policy'), - name: 'bond_xmit_hash_policy', - deleteEmpty: !me.isCreate, - disabled: true - }); - - column2.push({ - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - value: me.isCreate ? 'balance-rr' : undefined, - listeners: { - change: function(f, value) { - if (value === 'balance-xor' || - value === '802.3ad') { - policySelector.setDisabled(false); - } else { - policySelector.setDisabled(true); - policySelector.setValue(''); - } - } - }, - allowBlank: false - }); - - column2.push(policySelector); - - } else if (me.iftype === 'OVSBond') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } - - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Comment'), - allowBlank: true, - nodename: me.nodename, - name: 'comments' - }); - - var url; - var method; - - if (me.isCreate) { - url = "/api2/extjs/nodes/" + me.nodename + "/network"; - method = 'POST'; - } else { - url = "/api2/extjs/nodes/" + me.nodename + "/network/" + me.iface; - method = 'PUT'; - } - - var column1 = [ - { - xtype: 'hiddenfield', - name: 'type', - value: me.iftype - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'iface', - value: me.iface, - vtype: iface_vtype, - allowBlank: false - } - ]; - - if (me.iftype === 'OVSBond') { - column1.push( - { - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - openvswitch: true, - value: me.isCreate ? 'active-backup' : undefined, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'ovs_bonds' - } - ); - } else { - - column1.push( - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: 'IPv4/CIDR', - vtype: 'IPCIDRAddress', - name: 'cidr' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway') + ' (IPv4)', - vtype: 'IPAddress', - name: 'gateway' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: 'IPv6/CIDR', - vtype: 'IP6CIDRAddress', - name: 'cidr6' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway') + ' (IPv6)', - vtype: 'IP6Address', - name: 'gateway6' - } - ); - } - - Ext.applyIf(me, { - url: url, - method: method, - items: { - xtype: 'inputpanel', - column1: column1, - column2: column2 - } - }); - - me.callParent(); - - if (me.isCreate) { - me.down('field[name=iface]').setValue(me.iface_default); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.type !== me.iftype) { - var msg = "Got unexpected device type"; - Ext.Msg.alert(gettext('Error'), msg, function() { - me.close(); - }); - return; - } - me.setValues(data); - me.isValid(); // trigger validation - } - }); - } - } -}); -Ext.define('proxmox-networks', { - extend: 'Ext.data.Model', - fields: [ - 'iface', 'type', 'active', 'autostart', - 'bridge_ports', 'slaves', - 'address', 'netmask', 'gateway', - 'address6', 'netmask6', 'gateway6', - 'cidr', 'cidr6', - 'comments' - ], - idProperty: 'iface' -}); - -Ext.define('Proxmox.node.NetworkView', { - extend: 'Ext.panel.Panel', - - alias: ['widget.proxmoxNodeNetworkView'], - - // defines what types of network devices we want to create - // order is always the same - types: ['bridge', 'bond', 'ovs'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseUrl = '/nodes/' + me.nodename + '/network'; - - var store = Ext.create('Ext.data.Store', { - model: 'proxmox-networks', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseUrl - }, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }); - - var reload = function() { - var changeitem = me.down('#changes'); - Proxmox.Utils.API2Request({ - url: baseUrl, - failure: function(response, opts) { - store.loadData({}); - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - changeitem.update(''); - changeitem.setHidden(true); - }, - success: function(response, opts) { - var result = Ext.decode(response.responseText); - store.loadData(result.data); - var changes = result.changes; - if (changes === undefined || changes === '') { - changes = gettext("No changes"); - changeitem.setHidden(true); - } else { - changeitem.update("
" + Ext.htmlEncode(changes) + "
"); - changeitem.setHidden(false); - } - } - }); - }; - - var run_editor = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iface: rec.data.iface, - iftype: rec.data.type - }); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: run_editor - }); - - var del_btn = new Ext.Button({ - text: gettext('Remove'), - disabled: true, - handler: function(){ - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var iface = rec.data.iface; - - Proxmox.Utils.API2Request({ - url: baseUrl + '/' + iface, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var set_button_status = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - - edit_btn.setDisabled(!rec); - del_btn.setDisabled(!rec); - }; - - var render_ports = function(value, metaData, record) { - if (value === 'bridge') { - return record.data.bridge_ports; - } else if (value === 'bond') { - return record.data.slaves; - } else if (value === 'OVSBridge') { - return record.data.ovs_ports; - } else if (value === 'OVSBond') { - return record.data.ovs_bonds; - } - }; - - var find_next_iface_id = function(prefix) { - var next; - for (next = 0; next <= 9999; next++) { - if (!store.getById(prefix + next.toString())) { - break; - } - } - return prefix + next.toString(); - }; - - var menu_items = []; - - if (me.types.indexOf('bridge') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bridge', - iface_default: find_next_iface_id('vmbr') - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('bond') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bond', - iface_default: find_next_iface_id('bond') - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('ovs') !== -1) { - if (menu_items.length > 0) { - menu_items.push({ xtype: 'menuseparator' }); - } - - menu_items.push( - { - text: Proxmox.Utils.render_network_iface_type('OVSBridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBridge', - iface_default: find_next_iface_id('vmbr') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSBond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBond', - iface_default: find_next_iface_id('bond') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSIntPort'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSIntPort' - }); - win.on('destroy', reload); - win.show(); - } - } - ); - } - - var renderer_generator = function(fieldname) { - return function(val, metaData, rec) { - var tmp = []; - if (rec.data[fieldname]) { - tmp.push(rec.data[fieldname]); - } - if (rec.data[fieldname + '6']) { - tmp.push(rec.data[fieldname + '6']); - } - return tmp.join('
') || ''; - }; - }; - - Ext.apply(me, { - layout: 'border', - tbar: [ - { - text: gettext('Create'), - menu: { - plain: true, - items: menu_items - } - }, ' ', - { - text: gettext('Revert'), - handler: function() { - Proxmox.Utils.API2Request({ - url: baseUrl, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - edit_btn, - del_btn - ], - items: [ - { - xtype: 'gridpanel', - stateful: true, - stateId: 'grid-node-network', - store: store, - region: 'center', - border: false, - columns: [ - { - header: gettext('Name'), - sortable: true, - dataIndex: 'iface' - }, - { - header: gettext('Type'), - sortable: true, - width: 120, - renderer: Proxmox.Utils.render_network_iface_type, - dataIndex: 'type' - }, - { - xtype: 'booleancolumn', - header: gettext('Active'), - width: 80, - sortable: true, - dataIndex: 'active', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText, - }, - { - xtype: 'booleancolumn', - header: gettext('Autostart'), - width: 80, - sortable: true, - dataIndex: 'autostart', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - xtype: 'booleancolumn', - header: gettext('VLAN aware'), - width: 80, - sortable: true, - dataIndex: 'bridge_vlan_aware', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - header: gettext('Ports/Slaves'), - dataIndex: 'type', - renderer: render_ports - }, - { - header: gettext('Bond Mode'), - dataIndex: 'bond_mode', - renderer: Proxmox.Utils.render_bond_mode, - }, - { - header: gettext('Hash Policy'), - hidden: true, - dataIndex: 'bond_xmit_hash_policy', - }, - { - header: gettext('IP address'), - sortable: true, - width: 120, - hidden: true, - dataIndex: 'address', - renderer: renderer_generator('address'), - }, - { - header: gettext('Subnet mask'), - width: 120, - sortable: true, - hidden: true, - dataIndex: 'netmask', - renderer: renderer_generator('netmask'), - }, - { - header: gettext('CIDR'), - width: 120, - sortable: true, - dataIndex: 'cidr', - renderer: renderer_generator('cidr'), - }, - { - header: gettext('Gateway'), - width: 120, - sortable: true, - dataIndex: 'gateway', - renderer: renderer_generator('gateway'), - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - flex: 1, - renderer: Ext.String.htmlEncode - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: run_editor - } - }, - { - border: false, - region: 'south', - autoScroll: true, - hidden: true, - itemId: 'changes', - tbar: [ - gettext('Pending changes') + ' (' + - gettext('Please reboot to activate changes') + ')' - ], - split: true, - bodyPadding: 5, - flex: 0.6, - html: gettext("No changes") - } - ], - }); - - me.callParent(); - reload(); - } -}); -Ext.define('Proxmox.node.DNSEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeDNSEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.items = [ - { - xtype: 'textfield', - fieldLabel: gettext('Search domain'), - name: 'search', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 1", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns1' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 2", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns2' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 3", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns3' - } - ]; - - Ext.applyIf(me, { - subject: gettext('DNS'), - url: "/api2/extjs/nodes/" + me.nodename + "/dns", - fieldDefaults: { - labelWidth: 120 - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('Proxmox.node.HostsView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxNodeHostsView', - - reload: function() { - var me = this; - me.store.load(); - }, - - tbar: [ - { - text: gettext('Save'), - disabled: true, - itemId: 'savebtn', - handler: function() { - var me = this.up('panel'); - Proxmox.Utils.API2Request({ - params: { - digest: me.digest, - data: me.down('#hostsfield').getValue() - }, - method: 'POST', - url: '/nodes/' + me.nodename + '/hosts', - waitMsgTarget: me, - success: function(response, opts) { - me.reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - text: gettext('Revert'), - disabled: true, - itemId: 'resetbtn', - handler: function() { - var me = this.up('panel'); - me.down('#hostsfield').reset(); - } - } - ], - - layout: 'fit', - - items: [ - { - xtype: 'textarea', - itemId: 'hostsfield', - fieldStyle: { - 'font-family': 'monospace', - 'white-space': 'pre' - }, - listeners: { - dirtychange: function(ta, dirty) { - var me = this.up('panel'); - me.down('#savebtn').setDisabled(!dirty); - me.down('#resetbtn').setDisabled(!dirty); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.store = Ext.create('Ext.data.Store', { - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/hosts", - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.store); - - me.mon(me.store, 'load', function(store, records, success) { - if (!success || records.length < 1) { - return; - } - me.digest = records[0].data.digest; - var data = records[0].data.data; - me.down('#hostsfield').setValue(data); - me.down('#hostsfield').resetOriginalValue(); - }); - - me.reload(); - } -}); -Ext.define('Proxmox.node.DNSView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeDNSView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var run_editor = function() { - var win = Ext.create('Proxmox.node.DNSEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/dns", - cwidth1: 130, - interval: 1000, - run_editor: run_editor, - rows: { - search: { - header: 'Search domain', - required: true, - renderer: Ext.htmlEncode - }, - dns1: { - header: gettext('DNS server') + " 1", - required: true, - renderer: Ext.htmlEncode - }, - dns2: { - header: gettext('DNS server') + " 2", - renderer: Ext.htmlEncode - }, - dns3: { - header: gettext('DNS server') + " 3", - renderer: Ext.htmlEncode - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); -Ext.define('Proxmox.node.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeTasks'], - stateful: true, - stateId: 'grid-node-tasks', - loadMask: true, - sortableColumns: false, - vmidFilter: 0, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.BufferedStore', { - pageSize: 500, - autoLoad: true, - remoteFilter: true, - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - startParam: 'start', - limitParam: 'limit', - url: "/api2/json/nodes/" + me.nodename + "/tasks" - } - }); - - var userfilter = ''; - var filter_errors = 0; - - var updateProxyParams = function() { - var params = { - errors: filter_errors - }; - if (userfilter) { - params.userfilter = userfilter; - } - if (me.vmidFilter) { - params.vmid = me.vmidFilter; - } - store.proxy.extraParams = params; - }; - - updateProxyParams(); - - var reload_task = Ext.create('Ext.util.DelayedTask',function() { - updateProxyParams(); - store.reload(); - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - var view_btn = new Ext.Button({ - text: gettext('View'), - disabled: true, - handler: run_task_viewer - }); - - Proxmox.Utils.monStoreErrors(me, store, true); - - Ext.apply(me, { - store: store, - viewConfig: { - trackOver: false, - stripeRows: false, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - tbar: [ - view_btn, '->', gettext('User name') +':', ' ', - { - xtype: 'textfield', - width: 200, - value: userfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - userfilter = field.getValue(); - reload_task.delay(500); - } - } - }, ' ', gettext('Only Errors') + ':', ' ', - { - xtype: 'checkbox', - hideLabel: true, - checked: filter_errors, - listeners: { - change: function(field, checked) { - filter_errors = checked ? 1 : 0; - reload_task.delay(10); - } - } - }, ' ' - ], - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 100, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 100, - renderer: function(value, metaData, record) { - return Ext.Date.format(value,"M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return "ERROR: " + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - selectionchange: function(v, selections) { - view_btn.setDisabled(!(selections && selections[0])); - }, - show: function() { reload_task.delay(10); }, - destroy: function() { reload_task.cancel(); } - } - }); - - me.callParent(); - - } -}); -Ext.define('proxmox-services', { - extend: 'Ext.data.Model', - fields: [ 'service', 'name', 'desc', 'state' ], - idProperty: 'service' -}); - -Ext.define('Proxmox.node.ServiceView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeServiceView'], - - startOnlyServices: {}, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 1000, - storeid: 'proxmox-services' + me.nodename, - model: 'proxmox-services', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/services" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - sortAfterUpdate: true, - sorters: [ - { - property : 'name', - direction: 'ASC' - } - ] - }); - - var view_service_log = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - var win = Ext.create('Ext.window.Window', { - title: gettext('Syslog') + ': ' + rec.data.service, - modal: true, - width: 800, - height: 400, - layout: 'fit', - items: { - xtype: 'proxmoxLogView', - url: "/api2/extjs/nodes/" + me.nodename + "/syslog?service=" + - rec.data.service, - log_select_timespan: 1 - } - }); - win.show(); - }; - - var service_cmd = function(cmd) { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/services/" + rec.data.service + "/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.loading = true; - }, - success: function(response, opts) { - rstore.startUpdate(); - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - }; - - var start_btn = new Ext.Button({ - text: gettext('Start'), - disabled: true, - handler: function(){ - service_cmd("start"); - } - }); - - var stop_btn = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: function(){ - service_cmd("stop"); - } - }); - - var restart_btn = new Ext.Button({ - text: gettext('Restart'), - disabled: true, - handler: function(){ - service_cmd("restart"); - } - }); - - var syslog_btn = new Ext.Button({ - text: gettext('Syslog'), - disabled: true, - handler: view_service_log - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - start_btn.disable(); - stop_btn.disable(); - restart_btn.disable(); - syslog_btn.disable(); - return; - } - var service = rec.data.service; - var state = rec.data.state; - - syslog_btn.enable(); - - if (me.startOnlyServices[service]) { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - } - stop_btn.disable(); - } else { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - stop_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - stop_btn.disable(); - } - } - }; - - me.mon(store, 'refresh', set_button_status); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.apply(me, { - store: store, - stateful: false, - tbar: [ start_btn, stop_btn, restart_btn, syslog_btn ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: gettext('Status'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - dataIndex: 'desc', - flex: 2 - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: view_service_log, - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeTimeEdit'], - - subject: gettext('Time zone'), - - width: 400, - - autoLoad: true, - - fieldDefaults: { - labelWidth: 70 - }, - - items: { - xtype: 'combo', - fieldLabel: gettext('Time zone'), - name: 'timezone', - queryMode: 'local', - store: Ext.create('Proxmox.data.TimezoneStore'), - displayField: 'zone', - editable: true, - anyMatch: true, - forceSelection: true, - allowBlank: false - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.url = "/api2/extjs/nodes/" + me.nodename + "/time"; - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeTimeView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var tzoffset = (new Date()).getTimezoneOffset()*60000; - var renderlocaltime = function(value) { - var servertime = new Date((value * 1000) + tzoffset); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }; - - var run_editor = function() { - var win = Ext.create('Proxmox.node.TimeEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/time", - cwidth1: 150, - interval: 1000, - run_editor: run_editor, - rows: { - timezone: { - header: gettext('Time zone'), - required: true - }, - localtime: { - header: gettext('Server time'), - required: true, - renderer: renderlocaltime - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); diff --git a/serverside/jsmod/6.0-4/pvemanagerlib.js b/serverside/jsmod/6.0-4/pvemanagerlib.js deleted file mode 100644 index add3e49..0000000 --- a/serverside/jsmod/6.0-4/pvemanagerlib.js +++ /dev/null @@ -1,39779 +0,0 @@ -var pveOnlineHelpInfo = { - "ceph_rados_block_devices" : { - "link" : "/pve-docs/chapter-pvesm.html#ceph_rados_block_devices", - "title" : "Ceph RADOS Block Devices (RBD)" - }, - "chapter_ha_manager" : { - "link" : "/pve-docs/chapter-ha-manager.html#chapter_ha_manager", - "title" : "High Availability" - }, - "chapter_lvm" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_lvm", - "title" : "Logical Volume Manager (LVM)" - }, - "chapter_pct" : { - "link" : "/pve-docs/chapter-pct.html#chapter_pct", - "title" : "Proxmox Container Toolkit" - }, - "chapter_pve_firewall" : { - "link" : "/pve-docs/chapter-pve-firewall.html#chapter_pve_firewall", - "title" : "Proxmox VE Firewall" - }, - "chapter_pveceph" : { - "link" : "/pve-docs/chapter-pveceph.html#chapter_pveceph", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "chapter_pvecm" : { - "link" : "/pve-docs/chapter-pvecm.html#chapter_pvecm", - "title" : "Cluster Manager" - }, - "chapter_pvesr" : { - "link" : "/pve-docs/chapter-pvesr.html#chapter_pvesr", - "title" : "Storage Replication" - }, - "chapter_storage" : { - "link" : "/pve-docs/chapter-pvesm.html#chapter_storage", - "title" : "Proxmox VE Storage" - }, - "chapter_system_administration" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_system_administration", - "title" : "Host System Administration" - }, - "chapter_user_management" : { - "link" : "/pve-docs/chapter-pveum.html#chapter_user_management", - "title" : "User Management" - }, - "chapter_virtual_machines" : { - "link" : "/pve-docs/chapter-qm.html#chapter_virtual_machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "chapter_vzdump" : { - "link" : "/pve-docs/chapter-vzdump.html#chapter_vzdump", - "title" : "Backup and Restore" - }, - "chapter_zfs" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_zfs", - "title" : "ZFS on Linux" - }, - "datacenter_configuration_file" : { - "link" : "/pve-docs/pve-admin-guide.html#datacenter_configuration_file", - "title" : "Datacenter Configuration" - }, - "getting_help" : { - "link" : "/pve-docs/pve-admin-guide.html#getting_help", - "title" : "Getting Help" - }, - "gui_my_settings" : { - "link" : "/pve-docs/chapter-pve-gui.html#gui_my_settings", - "subtitle" : "My Settings", - "title" : "Graphical User Interface" - }, - "ha_manager_fencing" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_fencing", - "subtitle" : "Fencing", - "title" : "High Availability" - }, - "ha_manager_groups" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_groups", - "subtitle" : "Groups", - "title" : "High Availability" - }, - "ha_manager_resource_config" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resource_config", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "ha_manager_resources" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resources", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "pct_configuration" : { - "link" : "/pve-docs/chapter-pct.html#pct_configuration", - "subtitle" : "Configuration", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_images" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_images", - "subtitle" : "Container Images", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_network" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_network", - "subtitle" : "Network", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_storage" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_storage", - "subtitle" : "Container Storage", - "title" : "Proxmox Container Toolkit" - }, - "pct_cpu" : { - "link" : "/pve-docs/chapter-pct.html#pct_cpu", - "subtitle" : "CPU", - "title" : "Proxmox Container Toolkit" - }, - "pct_general" : { - "link" : "/pve-docs/chapter-pct.html#pct_general", - "subtitle" : "General Settings", - "title" : "Proxmox Container Toolkit" - }, - "pct_memory" : { - "link" : "/pve-docs/chapter-pct.html#pct_memory", - "subtitle" : "Memory", - "title" : "Proxmox Container Toolkit" - }, - "pct_migration" : { - "link" : "/pve-docs/chapter-pct.html#pct_migration", - "subtitle" : "Migration", - "title" : "Proxmox Container Toolkit" - }, - "pct_options" : { - "link" : "/pve-docs/chapter-pct.html#pct_options", - "subtitle" : "Options", - "title" : "Proxmox Container Toolkit" - }, - "pct_snapshots" : { - "link" : "/pve-docs/chapter-pct.html#pct_snapshots", - "subtitle" : "Snapshots", - "title" : "Proxmox Container Toolkit" - }, - "pct_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-pct.html#pct_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Containers", - "title" : "Proxmox Container Toolkit" - }, - "pve_admin_guide" : { - "link" : "/pve-docs/pve-admin-guide.html", - "title" : "Proxmox VE Administration Guide" - }, - "pve_ceph_install" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_install", - "subtitle" : "Installation of Ceph Packages", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_osds" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_osds", - "subtitle" : "Creating Ceph OSDs", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_pools" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_pools", - "subtitle" : "Creating Ceph Pools", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_documentation_index" : { - "link" : "/pve-docs/index.html", - "title" : "Proxmox VE Documentation Index" - }, - "pve_firewall_cluster_wide_setup" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_cluster_wide_setup", - "subtitle" : "Cluster Wide Setup", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_host_specific_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_host_specific_configuration", - "subtitle" : "Host Specific Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_aliases" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_aliases", - "subtitle" : "IP Aliases", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_sets" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_sets", - "subtitle" : "IP Sets", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_vm_container_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_vm_container_configuration", - "subtitle" : "VM/Container Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_service_daemons" : { - "link" : "/pve-docs/index.html#_service_daemons", - "title" : "Service Daemons" - }, - "pveceph_fs" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs", - "subtitle" : "CephFS", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pveceph_fs_create" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs_create", - "subtitle" : "Create a CephFS", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pvecm_create_cluster" : { - "link" : "/pve-docs/chapter-pvecm.html#pvecm_create_cluster", - "subtitle" : "Create the Cluster", - "title" : "Cluster Manager" - }, - "pvesr_schedule_time_format" : { - "link" : "/pve-docs/chapter-pvesr.html#pvesr_schedule_time_format", - "subtitle" : "Schedule Format", - "title" : "Storage Replication" - }, - "pveum_authentication_realms" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_authentication_realms", - "subtitle" : "Authentication Realms", - "title" : "User Management" - }, - "pveum_groups" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_groups", - "subtitle" : "Groups", - "title" : "User Management" - }, - "pveum_permission_management" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_permission_management", - "subtitle" : "Permission Management", - "title" : "User Management" - }, - "pveum_pools" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_pools", - "subtitle" : "Pools", - "title" : "User Management" - }, - "pveum_roles" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_roles", - "subtitle" : "Roles", - "title" : "User Management" - }, - "pveum_tfa_auth" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_tfa_auth", - "subtitle" : "Two factor authentication", - "title" : "User Management" - }, - "pveum_users" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_users", - "subtitle" : "Users", - "title" : "User Management" - }, - "qm_bios_and_uefi" : { - "link" : "/pve-docs/chapter-qm.html#qm_bios_and_uefi", - "subtitle" : "BIOS and UEFI", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cloud_init" : { - "link" : "/pve-docs/chapter-qm.html#qm_cloud_init", - "title" : "Cloud-Init Support" - }, - "qm_copy_and_clone" : { - "link" : "/pve-docs/chapter-qm.html#qm_copy_and_clone", - "subtitle" : "Copies and Clones", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cpu" : { - "link" : "/pve-docs/chapter-qm.html#qm_cpu", - "subtitle" : "CPU", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_general_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_general_settings", - "subtitle" : "General Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_hard_disk" : { - "link" : "/pve-docs/chapter-qm.html#qm_hard_disk", - "subtitle" : "Hard Disk", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_memory" : { - "link" : "/pve-docs/chapter-qm.html#qm_memory", - "subtitle" : "Memory", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_migration" : { - "link" : "/pve-docs/chapter-qm.html#qm_migration", - "subtitle" : "Migration", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_network_device" : { - "link" : "/pve-docs/chapter-qm.html#qm_network_device", - "subtitle" : "Network Device", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_options" : { - "link" : "/pve-docs/chapter-qm.html#qm_options", - "subtitle" : "Options", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_os_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_os_settings", - "subtitle" : "OS Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_pci_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_pci_passthrough", - "title" : "PCI(e) Passthrough" - }, - "qm_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-qm.html#qm_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Virtual Machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_system_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_system_settings", - "subtitle" : "System Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_usb_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_usb_passthrough", - "subtitle" : "USB Passthrough", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_virtual_machines_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_virtual_machines_settings", - "subtitle" : "Virtual Machines Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "storage_cephfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cephfs", - "title" : "Ceph Filesystem (CephFS)" - }, - "storage_cifs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cifs", - "title" : "CIFS Backend" - }, - "storage_directory" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_directory", - "title" : "Directory Backend" - }, - "storage_glusterfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_glusterfs", - "title" : "GlusterFS Backend" - }, - "storage_lvm" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvm", - "title" : "LVM Backend" - }, - "storage_lvmthin" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvmthin", - "title" : "LVM thin Backend" - }, - "storage_nfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_nfs", - "title" : "NFS Backend" - }, - "storage_open_iscsi" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_open_iscsi", - "title" : "Open-iSCSI initiator" - }, - "storage_zfspool" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_zfspool", - "title" : "Local ZFS Pool Backend" - }, - "sysadmin_certificate_management" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_certificate_management", - "title" : "Certificate Management" - }, - "sysadmin_network_configuration" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_network_configuration", - "title" : "Network Configuration" - } -}; -Ext.ns('PVE'); - -// 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)) { - var console = { - log: function() {} - }; -} -console.log("Starting PVE Manager"); - -Ext.Ajax.defaultHeaders = { - 'Accept': 'application/json' -}; - -/*jslint confusion: true */ -Ext.define('PVE.Utils', { utilities: { - - // this singleton contains miscellaneous utilities - - toolkit: undefined, // (extjs|touch), set inside Toolkit.js - - bus_match: /^(ide|sata|virtio|scsi)\d+$/, - - log_severity_hash: { - 0: "panic", - 1: "alert", - 2: "critical", - 3: "error", - 4: "warning", - 5: "notice", - 6: "info", - 7: "debug" - }, - - support_level_hash: { - 'c': gettext('Community'), - 'b': gettext('Basic'), - 's': gettext('Standard'), - 'p': gettext('Premium') - }, - - noSubKeyHtml: 'You do not have a valid subscription for this server. Please visit www.proxmox.com to get a list of available options.', - - kvm_ostypes: { - 'Linux': [ - { desc: '5.x - 2.6 Kernel', val: 'l26' }, - { desc: '2.4 Kernel', val: 'l24' } - ], - 'Microsoft Windows': [ - { desc: '10/2016', val: 'win10' }, - { desc: '8.x/2012/2012r2', val: 'win8' }, - { desc: '7/2008r2', val: 'win7' }, - { desc: 'Vista/2008', val: 'w2k8' }, - { desc: 'XP/2003', val: 'wxp' }, - { desc: '2000', val: 'w2k' } - ], - 'Solaris Kernel': [ - { desc: '-', val: 'solaris'} - ], - 'Other': [ - { desc: '-', val: 'other'} - ] - }, - - get_health_icon: function(state, circle) { - if (circle === undefined) { - circle = false; - } - - if (state === undefined) { - state = 'uknown'; - } - - var icon = 'faded fa-question'; - switch(state) { - case 'good': - icon = 'good fa-check'; - break; - case 'old': - icon = 'warning fa-refresh'; - break; - case 'warning': - icon = 'warning fa-exclamation'; - break; - case 'critical': - icon = 'critical fa-times'; - break; - default: break; - } - - if (circle) { - icon += '-circle'; - } - - return icon; - }, - - parse_ceph_version: function(service) { - if (service.ceph_version_short) { - return service.ceph_version_short; - } - - if (service.ceph_version) { - var match = service.ceph_version.match(/version (\d+(\.\d+)*)/); - if (match) { - return match[1]; - } - } - - return undefined; - }, - - compare_ceph_versions: function(a, b) { - if (a === b) { - return 0; - } - let avers = a.toString().split('.'); - let bvers = b.toString().split('.'); - - while (true) { - let av = avers.shift(); - let bv = bvers.shift(); - - if (av === undefined && bv === undefined) { - return 0; - } else if (av === undefined) { - return -1; - } else if (bv === undefined) { - return 1; - } else { - let diff = parseInt(av, 10) - parseInt(bv, 10); - if (diff != 0) return diff; - // else we need to look at the next parts - } - } - - }, - - get_ceph_icon_html: function(health, fw) { - var state = PVE.Utils.map_ceph_health[health]; - var cls = PVE.Utils.get_health_icon(state); - if (fw) { - cls += ' fa-fw'; - } - return " "; - }, - - map_ceph_health: { - 'HEALTH_OK':'good', - 'HEALTH_OLD':'old', - 'HEALTH_WARN':'warning', - 'HEALTH_ERR':'critical' - }, - - render_ceph_health: function(healthObj) { - var state = { - iconCls: PVE.Utils.get_health_icon(), - text: '' - }; - - if (!healthObj || !healthObj.status) { - return state; - } - - var health = PVE.Utils.map_ceph_health[healthObj.status]; - - state.iconCls = PVE.Utils.get_health_icon(health, true); - state.text = healthObj.status; - - return state; - }, - - render_zfs_health: function(value) { - if (typeof value == 'undefined'){ - return ""; - } - var iconCls = 'question-circle'; - switch (value) { - case 'AVAIL': - case 'ONLINE': - iconCls = 'check-circle good'; - break; - case 'REMOVED': - case 'DEGRADED': - iconCls = 'exclamation-circle warning'; - break; - case 'UNAVAIL': - case 'FAULTED': - case 'OFFLINE': - iconCls = 'times-circle critical'; - break; - default: //unknown - } - - return ' ' + value; - - }, - - get_kvm_osinfo: function(value) { - var info = { base: 'Other' }; // default - if (value) { - Ext.each(Object.keys(PVE.Utils.kvm_ostypes), function(k) { - Ext.each(PVE.Utils.kvm_ostypes[k], function(e) { - if (e.val === value) { - info = { desc: e.desc, base: k }; - } - }); - }); - } - return info; - }, - - render_kvm_ostype: function (value) { - var osinfo = PVE.Utils.get_kvm_osinfo(value); - if (osinfo.desc && osinfo.desc !== '-') { - return osinfo.base + ' ' + osinfo.desc; - } else { - return osinfo.base; - } - }, - - render_hotplug_features: function (value) { - var fa = []; - - if (!value || (value === '0')) { - return gettext('Disabled'); - } - - if (value === '1') { - value = 'disk,network,usb'; - } - - Ext.each(value.split(','), function(el) { - if (el === 'disk') { - fa.push(gettext('Disk')); - } else if (el === 'network') { - fa.push(gettext('Network')); - } else if (el === 'usb') { - fa.push('USB'); - } else if (el === 'memory') { - fa.push(gettext('Memory')); - } else if (el === 'cpu') { - fa.push(gettext('CPU')); - } else { - fa.push(el); - } - }); - - return fa.join(', '); - }, - - render_qga_features: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (' + Proxmox.Utils.disabledText + ')'; - } - var props = PVE.Parser.parsePropertyString(value, 'enabled'); - if (!PVE.Parser.parseBoolean(props.enabled)) { - return Proxmox.Utils.disabledText; - } - - delete props.enabled; - var agentstring = Proxmox.Utils.enabledText; - - Ext.Object.each(props, function(key, value) { - var keystring = '' ; - agentstring += ', ' + key + ': '; - - if (PVE.Parser.parseBoolean(value)) { - agentstring += Proxmox.Utils.enabledText; - } else { - agentstring += Proxmox.Utils.disabledText; - } - }); - - return agentstring; - }, - - render_qemu_machine: function(value) { - return value || (Proxmox.Utils.defaultText + ' (i440fx)'); - }, - - render_qemu_bios: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (SeaBIOS)'; - } else if (value === 'seabios') { - return "SeaBIOS"; - } else if (value === 'ovmf') { - return "OVMF (UEFI)"; - } else { - return value; - } - }, - - render_dc_ha_opts: function(value) { - if (!value) { - return Proxmox.Utils.defaultText; - } else { - return PVE.Parser.printPropertyString(value); - } - }, - render_as_property_string: function(value) { - return (!value) ? Proxmox.Utils.defaultText - : PVE.Parser.printPropertyString(value); - }, - - render_scsihw: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (LSI 53C895A)'; - } else if (value === 'lsi') { - return 'LSI 53C895A'; - } else if (value === 'lsi53c810') { - return 'LSI 53C810'; - } else if (value === 'megasas') { - return 'MegaRAID SAS 8708EM2'; - } else if (value === 'virtio-scsi-pci') { - return 'VirtIO SCSI'; - } else if (value === 'virtio-scsi-single') { - return 'VirtIO SCSI single'; - } else if (value === 'pvscsi') { - return 'VMware PVSCSI'; - } else { - return value; - } - }, - - // fixme: auto-generate this - // for now, please keep in sync with PVE::Tools::kvmkeymaps - kvm_keymaps: { - //ar: 'Arabic', - da: 'Danish', - de: 'German', - 'de-ch': 'German (Swiss)', - 'en-gb': 'English (UK)', - 'en-us': 'English (USA)', - es: 'Spanish', - //et: 'Estonia', - fi: 'Finnish', - //fo: 'Faroe Islands', - fr: 'French', - 'fr-be': 'French (Belgium)', - 'fr-ca': 'French (Canada)', - 'fr-ch': 'French (Swiss)', - //hr: 'Croatia', - hu: 'Hungarian', - is: 'Icelandic', - it: 'Italian', - ja: 'Japanese', - lt: 'Lithuanian', - //lv: 'Latvian', - mk: 'Macedonian', - nl: 'Dutch', - //'nl-be': 'Dutch (Belgium)', - no: 'Norwegian', - pl: 'Polish', - pt: 'Portuguese', - 'pt-br': 'Portuguese (Brazil)', - //ru: 'Russian', - sl: 'Slovenian', - sv: 'Swedish', - //th: 'Thai', - tr: 'Turkish' - }, - - kvm_vga_drivers: { - std: gettext('Standard VGA'), - vmware: gettext('VMware compatible'), - qxl: 'SPICE', - qxl2: 'SPICE dual monitor', - qxl3: 'SPICE three monitors', - qxl4: 'SPICE four monitors', - serial0: gettext('Serial terminal') + ' 0', - serial1: gettext('Serial terminal') + ' 1', - serial2: gettext('Serial terminal') + ' 2', - serial3: gettext('Serial terminal') + ' 3', - virtio: 'VirtIO-GPU', - none: Proxmox.Utils.noneText - }, - - render_kvm_language: function (value) { - if (!value || value === '__default__') { - return Proxmox.Utils.defaultText; - } - var text = PVE.Utils.kvm_keymaps[value]; - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_keymap_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_language('')]]; - Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) { - data.push([key, PVE.Utils.render_kvm_language(value)]); - }); - - return data; - }, - - console_map: { - '__default__': Proxmox.Utils.defaultText + ' (xterm.js)', - 'vv': 'SPICE (remote-viewer)', - 'html5': 'HTML5 (noVNC)', - 'xtermjs': 'xterm.js' - }, - - render_console_viewer: function(value) { - value = value || '__default__'; - if (PVE.Utils.console_map[value]) { - return PVE.Utils.console_map[value]; - } - return value; - }, - - console_viewer_array: function() { - return Ext.Array.map(Object.keys(PVE.Utils.console_map), function(v) { - return [v, PVE.Utils.render_console_viewer(v)]; - }); - }, - - render_kvm_vga_driver: function (value) { - if (!value) { - return Proxmox.Utils.defaultText; - } - var vga = PVE.Parser.parsePropertyString(value, 'type'); - var text = PVE.Utils.kvm_vga_drivers[vga.type]; - if (!vga.type) { - text = Proxmox.Utils.defaultText; - } - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_vga_driver_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_vga_driver('')]]; - Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) { - data.push([key, PVE.Utils.render_kvm_vga_driver(value)]); - }); - - return data; - }, - - render_kvm_startup: function(value) { - var startup = PVE.Parser.parseStartup(value); - - var res = 'order='; - if (startup.order === undefined) { - res += 'any'; - } else { - res += startup.order; - } - if (startup.up !== undefined) { - res += ',up=' + startup.up; - } - if (startup.down !== undefined) { - res += ',down=' + startup.down; - } - - return res; - }, - - extractFormActionError: function(action) { - var msg; - switch (action.failureType) { - case Ext.form.action.Action.CLIENT_INVALID: - msg = gettext('Form fields may not be submitted with invalid values'); - break; - case Ext.form.action.Action.CONNECT_FAILURE: - msg = gettext('Connection error'); - var resp = action.response; - if (resp.status && resp.statusText) { - msg += " " + resp.status + ": " + resp.statusText; - } - break; - case Ext.form.action.Action.LOAD_FAILURE: - case Ext.form.action.Action.SERVER_INVALID: - msg = Proxmox.Utils.extractRequestError(action.result, true); - break; - } - return msg; - }, - - format_duration_short: function(ut) { - - if (ut < 60) { - return ut.toFixed(1) + 's'; - } - - if (ut < 3600) { - var mins = ut / 60; - return mins.toFixed(1) + 'm'; - } - - if (ut < 86400) { - var hours = ut / 3600; - return hours.toFixed(1) + 'h'; - } - - var days = ut / 86400; - return days.toFixed(1) + 'd'; - }, - - contentTypes: { - 'images': gettext('Disk image'), - 'backup': gettext('VZDump backup file'), - 'vztmpl': gettext('Container template'), - 'iso': gettext('ISO image'), - 'rootdir': gettext('Container'), - 'snippets': gettext('Snippets') - }, - - storageSchema: { - dir: { - name: Proxmox.Utils.directoryText, - ipanel: 'DirInputPanel', - faIcon: 'folder' - }, - lvm: { - name: 'LVM', - ipanel: 'LVMInputPanel', - faIcon: 'folder' - }, - lvmthin: { - name: 'LVM-Thin', - ipanel: 'LvmThinInputPanel', - faIcon: 'folder' - }, - nfs: { - name: 'NFS', - ipanel: 'NFSInputPanel', - faIcon: 'building' - }, - cifs: { - name: 'CIFS', - ipanel: 'CIFSInputPanel', - faIcon: 'building' - }, - glusterfs: { - name: 'GlusterFS', - ipanel: 'GlusterFsInputPanel', - faIcon: 'building' - }, - iscsi: { - name: 'iSCSI', - ipanel: 'IScsiInputPanel', - faIcon: 'building' - }, - cephfs: { - name: 'CephFS', - ipanel: 'CephFSInputPanel', - faIcon: 'building' - }, - pvecephfs: { - name: 'CephFS (PVE)', - ipanel: 'CephFSInputPanel', - hideAdd: true, - faIcon: 'building' - }, - rbd: { - name: 'RBD', - ipanel: 'RBDInputPanel', - faIcon: 'building' - }, - pveceph: { - name: 'RBD (PVE)', - ipanel: 'RBDInputPanel', - hideAdd: true, - faIcon: 'building' - }, - zfs: { - name: 'ZFS over iSCSI', - ipanel: 'ZFSInputPanel', - faIcon: 'building' - }, - zfspool: { - name: 'ZFS', - ipanel: 'ZFSPoolInputPanel', - faIcon: 'folder' - }, - drbd: { - name: 'DRBD', - hideAdd: true - } - }, - - format_storage_type: function(value, md, record) { - if (value === 'rbd') { - value = (!record || record.get('monhost') ? 'rbd' : 'pveceph'); - } else if (value === 'cephfs') { - value = (!record || record.get('monhost') ? 'cephfs' : 'pvecephfs'); - } - - var schema = PVE.Utils.storageSchema[value]; - if (schema) { - return schema.name; - } - return Proxmox.Utils.unknownText; - }, - - format_ha: function(value) { - var text = Proxmox.Utils.noneText; - - if (value.managed) { - text = value.state || Proxmox.Utils.noneText; - - text += ', ' + Proxmox.Utils.groupText + ': '; - text += value.group || Proxmox.Utils.noneText; - } - - return text; - }, - - format_content_types: function(value) { - return value.split(',').sort().map(function(ct) { - return PVE.Utils.contentTypes[ct] || ct; - }).join(', '); - }, - - render_storage_content: function(value, metaData, record) { - var data = record.data; - if (Ext.isNumber(data.channel) && - Ext.isNumber(data.id) && - Ext.isNumber(data.lun)) { - return "CH " + - Ext.String.leftPad(data.channel,2, '0') + - " ID " + data.id + " LUN " + data.lun; - } - return data.volid.replace(/^.*:(.*\/)?/,''); - }, - - render_serverity: function (value) { - return PVE.Utils.log_severity_hash[value] || value; - }, - - render_cpu: function(value, metaData, record, rowIndex, colIndex, store) { - - if (!(record.data.uptime && Ext.isNumeric(value))) { - return ''; - } - - var maxcpu = record.data.maxcpu || 1; - - if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) { - return ''; - } - - var per = value * 100; - - return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU'); - }, - - render_size: function(value, metaData, record, rowIndex, colIndex, store) { - /*jslint confusion: true */ - - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value); - }, - - render_bandwidth: function(value) { - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value) + '/s'; - }, - - render_timestamp_human_readable: function(value) { - return Ext.Date.format(new Date(value * 1000), 'l d F Y H:i:s'); - }, - - render_duration: function(value) { - if (value === undefined) { - return '-'; - } - return PVE.Utils.format_duration_short(value); - }, - - calculate_mem_usage: function(data) { - if (!Ext.isNumeric(data.mem) || - data.maxmem === 0 || - data.uptime < 1) { - return -1; - } - - return (data.mem / data.maxmem); - }, - - render_mem_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - if (value > 1 ) { - // we got no percentage but bytes - var mem = value; - var maxmem = record.data.maxmem; - if (!record.data.uptime || - maxmem === 0 || - !Ext.isNumeric(mem)) { - return ''; - } - - return ((mem*100)/maxmem).toFixed(1) + " %"; - } - return (value*100).toFixed(1) + " %"; - }, - - render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var mem = value; - var maxmem = record.data.maxmem; - - if (!record.data.uptime) { - return ''; - } - - if (!(Ext.isNumeric(mem) && maxmem)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - calculate_disk_usage: function(data) { - - if (!Ext.isNumeric(data.disk) || - data.type === 'qemu' || - (data.type === 'lxc' && data.uptime === 0) || - data.maxdisk === 0) { - return -1; - } - - return (data.disk / data.maxdisk); - }, - - render_disk_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - - return (value * 100).toFixed(1) + " %"; - }, - - render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var disk = value; - var maxdisk = record.data.maxdisk; - var type = record.data.type; - - if (!Ext.isNumeric(disk) || - type === 'qemu' || - maxdisk === 0 || - (type === 'lxc' && record.data.uptime === 0)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - get_object_icon_class: function(type, record) { - var status = ''; - var objType = type; - - if (type === 'type') { - // for folder view - objType = record.groupbyid; - } else if (record.template) { - // templates - objType = 'template'; - status = type; - } else { - // everything else - status = record.status + ' ha-' + record.hastate; - } - - if (record.lock) { - status += ' locked lock-' + record.lock; - } - - var defaults = PVE.tree.ResourceTree.typeDefaults[objType]; - if (defaults && defaults.iconCls) { - var retVal = defaults.iconCls + ' ' + status; - return retVal; - } - - return ''; - }, - - render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) { - - var cls = PVE.Utils.get_object_icon_class(value,record.data); - - var fa = ' '; - return fa + value; - }, - - render_support_level: function(value, metaData, record) { - return PVE.Utils.support_level_hash[value] || '-'; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - /* render functions for new status panel */ - - render_usage: function(val) { - return (val*100).toFixed(2) + '%'; - }, - - render_cpu_usage: function(val, max) { - return Ext.String.format(gettext('{0}% of {1}') + - ' ' + gettext('CPU(s)'), (val*100).toFixed(2), max); - }, - - render_size_usage: function(val, max) { - if (max === 0) { - return gettext('N/A'); - } - return (val*100/max).toFixed(2) + '% '+ '(' + - Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(val), PVE.Utils.render_size(max)) + ')'; - }, - - /* this is different for nodes */ - render_node_cpu_usage: function(value, record) { - return PVE.Utils.render_cpu_usage(value, record.cpus); - }, - - /* this is different for nodes */ - render_node_size_usage: function(record) { - return PVE.Utils.render_size_usage(record.used, record.total); - }, - - render_optional_url: function(value) { - var match; - if (value && (match = value.match(/^https?:\/\//)) !== null) { - return '' + value + ''; - } - return value; - }, - - render_san: function(value) { - var names = []; - if (Ext.isArray(value)) { - value.forEach(function(val) { - if (!Ext.isNumber(val)) { - names.push(val); - } - }); - return names.join('
'); - } - return value; - }, - - render_full_name: function(firstname, metaData, record) { - var first = firstname || ''; - var last = record.data.lastname || ''; - return Ext.htmlEncode(first + " " + last); - }, - - render_u2f_error: function(error) { - var ErrorNames = { - '1': gettext('Other Error'), - '2': gettext('Bad Request'), - '3': gettext('Configuration Unsupported'), - '4': gettext('Device Ineligible'), - '5': gettext('Timeout') - }; - return "U2F Error: " + ErrorNames[error] || Proxmox.Utils.unknownText; - }, - - windowHostname: function() { - return window.location.hostname.replace(Proxmox.Utils.IP6_bracket_match, - function(m, addr, offset, original) { return addr; }); - }, - - openDefaultConsoleWindow: function(consoles, vmtype, vmid, nodename, vmname, cmd) { - var dv = PVE.Utils.defaultViewer(consoles); - PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname, cmd); - }, - - openConsoleWindow: function(viewer, vmtype, vmid, nodename, vmname, cmd) { - // kvm, lxc, shell, upgrade - - if (vmid == undefined && (vmtype === 'kvm' || vmtype === 'lxc')) { - throw "missing vmid"; - } - - if (!nodename) { - throw "no nodename specified"; - } - - if (viewer === 'html5') { - PVE.Utils.openVNCViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'xtermjs') { - Proxmox.Utils.openXtermJsViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'vv') { - var url; - var params = { proxy: PVE.Utils.windowHostname() }; - if (vmtype === 'kvm') { - url = '/nodes/' + nodename + '/qemu/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'lxc') { - url = '/nodes/' + nodename + '/lxc/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'shell') { - url = '/nodes/' + nodename + '/spiceshell'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'upgrade') { - url = '/nodes/' + nodename + '/spiceshell'; - params.upgrade = 1; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'cmd') { - url = '/nodes/' + nodename + '/spiceshell'; - params.cmd = cmd; - PVE.Utils.openSpiceViewer(url, params); - } - } else { - throw "unknown viewer type"; - } - }, - - defaultViewer: function(consoles) { - - var allowSpice, allowXtermjs; - - if (consoles === true) { - allowSpice = true; - allowXtermjs = true; - } else if (typeof consoles === 'object') { - allowSpice = consoles.spice; - allowXtermjs = !!consoles.xtermjs; - } - var dv = PVE.VersionInfo.console || 'xtermjs'; - if (dv === 'vv' && !allowSpice) { - dv = (allowXtermjs) ? 'xtermjs' : 'html5'; - } else if (dv === 'xtermjs' && !allowXtermjs) { - dv = (allowSpice) ? 'vv' : 'html5'; - } - - return dv; - }, - - openVNCViewer: function(vmtype, vmid, nodename, vmname, cmd) { - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - novnc: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - resize: 'off', - cmd: cmd - }); - var nw = window.open("?" + url, '_blank', "innerWidth=745,innerheight=427"); - if (nw) { - nw.focus(); - } - }, - - openSpiceViewer: function(url, params){ - - var downloadWithName = function(uri, name) { - var link = Ext.DomHelper.append(document.body, { - tag: 'a', - href: uri, - css : 'display:none;visibility:hidden;height:0px;' - }); - - // Note: we need to tell android the correct file name extension - // but we do not set 'download' tag for other environments, because - // It can have strange side effects (additional user prompt on firefox) - var andriod = navigator.userAgent.match(/Android/i) ? true : false; - if (andriod) { - link.download = name; - } - - if (link.fireEvent) { - link.fireEvent('onclick'); - } else { - var evt = document.createEvent("MouseEvents"); - evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null); - link.dispatchEvent(evt); - } - }; - - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - failure: function(response, opts){ - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts){ - var raw = "[virt-viewer]\n"; - Ext.Object.each(response.result.data, function(k, v) { - raw += k + "=" + v + "\n"; - }); - var url = 'data:application/x-virt-viewer;charset=UTF-8,' + - encodeURIComponent(raw); - - downloadWithName(url, "pve-spice.vv"); - } - }); - }, - - openTreeConsole: function(tree, record, item, index, e) { - e.stopEvent(); - var nodename = record.data.node; - var vmid = record.data.vmid; - var vmname = record.data.name; - if (record.data.type === 'qemu' && !record.data.template) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - let conf = response.result.data; - var consoles = { - spice: !!conf.spice, - xtermjs: !!conf.serial, - }; - PVE.Utils.openDefaultConsoleWindow(consoles, 'kvm', vmid, nodename, vmname); - } - }); - } else if (record.data.type === 'lxc' && !record.data.template) { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - }, - - // test automation helper - call_menu_handler: function(menu, text) { - - var list = menu.query('menuitem'); - - Ext.Array.each(list, function(item) { - if (item.text === text) { - if (item.handler) { - item.handler(); - return 1; - } else { - return undefined; - } - } - }); - }, - - createCmdMenu: function(v, record, item, index, event) { - event.stopEvent(); - if (!(v instanceof Ext.tree.View)) { - v.select(record); - } - var menu; - var template = !!record.data.template; - var type = record.data.type; - - if (template) { - if (type === 'qemu' || type == 'lxc') { - menu = Ext.create('PVE.menu.TemplateMenu', { - pveSelNode: record - }); - } - } else if (type === 'qemu' || - type === 'lxc' || - type === 'node') { - menu = Ext.create('PVE.' + type + '.CmdMenu', { - pveSelNode: record, - nodename: record.data.node - }); - } else { - return; - } - - menu.showAt(event.getXY()); - return menu; - }, - - // helper for deleting field which are set to there default values - delete_if_default: function(values, fieldname, default_val, create) { - if (values[fieldname] === '' || values[fieldname] === default_val) { - if (!create) { - if (values['delete']) { - values['delete'] += ',' + fieldname; - } else { - values['delete'] = fieldname; - } - } - - delete values[fieldname]; - } - }, - - loadSSHKeyFromFile: function(file, callback) { - // ssh-keygen produces 740 bytes for an average 4096 bit rsa key, with - // a user@host comment, 1420 for 8192 bits; current max is 16kbit - // assume: 740*8 for max. 32kbit (5920 byte file) - // round upwards to nearest nice number => 8192 bytes, leaves lots of comment space - if (file.size > 8192) { - Ext.Msg.alert(gettext('Error'), gettext("Invalid file size: ") + file.size); - return; - } - /*global - FileReader - */ - var reader = new FileReader(); - reader.onload = function(evt) { - callback(evt.target.result); - }; - reader.readAsText(file); - }, - - bus_counts: { ide: 4, sata: 6, scsi: 16, virtio: 16 }, - - // types is either undefined (all busses), an array of busses, or a single bus - forEachBus: function(types, func) { - var busses = Object.keys(PVE.Utils.bus_counts); - var i, j, count, cont; - - if (Ext.isArray(types)) { - busses = types; - } else if (Ext.isDefined(types)) { - busses = [ types ]; - } - - // check if we only have valid busses - for (i = 0; i < busses.length; i++) { - if (!PVE.Utils.bus_counts[busses[i]]) { - throw "invalid bus: '" + busses[i] + "'"; - } - } - - for (i = 0; i < busses.length; i++) { - count = PVE.Utils.bus_counts[busses[i]]; - for (j = 0; j < count; j++) { - cont = func(busses[i], j); - if (!cont && cont !== undefined) { - return; - } - } - } - }, - - mp_counts: { mps: 256, unused: 256 }, - - forEachMP: function(func, includeUnused) { - var i, cont; - for (i = 0; i < PVE.Utils.mp_counts.mps; i++) { - cont = func('mp', i); - if (!cont && cont !== undefined) { - return; - } - } - - if (!includeUnused) { - return; - } - - for (i = 0; i < PVE.Utils.mp_counts.unused; i++) { - cont = func('unused', i); - if (!cont && cont !== undefined) { - return; - } - } - }, - - cleanEmptyObjectKeys: function (obj) { - var propName; - for (propName in obj) { - if (obj.hasOwnProperty(propName)) { - if (obj[propName] === null || obj[propName] === undefined) { - delete obj[propName]; - } - } - } - }, - - handleStoreErrorOrMask: function(me, store, regex, callback) { - - me.mon(store, 'load', function (proxy, response, success, operation) { - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - var msg; - - if (operation.error.statusText) { - if (operation.error.statusText.match(regex)) { - callback(me, operation.error); - return; - } else { - msg = operation.error.statusText + ' (' + operation.error.status + ')'; - } - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - showCephInstallOrMask: function(container, msg, nodename, callback){ - var regex = new RegExp("not (installed|initialized)", "i"); - if (msg.match(regex)) { - if (Proxmox.UserName === 'root@pam') { - container.el.mask(); - if (!container.down('pveCephInstallWindow')){ - var isInstalled = msg.match(/not initialized/i) ? true : false; - var win = Ext.create('PVE.ceph.Install', { - nodename: nodename - }); - win.getViewModel().set('isInstalled', isInstalled); - container.add(win); - win.show(); - callback(win); - } - } else { - container.mask(Ext.String.format(gettext('{0} not installed.') + - ' ' + gettext('Log in as root to install.'), 'Ceph'), ['pve-static-mask']); - } - return true; - } else { - return false; - } - } -}, - - singleton: true, - constructor: function() { - var me = this; - Ext.apply(me, me.utilities); - } - -}); - -// ExtJS related things - -Proxmox.Utils.toolkit = 'extjs'; - -// custom PVE specific VTypes -Ext.apply(Ext.form.field.VTypes, { - - 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"', - IP64AddressList: function(v) { - var list = v.split(/[\ \,\;]+/); - var i; - for (i = 0; i < list.length; i++) { - if (list[i] == '') { - continue; - } - - if (!Proxmox.Utils.IP64_match.test(list[i])) { - return false; - } - } - - return true; - }, - IP64AddressListText: gettext('Example') + ': 192.168.1.1,192.168.1.2', - IP64AddressListMask: /[A-Fa-f0-9\,\:\.\;\ ]/ -}); - -Ext.define('PVE.form.field.Display', { - override: 'Ext.form.field.Display', - - setSubmitValue: function(value) { - // do nothing, this is only to allow generalized bindings for the: - // `me.isCreate ? 'textfield' : 'displayfield'` cases we have. - } -}); -// Some configuration values are complex strings - -// so we need parsers/generators for them. - -Ext.define('PVE.Parser', { statics: { - - // this class only contains static functions - - parseACME: function(value) { - if (!value) { - return; - } - - var res = {}; - var errors = false; - - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; //continue - } - - var match_res; - if ((match_res = p.match(/^(?:domains=)?((?:[a-zA-Z0-9\-\.]+[;, ]?)+)$/)) !== null) { - res.domains = match_res[1].split(/[;, ]/); - } else { - errors = true; - return false; - } - }); - - if (errors || !res) { - return; - } - - return res; - }, - - parseBoolean: function(value, default_value) { - if (!Ext.isDefined(value)) { - return default_value; - } - value = value.toLowerCase(); - return value === '1' || - value === 'on' || - value === 'yes' || - value === 'true'; - }, - - parsePropertyString: function(value, defaultKey) { - var res = {}, - error; - - Ext.Array.each(value.split(','), function(p) { - var kv = p.split('=', 2); - if (Ext.isDefined(kv[1])) { - res[kv[0]] = kv[1]; - } else if (Ext.isDefined(defaultKey)) { - if (Ext.isDefined(res[defaultKey])) { - error = 'defaultKey may be only defined once in propertyString'; - return false; // break - } - res[defaultKey] = kv[0]; - } else { - error = 'invalid propertyString, not a key=value pair and no defaultKey defined'; - return false; // break - } - }); - - if (error !== undefined) { - console.error(error); - return; - } - - return res; - }, - - printPropertyString: function(data, defaultKey) { - var stringparts = [], - gotDefaultKeyVal = false, - defaultKeyVal; - - Ext.Object.each(data, function(key, value) { - if (defaultKey !== undefined && key === defaultKey) { - gotDefaultKeyVal = true; - defaultKeyVal = value; - } else { - stringparts.push(key + '=' + value); - } - }); - - stringparts = stringparts.sort(); - if (gotDefaultKeyVal) { - stringparts.unshift(defaultKeyVal); - } - - return stringparts.join(','); - }, - - parseQemuNetwork: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(ne2k_pci|e1000|e1000-82540em|e1000-82544gc|e1000-82545em|vmxnet3|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i)) !== null) { - res.model = match_res[1].toLowerCase(); - if (match_res[3]) { - res.macaddr = match_res[3]; - } - } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) { - res.bridge = match_res[1]; - } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) { - res.rate = match_res[1]; - } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) { - res.tag = match_res[1]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - res.firewall = match_res[1]; - } else if ((match_res = p.match(/^link_down=(\d+)$/)) !== null) { - res.disconnect = match_res[1]; - } else if ((match_res = p.match(/^queues=(\d+)$/)) !== null) { - res.queues = match_res[1]; - } else if ((match_res = p.match(/^trunks=(\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*)$/)) !== null) { - res.trunks = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors || !res.model) { - return; - } - - return res; - }, - - printQemuNetwork: function(net) { - - var netstr = net.model; - if (net.macaddr) { - netstr += "=" + net.macaddr; - } - if (net.bridge) { - netstr += ",bridge=" + net.bridge; - if (net.tag) { - netstr += ",tag=" + net.tag; - } - if (net.firewall) { - netstr += ",firewall=" + net.firewall; - } - } - if (net.rate) { - netstr += ",rate=" + net.rate; - } - if (net.queues) { - netstr += ",queues=" + net.queues; - } - if (net.disconnect) { - netstr += ",link_down=" + net.disconnect; - } - if (net.trunks) { - netstr += ",trunks=" + net.trunks; - } - return netstr; - }, - - parseQemuDrive: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var match_res = key.match(/^([a-z]+)(\d+)$/); - if (!match_res) { - return; - } - res['interface'] = match_res[1]; - res.index = match_res[2]; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - if (k === 'cache' && v === 'off') { - v = 'none'; - } - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - return res; - }, - - printQemuDrive: function(drive) { - - var drivestr = drive.file; - - Ext.Object.each(drive, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'index' || key === 'interface') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseIPConfig: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - if ((match_res = p.match(/^ip=(\S+)$/)) !== null) { - res.ip = match_res[1]; - } else if ((match_res = p.match(/^gw=(\S+)$/)) !== null) { - res.gw = match_res[1]; - } else if ((match_res = p.match(/^ip6=(\S+)$/)) !== null) { - res.ip6 = match_res[1]; - } else if ((match_res = p.match(/^gw6=(\S+)$/)) !== null) { - res.gw6 = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printIPConfig: function(cfg) { - var c = ""; - var str = ""; - if (cfg.ip) { - str += "ip=" + cfg.ip; - c = ","; - } - if (cfg.gw) { - str += c + "gw=" + cfg.gw; - c = ","; - } - if (cfg.ip6) { - str += c + "ip6=" + cfg.ip6; - c = ","; - } - if (cfg.gw6) { - str += c + "gw6=" + cfg.gw6; - c = ","; - } - return str; - }, - - parseOpenVZNetIf: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(';'), function(item) { - if (!item || item.match(/^\s*$/)) { - return; // continue - } - - var data = {}; - Ext.Array.each(item.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - if (match_res[1] === 'bridge'){ - var bridgevlanf = match_res[2]; - var bridge_res = bridgevlanf.match(/^(vmbr(\d+))(v(\d+))?(f)?$/); - if (!bridge_res) { - errors = true; - return false; // break - } - data.bridge = bridge_res[1]; - data.tag = bridge_res[4]; - /*jslint confusion: true*/ - data.firewall = bridge_res[5] ? 1 : 0; - /*jslint confusion: false*/ - } else { - data[match_res[1]] = match_res[2]; - } - }); - - if (errors || !data.ifname) { - errors = true; - return false; // break - } - - data.raw = item; - - res[data.ifname] = data; - }); - - return errors ? undefined: res; - }, - - printOpenVZNetIf: function(netif) { - var netarray = []; - - Ext.Object.each(netif, function(iface, data) { - var tmparray = []; - Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname' , 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) { - var value = data[key]; - if (key === 'bridge'){ - if(data.tag){ - value = value + 'v' + data.tag; - } - if (data.firewall){ - value = value + 'f'; - } - } - if (value) { - tmparray.push(key + '=' + value); - } - - }); - netarray.push(tmparray.join(',')); - }); - - return netarray.join(';'); - }, - - parseLxcNetwork: function(value) { - if (!value) { - return; - } - - var data = {}; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|tag|rate)=(\S+)$/); - if (match_res) { - data[match_res[1]] = match_res[2]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - data.firewall = PVE.Parser.parseBoolean(match_res[1]); - } else { - // todo: simply ignore errors ? - return; // continue - } - }); - - return data; - }, - - printLxcNetwork: function(data) { - var tmparray = []; - Ext.Array.each(['bridge', 'hwaddr', 'mtu', 'name', 'ip', - 'gw', 'ip6', 'gw6', 'firewall', 'tag'], function(key) { - var value = data[key]; - if (value) { - tmparray.push(key + '=' + value); - } - }); - - /*jslint confusion: true*/ - if (data.rate > 0) { - tmparray.push('rate=' + data.rate); - } - /*jslint confusion: false*/ - return tmparray.join(','); - }, - - parseLxcMountPoint: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(.+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - var m = res.file.match(/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):/i); - if (m) { - res.storage = m[1]; - res.type = 'volume'; - } else if (res.file.match(/^\/dev\//)) { - res.type = 'device'; - } else { - res.type = 'bind'; - } - - return res; - }, - - printLxcMountPoint: function(mp) { - var drivestr = mp.file; - - Ext.Object.each(mp, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'type' || key === 'storage') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseStartup: function(value) { - if (value === undefined) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) { - res.order = match_res[2]; - } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) { - res.up = match_res[1]; - } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) { - res.down = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printStartup: function(startup) { - var arr = []; - if (startup.order !== undefined && startup.order !== '') { - arr.push('order=' + startup.order); - } - if (startup.up !== undefined && startup.up !== '') { - arr.push('up=' + startup.up); - } - if (startup.down !== undefined && startup.down !== '') { - arr.push('down=' + startup.down); - } - - return arr.join(','); - }, - - parseQemuSmbios1: function(value) { - var res = value.split(',').reduce(function (accumulator, currentValue) { - var splitted = currentValue.split(new RegExp("=(.+)")); - accumulator[splitted[0]] = splitted[1]; - return accumulator; - }, {}); - - if (PVE.Parser.parseBoolean(res.base64, false)) { - Ext.Object.each(res, function(key, value) { - if (key === 'uuid') { return; } - res[key] = Ext.util.Base64.decode(value); - }); - } - - return res; - }, - - printQemuSmbios1: function(data) { - - var datastr = ''; - var base64 = false; - Ext.Object.each(data, function(key, value) { - if (value === '') { return; } - if (key === 'uuid') { - datastr += (datastr !== '' ? ',' : '') + key + '=' + value; - } else { - // values should be base64 encoded from now on, mark config strings correspondingly - if (!base64) { - base64 = true; - datastr += (datastr !== '' ? ',' : '') + 'base64=1'; - } - datastr += (datastr !== '' ? ',' : '') + key + '=' + Ext.util.Base64.encode(value); - } - }); - - return datastr; - }, - - parseTfaConfig: function(value) { - var res = {}; - - Ext.Array.each(value.split(','), function(p) { - var kva = p.split('=', 2); - res[kva[0]] = kva[1]; - }); - - return res; - }, - - parseTfaType: function(value) { - /*jslint confusion: true*/ - var match; - if (!value || !value.length) { - return undefined; - } else if (value === 'x!oath') { - return 'totp'; - } else if (!!(match = value.match(/^x!(.+)$/))) { - return match[1]; - } else { - return 1; - } - }, - - parseQemuCpu: function(value) { - if (!value) { - return {}; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - if (!p.match(/\=/)) { - if (Ext.isDefined(res.cpu)) { - errors = true; - return false; // break - } - res.cputype = p; - return; // continue - } - - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - - var k = match_res[1]; - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - res[k] = match_res[2]; - }); - - if (errors || !res.cputype) { - return; - } - - return res; - }, - - printQemuCpu: function(cpu) { - var cpustr = cpu.cputype; - var optstr = ''; - - Ext.Object.each(cpu, function(key, value) { - if (!Ext.isDefined(value) || key === 'cputype') { - return; // continue - } - optstr += ',' + key + '=' + value; - }); - - if (!cpustr) { - if (optstr) { - return 'kvm64' + optstr; - } - return; - } - - return cpustr + optstr; - }, - - parseSSHKey: function(key) { - // |--- options can have quotes--| type key comment - var keyre = /^(?:((?:[^\s"]|\"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/; - var typere = /^(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)$/; - - var m = key.match(keyre); - if (!m) { - return null; - } - if (m.length < 3 || !m[2]) { // [2] is always either type or key - return null; - } - if (m[1] && m[1].match(typere)) { - return { - type: m[1], - key: m[2], - comment: m[3] - }; - } - if (m[2].match(typere)) { - return { - options: m[1], - type: m[2], - key: m[3], - comment: m[4] - }; - } - return null; - } -}}); -/* This state provider keeps part of the state inside - * the browser history. - * - * We compress (shorten) url using dictionary based compression - * i.e. use column separated list instead of url encoded hash: - * #v\d* version/format - * := indicates string values - * :\d+ lookup value in dictionary hash - * #v1:=value1:5:=value2:=value3:... -*/ - -Ext.define('PVE.StateProvider', { - extend: 'Ext.state.LocalStorageProvider', - - // private - setHV: function(name, newvalue, fireEvents) { - var me = this; - - var changes = false; - var oldtext = Ext.encode(me.UIState[name]); - var newtext = Ext.encode(newvalue); - if (newtext != oldtext) { - changes = true; - me.UIState[name] = newvalue; - //console.log("changed old " + name + " " + oldtext); - //console.log("changed new " + name + " " + newtext); - if (fireEvents) { - me.fireEvent("statechange", me, name, { value: newvalue }); - } - } - return changes; - }, - - // private - hslist: [ - // order is important for notifications - // [ name, default ] - ['view', 'server'], - ['rid', 'root'], - ['ltab', 'tasks'], - ['nodetab', ''], - ['storagetab', ''], - ['pooltab', ''], - ['kvmtab', ''], - ['lxctab', ''], - ['dctab', ''] - ], - - hprefix: 'v1', - - compDict: { - cloudinit: 52, - replication: 51, - system: 50, - monitor: 49, - 'ha-fencing': 48, - 'ha-groups': 47, - 'ha-resources': 46, - 'ceph-log': 45, - 'ceph-crushmap':44, - 'ceph-pools': 43, - 'ceph-osdtree': 42, - 'ceph-disklist': 41, - 'ceph-monlist': 40, - 'ceph-config': 39, - ceph: 38, - 'firewall-fwlog': 37, - 'firewall-options': 36, - 'firewall-ipset': 35, - 'firewall-aliases': 34, - 'firewall-sg': 33, - firewall: 32, - apt: 31, - members: 30, - snapshot: 29, - ha: 28, - support: 27, - pools: 26, - syslog: 25, - ubc: 24, - initlog: 23, - openvz: 22, - backup: 21, - resources: 20, - content: 19, - root: 18, - domains: 17, - roles: 16, - groups: 15, - users: 14, - time: 13, - dns: 12, - network: 11, - services: 10, - options: 9, - console: 8, - hardware: 7, - permissions: 6, - summary: 5, - tasks: 4, - clog: 3, - storage: 2, - folder: 1, - server: 0 - }, - - decodeHToken: function(token) { - var me = this; - - var state = {}; - if (!token) { - Ext.Array.each(me.hslist, function(rec) { - state[rec[0]] = rec[1]; - }); - return state; - } - - // return Ext.urlDecode(token); - - var items = token.split(':'); - var prefix = items.shift(); - - if (prefix != me.hprefix) { - return me.decodeHToken(); - } - - Ext.Array.each(me.hslist, function(rec) { - var value = items.shift(); - if (value) { - if (value[0] === '=') { - value = decodeURIComponent(value.slice(1)); - } else { - Ext.Object.each(me.compDict, function(key, cv) { - if (value == cv) { - value = key; - return false; - } - }); - } - } - state[rec[0]] = value; - }); - - return state; - }, - - encodeHToken: function(state) { - var me = this; - - // return Ext.urlEncode(state); - - var ctoken = me.hprefix; - Ext.Array.each(me.hslist, function(rec) { - var value = state[rec[0]]; - if (!Ext.isDefined(value)) { - value = rec[1]; - } - value = encodeURIComponent(value); - if (!value) { - ctoken += ':'; - } else { - var comp = me.compDict[value]; - if (Ext.isDefined(comp)) { - ctoken += ":" + comp; - } else { - ctoken += ":=" + value; - } - } - }); - - return ctoken; - }, - - constructor: function(config){ - var me = this; - - me.callParent([config]); - - me.UIState = me.decodeHToken(); // set default - - var history_change_cb = function(token) { - //console.log("HC " + token); - if (!token) { - var res = window.confirm(gettext('Are you sure you want to navigate away from this page?')); - if (res){ - // process text value and close... - Ext.History.back(); - } else { - Ext.History.forward(); - } - return; - } - - var newstate = me.decodeHToken(token); - Ext.Array.each(me.hslist, function(rec) { - if (typeof newstate[rec[0]] == "undefined") { - return; - } - me.setHV(rec[0], newstate[rec[0]], true); - }); - }; - - var start_token = Ext.History.getToken(); - if (start_token) { - history_change_cb(start_token); - } else { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - - Ext.History.on('change', history_change_cb); - }, - - get: function(name, defaultValue){ - /*jslint confusion: true */ - var me = this; - var data; - - if (typeof me.UIState[name] != "undefined") { - data = { value: me.UIState[name] }; - } else { - data = me.callParent(arguments); - if (!data && name === 'GuiCap') { - data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {} }; - } - } - - //console.log("GET " + name + " " + Ext.encode(data)); - return data; - }, - - clear: function(name){ - var me = this; - - if (typeof me.UIState[name] != "undefined") { - me.UIState[name] = null; - } - - me.callParent(arguments); - }, - - set: function(name, value, fireevent){ - var me = this; - - //console.log("SET " + name + " " + Ext.encode(value)); - if (typeof me.UIState[name] != "undefined") { - var newvalue = value ? value.value : null; - if (me.setHV(name, newvalue, fireevent)) { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - } else { - me.callParent(arguments); - } - } -}); -Ext.define('PVE.menu.Item', { - extend: 'Ext.menu.Item', - alias: 'widget.pveMenuItem', - - // set to wrap the handler callback in a confirm dialog showing this text - confirmMsg: false, - - // set to focus 'No' instead of 'Yes' button and show a warning symbol - dangerous: false, - - initComponent: function() { - var me = this; - - if (me.handler) { - me.setHandler(me.handler, me.scope); - } - - me.callParent(); - }, - - setHandler: function(fn, scope) { - var me = this; - me.scope = scope; - me.handler = function(button, e) { - var rec, msg; - if (me.confirmMsg) { - msg = me.confirmMsg; - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn === 'yes') { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - } - }); - } else { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - }; - } -}); -Ext.define('PVE.menu.TemplateMenu', { - extend: 'Ext.menu.Menu', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var guestType = me.pveSelNode.data.type; - if (guestType !== 'qemu' && guestType != 'lxc') { - throw "invalid guest type"; - } - - var vmname = me.pveSelNode.data.name; - - var template = me.pveSelNode.data.template; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - me.title = (guestType === 'qemu' ? 'VM ' : 'CT ') + vmid; - - me.items = [ - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: guestType, - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - handler: function() { - var win = Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: template - }); - win.show(); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.button.ConsoleButton', { - extend: 'Ext.button.Split', - alias: 'widget.pveConsoleButton', - - consoleType: 'shell', // one of 'shell', 'kvm', 'lxc', 'upgrade', 'cmd' - - cmd: undefined, - - consoleName: undefined, - - iconCls: 'fa fa-terminal', - - enableSpice: true, - enableXtermjs: true, - - nodename: undefined, - - vmid: 0, - - text: gettext('Console'), - - setEnableSpice: function(enable){ - var me = this; - - me.enableSpice = enable; - me.down('#spicemenu').setDisabled(!enable); - }, - - setEnableXtermJS: function(enable){ - var me = this; - - me.enableXtermjs = enable; - me.down('#xtermjs').setDisabled(!enable); - }, - - handler: function() { - var me = this; - var consoles = { - spice: me.enableSpice, - xtermjs: me.enableXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, me.consoleType, me.vmid, - me.nodename, me.consoleName, me.cmd); - }, - - menu: [ - { - xtype:'menuitem', - text: 'noVNC', - iconCls: 'pve-itype-icon-novnc', - type: 'html5', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - xterm: 'menuitem', - itemId: 'spicemenu', - text: 'SPICE', - type: 'vv', - iconCls: 'pve-itype-icon-virt-viewer', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - text: 'xterm.js', - itemId: 'xtermjs', - iconCls: 'pve-itype-icon-xtermjs', - type: 'xtermjs', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.callParent(); - } -}); -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - * - * does this for the button and every menu item - */ -Ext.define('PVE.button.Split', { - extend: 'Ext.button.Split', - alias: 'widget.pveSplitButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - handlerWrapper: function(button, event) { - var me = this; - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - // confirMsg can be boolean or function - /*jslint confusion: true*/ - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - /*jslint confusion: false*/ - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - callback: function(btn) { - if (btn !== 'yes') { - return; - } - me.realHandler(button, event, rec); - } - }); - } else { - me.realHandler(button, event, rec); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - me.realHandler = me.handler; - me.handler = me.handlerWrapper; - } - - if (me.menu && me.menu.items) { - me.menu.items.forEach(function(item) { - if (item.handler) { - item.realHandler = item.handler; - item.handler = me.handlerWrapper; - } - - if (item.selModel) { - me.mon(item.selModel, "selectionchange", function() { - var rec = item.selModel.getSelection()[0]; - if (!rec || (item.enableFn(rec) === false )) { - item.setDisabled(true); - } else { - item.setDisabled(false); - } - }); - } - }); - } - - me.callParent(); - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); -Ext.define('PVE.controller.StorageEdit', { - extend: 'Ext.app.ViewController', - alias: 'controller.storageEdit', - control: { - 'field[name=content]': { - change: function(field, value) { - var hasBackups = Ext.Array.contains(value, 'backup'); - var maxfiles = this.lookupReference('maxfiles'); - if (!maxfiles) { - return; - } - - if (!hasBackups) { - // clear values which will never be submitted - maxfiles.reset(); - } - maxfiles.setDisabled(!hasBackups); - } - } - } -}); -Ext.define('PVE.qemu.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'suspended': - stopped = false; - suspended = true; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = "VM " + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - hidden: running || suspended, - disabled: running || suspended, - handler: function() { - vm_command('start'); - } - }, - { - text: gettext('Pause'), - iconCls: 'fa fa-fw fa-pause', - hidden: stopped || suspended, - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmpause', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend'); - }); - } - }, - { - text: gettext('Hibernate'), - iconCls: 'fa fa-fw fa-download', - hidden: stopped || suspended, - disabled: stopped || suspended, - tooltip: gettext('Suspend to disk'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmsuspend', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend', { todisk: 1 }); - }); - } - }, - { - text: gettext('Resume'), - iconCls: 'fa fa-fw fa-play', - hidden: !suspended, - handler: function() { - vm_command('resume'); - } - }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: (standalone || !caps.vms['VM.Migrate']) && !caps.vms['VM.Allocate'] && !caps.vms['VM.Clone'] - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - hidden: !caps.vms['VM.Allocate'], - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmtemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var allowSpice = response.result.data.spice; - var allowXtermjs = response.result.data.serial; - var consoles = { - spice: allowSpice, - xtermjs: allowXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, 'kvm', vmid, nodename, vmname); - } - }); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.lxc.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no CT ID specified"; - } - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/lxc/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = 'CT ' + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - disabled: running, - handler: function() { - vm_command('start'); - } - }, -// { -// text: gettext('Suspend'), -// iconCls: 'fa fa-fw fa-pause', -// hidde: suspended, -// disabled: stopped || suspended, -// handler: function() { -// var msg = Proxmox.Utils.format_task_description('vzsuspend', vmid); -// Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { -// if (btn !== 'yes') { -// return; -// } -// -// vm_command('suspend'); -// }); -// } -// }, -// { -// text: gettext('Resume'), -// iconCls: 'fa fa-fw fa-play', -// hidden: !suspended, -// handler: function() { -// vm_command('resume'); -// } -// }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: standalone || !caps.vms['VM.Migrate'] - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'lxc'); - } - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - handler: function() { - var msg = Proxmox.Utils.format_task_description('vztemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.node.CmdMenu', { - extend: 'Ext.menu.Menu', - xtype: 'nodeCmdMenu', - - showSeparator: false, - - items: [ - { - text: gettext('Create VM'), - itemId: 'createvm', - iconCls: 'fa fa-desktop', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.qemu.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { - text: gettext('Create CT'), - itemId: 'createct', - iconCls: 'fa fa-cube', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.lxc.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Bulk Start'), - itemId: 'bulkstart', - iconCls: 'fa fa-fw fa-play', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - itemId: 'bulkstop', - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - itemId: 'bulkmigrate', - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Shell'), - itemId: 'shell', - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - var me = this.up('menu'); - PVE.Utils.openDefaultConsoleWindow(true, 'shell', undefined, me.nodename, undefined); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Wake-on-LAN'), - itemId: 'wakeonlan', - iconCls: 'fa fa-fw fa-power-off', - handler: function() { - var me = this.up('menu'); - Proxmox.Utils.API2Request({ - param: {}, - url: '/nodes/' + me.nodename + '/wakeonlan', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - Ext.Msg.show({ - title: 'Success', - icon: Ext.Msg.INFO, - msg: Ext.String.format(gettext("Wake on LAN packet send for '{0}': '{1}'"), me.nodename, response.result.data) - }); - } - }); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no nodename specified'; - } - - me.title = gettext('Node') + " '" + me.nodename + "'"; - me.callParent(); - - var caps = Ext.state.Manager.get('GuiCap'); - // disable not allowed options - if (!caps.vms['VM.Allocate']) { - me.getComponent('createct').setDisabled(true); - me.getComponent('createvm').setDisabled(true); - } - - if (!caps.nodes['Sys.PowerMgmt']) { - me.getComponent('bulkstart').setDisabled(true); - me.getComponent('bulkstop').setDisabled(true); - me.getComponent('bulkmigrate').setDisabled(true); - me.getComponent('wakeonlan').setDisabled(true); - } - - if (!caps.nodes['Sys.Console']) { - me.getComponent('shell').setDisabled(true); - } - - if (me.pveSelNode.data.running) { - me.getComponent('wakeonlan').setDisabled(true); - } - } -}); -Ext.define('PVE.noVncConsole', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNoVncConsole', - - nodename: undefined, - - vmid: undefined, - - cmd: undefined, - - consoleType: undefined, // lxc, kvm, shell, cmd - - layout: 'fit', - - xtermjs: false, - - border: false, - - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.consoleType) { - throw "no console type specified"; - } - - if (!me.vmid && me.consoleType !== 'shell' && me.consoleType !== 'cmd') { - throw "no VM ID specified"; - } - - // always use same iframe, to avoid running several noVnc clients - // at same time (to avoid performance problems) - var box = Ext.create('Ext.ux.IFrame', { itemid : "vncconsole" }); - - var type = me.xtermjs ? 'xtermjs' : 'novnc'; - Ext.apply(me, { - items: box, - listeners: { - activate: function() { - var queryDict = { - console: me.consoleType, // kvm, lxc, upgrade or shell - vmid: me.vmid, - node: me.nodename, - cmd: me.cmd, - resize: 'scale' - }; - queryDict[type] = 1; - PVE.Utils.cleanEmptyObjectKeys(queryDict); - var url = '/?' + Ext.Object.toQueryString(queryDict); - box.load(url); - } - } - }); - - me.callParent(); - - me.on('afterrender', function() { - me.focus(); - }); - } -}); - -Ext.define('PVE.data.PermPathStore', { - extend: 'Ext.data.Store', - alias: 'store.pvePermPath', - fields: [ 'value' ], - autoLoad: false, - data: [ - {'value': '/'}, - {'value': '/access'}, - {'value': '/nodes'}, - {'value': '/pool'}, - {'value': '/storage'}, - {'value': '/vms'} - ], - - constructor: function(config) { - var me = this; - - config = config || {}; - - me.callParent([config]); - - me.suspendEvents(); - PVE.data.ResourceStore.each(function(record) { - switch (record.get('type')) { - case 'node': - me.add({value: '/nodes/' + record.get('text')}); - break; - - case 'qemu': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'lxc': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'storage': - me.add({value: '/storage/' + record.get('storage')}); - break; - case 'pool': - me.add({value: '/pool/' + record.get('pool')}); - break; - } - }); - me.resumeEvents(); - - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - - me.sort({ - property: 'value', - direction: 'ASC' - }); - } -}); -Ext.define('PVE.data.ResourceStore', { - extend: 'Proxmox.data.UpdateStore', - singleton: true, - - findVMID: function(vmid) { - var me = this, i; - - return (me.findExact('vmid', parseInt(vmid, 10)) >= 0); - }, - - // returns the cached data from all nodes - getNodes: function() { - var me = this; - - var nodes = []; - me.each(function(record) { - if (record.get('type') == "node") { - nodes.push( record.getData() ); - } - }); - - return nodes; - }, - - storageIsShared: function(storage_path) { - var me = this; - - var index = me.findExact('id', storage_path); - - return me.getAt(index).data.shared; - }, - - guestNode: function(vmid) { - var me = this; - - var index = me.findExact('vmid', parseInt(vmid, 10)); - - return me.getAt(index).data.node; - }, - - constructor: function(config) { - // fixme: how to avoid those warnings - /*jslint confusion: true */ - - var me = this; - - config = config || {}; - - var field_defaults = { - type: { - header: gettext('Type'), - type: 'string', - renderer: PVE.Utils.render_resource_type, - sortable: true, - hideable: false, - width: 100 - }, - id: { - header: 'ID', - type: 'string', - hidden: true, - sortable: true, - width: 80 - }, - running: { - header: gettext('Online'), - type: 'boolean', - renderer: Proxmox.Utils.format_boolean, - hidden: true, - convert: function(value, record) { - var info = record.data; - return (Ext.isNumeric(info.uptime) && (info.uptime > 0)); - } - }, - text: { - header: gettext('Description'), - type: 'string', - sortable: true, - width: 200, - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - - if (Ext.isNumeric(info.vmid) && info.vmid > 0) { - text = String(info.vmid); - if (info.name) { - text += " (" + info.name + ')'; - } - } else { // node, pool, storage - text = info[info.type] || info.id; - if (info.node && info.type !== 'node') { - text += " (" + info.node + ")"; - } - } - - return text; - } - }, - vmid: { - header: 'VMID', - type: 'integer', - hidden: true, - sortable: true, - width: 80 - }, - name: { - header: gettext('Name'), - hidden: true, - sortable: true, - type: 'string' - }, - disk: { - header: gettext('Disk usage'), - type: 'integer', - renderer: PVE.Utils.render_disk_usage, - sortable: true, - width: 100, - hidden: true - }, - diskuse: { - header: gettext('Disk usage') + " %", - type: 'number', - sortable: true, - renderer: PVE.Utils.render_disk_usage_percent, - width: 100, - calculate: PVE.Utils.calculate_disk_usage, - sortType: 'asFloat' - }, - maxdisk: { - header: gettext('Disk size'), - type: 'integer', - renderer: PVE.Utils.render_size, - sortable: true, - hidden: true, - width: 100 - }, - mem: { - header: gettext('Memory usage'), - type: 'integer', - renderer: PVE.Utils.render_mem_usage, - sortable: true, - hidden: true, - width: 100 - }, - memuse: { - header: gettext('Memory usage') + " %", - type: 'number', - renderer: PVE.Utils.render_mem_usage_percent, - calculate: PVE.Utils.calculate_mem_usage, - sortType: 'asFloat', - sortable: true, - width: 100 - }, - maxmem: { - header: gettext('Memory size'), - type: 'integer', - renderer: PVE.Utils.render_size, - hidden: true, - sortable: true, - width: 100 - }, - cpu: { - header: gettext('CPU usage'), - type: 'float', - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100 - }, - maxcpu: { - header: gettext('maxcpu'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - diskread: { - header: gettext('Total Disk Read'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - diskwrite: { - header: gettext('Total Disk Write'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netin: { - header: gettext('Total NetIn'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netout: { - header: gettext('Total NetOut'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - template: { - header: gettext('Template'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - uptime: { - header: gettext('Uptime'), - type: 'integer', - renderer: Proxmox.Utils.render_uptime, - sortable: true, - width: 110 - }, - node: { - header: gettext('Node'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - storage: { - header: gettext('Storage'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - pool: { - header: gettext('Pool'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - hastate: { - header: gettext('HA State'), - type: 'string', - defaultValue: 'unmanaged', - hidden: true, - sortable: true - }, - status: { - header: gettext('Status'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - lock: { - header: gettext('Lock'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - } - }; - - var fields = []; - var fieldNames = []; - Ext.Object.each(field_defaults, function(key, value) { - var field = {name: key, type: value.type}; - if (Ext.isDefined(value.convert)) { - field.convert = value.convert; - } - - if (Ext.isDefined(value.calculate)) { - field.calculate = value.calculate; - } - - if (Ext.isDefined(value.defaultValue)) { - field.defaultValue = value.defaultValue; - } - - fields.push(field); - fieldNames.push(key); - }); - - Ext.define('PVEResources', { - extend: "Ext.data.Model", - fields: fields, - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/resources' - } - }); - - Ext.define('PVETree', { - extend: "Ext.data.Model", - fields: fields, - proxy: { type: 'memory' } - }); - - Ext.apply(config, { - storeid: 'PVEResources', - model: 'PVEResources', - defaultColumns: function() { - var res = []; - Ext.Object.each(field_defaults, function(field, info) { - var fi = Ext.apply({ dataIndex: field }, info); - res.push(fi); - }); - return res; - }, - fieldNames: fieldNames - }); - - me.callParent([config]); - } -}); -Ext.define('pve-domains', { - extend: "Ext.data.Model", - fields: [ - 'realm', 'type', 'comment', 'default', 'tfa', - { - name: 'descr', - // Note: We use this in the RealmComboBox.js (see Bug #125) - convert: function(value, record) { - if (value) { - return value; - } - - var info = record.data; - // return realm if there is no comment - var text = info.comment || info.realm; - - if (info.tfa) { - text += " (+ " + info.tfa + ")"; - } - - return Ext.String.htmlEncode(text); - } - } - ], - idProperty: 'realm', - proxy: { - type: 'proxmox', - url: "/api2/json/access/domains" - } -}); -Ext.define('pve-rrd-node', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - { - name:'iowait', - // percentage - convert: function(value) { - return value*100; - } - }, - 'loadavg', - 'maxcpu', - 'memtotal', - 'memused', - 'netin', - 'netout', - 'roottotal', - 'rootused', - 'swaptotal', - 'swapused', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-guest', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - 'maxcpu', - 'netin', - 'netout', - 'mem', - 'maxmem', - 'disk', - 'maxdisk', - 'diskread', - 'diskwrite', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-storage', { - extend: 'Ext.data.Model', - fields: [ - 'used', - 'total', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); -Ext.define('PVE.form.VlanField', { - extend: 'Ext.form.field.Number', - alias: ['widget.pveVlanField'], - - deleteEmpty: false, - - emptyText: 'no VLAN', - - fieldLabel: gettext('VLAN Tag'), - - allowBlank: true, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val) { - data = {}; - data[me.getName()] = val; - } else if (me.deleteEmpty) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - minValue: 1, - maxValue: 4094 - }); - - me.callParent(); - } -}); -// boolean type including 'Default' (delete property from file) -Ext.define('PVE.form.Boolean', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.booleanfield'], - comboItems: [ - ['__default__', gettext('Default')], - [1, gettext('Yes')], - [0, gettext('No')] - ] -}); -Ext.define('PVE.form.CompressionSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveCompressionSelector'], - comboItems: [ - ['0', Proxmox.Utils.noneText], - ['lzo', 'LZO (' + gettext('fast') + ')'], - ['gzip', 'GZIP (' + gettext('good') + ')'] - ] -}); -Ext.define('PVE.form.PoolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pvePoolSelector'], - - allowBlank: false, - valueField: 'poolid', - displayField: 'poolid', - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: 'poolid' - }); - - Ext.apply(me, { - store: store, - autoSelect: false, - listConfig: { - columns: [ - { - header: gettext('Pool'), - sortable: true, - dataIndex: 'poolid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-pools', { - extend: 'Ext.data.Model', - fields: [ 'poolid', 'comment' ], - proxy: { - type: 'proxmox', - url: "/api2/json/pools" - }, - idProperty: 'poolid' - }); - -}); -Ext.define('PVE.form.PrivilegesSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'pvePrivilegesSelector', - - multiSelect: true, - - initComponent: function() { - var me = this; - - // So me.store is available. - me.callParent(); - - Proxmox.Utils.API2Request({ - url: '/access/roles/Administrator', - method: 'GET', - success: function(response, options) { - var data = [], key; - /*jslint forin: true */ - for (key in response.result.data) { - data.push([key, key]); - } - /*jslint forin: false */ - - me.store.setData(data); - - me.store.sort({ - property: 'key', - direction: 'ASC' - }); - }, - - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -Ext.define('pve-groups', { - extend: 'Ext.data.Model', - fields: [ 'groupid', 'comment' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/groups" - }, - idProperty: 'groupid' -}); - -Ext.define('PVE.form.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveGroupSelector', - - allowBlank: false, - autoSelect: false, - valueField: 'groupid', - displayField: 'groupid', - listConfig: { - columns: [ - { - header: gettext('Group'), - sortable: true, - dataIndex: 'groupid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - }, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: [{ - property: 'groupid' - }] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}); -Ext.define('PVE.form.UserSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUserSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'userid', - displayField: 'userid', - - editable: true, - anyMatch: true, - forceSelection: true, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-users', - sorters: [{ - property: 'userid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('User'), - sortable: true, - dataIndex: 'userid', - flex: 1 - }, - { - header: gettext('Name'), - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load({ params: { enabled: 1 }}); - } - -}, function() { - - Ext.define('pve-users', { - extend: 'Ext.data.Model', - fields: [ - 'userid', 'firstname', 'lastname' , 'email', 'comment', - { type: 'boolean', name: 'enable' }, - { type: 'date', dateFormat: 'timestamp', name: 'expire' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/users" - }, - idProperty: 'userid' - }); - -}); - - -Ext.define('PVE.form.RoleSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveRoleSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'roleid', - displayField: 'roleid', - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: [{ - property: 'roleid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Role'), - sortable: true, - dataIndex: 'roleid', - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-roles', { - extend: 'Ext.data.Model', - fields: [ 'roleid', 'privs' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/roles" - }, - idProperty: 'roleid' - }); - -}); -Ext.define('PVE.form.GuestIDSelector', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveGuestIDSelector', - - allowBlank: false, - - minValue: 100, - - maxValue: 999999999, - - validateExists: undefined, - - loadNextFreeID: false, - - guestType: undefined, - - validator: function(value) { - var me = this; - - if (!Ext.isNumeric(value) || - value < me.minValue || - value > me.maxValue) { - // check is done by ExtJS - return true; - } - - if (me.validateExists === true && !me.exists) { - return me.unknownID; - } - - if (me.validateExists === false && me.exists) { - return me.inUseID; - } - - return true; - }, - - initComponent: function() { - var me = this; - var label = '{0} ID'; - var unknownID = gettext('This {0} ID does not exists'); - var inUseID = gettext('This {0} ID is already in use'); - var type = 'CT/VM'; - - if (me.guestType === 'lxc') { - type = 'CT'; - } else if (me.guestType === 'qemu') { - type = 'VM'; - } - - me.label = Ext.String.format(label, type); - me.unknownID = Ext.String.format(unknownID, type); - me.inUseID = Ext.String.format(inUseID, type); - - Ext.apply(me, { - fieldLabel: me.label, - listeners: { - 'change': function(field, newValue, oldValue) { - if (!Ext.isDefined(me.validateExists)) { - return; - } - Proxmox.Utils.API2Request({ - params: { vmid: newValue }, - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.exists = false; - me.validate(); - }, - failure: function(response, opts) { - me.exists = true; - me.validate(); - } - }); - } - } - }); - - me.callParent(); - - if (me.loadNextFreeID) { - Proxmox.Utils.API2Request({ - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.setRawValue(response.result.data); - } - }); - } - } -}); -Ext.define('PVE.form.MemoryField', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveMemoryField', - - allowBlank: false, - - hotplug: false, - - minValue: 32, - - maxValue: 4178944, - - step: 32, - - value: '512', // qm default - - allowDecimals: false, - - allowExponential: false, - - computeUpDown: function(value) { - var me = this; - - if (!me.hotplug) { - return { up: value + me.step, down: value - me.step }; - } - - var dimm_size = 512; - var prev_dimm_size = 0; - var min_size = 1024; - var current_size = min_size; - var value_up = min_size; - var value_down = min_size; - var value_start = min_size; - - var i, j; - for (j = 0; j < 9; j++) { - for (i = 0; i < 32; i++) { - if ((value >= current_size) && (value < (current_size + dimm_size))) { - value_start = current_size; - value_up = current_size + dimm_size; - value_down = current_size - ((i === 0) ? prev_dimm_size : dimm_size); - } - current_size += dimm_size; - } - prev_dimm_size = dimm_size; - dimm_size = dimm_size*2; - } - - return { up: value_up, down: value_down, start: value_start }; - }, - - onSpinUp: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.up, me.minValue, me.maxValue)); - } - }, - - onSpinDown: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.down, me.minValue, me.maxValue)); - } - }, - - initComponent: function() { - var me = this; - - if (me.hotplug) { - me.minValue = 1024; - - me.on('blur', function(field) { - var value = me.getValue(); - var res = me.computeUpDown(value); - if (value === res.start || value === res.up || value === res.down) { - return; - } - field.setValue(res.up); - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.form.NetworkCardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveNetworkCardSelector', - comboItems: [ - ['e1000', 'Intel E1000'], - ['virtio', 'VirtIO (' + gettext('paravirtualized') + ')'], - ['rtl8139', 'Realtek RTL8139'], - ['vmxnet3', 'VMware vmxnet3'] - ] -}); -Ext.define('PVE.form.DiskFormatSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveDiskFormatSelector', - comboItems: [ - ['raw', gettext('Raw disk image') + ' (raw)'], - ['qcow2', gettext('QEMU image format') + ' (qcow2)'], - ['vmdk', gettext('VMware image format') + ' (vmdk)'] - ] -}); -Ext.define('PVE.form.DiskSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveDiskSelector', - - // can be - // undefined: all - // unused: only unused - // journal_disk: all disks with gpt - diskType: undefined, - - valueField: 'devpath', - displayField: 'devpath', - emptyText: gettext('No Disks unused'), - listConfig: { - width: 600, - columns: [ - { - header: gettext('Device'), - flex: 3, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Size'), - flex: 2, - sortable: false, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Serial'), - flex: 5, - sortable: true, - dataIndex: 'serial' - } - ] - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - filterOnLoad: true, - model: 'pve-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list", - extraParams: { type: me.diskType } - }, - sorters: [ - { - property : 'devpath', - direction: 'ASC' - } - ] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}, function() { - - Ext.define('pve-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial'], - idProperty: 'devpath' - }); -}); -Ext.define('PVE.form.BusTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveBusSelector', - - noVirtIO: false, - - initComponent: function() { - var me = this; - - me.comboItems = [['ide', 'IDE'], ['sata', 'SATA']]; - - if (!me.noVirtIO) { - me.comboItems.push(['virtio', 'VirtIO Block']); - } - - me.comboItems.push(['scsi', 'SCSI']); - - me.callParent(); - } -}); -Ext.define('PVE.form.ControllerSelector', { - extend: 'Ext.form.FieldContainer', - alias: 'widget.pveControllerSelector', - - statics: { - maxIds: { - ide: 3, - sata: 5, - virtio: 15, - scsi: 13 - } - }, - - noVirtIO: false, - - vmconfig: {}, // used to check for existing devices - - sortByPreviousUsage: function(vmconfig, controllerList) { - - var usedControllers = Ext.clone(PVE.form.ControllerSelector.maxIds); - - var type; - for (type in usedControllers) { - if(usedControllers.hasOwnProperty(type)) { - usedControllers[type] = 0; - } - } - - var property; - for (property in vmconfig) { - if (vmconfig.hasOwnProperty(property)) { - if (property.match(PVE.Utils.bus_match) && !vmconfig[property].match(/media=cdrom/)) { - var foundController = property.match(PVE.Utils.bus_match)[1]; - usedControllers[foundController]++; - } - } - } - - var vmDefaults = PVE.qemu.OSDefaults[vmconfig.ostype]; - - var sortPriority = vmDefaults && vmDefaults.busPriority - ? vmDefaults.busPriority : PVE.qemu.OSDefaults.generic; - - var sortedList = Ext.clone(controllerList); - sortedList.sort(function(a,b) { - if (usedControllers[b] == usedControllers[a]) { - return sortPriority[b] - sortPriority[a]; - } - return usedControllers[b] - usedControllers[a]; - }); - - return sortedList; - }, - - setVMConfig: function(vmconfig, autoSelect) { - var me = this; - - me.vmconfig = Ext.apply({}, vmconfig); - - var clist = ['ide', 'virtio', 'scsi', 'sata']; - var bussel = me.down('field[name=controller]'); - var deviceid = me.down('field[name=deviceid]'); - - if (autoSelect === 'cdrom') { - clist = ['ide', 'scsi', 'sata']; - if (!Ext.isDefined(me.vmconfig.ide2)) { - bussel.setValue('ide'); - deviceid.setValue(2); - return; - } - } else { - // in most cases we want to add a disk to the same controller - // we previously used - clist = me.sortByPreviousUsage(me.vmconfig, clist); - } - - Ext.Array.each(clist, function(controller) { - var confid, i; - bussel.setValue(controller); - for (i = 0; i <= PVE.form.ControllerSelector.maxIds[controller]; i++) { - confid = controller + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - deviceid.setValue(i); - return false; // break - } - } - }); - deviceid.validate(); - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - fieldLabel: gettext('Bus/Device'), - layout: 'hbox', - defaults: { - hideLabel: true - }, - items: [ - { - xtype: 'pveBusSelector', - name: 'controller', - value: PVE.qemu.OSDefaults.generic.busType, - noVirtIO: me.noVirtIO, - allowBlank: false, - flex: 2, - listeners: { - change: function(t, value) { - if (!value) { - return; - } - var field = me.down('field[name=deviceid]'); - field.setMaxValue(PVE.form.ControllerSelector.maxIds[value]); - field.validate(); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'deviceid', - minValue: 0, - maxValue: PVE.form.ControllerSelector.maxIds.ide, - value: '0', - flex: 1, - allowBlank: false, - validator: function(value) { - /*jslint confusion: true */ - if (!me.rendered) { - return; - } - var field = me.down('field[name=controller]'); - var controller = field.getValue(); - var confid = controller + value; - if (Ext.isDefined(me.vmconfig[confid])) { - return "This device is already in use."; - } - return true; - } - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.EmailNotificationSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveEmailNotificationSelector'], - comboItems: [ - ['always', gettext('Always')], - ['failure', gettext('On failure only')] - ] -}); -/*global Proxmox*/ -Ext.define('PVE.form.RealmComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveRealmComboBox'], - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store.on('load', this.onLoad, view); - }, - - onLoad: function(store, records, success) { - if (!success) { - return; - } - var me = this; - var val = me.getValue(); - if (!val || !me.store.findRecord('realm', val)) { - var def = 'pam'; - Ext.each(records, function(rec) { - if (rec.data && rec.data['default']) { - def = rec.data.realm; - } - }); - me.setValue(def); - } - } - }, - - fieldLabel: gettext('Realm'), - name: 'realm', - queryMode: 'local', - allowBlank: false, - editable: false, - forceSelection: true, - autoSelect: false, - triggerAction: 'all', - valueField: 'realm', - displayField: 'descr', - getState: function() { - return { value: this.getValue() }; - }, - applyState : function(state) { - if (state && state.value) { - this.setValue(state.value); - } - }, - stateEvents: [ 'select' ], - stateful: true, // last chosen auth realm is saved between page reloads - id: 'pveloginrealm', // We need stable ids when using stateful, not autogenerated - stateID: 'pveloginrealm', - - needOTP: function(realm) { - var me = this; - // use exact match - var rec = me.store.findRecord('realm', realm, 0, false, false, true); - return rec && rec.data && rec.data.tfa ? rec.data.tfa : undefined; - }, - - store: { - model: 'pve-domains', - autoLoad: true - } -}); -/* - * Top left combobox, used to select a view of the underneath RessourceTree - */ -Ext.define('PVE.form.ViewSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveViewSelector'], - - editable: false, - allowBlank: false, - forceSelection: true, - autoSelect: false, - valueField: 'key', - displayField: 'value', - hideLabel: true, - queryMode: 'local', - - initComponent: function() { - var me = this; - - var default_views = { - server: { - text: gettext('Server View'), - groups: ['node'] - }, - folder: { - text: gettext('Folder View'), - groups: ['type'] - }, - storage: { - text: gettext('Storage View'), - groups: ['node'], - filterfn: function(node) { - return node.data.type === 'storage' || node.data.type === 'node'; - } - }, - pool: { - text: gettext('Pool View'), - groups: ['pool'], - // Pool View only lists VMs and Containers - filterfn: function(node) { - return node.data.type === 'qemu' || node.data.type === 'lxc' || node.data.type === 'openvz' || - node.data.type === 'pool'; - } - } - }; - - var groupdef = []; - Ext.Object.each(default_views, function(viewname, value) { - groupdef.push([viewname, value.text]); - }); - - var store = Ext.create('Ext.data.Store', { - model: 'KeyValue', - proxy: { - type: 'memory', - reader: 'array' - }, - data: groupdef, - autoload: true - }); - - Ext.apply(me, { - store: store, - value: groupdef[0][0], - getViewFilter: function() { - var view = me.getValue(); - return Ext.apply({ id: view }, default_views[view] || default_views.server); - }, - - getState: function() { - return { value: me.getValue() }; - }, - - applyState : function(state, doSelect) { - var view = me.getValue(); - if (state && state.value && (view != state.value)) { - var record = store.findRecord('key', state.value); - if (record) { - me.setValue(state.value, true); - if (doSelect) { - me.fireEvent('select', me, [record]); - } - } - } - }, - stateEvents: [ 'select' ], - stateful: true, - stateId: 'pveview', - id: 'view' - }); - - me.callParent(); - - var statechange = function(sp, key, value) { - if (key === me.id) { - me.applyState(value, true); - } - }; - - var sp = Ext.state.Manager.getProvider(); - me.mon(sp, 'statechange', statechange, me); - } -}); -Ext.define('PVE.form.NodeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveNodeSelector'], - - // invalidate nodes which are offline - onlineValidator: false, - - selectCurNode: false, - - // do not allow those nodes (array) - disallowedNodes: undefined, - - // only allow those nodes (array) - allowedNodes: undefined, - // set default value to empty array, else it inits it with - // null and after the store load it is an empty array, - // triggering dirtychange - value: [], - valueField: 'node', - displayField: 'node', - store: { - fields: [ 'node', 'cpu', 'maxcpu', 'mem', 'maxmem', 'uptime' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes' - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - }, - { - property : 'mem', - direction: 'DESC' - } - ] - }, - - listConfig: { - columns: [ - { - header: gettext('Node'), - dataIndex: 'node', - sortable: true, - hideable: false, - flex: 1 - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 100, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100, - dataIndex: 'cpu' - } - ] - }, - - validator: function(value) { - /*jslint confusion: true */ - var me = this; - if (!me.onlineValidator || (me.allowBlank && !value)) { - return true; - } - - var offline = []; - var notAllowed = []; - - Ext.Array.each(value.split(/\s*,\s*/), function(node) { - var rec = me.store.findRecord(me.valueField, node); - if (!(rec && rec.data) || rec.data.status !== 'online') { - offline.push(node); - } else if (me.allowedNodes && !Ext.Array.contains(me.allowedNodes, node)) { - notAllowed.push(node); - } - }); - - if (value && notAllowed.length !== 0) { - return "Node " + notAllowed.join(', ') + " is not allowed for this action!"; - } - - if (value && offline.length !== 0) { - return "Node " + offline.join(', ') + " seems to be offline!"; - } - return true; - }, - - initComponent: function() { - var me = this; - - if (me.selectCurNode && PVE.curSelectedNode && PVE.curSelectedNode.data.node) { - me.preferredValue = PVE.curSelectedNode.data.node; - } - - me.callParent(); - me.getStore().load(); - - // filter out disallowed nodes - me.getStore().addFilter(new Ext.util.Filter({ - filterFn: function(item) { - if (Ext.isArray(me.disallowedNodes)) { - return !Ext.Array.contains(me.disallowedNodes, item.data.node); - } else { - return true; - } - } - })); - - me.mon(me.getStore(), 'load', function(){ - me.isValid(); - }); - } -}); -Ext.define('PVE.form.FileSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFileSelector', - - editable: true, - anyMatch: true, - forceSelection: true, - - listeners: { - afterrender: function() { - var me = this; - if (!me.disabled) { - me.setStorage(me.storage, me.nodename); - } - } - }, - - setStorage: function(storage, nodename) { - var me = this; - - var change = false; - if (storage && (me.storage !== storage)) { - me.storage = storage; - change = true; - } - - if (nodename && (me.nodename !== nodename)) { - me.nodename = nodename; - change = true; - } - - if (!(me.storage && me.nodename && change)) { - return; - } - - var url = '/api2/json/nodes/' + me.nodename + '/storage/' + me.storage + '/content'; - if (me.storageContent) { - url += '?content=' + me.storageContent; - } - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - me.store.removeAll(); - me.store.load(); - }, - - setNodename: function(nodename) { - this.setStorage(undefined, nodename); - }, - - store: { - model: 'pve-storage-content' - }, - - allowBlank: false, - autoSelect: false, - valueField: 'volid', - displayField: 'text', - - listConfig: { - width: 600, - columns: [ - { - header: gettext('Name'), - dataIndex: 'text', - hideable: false, - flex: 1 - }, - { - header: gettext('Format'), - width: 60, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - dataIndex: 'size', - renderer: Proxmox.Utils.format_size - } - ] - } -}); -Ext.define('PVE.form.StorageSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveStorageSelector', - - allowBlank: false, - valueField: 'storage', - displayField: 'storage', - listConfig: { - width: 450, - columns: [ - { - header: gettext('Name'), - dataIndex: 'storage', - hideable: false, - flex: 1 - }, - { - header: gettext('Type'), - width: 75, - dataIndex: 'type' - }, - { - header: gettext('Avail'), - width: 90, - dataIndex: 'avail', - renderer: Proxmox.Utils.format_size - }, - { - header: gettext('Capacity'), - width: 90, - dataIndex: 'total', - renderer: Proxmox.Utils.format_size - } - ] - }, - - reloadStorageList: function() { - var me = this; - if (!me.nodename) { - return; - } - - var params = { - format: 1 - }; - var url = '/api2/json/nodes/' + me.nodename + '/storage'; - if (me.storageContent) { - params.content = me.storageContent; - } - if (me.targetNode) { - params.target = me.targetNode; - params.enabled = 1; // skip disabled storages - } - me.store.setProxy({ - type: 'proxmox', - url: url, - extraParams: params - }); - - me.store.load(); - - }, - - setTargetNode: function(targetNode) { - var me = this; - - if (!targetNode || (me.targetNode === targetNode)) { - return; - } - - me.targetNode = targetNode; - - me.reloadStorageList(); - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.reloadStorageList(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - var store = Ext.create('Ext.data.Store', { - model: 'pve-storage-status', - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - if (nodename) { - me.setNodename(nodename); - } - } -}, function() { - - Ext.define('pve-storage-status', { - extend: 'Ext.data.Model', - fields: [ 'storage', 'active', 'type', 'avail', 'total' ], - idProperty: 'storage' - }); - -}); -Ext.define('PVE.form.DiskStorageSelector', { - extend: 'Ext.container.Container', - alias: 'widget.pveDiskStorageSelector', - - layout: 'fit', - defaults: { - margin: '0 0 5 0' - }, - - // the fieldLabel for the storageselector - storageLabel: gettext('Storage'), - - // the content to show (e.g., images or rootdir) - storageContent: undefined, - - // if true, selects the first available storage - autoSelect: false, - - allowBlank: false, - emptyText: '', - - // hides the selection field - // this is always hidden on creation, - // and only shown when the storage needs a selection and - // hideSelection is not true - hideSelection: undefined, - - // hides the size field (e.g, for the efi disk dialog) - hideSize: false, - - // sets the initial size value - // string because else we get a type confusion - defaultSize: '32', - - changeStorage: function(f, value) { - var me = this; - var formatsel = me.getComponent('diskformat'); - var hdfilesel = me.getComponent('hdimage'); - var hdsizesel = me.getComponent('disksize'); - - // initial store load, and reset/deletion of the storage - if (!value) { - hdfilesel.setDisabled(true); - hdfilesel.setVisible(false); - - formatsel.setDisabled(true); - return; - } - - var rec = f.store.getById(value); - // if the storage is not defined, or valid, - // we cannot know what to enable/disable - if (!rec) { - return; - } - - var selectformat = false; - if (rec.data.format) { - var format = rec.data.format[0]; // 0 is the formats, 1 the default in the backend - delete format.subvol; // we never need subvol in the gui - selectformat = (Ext.Object.getSize(format) > 1); - } - - var select = !!rec.data.select_existing && !me.hideSelection; - - formatsel.setDisabled(!selectformat); - formatsel.setValue(selectformat ? 'qcow2' : 'raw'); - - hdfilesel.setDisabled(!select); - hdfilesel.setVisible(select); - if (select) { - hdfilesel.setStorage(value); - } - - hdsizesel.setDisabled(select || me.hideSize); - hdsizesel.setVisible(!select && !me.hideSize); - }, - - setNodename: function(nodename) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - var hdfilesel = me.getComponent('hdimage'); - - hdstorage.setNodename(nodename); - hdfilesel.setNodename(nodename); - }, - - setDisabled: function(value) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - - // reset on disable - if (value) { - hdstorage.setValue(); - } - hdstorage.setDisabled(value); - - // disabling does not always fire this event and we do not need - // the value of the validity - hdstorage.fireEvent('validitychange'); - }, - - initComponent: function() { - var me = this; - - me.items = [ - { - xtype: 'pveStorageSelector', - itemId: 'hdstorage', - name: 'hdstorage', - reference: 'hdstorage', - fieldLabel: me.storageLabel, - nodename: me.nodename, - storageContent: me.storageContent, - disabled: me.disabled, - autoSelect: me.autoSelect, - allowBlank: me.allowBlank, - emptyText: me.emptyText, - listeners: { - change: { - fn: me.changeStorage, - scope: me - } - } - }, - { - xtype: 'pveFileSelector', - name: 'hdimage', - reference: 'hdimage', - itemId: 'hdimage', - fieldLabel: gettext('Disk image'), - nodename: me.nodename, - disabled: true, - hidden: true - }, - { - xtype: 'numberfield', - itemId: 'disksize', - reference: 'disksize', - name: 'disksize', - fieldLabel: gettext('Disk size') + ' (GiB)', - hidden: me.hideSize, - disabled: me.hideSize, - minValue: 0.001, - maxValue: 128*1024, - decimalPrecision: 3, - value: me.defaultSize, - allowBlank: false - }, - { - xtype: 'pveDiskFormatSelector', - itemId: 'diskformat', - reference: 'diskformat', - name: 'diskformat', - fieldLabel: gettext('Format'), - nodename: me.nodename, - disabled: true, - hidden: me.storageContent === 'rootdir', - value: 'qcow2', - allowBlank: false - } - ]; - - // use it to disable the children but not ourself - me.disabled = false; - - me.callParent(); - } -}); -Ext.define('PVE.form.BridgeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.BridgeSelector'], - - bridgeType: 'any_bridge', // bridge, OVSBridge or any_bridge - - store: { - fields: [ 'iface', 'active', 'type' ], - filterOnLoad: true, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }, - valueField: 'iface', - displayField: 'iface', - listConfig: { - columns: [ - { - header: gettext('Bridge'), - dataIndex: 'iface', - hideable: false, - width: 100 - }, - { - header: gettext('Active'), - width: 60, - dataIndex: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/network?type=' + - me.bridgeType - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.PCISelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pvePCISelector', - - store: { - fields: [ 'id','vendor_name', 'device_name', 'vendor', 'device', 'iommugroup', 'mdev' ], - filterOnLoad: true, - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }, - - autoSelect: false, - valueField: 'id', - displayField: 'id', - - // can contain a load callback for the store - // useful to determine the state of the IOMMU - onLoadCallBack: undefined, - - listConfig: { - width: 800, - columns: [ - { - header: 'ID', - dataIndex: 'id', - width: 80 - }, - { - header: gettext('IOMMU Group'), - dataIndex: 'iommugroup', - width: 50 - }, - { - header: gettext('Vendor'), - dataIndex: 'vendor_name', - flex: 2 - }, - { - header: gettext('Device'), - dataIndex: 'device_name', - flex: 6 - }, - { - header: gettext('Mediated Devices'), - dataIndex: 'mdev', - flex: 1, - renderer: function(val) { - return Proxmox.Utils.format_boolean(!!val); - } - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - if (me.onLoadCallBack !== undefined) { - me.mon(me.getStore(), 'load', me.onLoadCallBack); - } - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.MDevSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveMDevSelector', - - store: { - fields: [ 'type','available', 'description' ], - filterOnLoad: true, - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ] - }, - autoSelect: false, - valueField: 'type', - displayField: 'type', - listConfig: { - columns: [ - { - header: gettext('Type'), - dataIndex: 'type', - flex: 1 - }, - { - header: gettext('Available'), - dataIndex: 'available', - width: 80 - }, - { - header: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value) { - if (!value) { - return ''; - } - - return value.split('\n').join('
'); - } - } - ] - }, - - setPciID: function(pciid, force) { - var me = this; - - if (!force && (!pciid || (me.pciid === pciid))) { - return; - } - - me.pciid = pciid; - me.updateProxy(); - }, - - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - me.updateProxy(); - }, - - updateProxy: function() { - var me = this; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci/' + me.pciid + '/mdev' - }); - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no node name specified'; - } - - me.callParent(); - - if (me.pciid) { - me.setPciID(me.pciid, true); - } - } -}); - -Ext.define('PVE.form.SecurityGroupsSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveSecurityGroupsSelector'], - - valueField: 'group', - displayField: 'group', - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'group', 'comment' ], - idProperty: 'group', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/groups" - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Security Group'), - dataIndex: 'group', - hideable: false, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPRefSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPRefSelector'], - - base_url: undefined, - - preferredValue: '', // hack: else Form sets dirty flag? - - ref_type: undefined, // undefined = any [undefined, 'ipset' or 'alias'] - - valueField: 'ref', - displayField: 'ref', - - initComponent: function() { - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - - var url = "/api2/json" + me.base_url; - if (me.ref_type) { - url += "?type=" + me.ref_type; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'type', 'name', 'ref', 'comment' ], - idProperty: 'ref', - proxy: { - type: 'proxmox', - url: url - }, - sorters: { - property: 'ref', - order: 'DESC' - } - }); - - var disable_query_for_ips = function(f, value) { - if (value === null || - value.match(/^\d/)) { // IP address starts with \d - f.queryDelay = 9999999999; // hack: disable with long delay - } else { - f.queryDelay = 10; - } - }; - - var columns = []; - - if (!me.ref_type) { - columns.push({ - header: gettext('Type'), - dataIndex: 'type', - hideable: false, - width: 60 - }); - } - - columns.push( - { - header: gettext('Name'), - dataIndex: 'ref', - hideable: false, - width: 140 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ); - - Ext.apply(me, { - store: store, - listConfig: { columns: columns } - }); - - me.on('change', disable_query_for_ips); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPProtocolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPProtocolSelector'], - valueField: 'p', - displayField: 'p', - listConfig: { - columns: [ - { - header: gettext('Protocol'), - dataIndex: 'p', - hideable: false, - sortable: false, - width: 100 - }, - { - header: gettext('Number'), - dataIndex: 'n', - hideable: false, - sortable: false, - width: 50 - }, - { - header: gettext('Description'), - dataIndex: 'd', - hideable: false, - sortable: false, - flex: 1 - } - ] - }, - store: { - fields: [ 'p', 'd', 'n'], - data: [ - { p: 'tcp', n: 6, d: 'Transmission Control Protocol' }, - { p: 'udp', n: 17, d: 'User Datagram Protocol' }, - { p: 'icmp', n: 1, d: 'Internet Control Message Protocol' }, - { p: 'igmp', n: 2, d: 'Internet Group Management' }, - { p: 'ggp', n: 3, d: 'gateway-gateway protocol' }, - { p: 'ipencap', n: 4, d: 'IP encapsulated in IP' }, - { p: 'st', n: 5, d: 'ST datagram mode' }, - { p: 'egp', n: 8, d: 'exterior gateway protocol' }, - { p: 'igp', n: 9, d: 'any private interior gateway (Cisco)' }, - { p: 'pup', n: 12, d: 'PARC universal packet protocol' }, - { p: 'hmp', n: 20, d: 'host monitoring protocol' }, - { p: 'xns-idp', n: 22, d: 'Xerox NS IDP' }, - { p: 'rdp', n: 27, d: '"reliable datagram" protocol' }, - { p: 'iso-tp4', n: 29, d: 'ISO Transport Protocol class 4 [RFC905]' }, - { p: 'dccp', n: 33, d: 'Datagram Congestion Control Prot. [RFC4340]' }, - { p: 'xtp', n: 36, d: 'Xpress Transfer Protocol' }, - { p: 'ddp', n: 37, d: 'Datagram Delivery Protocol' }, - { p: 'idpr-cmtp', n: 38, d: 'IDPR Control Message Transport' }, - { p: 'ipv6', n: 41, d: 'Internet Protocol, version 6' }, - { p: 'ipv6-route', n: 43, d: 'Routing Header for IPv6' }, - { p: 'ipv6-frag', n: 44, d: 'Fragment Header for IPv6' }, - { p: 'idrp', n: 45, d: 'Inter-Domain Routing Protocol' }, - { p: 'rsvp', n: 46, d: 'Reservation Protocol' }, - { p: 'gre', n: 47, d: 'General Routing Encapsulation' }, - { p: 'esp', n: 50, d: 'Encap Security Payload [RFC2406]' }, - { p: 'ah', n: 51, d: 'Authentication Header [RFC2402]' }, - { p: 'skip', n: 57, d: 'SKIP' }, - { p: 'ipv6-icmp', n: 58, d: 'ICMP for IPv6' }, - { p: 'ipv6-nonxt', n: 59, d: 'No Next Header for IPv6' }, - { p: 'ipv6-opts', n: 60, d: 'Destination Options for IPv6' }, - { p: 'vmtp', n: 81, d: 'Versatile Message Transport' }, - { p: 'eigrp', n: 88, d: 'Enhanced Interior Routing Protocol (Cisco)' }, - { p: 'ospf', n: 89, d: 'Open Shortest Path First IGP' }, - { p: 'ax.25', n: 93, d: 'AX.25 frames' }, - { p: 'ipip', n: 94, d: 'IP-within-IP Encapsulation Protocol' }, - { p: 'etherip', n: 97, d: 'Ethernet-within-IP Encapsulation [RFC3378]' }, - { p: 'encap', n: 98, d: 'Yet Another IP encapsulation [RFC1241]' }, - { p: 'pim', n: 103, d: 'Protocol Independent Multicast' }, - { p: 'ipcomp', n: 108, d: 'IP Payload Compression Protocol' }, - { p: 'vrrp', n: 112, d: 'Virtual Router Redundancy Protocol [RFC5798]' }, - { p: 'l2tp', n: 115, d: 'Layer Two Tunneling Protocol [RFC2661]' }, - { p: 'isis', n: 124, d: 'IS-IS over IPv4' }, - { p: 'sctp', n: 132, d: 'Stream Control Transmission Protocol' }, - { p: 'fc', n: 133, d: 'Fibre Channel' }, - { p: 'mobility-header', n: 135, d: 'Mobility Support for IPv6 [RFC3775]' }, - { p: 'udplite', n: 136, d: 'UDP-Lite [RFC3828]' }, - { p: 'mpls-in-ip', n: 137, d: 'MPLS-in-IP [RFC4023]' }, - { p: 'hip', n: 139, d: 'Host Identity Protocol' }, - { p: 'shim6', n: 140, d: 'Shim6 Protocol [RFC5533]' }, - { p: 'wesp', n: 141, d: 'Wrapped Encapsulating Security Payload' }, - { p: 'rohc', n: 142, d: 'Robust Header Compression' } - ] - } -}); -Ext.define('PVE.form.CPUModelSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.CPUModelSelector'], - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + ' (kvm64)'], - ['486', '486'], - ['athlon', 'athlon'], - ['core2duo', 'core2duo'], - ['coreduo', 'coreduo'], - ['kvm32', 'kvm32'], - ['kvm64', 'kvm64'], - ['pentium', 'pentium'], - ['pentium2', 'pentium2'], - ['pentium3', 'pentium3'], - ['phenom', 'phenom'], - ['qemu32', 'qemu32'], - ['qemu64', 'qemu64'], - ['Conroe', 'Conroe'], - ['Penryn', 'Penryn'], - ['Nehalem', 'Nehalem'], - ['Westmere', 'Westmere'], - ['SandyBridge', 'SandyBridge'], - ['IvyBridge', 'IvyBridge'], - ['Haswell', 'Haswell'], - ['Haswell-noTSX','Haswell-noTSX'], - ['Broadwell', 'Broadwell'], - ['Broadwell-noTSX','Broadwell-noTSX'], - ['Skylake-Client','Skylake-Client'], - ['Opteron_G1', 'Opteron_G1'], - ['Opteron_G2', 'Opteron_G2'], - ['Opteron_G3', 'Opteron_G3'], - ['Opteron_G4', 'Opteron_G4'], - ['Opteron_G5', 'Opteron_G5'], - ['EPYC', 'EPYC'], - ['host', 'host'] - - ] -}); -Ext.define('PVE.form.VNCKeyboardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.VNCKeyboardSelector'], - comboItems: PVE.Utils.kvm_keymap_array() -}); -Ext.define('PVE.form.CacheTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.CacheTypeSelector'], - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (" + gettext('No cache') + ")"], - ['directsync', 'Direct sync'], - ['writethrough', 'Write through'], - ['writeback', 'Write back'], - ['unsafe', 'Write back (' + gettext('unsafe') + ')'], - ['none', gettext('No cache')] - ] -}); -Ext.define('PVE.form.SnapshotSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.SnapshotSelector'], - - valueField: 'name', - displayField: 'name', - - loadStore: function(nodename, vmid) { - var me = this; - - if (!nodename) { - return; - } - - me.nodename = nodename; - - if (!vmid) { - return; - } - - me.vmid = vmid; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid +'/snapshot' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.guestType) { - throw "no guest type specified"; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'name'], - filterOnLoad: true - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Snapshot'), - dataIndex: 'name', - hideable: false, - flex: 1 - } - ] - } - }); - - me.callParent(); - - me.loadStore(me.nodename, me.vmid); - } -}); -Ext.define('PVE.form.ContentTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveContentTypeSelector'], - - cts: undefined, - - initComponent: function() { - var me = this; - - me.comboItems = []; - - if (me.cts === undefined) { - me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir', 'snippets']; - } - - Ext.Array.each(me.cts, function(ct) { - me.comboItems.push([ct, PVE.Utils.format_content_types(ct)]); - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.HotplugFeatureSelector', { - extend: 'Ext.form.CheckboxGroup', - alias: 'widget.pveHotplugFeatureSelector', - - columns: 1, - vertical: true, - - defaults: { - name: 'hotplug', - submitValue: false - }, - items: [ - { - boxLabel: gettext('Disk'), - inputValue: 'disk', - checked: true - }, - { - boxLabel: gettext('Network'), - inputValue: 'network', - checked: true - }, - { - boxLabel: 'USB', - inputValue: 'usb', - checked: true - }, - { - boxLabel: gettext('Memory'), - inputValue: 'memory' - }, - { - boxLabel: gettext('CPU'), - inputValue: 'cpu' - } - ], - - setValue: function(value) { - var me = this; - var newVal = []; - if (value === '1') { - newVal = ['disk', 'network', 'usb']; - } else if (value !== '0') { - newVal = value.split(','); - } - me.callParent([{ hotplug: newVal }]); - }, - - // override framework function to - // assemble the hotplug value - getSubmitData: function() { - var me = this, - boxes = me.getBoxes(), - data = []; - Ext.Array.forEach(boxes, function(box){ - if (box.getValue()) { - data.push(box.inputValue); - } - }); - - /* because above is hotplug an array */ - /*jslint confusion: true*/ - if (data.length === 0) { - return { 'hotplug':'0' }; - } else { - return { 'hotplug': data.join(',') }; - } - } - -}); -Ext.define('PVE.form.AgentFeatureSelector', { - extend: 'Proxmox.panel.InputPanel', - alias: ['widget.pveAgentFeatureSelector'], - - initComponent: function() { - var me = this; - me.items= [ - { - xtype: 'proxmoxcheckbox', - boxLabel: gettext('Qemu Agent'), - name: 'enabled', - uncheckedValue: 0, - listeners: { - change: function(f, value, old) { - var gtcb = me.down('proxmoxcheckbox[name=fstrim_cloned_disks]'); - if (value) { - gtcb.setDisabled(false); - } else { - gtcb.setDisabled(true); - } - } - } - }, - { - xtype: 'proxmoxcheckbox', - boxLabel: gettext('Run guest-trim after clone disk'), - name: 'fstrim_cloned_disks', - disabled: true - } - ]; - me.callParent(); - }, - - onGetValues: function(values) { - var agentstr = PVE.Parser.printPropertyString(values, 'enabled'); - return { agent: agentstr }; - }, - - setValues: function(values) { - var agent = values.agent || ''; - var res = PVE.Parser.parsePropertyString(agent, 'enabled'); - this.callParent([res]); - } -}); -Ext.define('PVE.form.iScsiProviderSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveiScsiProviderSelector'], - comboItems: [ - ['comstar', 'Comstar'], - [ 'istgt', 'istgt'], - [ 'iet', 'IET'], - [ 'LIO', 'LIO'] - ] -}); -Ext.define('PVE.form.DayOfWeekSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveDayOfWeekSelector'], - comboItems:[], - initComponent: function(){ - var me = this; - me.comboItems = [ - ['mon', Ext.util.Format.htmlDecode(Ext.Date.dayNames[1])], - ['tue', Ext.util.Format.htmlDecode(Ext.Date.dayNames[2])], - ['wed', Ext.util.Format.htmlDecode(Ext.Date.dayNames[3])], - ['thu', Ext.util.Format.htmlDecode(Ext.Date.dayNames[4])], - ['fri', Ext.util.Format.htmlDecode(Ext.Date.dayNames[5])], - ['sat', Ext.util.Format.htmlDecode(Ext.Date.dayNames[6])], - ['sun', Ext.util.Format.htmlDecode(Ext.Date.dayNames[0])] - ]; - this.callParent(); - } -}); -Ext.define('PVE.form.BackupModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveBackupModeSelector'], - comboItems: [ - ['snapshot', gettext('Snapshot')], - ['suspend', gettext('Suspend')], - ['stop', gettext('Stop')] - ] -}); -Ext.define('PVE.form.ScsiHwSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveScsiHwSelector'], - comboItems: [ - ['__default__', PVE.Utils.render_scsihw('')], - ['lsi', PVE.Utils.render_scsihw('lsi')], - ['lsi53c810', PVE.Utils.render_scsihw('lsi53c810')], - ['megasas', PVE.Utils.render_scsihw('megasas')], - ['virtio-scsi-pci', PVE.Utils.render_scsihw('virtio-scsi-pci')], - ['virtio-scsi-single', PVE.Utils.render_scsihw('virtio-scsi-single')], - ['pvscsi', PVE.Utils.render_scsihw('pvscsi')] - ] -}); -Ext.define('PVE.form.FirewallPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallPolicySelector'], - comboItems: [ - ['ACCEPT', 'ACCEPT'], - ['REJECT', 'REJECT'], - [ 'DROP', 'DROP'] - ] -}); -/* - * This is a global search field - * it loads the /cluster/resources on focus - * and displays the result in a floating grid - * - * it filters and sorts the objects by the algorithm in - * the customFilter function - * - * also it does accept key up/down and enter for input - * and it opens to ctrl+shift+f and ctrl+space - */ -Ext.define('PVE.form.GlobalSearchField', { - extend: 'Ext.form.field.Text', - alias: 'widget.pveGlobalSearchField', - - emptyText: gettext('Search'), - enableKeyEvents: true, - selectOnFocus: true, - padding: '0 5 0 5', - - grid: { - xtype: 'gridpanel', - focusOnToFront: false, - floating: true, - emptyText: Proxmox.Utils.noneText, - width: 600, - height: 400, - scrollable: { - xtype: 'scroller', - y: true, - x:false - }, - store: { - model: 'PVEResources', - proxy:{ - type: 'proxmox', - url: '/api2/extjs/cluster/resources' - } - }, - plugins: { - ptype: 'bufferedrenderer', - trailingBufferZone: 20, - leadingBufferZone: 20 - }, - - hideMe: function() { - var me = this; - if (typeof me.ctxMenu !== 'undefined' && me.ctxMenu.isVisible()) { - return; - } - me.hasFocus = false; - if (!me.textfield.hasFocus) { - me.hide(); - } - }, - - setFocus: function() { - var me = this; - me.hasFocus = true; - }, - - listeners: { - rowclick: function(grid, record) { - var me = this; - me.textfield.selectAndHide(record.id); - }, - itemcontextmenu: function(v, record, item, index, event) { - var me = this; - me.ctxMenu = PVE.Utils.createCmdMenu(v, record, item, index, event); - }, - /* because of lint */ - focusleave: { - fn: 'hideMe' - }, - focusenter: 'setFocus' - }, - - columns: [ - { - text: gettext('Type'), - dataIndex: 'type', - width: 100, - renderer: PVE.Utils.render_resource_type - }, - { - text: gettext('Description'), - flex: 1, - dataIndex: 'text' - }, - { - text: gettext('Node'), - dataIndex: 'node' - }, - { - text: gettext('Pool'), - dataIndex: 'pool' - } - ] - }, - - customFilter: function(item) { - var me = this; - var match = 0; - var fieldArr = []; - var i,j, fields; - - // different types of objects have different fields to search - // for example, a node will never have a pool and vice versa - switch (item.data.type) { - case 'pool': fieldArr = ['type', 'pool', 'text']; break; - case 'node': fieldArr = ['type', 'node', 'text']; break; - case 'storage': fieldArr = ['type', 'pool', 'node', 'storage']; break; - default: fieldArr = ['name', 'type', 'node', 'pool', 'vmid']; - } - if (me.filterVal === '') { - item.data.relevance = 0; - return true; - } - - // all text is case insensitive and each word is - // searched alone - // for every partial match, the row gets - // 1 match point, for every exact match - // it gets 2 points - // - // results gets sorted by points (descending) - fields = me.filterVal.split(/\s+/); - for(i = 0; i < fieldArr.length; i++) { - var v = item.data[fieldArr[i]]; - if (v !== undefined) { - v = v.toString().toLowerCase(); - for(j = 0; j < fields.length; j++) { - if (v.indexOf(fields[j]) !== -1) { - match++; - if(v === fields[j]) { - match++; - } - } - } - } - } - // give the row the 'relevance' value - item.data.relevance = match; - return (match > 0); - }, - - updateFilter: function(field, newValue, oldValue) { - var me = this; - // parse input and filter store, - // show grid - me.grid.store.filterVal = newValue.toLowerCase().trim(); - me.grid.store.clearFilter(true); - me.grid.store.filterBy(me.customFilter); - me.grid.getSelectionModel().select(0); - }, - - selectAndHide: function(id) { - var me = this; - me.tree.selectById(id); - me.grid.hide(); - me.setValue(''); - me.blur(); - }, - - onKey: function(field, e) { - var me = this; - var key = e.getKey(); - - switch(key) { - case Ext.event.Event.ENTER: - // go to first entry if there is one - if (me.grid.store.getCount() > 0) { - me.selectAndHide(me.grid.getSelection()[0].data.id); - } - break; - case Ext.event.Event.UP: - me.grid.getSelectionModel().selectPrevious(); - break; - case Ext.event.Event.DOWN: - me.grid.getSelectionModel().selectNext(); - break; - case Ext.event.Event.ESC: - me.grid.hide(); - me.blur(); - break; - } - }, - - loadValues: function(field) { - var me = this; - var records = []; - - me.hasFocus = true; - me.grid.textfield = me; - me.grid.store.load(); - me.grid.showBy(me, 'tl-bl'); - }, - - hideGrid: function() { - var me = this; - - me.hasFocus = false; - if (!me.grid.hasFocus) { - me.grid.hide(); - } - }, - - listeners: { - change: { - fn: 'updateFilter', - buffer: 250 - }, - specialkey: 'onKey', - focusenter: 'loadValues', - focusleave: { - fn: 'hideGrid', - delay: 100 - } - }, - - toggleFocus: function() { - var me = this; - if (!me.hasFocus) { - me.focus(); - } else { - me.blur(); - } - }, - - initComponent: function() { - var me = this; - - if (!me.tree) { - throw "no tree given"; - } - - me.grid = Ext.create(me.grid); - - me.callParent(); - - /*jslint confusion: true*/ - /*because shift is also a function*/ - // bind ctrl+shift+f and ctrl+space - // to open/close the search - me.keymap = new Ext.KeyMap({ - target: Ext.get(document), - binding: [{ - key:'F', - ctrl: true, - shift: true, - fn: me.toggleFocus, - scope: me - },{ - key:' ', - ctrl: true, - fn: me.toggleFocus, - scope: me - }] - }); - - // always select first item and - // sort by relevance after load - me.mon(me.grid.store, 'load', function() { - me.grid.getSelectionModel().select(0); - me.grid.store.sort({ - property: 'relevance', - direction: 'DESC' - }); - }); - } - -}); -Ext.define('PVE.form.QemuBiosSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveQemuBiosSelector'], - - initComponent: function() { - var me = this; - - me.comboItems = [ - ['__default__', PVE.Utils.render_qemu_bios('')], - ['seabios', PVE.Utils.render_qemu_bios('seabios')], - ['ovmf', PVE.Utils.render_qemu_bios('ovmf')] - ]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -/* filter is a javascript builtin, but extjs calls it also filter */ -Ext.define('PVE.form.VMSelector', { - extend: 'Ext.grid.Panel', - alias: 'widget.vmselector', - - mixins: { - field: 'Ext.form.field.Field' - }, - - allowBlank: true, - selectAll: false, - isFormField: true, - - plugins: 'gridfilters', - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - // due to EXTJS-18711 - // we have to do a static list via a store - // but to avoid creating an object, - // we have to have a pseudo un function - un: function(){} - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - flex: 1, - filter: { - type: 'list' - } - } - ], - - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE' - }, - - checkChangeEvents: [ - 'selectionchange', - 'change' - ], - - listeners: { - selectionchange: function() { - // to trigger validity and error checks - this.checkChange(); - } - }, - - getValue: function() { - var me = this; - var sm = me.getSelectionModel(); - var selection = sm.getSelection(); - var values = []; - var store = me.getStore(); - selection.forEach(function(item) { - // only add if not filtered - if (store.findExact('vmid', item.data.vmid) !== -1) { - values.push(item.data.vmid); - } - }); - return values; - }, - - setValue: function(value) { - console.log(value); - var me = this; - var sm = me.getSelectionModel(); - if (!Ext.isArray(value)) { - value = value.split(','); - } - var selection = []; - var store = me.getStore(); - - value.forEach(function(item) { - var rec = store.findRecord('vmid',item, 0, false, true, true); - console.log(store); - - if (rec) { - console.log(rec); - selection.push(rec); - } - }); - - sm.select(selection); - - return me.mixins.field.setValue.call(me, value); - }, - - getErrors: function(value) { - var me = this; - if (me.allowBlank === false && - me.getSelectionModel().getCount() === 0) { - me.addBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return [gettext('No VM selected')]; - } - - me.removeBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return []; - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.nodename) { - me.store.filters.add({ - property: 'node', - exactMatch: true, - value: me.nodename - }); - } - - // only show the relevant guests by default - if (me.action) { - var statusfilter = ''; - switch (me.action) { - case 'startall': - statusfilter = 'stopped'; - break; - case 'stopall': - statusfilter = 'running'; - break; - } - if (statusfilter !== '') { - me.store.filters.add({ - property: 'template', - value: 0 - },{ - id: 'x-gridfilter-status', - operator: 'in', - property: 'status', - value: [statusfilter] - }); - } - } - - var store = me.getStore(); - var sm = me.getSelectionModel(); - - if (me.selectAll) { - me.mon(store,'load', function(){ - me.getSelectionModel().selectAll(false); - }); - } - } -}); - - -Ext.define('PVE.form.VMComboSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.vmComboSelector', - - valueField: 'vmid', - displayField: 'vmid', - - autoSelect: false, - editable: true, - anyMatch: true, - forceSelection: true, - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - - listConfig: { - width: 600, - plugins: 'gridfilters', - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - hidden: true, - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - un: function(){} // due to EXTJS-18711 - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - hidden: true, - flex: 1, - filter: { - type: 'list' - } - } - ] - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.form.VMCPUFlagSelector', { - extend: 'Ext.grid.Panel', - alias: 'widget.vmcpuflagselector', - - mixins: { - field: 'Ext.form.field.Field' - }, - - disableSelection: true, - columnLines: false, - selectable: false, - hideHeaders: true, - - scrollable: 'y', - height: 200, - - unkownFlags: [], - - store: { - type: 'store', - fields: ['flag', { name: 'state', defaultValue: '=' }, 'desc'], - data: [ - // FIXME: let qemu-server host this and autogenerate or get from API call?? - { flag: 'md-clear', desc: 'Required to let the guest OS know if MDS is mitigated correctly' }, - { flag: 'pcid', desc: 'Meltdown fix cost reduction on Westmere, Sandy-, and IvyBridge Intel CPUs' }, - { flag: 'spec-ctrl', desc: 'Allows improved Spectre mitigation with Intel CPUs' }, - { flag: 'ssbd', desc: 'Protection for "Speculative Store Bypass" for Intel models' }, - { flag: 'ibpb', desc: 'Allows improved Spectre mitigation with AMD CPUs' }, - { flag: 'virt-ssbd', desc: 'Basis for "Speculative Store Bypass" protection for AMD models' }, - { flag: 'amd-ssbd', desc: 'Improves Spectre mitigation performance with AMD CPUs, best used with "virt-ssbd"' }, - { flag: 'amd-no-ssb', desc: 'Notifies guest OS that host is not vulnerable for Spectre on AMD CPUs' }, - { flag: 'pdpe1gb', desc: 'Allow guest OS to use 1GB size pages, if host HW supports it' } - ], - listeners: { - update: function() { - this.commitChanges(); - } - } - }, - - getValue: function() { - var me = this; - var store = me.getStore(); - var flags = ''; - - // ExtJS does not has a nice getAllRecords interface for stores :/ - store.queryBy(Ext.returnTrue).each(function(rec) { - var s = rec.get('state'); - if (s && s !== '=') { - var f = rec.get('flag'); - if (flags === '') { - flags = s + f; - } else { - flags += ';' + s + f; - } - } - }); - - flags += me.unkownFlags.join(';'); - - return flags; - }, - - setValue: function(value) { - var me = this; - var store = me.getStore(); - - me.value = value || ''; - - me.unkownFlags = []; - - me.getStore().queryBy(Ext.returnTrue).each(function(rec) { - rec.set('state', '='); - }); - - var flags = value ? value.split(';') : []; - flags.forEach(function(flag) { - var sign = flag.substr(0, 1); - flag = flag.substr(1); - - var rec = store.findRecord('flag', flag); - if (rec !== null) { - rec.set('state', sign); - } else { - me.unkownFlags.push(flag); - } - }); - store.reload(); - - var res = me.mixins.field.setValue.call(me, value); - - return res; - }, - columns: [ - { - dataIndex: 'state', - renderer: function(v) { - switch(v) { - case '=': return 'Default'; - case '-': return 'Off'; - case '+': return 'On'; - default: return 'Unknown'; - } - }, - width: 65 - }, - { - xtype: 'widgetcolumn', - dataIndex: 'state', - width: 95, - onWidgetAttach: function (column, widget, record) { - var val = record.get('state') || '='; - widget.down('[inputValue=' + val + ']').setValue(true); - // TODO: disable if selected CPU model and flag are incompatible - }, - widget: { - xtype: 'radiogroup', - hideLabel: true, - layout: 'hbox', - validateOnChange: false, - value: '=', - listeners: { - change: function(f, value) { - var v = Object.values(value)[0]; - f.getWidgetRecord().set('state', v); - - var view = this.up('grid'); - view.dirty = view.getValue() !== view.originalValue; - view.checkDirty(); - //view.checkChange(); - } - }, - items: [ - { - boxLabel: '-', - boxLabelAlign: 'before', - inputValue: '-' - }, - { - checked: true, - inputValue: '=' - }, - { - boxLabel: '+', - inputValue: '+' - } - ] - } - }, - { - dataIndex: 'flag', - width: 100 - }, - { - dataIndex: 'desc', - cellWrap: true, - flex: 1 - } - ], - - initComponent: function() { - var me = this; - - // static class store, thus gets not recreated, so ensure defaults are set! - me.getStore().data.forEach(function(v) { - v.state = '='; - }); - - me.value = me.originalValue = ''; - - me.callParent(arguments); - } -}); -Ext.define('PVE.form.USBSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUSBSelector'], - allowBlank: false, - autoSelect: false, - displayField: 'usbid', - valueField: 'usbid', - editable: true, - - getUSBValue: function() { - var me = this; - var rec = me.store.findRecord('usbid', me.value); - var val = 'host='+ me.value; - if (rec && rec.data.speed === "5000") { - val = 'host=' + me.value + ",usb3=1"; - } - return val; - }, - - validator: function(value) { - var me = this; - if (me.type === 'device') { - return (/^[a-f0-9]{4}\:[a-f0-9]{4}$/i).test(value); - } else if (me.type === 'port') { - return (/^[0-9]+\-[0-9]+(\.[0-9]+)*$/).test(value); - } - return false; - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - - if (!nodename) { - throw "no nodename specified"; - } - - if (me.type !== 'device' && me.type !== 'port') { - throw "no valid type specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-usb-' + me.type, - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/scan/usb" - }, - filters: [ - function (item) { - return !!item.data.usbpath && !!item.data.prodid && item.data['class'] != 9; - } - ] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: (me.type === 'device')?gettext('Device'):gettext('Port'), - sortable: true, - dataIndex: 'usbid', - width: 80 - }, - { - header: gettext('Manufacturer'), - sortable: true, - dataIndex: 'manufacturer', - width: 100 - }, - { - header: gettext('Product'), - sortable: true, - dataIndex: 'product', - flex: 1 - }, - { - header: gettext('Speed'), - width: 70, - sortable: true, - dataIndex: 'speed', - renderer: function(value) { - if (value === "5000") { - return "USB 3.0"; - } else if (value === "480") { - return "USB 2.0"; - } else { - return "USB 1.x"; - } - } - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-usb-device', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val, data) { - if (val) { - return val; - } - return data.get('vendid') + ':' + data.get('prodid'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' } - ] - }); - - Ext.define('pve-usb-port', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val,data) { - if (val) { - return val; - } - return data.get('busnum') + '-' + data.get('usbpath'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' } - ] - }); -}); -Ext.define('PVE.form.CalendarEvent', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pveCalendarEvent', - - editable: true, - - valueField: 'value', - displayField: 'text', - queryMode: 'local', - - store: { - field: [ 'value', 'text'], - data: [ - { value: '*/30', text: Ext.String.format(gettext("Every {0} minutes"), 30) }, - { value: '*/2:00', text: gettext("Every two hours")}, - { value: '2,22:30', text: gettext("Every day") + " 02:30, 22:30"}, - { value: 'mon..fri', text: gettext("Monday to Friday") + " 00:00"}, - { value: 'mon..fri */1:00', text: gettext("Monday to Friday") + ': ' + gettext("hourly")}, - { value: 'sun 01:00', text: gettext("Sunday") + " 01:00"} - ] - }, - - tpl: [ - '
    ', - '
  • {text}
  • ', - '
' - ], - - displayTpl: [ - '', - '{value}', - '' - ] - -}); -Ext.define('PVE.form.CephPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephPoolSelector', - - allowBlank: false, - valueField: 'pool_name', - displayField: 'pool_name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/pools' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.form.PermPathSelector', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pvePermPathSelector', - - valueField: 'value', - displayField: 'value', - typeAhead: true, - queryMode: 'local', - store: { - type: 'pvePermPath' - } -}); -/* This class defines the "Tasks" tab of the bottom status panel - * Tasks are jobs with a start, end and log output - */ - -Ext.define('PVE.dc.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterTasks'], - - initComponent : function() { - var me = this; - - var taskstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-tasks', - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/tasks' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: taskstore, - sortAfterUpdate: true, - appendAtStart: true, - sorters: [ - { - property : 'pid', - direction: 'DESC' - }, - { - property : 'starttime', - direction: 'DESC' - } - ] - - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type == "vncproxy" || - record.data.type == "vncshell" || - record.data.type == "spiceproxy") { - metaData.tdCls = "x-grid-row-console"; - } else { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type != "vncproxy") { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return Proxmox.Utils.errorText + ': ' + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - show: taskstore.startUpdate, - destroy: taskstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* This class defines the "Cluster log" tab of the bottom status panel - * A log entry is a timestamp associated with an action on a cluster - */ - -Ext.define('PVE.dc.Log', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterLog'], - - initComponent : function() { - var me = this; - - var logstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-log', - model: 'proxmox-cluster-log', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/log' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: logstore, - appendAtStart: true - }); - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, - - getRowClass: function(record, index) { - var pri = record.get('pri'); - - if (pri && pri <= 3) { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Time"), - dataIndex: 'time', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 150 - }, - { - header: gettext("Service"), - dataIndex: 'tag', - width: 100 - }, - { - header: "PID", - dataIndex: 'pid', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Severity"), - dataIndex: 'pri', - renderer: PVE.Utils.render_serverity, - width: 100 - }, - { - header: gettext("Message"), - dataIndex: 'msg', - flex: 1 - } - ], - listeners: { - activate: logstore.startUpdate, - deactivate: logstore.stopUpdate, - destroy: logstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* - * This class describes the bottom panel - */ -Ext.define('PVE.panel.StatusPanel', { - extend: 'Ext.tab.Panel', - alias: 'widget.pveStatusPanel', - - - //title: "Logs", - //tabPosition: 'bottom', - - initComponent: function() { - var me = this; - - var stateid = 'ltab'; - var sp = Ext.state.Manager.getProvider(); - - var state = sp.get(stateid); - if (state && state.value) { - me.activeTab = state.value; - } - - Ext.apply(me, { - listeners: { - tabchange: function() { - var atab = me.getActiveTab().itemId; - var state = { value: atab }; - sp.set(stateid, state); - } - }, - items: [ - { - itemId: 'tasks', - title: gettext('Tasks'), - xtype: 'pveClusterTasks' - }, - { - itemId: 'clog', - title: gettext('Cluster log'), - xtype: 'pveClusterLog' - } - ] - }); - - me.callParent(); - - me.items.get(0).fireEvent('show', me.items.get(0)); - - var statechange = function(sp, key, state) { - if (key === stateid) { - var atab = me.getActiveTab().itemId; - var ntab = state.value; - if (state && ntab && (atab != ntab)) { - me.setActiveTab(ntab); - } - } - }; - - sp.on('statechange', statechange); - me.on('destroy', function() { - sp.un('statechange', statechange); - }); - - } -}); -Ext.define('PVE.panel.StatusView', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStatusView', - - layout: { - type: 'column' - }, - - title: gettext('Status'), - - getRecordValue: function(key, store) { - if (!key) { - throw "no key given"; - } - var me = this; - - if (store === undefined) { - store = me.getStore(); - } - - var rec = store.getById(key); - if (rec) { - return rec.data.value; - } - - return ''; - }, - - fieldRenderer: function(val,max) { - if (max === undefined) { - return val; - } - - if (!Ext.isNumeric(max) || max === 1) { - return PVE.Utils.render_usage(val); - } - return PVE.Utils.render_size_usage(val,max); - }, - - fieldCalculator: function(used, max) { - if (!Ext.isNumeric(max) && Ext.isNumeric(used)) { - return used; - } else if(!Ext.isNumeric(used)) { - /* we come here if the field is from a node - * where the records are not mem and maxmem - * but mem.used and mem.total - */ - if (used.used !== undefined && - used.total !== undefined) { - return used.used/used.total; - } - } - - return used/max; - }, - - updateField: function(field) { - var me = this; - var text = ''; - var renderer = me.fieldRenderer; - if (Ext.isFunction(field.renderer)) { - renderer = field.renderer; - } - if (field.multiField === true) { - field.updateValue(renderer.call(field, me.getStore().getRecord())); - } else if (field.textField !== undefined) { - field.updateValue(renderer.call(field, me.getRecordValue(field.textField))); - } else if(field.valueField !== undefined) { - var used = me.getRecordValue(field.valueField); - /*jslint confusion: true*/ - /* string and int */ - var max = field.maxField !== undefined ? me.getRecordValue(field.maxField) : 1; - - var calculate = me.fieldCalculator; - - if (Ext.isFunction(field.calculate)) { - calculate = field.calculate; - } - field.updateValue(renderer.call(field, used,max), calculate(used,max)); - } - }, - - getStore: function() { - var me = this; - if (!me.rstore) { - throw "there is no rstore"; - } - - return me.rstore; - }, - - updateTitle: function() { - var me = this; - me.setTitle(me.getRecordValue('name')); - }, - - updateValues: function(store, records, success) { - if (!success) { - return; // do not update if store load was not successful - } - var me = this; - var itemsToUpdate = me.query('pveInfoWidget'); - - itemsToUpdate.forEach(me.updateField, me); - - me.updateTitle(store); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - if (!me.title) { - throw "no title given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.callParent(); - - me.mon(me.rstore, 'load', 'updateValues'); - } - -}); -Ext.define('PVE.panel.GuestStatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveGuestStatusView', - mixins: ['Proxmox.Mixin.CBind'], - - height: 300, - - cbindData: function (initialConfig) { - var me = this; - return { - isQemu: me.pveSelNode.data.type === 'qemu', - isLxc: me.pveSelNode.data.type === 'lxc' - }; - }, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'status', - title: gettext('Status'), - iconCls: 'fa fa-info fa-fw', - printBar: false, - multiField: true, - renderer: function(record) { - var me = this; - var text = record.data.status; - var qmpstatus = record.data.qmpstatus; - if (qmpstatus && qmpstatus !== record.data.status) { - text += ' (' + qmpstatus + ')'; - } - return text; - } - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - xtype: 'pveInfoWidget', - itemId: 'node', - iconCls: 'fa fa-building fa-fw', - title: gettext('Node'), - cbind: { - text: '{pveSelNode.data.node}' - }, - printBar: false - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpus', - renderer: PVE.Utils.render_cpu_usage, - // in this specific api call - // we already have the correct value for the usage - calculate: Ext.identityFn - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory usage'), - valueField: 'mem', - maxField: 'maxmem' - }, - { - itemId: 'swap', - xtype: 'pveInfoWidget', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'maxswap', - cbind: { - hidden: '{isQemu}', - disabled: '{isQemu}' - } - }, - { - itemId: 'rootfs', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - valueField: 'disk', - maxField: 'maxdisk', - printBar: false, - renderer: function(used, max) { - var me = this; - me.setPrintBar(used > 0); - if (used === 0) { - return PVE.Utils.render_size(max); - } else { - return PVE.Utils.render_size_usage(used,max); - } - } - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'ips', - xtype: 'pveAgentIPView', - cbind: { - rstore: '{rstore}', - pveSelNode: '{pveSelNode}', - hidden: '{isLxc}', - disabled: '{isLxc}' - } - } - ], - - updateTitle: function() { - var me = this; - var uptime = me.getRecordValue('uptime'); - - var text = ""; - if (Number(uptime) > 0) { - text = " (" + gettext('Uptime') + ': ' + Proxmox.Utils.format_duration_long(uptime) - + ')'; - } - - me.setTitle(me.getRecordValue('name') + text); - } -}); -/* - * This is a running chart widget - * you add time datapoints to it, - * and we only show the last x of it - * used for ceph performance charts - */ -Ext.define('PVE.widget.RunningChart', { - extend: 'Ext.container.Container', - alias: 'widget.pveRunningChart', - - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - width: 80, - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}:

' - }, - { - flex: 1, - xtype: 'cartesian', - height: '100%', - itemId: 'chart', - border: false, - axes: [ - { - type: 'numeric', - position: 'left', - hidden: true, - minimum: 0 - }, - { - type: 'numeric', - position: 'bottom', - hidden: true - } - ], - - store: { - data: {} - }, - - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '0 B/s', - textAlign: 'end', - textBaseline: 'middle', - fontSize: 14 - }], - - series: [{ - type: 'line', - xField: 'time', - yField: 'val', - fill: 'true', - colors: ['#cfcfcf'], - tooltip: { - trackMouse: true, - renderer: function( tooltip, record, ctx) { - var me = this.getChart(); - var date = new Date(record.data.time); - var value = me.up().renderer(record.data.val); - tooltip.setHtml( - me.up().title + ': ' + value + '
' + - Ext.Date.format(date, 'H:i:s') - ); - } - }, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - } - }] - } - ], - - // the renderer for the tooltip and last value, - // default just the value - renderer: Ext.identityFn, - - // show the last x seconds - // default is 5 minutes - timeFrame: 5*60, - - addDataPoint: function(value, time) { - var me = this.chart; - var panel = me.up(); - var now = new Date(); - var begin = new Date(now.getTime() - (1000*panel.timeFrame)); - - me.store.add({ - time: time || now.getTime(), - val: value || 0 - }); - - // delete all old records when we have 20 times more datapoints - // than seconds in our timeframe (so even a subsecond graph does - // not trigger this often) - // - // records in the store do not take much space, but like this, - // we prevent a memory leak when someone has the site open for a long time - // with minimal graphical glitches - if (me.store.count() > panel.timeFrame * 20) { - var oldData = me.store.getData().createFiltered(function(item) { - return item.data.time < begin.getTime(); - }); - - me.store.remove(oldData.getRange()); - } - - me.timeaxis.setMinimum(begin.getTime()); - me.timeaxis.setMaximum(now.getTime()); - me.valuesprite.setText(panel.renderer(value || 0).toString()); - me.valuesprite.setAttributes({ - x: me.getWidth() - 15, - y: me.getHeight()/2 - }, true); - me.redraw(); - }, - - setTitle: function(title) { - this.title = title; - var me = this.getComponent('title'); - me.update({title: title}); - }, - - initComponent: function(){ - var me = this; - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.chart = me.getComponent('chart'); - me.chart.timeaxis = me.chart.getAxes()[1]; - me.chart.valuesprite = me.chart.getSurface('chart').get('valueSprite'); - if (me.color) { - me.chart.series[0].setStyle({ - fill: me.color, - stroke: me.color - }); - } - } -}); -Ext.define('PVE.widget.Info',{ - extend: 'Ext.container.Container', - alias: 'widget.pveInfoWidget', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - value: 0, - maximum: 1, - printBar: true, - items: [ - { - xtype: 'component', - itemId: 'label', - data: { - title: '', - usage: '', - iconCls: undefined - }, - tpl: [ - '
', - '', - ' ', - '', - '{title}
 
{usage}
' - ] - }, - { - height: 2, - border: 0 - }, - { - xtype: 'progressbar', - itemId: 'progress', - height: 5, - value: 0, - animate: true - } - ], - - warningThreshold: 0.6, - criticalThreshold: 0.9, - - setPrintBar: function(enable) { - var me = this; - me.printBar = enable; - me.getComponent('progress').setVisible(enable); - }, - - setIconCls: function(iconCls) { - var me = this; - me.getComponent('label').data.iconCls = iconCls; - }, - - updateValue: function(text, usage) { - var me = this; - var label = me.getComponent('label'); - label.update(Ext.apply(label.data, {title: me.title, usage:text})); - - if (usage !== undefined && - me.printBar && - Ext.isNumeric(usage) && - usage >= 0) { - var progressBar = me.getComponent('progress'); - progressBar.updateProgress(usage, ''); - if (usage > me.criticalThreshold) { - progressBar.removeCls('warning'); - progressBar.addCls('critical'); - } else if (usage > me.warningThreshold) { - progressBar.removeCls('critical'); - progressBar.addCls('warning'); - } else { - progressBar.removeCls('warning'); - progressBar.removeCls('critical'); - } - } - }, - - initComponent: function() { - var me = this; - - if (!me.title) { - throw "no title defined"; - } - - me.callParent(); - - me.getComponent('progress').setVisible(me.printBar); - - me.updateValue(me.text, me.value); - me.setIconCls(me.iconCls); - } - -}); -Ext.define('PVE.panel.TemplateStatusView',{ - extend: 'PVE.panel.StatusView', - alias: 'widget.pveTemplateStatusView', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - printBar: false, - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - itemId: 'node', - iconCls: 'fa fa-fw fa-building', - title: gettext('Node') - }, - { - xtype: 'box', - height: 20 - }, - { - itemId: 'cpus', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('Processors'), - textField: 'cpus' - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory'), - textField: 'maxmem', - renderer: PVE.Utils.render_size - }, - { - itemId: 'swap', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('Swap'), - textField: 'maxswap', - renderer: PVE.Utils.render_size - }, - { - itemId: 'disk', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - textField: 'maxdisk', - renderer: PVE.Utils.render_size - }, - { - xtype: 'box', - height: 20 - } - ], - - initComponent: function() { - var me = this; - - var name = me.pveSelNode.data.name; - if (!name) { - throw "no name specified"; - } - - me.title = name; - - me.callParent(); - if (me.pveSelNode.data.type !== 'lxc') { - me.remove(me.getComponent('swap')); - } - me.getComponent('node').updateValue(me.pveSelNode.data.node); - } -}); -Ext.define('PVE.widget.HealthWidget', { - extend: 'Ext.Component', - alias: 'widget.pveHealthWidget', - - data: { - iconCls: PVE.Utils.get_health_icon(undefined, true), - text: '', - title: '' - }, - - style: { - 'text-align':'center' - }, - - tpl: [ - '

{title}

', - '', - '

', - '{text}' - ], - - updateHealth: function(data) { - var me = this; - me.update(Ext.apply(me.data, data)); - }, - - initComponent: function(){ - var me = this; - - if (me.title) { - me.config.data.title = me.title; - } - - me.callParent(); - } - -}); -/*global u2f*/ -Ext.define('PVE.window.LoginWindow', { - extend: 'Ext.window.Window', - - controller: { - - xclass: 'Ext.app.ViewController', - - onLogon: function() { - var me = this; - - var form = this.lookupReference('loginForm'); - var unField = this.lookupReference('usernameField'); - var saveunField = this.lookupReference('saveunField'); - var view = this.getView(); - - if (!form.isValid()) { - return; - } - - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - - // set or clear username - var sp = Ext.state.Manager.getProvider(); - if (saveunField.getValue() === true) { - sp.set(unField.getStateId(), unField.getValue()); - } else { - sp.clear(unField.getStateId()); - } - sp.set(saveunField.getStateId(), saveunField.getValue()); - - form.submit({ - failure: function(f, resp){ - me.failure(resp); - }, - success: function(f, resp){ - view.el.unmask(); - - var data = resp.result.data; - if (Ext.isDefined(data.NeedTFA)) { - // Store first factor login information first: - data.LoggedOut = true; - Proxmox.Utils.setAuthData(data); - - if (Ext.isDefined(data.U2FChallenge)) { - me.perform_u2f(data); - } else { - me.perform_otp(); - } - } else { - me.success(data); - } - } - }); - - }, - failure: function(resp) { - var me = this; - var view = me.getView(); - view.el.unmask(); - var handler = function() { - var uf = me.lookupReference('usernameField'); - uf.focus(true, true); - }; - - Ext.MessageBox.alert(gettext('Error'), - gettext("Login failed. Please try again"), - handler); - }, - success: function(data) { - var me = this; - var view = me.getView(); - var handler = view.handler || Ext.emptyFn; - handler.call(me, data); - view.close(); - }, - - perform_otp: function() { - var me = this; - var win = Ext.create('PVE.window.TFALoginWindow', { - onLogin: function(value) { - me.finish_tfa(value); - }, - onCancel: function() { - Proxmox.LoggedOut = false; - Proxmox.Utils.authClear(); - me.getView().show(); - } - }); - win.show(); - }, - - perform_u2f: function(data) { - var me = this; - // Show the message: - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Verification'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - var chlg = data.U2FChallenge; - var key = { - version: chlg.version, - keyHandle: chlg.keyHandle - }; - u2f.sign(chlg.appId, chlg.challenge, [key], function(res) { - msg.close(); - if (res.errorCode) { - Proxmox.Utils.authClear(); - Ext.Msg.alert(gettext('Error'), PVE.Utils.render_u2f_error(res.errorCode)); - return; - } - delete res.errorCode; - me.finish_tfa(JSON.stringify(res)); - }); - }, - finish_tfa: function(res) { - var me = this; - var view = me.getView(); - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - var params = { response: res }; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'POST', - timeout: 5000, // it'll delay both success & failure - success: function(resp, opts) { - view.el.unmask(); - // Fill in what we copy over from the 1st factor: - var data = resp.result.data; - data.CSRFPreventionToken = Proxmox.CSRFPreventionToken; - data.username = Proxmox.UserName; - // Finish logging in: - me.success(data); - }, - failure: function(resp, opts) { - Proxmox.Utils.authClear(); - me.failure(resp); - } - }); - }, - - control: { - 'field[name=username]': { - specialkey: function(f, e) { - if (e.getKey() === e.ENTER) { - var pf = this.lookupReference('passwordField'); - if (!pf.getValue()) { - pf.focus(false); - } - } - } - }, - 'field[name=lang]': { - change: function(f, value) { - var dt = Ext.Date.add(new Date(), Ext.Date.YEAR, 10); - Ext.util.Cookies.set('PVELangCookie', value, dt); - this.getView().mask(gettext('Please wait...'), 'x-mask-loading'); - window.location.reload(); - } - }, - 'button[reference=loginButton]': { - click: 'onLogon' - }, - '#': { - show: function() { - var sp = Ext.state.Manager.getProvider(); - var checkboxField = this.lookupReference('saveunField'); - var unField = this.lookupReference('usernameField'); - - var checked = sp.get(checkboxField.getStateId()); - checkboxField.setValue(checked); - - if(checked === true) { - var username = sp.get(unField.getStateId()); - unField.setValue(username); - var pwField = this.lookupReference('passwordField'); - pwField.focus(); - } - } - } - } - }, - - width: 400, - - modal: true, - - border: false, - - draggable: true, - - closable: false, - - resizable: false, - - layout: 'auto', - - title: gettext('Proxmox VE Login'), - - defaultFocus: 'usernameField', - - defaultButton: 'loginButton', - - items: [{ - xtype: 'form', - layout: 'form', - url: '/api2/extjs/access/ticket', - reference: 'loginForm', - - fieldDefaults: { - labelAlign: 'right', - allowBlank: false - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('User name'), - name: 'username', - itemId: 'usernameField', - reference: 'usernameField', - stateId: 'login-username' - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - name: 'password', - reference: 'passwordField' - }, - { - xtype: 'pveRealmComboBox', - name: 'realm' - }, - { - xtype: 'proxmoxLanguageSelector', - fieldLabel: gettext('Language'), - value: Ext.util.Cookies.get('PVELangCookie') || Proxmox.defaultLang || 'en', - name: 'lang', - reference: 'langField', - submitValue: false - } - ], - buttons: [ - { - xtype: 'checkbox', - fieldLabel: gettext('Save User name'), - name: 'saveusername', - reference: 'saveunField', - stateId: 'login-saveusername', - labelWidth: 'auto', - labelAlign: 'right', - submitValue: false - }, - { - text: gettext('Login'), - reference: 'loginButton' - } - ] - }] - }); -Ext.define('PVE.window.TFALoginWindow', { - extend: 'Ext.window.Window', - - modal: true, - resizable: false, - title: 'Two-Factor Authentication', - layout: 'form', - defaultButton: 'loginButton', - defaultFocus: 'otpField', - - controller: { - xclass: 'Ext.app.ViewController', - login: function() { - var me = this; - var view = me.getView(); - view.onLogin(me.lookup('otpField').getValue()); - view.close(); - }, - cancel: function() { - var me = this; - var view = me.getView(); - view.onCancel(); - view.close(); - } - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Please enter your OTP verification code:'), - name: 'otp', - itemId: 'otpField', - reference: 'otpField', - allowBlank: false - } - ], - - buttons: [ - { - text: gettext('Login'), - reference: 'loginButton', - handler: 'login' - }, - { - text: gettext('Cancel'), - handler: 'cancel' - } - ] -}); -Ext.define('PVE.window.Wizard', { - extend: 'Ext.window.Window', - - activeTitle: '', // used for automated testing - - width: 700, - height: 510, - - modal: true, - border: false, - - draggable: true, - closable: true, - resizable: false, - - layout: 'border', - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.down('form').getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - initComponent: function() { - var me = this; - - var tabs = me.items || []; - delete me.items; - - /* - * Items may have the following functions: - * validator(): per tab custom validation - * onSubmit(): submit handler - * onGetValues(): overwrite getValues results - */ - - Ext.Array.each(tabs, function(tab) { - tab.disabled = true; - }); - tabs[0].disabled = false; - - var maxidx = 0; - var curidx = 0; - - var check_card = function(card) { - var valid = true; - var fields = card.query('field, fieldcontainer'); - if (card.isXType('fieldcontainer')) { - fields.unshift(card); - } - Ext.Array.each(fields, function(field) { - // Note: not all fielcontainer have isValid() - if (Ext.isFunction(field.isValid) && !field.isValid()) { - valid = false; - } - }); - - if (Ext.isFunction(card.validator)) { - return card.validator(); - } - - return valid; - }; - - var disable_at = function(card) { - var tp = me.down('#wizcontent'); - var idx = tp.items.indexOf(card); - for(;idx < tp.items.getCount();idx++) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }; - - var tabchange = function(tp, newcard, oldcard) { - if (newcard.onSubmit) { - me.down('#next').setVisible(false); - me.down('#submit').setVisible(true); - } else { - me.down('#next').setVisible(true); - me.down('#submit').setVisible(false); - } - var valid = check_card(newcard); - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - me.down('#back').setDisabled(tp.items.indexOf(newcard) == 0); - - var idx = tp.items.indexOf(newcard); - if (idx > maxidx) { - maxidx = idx; - } - curidx = idx; - - var next = idx + 1; - var ntab = tp.items.getAt(next); - if (valid && ntab && !newcard.onSubmit) { - ntab.enable(); - } - }; - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, true, false); - } - - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - - Ext.apply(me, { - items: [ - { - xtype: 'form', - region: 'center', - layout: 'fit', - border: false, - margins: '5 5 0 5', - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [{ - itemId: 'wizcontent', - xtype: 'tabpanel', - activeItem: 0, - bodyPadding: 10, - listeners: { - afterrender: function(tp) { - var atab = this.getActiveTab(); - tabchange(tp, atab); - }, - tabchange: function(tp, newcard, oldcard) { - tabchange(tp, newcard, oldcard); - } - }, - items: tabs - }] - } - ], - fbar: [ - { - xtype: 'proxmoxHelpButton', - itemId: 'help' - }, - '->', - { - xtype: 'proxmoxcheckbox', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - value: advchecked, - listeners: { - change: function(cb, val) { - var tp = me.down('#wizcontent'); - tp.query('inputpanel').forEach(function(ip) { - ip.setAdvancedVisible(val); - }); - - sp.set('proxmox-advanced-cb', val); - } - } - }, - { - text: gettext('Back'), - disabled: true, - itemId: 'back', - minWidth: 60, - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - var prev = tp.items.indexOf(atab) - 1; - if (prev < 0) { - return; - } - var ntab = tp.items.getAt(prev); - if (ntab) { - tp.setActiveTab(ntab); - } - } - }, - { - text: gettext('Next'), - disabled: true, - itemId: 'next', - minWidth: 60, - handler: function() { - - var form = me.down('form').getForm(); - - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - if (!check_card(atab)) { - return; - } - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - - } - }, - { - text: gettext('Finish'), - minWidth: 60, - hidden: true, - itemId: 'submit', - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - atab.onSubmit(); - } - } - ] - }); - me.callParent(); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setAdvancedVisible(advchecked); - }); - - Ext.Array.each(me.query('field'), function(field) { - var validcheck = function() { - var tp = me.down('#wizcontent'); - - // check tabs from current to the last enabled for validity - // since we might have changed a validity on a later one - var i; - for (i = curidx; i <= maxidx && i < tp.items.getCount(); i++) { - var tab = tp.items.getAt(i); - var valid = check_card(tab); - - // only set the buttons on the current panel - if (i === curidx) { - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - } - - // if a panel is invalid, then disable it and all following, - // else enable it and go to the next - var ntab = tp.items.getAt(i + 1); - if (!valid) { - disable_at(ntab); - return; - } else if (ntab && !tab.onSubmit) { - ntab.enable(); - } - } - }; - field.on('change', validcheck); - field.on('validitychange', validcheck); - }); - } -}); -Ext.define('PVE.window.NotesEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - title: gettext('Notes'), - width: 600, - height: '400px', - resizable: true, - layout: 'fit', - defaultButton: undefined, - items: { - xtype: 'textarea', - name: 'description', - height: '100%', - value: '', - hideLabel: true - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.window.Backup', { - extend: 'Ext.window.Window', - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.vmtype) { - throw "no VM type specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: me.storage, - fieldLabel: gettext('Storage'), - storageContent: 'backup', - allowBlank: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - storagesel, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'pveCompressionSelector', - name: 'compress', - value: 'lzo', - fieldLabel: gettext('Compression') - }, - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto', - emptyText: Proxmox.Utils.noneText - } - ] - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Backup'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - var params = { - storage: storage, - vmid: me.vmid, - mode: values.mode, - remove: 0 - }; - - if ( values.mailto ) { - params.mailto = values.mailto; - } - - if (values.compress) { - params.compress = values.compress; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/vzdump', - params: params, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - }, - success: function(response, options) { - // close later so we reload the grid - // after the task has completed - me.hide(); - - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - close: function() { - me.close(); - } - } - }); - win.show(); - } - }); - } - }); - - var helpBtn = Ext.create('Proxmox.button.Help', { - onlineHelp: 'chapter_vzdump', - listenToGlobalEvent: false, - hidden: false - }); - - var title = gettext('Backup') + " " + - ((me.vmtype === 'lxc') ? "CT" : "VM") + - " " + me.vmid; - - Ext.apply(me, { - title: title, - width: 350, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ helpBtn, '->', submitBtn ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.window.Restore', { - extend: 'Ext.window.Window', // fixme: Proxmox.window.Edit? - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.volid) { - throw "no volume ID specified"; - } - - if (!me.vmtype) { - throw "no vmtype specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: '', - fieldLabel: gettext('Storage'), - storageContent: (me.vmtype === 'lxc') ? 'rootdir' : 'images', - allowBlank: true - }); - - var IDfield; - if (me.vmid) { - IDfield = Ext.create('Ext.form.field.Display', { - name: 'vmid', - value: me.vmid, - fieldLabel: (me.vmtype === 'lxc') ? 'CT' : 'VM' - }); - } else { - IDfield = Ext.create('PVE.form.GuestIDSelector', { - name: 'vmid', - guestType: me.vmtype, - loadNextFreeID: true, - validateExists: false - }); - } - - var items = [ - { - xtype: 'displayfield', - value: me.volidText || me.volid, - fieldLabel: gettext('Source') - }, - storagesel, - IDfield, - { - xtype: 'proxmoxintegerfield', - name: 'bwlimit', - fieldLabel: gettext('Read Limit (MiB/s)'), - minValue: 0, - emptyText: gettext('Defaults to target storage restore limit'), - autoEl: { - tag: 'div', - 'data-qtip': gettext("Use '0' to disable all bandwidth limits.") - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'unique', - fieldLabel: gettext('Unique'), - hidden: !!me.vmid, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses') - }, - checked: false - } - ]; - - /*jslint confusion: true*/ - if (me.vmtype === 'lxc') { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - fieldLabel: gettext('Unprivileged container') - }); - } - /*jslint confusion: false*/ - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var doRestore = function(url, params) { - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - waitMsgTarget: me, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.close(); - } - }); - }; - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Restore'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - - var params = { - storage: storage, - vmid: me.vmid || values.vmid, - force: me.vmid ? 1 : 0 - }; - if (values.unique) { params.unique = 1; } - - if (values.bwlimit !== undefined) { - params.bwlimit = values.bwlimit * 1024; - } - - var url; - var msg; - if (me.vmtype === 'lxc') { - url = '/nodes/' + me.nodename + '/lxc'; - params.ostemplate = me.volid; - params.restore = 1; - if (values.unprivileged) { params.unprivileged = 1; } - msg = Proxmox.Utils.format_task_description('vzrestore', params.vmid); - } else if (me.vmtype === 'qemu') { - url = '/nodes/' + me.nodename + '/qemu'; - params.archive = me.volid; - msg = Proxmox.Utils.format_task_description('qmrestore', params.vmid); - } else { - throw 'unknown VM type'; - } - - if (me.vmid) { - msg += '. ' + gettext('This will permanently erase current VM data.'); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - doRestore(url, params); - }); - } else { - doRestore(url, params); - } - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - var title = gettext('Restore') + ": " + ( - (me.vmtype === 'lxc') ? 'CT' : 'VM'); - - if (me.vmid) { - title += " " + me.vmid; - } - - Ext.apply(me, { - title: title, - width: 500, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); -/* Popup a message window - * where the user has to manually enter the resource ID - * to enable the destroy button - */ -Ext.define('PVE.window.SafeDestroy', { - extend: 'Ext.window.Window', - alias: 'widget.pveSafeDestroy', - - title: gettext('Confirm'), - modal: true, - buttonAlign: 'center', - bodyPadding: 10, - width: 450, - layout: { type:'hbox' }, - defaultFocus: 'confirmField', - showProgress: false, - - config: { - item: { - id: undefined, - type: undefined - }, - url: undefined, - params: {} - }, - - getParams: function() { - var me = this; - if (Ext.Object.isEmpty(me.params)) { - return ''; - } - return '?' + Ext.Object.toQueryString(me.params); - }, - - controller: { - - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=confirm]': { - change: function(f, value) { - var view = this.getView(); - var removeButton = this.lookupReference('removeButton'); - if (value === view.getItem().id.toString()) { - removeButton.enable(); - } else { - removeButton.disable(); - } - }, - specialkey: function (field, event) { - var removeButton = this.lookupReference('removeButton'); - if (!removeButton.isDisabled() && event.getKey() == event.ENTER) { - removeButton.fireEvent('click', removeButton, event); - } - } - }, - 'button[reference=removeButton]': { - click: function() { - var view = this.getView(); - Proxmox.Utils.API2Request({ - url: view.getUrl() + view.getParams(), - method: 'DELETE', - waitMsgTarget: view, - failure: function(response, opts) { - view.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = view.showProgress && - response.result.data ? true : false; - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - view.hide(); - - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: function () { - view.close(); - } - } - }); - win.show(); - } else { - view.close(); - } - } - }); - } - } - } - }, - - items: [ - { - xtype: 'component', - cls: [ Ext.baseCSSPrefix + 'message-box-icon', - Ext.baseCSSPrefix + 'message-box-warning', - Ext.baseCSSPrefix + 'dlg-icon'] - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'component', - reference: 'messageCmp' - }, - { - itemId: 'confirmField', - reference: 'confirmField', - xtype: 'textfield', - name: 'confirm', - labelWidth: 300, - hideTrigger: true, - allowBlank: false - } - ] - } - ], - buttons: [ - { - reference: 'removeButton', - text: gettext('Remove'), - disabled: true - } - ], - - initComponent : function() { - var me = this; - - me.callParent(); - - var item = me.getItem(); - - if (!Ext.isDefined(item.id)) { - throw "no ID specified"; - } - - if (!Ext.isDefined(item.type)) { - throw "no VM type specified"; - } - - var messageCmp = me.lookupReference('messageCmp'); - var msg; - - if (item.type === 'VM') { - msg = Proxmox.Utils.format_task_description('qmdestroy', item.id); - } else if (item.type === 'CT') { - msg = Proxmox.Utils.format_task_description('vzdestroy', item.id); - } else if (item.type === 'CephPool') { - msg = Proxmox.Utils.format_task_description('cephdestroypool', item.id); - } else if (item.type === 'Image') { - msg = Proxmox.Utils.format_task_description('unknownimgdel', item.id); - } else { - throw "unknown item type specified"; - } - - messageCmp.setHtml(msg); - - var confirmField = me.lookupReference('confirmField'); - msg = gettext('Please enter the ID to confirm') + - ' (' + item.id + ')'; - confirmField.setFieldLabel(msg); - } -}); -Ext.define('PVE.window.BackupConfig', { - extend: 'Ext.window.Window', - title: gettext('Configuration'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: { - xtype: 'component', - itemId: 'configtext', - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }, - - initComponent: function() { - var me = this; - - if (!me.volume) { - throw "no volume specified"; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.callParent(); - - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/vzdump/extractconfig", - method: 'GET', - params: { - volume: me.volume - }, - failure: function(response, opts) { - me.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response,options) { - me.show(); - me.down('#configtext').update(Ext.htmlEncode(response.result.data)); - } - }); - } -}); -Ext.define('PVE.window.Settings', { - extend: 'Ext.window.Window', - - width: '800px', - title: gettext('My Settings'), - iconCls: 'fa fa-gear', - modal: true, - bodyPadding: 10, - resizable: false, - - buttons: [ - { - xtype: 'proxmoxHelpButton', - onlineHelp: 'gui_my_settings', - hidden: false - }, - '->', - { - text: gettext('Close'), - handler: function() { - this.up('window').close(); - } - } - ], - - layout: { - type: 'hbox', - align: 'top' - }, - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - var username = sp.get('login-username') || Proxmox.Utils.noneText; - me.lookupReference('savedUserName').setValue(username); - - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var val = localStorage.getItem('pve-xterm-' + setting); - if (val !== undefined && val !== null) { - var field = me.lookup(setting); - field.setValue(val); - field.resetOriginalValue(); - } - }); - }, - - set_button_status: function() { - var me = this; - - var form = me.lookup('xtermform'); - var valid = form.isValid(); - var dirty = form.isDirty(); - - var hasvalues = false; - var values = form.getValues(); - Ext.Object.eachValue(values, function(value) { - if (value) { - hasvalues = true; - return false; - } - }); - - me.lookup('xtermsave').setDisabled(!dirty || !valid); - me.lookup('xtermreset').setDisabled(!hasvalues); - }, - - control: { - '#xtermjs form': { - dirtychange: 'set_button_status', - validitychange: 'set_button_status' - }, - '#xtermjs button': { - click: function(button) { - var me = this; - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var field = me.lookup(setting); - if (button.reference === 'xtermsave') { - var value = field.getValue(); - if (value) { - localStorage.setItem('pve-xterm-' + setting, value); - } else { - localStorage.removeItem('pve-xterm-' + setting); - } - } else if (button.reference === 'xtermreset') { - field.setValue(undefined); - localStorage.removeItem('pve-xterm-' + setting); - } - field.resetOriginalValue(); - }); - me.set_button_status(); - } - }, - 'button[name=reset]': { - click: function () { - var blacklist = ['GuiCap', 'login-username', 'dash-storages']; - var sp = Ext.state.Manager.getProvider(); - var state; - for (state in sp.state) { - if (sp.state.hasOwnProperty(state)) { - if (blacklist.indexOf(state) !== -1) { - continue; - } - - sp.clear(state); - } - } - - window.location.reload(); - } - }, - 'button[name=clear-username]': { - click: function () { - var me = this; - var usernamefield = me.lookupReference('savedUserName'); - var sp = Ext.state.Manager.getProvider(); - - usernamefield.setValue(Proxmox.Utils.noneText); - sp.clear('login-username'); - } - }, - 'grid[reference=dashboard-storages]': { - selectionchange: function(grid, selected) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - // saves the selected storageids as - // "id1,id2,id3,..." - // or clears the variable - if (selected.length > 0) { - sp.set('dash-storages', - Ext.Array.pluck(selected, 'id').join(',')); - } else { - sp.clear('dash-storages'); - } - }, - afterrender: function(grid) { - var me = grid; - var sp = Ext.state.Manager.getProvider(); - var store = me.getStore(); - var items = []; - me.suspendEvent('selectionchange'); - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - // we have to get the records - // to be able to select them - if (storage !== '') { - var item = store.getById(storage); - if (item) { - items.push(item); - } - } - }); - me.getSelectionModel().select(items); - me.resumeEvent('selectionchange'); - } - } - } - }, - - items: [{ - xtype: 'fieldset', - width: '50%', - title: gettext('Webinterface Settings'), - margin: '5', - layout: { - type: 'vbox', - align: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Dashboard Storages'), - labelAlign: 'left', - labelWidth: '50%' - }, - { - xtype: 'grid', - maxHeight: 150, - reference: 'dashboard-storages', - selModel: { - selType: 'checkboxmodel' - }, - columns: [{ - header: gettext('Name'), - dataIndex: 'storage', - flex: 1 - },{ - header: gettext('Node'), - dataIndex: 'node', - flex: 1 - }], - store: { - type: 'diff', - field: ['type', 'storage', 'id', 'node'], - rstore: PVE.data.ResourceStore, - filters: [{ - property: 'type', - value: 'storage' - }], - sorters: [ 'node','storage'] - } - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Saved User name'), - labelAlign: 'left', - labelWidth: '50%', - stateId: 'login-username', - reference: 'savedUserName', - value: '' - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Clear User name'), - width: 'auto', - name: 'clear-username' - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Layout'), - labelAlign: 'left', - labelWidth: '50%' - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Reset Layout'), - width: 'auto', - name: 'reset' - } - ] - },{ - xtype: 'fieldset', - itemId: 'xtermjs', - width: '50%', - margin: '5', - title: gettext('xterm.js Settings'), - items: [{ - xtype: 'form', - reference: 'xtermform', - border: false, - layout: { - type: 'vbox', - algin: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0' - }, - items: [ - { - xtype: 'textfield', - name: 'fontFamily', - reference: 'fontFamily', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Font-Family') - }, - { - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - name: 'fontSize', - reference: 'fontSize', - minValue: 1, - fieldLabel: gettext('Font-Size') - }, - { - xtype: 'numberfield', - name: 'letterSpacing', - reference: 'letterSpacing', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Letter Spacing') - }, - { - xtype: 'numberfield', - name: 'lineHeight', - minValue: 0.1, - reference: 'lineHeight', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Line Height') - }, - { - xtype: 'container', - layout: { - type: 'hbox', - pack: 'end' - }, - items: [ - { - xtype: 'button', - reference: 'xtermreset', - disabled: true, - text: gettext('Reset') - }, - { - xtype: 'button', - reference: 'xtermsave', - disabled: true, - text: gettext('Save') - } - ] - } - ] - }] - }], - - onShow: function() { - var me = this; - me.callParent(); - } -}); -Ext.define('PVE.panel.StartupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'qm_startup_and_shutdown', - - onGetValues: function(values) { - var me = this; - - var res = PVE.Parser.printStartup(values); - - if (res === undefined || res === '') { - return { 'delete': 'startup' }; - } - - return { startup: res }; - }, - - setStartup: function(value) { - var me = this; - - var startup = PVE.Parser.parseStartup(value); - if (startup) { - me.setValues(startup); - } - }, - - initComponent : function() { - var me = this; - - me.items = [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Shutdown timeout') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.window.StartupEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveWindowStartupEdit', - onlineHelp: undefined, - - initComponent : function() { - - var me = this; - var ipanelConfig = me.onlineHelp ? {onlineHelp: me.onlineHelp} : {}; - var ipanel = Ext.create('PVE.panel.StartupInputPanel', ipanelConfig); - - Ext.applyIf(me, { - subject: gettext('Start/Shutdown order'), - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - ipanel.setStartup(me.vmconfig.startup); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.Install', { - extend: 'Ext.window.Window', - xtype: 'pveCephInstallWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 220, - header: false, - resizable: false, - draggable: false, - modal: true, - nodename: undefined, - shadow: false, - border: false, - bodyBorder: false, - closable: false, - cls: 'install-mask', - bodyCls: 'install-mask', - layout: { - align: 'stretch', - pack: 'center', - type: 'vbox' - }, - viewModel: { - data: { - cephVersion: 'nautilus', - isInstalled: false - }, - formulas: { - buttonText: function (get){ - if (get('isInstalled')) { - return gettext('Configure Ceph'); - } else { - return gettext('Install Ceph-') + get('cephVersion'); - } - }, - windowText: function (get) { - if (get('isInstalled')) { - return '

' + - Ext.String.format(gettext('{0} is not initialized.'), 'Ceph') + ' '+ - gettext('You need to create a initial config once.') + '

'; - } else { - return '

' + - Ext.String.format(gettext('{0} is not installed on this node.'), 'Ceph') + '
' + - gettext('Would you like to install it now?') + '

'; - } - } - } - }, - items: [ - { - bind: { - html: '{windowText}' - }, - border: false, - padding: 5, - bodyCls: 'install-mask' - - }, - { - xtype: 'button', - bind: { - text: '{buttonText}' - }, - viewModel: {}, - cbind: { - nodename: '{nodename}' - }, - handler: function() { - var me = this.up('pveCephInstallWindow'); - var win = Ext.create('PVE.ceph.CephInstallWizard',{ - nodename: me.nodename - }); - win.getViewModel().set('isInstalled', this.getViewModel().get('isInstalled')); - win.show(); - me.mon(win,'beforeClose', function(){ - me.fireEvent("cephInstallWindowClosed"); - me.close(); - }); - - } - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallEnableEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveFirewallEnableEdit'], - mixins: ['Proxmox.Mixin.CBind'], - - subject: gettext('Firewall'), - cbindData: { - defaultValue: 0 - }, - width: 350, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - uncheckedValue: 0, - cbind: { - defaultValue: '{defaultValue}', - checked: '{defaultValue}' - }, - deleteDefaultValue: false, - fieldLabel: gettext('Firewall') - }, - { - xtype: 'displayfield', - name: 'warning', - userCls: 'pve-hint', - value: gettext('Warning: Firewall still disabled at datacenter level!'), - hidden: true - } - ], - - beforeShow: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/cluster/firewall/options', - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - if (!response.result.data.enable) { - me.down('displayfield[name=warning]').setVisible(true); - } - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallLograteInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveFirewallLograteInputPanel', - - viewModel: {}, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - reference: 'enable', - fieldLabel: gettext('Enable'), - value: true - }, - { - layout: 'hbox', - border: false, - items: [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Log rate limit'), - minValue: 1, - maxValue: 99, - allowBlank: false, - flex: 2, - value: 1 - }, - { - xtype: 'box', - html: '
/
' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'unit', - comboItems: [['second', 'second'], ['minute', 'minute'], - ['hour', 'hour'], ['day', 'day']], - allowBlank: false, - flex: 1, - value: 'second' - } - ] - }, - { - xtype: 'numberfield', - name: 'burst', - fieldLabel: gettext('Log burst limit'), - minValue: 1, - maxValue: 99, - value: 5 - } - ], - - onGetValues: function(values) { - var me = this; - - var vals = {}; - vals.enable = values.enable !== undefined ? 1 : 0; - vals.rate = values.rate + '/' + values.unit; - vals.burst = values.burst; - var properties = PVE.Parser.printPropertyString(vals, undefined); - if (properties == '') { - return { 'delete': 'log_ratelimit' }; - } - return { log_ratelimit: properties }; - }, - - setValues: function(values) { - var me = this; - - var properties = {}; - if (values.log_ratelimit !== undefined) { - properties = PVE.Parser.parsePropertyString(values.log_ratelimit, 'enable'); - if (properties.rate) { - var matches = properties.rate.match(/^(\d+)\/(second|minute|hour|day)$/); - if (matches) { - properties.rate = matches[1]; - properties.unit = matches[2]; - } - } - } - me.callParent([properties]); - } -}); - -Ext.define('PVE.FirewallLograteEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveFirewallLograteEdit', - - subject: gettext('Log rate limit'), - - items: [{ - xtype: 'pveFirewallLograteInputPanel' - }], - autoLoad: true -}); -Ext.define('PVE.panel.NotesView', { - extend: 'Ext.panel.Panel', - xtype: 'pveNotesView', - - title: gettext("Notes"), - bodyStyle: 'white-space:pre', - bodyPadding: 10, - scrollable: true, - - tbar: { - itemId: 'tbar', - hidden: true, - items: [ - { - text: gettext('Edit'), - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - } - ] - }, - - run_editor: function() { - var me = this; - var win = Ext.create('PVE.window.NotesEdit', { - pveSelNode: me.pveSelNode, - url: me.url - }); - win.show(); - win.on('destroy', me.load, me); - }, - - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data.description || ''; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - listeners: { - render: function(c) { - var me = this; - me.getEl().on('dblclick', me.run_editor, me); - } - }, - - tools: [{ - type: 'gear', - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - }], - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var type = me.pveSelNode.data.type; - if (!Ext.Array.contains(['node', 'qemu', 'lxc'], type)) { - throw 'invalid type specified'; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid && type !== 'node') { - throw "no VM ID specified"; - } - - me.url = '/api2/extjs/nodes/' + nodename + '/'; - - // add the type specific path if qemu/lxc - if (type === 'qemu' || type === 'lxc') { - me.url += type + '/' + vmid + '/'; - } - - me.url += 'config'; - - me.callParent(); - if (type === 'node') { - me.down('#tbar').setVisible(true); - } - me.load(); - } -}); -Ext.define('PVE.grid.ResourceGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveResourceGrid'], - - border: false, - defaultSorter: { - property: 'type', - direction: 'ASC' - }, - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - var coldef = rstore.defaultColumns(); - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: me.defaultSorter, - proxy: { type: 'memory' } - }); - - var textfilter = ''; - - var textfilter_match = function(item) { - var match = false; - Ext.each(['name', 'storage', 'node', 'type', 'text'], function(field) { - var v = item.data[field]; - if (v !== undefined) { - v = v.toLowerCase(); - if (v.indexOf(textfilter) >= 0) { - match = true; - return false; - } - } - }); - return match; - }; - - var updateGrid = function() { - - var filterfn = me.viewFilter ? me.viewFilter.filterfn : null; - - //console.log("START GRID UPDATE " + me.viewFilter); - - store.suspendEvents(); - - var nodeidx = {}; - var gather_child_nodes = function(cn) { - if (!cn) { - return; - } - var cs = cn.childNodes; - if (!cs) { - return; - } - var len = cs.length, i = 0, n, res; - - for (; i < len; i++) { - var child = cs[i]; - var orgnode = rstore.data.get(child.data.id); - if (orgnode) { - if ((!filterfn || filterfn(child)) && - (!textfilter || textfilter_match(child))) { - nodeidx[child.data.id] = orgnode; - } - } - gather_child_nodes(child); - } - }; - gather_child_nodes(me.pveSelNode); - - // remove vanished items - var rmlist = []; - store.each(function(olditem) { - var item = nodeidx[olditem.data.id]; - if (!item) { - //console.log("GRID REM UID: " + olditem.data.id); - rmlist.push(olditem); - } - }); - - if (rmlist.length) { - store.remove(rmlist); - } - - // add new items - var addlist = []; - var key; - for (key in nodeidx) { - if (nodeidx.hasOwnProperty(key)) { - var item = nodeidx[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var olditem = store.getById(item.data.id); - var olditem = store.data.get(item.data.id); - - if (!olditem) { - //console.log("GRID ADD UID: " + item.data.id); - var info = Ext.apply({}, item.data); - var child = Ext.create(store.model, info); - addlist.push(item); - continue; - } - // try to detect changes - var changes = false; - var fieldkeys = PVE.data.ResourceStore.fieldNames; - var fieldcount = fieldkeys.length; - var fieldind; - for (fieldind = 0; fieldind < fieldcount; fieldind++) { - var field = fieldkeys[fieldind]; - if (field != 'id' && item.data[field] != olditem.data[field]) { - changes = true; - //console.log("changed item " + item.id + " " + field + " " + item.data[field] + " != " + olditem.data[field]); - olditem.beginEdit(); - olditem.set(field, item.data[field]); - } - } - if (changes) { - olditem.endEdit(true); - olditem.commit(true); - } - } - } - - if (addlist.length) { - store.add(addlist); - } - - store.sort(); - - store.resumeEvents(); - - store.fireEvent('refresh', store); - - //console.log("END GRID UPDATE"); - }; - - var filter_task = new Ext.util.DelayedTask(function(){ - updateGrid(); - }); - - var load_cb = function() { - updateGrid(); - }; - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-resource', - tbar: [ - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - value: textfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - var v = field.getValue(); - textfilter = v.toLowerCase(); - filter_task.delay(500); - } - } - } - ], - viewConfig: { - stripeRows: true - }, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - destroy: function() { - rstore.un("load", load_cb); - } - }, - columns: coldef - }); - me.callParent(); - updateGrid(); - rstore.on("load", load_cb); - } -}); -Ext.define('PVE.pool.AddVM', { - extend: 'Proxmox.window.Edit', - width: 600, - height: 400, - isAdd: true, - isCreate: true, - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - var vmsField = Ext.create('Ext.form.field.Text', { - name: 'vms', - hidden: true, - allowBlank: false - }); - - var vmStore = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property: 'vmid', - order: 'ASC' - } - ], - filters: [ - function(item) { - return ((item.data.type === 'lxc' || item.data.type === 'qemu') && item.data.pool === ''); - } - ] - }); - - var vmGrid = Ext.create('widget.grid',{ - store: vmStore, - border: true, - height: 300, - scrollable: true, - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected, opts) { - var selectedVms = []; - selected.forEach(function(vm) { - selectedVms.push(vm.data.vmid); - }); - vmsField.setValue(selectedVms); - } - } - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - Ext.apply(me, { - subject: gettext('Virtual Machine'), - items: [ vmsField, vmGrid ] - }); - - me.callParent(); - vmStore.load(); - } -}); - -Ext.define('PVE.pool.AddStorage', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.isCreate = true; - me.isAdd = true; - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - Ext.apply(me, { - subject: gettext('Storage'), - width: 350, - items: [ - { - xtype: 'pveStorageSelector', - name: 'storage', - nodename: 'localhost', - autoSelect: false, - value: '', - fieldLabel: gettext("Storage") - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.grid.PoolMembers', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pvePoolMembers'], - - // fixme: dynamic status update ? - - stateful: true, - stateId: 'grid-pool-members', - - initComponent : function() { - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ], - proxy: { - type: 'proxmox', - root: 'data.members', - url: "/api2/json/pools/" + me.pool - } - }); - - var coldef = PVE.data.ResourceStore.defaultColumns(); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - var params = { 'delete': 1 }; - if (rec.data.type === 'storage') { - params.storage = rec.data.storage; - } else if (rec.data.type === 'qemu' || rec.data.type === 'lxc' || rec.data.type === 'openvz') { - params.vms = rec.data.vmid; - } else { - throw "unknown resource type"; - } - - Proxmox.Utils.API2Request({ - url: '/pools/' + me.pool, - method: 'PUT', - params: params, - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Virtual Machine'), - iconCls: 'pve-itype-icon-qemu', - handler: function() { - var win = Ext.create('PVE.pool.AddVM', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Storage'), - iconCls: 'pve-itype-icon-storage', - handler: function() { - var win = Ext.create('PVE.pool.AddStorage', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - remove_btn - ], - viewConfig: { - stripeRows: true - }, - columns: coldef, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.FWMacroSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFWMacroSelector', - allowBlank: true, - autoSelect: false, - valueField: 'macro', - displayField: 'macro', - listConfig: { - columns: [ - { - header: gettext('Macro'), - dataIndex: 'macro', - hideable: false, - width: 100 - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - flex: 1, - dataIndex: 'descr' - } - ] - }, - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'macro', 'descr' ], - idProperty: 'macro', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/macros" - }, - sorters: { - property: 'macro', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRulePanel', { - extend: 'Proxmox.panel.InputPanel', - - allow_iface: false, - - list_refs_url: undefined, - - onGetValues: function(values) { - var me = this; - - // hack: editable ComboGrid returns nothing when empty, so we need to set '' - // Also, disabled text fields return nothing, so we need to set '' - - Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'log'], function(key) { - if (values[key] === undefined) { - values[key] = ''; - } - }); - - delete values.modified_marker; - - return values; - }, - - initComponent : function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.column1 = [ - { - // hack: we use this field to mark the form 'dirty' when the - // record has errors- so that the user can safe the unmodified - // form again. - xtype: 'hiddenfield', - name: 'modified_marker', - value: '' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'type', - value: 'in', - comboItems: [['in', 'in'], ['out', 'out']], - fieldLabel: gettext('Direction'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - name: 'action', - value: 'ACCEPT', - comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']], - fieldLabel: gettext('Action'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - me.column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } else { - me.column1.push({ - xtype: 'displayfield', - fieldLabel: '', - value: '' - }); - } - - me.column1.push( - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'pveIPRefSelector', - name: 'source', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Source') - - }, - { - xtype: 'pveIPRefSelector', - name: 'dest', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Destination') - } - ); - - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - }, - { - xtype: 'pveFWMacroSelector', - name: 'macro', - fieldLabel: gettext('Macro'), - editable: true, - allowBlank: true, - listeners: { - change: function(f, value) { - if (value === null) { - me.down('field[name=proto]').setDisabled(false); - me.down('field[name=sport]').setDisabled(false); - me.down('field[name=dport]').setDisabled(false); - } else { - me.down('field[name=proto]').setDisabled(true); - me.down('field[name=proto]').setValue(''); - me.down('field[name=sport]').setDisabled(true); - me.down('field[name=sport]').setValue(''); - me.down('field[name=dport]').setDisabled(true); - me.down('field[name=dport]').setValue(''); - } - } - } - }, - { - xtype: 'pveIPProtocolSelector', - name: 'proto', - autoSelect: false, - editable: true, - value: '', - fieldLabel: gettext('Protocol') - }, - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'textfield', - name: 'sport', - value: '', - fieldLabel: gettext('Source port') - }, - { - xtype: 'textfield', - name: 'dport', - value: '', - fieldLabel: gettext('Dest. port') - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'pveFirewallLogLevels' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - list_refs_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.FirewallRulePanel', { - isCreate: me.isCreate, - list_refs_url: me.list_refs_url, - allow_iface: me.allow_iface, - rule_pos: me.rule_pos - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - if (values.errors) { - var field = me.query('[isFormField][name=modified_marker]')[0]; - field.setValue(1); - Ext.Function.defer(function() { - var form = ipanel.up('form').getForm(); - form.markInvalid(values.errors); - }, 100); - } - } - }); - } else if (me.rec) { - ipanel.setValues(me.rec.data); - } - } -}); - -Ext.define('PVE.FirewallGroupRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var column1 = [ - { - xtype: 'hiddenfield', - name: 'type', - value: 'group' - }, - { - xtype: 'pveSecurityGroupsSelector', - name: 'action', - value: '', - fieldLabel: gettext('Security Group'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.FirewallRules', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveFirewallRules', - - onlineHelp: 'chapter_pve_firewall', - - stateful: true, - stateId: 'grid-firewall-rules', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - groupBtn: undefined, - - tbar_prefix: undefined, - - allow_groups: true, - allow_iface: false, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - if (me.groupBtn) { - me.groupBtn.setDisabled(true); - } - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - if (me.groupBtn) { - me.groupBtn.setDisabled(false); - } - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - moveRule: function(from, to) { - var me = this; - - if (!me.base_url) { - return; - } - - Proxmox.Utils.API2Request({ - url: me.base_url + "/" + from, - method: 'PUT', - params: { moveto: to }, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - updateRule: function(rule) { - var me = this; - - if (!me.base_url) { - return; - } - - rule.enable = rule.enable ? 1 : 0; - - var pos = rule.pos; - delete rule.pos; - delete rule.errors; - - Proxmox.Utils.API2Request({ - url: me.base_url + '/' + pos.toString(), - method: 'PUT', - params: rule, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-fw-rule' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type; - - var editor; - if (type === 'in' || type === 'out') { - editor = 'PVE.FirewallRuleEdit'; - } else if (type === 'group') { - editor = 'PVE.FirewallGroupRuleEdit'; - } else { - return; - } - - var win = Ext.create(editor, { - digest: rec.data.digest, - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rule_pos: rec.data.pos - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - var run_copy_editor = function() { - var rec = sm.getSelection()[0]; - - if (!rec) { - return; - } - var type = rec.data.type; - - - if (!(type === 'in' || type === 'out')) { - return; - } - - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rec: rec - }); - - win.show(); - win.on('destroy', reload); - }; - - me.copyBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Copy'), - selModel: sm, - enableFn: function(rec) { - return (rec.data.type === 'in' || rec.data.type === 'out'); - }, - disabled: true, - handler: run_copy_editor - }); - - if (me.allow_groups) { - me.groupBtn = Ext.create('Ext.Button', { - text: gettext('Insert') + ': ' + - gettext('Security Group'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallGroupRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - baseurl: me.base_url + '/', - confirmMsg: false, - getRecordName: function(rec) { - var rule = rec.data; - return rule.pos.toString() + - '?digest=' + encodeURIComponent(rule.digest); - }, - callback: function() { - me.store.load(); - } - }); - - var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : []; - tbar.push(me.addBtn, me.copyBtn); - if (me.groupBtn) { - tbar.push(me.groupBtn); - } - tbar.push(me.removeBtn, me.editBtn); - - var render_errors = function(name, value, metaData, record) { - var errors = record.data.errors; - if (errors && errors[name]) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(errors[name]) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - return value; - }; - - var columns = [ - { - // similar to xtype: 'rownumberer', - dataIndex: 'pos', - resizable: false, - width: 23, - sortable: false, - align: 'right', - hideable: false, - menuDisabled: true, - renderer: function(value, metaData, record, rowIdx, colIdx, store) { - metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special'; - if (value >= 0) { - return value; - } - return ''; - } - }, - { - xtype: 'checkcolumn', - header: gettext('Enable'), - dataIndex: 'enable', - listeners: { - checkchange: function(column, recordIndex, checked) { - var record = me.getStore().getData().items[recordIndex]; - record.commit(); - var data = {}; - Ext.Array.forEach(record.getFields(), function(field) { - data[field.name] = record.get(field.name); - }); - if (!me.allow_iface || !data.iface) { - delete data.iface; - } - me.updateRule(data); - } - }, - width: 50 - }, - { - header: gettext('Type'), - dataIndex: 'type', - renderer: function(value, metaData, record) { - return render_errors('type', value, metaData, record); - }, - width: 50 - }, - { - header: gettext('Action'), - dataIndex: 'action', - renderer: function(value, metaData, record) { - return render_errors('action', value, metaData, record); - }, - width: 80 - }, - { - header: gettext('Macro'), - dataIndex: 'macro', - renderer: function(value, metaData, record) { - return render_errors('macro', value, metaData, record); - }, - width: 80 - } - ]; - - if (me.allow_iface) { - columns.push({ - header: gettext('Interface'), - dataIndex: 'iface', - renderer: function(value, metaData, record) { - return render_errors('iface', value, metaData, record); - }, - width: 80 - }); - } - - columns.push( - { - header: gettext('Source'), - dataIndex: 'source', - renderer: function(value, metaData, record) { - return render_errors('source', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Destination'), - dataIndex: 'dest', - renderer: function(value, metaData, record) { - return render_errors('dest', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Protocol'), - dataIndex: 'proto', - renderer: function(value, metaData, record) { - return render_errors('proto', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Dest. port'), - dataIndex: 'dport', - renderer: function(value, metaData, record) { - return render_errors('dport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Source port'), - dataIndex: 'sport', - renderer: function(value, metaData, record) { - return render_errors('sport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Log level'), - dataIndex: 'log', - renderer: function(value, metaData, record) { - return render_errors('log', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value, metaData, record) { - return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record); - } - } - ); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - plugins: [ - { - ptype: 'gridviewdragdrop', - dragGroup: 'FWRuleDDGroup', - dropGroup: 'FWRuleDDGroup' - } - ], - listeners: { - beforedrop: function(node, data, dropRec, dropPosition) { - if (!dropRec) { - return false; // empty view - } - var moveto = dropRec.get('pos'); - if (dropPosition === 'after') { - moveto++; - } - var pos = data.records[0].get('pos'); - me.moveRule(pos, moveto); - return 0; - }, - itemdblclick: run_editor - } - }, - sortableColumns: false, - columns: columns - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-fw-rule', { - extend: 'Ext.data.Model', - fields: [ { name: 'enable', type: 'boolean' }, - 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface', - 'dport', 'sport', 'comment', 'pos', 'digest', 'errors' ], - idProperty: 'pos' - }); - -}); -Ext.define('PVE.FirewallAliasEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - alias_name: undefined, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.alias_name === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.alias_name; - me.method = 'PUT'; - } - - var items = [ - { - xtype: 'textfield', - name: me.isCreate ? 'name' : 'rename', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'cidr', - fieldLabel: gettext('IP/CIDR'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ]; - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - items: items - }); - - Ext.apply(me, { - subject: gettext('Alias'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - values.rename = values.name; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('pve-fw-aliases', { - extend: 'Ext.data.Model', - - fields: [ 'name', 'cidr', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.FirewallAliases', { - extend: 'Ext.grid.Panel', - alias: ['widget.pveFirewallAliases'], - - onlineHelp: 'pve_firewall_ip_aliases', - - stateful: true, - stateId: 'grid-firewall-aliases', - - base_url: undefined, - - title: gettext('Alias'), - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-aliases', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url, - alias_name: rec.data.name - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - - Ext.apply(me, { - store: store, - tbar: [ me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: gettext('Name'), dataIndex: 'name', width: 100 }, - { header: gettext('IP/CIDR'), dataIndex: 'cidr', width: 100 }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - me.on('activate', reload); - } -}); -Ext.define('PVE.FirewallOptions', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveFirewallOptions'], - - fwtype: undefined, // 'dc', 'node' or 'vm' - - base_url: undefined, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - if (me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm') { - if (me.fwtype === 'node') { - me.cwidth1 = 250; - } - } else { - throw "unknown firewall option type"; - } - - me.rows = {}; - - var add_boolean_row = function(name, text, defaultValue) { - me.add_boolean_row(name, text, { defaultValue: defaultValue }); - }; - var add_integer_row = function(name, text, minValue, labelWidth) { - me.add_integer_row(name, text, { - minValue: minValue, - deleteEmpty: true, - labelWidth: labelWidth, - renderer: function(value) { - if (value === undefined) { - return Proxmox.Utils.defaultText; - } - - return value; - } - }); - }; - - var add_log_row = function(name, labelWidth) { - me.rows[name] = { - header: name, - required: true, - defaultValue: 'nolog', - editor: { - xtype: 'proxmoxWindowEdit', - subject: name, - fieldDefaults: { labelWidth: labelWidth || 100 }, - items: { - xtype: 'pveFirewallLogLevels', - name: name, - fieldLabel: name - } - } - }; - }; - - if (me.fwtype === 'node') { - me.rows.enable = { - required: true, - defaultValue: 1, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 1 - } - }; - add_boolean_row('nosmurfs', gettext('SMURFS filter'), 1); - add_boolean_row('tcpflags', gettext('TCP flags filter'), 0); - add_boolean_row('ndp', 'NDP', 1); - add_integer_row('nf_conntrack_max', 'nf_conntrack_max', 32768, 120); - add_integer_row('nf_conntrack_tcp_timeout_established', - 'nf_conntrack_tcp_timeout_established', 7875, 250); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - add_log_row('tcp_flags_log_level', 120); - add_log_row('smurf_log_level'); - } else if (me.fwtype === 'vm') { - me.rows.enable = { - required: true, - defaultValue: 0, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 0 - } - }; - add_boolean_row('dhcp', 'DHCP', 1); - add_boolean_row('ndp', 'NDP', 1); - add_boolean_row('radv', gettext('Router Advertisement'), 0); - add_boolean_row('macfilter', gettext('MAC filter'), 1); - add_boolean_row('ipfilter', gettext('IP filter'), 0); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - } else if (me.fwtype === 'dc') { - add_boolean_row('enable', gettext('Firewall'), 0); - add_boolean_row('ebtables', 'ebtables', 1); - me.rows.log_ratelimit = { - header: gettext('Log rate limit'), - required: true, - defaultValue: gettext('Default') + ' (enable=1,rate1/second,burst=5)', - editor: { - xtype: 'pveFirewallLograteEdit', - defaultValue: 'enable=1' - } - }; - } - - if (me.fwtype === 'dc' || me.fwtype === 'vm') { - me.rows.policy_in = { - header: gettext('Input Policy'), - required: true, - defaultValue: 'DROP', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Input Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_in', - value: 'DROP', - fieldLabel: gettext('Input Policy') - } - } - }; - - me.rows.policy_out = { - header: gettext('Output Policy'), - required: true, - defaultValue: 'ACCEPT', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Output Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_out', - value: 'ACCEPT', - fieldLabel: gettext('Output Policy') - } - } - }; - } - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - var rowdef = me.rows[rec.data.key]; - edit_btn.setDisabled(!rowdef.editor); - }; - - Ext.apply(me, { - url: "/api2/json" + me.base_url, - tbar: [ edit_btn ], - editorConfig: { - url: '/api2/extjs/' + me.base_url - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); - - -Ext.define('PVE.FirewallLogLevels', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallLogLevels'], - - name: 'log', - fieldLabel: gettext('Log level'), - value: 'nolog', - comboItems: [['nolog', 'nolog'], ['emerg', 'emerg'], ['alert', 'alert'], - ['crit', 'crit'], ['err', 'err'], ['warning', 'warning'], - ['notice', 'notice'], ['info', 'info'], ['debug', 'debug']] -}); -/* - * Left Treepanel, containing all the resources we manage in this datacenter: server nodes, server storages, VMs and Containers - */ -Ext.define('PVE.tree.ResourceTree', { - extend: 'Ext.tree.TreePanel', - alias: ['widget.pveResourceTree'], - - statics: { - typeDefaults: { - node: { - iconCls: 'fa fa-building', - text: gettext('Nodes') - }, - pool: { - iconCls: 'fa fa-tags', - text: gettext('Resource Pool') - }, - storage: { - iconCls: 'fa fa-database', - text: gettext('Storage') - }, - qemu: { - iconCls: 'fa fa-desktop', - text: gettext('Virtual Machine') - }, - lxc: { - //iconCls: 'x-tree-node-lxc', - iconCls: 'fa fa-cube', - text: gettext('LXC Container') - }, - template: { - iconCls: 'fa fa-file-o' - } - } - }, - - useArrows: true, - - // private - nodeSortFn: function(node1, node2) { - var n1 = node1.data; - var n2 = node2.data; - - if ((n1.groupbyid && n2.groupbyid) || - !(n1.groupbyid || n2.groupbyid)) { - - var tcmp; - - var v1 = n1.type; - var v2 = n2.type; - - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - - // numeric compare for VM IDs - // sort templates after regular VMs - if (v1 === 'qemu' || v1 === 'lxc') { - if (n1.template && !n2.template) { - return 1; - } else if (n2.template && !n1.template) { - return -1; - } - v1 = n1.vmid; - v2 = n2.vmid; - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - } - - return n1.id > n2.id ? 1 : (n1.id < n2.id ? -1 : 0); - } else if (n1.groupbyid) { - return -1; - } else if (n2.groupbyid) { - return 1; - } - }, - - // private: fast binary search - findInsertIndex: function(node, child, start, end) { - var me = this; - - var diff = end - start; - - var mid = start + (diff>>1); - - if (diff <= 0) { - return start; - } - - var res = me.nodeSortFn(child, node.childNodes[mid]); - if (res <= 0) { - return me.findInsertIndex(node, child, start, mid); - } else { - return me.findInsertIndex(node, child, mid + 1, end); - } - }, - - setIconCls: function(info) { - var me = this; - - var cls = PVE.Utils.get_object_icon_class(info.type, info); - - if (cls !== '') { - info.iconCls = cls; - } - }, - - // add additional elements to text - // at the moment only the usage indicator for storages - setText: function(info) { - var me = this; - - var status = ''; - if (info.type === 'storage') { - var maxdisk = info.maxdisk; - var disk = info.disk; - var usage = disk/maxdisk; - var cls = ''; - if (usage <= 1.0 && usage >= 0.0) { - var height = (usage*100).toFixed(0); - var neg_height = (100-usage*100).toFixed(0); - status = '
'; - status += '
'; - status += '
'; - status += '
'; - } - } - - info.text = status + info.text; - }, - - setToolTip: function(info) { - if (info.type === 'pool' || info.groupbyid !== undefined) { - return; - } - - var qtips = [gettext('Status') + ': ' + (info.qmpstatus || info.status)]; - if (info.lock) { - qtips.push('Config locked (' + info.lock + ')'); - } - if (info.hastate != 'unmanaged') { - qtips.push(gettext('HA State') + ": " + info.hastate); - } - - info.qtip = qtips.join(', '); - }, - - // private - addChildSorted: function(node, info) { - var me = this; - - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - - var defaults; - if (info.groupbyid) { - info.text = info.groupbyid; - if (info.type === 'type') { - defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid]; - if (defaults && defaults.text) { - info.text = defaults.text; - } - } - } - var child = Ext.create('PVETree', info); - - var cs = node.childNodes; - var pos; - if (cs) { - pos = cs[me.findInsertIndex(node, child, 0, cs.length)]; - } - - node.insertBefore(child, pos); - - return child; - }, - - // private - groupChild: function(node, info, groups, level) { - var me = this; - - var groupby = groups[level]; - var v = info[groupby]; - - if (v) { - var group = node.findChild('groupbyid', v); - if (!group) { - var groupinfo; - if (info.type === groupby) { - groupinfo = info; - } else { - groupinfo = { - type: groupby, - id : groupby + "/" + v - }; - if (groupby !== 'type') { - groupinfo[groupby] = v; - } - } - groupinfo.leaf = false; - groupinfo.groupbyid = v; - group = me.addChildSorted(node, groupinfo); - } - if (info.type === groupby) { - return group; - } - if (group) { - return me.groupChild(group, info, groups, level + 1); - } - } - - return me.addChildSorted(node, info); - }, - - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - if (!me.viewFilter) { - me.viewFilter = {}; - } - - var pdata = { - dataIndex: {}, - updateCount: 0 - }; - - var store = Ext.create('Ext.data.TreeStore', { - model: 'PVETree', - root: { - expanded: true, - id: 'root', - text: gettext('Datacenter'), - iconCls: 'fa fa-server' - } - }); - - var stateid = 'rid'; - - var updateTree = function() { - var tmp; - - store.suspendEvents(); - - var rootnode = me.store.getRootNode(); - // remember selected node (and all parents) - var sm = me.getSelectionModel(); - - var lastsel = sm.getSelection()[0]; - var reselect = false; - var parents = []; - var p = lastsel; - while (p && !!(p = p.parentNode)) { - parents.push(p); - } - - var index = pdata.dataIndex; - - var groups = me.viewFilter.groups || []; - var filterfn = me.viewFilter.filterfn; - - // remove vanished or moved items - // update in place changed items - var key; - for (key in index) { - if (index.hasOwnProperty(key)) { - var olditem = index[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var item = rstore.getById(olditem.data.id); - var item = rstore.data.get(olditem.data.id); - - var changed = false; - var moved = false; - if (item) { - // test if any grouping attributes changed - // this will also catch migrated nodes - // in server view - var i, len; - for (i = 0, len = groups.length; i < len; i++) { - var attr = groups[i]; - if (item.data[attr] != olditem.data[attr]) { - //console.log("changed " + attr); - moved = true; - break; - } - } - - // explicitly check for node, since - // in some views, node is not a grouping - // attribute - if (!moved && item.data.node !== olditem.data.node) { - moved = true; - } - - // tree item has been updated - var fields = [ - 'text', 'running', 'template', 'status', - 'qmpstatus', 'hastate', 'lock' - ]; - - var field; - for (i = 0; i < fields.length; i++) { - field = fields[i]; - if (item.data[field] !== olditem.data[field]) { - changed = true; - break; - } - } - - // fixme: also test filterfn()? - } - - if (changed) { - olditem.beginEdit(); - //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running); - var info = olditem.data; - Ext.apply(info, item.data); - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - olditem.commit(); - } - if ((!item || moved) && olditem.isLeaf()) { - //console.log("REM UID: " + key + " ITEM " + olditem.data.id); - delete index[key]; - var parentNode = olditem.parentNode; - // when the selected item disappears, - // we have to deselect it here, and reselect it - // later - if (lastsel && olditem.data.id === lastsel.data.id) { - reselect = true; - sm.deselect(olditem); - } - // since the store events are suspended, we - // manually remove the item from the store also - store.remove(olditem); - parentNode.removeChild(olditem, true); - } - } - } - - // add new items - rstore.each(function(item) { - var olditem = index[item.data.id]; - if (olditem) { - return; - } - - if (filterfn && !filterfn(item)) { - return; - } - - //console.log("ADD UID: " + item.data.id); - - var info = Ext.apply({ leaf: true }, item.data); - - var child = me.groupChild(rootnode, info, groups, 0); - if (child) { - index[item.data.id] = child; - } - }); - - store.resumeEvents(); - store.fireEvent('refresh', store); - - // select parent node is selection vanished - if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) { - lastsel = rootnode; - while (!!(p = parents.shift())) { - if (!!(tmp = rootnode.findChild('id', p.data.id, true))) { - lastsel = tmp; - break; - } - } - me.selectById(lastsel.data.id); - } else if (lastsel && reselect) { - me.selectById(lastsel.data.id); - } - - // on first tree load set the selection from the stateful provider - if (!pdata.updateCount) { - rootnode.expand(); - me.applyState(sp.get(stateid)); - } - - pdata.updateCount++; - }; - - var statechange = function(sp, key, value) { - if (key === stateid) { - me.applyState(value); - } - }; - - sp.on('statechange', statechange); - - Ext.apply(me, { - allowSelection: true, - store: store, - viewConfig: { - // note: animate cause problems with applyState - animate: false - }, - //useArrows: true, - //rootVisible: false, - //title: 'Resource Tree', - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - destroy: function() { - rstore.un("load", updateTree); - }, - beforecellmousedown: function (tree, td, cellIndex, record, tr, rowIndex, ev) { - var sm = me.getSelectionModel(); - // disable selection when right clicking - // except the record is already selected - me.allowSelection = (ev.button !== 2) || sm.isSelected(record); - }, - beforeselect: function (tree, record, index, eopts) { - var allow = me.allowSelection; - me.allowSelection = true; - return allow; - }, - itemdblclick: PVE.Utils.openTreeConsole - }, - setViewFilter: function(view) { - me.viewFilter = view; - me.clearTree(); - updateTree(); - }, - setDatacenterText: function(clustername) { - var rootnode = me.store.getRootNode(); - - var rnodeText = gettext('Datacenter'); - if (clustername !== undefined) { - rnodeText += ' (' + clustername + ')'; - } - - rootnode.beginEdit(); - rootnode.data.text = rnodeText; - rootnode.commit(); - }, - clearTree: function() { - pdata.updateCount = 0; - var rootnode = me.store.getRootNode(); - rootnode.collapse(); - rootnode.removeAll(); - pdata.dataIndex = {}; - me.getSelectionModel().deselectAll(); - }, - selectExpand: function(node) { - var sm = me.getSelectionModel(); - if (!sm.isSelected(node)) { - sm.select(node); - var cn = node; - while (!!(cn = cn.parentNode)) { - if (!cn.isExpanded()) { - cn.expand(); - } - } - me.getView().focusRow(node); - } - }, - selectById: function(nodeid) { - var rootnode = me.store.getRootNode(); - var sm = me.getSelectionModel(); - var node; - if (nodeid === 'root') { - node = rootnode; - } else { - node = rootnode.findChild('id', nodeid, true); - } - if (node) { - me.selectExpand(node); - } - return node; - }, - applyState : function(state) { - var sm = me.getSelectionModel(); - if (state && state.value) { - me.selectById(state.value); - } else { - sm.deselectAll(); - } - } - }); - - me.callParent(); - - var sm = me.getSelectionModel(); - sm.on('select', function(sm, n) { - sp.set(stateid, { value: n.data.id}); - }); - - rstore.on("load", updateTree); - rstore.startUpdate(); - //rstore.stopUpdate(); - } - -}); -Ext.define('pve-fw-ipsets', { - extend: 'Ext.data.Model', - fields: [ 'name', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.IPSetList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetList', - - stateful: true, - stateId: 'grid-firewall-ipsetlist', - - ipset_panel: undefined, - - base_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - initComponent: function() { - - var me = this; - - if (me.ipset_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-ipsets', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('Proxmox.window.Edit', { - subject: "IPSet '" + rec.data.name + "'", - url: me.base_url, - method: 'POST', - digest: rec.data.digest, - items: [ - { - xtype: 'hiddenfield', - name: 'rename', - value: rec.data.name - }, - { - xtype: 'textfield', - name: 'name', - value: rec.data.name, - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: rec.data.comment, - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('Proxmox.window.Edit', { - subject: 'IPSet', - url: me.base_url, - method: 'POST', - items: [ - { - xtype: 'textfield', - name: 'name', - value: '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - Ext.apply(me, { - store: store, - tbar: [ 'IPSet:', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: 'IPSet', dataIndex: 'name', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = me.base_url + '/' + rec.data.name; - me.ipset_panel.setBaseUrl(url); - }, - deselect: function() { - me.ipset_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.IPSetCidrEdit', { - extend: 'Proxmox.window.Edit', - - cidr: undefined, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.cidr === undefined); - - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.cidr; - me.method = 'PUT'; - } - - var column1 = []; - - if (me.isCreate) { - if (!me.list_refs_url) { - throw "no alias_base_url specified"; - } - - column1.push({ - xtype: 'pveIPRefSelector', - name: 'cidr', - ref_type: 'alias', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } else { - column1.push({ - xtype: 'displayfield', - name: 'cidr', - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'nomatch', - checked: false, - uncheckedValue: 0, - fieldLabel: 'nomatch' - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('IP/CIDR'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.IPSetGrid', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetGrid', - - stateful: true, - stateId: 'grid-firewall-ipsets', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no1 list_refs_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-ipset' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - cidr: rec.data.cidr - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Add'), - disabled: true, - handler: function() { - if (!me.base_url) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - var render_errors = function(value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors.cidr || errors.nomatch; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - Ext.apply(me, { - tbar: [ 'IP/CIDR:', me.addBtn, me.removeBtn, me.editBtn ], - store: store, - selModel: sm, - listeners: { - itemdblclick: run_editor - }, - columns: [ - { - xtype: 'rownumberer' - }, - { - header: gettext('IP/CIDR'), - dataIndex: 'cidr', - width: 150, - renderer: function(value, metaData, record) { - value = render_errors(value, metaData, record); - if (record.data.nomatch) { - return '! ' + value; - } - return value; - } - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value) { - return Ext.util.Format.htmlEncode(value); - } - } - ] - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-ipset', { - extend: 'Ext.data.Model', - fields: [ { name: 'nomatch', type: 'boolean' }, - 'cidr', 'comment', 'errors' ], - idProperty: 'cidr' - }); - -}); - -Ext.define('PVE.IPSet', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveIPSet', - - title: 'IPSet', - - onlineHelp: 'pve_firewall_ip_sets', - - list_refs_url: undefined, - - initComponent: function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var ipset_panel = Ext.createWidget('pveIPSetGrid', { - region: 'center', - list_refs_url: me.list_refs_url, - border: false - }); - - var ipset_list = Ext.createWidget('pveIPSetList', { - region: 'west', - ipset_panel: ipset_panel, - base_url: me.base_url, - width: '50%', - border: false, - split: true - }); - - Ext.apply(me, { - layout: 'border', - items: [ ipset_list, ipset_panel ], - listeners: { - show: function() { - ipset_list.fireEvent('show', ipset_list); - } - } - }); - - me.callParent(); - } -}); -/* - * Base class for all the multitab config panels - * - * How to use this: - * - * You create a subclass of this, and then define your wanted tabs - * as items like this: - * - * items: [{ - * title: "myTitle", - * xytpe: "somextype", - * iconCls: 'fa fa-icon', - * groups: ['somegroup'], - * expandedOnInit: true, - * itemId: 'someId' - * }] - * - * this has to be in the declarative syntax, else we - * cannot save them for later - * (so no Ext.create or Ext.apply of an item in the subclass) - * - * the groups array expects the itemids of the items - * which are the parents, which have to come before they - * are used - * - * if you want following the tree: - * - * Option1 - * Option2 - * -> SubOption1 - * -> SubSubOption1 - * - * the suboption1 group array has to look like this: - * groups: ['itemid-of-option2'] - * - * and of subsuboption1: - * groups: ['itemid-of-option2', 'itemid-of-suboption1'] - * - * setting the expandedOnInit determines if the item/group is expanded - * initially (false by default) - */ -Ext.define('PVE.panel.Config', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePanelConfig', - - showSearch: true, // add a resource grid with a search button as first tab - viewFilter: undefined, // a filter to pass to that resource grid - - tbarSpacing: true, // if true, adds a spacer after the title in tbar - - dockedItems: [{ - // this is needed for the overflow handler - xtype: 'toolbar', - overflowHandler: 'scroller', - dock: 'left', - style: { - backgroundColor: '#f5f5f5', - padding: 0, - margin: 0 - }, - items: { - xtype: 'treelist', - itemId: 'menu', - ui: 'nav', - expanderOnly: true, - expanderFirst: false, - animation: false, - singleExpand: false, - listeners: { - selectionchange: function(treeList, selection) { - var me = this.up('panel'); - me.suspendLayout = true; - me.activateCard(selection.data.id); - me.suspendLayout = false; - me.updateLayout(); - }, - itemclick: function(treelist, info) { - var olditem = treelist.getSelection(); - var newitem = info.node; - - // when clicking on the expand arrow, - // we don't select items, but still want - // the original behaviour - if (info.select === false) { - return; - } - - // if you click on a different item which is open, - // leave it open - // else toggle the clicked item - if (olditem.data.id !== newitem.data.id && - newitem.data.expanded === true) { - info.toggle = false; - } else { - info.toggle = true; - } - } - } - } - }, - { - xtype: 'toolbar', - itemId: 'toolbar', - dock: 'top', - height: 36, - overflowHandler: 'scroller' - }], - - firstItem: '', - layout: 'card', - border: 0, - - // used for automated test - selectById: function(cardid) { - var me = this; - - var root = me.store.getRoot(); - var selection = root.findChild('id', cardid, true); - - if (selection) { - selection.expand(); - var menu = me.down('#menu'); - menu.setSelection(selection); - return cardid; - } - }, - - activateCard: function(cardid) { - var me = this; - if (me.savedItems[cardid]) { - var curcard = me.getLayout().getActiveItem(); - var newcard = me.add(me.savedItems[cardid]); - me.helpButton.setOnlineHelp(newcard.onlineHelp || me.onlineHelp); - if (curcard) { - me.setActiveItem(cardid); - me.remove(curcard, true); - - // trigger state change - - var ncard = cardid; - // Note: '' is alias for first tab. - // First tab can be 'search' or something else - if (cardid === me.firstItem) { - ncard = ''; - } - if (me.hstateid) { - me.sp.set(me.hstateid, { value: ncard }); - } - } - } - }, - - initComponent: function() { - var me = this; - - var stateid = me.hstateid; - - me.sp = Ext.state.Manager.getProvider(); - - var activeTab; // leaving this undefined means items[0] will be the default tab - - if (stateid) { - var state = me.sp.get(stateid); - if (state && state.value) { - // if this tab does not exists, it chooses the first - activeTab = state.value; - } - } - - // get title - var title = me.title || me.pveSelNode.data.text; - me.title = undefined; - - // create toolbar - var tbar = me.tbar || []; - me.tbar = undefined; - - if (!me.onlineHelp) { - switch(me.pveSelNode.data.id) { - case 'type/storage':me.onlineHelp = 'chapter-pvesm.html'; break; - case 'type/qemu':me.onlineHelp = 'chapter-qm.html'; break; - case 'type/lxc':me.onlineHelp = 'chapter-pct.html'; break; - case 'type/pool':me.onlineHelp = 'chapter-pveum.html#_pools'; break; - case 'type/node':me.onlineHelp = 'chapter-sysadmin.html'; break; - } - } - - if (me.tbarSpacing) { - tbar.unshift('->'); - } - tbar.unshift({ - xtype: 'tbtext', - text: title, - baseCls: 'x-panel-header-text' - }); - - me.helpButton = Ext.create('Proxmox.button.Help', { - hidden: false, - listenToGlobalEvent: false, - onlineHelp: me.onlineHelp || undefined - }); - - tbar.push(me.helpButton); - - me.dockedItems[1].items = tbar; - - // include search tab - me.items = me.items || []; - if (me.showSearch) { - me.items.unshift({ - itemId: 'search', - title: gettext('Search'), - iconCls: 'fa fa-search', - xtype: 'pveResourceGrid', - pveSelNode: me.pveSelNode - }); - } - - me.savedItems = {}; - /*jslint confusion:true*/ - if (me.items[0]) { - me.firstItem = me.items[0].itemId; - } - /*jslint confusion:false*/ - - me.store = Ext.create('Ext.data.TreeStore', { - root: { - expanded: true - } - }); - var root = me.store.getRoot(); - me.items.forEach(function(item){ - var treeitem = Ext.create('Ext.data.TreeModel',{ - id: item.itemId, - text: item.title, - iconCls: item.iconCls, - leaf: true, - expanded: item.expandedOnInit - }); - item.header = false; - if (me.savedItems[item.itemId] !== undefined) { - throw "itemId already exists, please use another"; - } - me.savedItems[item.itemId] = item; - - var group; - var curnode = root; - - // get/create the group items - while (Ext.isArray(item.groups) && item.groups.length > 0) { - group = item.groups.shift(); - - var child = curnode.findChild('id', group); - if (child === null) { - // did not find the group item - // so add it where we are - break; - } - curnode = child; - } - - // insert the item - - // lets see if it already exists - var node = curnode.findChild('id', item.itemId); - - if (node === null) { - curnode.appendChild(treeitem); - } else { - // should not happen! - throw "id already exists"; - } - }); - - delete me.items; - me.defaults = me.defaults || {}; - Ext.apply(me.defaults, { - pveSelNode: me.pveSelNode, - viewFilter: me.viewFilter, - workspace: me.workspace, - border: 0 - }); - - me.callParent(); - - var menu = me.down('#menu'); - var selection = root.findChild('id', activeTab, true) || root.firstChild; - var node = selection; - while (node !== root) { - node.expand(); - node = node.parentNode; - } - menu.setStore(me.store); - menu.setSelection(selection); - - // on a state change, - // select the new item - var statechange = function(sp, key, state) { - // it the state change is for this panel - if (stateid && (key === stateid) && state) { - // get active item - var acard = me.getLayout().getActiveItem().itemId; - // get the itemid of the new value - var ncard = state.value || me.firstItem; - if (ncard && (acard != ncard)) { - // select the chosen item - menu.setSelection(root.findChild('id', ncard, true) || root.firstChild); - } - } - }; - - if (stateid) { - me.mon(me.sp, 'statechange', statechange); - } - } -}); -Ext.define('PVE.grid.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveBackupView'], - - onlineHelp: 'chapter_vzdump', - - stateful: true, - stateId: 'grid-guest-backup', - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmtype = me.pveSelNode.data.type; - if (!vmtype) { - throw "no VM type specified"; - } - - var vmtypeFilter; - if (vmtype === 'openvz') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-openvz-'); - }; - } else if (vmtype === 'lxc') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-lxc-'); - }; - } else if (vmtype === 'qemu') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-qemu-'); - }; - } else { - throw "unsupported VM type '" + vmtype + "'"; - } - - var searchFilter = { - property: 'volid', - // on initial store display only our vmid backups - // surround with minus sign to prevent the 2016 VMID bug - value: vmtype + '-' + vmid + '-', - anyMatch: true, - caseSensitive: false - }; - - me.store = Ext.create('Ext.data.Store', { - model: 'pve-storage-content', - sorters: { - property: 'volid', - order: 'DESC' - }, - filters: [ - vmtypeFilter, - searchFilter - ] - }); - - var reload = Ext.Function.createBuffered(function() { - if (me.store) { - me.store.load(); - } - }, 100); - - var setStorage = function(storage) { - var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content'; - url += '?content=backup'; - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - reload(); - }; - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'backup', - allowBlank: false, - listeners: { - change: function(f, value) { - setStorage(value); - } - } - }); - - var storagefilter = Ext.create('Ext.form.field.Text', { - fieldLabel: gettext('Search'), - labelWidth: 50, - labelAlign: 'right', - enableKeyEvents: true, - value: searchFilter.value, - listeners: { - buffer: 500, - keyup: function(field) { - me.store.clearFilter(true); - searchFilter.value = field.getValue(); - me.store.filter([ - vmtypeFilter, - searchFilter - ]); - } - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var backup_btn = Ext.create('Ext.button.Button', { - text: gettext('Backup now'), - handler: function() { - var win = Ext.create('PVE.window.Backup', { - nodename: nodename, - vmid: vmid, - vmtype: vmtype, - storage: storagesel.getValue(), - listeners : { - close: function() { - reload(); - } - } - }); - win.show(); - } - }); - - var restore_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Restore'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var volid = rec.data.volid; - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - vmid: vmid, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }); - - var delete_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - dangerous: true, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.volid + "'"); - msg += " " + gettext('This will permanently erase all data.'); - - return msg; - }, - getUrl: function(rec) { - var storage = storagesel.getValue(); - return '/nodes/' + nodename + '/storage/' + storage + '/content/' + rec.data.volid; - }, - callback: function() { - reload(); - } - }); - - var config_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var storage = storagesel.getValue(); - if (!storage) { - return; - } - - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }); - - Ext.apply(me, { - selModel: sm, - tbar: [ backup_btn, restore_btn, delete_btn,config_btn, '->', storagesel, storagefilter ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'volid' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.CephCreateService', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCephCreateService', - - showProgress: true, - - setNode: function(nodename) { - var me = this; - - me.nodename = nodename; - me.url = "/nodes/" + nodename + "/ceph/" + me.type + "/" + nodename; - }, - - method: 'POST', - isCreate: true, - - items: [ - { - xtype: 'pveNodeSelector', - submitValue: false, - fieldLabel: gettext('Host'), - selectCurNode: true, - allowBlank: false, - listeners: { - change: function(f, value) { - var me = this.up('pveCephCreateService'); - me.setNode(value); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.type) { - throw "no type specified"; - } - - me.setNode(me.nodename); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephServiceList', { - extend: 'Ext.grid.GridPanel', - xtype: 'pveNodeCephServiceList', - - onlineHelp: 'chapter_pveceph', - emptyText: gettext('No such service configured.'), - - stateful: true, - - // will be called when the store loads - storeLoadCallback: Ext.emptyFn, - - // if set to true, does shows the ceph install mask if needed - showCephInstallMask: false, - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - if (view.pveSelNode) { - view.nodename = view.pveSelNode.data.node; - } - if (!view.nodename) { - throw "no node name specified"; - } - - if (!view.type) { - throw "no type specified"; - } - - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - autoStart: true, - interval: 3000, - storeid: 'ceph-' + view.type + '-list' + view.nodename, - model: 'ceph-service-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + view.nodename + "/ceph/" + view.type - } - }); - - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: [{ property: 'name' }] - })); - - if (view.storeLoadCallback) { - view.rstore.on('load', view.storeLoadCallback, this); - } - view.on('destroy', view.rstore.stopUpdate); - - if (view.showCephInstallMask) { - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error) { - view.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(view.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - view.rstore.startUpdate(); - }); - } - ); - }); - } - }, - - service_cmd: function(rec, cmd) { - var view = this.getView(); - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/" + cmd, - method: 'POST', - params: { service: view.type + '.' + rec.data.name }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - taskDone: function() { - view.rstore.load(); - } - }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - onChangeService: function(btn) { - var me = this; - var view = this.getView(); - var cmd = btn.action; - var rec = view.getSelection()[0]; - me.service_cmd(rec, cmd); - }, - - showSyslog: function() { - var view = this.getView(); - var rec = view.getSelection()[0]; - var servicename = 'ceph-' + view.type + '@' + rec.data.name; - var url = "/api2/extjs/nodes/" + rec.data.host + "/syslog?service=" + encodeURIComponent(servicename); - var win = Ext.create('Ext.window.Window', { - title: gettext('Syslog') + ': ' + servicename, - modal: true, - width: 800, - height: 400, - layout: 'fit', - items: [{ - xtype: 'proxmoxLogView', - url: url, - log_select_timespan: 1 - }] - }); - win.show(); - }, - - onCreate: function() { - var view = this.getView(); - var win = Ext.create('PVE.CephCreateService', { - autoShow: true, - nodename: view.nodename, - subject: view.getTitle(), - type: view.type, - taskDone: function() { - view.rstore.load(); - } - }); - } - }, - - tbar: [ - { - xtype: 'proxmoxButton', - text: gettext('Start'), - iconCls: 'fa fa-play', - action: 'start', - disabled: true, - enableFn: function(rec) { - return rec.data.state === 'stopped' || - rec.data.state === 'unknown'; - }, - handler: 'onChangeService' - }, - { - xtype: 'proxmoxButton', - text: gettext('Stop'), - iconCls: 'fa fa-stop', - action: 'stop', - enableFn: function(rec) { - return rec.data.state !== 'stopped'; - }, - disabled: true, - handler: 'onChangeService' - }, - { - xtype: 'proxmoxButton', - text: gettext('Restart'), - iconCls: 'fa fa-refresh', - action: 'restart', - disabled: true, - enableFn: function(rec) { - return rec.data.state !== 'stopped'; - }, - handler: 'onChangeService' - }, - '-', - { - text: gettext('Create'), - reference: 'createButton', - handler: 'onCreate' - }, - { - text: gettext('Destroy'), - xtype: 'proxmoxStdRemoveButton', - getUrl: function(rec) { - var view = this.up('grid'); - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - return "/nodes/" + rec.data.host + "/ceph/" + view.type + "/" + rec.data.name; - }, - callback: function(options, success, response) { - var view = this.up('grid'); - if (!success) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - return; - } - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - taskDone: function() { - view.rstore.load(); - } - }); - win.show(); - } - }, - '-', - { - xtype: 'proxmoxButton', - text: gettext('Syslog'), - disabled: true, - handler: 'showSyslog' - } - ], - - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: function(v) { - return this.type + '.' + v; - }, - dataIndex: 'name' - }, - { - header: gettext('Host'), - flex: 1, - sortable: true, - renderer: function(v) { - return v || Proxmox.Utils.unknownText; - }, - dataIndex: 'host' - }, - { - header: gettext('Status'), - flex: 1, - sortable: false, - dataIndex: 'state' - }, - { - header: gettext('Address'), - flex: 3, - sortable: true, - renderer: function(v) { - return v || Proxmox.Utils.unknownText; - }, - dataIndex: 'addr' - }, - { - header: gettext('Version'), - flex: 3, - sortable: true, - dataIndex: 'version' - } - ], - - initComponent: function() { - var me = this; - - if (me.additionalColumns) { - me.columns = me.columns.concat(me.additionalColumns); - } - - me.callParent(); - } - -}, function() { - - Ext.define('ceph-service-list', { - extend: 'Ext.data.Model', - fields: [ 'addr', 'name', 'rank', 'host', 'quorum', 'state', - 'ceph_version', 'ceph_version_short', - { type: 'string', name: 'version', calculate: function(data) { - return PVE.Utils.parse_ceph_version(data); - } } - ], - idProperty: 'name' - }); -}); -/*jslint confusion: true */ -Ext.define('PVE.CephCreateFS', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreateFS', - - showTaskViewer: true, - onlineHelp: 'pveceph_fs_create', - - subject: 'Ceph FS', - isCreate: true, - method: 'POST', - - setFSName: function(fsName) { - var me = this; - - if (fsName === '' || fsName === undefined) { - fsName = 'cephfs'; - } - - me.url = "/nodes/" + me.nodename + "/ceph/fs/" + fsName; - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - value: 'cephfs', - listeners: { - change: function(f, value) { - this.up('pveCephCreateFS').setFSName(value); - } - }, - submitValue: false, // already encoded in apicall URL - emptyText: 'cephfs' - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'Placement Groups', - name: 'pg_num', - value: 128, - emptyText: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add as Storage'), - value: true, - name: 'add-storage', - autoEl: { - tag: 'div', - 'data-qtip': gettext('Add the new CephFS to the cluster storage configuration.'), - }, - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.setFSName(); - - me.callParent(); - } -}); - -Ext.define('PVE.NodeCephFSPanel', { - extend: 'Ext.panel.Panel', - xtype: 'pveNodeCephFSPanel', - mixins: ['Proxmox.Mixin.CBind'], - - title: gettext('CephFS'), - onlineHelp: 'pveceph_fs', - - border: false, - defaults: { - border: false, - cbind: { - nodename: '{nodename}' - } - }, - - viewModel: { - parent: null, - data: { - cephfsConfigured: false, - mdsCount: 0 - }, - formulas: { - canCreateFS: function(get) { - return (!get('cephfsConfigured') && get('mdsCount') > 0); - } - } - }, - - items: [ - { - xtype: 'grid', - emptyText: Ext.String.format(gettext('No {0} configured.'), 'CephFS'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-ceph-fs', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + view.nodename + '/ceph/fs' - }, - model: 'pve-ceph-fs' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'name', - order: 'DESC' - } - })); - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){ - me.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.rstore.startUpdate(); - }); - } - ); - }); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onCreate: function() { - var view = this.getView(); - view.rstore.stopUpdate(); - var win = Ext.create('PVE.CephCreateFS', { - autoShow: true, - nodename: view.nodename, - listeners: { - destroy: function() { - view.rstore.startUpdate(); - } - } - }); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!(success && records && records.length > 0)) { - vm.set('cephfsConfigured', false); - return; - } - vm.set('cephfsConfigured', true); - } - }, - tbar: [ - { - text: gettext('Create CephFS'), - reference: 'createButton', - handler: 'onCreate', - bind: { - // only one CephFS per Ceph cluster makes sense for now - disabled: '{!canCreateFS}' - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - dataIndex: 'name' - }, - { - header: 'Data Pool', - flex: 1, - dataIndex: 'data_pool' - }, - { - header: 'Metadata Pool', - flex: 1, - dataIndex: 'metadata_pool' - } - ], - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'pveNodeCephServiceList', - title: gettext('Metadata Servers'), - stateId: 'grid-ceph-mds', - type: 'mds', - storeLoadCallback: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records) { - vm.set('mdsCount', 0); - return; - } - vm.set('mdsCount', records.length); - }, - cbind: { - nodename: '{nodename}' - } - } - ] -}, function() { - Ext.define('pve-ceph-fs', { - extend: 'Ext.data.Model', - fields: [ 'name', 'data_pool', 'metadata_pool' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/localhost/ceph/fs" - }, - idProperty: 'name' - }); -}); -Ext.define('PVE.CephCreatePool', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreatePool', - - showProgress: true, - onlineHelp: 'pve_ceph_pools', - - subject: 'Ceph Pool', - isCreate: true, - method: 'POST', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Size'), - name: 'size', - value: 3, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Min. Size'), - name: 'min_size', - value: 2, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'pveCephRuleSelector', - fieldLabel: 'Crush Rule', // do not localize - name: 'crush_rule', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'pg_num', - name: 'pg_num', - value: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add as Storage'), - value: true, - name: 'add_storages', - autoEl: { - tag: 'div', - 'data-qtip': gettext('Add the new pool to the cluster storage configuration.'), - }, - } - ], - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: "/nodes/" + me.nodename + "/ceph/pools", - defaults: { - nodename: me.nodename - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephPoolList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeCephPoolList', - - onlineHelp: 'chapter_pveceph', - - stateful: true, - stateId: 'grid-ceph-pools', - bufferedRenderer: false, - - features: [ { ftype: 'summary'} ], - - columns: [ - { - header: gettext('Name'), - width: 120, - sortable: true, - dataIndex: 'pool_name' - }, - { - header: gettext('Size') + '/min', - width: 100, - align: 'right', - renderer: function(v, meta, rec) { - return v + '/' + rec.data.min_size; - }, - dataIndex: 'size' - }, - { - text: '# Placement Groups', // pg_num', - width: 180, - align: 'right', - dataIndex: 'pg_num' - }, - { - text: 'CRUSH Rule', - columns: [ - { - text: 'ID', - align: 'right', - width: 50, - dataIndex: 'crush_rule' - }, - { - text: gettext('Name'), - width: 150, - dataIndex: 'crush_rule_name', - }, - ] - }, - { - text: gettext('Used'), - columns: [ - { - text: '%', - width: 100, - sortable: true, - align: 'right', - renderer: function(val) { - return Ext.util.Format.percent(val, '0.00'); - }, - dataIndex: 'percent_used', - summaryType: 'sum', - summaryRenderer: function(val) { - return Ext.util.Format.percent(val, '0.00'); - }, - }, - { - text: gettext('Total'), - width: 100, - sortable: true, - renderer: PVE.Utils.render_size, - align: 'right', - dataIndex: 'bytes_used', - summaryType: 'sum', - summaryRenderer: PVE.Utils.render_size - } - ] - } - ], - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'ceph-pool-list' + nodename, - model: 'ceph-pool-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/ceph/pools" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore }); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ - me.store.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.rstore.startUpdate(); - }); - } - ); - }); - - var create_btn = new Ext.Button({ - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.CephCreatePool', { - nodename: nodename - }); - win.show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - var destroy_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Destroy'), - selModel: sm, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - if (!rec.data.pool_name) { - return; - } - var base_url = '/nodes/' + nodename + '/ceph/pools/' + - rec.data.pool_name; - - var win = Ext.create('PVE.window.SafeDestroy', { - showProgress: true, - url: base_url, - params: { - remove_storages: 1 - }, - item: { type: 'CephPool', id: rec.data.pool_name } - }).show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ create_btn, destroy_btn ], - listeners: { - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('ceph-pool-list', { - extend: 'Ext.data.Model', - fields: [ 'pool_name', - { name: 'pool', type: 'integer'}, - { name: 'size', type: 'integer'}, - { name: 'min_size', type: 'integer'}, - { name: 'pg_num', type: 'integer'}, - { name: 'bytes_used', type: 'integer'}, - { name: 'percent_used', type: 'number'}, - { name: 'crush_rule', type: 'integer'}, - { name: 'crush_rule_name', type: 'string'} - ], - idProperty: 'pool_name' - }); -}); - -Ext.define('PVE.form.CephRuleSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephRuleSelector', - - allowBlank: false, - valueField: 'name', - displayField: 'name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/rules' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.CephCreateOsd', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCephCreateOsd', - - subject: 'Ceph OSD', - - showProgress: true, - - onlineHelp: 'pve_ceph_osds', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd", - method: 'POST', - items: [ - { - xtype: 'inputpanel', - onGetValues: function(values) { - Object.keys(values || {}).forEach(function(name) { - if (values[name] === '') { - delete values[name]; - } - }); - - return values; - }, - column1: [ - { - xtype: 'pveDiskSelector', - name: 'dev', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - } - ], - column2: [ - { - xtype: 'pveDiskSelector', - name: 'db_dev', - nodename: me.nodename, - diskType: 'journal_disks', - fieldLabel: gettext('DB Disk'), - value: '', - autoSelect: false, - allowBlank: true, - emptyText: 'use OSD disk', - listeners: { - change: function(field, val) { - me.down('field[name=db_size]').setDisabled(!val); - } - } - }, - { - xtype: 'numberfield', - name: 'db_size', - fieldLabel: gettext('DB size') + ' (GiB)', - minValue: 1, - maxValue: 128*1024, - decimalPrecision: 2, - allowBlank: true, - disabled: true, - emptyText: gettext('Automatic') - } - ], - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'encrypted', - fieldLabel: gettext('Encrypt OSD') - }, - ], - advancedColumn2: [ - { - xtype: 'pveDiskSelector', - name: 'wal_dev', - nodename: me.nodename, - diskType: 'journal_disks', - fieldLabel: gettext('WAL Disk'), - value: '', - autoSelect: false, - allowBlank: true, - emptyText: 'use OSD/DB disk', - listeners: { - change: function(field, val) { - me.down('field[name=wal_size]').setDisabled(!val); - } - } - }, - { - xtype: 'numberfield', - name: 'wal_size', - fieldLabel: gettext('WAL size') + ' (GiB)', - minValue: 0.5, - maxValue: 128*1024, - decimalPrecision: 2, - allowBlank: true, - disabled: true, - emptyText: gettext('Automatic') - } - ] - }, - { - xtype: 'displayfield', - padding: '5 0 0 0', - userCls: 'pve-hint', - value: 'Note: Ceph is not compatible with disks backed by a hardware ' + - 'RAID controller. For details see ' + - 'the reference documentation.', - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.CephRemoveOsd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephRemoveOsd'], - - isRemove: true, - - showProgress: true, - method: 'DELETE', - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'cleanup', - checked: true, - labelWidth: 130, - fieldLabel: gettext('Cleanup Disks') - } - ], - initComponent : function() { - - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - if (me.osdid === undefined || me.osdid < 0) { - throw "no osdid specified"; - } - - me.isCreate = true; - - me.title = gettext('Destroy') + ': Ceph OSD osd.' + me.osdid.toString(); - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd/" + me.osdid.toString() - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephOsdTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveNodeCephOsdTree'], - onlineHelp: 'chapter_pveceph', - - viewModel: { - data: { - nodename: '', - flags: [], - maxversion: '0', - versions: {}, - isOsd: false, - downOsd: false, - upOsd: false, - inOsd: false, - outOsd: false, - osdid: '', - osdhost: '', - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - - reload: function() { - var me = this.getView(); - var vm = this.getViewModel(); - var nodename = vm.get('nodename'); - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/ceph/osd", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me, msg, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', this.reload); - } - ); - }, - success: function(response, opts) { - var data = response.result.data; - var selected = me.getSelection(); - var name; - if (selected.length) { - name = selected[0].data.name; - } - vm.set('versions', data.versions); - // extract max version - var maxversion = vm.get('maxversion'); - Object.values(data.versions || {}).forEach(function(version) { - if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) { - maxversion = version; - } - }); - vm.set('maxversion', maxversion); - sm.deselectAll(); - me.setRootNode(data.root); - me.expandAll(); - if (name) { - var node = me.getRootNode().findChild('name', name, true); - if (node) { - me.setSelection([node]); - } - } - - var flags = data.flags.split(','); - vm.set('flags', flags); - var noout = flags.includes('noout'); - me.down('#nooutBtn').setText(noout ? gettext("Unset noout") : gettext("Set noout")); - } - }); - }, - - osd_cmd: function(comp) { - var me = this; - var vm = this.getViewModel(); - var cmd = comp.cmd; - var params = comp.params || {}; - var osdid = vm.get('osdid'); - - var doRequest = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + vm.get('osdhost') + "/ceph/osd/" + osdid + '/' + cmd, - waitMsgTarget: me.getView(), - method: 'POST', - params: params, - success: () => { me.reload(); }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - if (cmd === 'scrub') { - Ext.MessageBox.defaultButton = params.deep === 1 ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: params.deep === 1 ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: params.deep !== 1 ? - Ext.String.format(gettext("Scrub OSD.{0}"), osdid) : - Ext.String.format(gettext("Deep Scrub OSD.{0}"), osdid) + - "
Caution: This can reduce performance while it is running.", - buttons: Ext.Msg.YESNO, - callback: function(btn) { - if (btn !== 'yes') { - return; - } - doRequest(); - } - }); - } else { - doRequest(); - } - }, - - create_osd: function() { - var me = this; - var vm = this.getViewModel(); - Ext.create('PVE.CephCreateOsd', { - nodename: vm.get('nodename'), - taskDone: () => { me.reload(); } - }).show(); - }, - - destroy_osd: function() { - var me = this; - var vm = this.getViewModel(); - Ext.create('PVE.CephRemoveOsd', { - nodename: vm.get('osdhost'), - osdid: vm.get('osdid'), - taskDone: () => { me.reload(); } - }).show(); - }, - - set_flag: function() { - var me = this; - var vm = this.getViewModel(); - var flags = vm.get('flags'); - Proxmox.Utils.API2Request({ - url: "/nodes/" + vm.get('nodename') + "/ceph/flags/noout", - waitMsgTarget: me.getView(), - method: flags.includes('noout') ? 'DELETE' : 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: () => { me.reload(); } - }); - }, - - service_cmd: function(comp) { - var me = this; - var vm = this.getViewModel(); - var cmd = comp.cmd || comp; - Proxmox.Utils.API2Request({ - url: "/nodes/" + vm.get('osdhost') + "/ceph/" + cmd, - params: { service: "osd." + vm.get('osdid') }, - waitMsgTarget: me.getView(), - method: 'POST', - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - taskDone: () => { me.reload(); } - }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - set_selection_status: function(tp, selection) { - if (selection.length < 1) { - return; - } - var rec = selection[0]; - var vm = this.getViewModel(); - - var isOsd = (rec.data.host && (rec.data.type === 'osd') && (rec.data.id >= 0)); - - vm.set('isOsd', isOsd); - vm.set('downOsd', isOsd && rec.data.status === 'down'); - vm.set('upOsd', isOsd && rec.data.status !== 'down'); - vm.set('inOsd', isOsd && rec.data.in); - vm.set('outOsd', isOsd && !rec.data.in); - vm.set('osdid', isOsd ? rec.data.id : undefined); - vm.set('osdhost', isOsd ? rec.data.host : undefined); - }, - - render_status: function(value, metaData, rec) { - if (!value) { - return value; - } - var inout = rec.data['in'] ? 'in' : 'out'; - var updownicon = value === 'up' ? 'good fa-arrow-circle-up' : - 'critical fa-arrow-circle-down'; - - var inouticon = rec.data['in'] ? 'good fa-circle' : - 'warning fa-circle-o'; - - var text = value + ' / ' + - inout + ' '; - - return text; - }, - - render_wal: function(value, metaData, rec) { - if (!value && - rec.data.osdtype === 'bluestore' && - rec.data.type === 'osd') { - return 'N/A'; - } - return value; - }, - - render_version: function(value, metadata, rec) { - var vm = this.getViewModel(); - var versions = vm.get('versions'); - var icon = ""; - var version = value || ""; - if (value && value != vm.get('maxversion')) { - icon = PVE.Utils.get_ceph_icon_html('HEALTH_OLD'); - } - - if (!value && rec.data.type == 'host') { - version = versions[rec.data.name] || Proxmox.Utils.unknownText; - } - - return icon + version; - }, - - render_osd_val: function(value, metaData, rec) { - return (rec.data.type === 'osd') ? value : ''; - }, - render_osd_weight: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return Ext.util.Format.number(value, '0.00###'); - }, - - render_osd_latency: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - let commit_ms = rec.data.commit_latency_ms, - apply_ms = rec.data.apply_latency_ms; - return apply_ms + ' / ' + commit_ms; - }, - - render_osd_size: function(value, metaData, rec) { - return this.render_osd_val(PVE.Utils.render_size(value), metaData, rec); - }, - - control: { - '#': { - selectionchange: 'set_selection_status' - } - }, - - init: function(view) { - var me = this; - var vm = this.getViewModel(); - - if (!view.pveSelNode.data.node) { - throw "no node name specified"; - } - - vm.set('nodename', view.pveSelNode.data.node); - - me.callParent(); - me.reload(); - } - }, - - stateful: true, - stateId: 'grid-ceph-osd', - rootVisible: false, - useArrows: true, - - columns: [ - { - xtype: 'treecolumn', - text: 'Name', - dataIndex: 'name', - width: 150 - }, - { - text: 'Type', - dataIndex: 'type', - hidden: true, - align: 'right', - width: 75 - }, - { - text: gettext("Class"), - dataIndex: 'device_class', - align: 'right', - width: 75 - }, - { - text: "OSD Type", - dataIndex: 'osdtype', - align: 'right', - width: 100 - }, - { - text: "Bluestore Device", - dataIndex: 'blfsdev', - align: 'right', - width: 75, - hidden: true - }, - { - text: "DB Device", - dataIndex: 'dbdev', - align: 'right', - width: 75, - hidden: true - }, - { - text: "WAL Device", - dataIndex: 'waldev', - align: 'right', - renderer: 'render_wal', - width: 75, - hidden: true - }, - { - text: 'Status', - dataIndex: 'status', - align: 'right', - renderer: 'render_status', - width: 120 - }, - { - text: gettext('Version'), - dataIndex: 'version', - align: 'right', - renderer: 'render_version' - }, - { - text: 'weight', - dataIndex: 'crush_weight', - align: 'right', - renderer: 'render_osd_weight', - width: 90 - }, - { - text: 'reweight', - dataIndex: 'reweight', - align: 'right', - renderer: 'render_osd_weight', - width: 90 - }, - { - text: gettext('Used') + ' (%)', - dataIndex: 'percent_used', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return Ext.util.Format.number(value, '0.00'); - }, - width: 100 - }, - { - text: gettext('Total'), - dataIndex: 'total_space', - align: 'right', - renderer: 'render_osd_size', - width: 100 - }, - { - text: 'Apply/Commit
Latency (ms)', - dataIndex: 'apply_latency_ms', - align: 'right', - renderer: 'render_osd_latency', - width: 120 - } - ], - - - tbar: { - items: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: 'reload' - }, - '-', - { - text: gettext('Create') + ': OSD', - handler: 'create_osd', - }, - { - text: gettext('Set noout'), - itemId: 'nooutBtn', - handler: 'set_flag', - }, - '->', - { - xtype: 'tbtext', - data: { - osd: undefined - }, - bind: { - data: { - osd: "{osdid}" - } - }, - tpl: [ - '', - 'osd.{osd}:', - '', - gettext('No OSD selected'), - '' - ] - }, - { - text: gettext('Start'), - iconCls: 'fa fa-play', - disabled: true, - bind: { - disabled: '{!downOsd}' - }, - cmd: 'start', - handler: 'service_cmd' - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-stop', - disabled: true, - bind: { - disabled: '{!upOsd}' - }, - cmd: 'stop', - handler: 'service_cmd' - }, - { - text: gettext('Restart'), - iconCls: 'fa fa-refresh', - disabled: true, - bind: { - disabled: '{!upOsd}' - }, - cmd: 'restart', - handler: 'service_cmd' - }, - '-', - { - text: 'Out', - iconCls: 'fa fa-circle-o', - disabled: true, - bind: { - disabled: '{!inOsd}' - }, - cmd: 'out', - handler: 'osd_cmd' - }, - { - text: 'In', - iconCls: 'fa fa-circle', - disabled: true, - bind: { - disabled: '{!outOsd}' - }, - cmd: 'in', - handler: 'osd_cmd' - }, - '-', - { - text: gettext('More'), - iconCls: 'fa fa-bars', - disabled: true, - bind: { - disabled: '{!isOsd}' - }, - menu: [ - { - text: gettext('Scrub'), - iconCls: 'fa fa-shower', - cmd: 'scrub', - handler: 'osd_cmd' - }, - { - text: gettext('Deep Scrub'), - iconCls: 'fa fa-bath', - cmd: 'scrub', - params: { - deep: 1, - }, - handler: 'osd_cmd' - }, - { - text: gettext('Destroy'), - itemId: 'remove', - iconCls: 'fa fa-fw fa-trash-o', - bind: { - disabled: '{!downOsd}' - }, - handler: 'destroy_osd' - } - ], - } - ] - }, - - fields: [ - 'name', 'type', 'status', 'host', 'in', 'id' , - { type: 'number', name: 'reweight' }, - { type: 'number', name: 'percent_used' }, - { type: 'integer', name: 'bytes_used' }, - { type: 'integer', name: 'total_space' }, - { type: 'integer', name: 'apply_latency_ms' }, - { type: 'integer', name: 'commit_latency_ms' }, - { type: 'string', name: 'device_class' }, - { type: 'string', name: 'osdtype' }, - { type: 'string', name: 'blfsdev' }, - { type: 'string', name: 'dbdev' }, - { type: 'string', name: 'waldev' }, - { type: 'string', name: 'version', calculate: function(data) { - return PVE.Utils.parse_ceph_version(data); - } }, - { type: 'string', name: 'iconCls', calculate: function(data) { - var iconMap = { - host: 'fa-building', - osd: 'fa-hdd-o', - root: 'fa-server', - }; - return 'fa x-fa-tree ' + iconMap[data.type]; - } }, - { type: 'number', name: 'crush_weight' } - ], -}); -Ext.define('PVE.node.CephMonMgrList', { - extend: 'Ext.container.Container', - xtype: 'pveNodeCephMonMgr', - - mixins: ['Proxmox.Mixin.CBind' ], - - onlineHelp: 'chapter_pveceph', - - defaults: { - border: false, - onlineHelp: 'chapter_pveceph', - flex: 1 - }, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - items: [ - { - xtype: 'pveNodeCephServiceList', - cbind: { pveSelNode: '{pveSelNode}' }, - type: 'mon', - additionalColumns: [ - { - header: gettext('Quorum'), - width: 70, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'quorum' - } - ], - stateId: 'grid-ceph-monitor', - showCephInstallMask: true, - title: gettext('Monitor') - }, - { - xtype: 'pveNodeCephServiceList', - type: 'mgr', - stateId: 'grid-ceph-manager', - cbind: { pveSelNode: '{pveSelNode}' }, - title: gettext('Manager') - } - ] -}); -Ext.define('PVE.node.CephCrushMap', { - extend: 'Ext.panel.Panel', - alias: ['widget.pveNodeCephCrushMap'], - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - stateful: true, - stateId: 'layout-ceph-crush', - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/crush', - - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.node.CephStatus', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephStatus', - - onlineHelp: 'chapter_pveceph', - - scrollable: true, - - bodyPadding: 5, - - layout: { - type: 'column' - }, - - defaults: { - padding: 5 - }, - - items: [ - { - xtype: 'panel', - title: gettext('Health'), - bodyPadding: 10, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - minHeight: 230, - columnWidth: 1 - }, - 'width >= 1900': { - minHeight: 500, - columnWidth: 0.5 - } - }, - layout: { - type: 'hbox', - align: 'stretch' - }, - items: [ - { - flex: 1, - itemId: 'overallhealth', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - flex: 2, - itemId: 'warnings', - stateful: true, - stateId: 'ceph-status-warnings', - xtype: 'grid', - // since we load the store manually, - // to show the emptytext, we have to - // specify an empty store - store: { data:[] }, - emptyText: gettext('No Warnings/Errors'), - columns: [ - { - dataIndex: 'severity', - header: gettext('Severity'), - align: 'center', - width: 70, - renderer: function(value) { - var health = PVE.Utils.map_ceph_health[value]; - var classes = PVE.Utils.get_health_icon(health); - - return ''; - }, - sorter: { - sorterFn: function(a,b) { - var healthArr = ['HEALTH_ERR', 'HEALTH_WARN', 'HEALTH_OK']; - return healthArr.indexOf(b.data.severity) - healthArr.indexOf(a.data.severity); - } - } - }, - { - dataIndex: 'summary', - header: gettext('Summary'), - flex: 1 - }, - { - xtype: 'actioncolumn', - width: 40, - align: 'center', - tooltip: gettext('Detail'), - items: [ - { - iconCls: 'x-fa fa-info-circle', - handler: function(grid, rowindex, colindex, item, e, record) { - var win = Ext.create('Ext.window.Window', { - title: gettext('Detail'), - resizable: true, - modal: true, - width: 650, - height: 400, - layout: { - type: 'fit' - }, - items: [{ - scrollable: true, - padding: 10, - xtype: 'box', - html: [ - '' + Ext.htmlEncode(record.data.summary) + '', - '
' + Ext.htmlEncode(record.data.detail) + '
' - ] - }] - }); - win.show(); - } - } - ] - } - ] - } - ] - }, - { - xtype: 'pveCephStatusDetail', - itemId: 'statusdetail', - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1, - minHeight: 250 - }, - 'width >= 1900': { - columnWidth: 0.5, - minHeight: 300 - } - }, - title: gettext('Status') - }, - { - title: gettext('Services'), - xtype: 'pveCephServices', - itemId: 'services', - plugins: 'responsive', - layout: { - type: 'hbox', - align: 'stretch' - }, - responsiveConfig: { - 'width < 1900': { - columnWidth: 1, - minHeight: 200 - }, - 'width >= 1900': { - columnWidth: 0.5, - minHeight: 200 - } - } - }, - { - xtype: 'panel', - title: gettext('Performance'), - columnWidth: 1, - bodyPadding: 5, - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - flex: 1, - xtype: 'proxmoxGauge', - itemId: 'space', - title: gettext('Usage') - }, - { - flex: 2, - xtype: 'container', - defaults: { - padding: 0, - height: 100 - }, - items: [ - { - itemId: 'reads', - xtype: 'pveRunningChart', - title: gettext('Reads'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'writes', - xtype: 'pveRunningChart', - title: gettext('Writes'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'iops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS', // do not localize - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'readiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Reads'), - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'writeiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Writes'), - renderer: Ext.util.Format.numberRenderer('0,000') - } - ] - } - ] - } - ], - - generateCheckData: function(health) { - var result = []; - var checks = health.checks || {}; - var keys = Ext.Object.getKeys(checks).sort(); - - Ext.Array.forEach(keys, function(key) { - var details = checks[key].detail || []; - result.push({ - id: key, - summary: checks[key].summary.message, - detail: Ext.Array.reduce( - checks[key].detail, - function(first, second) { - return first + '\n' + second.message; - }, - '' - ), - severity: checks[key].severity - }); - }); - - return result; - }, - - updateAll: function(store, records, success) { - if (!success || records.length === 0) { - return; - } - - var me = this; - var rec = records[0]; - me.status = rec.data; - - // add health panel - me.down('#overallhealth').updateHealth(PVE.Utils.render_ceph_health(rec.data.health || {})); - // add errors to gridstore - me.down('#warnings').getStore().loadRawData(me.generateCheckData(rec.data.health || {}), false); - - // update services - me.getComponent('services').updateAll(me.metadata || {}, rec.data); - - // update detailstatus panel - me.getComponent('statusdetail').updateAll(me.metadata || {}, rec.data); - - // add performance data - var used = rec.data.pgmap.bytes_used; - var total = rec.data.pgmap.bytes_total; - - var text = Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(used), - PVE.Utils.render_size(total) - ); - - // update the usage widget - me.down('#space').updateValue(used/total, text); - - // TODO: logic for jewel (iops split in read/write) - - var iops = rec.data.pgmap.op_per_sec; - var readiops = rec.data.pgmap.read_op_per_sec; - var writeiops = rec.data.pgmap.write_op_per_sec; - var reads = rec.data.pgmap.read_bytes_sec || 0; - var writes = rec.data.pgmap.write_bytes_sec || 0; - - if (iops !== undefined && me.version !== 'hammer') { - me.change_version('hammer'); - } else if((readiops !== undefined || writeiops !== undefined) && me.version !== 'jewel') { - me.change_version('jewel'); - } - // update the graphs - me.reads.addDataPoint(reads); - me.writes.addDataPoint(writes); - me.iops.addDataPoint(iops); - me.readiops.addDataPoint(readiops); - me.writeiops.addDataPoint(writeiops); - }, - - change_version: function(version) { - var me = this; - me.version = version; - me.sp.set('ceph-version', version); - me.iops.setVisible(version === 'hammer'); - me.readiops.setVisible(version === 'jewel'); - me.writeiops.setVisible(version === 'jewel'); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - - me.callParent(); - var baseurl = '/api2/json' + (nodename ? '/nodes/' + nodename : '/cluster') + '/ceph'; - me.store = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + (nodename || 'cluster'), - interval: 5000, - proxy: { - type: 'proxmox', - url: baseurl + '/status' - } - }); - - me.metadatastore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-metadata-' + (nodename || 'cluster'), - interval: 15*1000, - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/ceph/metadata' - } - }); - - // save references for the updatefunction - me.iops = me.down('#iops'); - me.readiops = me.down('#readiops'); - me.writeiops = me.down('#writeiops'); - me.reads = me.down('#reads'); - me.writes = me.down('#writes'); - - // get ceph version - me.sp = Ext.state.Manager.getProvider(); - me.version = me.sp.get('ceph-version'); - me.change_version(me.version); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, me.store, regex, function(me, error){ - me.store.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, (nodename || 'localhost'), - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.startUpdate(); - }); - } - ); - }); - - me.mon(me.store, 'load', me.updateAll, me); - me.mon(me.metadatastore, 'load', function(store, records, success) { - if (!success || records.length < 1) { - return; - } - var rec = records[0]; - me.metadata = rec.data; - - // update services - me.getComponent('services').updateAll(rec.data, me.status || {}); - - // update detailstatus panel - me.getComponent('statusdetail').updateAll(rec.data, me.status || {}); - - }, me); - - me.on('destroy', me.store.stopUpdate); - me.on('destroy', me.metadatastore.stopUpdate); - me.store.startUpdate(); - me.metadatastore.startUpdate(); - } - -}); -Ext.define('PVE.ceph.StatusDetail', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveCephStatusDetail', - - layout: { - type: 'hbox', - align: 'stretch' - }, - - bodyPadding: '0 5', - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [{ - flex: 1, - itemId: 'osds', - maxHeight: 250, - scrollable: true, - padding: '0 10 5 10', - data: { - total: 0, - upin: 0, - upout: 0, - downin: 0, - downout: 0, - oldosds: [] - }, - tpl: [ - '

' + 'OSDs' + '

', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '
', - gettext('In'), - '', - gettext('Out'), - '
', - gettext('Up'), - '{upin}{upout}
', - gettext('Down'), - '{downin}{downout}
', - '
', - gettext('Total'), - ': {total}', - '

', - '', - ' ' + gettext('Outdated OSDs') + "
", - '
', - '', - '
osd.{id}:
', - '
{version}

', - '
', - '
', - '
', - '
' - ] - }, - { - flex: 1, - border: false, - itemId: 'pgchart', - xtype: 'polar', - height: 184, - innerPadding: 5, - insetPadding: 5, - colors: [ - '#CFCFCF', - '#21BF4B', - '#FFCC00', - '#FF6C59' - ], - store: { }, - series: [ - { - type: 'pie', - donut: 60, - angleField: 'count', - tooltip: { - trackMouse: true, - renderer: function(tooltip, record, ctx) { - var html = record.get('text'); - html += '
'; - record.get('states').forEach(function(state) { - html += '
' + - state.state_name + ': ' + state.count.toString(); - }); - tooltip.setHtml(html); - } - }, - subStyle: { - strokeStyle: false - } - } - ] - }, - { - flex: 1.6, - itemId: 'pgs', - padding: '0 10', - maxHeight: 250, - scrollable: true, - data: { - states: [] - }, - tpl: [ - '

' + 'PGs' + '

', - '', - '
{state_name}:
', - '
{count}

', - '
', - '
' - ] - }], - - // similar to mgr dashboard - pgstates: { - // clean - clean: 1, - active: 1, - - // working - activating: 2, - backfill_wait: 2, - backfilling: 2, - creating: 2, - deep: 2, - degraded: 2, - forced_backfill: 2, - forced_recovery: 2, - peered: 2, - peering: 2, - recovering: 2, - recovery_wait: 2, - repair: 2, - scrubbing: 2, - snaptrim: 2, - snaptrim_wait: 2, - - // error - backfill_toofull: 3, - backfill_unfound: 3, - down: 3, - incomplete: 3, - inconsistent: 3, - recovery_toofull: 3, - recovery_unfound: 3, - remapped: 3, - snaptrim_error: 3, - stale: 3, - undersized: 3 - }, - - statecategories: [ - { - text: gettext('Unknown'), - count: 0, - states: [], - cls: 'faded' - }, - { - text: gettext('Clean'), - cls: 'good' - }, - { - text: gettext('Working'), - cls: 'warning' - }, - { - text: gettext('Error'), - cls: 'critical' - } - ], - - updateAll: function(metadata, status) { - var me = this; - me.suspendLayout = true; - - var maxversion = "0"; - Object.values(metadata.version || {}).forEach(function(version) { - if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) { - maxversion = version; - } - }); - - var oldosds = []; - - if (metadata.osd) { - metadata.osd.forEach(function(osd) { - var version = PVE.Utils.parse_ceph_version(osd); - if (version != maxversion) { - oldosds.push({ - id: osd.id, - version: version - }); - } - }); - } - - var pgmap = status.pgmap || {}; - var health = status.health || {}; - var osdmap = status.osdmap || { osdmap: {} }; - - - // update pgs sorted - var pgs_by_state = pgmap.pgs_by_state || []; - pgs_by_state.sort(function(a,b){ - return (a.state_name < b.state_name)?-1:(a.state_name === b.state_name)?0:1; - }); - - me.statecategories.forEach(function(cat) { - cat.count = 0; - cat.states = []; - }); - - pgs_by_state.forEach(function(state) { - var i; - var states = state.state_name.split(/[^a-z]+/); - var result = 0; - for (i = 0; i < states.length; i++) { - if (me.pgstates[states[i]] > result) { - result = me.pgstates[states[i]]; - } - } - // for the list - state.cls = me.statecategories[result].cls; - - me.statecategories[result].count += state.count; - me.statecategories[result].states.push(state); - }); - - me.getComponent('pgchart').getStore().setData(me.statecategories); - me.getComponent('pgs').update({states: pgs_by_state}); - - var downinregex = /(\d+) osds down/; - var downin_osds = 0; - - // we collect monitor/osd information from the checks - Ext.Object.each(health.checks, function(key, value, obj) { - var found = null; - if (key === 'OSD_DOWN') { - found = value.summary.message.match(downinregex); - if (found !== null) { - downin_osds = parseInt(found[1],10); - } - } - }); - - // update osds counts - - var total_osds = osdmap.osdmap.num_osds || 0; - var in_osds = osdmap.osdmap.num_in_osds || 0; - var up_osds = osdmap.osdmap.num_up_osds || 0; - var out_osds = total_osds - in_osds; - var down_osds = total_osds - up_osds; - - var downout_osds = down_osds - downin_osds; - var upin_osds = in_osds - downin_osds; - var upout_osds = up_osds - upin_osds; - var osds = { - total: total_osds, - upin: upin_osds, - upout: upout_osds, - downin: downin_osds, - downout: downout_osds, - oldosds: oldosds - }; - var osdcomponent = me.getComponent('osds'); - osdcomponent.update(Ext.apply(osdcomponent.data, osds)); - - me.suspendLayout = false; - me.updateLayout(); - } -}); - -Ext.define('PVE.ceph.Services', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveCephServices', - - layout: { - type: 'hbox', - align: 'stretch' - }, - - bodyPadding: '0 5 20', - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [ - { - flex: 1, - xtype: 'pveCephServiceList', - itemId: 'mons', - title: gettext('Monitors') - }, - { - flex: 1, - xtype: 'pveCephServiceList', - itemId: 'mgrs', - title: gettext('Managers') - }, - { - flex: 1, - xtype: 'pveCephServiceList', - itemId: 'mdss', - title: gettext('Meta Data Servers') - } - ], - - updateAll: function(metadata, status) { - var me = this; - - var healthstates = { - 'HEALTH_UNKNOWN': 0, - 'HEALTH_ERR': 1, - 'HEALTH_WARN': 2, - 'HEALTH_OLD': 3, - 'HEALTH_OK': 4 - }; - var healthmap = [ - 'HEALTH_UNKNOWN', - 'HEALTH_ERR', - 'HEALTH_WARN', - 'HEALTH_OLD', - 'HEALTH_OK' - ]; - var reduceFn = function(first, second) { - return first + '\n' + second.message; - }; - var services = ['mon','mgr','mds']; - var maxversion = "00.0.00"; - Object.values(metadata.version || {}).forEach(function(version) { - if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) { - maxversion = version; - } - }); - var i; - var quorummap = (status && status.quorum_names) ? status.quorum_names : []; - var monmessages = {}; - var mgrmessages = {}; - var mdsmessages = {}; - if (status) { - if (status.health) { - Ext.Object.each(status.health.checks, function(key, value, obj) { - if (!Ext.String.startsWith(key, "MON_")) { - return; - } - - var i; - for (i = 0; i < value.detail.length; i++) { - var match = value.detail[i].message.match(/mon.([a-zA-Z0-9\-\.]+)/); - if (!match) { - continue; - } - var monid = match[1]; - - if (!monmessages[monid]) { - monmessages[monid] = { - worstSeverity: healthstates.HEALTH_OK, - messages: [] - }; - } - - - monmessages[monid].messages.push( - PVE.Utils.get_ceph_icon_html(value.severity, true) + - Ext.Array.reduce(value.detail, reduceFn, '') - ); - if (healthstates[value.severity] < monmessages[monid].worstSeverity) { - monmessages[monid].worstSeverity = healthstates[value.severity]; - } - } - }); - } - - if (status.mgrmap) { - mgrmessages[status.mgrmap.active_name] = "active"; - status.mgrmap.standbys.forEach(function(mgr) { - mgrmessages[mgr.name] = "standby"; - }); - } - - if (status.fsmap) { - status.fsmap.by_rank.forEach(function(mds) { - mdsmessages[mds.name] = 'rank: ' + mds.rank + "; " + mds.status; - }); - } - } - - var checks = { - mon: function(mon) { - if (quorummap.indexOf(mon.name) !== -1) { - mon.health = healthstates.HEALTH_OK; - } else { - mon.health = healthstates.HEALTH_ERR; - } - if (monmessages[mon.name]) { - if (monmessages[mon.name].worstSeverity < mon.health) { - mon.health = monmessages[mon.name].worstSeverity; - } - Array.prototype.push.apply(mon.messages, monmessages[mon.name].messages); - } - return mon; - }, - mgr: function(mgr) { - if (mgrmessages[mgr.name] === 'active') { - mgr.title = '' + mgr.title + ''; - mgr.statuses.push(gettext('Status') + ': active'); - } else if (mgrmessages[mgr.name] === 'standby') { - mgr.statuses.push(gettext('Status') + ': standby'); - } else if (mgr.health > healthstates.HEALTH_WARN) { - mgr.health = healthstates.HEALTH_WARN; - } - - return mgr; - }, - mds: function(mds) { - if (mdsmessages[mds.name]) { - mds.title = '' + mds.title + ''; - mds.statuses.push(gettext('Status') + ': ' + mdsmessages[mds.name]+""); - } else if (mds.addr !== Proxmox.Utils.unknownText) { - mds.statuses.push(gettext('Status') + ': standby'); - } - - return mds; - } - }; - - for (i = 0; i < services.length; i++) { - var type = services[i]; - var ids = Object.keys(metadata[type] || {}); - me[type] = {}; - - var j; - for (j = 0; j < ids.length; j++) { - var id = ids[j]; - var tmp = id.split('@'); - var name = tmp[0]; - var host = tmp[1]; - var result = { - id: id, - health: healthstates.HEALTH_OK, - statuses: [], - messages: [], - name: name, - title: metadata[type][id].name || name, - host: host, - version: PVE.Utils.parse_ceph_version(metadata[type][id]), - service: metadata[type][id].service, - addr: metadata[type][id].addr || metadata[type][id].addrs || Proxmox.Utils.unknownText - }; - - result.statuses = [ - gettext('Host') + ": " + result.host, - gettext('Address') + ": " + result.addr - ]; - - if (checks[type]) { - result = checks[type](result); - } - - if (result.service && !result.version) { - result.messages.push( - PVE.Utils.get_ceph_icon_html('HEALTH_UNKNOWN', true) + - gettext('Stopped') - ); - result.health = healthstates.HEALTH_UNKNOWN; - } - - if (!result.version && result.addr === Proxmox.Utils.unknownText) { - result.health = healthstates.HEALTH_UNKNOWN; - } - - if (result.version) { - result.statuses.push(gettext('Version') + ": " + result.version); - - if (result.version != maxversion) { - if (result.health > healthstates.HEALTH_OLD) { - result.health = healthstates.HEALTH_OLD; - } - result.messages.push( - PVE.Utils.get_ceph_icon_html('HEALTH_OLD', true) + - gettext('Not Current Version, please upgrade') - ); - } - } - - result.statuses.push(''); // empty line - result.text = result.statuses.concat(result.messages).join('
'); - - result.health = healthmap[result.health]; - - me[type][id] = result; - } - } - - me.getComponent('mons').updateAll(Object.values(me.mon)); - me.getComponent('mgrs').updateAll(Object.values(me.mgr)); - me.getComponent('mdss').updateAll(Object.values(me.mds)); - } -}); - -Ext.define('PVE.ceph.ServiceList', { - extend: 'Ext.container.Container', - xtype: 'pveCephServiceList', - - style: { - 'text-align':'center' - }, - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [ - { - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}

' - } - ], - - updateAll: function(list) { - var me = this; - me.suspendLayout = true; - - var i; - list.sort(function(a,b) { - return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; - }); - var ids = {}; - if (me.ids) { - me.ids.forEach(function(id) { - ids[id] = true; - }); - } - for (i = 0; i < list.length; i++) { - var service = me.getComponent(list[i].id); - if (!service) { - // since services are already sorted, and - // we always have a sorted list - // we can add it at the service+1 position (because of the title) - service = me.insert(i+1, { - xtype: 'pveCephServiceWidget', - itemId: list[i].id - }); - if (!me.ids) { - me.ids = []; - } - me.ids.push(list[i].id); - } else { - delete ids[list[i].id]; - } - service.updateService(list[i].title, list[i].text, list[i].health); - } - - Object.keys(ids).forEach(function(id) { - me.remove(id); - }); - me.suspendLayout = false; - me.updateLayout(); - }, - - initComponent: function() { - var me = this; - me.callParent(); - me.getComponent('title').update({ - title: me.title - }); - } -}); - -/*jslint confusion: true*/ -Ext.define('PVE.ceph.ServiceWidget', { - extend: 'Ext.Component', - alias: 'widget.pveCephServiceWidget', - - userCls: 'monitor inline-block', - data: { - title: '0', - health: 'HEALTH_ERR', - text: '', - iconCls: PVE.Utils.get_health_icon() - }, - - tpl: [ - '{title}: ', - '' - ], - - updateService: function(title, text, health) { - var me = this; - - me.update(Ext.apply(me.data, { - health: health, - text: text, - title: title, - iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[health]) - })); - - if (me.tooltip) { - me.tooltip.setHtml(text); - } - }, - - listeners: { - destroy: function() { - var me = this; - if (me.tooltip) { - me.tooltip.destroy(); - delete me.tooltip; - } - }, - mouseenter: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (!me) { - return; - } - if (!me.tooltip) { - me.tooltip = Ext.create('Ext.tip.ToolTip', { - target: me.el, - trackMouse: true, - dismissDelay: 0, - renderTo: Ext.getBody(), - html: me.data.text - }); - } - me.tooltip.show(); - } - }, - mouseleave: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (me.tooltip) { - me.tooltip.destroy(); - delete me.tooltip; - } - } - } - } -}); -Ext.define('PVE.node.CephConfigDb', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveNodeCephConfigDb', - - border: false, - store: { - proxy: { - type: 'proxmox' - } - }, - - columns: [ - { - dataIndex: 'section', - text: 'WHO', - width: 100, - }, - { - dataIndex: 'mask', - text: 'MASK', - hidden: true, - width: 80, - }, - { - dataIndex: 'level', - hidden: true, - text: 'LEVEL', - }, - { - dataIndex: 'name', - flex: 1, - text: 'OPTION', - }, - { - dataIndex: 'value', - flex: 1, - text: 'VALUE', - }, - { - dataIndex: 'can_update_at_runtime', - text: 'Runtime Updatable', - hidden: true, - width: 80, - renderer: Proxmox.Utils.format_boolean - }, - ], - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.store.proxy.url = '/api2/json/nodes/' + nodename + '/ceph/configdb'; - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore()); - me.getStore().load(); - } -}); -Ext.define('PVE.node.CephConfig', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfig', - - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/config', - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.CephConfigCrush', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfigCrush', - - onlineHelp: 'chapter_pveceph', - - layout: 'border', - items: [{ - title: gettext('Configuration'), - xtype: 'pveNodeCephConfig', - region: 'center' - }, - { - title: 'Crush Map', // do not localize - xtype: 'pveNodeCephCrushMap', - region: 'east', - split: true, - width: '50%' - }, - { - title: gettext('Configuration Database'), - xtype: 'pveNodeCephConfigDb', - region: 'south', - split: true, - weight: -30, - height: '50%' - }], - - initComponent: function() { - var me = this; - me.defaults = { - pveSelNode: me.pveSelNode - }; - me.callParent(); - } -}); -Ext.define('PVE.ceph.Log', { - extend: 'Proxmox.panel.LogView', - xtype: 'cephLogView', - nodename: undefined, - failCallback: function(response) { - var me = this; - var msg = response.htmlStatus; - var windowShow = PVE.Utils.showCephInstallOrMask(me, msg, me.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.loadTask.delay(200); - }); - } - ); - if (!windowShow) { - Proxmox.Utils.setErrorMask(me, msg); - } - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.CephInstallWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveCephInstallWizard', - mixins: ['Proxmox.Mixin.CBind'], - resizable: false, - nodename: undefined, - viewModel: { - data: { - nodename: '', - configuration: true, - isInstalled: false - } - }, - cbindData: { - nodename: undefined - }, - title: gettext('Setup'), - navigateNext: function() { - var tp = this.down('#wizcontent'); - var atab = tp.getActiveTab(); - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - }, - setInitialTab: function (index) { - var tp = this.down('#wizcontent'); - var initialTab = tp.items.getAt(index); - initialTab.enable(); - tp.setActiveTab(initialTab); - }, - onShow: function() { - this.callParent(arguments); - var isInstalled = this.getViewModel().get('isInstalled'); - if (isInstalled) { - this.getViewModel().set('configuration', false); - this.setInitialTab(2); - } - }, - items: [ - { - title: gettext('Info'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'chapter_pveceph', - html: '

Ceph?

'+ - '

"Ceph is a unified, distributed storage system designed for excellent performance, reliability and scalability."

'+ - '

Ceph is currently not installed on this node, click on the next button below to start the installation.'+ - ' This wizard will guide you through the necessary steps, after the initial installation you will be offered to create an initial configuration.'+ - ' The configuration step is only needed once per cluster and will be skipped if a config is already present.

'+ - '

Please take a look at our documentation, by clicking the help button below, before starting the installation, '+ - 'if you want to gain deeper knowledge about Ceph visit ceph.com.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#back').hide(true); - this.up('pveCephInstallWizard').down('#next').setText(gettext('Start installation')); - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#next').setText(gettext('Next')); - } - } - }, - { - title: gettext('Installation'), - xtype: 'panel', - layout: 'fit', - cbind:{ - nodename: '{nodename}' - }, - viewModel: {}, // needed to inherit parent viewModel data - listeners: { - afterrender: function() { - var me = this; - if (this.getViewModel().get('isInstalled')) { - this.mask("Ceph is already installed, click next to create your configuration.",['pve-static-mask']); - } else { - me.down('pveNoVncConsole').fireEvent('activate'); - } - }, - activate: function() { - var me = this; - var nodename = me.nodename; - me.updateStore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + nodename, - interval: 1000, - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/ceph/status' - }, - listeners: { - load: function(rec, response, success, operation) { - - if (success) { - me.updateStore.stopUpdate(); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("not initialized", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',false); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("rados_connect failed", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',true); - me.down('textfield').setValue('success'); - } else if (!operation.error.statusText.match("not installed", "i")) { - Proxmox.Utils.setErrorMask(me, operation.error.statusText); - } - } - } - }); - me.updateStore.startUpdate(); - }, - destroy: function() { - var me = this; - if (me.updateStore) { - me.updateStore.stopUpdate(); - } - } - }, - items: [ - { - itemId: 'jsconsole', - consoleType: 'cmd', - xtermjs: true, - xtype: 'pveNoVncConsole', - cbind:{ - nodename: '{nodename}' - }, - cmd: 'ceph_install' - }, - { - xtype: 'textfield', - name: 'installSuccess', - value: '', - allowBlank: false, - submitValue: false, - hidden: true - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Configuration'), - onlineHelp: 'chapter_pveceph', - cbind: { - nodename: '{nodename}' - }, - viewModel: { - data: { - replicas: undefined, - minreplicas: undefined - } - }, - listeners: { - activate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Next')); - }, - beforeshow: function() { - if (this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - this.mask("Coniguration already initialized",['pve-static-mask']); - } else { - this.unmask(); - } - }, - deactivate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Finish')); - } - }, - column1: [ - { - xtype: 'displayfield', - value: gettext('Ceph cluster configuration') + ':' - }, - { - xtype: 'proxmoxNetworkSelector', - name: 'network', - value: '', - fieldLabel: 'Public Network IP/CIDR', - bind: { - allowBlank: '{configuration}' - } - }, - { - xtype: 'proxmoxNetworkSelector', - name: 'cluster-network', - fieldLabel: 'Cluster Network IP/CIDR', - allowBlank: true, - autoSelect: false, - emptyText: gettext('Same as Public Network') - } - // FIXME: add hint about cluster network and/or reference user to docs?? - ], - column2: [ - { - xtype: 'displayfield', - value: gettext('First Ceph monitor') + ':' - }, - { - xtype: 'pveNodeSelector', - fieldLabel: gettext('Monitor node'), - name: 'mon-node', - selectCurNode: true, - allowBlank: false - }, - { - xtype: 'displayfield', - value: gettext('Additional monitors are recommended. They can be created at any time in the Monitor tab.'), - userCls: 'pve-hint' - } - ], - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'size', - fieldLabel: 'Number of replicas', - bind: { - value: '{replicas}' - }, - maxValue: 7, - minValue: 2, - emptyText: '3' - }, - { - xtype: 'numberfield', - name: 'min_size', - fieldLabel: 'Minimum replicas', - bind: { - maxValue: '{replicas}', - value: '{minreplicas}' - }, - minValue: 2, - maxValue: 3, - setMaxValue: function(value) { - this.maxValue = Ext.Number.from(value, 2); - // allow enough to avoid split brains with max 'size', but more makes simply no sense - if (this.maxValue > 4) { - this.maxValue = 4; - } - this.toggleSpinners(); - this.validate(); - }, - emptyText: '2' - } - ], - onGetValues: function(values) { - ['cluster-network', 'size', 'min_size'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - return values; - }, - onSubmit: function() { - var me = this; - if (!this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - var wizard = me.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - var monNode = kv['mon-node']; - delete kv['mon-node']; - var nodename = me.nodename; - delete kv.nodename; - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/ceph/init', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + monNode + '/ceph/mon/' + monNode, - waitMsgTarget: wizard, - method: 'POST', - success: function() { - me.up('pveCephInstallWizard').navigateNext(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - - } else { - me.up('pveCephInstallWizard').navigateNext(); - } - } - }, - { - title: gettext('Success'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'pve_ceph_install', - html: '

Installation successful!

'+ - '

The basic installation and configuration is completed, depending on your setup some of the following steps are required to start using Ceph:

'+ - '
  1. Install Ceph on other nodes
  2. '+ - '
  3. Create additional Ceph Monitors
  4. '+ - '
  5. Create Ceph OSDs
  6. '+ - '
  7. Create Ceph Pools
'+ - '

To learn more click on the help button below.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - - var tp = this.up('#wizcontent'); - var idx = tp.items.indexOf(this)-1; - for(;idx >= 0;idx--) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - onSubmit: function() { - var wizard = this.up('pveCephInstallWizard'); - wizard.close(); - } - } - ] - }); -Ext.define('PVE.node.DiskList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeDiskList', - - emptyText: gettext('No Disks found'), - - stateful: true, - stateId: 'grid-node-disks', - - columns: [ - { - header: gettext('Device'), - width: 150, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Type'), - width: 80, - sortable: true, - dataIndex: 'type', - renderer: function(v) { - if (v === 'ssd') { - return 'SSD'; - } else if (v === 'hdd') { - return 'Hard Disk'; - } else if (v === 'usb'){ - return 'USB'; - } else { - return gettext('Unknown'); - } - } - }, - { - header: gettext('Usage'), - width: 150, - sortable: false, - renderer: function(v, metaData, rec) { - if (rec) { - if (rec.data.osdid >= 0) { - var bluestore = ''; - if (rec.data.bluestore === 1) { - bluestore = ' (Bluestore)'; - } - return "Ceph osd." + rec.data.osdid.toString() + bluestore; - } - - var types = []; - if (rec.data.journals > 0) { - types.push('Journal'); - } - - if (rec.data.db > 0) { - types.push('DB'); - } - - if (rec.data.wal > 0) { - types.push('WAL'); - } - - if (types.length > 0) { - return 'Ceph (' + types.join(', ') + ')'; - } - } - - return v || Proxmox.Utils.noText; - }, - dataIndex: 'used' - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: 'GPT', - width: 60, - align: 'right', - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'gpt' - }, - { - header: gettext('Vendor'), - width: 100, - sortable: true, - hidden: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'vendor' - }, - { - header: gettext('Model'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'model' - }, - { - header: gettext('Serial'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'serial' - }, - { - header: 'S.M.A.R.T.', - width: 100, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'health' - }, - { - header: 'Wearout', - width: 90, - sortable: true, - align: 'right', - dataIndex: 'wearout', - renderer: function(value) { - if (Ext.isNumeric(value)) { - return (100 - value).toString() + '%'; - } - return 'N/A'; - } - } - ], - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var store = Ext.create('Ext.data.Store', { - storeid: 'node-disk-list' + nodename, - model: 'node-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list" - }, - sorters: [ - { - property : 'dev', - direction: 'ASC' - } - ] - }); - - var reloadButton = Ext.create('Proxmox.button.Button', { - text: gettext('Reload'), - handler: function() { - me.store.load(); - } - }); - - var smartButton = Ext.create('Proxmox.button.Button', { - text: gettext('Show S.M.A.R.T. values'), - selModel: sm, - enableFn: function() { - return !!sm.getSelection().length; - }, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - }); - - var initButton = Ext.create('Proxmox.button.Button', { - text: gettext('Initialize Disk with GPT'), - selModel: sm, - enableFn: function() { - var selection = sm.getSelection(); - - if (!selection.length || selection[0].data.used) { - return false; - } else { - return true; - } - }, - disabled: true, - - handler: function() { - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + nodename + '/disks/initgpt', - waitMsgTarget: me, - method: 'POST', - params: { disk: rec.data.devpath}, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - } - }); - - me.loadCount = 1; // avoid duplicate loadmask - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ reloadButton, smartButton, initButton ], - listeners: { - itemdblclick: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - } - }); - - - me.callParent(); - me.store.load(); - } -}, function() { - - Ext.define('node-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial', 'rpm', 'type', 'health', 'wearout' ], - idProperty: 'devpath' - }); -}); - -Ext.define('PVE.DiskSmartWindow', { - extend: 'Ext.window.Window', - alias: 'widget.pveSmartWindow', - - modal: true, - - items: [ - { - xtype: 'gridpanel', - layout: { - type: 'fit' - }, - emptyText: gettext('No S.M.A.R.T. Values'), - scrollable: true, - flex: 1, - itemId: 'smarts', - reserveScrollbar: true, - columns: [ - { text: 'ID', dataIndex: 'id', width: 50 }, - { text: gettext('Attribute'), flex: 1, dataIndex: 'name', renderer: Ext.String.htmlEncode }, - { text: gettext('Value'), dataIndex: 'raw', renderer: Ext.String.htmlEncode }, - { text: gettext('Normalized'), dataIndex: 'value', width: 60}, - { text: gettext('Threshold'), dataIndex: 'threshold', width: 60}, - { text: gettext('Worst'), dataIndex: 'worst', width: 60}, - { text: gettext('Flags'), dataIndex: 'flags'}, - { text: gettext('Failing'), dataIndex: 'fail', renderer: Ext.String.htmlEncode } - ] - }, - { - xtype: 'component', - itemId: 'text', - layout: { - type: 'fit' - }, - hidden: true, - style: { - 'background-color': '#23272a', - 'white-space': 'pre', - 'font-family': 'monospace' - } - } - ], - - buttons: [ - { - text: gettext('Reload'), - name: 'reload', - handler: function() { - var me = this; - me.up('window').store.reload(); - } - }, - { - text: gettext('Close'), - name: 'close', - handler: function() { - var me = this; - me.up('window').close(); - } - } - ], - - layout: { - type: 'vbox', - align: 'stretch' - }, - width: 800, - height: 500, - minWidth: 600, - minHeight: 400, - bodyPadding: 5, - title: gettext('S.M.A.R.T. Values'), - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var dev = me.dev; - if (!dev) { - throw "no device specified"; - } - - me.store = Ext.create('Ext.data.Store', { - model: 'disk-smart', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/smart?disk=" + dev - } - }); - - me.callParent(); - var grid = me.down('#smarts'); - var text = me.down('#text'); - - Proxmox.Utils.monStoreErrors(grid, me.store); - me.mon(me.store, 'load', function(s, records, success) { - if (success && records.length > 0) { - var rec = records[0]; - switch (rec.data.type) { - case 'text': - grid.setVisible(false); - text.setVisible(true); - text.setHtml(Ext.String.htmlEncode(rec.data.text)); - break; - default: - // includes 'ata' - // cannot use empty case because - // of jslint - grid.setVisible(true); - text.setVisible(false); - grid.setStore(rec.attributes()); - break; - } - } - }); - - me.store.load(); - } -}, function() { - - Ext.define('disk-smart', { - extend: 'Ext.data.Model', - fields: [ - { name:'health'}, - { name:'type'}, - { name:'text'} - ], - hasMany: {model: 'smart-attribute', name: 'attributes'} - }); - Ext.define('smart-attribute', { - extend: 'Ext.data.Model', - fields: [ - { name:'id', type:'number' }, 'name', 'value', 'worst', 'threshold', 'flags', 'fail', 'raw' - ] - }); -}); -Ext.define('PVE.node.CreateLVM', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVM', - - subject: 'LVM Volume Group', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvm", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMList', { - extend: 'Ext.tree.Panel', - xtype: 'pveLVMList', - emptyText: gettext('No Volume Groups found'), - stateful: true, - stateId: 'grid-node-lvm', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Number of LVs'), - dataIndex: 'lvcount', - width: 150, - align: 'right' - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Volume Group', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVM', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/lvm", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'size', 'free', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - txt += (data.leaf) ? 'hdd-o' : 'object-group'; - return txt; - } - }, - { - type: 'number', - name: 'usage', - calculate: function(data) { - return ((data.size-data.free)/data.size); - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.CreateLVMThin', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVMThin', - - subject: 'LVM Thinpool', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvmthin", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMThinList', { - extend: 'Ext.grid.Panel', - xtype: 'pveLVMThinList', - - emptyText: gettext('No thinpools found'), - stateful: true, - stateId: 'grid-node-lvmthin', - columns: [ - { - text: gettext('Name'), - dataIndex: 'lv', - flex: 1 - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'lv_size' - }, - { - header: gettext('Used'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'used' - }, - { - header: gettext('Metadata Usage'), - width: 120, - dataIndex: 'metadata_usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Metadata Size'), - width: 120, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_size' - }, - { - header: gettext('Metadata Used'), - width: 125, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_used' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Thinpool', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVMThin', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['lv', 'lv_size', 'used', 'metadata_size', 'metadata_used', - { - type: 'number', - name: 'usage', - calculate: function(data) { - return data.used/data.lv_size; - } - }, - { - type: 'number', - name: 'metadata_usage', - calculate: function(data) { - return data.metadata_used/data.metadata_size; - } - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/lvmthin' - }, - sorters: 'lv' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.CreateDirectory', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateDirectory', - - subject: Proxmox.Utils.directoryText, - - showProgress: true, - - onlineHelp: 'chapter_storage', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/directory", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['ext4', 'ext4'], - ['xfs', 'xfs'] - ], - fieldLabel: gettext('Filesystem'), - name: 'filesystem', - value: '', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.Directorylist', { - extend: 'Ext.grid.Panel', - xtype: 'pveDirectoryList', - - stateful: true, - stateId: 'grid-node-directory', - columns: [ - { - text: gettext('Path'), - dataIndex: 'path', - flex: 1 - }, - { - header: gettext('Device'), - flex: 1, - dataIndex: 'device' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'type' - }, - { - header: gettext('Options'), - width: 100, - dataIndex: 'options' - }, - { - header: gettext('Unit File'), - hidden: true, - dataIndex: 'unitfile' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Directory', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateDirectory', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['path', 'device', 'type', 'options', 'unitfile' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/directory' - }, - sorters: 'path' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -/*jslint confusion: true*/ -Ext.define('PVE.node.CreateZFS', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateZFS', - - subject: 'ZFS', - - showProgress: true, - - onlineHelp: 'chapter_zfs', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - var update_disklist = function() { - var grid = me.down('#disklist'); - var disks = grid.getSelection(); - - var val = []; - disks.sort(function(a,b) { - var aorder = a.get('order') || 0; - var border = b.get('order') || 0; - return (aorder - border); - }); - - disks.forEach(function(disk) { - val.push(disk.get('devpath')); - }); - - me.down('field[name=devices]').setValue(val.join(',')); - }; - - Ext.apply(me, { - url: '/nodes/' + me.nodename + '/disks/zfs', - method: 'POST', - items: [ - { - xtype: 'inputpanel', - onGetValues: function(values) { - return values; - }, - column1: [ - { - xtype: 'textfield', - hidden: true, - name: 'devices', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ], - column2: [ - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('RAID Level'), - name: 'raidlevel', - value: 'single', - comboItems: [ - ['single', gettext('Single Disk')], - ['mirror', 'Mirror'], - ['raid10', 'RAID10'], - ['raidz', 'RAIDZ'], - ['raidz2', 'RAIDZ2'], - ['raidz3', 'RAIDZ3'] - ] - }, - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Compression'), - name: 'compression', - value: 'on', - comboItems: [ - ['on', 'on'], - ['off', 'off'], - ['gzip', 'gzip'], - ['lz4', 'lz4'], - ['lzjb', 'lzjb'], - ['zle', 'zle'] - ] - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('ashift'), - minValue: 9, - maxValue: 16, - value: '12', - name: 'ashift' - } - ], - columnB: [ - { - xtype: 'grid', - height: 200, - emptyText: gettext('No Disks unused'), - itemId: 'disklist', - selModel: 'checkboxmodel', - listeners: { - selectionchange: update_disklist - }, - store: { - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/disks/list?type=unused' - } - }, - columns: [ - { - text: gettext('Device'), - dataIndex: 'devpath', - flex: 1 - }, - { - text: gettext('Serial'), - dataIndex: 'serial' - }, - { - text: gettext('Size'), - dataIndex: 'size', - renderer: PVE.Utils.render_size - }, - { - header: gettext('Order'), - xtype: 'widgetcolumn', - dataIndex: 'order', - sortable: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 1, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('order', value); - update_disklist(record); - } - } - } - } - ] - } - ] - }, - { - xtype: 'displayfield', - padding: '5 0 0 0', - userCls: 'pve-hint', - value: 'Note: ZFS is not compatible with disks backed by a hardware ' + - 'RAID controller. For details see ' + - 'the reference documentation.', - } - ] - }); - - me.callParent(); - me.down('#disklist').getStore().load(); - } -}); - -Ext.define('PVE.node.ZFSDevices', { - extend: 'Ext.tree.Panel', - xtype: 'pveZFSDevices', - stateful: true, - stateId: 'grid-node-zfsstatus', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'state' - }, - { - text: 'READ', - dataIndex: 'read' - }, - { - text: 'WRITE', - dataIndex: 'write' - }, - { - text: 'CKSUM', - dataIndex: 'cksum' - }, - { - text: gettext('Message'), - dataIndex: 'msg' - } - ], - - rootVisible: true, - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/zfs/" + me.zpool, - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'status', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - if (data.leaf) { - return txt + 'hdd-o'; - } - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSStatus', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveZFSStatus', - layout: 'fit', - border: false, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - me.url = "/api2/extjs/nodes/" + me.nodename + "/disks/zfs/" + me.zpool; - - me.rows = { - scan: { - header: gettext('Scan') - }, - status: { - header: gettext('Status') - }, - action: { - header: gettext('Action') - }, - errors: { - header: gettext('Errors') - } - }; - - me.callParent(); - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSList', { - extend: 'Ext.grid.Panel', - xtype: 'pveZFSList', - - stateful: true, - stateId: 'grid-node-zfs', - columns: [ - { - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Size'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - }, - { - header: gettext('Allocated'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'alloc' - }, - { - header: gettext('Fragmentation'), - renderer: function(value) { - return value.toString() + '%'; - }, - dataIndex: 'frag' - }, - { - header: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'health' - }, - { - header: gettext('Deduplication'), - hidden: true, - renderer: function(value) { - return value.toFixed(2).toString() + 'x'; - }, - dataIndex: 'dedup' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': ZFS', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateZFS', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - }, - { - text: gettext('Detail'), - itemId: 'detailbtn', - disabled: true, - handler: function() { - var me = this.up('panel'); - var selection = me.getSelection(); - if (selection.length < 1) { - return; - } - me.show_detail(selection[0].get('name')); - } - } - ], - - show_detail: function(zpool) { - var me = this; - - var detailsgrid = Ext.create('PVE.node.ZFSStatus', { - layout: 'fit', - nodename: me.nodename, - flex: 0, - zpool: zpool - }); - - var devicetree = Ext.create('PVE.node.ZFSDevices', { - title: gettext('Devices'), - nodename: me.nodename, - flex: 1, - zpool: zpool - }); - - - var win = Ext.create('Ext.window.Window', { - modal: true, - width: 800, - height: 400, - resizable: true, - layout: 'fit', - title: gettext('Status') + ': ' + zpool, - items:[{ - xtype: 'panel', - region: 'center', - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [detailsgrid, devicetree], - tbar: [{ - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - - devicetree.reload(); - detailsgrid.reload(); - } - }] - }] - }).show(); - }, - - set_button_status: function() { - var me = this; - var selection = me.getSelection(); - me.down('#detailbtn').setDisabled(selection.length === 0); - }, - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - }, - selectionchange: function() { - this.set_button_status(); - }, - itemdblclick: function(grid, record) { - var me = this; - me.show_detail(record.get('name')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['name', 'size', 'free', 'alloc', 'dedup', 'frag', 'health'], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/zfs' - }, - sorters: 'name' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveNodeStatus', - - height: 300, - bodyPadding: '20 15 20 15', - - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 15 5 15' - }, - - items: [ - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpuinfo', - renderer: PVE.Utils.render_node_cpu_usage - }, - { - itemId: 'wait', - iconCls: 'fa fa-fw fa-clock-o', - title: gettext('IO delay'), - valueField: 'wait', - rowspan: 2 - }, - { - itemId: 'load', - iconCls: 'fa fa-fw fa-tasks', - title: gettext('Load average'), - printBar: false, - textField: 'loadavg' - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - itemId: 'memory', - title: gettext('RAM usage'), - valueField: 'memory', - maxField: 'memory', - renderer: PVE.Utils.render_node_size_usage - }, - { - itemId: 'ksm', - printBar: false, - title: gettext('KSM sharing'), - textField: 'ksm', - renderer: function(record) { - return PVE.Utils.render_size(record.shared); - }, - padding: '0 15 10 15' - }, - { - iconCls: 'fa fa-fw fa-hdd-o', - itemId: 'rootfs', - title: gettext('HD space') + '(root)', - valueField: 'rootfs', - maxField: 'rootfs', - renderer: PVE.Utils.render_node_size_usage - }, - { - iconCls: 'fa fa-fw fa-refresh', - itemId: 'swap', - printSize: true, - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'swap', - renderer: PVE.Utils.render_node_size_usage - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - itemId: 'cpus', - colspan: 2, - printBar: false, - title: gettext('CPU(s)'), - textField: 'cpuinfo', - renderer: function(cpuinfo) { - return cpuinfo.cpus + " x " + cpuinfo.model + " (" + - cpuinfo.sockets.toString() + " " + - (cpuinfo.sockets > 1 ? - gettext('Sockets') : - gettext('Socket') - ) + ")"; - }, - value: '' - }, - { - itemId: 'kversion', - colspan: 2, - title: gettext('Kernel Version'), - printBar: false, - textField: 'kversion', - value: '' - }, - { - itemId: 'version', - colspan: 2, - printBar: false, - title: gettext('PVE Manager Version'), - textField: 'pveversion', - value: '' - } - ], - - updateTitle: function() { - var me = this; - var uptime = Proxmox.Utils.render_uptime(me.getRecordValue('uptime')); - me.setTitle(me.pveSelNode.data.node + ' (' + gettext('Uptime') + ': ' + uptime + ')'); - } - -}); -Ext.define('PVE.node.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeSummary', - - scrollable: true, - bodyPadding: 5, - - showVersions: function() { - var me = this; - - // Note: we use simply text/html here, because ExtJS grid has problems - // with cut&paste - - var nodename = me.pveSelNode.data.node; - - var view = Ext.createWidget('component', { - autoScroll: true, - padding: 5, - style: { - 'background-color': '#23272a', - 'white-space': 'pre', - 'font-family': 'monospace' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Package versions'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + nodename + "/apt/versions", - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - var text = ''; - - Ext.Array.each(response.result.data, function(rec) { - var version = "not correctly installed"; - var pkg = rec.Package; - if (rec.OldVersion && rec.CurrentState === 'Installed') { - version = rec.OldVersion; - } - if (rec.RunningKernel) { - text += pkg + ': ' + version + ' (running kernel: ' + - rec.RunningKernel + ')\n'; - } else if (rec.ManagerVersion) { - text += pkg + ': ' + version + ' (running version: ' + - rec.ManagerVersion + ')\n'; - } else { - text += pkg + ': ' + version + '\n'; - } - }); - - view.update(Ext.htmlEncode(text)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var rstore = me.statusStore; - - var version_btn = new Ext.Button({ - text: gettext('Package versions'), - handler: function(){ - Proxmox.Utils.checked_command(function() { me.showVersions(); }); - } - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/rrddata", - model: 'pve-rrd-node' - }); - - Ext.apply(me, { - tbar: [version_btn, '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: 'column', - defaults: { - minHeight: 320, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: [ - { - xtype: 'pveNodeStatus', - rstore: rstore, - width: 770, - pveSelNode: me.pveSelNode - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - fields: ['cpu','iowait'], - fieldTitles: [gettext('CPU usage'), gettext('IO delay')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Server load'), - fields: ['loadavg'], - fieldTitles: [gettext('Load average')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - fields: ['memtotal','memused'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - fields: ['netin','netout'], - store: rrdstore - } - ] - } - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - } -}); -/*global Blob*/ -Ext.define('PVE.node.SubscriptionKeyEdit', { - extend: 'Proxmox.window.Edit', - title: gettext('Upload Subscription Key'), - width: 300, - items: { - xtype: 'textfield', - name: 'key', - value: '', - fieldLabel: gettext('Subscription Key') - }, - initComponent : function() { - var me = this; - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.Subscription', { - extend: 'Proxmox.grid.ObjectGrid', - - alias: ['widget.pveNodeSubscription'], - - onlineHelp: 'getting_help', - - viewConfig: { - enableTextSelection: true - }, - - showReport: function() { - var me = this; - var nodename = me.pveSelNode.data.node; - - var getReportFileName = function() { - var now = Ext.Date.format(new Date(), 'D-d-F-Y-G-i'); - return me.nodename + '-report-' + now + '.txt'; - }; - - var view = Ext.createWidget('component', { - itemId: 'system-report-view', - scrollable: true, - style: { - 'background-color': '#23272a', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var reportWindow = Ext.create('Ext.window.Window', { - title: gettext('System Report'), - width: 1024, - height: 600, - layout: 'fit', - modal: true, - buttons: [ - '->', - { - text: gettext('Download'), - handler: function() { - var fileContent = reportWindow.getComponent('system-report-view').html; - var fileName = getReportFileName(); - - // Internet Explorer - if (window.navigator.msSaveOrOpenBlob) { - navigator.msSaveOrOpenBlob(new Blob([fileContent]), fileName); - } else { - var element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset=utf-8,' - + encodeURIComponent(fileContent)); - element.setAttribute('download', fileName); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - } - } - } - ], - items: view - }); - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + me.nodename + '/report', - method: 'GET', - waitMsgTarget: me, - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var report = Ext.htmlEncode(response.result.data); - reportWindow.show(); - view.update(report); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var reload = function() { - me.rstore.load(); - }; - - var baseurl = '/nodes/' + me.nodename + '/subscription'; - - var render_status = function(value) { - - var message = me.getObjectValue('message'); - - if (message) { - return value + ": " + message; - } - return value; - }; - - var rows = { - productname: { - header: gettext('Type') - }, - key: { - header: gettext('Subscription Key') - }, - status: { - header: gettext('Status'), - renderer: render_status - }, - message: { - visible: false - }, - serverid: { - header: gettext('Server ID') - }, - sockets: { - header: gettext('Sockets') - }, - checktime: { - header: gettext('Last checked'), - renderer: Proxmox.Utils.render_timestamp - }, - nextduedate: { - header: gettext('Next due date') - } - }; - - Ext.apply(me, { - url: '/api2/json' + baseurl, - cwidth1: 170, - tbar: [ - { - text: gettext('Upload Subscription Key'), - handler: function() { - var win = Ext.create('PVE.node.SubscriptionKeyEdit', { - url: '/api2/extjs/' + baseurl - }); - win.show(); - win.on('destroy', reload); - } - }, - { - text: gettext('Check'), - handler: function() { - Proxmox.Utils.API2Request({ - params: { force: 1 }, - url: baseurl, - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: reload - }); - } - }, - { - text: gettext('System Report'), - handler: function() { - Proxmox.Utils.checked_command(function (){ me.showReport(); }); - } - } - ], - rows: rows, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.node.CertificateView', { - extend: 'Ext.container.Container', - xtype: 'pveCertificatesView', - - onlineHelp: 'sysadmin_certificate_management', - - mixins: ['Proxmox.Mixin.CBind' ], - - items: [ - { - xtype: 'pveCertView', - border: 0, - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'pveACMEView', - border: 0, - cbind: { - nodename: '{nodename}' - } - } - ] - -}); - -Ext.define('PVE.node.CertificateViewer', { - extend: 'Proxmox.window.Edit', - - title: gettext('Certificate'), - - fieldDefaults: { - labelWidth: 120 - }, - width: 800, - resizable: true, - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Name'), - name: 'filename' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Fingerprint'), - name: 'fingerprint' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Issuer'), - name: 'issuer' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject'), - name: 'subject' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Valid Since'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notbefore' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Expires'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notafter' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject Alternative Names'), - name: 'san', - renderer: PVE.Utils.render_san - }, - { - xtype: 'textarea', - editable: false, - grow: true, - growMax: 200, - fieldLabel: gettext('Certificate'), - name: 'pem' - } - ], - - initComponent: function() { - var me = this; - - if (!me.cert) { - throw "no cert given"; - } - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/info'; - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - if (Ext.isArray(response.result.data)) { - Ext.Array.each(response.result.data, function(item) { - if (item.filename === me.cert) { - me.setValues(item); - return false; - } - }); - } - } - }); - } -}); - -Ext.define('PVE.node.CertUpload', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCertUpload', - - title: gettext('Upload Custom Certificate'), - resizable: false, - isCreate: true, - submitText: gettext('Upload'), - method: 'POST', - width: 600, - - apiCallDone: function(success, response, options) { - if (!success) { - return; - } - - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - items: [ - { - fieldLabel: gettext('Private Key (Optional)'), - labelAlign: 'top', - emptyText: gettext('No change'), - name: 'key', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=key]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'box', - autoEl: 'hr' - }, - { - fieldLabel: gettext('Certificate Chain'), - labelAlign: 'top', - allowBlank: false, - name: 'certificates', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=certificates]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'hidden', - name: 'restart', - value: '1' - }, - { - xtype: 'hidden', - name: 'force', - value: '1' - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/custom'; - - me.callParent(); - } -}); - -Ext.define('pve-certificate', { - extend: 'Ext.data.Model', - - fields: [ 'filename', 'fingerprint', 'issuer', 'notafter', 'notbefore', 'subject', 'san' ], - idProperty: 'filename' -}); - -Ext.define('PVE.node.Certificates', { - extend: 'Ext.grid.Panel', - xtype: 'pveCertView', - - tbar: [ - { - xtype: 'button', - text: gettext('Upload Custom Certificate'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.CertUpload', { - nodename: me.nodename - }); - win.show(); - win.on('destroy', me.reload, me); - } - }, - { - xtype: 'button', - itemId: 'deletebtn', - text: gettext('Delete Custom Certificate'), - handler: function() { - var me = this.up('grid'); - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/certificates/custom?restart=1', - method: 'DELETE', - success: function(response, opt) { - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - '-', - { - xtype: 'proxmoxButton', - itemId: 'viewbtn', - disabled: true, - text: gettext('View Certificate'), - handler: function() { - var me = this.up('grid'); - me.view_certificate(); - } - } - ], - - columns: [ - { - header: gettext('File'), - width: 150, - dataIndex: 'filename' - }, - { - header: gettext('Issuer'), - flex: 1, - dataIndex: 'issuer' - }, - { - header: gettext('Subject'), - flex: 1, - dataIndex: 'subject' - }, - { - header: gettext('Valid Since'), - width: 150, - dataIndex: 'notbefore', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Expires'), - width: 150, - dataIndex: 'notafter', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Subject Alternative Names'), - flex: 1, - dataIndex: 'san', - renderer: PVE.Utils.render_san - }, - { - header: gettext('Fingerprint'), - dataIndex: 'fingerprint', - hidden: true - }, - { - header: gettext('PEM'), - dataIndex: 'pem', - hidden: true - } - ], - - reload: function() { - var me = this; - me.rstore.load(); - }, - - set_button_status: function() { - var me = this; - var rec = me.rstore.getById('pveproxy-ssl.pem'); - - me.down('#deletebtn').setDisabled(!rec); - }, - - view_certificate: function() { - var me = this; - var selection = me.getSelection(); - if (!selection || selection.length < 1) { - return; - } - var win = Ext.create('PVE.node.CertificateViewer', { - cert: selection[0].data.filename, - nodename : me.nodename - }); - win.show(); - }, - - listeners: { - itemdblclick: 'view_certificate' - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'certs-' + me.nodename, - model: 'pve-certificate', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/certificates/info' - } - }); - - me.store = { - type: 'diff', - rstore: me.rstore - }; - - me.callParent(); - - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - } -}); -Ext.define('PVE.node.ACMEEditor', { - extend: 'Proxmox.window.Edit', - xtype: 'pveACMEEditor', - - subject: gettext('Domains'), - items: [ - { - xtype: 'inputpanel', - items: [ - { - xtype: 'textarea', - fieldLabel: gettext('Domains'), - emptyText: "domain1.example.com\ndomain2.example.com", - name: 'domains' - } - ], - onGetValues: function(values) { - if (!values.domains) { - return { - 'delete': 'acme' - }; - } - var domains = values.domains.split(/\n/).join(';'); - return { - 'acme': 'domains=' + domains - }; - } - } - ], - - initComponent: function() { - var me = this; - me.callParent(); - - me.load({ - success: function(response, opts) { - var res = PVE.Parser.parseACME(response.result.data.acme); - if (res) { - res.domains = res.domains.join(' '); - me.setValues(res); - } - } - }); - } -}); - -Ext.define('PVE.node.ACMEAccountCreate', { - extend: 'Proxmox.window.Edit', - - width: 400, - title: gettext('Register Account'), - isCreate: true, - method: 'POST', - submitText: gettext('Register'), - url: '/cluster/acme/account', - showTaskViewer: true, - - items: [ - { - xtype: 'proxmoxComboGrid', - name: 'directory', - allowBlank: false, - valueField: 'url', - displayField: 'name', - fieldLabel: gettext('ACME Directory'), - store: { - autoLoad: true, - fields: ['name', 'url'], - idProperty: ['name'], - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/acme/directories' - }, - sorters: { - property: 'name', - order: 'ASC' - } - }, - listConfig: { - columns: [ - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('URL'), - dataIndex: 'url', - flex: 1 - } - ] - }, - listeners: { - change: function(combogrid, value) { - var me = this; - if (!value) { - return; - } - - var disp = me.up('window').down('#tos_url_display'); - var field = me.up('window').down('#tos_url'); - var checkbox = me.up('window').down('#tos_checkbox'); - - disp.setValue(gettext('Loading')); - field.setValue(undefined); - checkbox.setValue(undefined); - - Proxmox.Utils.API2Request({ - url: '/cluster/acme/tos', - method: 'GET', - params: { - directory: value - }, - success: function(response, opt) { - me.up('window').down('#tos_url').setValue(response.result.data); - me.up('window').down('#tos_url_display').setValue(response.result.data); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - }, - { - xtype: 'displayfield', - itemId: 'tos_url_display', - fieldLabel: gettext('Terms of Service'), - renderer: PVE.Utils.render_optional_url, - name: 'tos_url_display' - }, - { - xtype: 'hidden', - itemId: 'tos_url', - name: 'tos_url' - }, - { - xtype: 'proxmoxcheckbox', - itemId: 'tos_checkbox', - fieldLabel: gettext('Accept TOS'), - submitValue: false, - validateValue: function(value) { - if (value && this.checked) { - return true; - } - return false; - } - }, - { - xtype: 'textfield', - name: 'contact', - vtype: 'email', - allowBlank: false, - fieldLabel: gettext('E-Mail') - } - ] - -}); - -Ext.define('PVE.node.ACMEAccountView', { - extend: 'Proxmox.window.Edit', - - width: 600, - fieldDefaults: { - labelWidth: 140 - }, - - title: gettext('Account'), - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('E-Mail'), - name: 'email' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Created'), - name: 'createdAt' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Status'), - name: 'status' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Directory'), - renderer: PVE.Utils.render_optional_url, - name: 'directory' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Terms of Services'), - renderer: PVE.Utils.render_optional_url, - name: 'tos' - } - ], - - initComponent: function() { - var me = this; - - if (!me.accountname) { - throw "no account name defined"; - } - - me.url = '/cluster/acme/account/' + me.accountname; - - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - var data = response.result.data; - data.email = data.account.contact[0]; - data.createdAt = data.account.createdAt; - data.status = data.account.status; - me.setValues(data); - } - }); - } -}); - -Ext.define('PVE.node.ACME', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveACMEView', - - margin: '10 0 0 0', - title: 'ACME', - - tbar: [ - { - xtype: 'button', - itemId: 'edit', - text: gettext('Edit Domains'), - handler: function() { - this.up('grid').run_editor(); - } - }, - { - xtype: 'button', - itemId: 'createaccount', - text: gettext('Register Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountCreate', { - taskDone: function() { - me.load_account(); - me.reload(); - } - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'viewaccount', - text: gettext('View Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountView', { - accountname: 'default' - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'order', - text: gettext('Order Certificate'), - handler: function() { - var me = this.up('grid'); - - Proxmox.Utils.API2Request({ - method: 'POST', - params: { - force: 1 - }, - url: '/nodes/' + me.nodename + '/certificates/acme/certificate', - success: function(response, opt) { - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: response.result.data, - taskDone: function(success) { - me.certificate_order_finished(success); - } - }); - win.show(); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ], - - certificate_order_finished: function(success) { - if (!success) { - return; - } - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - set_button_status: function() { - var me = this; - - var account = !!me.account; - var acmeObj = PVE.Parser.parseACME(me.getObjectValue('acme')); - var domains = acmeObj ? acmeObj.domains.length : 0; - - var order = me.down('#order'); - order.setVisible(account); - order.setDisabled(!account || !domains); - - me.down('#createaccount').setVisible(!account); - me.down('#viewaccount').setVisible(account); - }, - - load_account: function() { - var me = this; - - // for now we only use the 'default' account - Proxmox.Utils.API2Request({ - url: '/cluster/acme/account/default', - success: function(response, opt) { - me.account = response.result.data; - me.set_button_status(); - }, - failure: function(response, opt) { - me.account = undefined; - me.set_button_status(); - } - }); - }, - - run_editor: function() { - var me = this; - var win = Ext.create(me.rows.acme.editor, me.editorConfig); - win.show(); - win.on('destroy', me.reload, me); - }, - - listeners: { - itemdblclick: 'run_editor' - }, - - // account data gets loaded here - account: undefined, - - disableSelection: true, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/api2/json/nodes/' + me.nodename + '/config'; - - me.editorConfig = { - url: '/api2/extjs/nodes/' + me.nodename + '/config' - }; - /*jslint confusion: true*/ - /*acme is a string above*/ - me.rows = { - acme: { - defaultValue: '', - header: gettext('Domains'), - editor: 'PVE.node.ACMEEditor', - renderer: function(value) { - var acmeObj = PVE.Parser.parseACME(value); - if (acmeObj) { - return acmeObj.domains.join('
'); - } - return Proxmox.Utils.noneText; - } - } - }; - /*jslint confusion: false*/ - - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - me.load_account(); - } -}); -Ext.define('PVE.node.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.node.Config', - - onlineHelp: 'chapter_system_administration', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/status", - interval: 1000 - }); - - var node_command = function(cmd) { - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/status', - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var actionBtn = Ext.create('Ext.Button', { - text: gettext('Bulk Actions'), - iconCls: 'fa fa-fw fa-ellipsis-v', - disabled: !caps.nodes['Sys.PowerMgmt'], - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Bulk Start'), - iconCls: 'fa fa-fw fa-play', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - } - ] - }) - }); - - var restartBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Reboot'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Reboot node '{0}'?"), nodename), - handler: function() { - node_command('reboot'); - }, - iconCls: 'fa fa-undo' - }); - - var shutdownBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Shutdown'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Shutdown node '{0}'?"), nodename), - handler: function() { - node_command('shutdown'); - }, - iconCls: 'fa fa-power-off' - }); - - var shellBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.nodes['Sys.Console'], - text: gettext('Shell'), - consoleType: 'shell', - nodename: nodename - }); - - me.items = []; - - Ext.apply(me, { - title: gettext('Node') + " '" + nodename + "'", - hstateid: 'nodetab', - defaults: { statusStore: me.statusStore }, - tbar: [ restartBtn, shutdownBtn, shellBtn, actionBtn] - }); - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - itemId: 'summary', - xtype: 'pveNodeSummary' - }, - { - title: gettext('Notes'), - iconCls: 'fa fa-sticky-note-o', - itemId: 'notes', - xtype: 'pveNotesView' - } - ); - } - - if (caps.nodes['Sys.Console']) { - me.items.push( - { - title: gettext('Shell'), - iconCls: 'fa fa-terminal', - itemId: 'jsconsole', - xtype: 'pveNoVncConsole', - consoleType: 'shell', - xtermjs: true, - nodename: nodename - } - ); - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('System'), - iconCls: 'fa fa-cogs', - itemId: 'services', - expandedOnInit: true, - startOnlyServices: { - 'pveproxy': true, - 'pvedaemon': true, - 'pve-cluster': true - }, - nodename: nodename, - onlineHelp: 'pve_service_daemons', - xtype: 'proxmoxNodeServiceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - groups: ['services'], - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeNetworkView' - }, - { - title: gettext('Certificates'), - iconCls: 'fa fa-certificate', - itemId: 'certificates', - groups: ['services'], - nodename: nodename, - xtype: 'pveCertificatesView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'dns', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeDNSView' - }, - { - title: gettext('Hosts'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'hosts', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeHostsView' - }, - { - title: gettext('Time'), - itemId: 'time', - groups: ['services'], - nodename: nodename, - xtype: 'proxmoxNodeTimeView', - iconCls: 'fa fa-clock-o' - }); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push({ - title: 'Syslog', - iconCls: 'fa fa-list', - groups: ['services'], - disabled: !caps.nodes['Sys.Syslog'], - itemId: 'syslog', - xtype: 'proxmoxJournalView', - url: "/api2/extjs/nodes/" + nodename + "/journal" - }); - - if (caps.nodes['Sys.Modify']) { - me.items.push({ - title: gettext('Updates'), - iconCls: 'fa fa-refresh', - disabled: !caps.nodes['Sys.Console'], - // do we want to link to system updates instead? - itemId: 'apt', - xtype: 'proxmoxNodeAPT', - upgradeBtn: { - xtype: 'pveConsoleButton', - disabled: Proxmox.UserName !== 'root@pam', - text: gettext('Upgrade'), - consoleType: 'upgrade', - nodename: nodename - }, - nodename: nodename - }); - } - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - xtype: 'pveFirewallRules', - iconCls: 'fa fa-shield', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/nodes/' + nodename + '/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_host_specific_configuration', - groups: ['firewall'], - base_url: '/nodes/' + nodename + '/firewall/options', - fwtype: 'node', - itemId: 'firewall-options' - }); - } - - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Disks'), - itemId: 'storage', - expandedOnInit: true, - iconCls: 'fa fa-hdd-o', - xtype: 'pveNodeDiskList' - }, - { - title: 'LVM', - itemId: 'lvm', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square', - groups: ['storage'], - xtype: 'pveLVMList' - }, - { - title: 'LVM-Thin', - itemId: 'lvmthin', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square-o', - groups: ['storage'], - xtype: 'pveLVMThinList' - }, - { - title: Proxmox.Utils.directoryText, - itemId: 'directory', - onlineHelp: 'chapter_storage', - iconCls: 'fa fa-folder', - groups: ['storage'], - xtype: 'pveDirectoryList' - }, - { - title: 'ZFS', - itemId: 'zfs', - onlineHelp: 'chapter_zfs', - iconCls: 'fa fa-th-large', - groups: ['storage'], - xtype: 'pveZFSList' - }, - { - title: 'Ceph', - itemId: 'ceph', - iconCls: 'fa fa-ceph', - xtype: 'pveNodeCephStatus' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveNodeCephConfigCrush', - title: gettext('Configuration'), - iconCls: 'fa fa-gear', - groups: ['ceph'], - itemId: 'ceph-config' - }, - { - xtype: 'pveNodeCephMonMgr', - title: gettext('Monitor'), - iconCls: 'fa fa-tv', - groups: ['ceph'], - itemId: 'ceph-monlist' - }, - { - xtype: 'pveNodeCephOsdTree', - title: 'OSD', - iconCls: 'fa fa-hdd-o', - groups: ['ceph'], - itemId: 'ceph-osdtree' - }, - { - xtype: 'pveNodeCephFSPanel', - title: 'CephFS', - iconCls: 'fa fa-folder', - groups: ['ceph'], - nodename: nodename, - itemId: 'ceph-cephfspanel' - }, - { - xtype: 'pveNodeCephPoolList', - title: 'Pools', - iconCls: 'fa fa-sitemap', - groups: ['ceph'], - itemId: 'ceph-pools' - } - ); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push( - { - xtype: 'proxmoxLogView', - title: gettext('Log'), - iconCls: 'fa fa-list', - groups: ['firewall'], - onlineHelp: 'chapter_pve_firewall', - url: '/api2/extjs/nodes/' + nodename + '/firewall/log', - itemId: 'firewall-fwlog' - }, - { - title: gettext('Log'), - itemId: 'ceph-log', - iconCls: 'fa fa-list', - groups: ['ceph'], - onlineHelp: 'chapter_pveceph', - xtype: 'cephLogView', - url: "/api2/extjs/nodes/" + nodename + "/ceph/log", - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Task History'), - iconCls: 'fa fa-list', - itemId: 'tasks', - nodename: nodename, - xtype: 'proxmoxNodeTasks' - }, - { - title: gettext('Subscription'), - iconCls: 'fa fa-support', - itemId: 'support', - xtype: 'pveNodeSubscription', - nodename: nodename - } - ); - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var uptimerec = s.data.get('uptime'); - var powermgmt = uptimerec ? uptimerec.data.value : false; - if (!caps.nodes['Sys.PowerMgmt']) { - powermgmt = false; - } - restartBtn.setDisabled(!powermgmt); - shutdownBtn.setDisabled(!powermgmt); - shellBtn.setDisabled(!powermgmt); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.window.Migrate', { - extend: 'Ext.window.Window', - - vmtype: undefined, - nodename: undefined, - vmid: undefined, - - viewModel: { - data: { - vmid: undefined, - nodename: undefined, - vmtype: undefined, - running: false, - qemu: { - onlineHelp: 'qm_migration', - commonName: 'VM' - }, - lxc: { - onlineHelp: 'pct_migration', - commonName: 'CT' - }, - migration: { - possible: true, - preconditions: [], - 'with-local-disks': 0, - mode: undefined, - allowedNodes: undefined - } - - }, - - formulas: { - setMigrationMode: function(get) { - if (get('running')){ - if (get('vmtype') === 'qemu') { - return gettext('Online'); - } else { - return gettext('Restart Mode'); - } - } else { - return gettext('Offline'); - } - }, - setStorageselectorHidden: function(get) { - if (get('migration.with-local-disks') && get('running')) { - return false; - } else { - return true; - } - } - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=formPanel]': { - validityChange: function(panel, isValid) { - this.getViewModel().set('migration.possible', isValid); - this.checkMigratePreconditions(); - } - } - }, - - init: function(view) { - var me = this, - vm = view.getViewModel(); - - if (!view.nodename) { - throw "missing custom view config: nodename"; - } - vm.set('nodename', view.nodename); - - if (!view.vmid) { - throw "missing custom view config: vmid"; - } - vm.set('vmid', view.vmid); - - if (!view.vmtype) { - throw "missing custom view config: vmtype"; - } - vm.set('vmtype', view.vmtype); - - - view.setTitle( - Ext.String.format('{0} {1}{2}', gettext('Migrate'), vm.get(view.vmtype).commonName, view.vmid) - ); - me.lookup('proxmoxHelpButton').setHelpConfig({ - onlineHelp: vm.get(view.vmtype).onlineHelp - }); - me.checkMigratePreconditions(); - me.lookup('formPanel').isValid(); - - }, - - onTargetChange: function (nodeSelector) { - //Always display the storages of the currently seleceted migration target - this.lookup('pveDiskStorageSelector').setNodename(nodeSelector.value); - this.checkMigratePreconditions(); - }, - - startMigration: function() { - var me = this, - view = me.getView(), - vm = me.getViewModel(); - - var values = me.lookup('formPanel').getValues(); - var params = { - target: values.target - }; - - if (vm.get('migration.mode')) { - params[vm.get('migration.mode')] = 1; - } - if (vm.get('migration.with-local-disks')) { - params['with-local-disks'] = 1; - } - //only submit targetstorage if vm is running, storage migration to different storage is only possible online - if (vm.get('migration.with-local-disks') && vm.get('running')) { - params.targetstorage = values.targetstorage; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate', - waitMsgTarget: view, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var extraTitle = Ext.String.format(' ({0} ---> {1})', vm.get('nodename'), params.target); - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - extraTitle: extraTitle - }).show(); - - view.close(); - } - }); - - }, - - checkMigratePreconditions: function() { - var me = this, - vm = me.getViewModel(); - - - var vmrec = PVE.data.ResourceStore.findRecord('vmid', vm.get('vmid'), - 0, false, false, true); - if (vmrec && vmrec.data && vmrec.data.running) { - vm.set('running', true); - } - - if (vm.get('vmtype') === 'qemu') { - me.checkQemuPreconditions(); - } else { - me.checkLxcPreconditions(); - } - me.lookup('pveNodeSelector').disallowedNodes = [vm.get('nodename')]; - - // Only allow nodes where the local storage is available in case of offline migration - // where storage migration is not possible - me.lookup('pveNodeSelector').allowedNodes = vm.get('migration.allowedNodes'); - - me.lookup('formPanel').isValid(); - - }, - - checkQemuPreconditions: function() { - var me = this, - vm = me.getViewModel(), - migrateStats; - - if (vm.get('running')) { - vm.set('migration.mode', 'online'); - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate', - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - migrateStats = response.result.data; - if (migrateStats.running) { - vm.set('running', true); - } - // Get migration object from viewmodel to prevent - // to many bind callbacks - var migration = vm.get('migration'); - migration.preconditions = []; - - if (migrateStats.allowed_nodes) { - migration.allowedNodes = migrateStats.allowed_nodes; - var target = me.lookup('pveNodeSelector').value; - if (target.length && !migrateStats.allowed_nodes.includes(target)) { - let disallowed = migrateStats.not_allowed_nodes[target]; - let missing_storages = disallowed.unavailable_storages.join(', '); - - migration.possible = false; - migration.preconditions.push({ - text: 'Storage (' + missing_storages + ') not available on selected target. ' + - 'Start VM to use live storage migration or select other target node', - severity: 'error' - }); - } - } - - if (migrateStats.local_resources.length) { - migration.possible = false; - migration.preconditions.push({ - text: 'Can\'t migrate VM with local resources: '+ migrateStats.local_resources.join(', '), - severity: 'error' - }); - } - - if (migrateStats.local_disks.length) { - - migrateStats.local_disks.forEach(function (disk) { - if (disk.cdrom && disk.cdrom === 1) { - migration.possible = false; - migration.preconditions.push({ - text: "Can't migrate VM with local CD/DVD", - severity: 'error' - }); - - } else if (!disk.referenced_in_config) { - migration.possible = false; - migration.preconditions.push({ - text: 'Found not referenced/unused disk via storage: '+ disk.volid, - severity: 'error' - }); - } else { - migration['with-local-disks'] = 1; - migration.preconditions.push({ - text:'Migration with local disk might take long: ' + disk.volid - +' (' + PVE.Utils.render_size(disk.size) + ')', - severity: 'warning' - }); - } - }); - - } - - vm.set('migration', migration); - - } - }); - }, - checkLxcPreconditions: function() { - var me = this, - vm = me.getViewModel(); - if (vm.get('running')) { - vm.set('migration.mode', 'restart'); - } - } - - - }, - - width: 600, - modal: true, - layout: { - type: 'vbox', - align: 'stretch' - }, - border: false, - items: [ - { - xtype: 'form', - reference: 'formPanel', - bodyPadding: 10, - border: false, - layout: { - type: 'column' - }, - items: [ - { - xtype: 'container', - columnWidth: 0.5, - items: [{ - xtype: 'displayfield', - name: 'source', - fieldLabel: gettext('Source node'), - bind: { - value: '{nodename}' - } - }, - { - xtype: 'displayfield', - reference: 'migrationMode', - fieldLabel: gettext('Mode'), - bind: { - value: '{setMigrationMode}' - } - }] - }, - { - xtype: 'container', - columnWidth: 0.5, - items: [{ - xtype: 'pveNodeSelector', - reference: 'pveNodeSelector', - name: 'target', - fieldLabel: gettext('Target node'), - allowBlank: false, - disallowedNodes: undefined, - onlineValidator: true, - listeners: { - change: 'onTargetChange' - } - }, - { - xtype: 'pveStorageSelector', - reference: 'pveDiskStorageSelector', - name: 'targetstorage', - fieldLabel: gettext('Target storage'), - storageContent: 'images', - bind: { - hidden: '{setStorageselectorHidden}' - } - }] - } - ] - }, - { - xtype: 'gridpanel', - reference: 'preconditionGrid', - selectable: false, - flex: 1, - columns: [{ - text: '', - dataIndex: 'severity', - renderer: function(v) { - switch (v) { - case 'warning': - return ' '; - case 'error': - return ''; - default: - return v; - } - }, - width: 35 - }, - { - text: 'Info', - dataIndex: 'text', - cellWrap: true, - flex: 1 - }], - bind: { - hidden: '{!migration.preconditions.length}', - store: { - fields: ['severity','text'], - data: '{migration.preconditions}' - } - } - } - - ], - buttons: [ - { - xtype: 'proxmoxHelpButton', - reference: 'proxmoxHelpButton', - onlineHelp: 'pct_migration', - listenToGlobalEvent: false, - hidden: false - }, - '->', - { - xtype: 'button', - reference: 'submitButton', - text: gettext('Migrate'), - handler: 'startMigration', - bind: { - disabled: '{!migration.possible}' - } - } - ] -}); -Ext.define('PVE.window.BulkAction', { - extend: 'Ext.window.Window', - - resizable: true, - width: 800, - modal: true, - layout: { - type: 'fit' - }, - border: false, - - // the action to be set - // currently there are - // startall - // migrateall - // stopall - action: undefined, - - submit: function(params) { - var me = this; - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.action, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.hide(); - win.on('destroy', function() { - me.close(); - }); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.action) { - throw "no action specified"; - } - - if (!me.btnText) { - throw "no button text specified"; - } - - if (!me.title) { - throw "no title specified"; - } - - var items = []; - - if (me.action === 'migrateall') { - /*jslint confusion: true*/ - /*value is string and number*/ - items.push( - { - xtype: 'pveNodeSelector', - name: 'target', - disallowedNodes: [me.nodename], - fieldLabel: gettext('Target node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'maxworkers', - minValue: 1, - maxValue: 100, - value: 1, - fieldLabel: gettext('Parallel jobs'), - allowBlank: false - }, - { - itemId: 'lxcwarning', - xtype: 'displayfield', - userCls: 'pve-hint', - value: 'Warning: Running CTs will be migrated in Restart Mode.', - hidden: true // only visible if running container chosen - } - ); - /*jslint confusion: false*/ - } else if (me.action === 'startall') { - items.push({ - xtype: 'hiddenfield', - name: 'force', - value: 1 - }); - } - - items.push({ - xtype: 'vmselector', - itemId: 'vms', - name: 'vms', - flex: 1, - height: 300, - selectAll: true, - allowBlank: false, - nodename: me.nodename, - action: me.action, - listeners: { - selectionchange: function(vmselector, records) { - if (me.action == 'migrateall') { - var showWarning = records.some(function(item) { - return (item.data.type == 'lxc' && - item.data.status == 'running'); - }); - me.down('#lxcwarning').setVisible(showWarning); - } - } - } - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - layout: { - type: 'vbox', - align: 'stretch' - }, - fieldDefaults: { - labelWidth: 300, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: me.btnText, - handler: function() { - form.isValid(); - me.submit(form.getValues()); - } - }); - - Ext.apply(me, { - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - - form.on('validitychange', function() { - var valid = form.isValid(); - submitBtn.setDisabled(!valid); - }); - form.isValid(); - } -}); -Ext.define('PVE.window.Clone', { - extend: 'Ext.window.Window', - - resizable: false, - - isTemplate: false, - - onlineHelp: 'qm_copy_and_clone', - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=cloneform]': { - validitychange: 'disableSubmit' - } - }, - disableSubmit: function(form) { - this.lookupReference('submitBtn').setDisabled(!form.isValid()); - } - }, - - statics: { - // display a snapshot selector only if needed - wrap: function(nodename, vmid, isTemplate, guestType) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid +'/snapshot', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var snapshotList = response.result.data; - var hasSnapshots = snapshotList.length === 1 && - snapshotList[0].name === 'current' ? false : true; - - Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: isTemplate, - hasSnapshots: hasSnapshots - }).show(); - } - }); - } - }, - - create_clone: function(values) { - var me = this; - - var params = { newid: values.newvmid }; - - if (values.snapname && values.snapname !== 'current') { - params.snapname = values.snapname; - } - - if (values.pool) { - params.pool = values.pool; - } - - if (values.name) { - if (me.guestType === 'lxc') { - params.hostname = values.name; - } else { - params.name = values.name; - } - } - - if (values.target) { - params.target = values.target; - } - - if (values.clonemode === 'copy') { - params.full = 1; - if (values.hdstorage) { - params.storage = values.hdstorage; - if (values.diskformat && me.guestType !== 'lxc') { - params.format = values.diskformat; - } - } - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/clone', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - - }, - - // disable the Storage selector when clone mode is linked clone - updateVisibility: function() { - var me = this; - var clonemode = me.lookupReference('clonemodesel').getValue(); - var disksel = me.lookup('diskselector'); - disksel.setDisabled(clonemode === 'clone'); - }, - - // add to the list of valid nodes each node where - // all the VM disks are available - verifyFeature: function() { - var me = this; - - var snapname = me.lookupReference('snapshotsel').getValue(); - var clonemode = me.lookupReference('clonemodesel').getValue(); - - var params = { feature: clonemode }; - if (snapname !== 'current') { - params.snapname = snapname; - } - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/feature', - params: params, - method: 'GET', - failure: function(response, opts) { - me.lookupReference('submitBtn').setDisabled(true); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var res = response.result.data; - - me.lookupReference('targetsel').allowedNodes = res.nodes; - me.lookupReference('targetsel').validate(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.snapname) { - me.snapname = 'current'; - } - - if (!me.guestType) { - throw "no Guest Type specified"; - } - - var titletext = me.guestType === 'lxc' ? 'CT' : 'VM'; - if (me.isTemplate) { - titletext += ' Template'; - } - me.title = "Clone " + titletext + " " + me.vmid; - - var col1 = []; - var col2 = []; - - col1.push({ - xtype: 'pveNodeSelector', - name: 'target', - reference: 'targetsel', - fieldLabel: gettext('Target node'), - selectCurNode: true, - allowBlank: false, - onlineValidator: true, - listeners: { - change: function(f, value) { - me.lookupReference('hdstorage').setTargetNode(value); - } - } - }); - - var modelist = [['copy', gettext('Full Clone')]]; - if (me.isTemplate) { - modelist.push(['clone', gettext('Linked Clone')]); - } - - col1.push({ - xtype: 'pveGuestIDSelector', - name: 'newvmid', - guestType: me.guestType, - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - allowBlank: true, - fieldLabel: me.guestType === 'lxc' ? gettext('Hostname') : gettext('Name') - }, - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ); - - col2.push({ - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Mode'), - name: 'clonemode', - reference: 'clonemodesel', - allowBlank: false, - hidden: !me.isTemplate, - value: me.isTemplate ? 'clone' : 'copy', - comboItems: modelist, - listeners: { - change: function(t, value) { - me.updateVisibility(); - me.verifyFeature(); - } - } - }, - { - xtype: 'PVE.form.SnapshotSelector', - name: 'snapname', - reference: 'snapshotsel', - fieldLabel: gettext('Snapshot'), - nodename: me.nodename, - guestType: me.guestType, - vmid: me.vmid, - hidden: me.isTemplate || !me.hasSnapshots ? true : false, - disabled: false, - allowBlank: false, - value : me.snapname, - listeners: { - change: function(f, value) { - me.verifyFeature(); - } - } - }, - { - xtype: 'pveDiskStorageSelector', - reference: 'diskselector', - nodename: me.nodename, - autoSelect: false, - hideSize: true, - hideSelection: true, - storageLabel: gettext('Target Storage'), - allowBlank: true, - storageContent: me.guestType === 'qemu' ? 'images' : 'rootdir', - emptyText: gettext('Same as source'), - disabled: me.isTemplate ? true : false // because default mode is clone for templates - }); - - var formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - reference: 'cloneform', - border: false, - layout: 'column', - defaultType: 'container', - columns: 2, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: col1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: col2 - } - ] - }); - - Ext.apply(me, { - modal: true, - width: 600, - height: 250, - border: false, - layout: 'fit', - buttons: [ { - xtype: 'proxmoxHelpButton', - listenToGlobalEvent: false, - hidden: false, - onlineHelp: me.onlineHelp - }, - '->', - { - reference: 'submitBtn', - text: gettext('Clone'), - disabled: true, - handler: function() { - var cloneForm = me.lookupReference('cloneform'); - if (cloneForm.isValid()) { - me.create_clone(cloneForm.getValues()); - } - } - } ], - items: [ formPanel ] - }); - - me.callParent(); - - me.verifyFeature(); - } -}); -Ext.define('PVE.qemu.Monitor', { - extend: 'Ext.panel.Panel', - - alias: 'widget.pveQemuMonitor', - - maxLines: 500, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var history = []; - var histNum = -1; - var lines = []; - - var textbox = Ext.createWidget('panel', { - region: 'center', - xtype: 'panel', - autoScroll: true, - border: true, - margins: '5 5 5 5', - bodyStyle: 'font-family: monospace;' - }); - - var scrollToEnd = function() { - var el = textbox.getTargetEl(); - var dom = Ext.getDom(el); - - var clientHeight = dom.clientHeight; - // BrowserBug: clientHeight reports 0 in IE9 StrictMode - // Instead we are using offsetHeight and hardcoding borders - if (Ext.isIE9 && Ext.isStrict) { - clientHeight = dom.offsetHeight + 2; - } - dom.scrollTop = dom.scrollHeight - clientHeight; - }; - - var refresh = function() { - textbox.update('
' + lines.join('\n') + '
'); - scrollToEnd(); - }; - - var addLine = function(line) { - lines.push(line); - if (lines.length > me.maxLines) { - lines.shift(); - } - }; - - var executeCmd = function(cmd) { - addLine("# " + Ext.htmlEncode(cmd)); - if (cmd) { - history.unshift(cmd); - if (history.length > 20) { - history.splice(20); - } - } - histNum = -1; - - refresh(); - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/monitor", - method: 'POST', - waitMsgTarget: me, - success: function(response, opts) { - var res = response.result.data; - Ext.Array.each(res.split('\n'), function(line) { - addLine(Ext.htmlEncode(line)); - }); - refresh(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - textbox, - { - region: 'south', - margins:'0 5 5 5', - border: false, - xtype: 'textfield', - name: 'cmd', - value: '', - fieldStyle: 'font-family: monospace;', - allowBlank: true, - listeners: { - afterrender: function(f) { - f.focus(false); - addLine("Type 'help' for help."); - refresh(); - }, - specialkey: function(f, e) { - var key = e.getKey(); - switch (key) { - case e.ENTER: - var cmd = f.getValue(); - f.setValue(''); - executeCmd(cmd); - break; - case e.PAGE_UP: - textbox.scrollBy(0, -0.9*textbox.getHeight(), false); - break; - case e.PAGE_DOWN: - textbox.scrollBy(0, 0.9*textbox.getHeight(), false); - break; - case e.UP: - if (histNum + 1 < history.length) { - f.setValue(history[++histNum]); - } - e.preventDefault(); - break; - case e.DOWN: - if (histNum > 0) { - f.setValue(history[--histNum]); - } - e.preventDefault(); - break; - default: - break; - } - } - } - } - ], - listeners: { - show: function() { - var field = me.query('textfield[name="cmd"]')[0]; - field.focus(false, true); - } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.qemu.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveQemuSummary', - - scrollable: true, - bodyPadding: 5, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - if (!me.workspace) { - throw "no workspace specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var template = !!me.pveSelNode.data.template; - var rstore = me.statusStore; - - var width = template ? 1 : 0.5; - var items = [ - { - xtype: template ? 'pveTemplateStatusView' : 'pveGuestStatusView', - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - }, - itemId: 'gueststatus', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'pveNotesView', - maxHeight: 330, - itemId: 'notesview', - pveSelNode: me.pveSelNode, - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - } - } - ]; - - var rrdstore; - if (!template) { - - rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/qemu/" + vmid + "/rrddata", - model: 'pve-rrd-guest' - }); - - items.push( - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - pveSelNode: me.pveSelNode, - fields: ['cpu'], - fieldTitles: [gettext('CPU usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - pveSelNode: me.pveSelNode, - fields: ['maxmem', 'mem'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - pveSelNode: me.pveSelNode, - fields: ['netin','netout'], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Disk IO'), - pveSelNode: me.pveSelNode, - fields: ['diskread','diskwrite'], - store: rrdstore - } - ); - - } - - Ext.apply(me, { - tbar: [ '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: { - type: 'column' - }, - defaults: { - minHeight: 330, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: items - } - ] - }); - - me.callParent(); - if (!template) { - rrdstore.startUpdate(); - me.on('destroy', rrdstore.stopUpdate); - } - } -}); -Ext.define('PVE.qemu.OSTypeInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuOSTypePanel', - onlineHelp: 'qm_os_settings', - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'combobox[name=osbase]': { - change: 'onOSBaseChange' - }, - 'combobox[name=ostype]': { - afterrender: 'onOSTypeChange', - change: 'onOSTypeChange' - } - }, - onOSBaseChange: function(field, value) { - this.lookup('ostype').getStore().setData(PVE.Utils.kvm_ostypes[value]); - }, - onOSTypeChange: function(field) { - var me = this, ostype = field.getValue(); - if (!me.getView().insideWizard) { - return; - } - var targetValues = PVE.qemu.OSDefaults.getDefaults(ostype); - - me.setWidget('pveBusSelector', targetValues.busType); - me.setWidget('pveNetworkCardSelector', targetValues.networkCard); - var scsihw = targetValues.scsihw || '__default__'; - this.getViewModel().set('current.scsihw', scsihw); - }, - setWidget: function(widget, newValue) { - // changing a widget is safe only if ComponentQuery.query returns us - // a single value array - var widgets = Ext.ComponentQuery.query('pveQemuCreateWizard ' + widget); - if (widgets.length === 1) { - widgets[0].setValue(newValue); - } else { - throw 'non unique widget :' + widget + ' in Wizard'; - } - } - }, - - initComponent : function() { - var me = this; - - /*jslint confusion: true */ - me.items = [ - { - xtype: 'displayfield', - value: gettext('Guest OS') + ':', - hidden: !me.insideWizard - }, - { - xtype: 'combobox', - submitValue: false, - name: 'osbase', - fieldLabel: gettext('Type'), - editable: false, - queryMode: 'local', - value: 'Linux', - store: Object.keys(PVE.Utils.kvm_ostypes) - }, - { - xtype: 'combobox', - name: 'ostype', - reference: 'ostype', - fieldLabel: gettext('Version'), - value: 'l26', - allowBlank : false, - editable: false, - queryMode: 'local', - valueField: 'val', - displayField: 'desc', - store: { - fields: ['desc', 'val'], - data: PVE.Utils.kvm_ostypes.Linux, - listeners: { - datachanged: function (store) { - var ostype = me.lookup('ostype'); - var old_val = ostype.getValue(); - if (!me.insideWizard && old_val && store.find('val', old_val) != -1) { - ostype.setValue(old_val); - } else { - ostype.setValue(store.getAt(0)); - } - } - } - } - } - ]; - /*jslint confusion: false */ - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.OSTypeEdit', { - extend: 'Proxmox.window.Edit', - - subject: 'OS Type', - - items: [{ xtype: 'pveQemuOSTypePanel' }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response, options) { - var value = response.result.data.ostype || 'other'; - var osinfo = PVE.Utils.get_kvm_osinfo(value); - me.setValues({ ostype: value, osbase: osinfo.base }); - } - }); - } -}); -/* - * This class holds performance *recommended* settings for the PVE Qemu wizards - * the *mandatory* settings are set in the PVE::QemuServer - * config_to_command sub - * We store this here until we get the data from the API server -*/ - -// this is how you would add an hypothetic FreeBSD > 10 entry -// -//virtio-blk is stable but virtIO net still -// problematic as of 10.3 -// see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059 -// addOS({ -// parent: 'generic', // inherits defaults -// pveOS: 'freebsd10', // must match a radiofield in OSTypeEdit.js -// busType: 'virtio' // must match a pveBusController value -// // networkCard muss match a pveNetworkCardSelector - - -Ext.define('PVE.qemu.OSDefaults', { - singleton: true, // will also force creation when loaded - - constructor: function() { - var me = this; - - var addOS = function(settings) { - if (me.hasOwnProperty(settings.parent)) { - var child = Ext.clone(me[settings.parent]); - me[settings.pveOS] = Ext.apply(child, settings); - - } else { - throw("Could not find your genitor"); - } - }; - - // default values - me.generic = { - busType: 'ide', - networkCard: 'e1000', - busPriority: { - ide: 4, - sata: 3, - scsi: 2, - virtio: 1 - }, - scsihw: 'virtio-scsi-pci' - }; - - // virtio-net is in kernel since 2.6.25 - // virtio-scsi since 3.2 but backported in RHEL with 2.6 kernel - addOS({ - pveOS: 'l26', - parent : 'generic', - busType: 'scsi', - busPriority: { - scsi: 4, - virtio: 3, - sata: 2, - ide: 1 - }, - networkCard: 'virtio' - }); - - // recommandation from http://wiki.qemu.org/Windows2000 - addOS({ - pveOS: 'w2k', - parent : 'generic', - networkCard: 'rtl8139', - scsihw: '' - }); - // https://pve.proxmox.com/wiki/Windows_XP_Guest_Notes - addOS({ - pveOS: 'wxp', - parent : 'w2k' - }); - - me.getDefaults = function(ostype) { - if (PVE.qemu.OSDefaults[ostype]) { - return PVE.qemu.OSDefaults[ostype]; - } else { - return PVE.qemu.OSDefaults.generic; - } - }; - } -}); -Ext.define('PVE.qemu.ProcessorInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuProcessorPanel', - onlineHelp: 'qm_cpu', - - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - - updateCores: function() { - var me = this.getView(); - var sockets = me.down('field[name=sockets]').getValue(); - var cores = me.down('field[name=cores]').getValue(); - me.down('field[name=totalcores]').setValue(sockets*cores); - var vcpus = me.down('field[name=vcpus]'); - vcpus.setMaxValue(sockets*cores); - vcpus.setEmptyText(sockets*cores); - vcpus.validate(); - }, - - control: { - 'field[name=sockets]': { - change: 'updateCores' - }, - 'field[name=cores]': { - change: 'updateCores' - } - } - }, - - onGetValues: function(values) { - var me = this; - - if (Array.isArray(values['delete'])) { - values['delete'] = values['delete'].join(','); - } - - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - // build the cpu options: - me.cpu.cputype = values.cputype; - - if (values.flags) { - me.cpu.flags = values.flags; - } else { - delete me.cpu.flags; - } - - delete values.cputype; - delete values.flags; - var cpustring = PVE.Parser.printQemuCpu(me.cpu); - - // remove cputype delete request: - var del = values['delete']; - delete values['delete']; - if (del) { - del = del.split(','); - Ext.Array.remove(del, 'cputype'); - } else { - del = []; - } - - if (cpustring) { - values.cpu = cpustring; - } else { - del.push('cpu'); - } - - var delarr = del.join(','); - if (delarr) { - values['delete'] = delarr; - } - - return values; - }, - - cpu: {}, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'sockets', - minValue: 1, - maxValue: 4, - value: '1', - fieldLabel: gettext('Sockets'), - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: '1', - fieldLabel: gettext('Cores'), - allowBlank: false - } - ], - - column2: [ - { - xtype: 'CPUModelSelector', - name: 'cputype', - value: '__default__', - fieldLabel: gettext('Type') - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Total cores'), - name: 'totalcores', - value: '1' - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxintegerfield', - name: 'vcpus', - minValue: 1, - maxValue: 1, - value: '', - fieldLabel: gettext('VCPUs'), - deleteEmpty: true, - allowBlank: true, - emptyText: '1' - }, - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - maxValue: 128, // api maximum - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - minValue: 8, - maxValue: 500000, - value: '1024', - deleteEmpty: true, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable NUMA'), - name: 'numa', - uncheckedValue: 0 - } - ], - advancedColumnB: [ - { - xtype: 'label', - text: 'Extra CPU Flags:' - }, - { - xtype: 'vmcpuflagselector', - name: 'flags' - } - ] -}); - -Ext.define('PVE.qemu.ProcessorEdit', { - extend: 'Proxmox.window.Edit', - - width: 700, - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.ProcessorInputPanel'); - - Ext.apply(me, { - subject: gettext('Processors'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - var value = data.cpu; - if (value) { - var cpu = PVE.Parser.parseQemuCpu(value); - ipanel.cpu = cpu; - data.cputype = cpu.cputype; - if (cpu.flags) { - data.flags = cpu.flags; - } - } - me.setValues(data); - } - }); - } -}); -Ext.define('PVE.qemu.BootOrderPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuBootOrderPanel', - vmconfig: {}, // store loaded vm config - - bootdisk: undefined, - selection: [], - list: [], - comboboxes: [], - - isBootDisk: function(value) { - return PVE.Utils.bus_match.test(value); - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - var order = me.vmconfig.boot || 'cdn'; - me.bootdisk = me.vmconfig.bootdisk || undefined; - - // get the first 3 characters - // ignore the rest (there should never be more than 3) - me.selection = order.split('').slice(0,3); - - // build bootdev list - me.list = []; - Ext.Object.each(me.vmconfig, function(key, value) { - if (me.isBootDisk(key) && - !(/media=cdrom/).test(value)) { - me.list.push([key, "Disk '" + key + "'"]); - } - }); - - me.list.push(['d', 'CD-ROM']); - me.list.push(['n', gettext('Network')]); - me.list.push(['__none__', Proxmox.Utils.noneText]); - - me.recomputeList(); - - me.comboboxes.forEach(function(box) { - box.resetOriginalValue(); - }); - }, - - onGetValues: function(values) { - var me = this; - var order = me.selection.join(''); - var res = { boot: order }; - - if (me.bootdisk && order.indexOf('c') !== -1) { - res.bootdisk = me.bootdisk; - } else { - res['delete'] = 'bootdisk'; - } - - return res; - }, - - recomputeSelection: function(combobox, newVal, oldVal) { - var me = this.up('#inputpanel'); - me.selection = []; - me.comboboxes.forEach(function(item) { - var val = item.getValue(); - - // when selecting an already selected item, - // switch it around - if ((val === newVal || (me.isBootDisk(val) && me.isBootDisk(newVal))) && - item.name !== combobox.name && - newVal !== '__none__') { - // swap items - val = oldVal; - } - - // push 'c','d' or 'n' in the array - if (me.isBootDisk(val)) { - me.selection.push('c'); - me.bootdisk = val; - } else if (val === 'd' || - val === 'n') { - me.selection.push(val); - } - }); - - me.recomputeList(); - }, - - recomputeList: function(){ - var me = this; - // set the correct values in the kvcomboboxes - var cnt = 0; - me.comboboxes.forEach(function(item) { - if (cnt === 0) { - // never show 'none' on first combobox - item.store.loadData(me.list.slice(0, me.list.length-1)); - } else { - item.store.loadData(me.list); - } - item.suspendEvent('change'); - if (cnt < me.selection.length) { - item.setValue((me.selection[cnt] !== 'c')?me.selection[cnt]:me.bootdisk); - } else if (cnt === 0){ - item.setValue(''); - } else { - item.setValue('__none__'); - } - cnt++; - item.resumeEvent('change'); - item.validate(); - }); - }, - - initComponent : function() { - var me = this; - - // this has to be done here, because of - // the way our inputPanel class handles items - me.comboboxes = [ - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 1", - labelWidth: 120, - name: 'bd1', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 2", - labelWidth: 120, - name: 'bd2', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 3", - labelWidth: 120, - name: 'bd3', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }) - ]; - Ext.apply(me, { items: me.comboboxes }); - me.callParent(); - } -}); - -Ext.define('PVE.qemu.BootOrderEdit', { - extend: 'Proxmox.window.Edit', - - items: [{ - xtype: 'pveQemuBootOrderPanel', - itemId: 'inputpanel' - }], - - subject: gettext('Boot Order'), - - initComponent : function() { - var me = this; - me.callParent(); - me.load({ - success: function(response, options) { - me.down('#inputpanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuMemoryPanel', - onlineHelp: 'qm_memory', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var res = {}; - - res.memory = values.memory; - res.balloon = values.balloon; - - if (!values.ballooning) { - res.balloon = 0; - res['delete'] = 'shares'; - } else if (values.memory === values.balloon) { - delete res.balloon; - res['delete'] = 'balloon,shares'; - } else if (Ext.isDefined(values.shares) && (values.shares !== "")) { - res.shares = values.shares; - } else { - res['delete'] = "shares"; - } - - return res; - }, - - initComponent: function() { - var me = this; - var labelWidth = 160; - - me.items= [ - { - xtype: 'pveMemoryField', - labelWidth: labelWidth, - fieldLabel: gettext('Memory') + ' (MiB)', - name: 'memory', - minValue: 1, - step: 32, - hotplug: me.hotplug, - listeners: { - change: function(f, value, old) { - var bf = me.down('field[name=balloon]'); - var balloon = bf.getValue(); - bf.setMaxValue(value); - if (balloon === old) { - bf.setValue(value); - } - bf.validate(); - } - } - } - ]; - - me.advancedItems= [ - { - xtype: 'pveMemoryField', - name: 'balloon', - minValue: 1, - step: 32, - fieldLabel: gettext('Minimum memory') + ' (MiB)', - hotplug: me.hotplug, - labelWidth: labelWidth, - allowBlank: false, - listeners: { - change: function(f, value) { - var memory = me.down('field[name=memory]').getValue(); - var shares = me.down('field[name=shares]'); - shares.setDisabled(value === memory); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'shares', - disabled: true, - minValue: 0, - maxValue: 50000, - value: '', - step: 10, - fieldLabel: gettext('Shares'), - labelWidth: labelWidth, - allowBlank: true, - emptyText: Proxmox.Utils.defaultText + ' (1000)', - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - labelWidth: labelWidth, - value: '1', - name: 'ballooning', - fieldLabel: gettext('Ballooning Device'), - listeners: { - change: function(f, value) { - var bf = me.down('field[name=balloon]'); - var shares = me.down('field[name=shares]'); - var memory = me.down('field[name=memory]'); - bf.setDisabled(!value); - shares.setDisabled(!value || (bf.getValue() === memory.getValue())); - } - } - } - ]; - - if (me.insideWizard) { - me.column1 = me.items; - me.items = undefined; - me.advancedColumn1 = me.advancedItems; - me.advancedItems = undefined; - } - me.callParent(); - } - -}); - -Ext.define('PVE.qemu.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent: function() { - var me = this; - - var memoryhotplug; - if(me.hotplug) { - Ext.each(me.hotplug.split(','), function(el) { - if (el === 'memory') { - memoryhotplug = 1; - } - }); - } - - var ipanel = Ext.create('PVE.qemu.MemoryInputPanel', { - hotplug: memoryhotplug - }); - - Ext.apply(me, { - subject: gettext('Memory'), - items: [ ipanel ], - // uncomment the following to use the async configiguration API - // backgroundDelay: 5, - width: 400 - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - - var values = { - ballooning: data.balloon === 0 ? '0' : '1', - shares: data.shares, - memory: data.memory || '512', - balloon: data.balloon > 0 ? data.balloon : (data.memory || '512') - }; - - ipanel.setValues(values); - } - }); - } -}); -Ext.define('PVE.qemu.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuNetworkInputPanel', - onlineHelp: 'qm_network_device', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - me.network.model = values.model; - if (values.nonetwork) { - return {}; - } else { - me.network.bridge = values.bridge; - me.network.tag = values.tag; - me.network.firewall = values.firewall; - } - me.network.macaddr = values.macaddr; - me.network.disconnect = values.disconnect; - me.network.queues = values.queues; - - if (values.rate) { - me.network.rate = values.rate; - } else { - delete me.network.rate; - } - - var params = {}; - - params[me.confid] = PVE.Parser.printQemuNetwork(me.network); - - return params; - }, - - setNetwork: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data) { - data.networkmode = data.bridge ? 'bridge' : 'nat'; - } else { - data = {}; - data.networkmode = 'bridge'; - } - me.network = data; - - me.setValues(me.network); - }, - - setNodename: function(nodename) { - var me = this; - - me.bridgesel.setNodename(nodename); - }, - - initComponent : function() { - var me = this; - - me.network = {}; - me.confid = 'net0'; - - me.column1 = []; - me.column2 = []; - - me.bridgesel = Ext.create('PVE.form.BridgeSelector', { - name: 'bridge', - fieldLabel: gettext('Bridge'), - nodename: me.nodename, - autoSelect: true, - allowBlank: false - }); - - me.column1 = [ - me.bridgesel, - { - xtype: 'pveVlanField', - name: 'tag', - value: '' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - checked: (me.insideWizard || me.isCreate) - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Disconnect'), - name: 'disconnect' - } - ]; - - if (me.insideWizard) { - me.column1.unshift({ - xtype: 'checkbox', - name: 'nonetwork', - inputValue: 'none', - boxLabel: gettext('No network device'), - listeners: { - change: function(cb, value) { - var fields = [ - 'disconnect', - 'bridge', - 'tag', - 'firewall', - 'model', - 'macaddr', - 'rate', - 'queues' - ]; - fields.forEach(function(fieldname) { - me.down('field[name='+fieldname+']').setDisabled(value); - }); - me.down('field[name=bridge]').validate(); - } - } - }); - me.column2.unshift({ - xtype: 'displayfield' - }); - } - - me.column2.push( - { - xtype: 'pveNetworkCardSelector', - name: 'model', - fieldLabel: gettext('Model'), - value: PVE.qemu.OSDefaults.generic.networkCard, - allowBlank: false - }, - { - xtype: 'textfield', - name: 'macaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - allowBlank: true, - emptyText: 'auto' - }); - me.advancedColumn2 = [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: '', - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'queues', - fieldLabel: 'Multiqueue', - minValue: 1, - maxValue: 8, - value: '', - allowBlank: true - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.NetworkInputPanel', { - confid: me.confid, - nodename: nodename, - isCreate: me.isCreate - }); - - Ext.applyIf(me, { - subject: gettext('Network Device'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - if (!me.isCreate) { - var value = me.vmconfig[me.confid]; - var network = PVE.Parser.parseQemuNetwork(me.confid, value); - if (!network) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse network options'); - me.close(); - return; - } - ipanel.setNetwork(me.confid, network); - } else { - for (i = 0; i < 100; i++) { - confid = 'net' + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - me.confid = confid; - break; - } - } - ipanel.setNetwork(me.confid); - } - } - }); - } -}); -Ext.define('PVE.qemu.Smbios1InputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.PVE.qemu.Smbios1InputPanel', - - insideWizard: false, - - smbios1: {}, - - onGetValues: function(values) { - var me = this; - - var params = { - smbios1: PVE.Parser.printQemuSmbios1(values) - }; - - return params; - }, - - setSmbios1: function(data) { - var me = this; - - me.smbios1 = data; - - me.setValues(me.smbios1); - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: 'UUID', - regex: /^[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$/, - name: 'uuid' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Manufacturer'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'manufacturer' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Product'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'product' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Version'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'version' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Serial'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'serial' - }, - { - xtype: 'textareafield', - fieldLabel: 'SKU', - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'sku' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Family'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'family' - } - ] -}); - -Ext.define('PVE.qemu.Smbios1Edit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var ipanel = Ext.create('PVE.qemu.Smbios1InputPanel', {}); - - Ext.applyIf(me, { - subject: gettext('SMBIOS settings (type1)'), - width: 450, - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - var value = me.vmconfig.smbios1; - if (value) { - var data = PVE.Parser.parseQemuSmbios1(value); - if (!data) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse smbios options'); - me.close(); - return; - } - ipanel.setSmbios1(data); - } - } - }); - } -}); -Ext.define('PVE.qemu.CDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuCDInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || (values.controller + values.deviceid); - - me.drive.media = 'cdrom'; - if (values.mediaType === 'iso') { - me.drive.file = values.cdimage; - } else if (values.mediaType === 'cdrom') { - me.drive.file = 'cdrom'; - } else { - me.drive.file = 'none'; - } - - var params = {}; - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig, 'cdrom'); - } - }, - - setDrive: function(drive) { - var me = this; - - var values = {}; - if (drive.file === 'cdrom') { - values.mediaType = 'cdrom'; - } else if (drive.file === 'none') { - values.mediaType = 'none'; - } else { - values.mediaType = 'iso'; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.cdstorage = match[1]; - values.cdimage = drive.file; - } - } - - me.drive = drive; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - - me.cdstoragesel.setNodename(nodename); - me.cdfilesel.setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - var items = []; - - if (!me.confid) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - noVirtIO: true - }); - items.push(me.bussel); - } - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'iso', - boxLabel: gettext('Use CD/DVD disc image file (iso)'), - checked: true, - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=cdstorage]').setDisabled(!value); - me.down('field[name=cdimage]').setDisabled(!value); - me.down('field[name=cdimage]').validate(); - } - } - }); - - me.cdfilesel = Ext.create('PVE.form.FileSelector', { - name: 'cdimage', - nodename: me.nodename, - storageContent: 'iso', - fieldLabel: gettext('ISO image'), - labelAlign: 'right', - allowBlank: false - }); - - me.cdstoragesel = Ext.create('PVE.form.StorageSelector', { - name: 'cdstorage', - nodename: me.nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'iso', - allowBlank: false, - autoSelect: me.insideWizard, - listeners: { - change: function(f, value) { - me.cdfilesel.setStorage(value); - } - } - }); - - items.push(me.cdstoragesel); - items.push(me.cdfilesel); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'cdrom', - boxLabel: gettext('Use physical CD/DVD Drive') - }); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'none', - boxLabel: gettext('Do not use any media') - }); - - me.items = items; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CDEdit', { - extend: 'Proxmox.window.Edit', - - width: 400, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.CDInputPanel', { - confid: me.confid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: 'CD/DVD Drive', - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert('Error', 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - } - } - }); - } -}); -/*jslint confusion: true */ -/* 'change' property is assigned a string and then a function */ -Ext.define('PVE.qemu.HDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuHDInputPanel', - onlineHelp: 'qm_hard_disk', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - controller: { - - xclass: 'Ext.app.ViewController', - - onControllerChange: function(field) { - var value = field.getValue(); - - var allowIOthread = value.match(/^(virtio|scsi)/); - this.lookup('iothread').setDisabled(!allowIOthread); - if (!allowIOthread) { - this.lookup('iothread').setValue(false); - } - - var virtio = value.match(/^virtio/); - this.lookup('discard').setDisabled(virtio); - this.lookup('ssd').setDisabled(virtio); - if (virtio) { - this.lookup('discard').setValue(false); - this.lookup('ssd').setValue(false); - } - - this.lookup('scsiController').setVisible(value.match(/^scsi/)); - }, - - control: { - 'field[name=controller]': { - change: 'onControllerChange', - afterrender: 'onControllerChange' - }, - 'field[name=iothread]' : { - change: function(f, value) { - if (!this.getView().insideWizard) { - return; - } - var vmScsiType = value ? 'virtio-scsi-single': 'virtio-scsi-pci'; - this.lookupReference('scsiController').setValue(vmScsiType); - } - } - } - }, - - onGetValues: function(values) { - var me = this; - - var params = {}; - var confid = me.confid || (values.controller + values.deviceid); - - if (me.unused) { - me.drive.file = me.vmconfig[values.unusedId]; - confid = values.controller + values.deviceid; - } else if (me.isCreate) { - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - me.drive.file = values.hdstorage + ":" + values.disksize; - } - me.drive.format = values.diskformat; - } - - if (values.nobackup) { - me.drive.backup = 'no'; - } else { - delete me.drive.backup; - } - - if (values.noreplicate) { - me.drive.replicate = 'no'; - } else { - delete me.drive.replicate; - } - - if (values.discard) { - me.drive.discard = 'on'; - } else { - delete me.drive.discard; - } - - if (values.ssd) { - me.drive.ssd = 'on'; - } else { - delete me.drive.ssd; - } - - if (values.iothread) { - me.drive.iothread = 'on'; - } else { - delete me.drive.iothread; - } - - if (values.cache) { - me.drive.cache = values.cache; - } else { - delete me.drive.cache; - } - - var names = ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr']; - Ext.Array.each(names, function(name) { - if (values[name]) { - me.drive[name] = values[name]; - } else { - delete me.drive[name]; - } - var burst_name = name + '_max'; - if (values[burst_name] && values[name]) { - me.drive[burst_name] = values[burst_name]; - } else { - delete me.drive[burst_name]; - } - }); - - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - me.vmconfig = vmconfig; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig); - me.scsiController.setValue(vmconfig.scsihw); - } - if (me.unusedDisks) { - var disklist = []; - Ext.Object.each(vmconfig, function(key, value) { - if (key.match(/^unused\d+$/)) { - disklist.push([key, value]); - } - }); - me.unusedDisks.store.loadData(disklist); - me.unusedDisks.setValue(me.confid); - } - }, - - setDrive: function(drive) { - var me = this; - - me.drive = drive; - - var values = {}; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.hdstorage = match[1]; - } - - values.hdimage = drive.file; - values.nobackup = !PVE.Parser.parseBoolean(drive.backup, 1); - values.noreplicate = !PVE.Parser.parseBoolean(drive.replicate, 1); - values.diskformat = drive.format || 'raw'; - values.cache = drive.cache || '__default__'; - values.discard = (drive.discard === 'on'); - values.ssd = PVE.Parser.parseBoolean(drive.ssd); - values.iothread = PVE.Parser.parseBoolean(drive.iothread); - - values.mbps_rd = drive.mbps_rd; - values.mbps_wr = drive.mbps_wr; - values.iops_rd = drive.iops_rd; - values.iops_wr = drive.iops_wr; - values.mbps_rd_max = drive.mbps_rd_max; - values.mbps_wr_max = drive.mbps_wr_max; - values.iops_rd_max = drive.iops_rd_max; - values.iops_wr_max = drive.iops_wr_max; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - var labelWidth = 140; - - me.drive = {}; - - me.column1 = []; - me.column2 = []; - - me.advancedColumn1 = []; - me.advancedColumn2 = []; - - if (!me.confid || me.unused) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - vmconfig: me.insideWizard ? {ide2: 'cdrom'} : {} - }); - me.column1.push(me.bussel); - - me.scsiController = Ext.create('Ext.form.field.Display', { - fieldLabel: gettext('SCSI Controller'), - reference: 'scsiController', - bind: me.insideWizard ? { - value: '{current.scsihw}' - } : undefined, - renderer: PVE.Utils.render_scsihw, - submitValue: false, - hidden: true - }); - me.column1.push(me.scsiController); - } - - if (me.unused) { - me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', { - name: 'unusedId', - fieldLabel: gettext('Disk image'), - matchFieldWidth: false, - listConfig: { - width: 350 - }, - data: [], - allowBlank: false - }); - me.column1.push(me.unusedDisks); - } else if (me.isCreate) { - me.column1.push({ - xtype: 'pveDiskStorageSelector', - storageContent: 'images', - name: 'disk', - nodename: me.nodename, - autoSelect: me.insideWizard - }); - } else { - me.column1.push({ - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'hdimage' - }); - } - - me.column2.push( - { - xtype: 'CacheTypeSelector', - name: 'cache', - value: '__default__', - fieldLabel: gettext('Cache') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Discard'), - disabled: me.confid && me.confid.match(/^virtio/), - reference: 'discard', - name: 'discard' - } - ); - - me.advancedColumn1.push( - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && me.confid.match(/^virtio/), - fieldLabel: gettext('SSD emulation'), - labelWidth: labelWidth, - name: 'ssd', - reference: 'ssd' - }, - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && !me.confid.match(/^(virtio|scsi)/), - fieldLabel: 'IO thread', - labelWidth: labelWidth, - reference: 'iothread', - name: 'iothread' - }, - { - xtype: 'numberfield', - name: 'mbps_rd', - minValue: 1, - step: 1, - fieldLabel: gettext('Read limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'numberfield', - name: 'mbps_wr', - minValue: 1, - step: 1, - fieldLabel: gettext('Write limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd', - minValue: 10, - step: 10, - fieldLabel: gettext('Read limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr', - minValue: 10, - step: 10, - fieldLabel: gettext('Write limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - } - ); - - me.advancedColumn2.push( - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('No backup'), - labelWidth: labelWidth, - name: 'nobackup' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Skip replication'), - labelWidth: labelWidth, - name: 'noreplicate' - }, - { - xtype: 'numberfield', - name: 'mbps_rd_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Read max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'numberfield', - name: 'mbps_wr_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Write max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Read max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Write max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - } - ); - - me.callParent(); - } -}); -/*jslint confusion: false */ - -Ext.define('PVE.qemu.HDEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - backgroundDelay: 5, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.qemu.HDInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - me.subject = gettext('Unused Disk'); - } else if (me.isCreate) { - me.subject = gettext('Hard Disk'); - } else { - me.subject = gettext('Hard Disk') + ' (' + me.confid + ')'; - } - - me.items = [ ipanel ]; - - me.callParent(); - /*jslint confusion: true*/ - /* 'data' is assigned an empty array in same file, and here we - * use it like an object - */ - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - me.isValid(); // trigger validation - } - } - }); - /*jslint confusion: false*/ - } -}); -Ext.define('PVE.window.HDResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 140, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 250, - height: 150, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -Ext.define('PVE.window.HDMove', { - extend: 'Ext.window.Window', - - resizable: false, - - - move_disk: function(disk, storage, format, delete_disk) { - var me = this; - var qemu = (me.type === 'qemu'); - var params = {}; - params.storage = storage; - params[qemu ? 'disk':'volume'] = disk; - - if (format && qemu) { - params.format = format; - } - - if (delete_disk) { - params['delete'] = 1; - } - - var url = '/nodes/' + me.nodename + '/' + me.type + '/' + me.vmid + '/'; - url += qemu ? 'move_disk' : 'move_volume'; - - Proxmox.Utils.API2Request({ - params: params, - url: url, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - win.on('destroy', function() { me.close(); }); - } - }); - - }, - - initComponent : function() { - var me = this; - - var diskarray = []; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.type) { - me.type = 'qemu'; - } - - var qemu = (me.type === 'qemu'); - - var items = [ - { - xtype: 'displayfield', - name: qemu ? 'disk' : 'volume', - value: me.disk, - fieldLabel: qemu ? gettext('Disk') : gettext('Mount Point'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - items.push({ - xtype: 'pveDiskStorageSelector', - storageLabel: gettext('Target Storage'), - nodename: me.nodename, - storageContent: qemu ? 'images' : 'rootdir', - hideSize: true - }); - - items.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Delete source'), - name: 'deleteDisk', - uncheckedValue: 0, - checked: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = qemu ? gettext("Move disk") : gettext('Move Volume'); - submitBtn = Ext.create('Ext.Button', { - text: me.title, - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.move_disk(me.disk, values.hdstorage, values.diskformat, - values.deleteDisk); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 350, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - me.mon(me.formPanel, 'validitychange', function(fp, isValid) { - submitBtn.setDisabled(!isValid); - }); - - me.formPanel.isValid(); - } -}); -Ext.define('PVE.qemu.EFIDiskInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveEFIDiskInputPanel', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var confid = 'efidisk0'; - - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - // we use 1 here, because for efi the size gets overridden from the backend - me.drive.file = values.hdstorage + ":1"; - } - - me.drive.format = values.diskformat; - var params = {}; - params[confid] = PVE.Parser.printQemuDrive(me.drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items= []; - - me.items.push({ - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - }); - me.callParent(); - } -}); - -Ext.define('PVE.qemu.EFIDiskEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - subject: gettext('EFI Disk'), - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveEFIDiskInputPanel', - onlineHelp: 'qm_bios_and_uefi', - confid: me.confid, - nodename: nodename, - isCreate: true - }]; - - me.callParent(); - } -}); -Ext.define('PVE.qemu.DisplayInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveDisplayInputPanel', - - onGetValues: function(values) { - var ret = PVE.Parser.printPropertyString(values, 'type'); - if (ret === '') { - return { - 'delete': 'vga' - }; - } - return { - vga: ret - }; - }, - - items: [{ - name: 'type', - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - comboItems: PVE.Utils.kvm_vga_driver_array(), - validator: function() { - var v = this.getValue(); - var cfg = this.up('proxmoxWindowEdit').vmconfig || {}; - - if (v.match(/^serial\d+$/) && (!cfg[v] || cfg[v] !== 'socket')) { - var fmt = gettext("Serial interface '{0}' is not correctly configured."); - return Ext.String.format(fmt, v); - } - return true; - }, - listeners: { - change: function(cb, val) { - var me = this.up('panel'); - if (!val) { - return; - } - var disable = false; - var emptyText = Proxmox.Utils.defaultText; - switch (val) { - case "cirrus": - emptyText = "4"; - break; - case "std": - emptyText = "16"; - break; - case "qxl": - case "qxl2": - case "qxl3": - case "qxl4": - emptyText = "16"; - break; - case "vmware": - emptyText = "16"; - break; - case "none": - case "serial0": - case "serial1": - case "serial2": - case "serial3": - emptyText = 'N/A'; - disable = true; - break; - case "virtio": - emptyText = "256"; - break; - default: - break; - } - var memoryfield = me.down('field[name=memory]'); - memoryfield.setEmptyText(emptyText); - memoryfield.setDisabled(disable); - } - } - },{ - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Memory') + ' (MiB)', - minValue: 4, - maxValue: 512, - step: 4, - name: 'memory' - }] -}); - -Ext.define('PVE.qemu.DisplayEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - subject: gettext('Display'), - width: 350, - - items: [{ - xtype: 'pveDisplayInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response) { - me.vmconfig = response.result.data; - var vga = me.vmconfig.vga || '__default__'; - me.setValues(PVE.Parser.parsePropertyString(vga, 'type')); - } - }); - } -}); -Ext.define('PVE.qemu.KeyboardEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('Keyboard Layout'), - items: { - xtype: 'VNCKeyboardSelector', - name: 'keyboard', - value: '__default__', - fieldLabel: gettext('Keyboard Layout') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.HardwareView', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.HardwareView'], - - onlineHelp: 'qm_virtual_machines_settings', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - var iconCls = rowdef.iconCls; - var icon = ''; - var txt = (rowdef.header || key); - - metaData.tdAttr = "valign=middle"; - - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - if (rowdef.tdCls == 'pve-itype-icon-storage') { - var value = me.getObjectValue(key, '', false); - if (value === '') { - value = me.getObjectValue(key, '', true); - } - if (value.match(/vm-.*-cloudinit/)) { - metaData.tdCls = 'pve-itype-icon-cloud'; - return rowdef.cloudheader; - } else if (value.match(/media=cdrom/)) { - metaData.tdCls = 'pve-itype-icon-cdrom'; - return rowdef.cdheader; - } - } - } else if (iconCls) { - icon = ""; - metaData.tdCls += " pve-itype-fa"; - } - return icon + txt; - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - /*jslint confusion: true */ - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined, - never_delete: true, - defaultValue: '512', - tdCls: 'pve-itype-icon-memory', - group: 2, - multiKey: ['memory', 'balloon', 'shares'], - renderer: function(value, metaData, record, ri, ci, store, pending) { - var res = ''; - - var max = me.getObjectValue('memory', 512, pending); - var balloon = me.getObjectValue('balloon', undefined, pending); - var shares = me.getObjectValue('shares', undefined, pending); - - res = Proxmox.Utils.format_size(max*1024*1024); - - if (balloon !== undefined && balloon > 0) { - res = Proxmox.Utils.format_size(balloon*1024*1024) + "/" + res; - - if (shares) { - res += ' [shares=' + shares +']'; - } - } else if (balloon === 0) { - res += ' [balloon=0]'; - } - return res; - } - }, - sockets: { - header: gettext('Processors'), - never_delete: true, - editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ? - 'PVE.qemu.ProcessorEdit' : undefined, - tdCls: 'pve-itype-icon-processor', - group: 3, - defaultValue: '1', - multiKey: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'], - renderer: function(value, metaData, record, rowIndex, colIndex, store, pending) { - - var sockets = me.getObjectValue('sockets', 1, pending); - var model = me.getObjectValue('cpu', undefined, pending); - var cores = me.getObjectValue('cores', 1, pending); - var numa = me.getObjectValue('numa', undefined, pending); - var vcpus = me.getObjectValue('vcpus', undefined, pending); - var cpulimit = me.getObjectValue('cpulimit', undefined, pending); - var cpuunits = me.getObjectValue('cpuunits', undefined, pending); - - var res = Ext.String.format('{0} ({1} sockets, {2} cores)', - sockets*cores, sockets, cores); - - if (model) { - res += ' [' + model + ']'; - } - - if (numa) { - res += ' [numa=' + numa +']'; - } - - if (vcpus) { - res += ' [vcpus=' + vcpus +']'; - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit +']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits +']'; - } - - return res; - } - }, - bios: { - header: 'BIOS', - group: 4, - never_delete: true, - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.BiosEdit' : undefined, - defaultValue: '', - iconCls: 'microchip', - renderer: PVE.Utils.render_qemu_bios - }, - vga: { - header: gettext('Display'), - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined, - never_delete: true, - tdCls: 'pve-itype-icon-display', - group:5, - defaultValue: '', - renderer: PVE.Utils.render_kvm_vga_driver - }, - machine: { - header: gettext('Machine'), - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Machine'), - width: 350, - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - }]} : undefined, - iconCls: 'cogs', - never_delete: true, - group: 6, - defaultValue: '', - renderer: PVE.Utils.render_qemu_machine - }, - scsihw: { - header: gettext('SCSI Controller'), - iconCls: 'database', - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.ScsiHwEdit' : undefined, - renderer: PVE.Utils.render_scsihw, - group: 7, - never_delete: true, - defaultValue: '' - }, - cores: { - visible: false - }, - cpu: { - visible: false - }, - numa: { - visible: false - }, - balloon: { - visible: false - }, - hotplug: { - visible: false - }, - vcpus: { - visible: false - }, - cpuunits: { - visible: false - }, - cpulimit: { - visible: false - }, - shares: { - visible: false - } - }; - /*jslint confusion: false */ - - PVE.Utils.forEachBus(undefined, function(type, id) { - var confid = type + id; - rows[confid] = { - group: 10, - tdCls: 'pve-itype-icon-storage', - editor: 'PVE.qemu.HDEdit', - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - header: gettext('Hard Disk') + ' (' + confid +')', - cdheader: gettext('CD/DVD Drive') + ' (' + confid +')', - cloudheader: gettext('CloudInit Drive') + ' (' + confid + ')' - }; - }); - for (i = 0; i < 32; i++) { - confid = "net" + i.toString(); - rows[confid] = { - group: 15, - order: i, - tdCls: 'pve-itype-icon-network', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined, - never_delete: caps.vms['VM.Config.Network'] ? false : true, - header: gettext('Network Device') + ' (' + confid +')' - }; - } - rows.efidisk0 = { - group: 20, - tdCls: 'pve-itype-icon-storage', - editor: null, - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - header: gettext('EFI Disk') - }; - for (i = 0; i < 5; i++) { - confid = "usb" + i.toString(); - rows[confid] = { - group: 25, - order: i, - tdCls: 'pve-itype-icon-usb', - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined, - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('USB Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < 4; i++) { - confid = "hostpci" + i.toString(); - rows[confid] = { - group: 30, - order: i, - tdCls: 'pve-itype-icon-pci', - never_delete: caps.nodes['Sys.Console'] ? false : true, - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.PCIEdit' : undefined, - header: gettext('PCI Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < 4; i++) { - confid = "serial" + i.toString(); - rows[confid] = { - group: 35, - order: i, - tdCls: 'pve-itype-icon-serial', - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('Serial Port') + ' (' + confid + ')' - }; - } - for (i = 0; i < 256; i++) { - rows["unused" + i.toString()] = { - group: 99, - order: i, - tdCls: 'pve-itype-icon-storage', - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined, - header: gettext('Unused Disk') + ' ' + i.toString() - }; - } - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - var reload = function() { - me.rstore.load(); - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var editor = rowdef.editor; - if (rowdef.tdCls == 'pve-itype-icon-storage') { - var value = me.getObjectValue(rec.data.key, '', true); - if (value.match(/vm-.*-cloudinit/)) { - return; - } else if (value.match(/media=cdrom/)) { - editor = 'PVE.qemu.CDEdit'; - } else if (!diskCap) { - return; - } - } - - var win; - - if (Ext.isString(editor)) { - win = Ext.create(editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', reload); - }; - - var run_resize = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', reload); - }; - - var run_move = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', reload); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - handler: run_editor - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: sm, - disabled: true, - handler: run_resize - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move disk'), - selModel: sm, - disabled: true, - handler: run_move - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - defaultText: gettext('Remove'), - altText: gettext('Detach'), - selModel: sm, - disabled: true, - dangerous: true, - RESTMethod: 'PUT', - confirmMsg: function(rec) { - var warn = gettext('Are you sure you want to remove entry {0}'); - if (this.text === this.altText) { - warn = gettext('Are you sure you want to detach entry {0}'); - } - - var entry = rec.data.key; - var msg = Ext.String.format(warn, "'" - + me.renderKey(entry, {}, rec) + "'"); - - if (entry.match(/^unused\d+$/)) { - msg += " " + gettext('This will permanently erase all data.'); - } - - return msg; - }, - handler: function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: b.RESTMethod, - params: { - 'delete': rec.data.key - }, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - if (b.RESTMethod === 'POST') { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: function () { - me.reload(); - } - } - }); - win.show(); - } - } - }); - }, - listeners: { - render: function(btn) { - // hack: calculate an optimal button width on first display - // to prevent the whole toolbar to move when we switch - // between the "Remove" and "Detach" labels - var def = btn.getSize().width; - - btn.setText(btn.altText); - var alt = btn.getSize().width; - - btn.setText(btn.defaultText); - - var optimal = alt > def ? alt : def; - btn.setSize({ width: optimal }); - } - } - }); - - var revert_btn = new Proxmox.button.Button({ - text: gettext('Revert'), - selModel: sm, - disabled: true, - handler: function(b, e, rec) { - var rowdef = me.rows[rec.data.key] || {}; - var keys = rowdef.multiKey || [ rec.data.key ]; - var revert = keys.join(','); - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'revert': revert - }, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - } - }); - } - }); - - var efidisk_menuitem = Ext.create('Ext.menu.Item',{ - text: gettext('EFI Disk'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - - var rstoredata = me.rstore.getData().map; - // check if ovmf is configured - if (rstoredata.bios && rstoredata.bios.data.value === 'ovmf') { - var win = Ext.create('PVE.qemu.EFIDiskEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } else { - Ext.Msg.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.')); - } - - } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - // disable button when we have an efidisk already - // disable is ok in this case, because you can instantly - // see that there is already one - efidisk_menuitem.setDisabled(me.rstore.getData().map.efidisk0 !== undefined); - // en/disable usb add button - var usbcount = 0; - var pcicount = 0; - var hasCloudInit = false; - me.rstore.getData().items.forEach(function(item){ - if (/^usb\d+/.test(item.id)) { - usbcount++; - } else if (/^hostpci\d+/.test(item.id)) { - pcicount++; - } - if (!hasCloudInit && /vm-.*-cloudinit/.test(item.data.value)) { - hasCloudInit = true; - } - }); - - // heuristic only for disabling some stuff, the backend has the final word. - var noSysConsolePerm = !caps.nodes['Sys.Console']; - - me.down('#addusb').setDisabled(noSysConsolePerm || (usbcount >= 5)); - me.down('#addpci').setDisabled(noSysConsolePerm || (pcicount >= 4)); - me.down('#addci').setDisabled(noSysConsolePerm || hasCloudInit); - - if (!rec) { - remove_btn.disable(); - edit_btn.disable(); - resize_btn.disable(); - move_btn.disable(); - revert_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var isCDRom = (value && !!value.toString().match(/media=cdrom/)); - var isUnusedDisk = key.match(/^unused\d+/); - var isUsedDisk = !isUnusedDisk && - rowdef.tdCls == 'pve-itype-icon-storage' && - !isCDRom; - - var isCloudInit = (value && value.toString().match(/vm-.*-cloudinit/)); - - var isEfi = (key === 'efidisk0'); - - remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete === true) || (isUnusedDisk && !diskCap)); - remove_btn.setText((isUsedDisk && !isCloudInit) ? remove_btn.altText : remove_btn.defaultText); - remove_btn.RESTMethod = isUnusedDisk ? 'POST':'PUT'; - - edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor || isCloudInit || (!isCDRom && !diskCap)); - - resize_btn.setDisabled(pending || !isUsedDisk || !diskCap); - - move_btn.setDisabled(pending || !isUsedDisk || !diskCap); - - revert_btn.setDisabled(!pending); - - }; - - Ext.apply(me, { - url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + '/pending', - interval: 5000, - selModel: sm, - run_editor: run_editor, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Hard Disk'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.HDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('CD/DVD Drive'), - iconCls: 'pve-itype-icon-cdrom', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.CDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Network Device'), - iconCls: 'pve-itype-icon-network', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.qemu.NetworkEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode, - isCreate: true - }); - win.on('destroy', reload); - win.show(); - } - }, - efidisk_menuitem, - { - text: gettext('USB Device'), - itemId: 'addusb', - iconCls: 'pve-itype-icon-usb', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.USBEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('PCI Device'), - itemId: 'addpci', - iconCls: 'pve-itype-icon-pci', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.PCIEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Serial Port'), - itemId: 'addserial', - iconCls: 'pve-itype-icon-serial', - disabled: !caps.vms['VM.Config.Options'], - handler: function() { - var win = Ext.create('PVE.qemu.SerialEdit', { - url: '/api2/extjs/' + baseurl - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('CloudInit Drive'), - itemId: 'addci', - iconCls: 'pve-itype-icon-cloud', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.CIDriveEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - remove_btn, - edit_btn, - resize_btn, - move_btn, - revert_btn - ], - rows: rows, - sorterFn: sorterFn, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - me.mon(me.rstore, 'refresh', function() { - set_button_status(); - }); - } -}); -Ext.define('PVE.qemu.ScsiHwEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('SCSI Controller Type'), - items: { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - fieldLabel: gettext('Type') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.BiosEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveQemuBiosEdit', - - initComponent : function() { - var me = this; - - var EFIHint = Ext.createWidget({ - xtype: 'displayfield', //submitValue is false, so we don't get submitted - userCls: 'pve-hint', - value: 'You need to add an EFI disk for storing the ' + - 'EFI settings. See the online help for details.', - hidden: true - }); - - Ext.applyIf(me, { - subject: 'BIOS', - items: [ { - xtype: 'pveQemuBiosSelector', - onlineHelp: 'qm_bios_and_uefi', - name: 'bios', - value: '__default__', - fieldLabel: 'BIOS', - listeners: { - 'change' : function(field, newValue) { - if (newValue == 'ovmf') { - Proxmox.Utils.API2Request({ - url : me.url, - method : 'GET', - failure : function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success : function(response, opts) { - var vmConfig = response.result.data; - // there can be only one - if (!vmConfig.efidisk0) { - EFIHint.setVisible(true); - } - } - }); - } else { - if (EFIHint.isVisible()) { - EFIHint.setVisible(false); - } - } - } - } - }, - EFIHint - ] }); - - me.callParent(); - - me.load(); - - } -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.Options', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.Options'], - - onlineHelp: 'qm_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - name: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Name'), - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Name'), - items: { - xtype: 'inputpanel', - items:{ - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - }, - onGetValues: function(values) { - var params = values; - if (values.name === undefined || - values.name === null || - values.name === '') { - params = { 'delete':'name'}; - } - return params; - } - } - } : undefined - }, - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'qm_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.OSTypeEdit' : undefined, - renderer: PVE.Utils.render_kvm_ostype, - defaultValue: 'other' - }, - bootdisk: { - visible: false - }, - boot: { - header: gettext('Boot Order'), - defaultValue: 'cdn', - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.BootOrderEdit' : undefined, - multiKey: ['boot', 'bootdisk'], - renderer: function(order, metaData, record, rowIndex, colIndex, store, pending) { - var i; - var text = ''; - var bootdisk = me.getObjectValue('bootdisk', undefined, pending); - order = order || 'cdn'; - for (i = 0; i < order.length; i++) { - var sel = order.substring(i, i + 1); - if (text) { - text += ', '; - } - if (sel === 'c') { - if (bootdisk) { - text += "Disk '" + bootdisk + "'"; - } else { - text += "Disk"; - } - } else if (sel === 'n') { - text += 'Network'; - } else if (sel === 'a') { - text += 'Floppy'; - } else if (sel === 'd') { - text += 'CD-ROM'; - } else { - text += sel; - } - } - return text; - } - }, - tablet: { - header: gettext('Use tablet for pointer'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use tablet for pointer'), - items: { - xtype: 'proxmoxcheckbox', - name: 'tablet', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - hotplug: { - header: gettext('Hotplug'), - defaultValue: 'disk,network,usb', - renderer: PVE.Utils.render_hotplug_features, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hotplug'), - items: { - xtype: 'pveHotplugFeatureSelector', - name: 'hotplug', - value: '', - multiSelect: true, - fieldLabel: gettext('Hotplug'), - allowBlank: true - } - } : undefined - }, - acpi: { - header: gettext('ACPI support'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('ACPI support'), - items: { - xtype: 'proxmoxcheckbox', - name: 'acpi', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - kvm: { - header: gettext('KVM hardware virtualization'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('KVM hardware virtualization'), - items: { - xtype: 'proxmoxcheckbox', - name: 'kvm', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - freeze: { - header: gettext('Freeze CPU at startup'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.PowerMgmt'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Freeze CPU at startup'), - items: { - xtype: 'proxmoxcheckbox', - name: 'freeze', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Freeze CPU at startup') - } - } : undefined - }, - localtime: { - header: gettext('Use local time for RTC'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use local time for RTC'), - items: { - xtype: 'proxmoxcheckbox', - name: 'localtime', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Use local time for RTC') - } - } : undefined - }, - startdate: { - header: gettext('RTC start date'), - defaultValue: 'now', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('RTC start date'), - items: { - xtype: 'proxmoxtextfield', - name: 'startdate', - deleteEmpty: true, - value: 'now', - fieldLabel: gettext('RTC start date'), - vtype: 'QemuStartDate', - allowBlank: true - } - } : undefined - }, - smbios1: { - header: gettext('SMBIOS settings (type1)'), - defaultValue: '', - renderer: Ext.String.htmlEncode, - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.Smbios1Edit' : undefined - }, - agent: { - header: gettext('Qemu Agent'), - defaultValue: false, - renderer: PVE.Utils.render_qga_features, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Qemu Agent'), - items: { - xtype: 'pveAgentFeatureSelector', - name: 'agent' - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var revert_btn = new Proxmox.button.Button({ - text: gettext('Revert'), - disabled: true, - handler: function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = me.rows[rec.data.key] || {}; - var keys = rowdef.multiKey || [ rec.data.key ]; - var revert = keys.join(','); - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'revert': revert - }, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - } - }); - } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - - var key = rec.data.key; - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var rowdef = rows[key]; - - edit_btn.setDisabled(!rowdef.editor); - revert_btn.setDisabled(!pending); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/qemu/" + vmid + "/pending", - interval: 5000, - cwidth1: 250, - tbar: [ edit_btn, revert_btn ], - rows: rows, - editorConfig: { - url: "/api2/extjs/" + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - me.rstore.on('datachanged', function() { - set_button_status(); - }); - } -}); - -Ext.define('PVE.window.Snapshot', { - extend: 'Ext.window.Window', - - resizable: false, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - defaultFocus: 'field', - - take_snapshot: function(snapname, descr, vmstate) { - var me = this; - var params = { snapname: snapname, vmstate: vmstate ? 1 : 0 }; - if (descr) { - params.description = descr; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot", - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - update_snapshot: function(snapname, descr) { - var me = this; - Proxmox.Utils.API2Request({ - params: { description: descr }, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot/" + - snapname + '/config', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var summarystore = Ext.create('Ext.data.Store', { - model: 'KeyValue', - sorters: [ - { - property : 'key', - direction: 'ASC' - } - ] - }); - - var items = [ - { - xtype: me.snapname ? 'displayfield' : 'textfield', - name: 'snapname', - value: me.snapname, - fieldLabel: gettext('Name'), - vtype: 'ConfigId', - allowBlank: false - } - ]; - - if (me.snapname) { - items.push({ - xtype: 'displayfield', - name: 'snaptime', - renderer: PVE.Utils.render_timestamp_human_readable, - fieldLabel: gettext('Timestamp') - }); - } else { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'vmstate', - uncheckedValue: 0, - defaultValue: 0, - checked: 1, - fieldLabel: gettext('Include RAM') - }); - } - - items.push({ - xtype: 'textareafield', - grow: true, - name: 'description', - fieldLabel: gettext('Description') - }); - - if (me.snapname) { - items.push({ - title: gettext('Settings'), - xtype: 'grid', - height: 200, - store: summarystore, - columns: [ - {header: gettext('Key'), width: 150, dataIndex: 'key'}, - {header: gettext('Value'), flex: 1, dataIndex: 'value'} - ] - }); - } - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - if (me.snapname) { - me.title = gettext('Edit') + ': ' + gettext('Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Update'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.update_snapshot(me.snapname, values.description); - } - } - }); - } else { - me.title ="VM " + me.vmid + ': ' + gettext('Take Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Take Snapshot'), - reference: 'submitbutton', - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.take_snapshot(values.snapname, values.description, values.vmstate); - } - } - }); - } - - Ext.apply(me, { - modal: true, - width: 450, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - if (me.snapname) { - Ext.apply(me, { - width: 620, - height: 420 - }); - } - - me.callParent(); - - if (!me.snapname) { - return; - } - - // else load data - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot/" + - me.snapname + '/config', - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.close(); - }, - success: function(response, options) { - var data = response.result.data; - var kvarray = []; - Ext.Object.each(data, function(key, value) { - if (key === 'description' || key === 'snaptime') { - return; - } - kvarray.push({ key: key, value: value }); - }); - - summarystore.suspendEvents(); - summarystore.add(kvarray); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh', summarystore); - - form.findField('snaptime').setValue(data.snaptime); - form.findField('description').setValue(data.description); - } - }); - } -}); -Ext.define('PVE.qemu.SnapshotTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveQemuSnapshotTree'], - - load_delay: 3000, - - old_digest: 'invalid', - - stateful: true, - stateId: 'grid-qemu-snapshots', - - sorterFn: function(rec1, rec2) { - var v1 = rec1.data.snaptime; - var v2 = rec2.data.snaptime; - - if (rec1.data.name === 'current') { - return 1; - } - if (rec2.data.name === 'current') { - return -1; - } - - return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); - }, - - reload: function(repeat) { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot', - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - me.load_task.delay(me.load_delay); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var digest = 'invalid'; - var idhash = {}; - var root = { name: '__root', expanded: true, children: [] }; - Ext.Array.each(response.result.data, function(item) { - item.leaf = true; - item.children = []; - if (item.name === 'current') { - digest = item.digest + item.running; - if (item.running) { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running'; - } else { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree'; - } - } else { - item.iconCls = 'fa fa-fw fa-history x-fa-tree'; - } - idhash[item.name] = item; - }); - - if (digest !== me.old_digest) { - me.old_digest = digest; - - Ext.Array.each(response.result.data, function(item) { - if (item.parent && idhash[item.parent]) { - var parent_item = idhash[item.parent]; - parent_item.children.push(item); - parent_item.leaf = false; - parent_item.expanded = true; - parent_item.expandable = false; - } else { - root.children.push(item); - } - }); - - me.setRootNode(root); - } - - me.load_task.delay(me.load_delay); - } - }); - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/feature', - params: { feature: 'snapshot' }, - method: 'GET', - success: function(response, options) { - var res = response.result.data; - if (res.hasFeature) { - var snpBtns = Ext.ComponentQuery.query('#snapshotBtn'); - snpBtns.forEach(function(item){ - item.enable(); - }); - } - } - }); - - - }, - - listeners: { - beforestatesave: function(grid, state, eopts) { - // extjs cannot serialize functions, - // so a the sorter with only the sorterFn will - // not be a valid sorter when restoring the state - delete state.storeState.sorters; - } - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.vmid = me.pveSelNode.data.vmid; - if (!me.vmid) { - throw "no VM ID specified"; - } - - me.load_task = new Ext.util.DelayedTask(me.reload, me); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var valid_snapshot = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current'; - }; - - var valid_snapshot_rollback = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current' && !record.data.snapstate; - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (valid_snapshot(rec)) { - var win = Ext.create('PVE.window.Snapshot', { - snapname: rec.data.name, - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - me.mon(win, 'close', me.reload, me); - } - }; - - var editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot, - handler: run_editor - }); - - var rollbackBtn = new Proxmox.button.Button({ - text: gettext('Rollback'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot_rollback, - confirmMsg: function(rec) { - return Proxmox.Utils.format_task_description('qmrollback', me.vmid) + - " '" + rec.data.name + "'"; - }, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname + '/rollback', - method: 'POST', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var removeBtn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.name + "'"); - return msg; - }, - enableFn: valid_snapshot, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var snapshotBtn = Ext.create('Ext.Button', { - itemId: 'snapshotBtn', - text: gettext('Take Snapshot'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.window.Snapshot', { - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - } - }); - - Ext.apply(me, { - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - selModel: sm, - tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ], - fields: [ - 'name', 'description', 'snapstate', 'vmstate', 'running', - { name: 'snaptime', type: 'date', dateFormat: 'timestamp' } - ], - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - width: 200, - renderer: function(value, metaData, record) { - if (value === 'current') { - return "NOW"; - } else { - return value; - } - } - }, - { - text: gettext('RAM'), - align: 'center', - resizable: false, - dataIndex: 'vmstate', - width: 50, - renderer: function(value, metaData, record) { - if (record.data.name !== 'current') { - return Proxmox.Utils.format_boolean(value); - } - } - }, - { - text: gettext('Date') + "/" + gettext("Status"), - dataIndex: 'snaptime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.snapstate) { - return record.data.snapstate; - } - if (value) { - return Ext.Date.format(value,'Y-m-d H:i:s'); - } - } - }, - { - text: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value, metaData, record) { - if (record.data.name === 'current') { - return gettext("You are here!"); - } else { - return Ext.String.htmlEncode(value); - } - } - } - ], - columnLines: true, // will work in 4.1? - listeners: { - activate: me.reload, - destroy: me.load_task.cancel, - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.store.sorters.add(new Ext.util.Sorter({ - sorterFn: me.sorterFn - })); - } -}); - -Ext.define('PVE.qemu.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.qemu.Config', - - onlineHelp: 'chapter_virtual_machines', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!me.pveSelNode.data.template; - - var running = !!me.pveSelNode.data.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + "/qemu/" + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + '/status/' + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var resumeBtn = Ext.create('Ext.Button', { - text: gettext('Resume'), - disabled: !caps.vms['VM.PowerMgmt'], - hidden: true, - handler: function() { - vm_command('resume'); - }, - iconCls: 'fa fa-play' - }); - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('qmtemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = me.pveSelNode.data.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - itemId: 'removeBtn', - disabled: !caps.vms['VM.Allocate'], - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'VM', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('qmshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items: [{ - text: gettext('Pause'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmpause', vmid), - handler: function() { - vm_command("suspend"); - }, - iconCls: 'fa fa-pause' - },{ - text: gettext('Hibernate'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmsuspend', vmid), - tooltip: gettext('Suspend to disk'), - handler: function() { - vm_command("suspend", { todisk: 1 }); - }, - iconCls: 'fa fa-download' - },{ - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - dangerous: true, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - confirmMsg: Proxmox.Utils.format_task_description('qmstop', vmid), - handler: function() { - vm_command("stop", { timeout: 30 }); - }, - iconCls: 'fa fa-stop' - },{ - text: gettext('Reset'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmreset', vmid), - handler: function() { - vm_command("reset"); - }, - iconCls: 'fa fa-bolt' - }] - }, - iconCls: 'fa fa-power-off' - }); - - var vm = me.pveSelNode.data; - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - hidden: template, - consoleType: 'kvm', - consoleName: vm.name, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - Ext.apply(me, { - title: Ext.String.format(gettext("Virtual Machine {0} on node '{1}'"), vm.text, nodename), - hstateid: 'kvmtab', - tbarSpacing: false, - tbar: [ statusTxt, '->', resumeBtn, startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveQemuSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push({ - title: gettext('Console'), - itemId: 'console', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'kvm', - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Hardware'), - itemId: 'hardware', - iconCls: 'fa fa-desktop', - xtype: 'PVE.qemu.HardwareView' - }, - { - title: 'Cloud-Init', - itemId: 'cloudinit', - iconCls: 'fa fa-cloud', - xtype: 'pveCiPanel' - }, - { - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options', - xtype: 'PVE.qemu.Options' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - xtype: 'proxmoxNodeTasks', - iconCls: 'fa fa-list', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Monitor'] && !template) { - me.items.push({ - title: gettext('Monitor'), - iconCls: 'fa fa-eye', - itemId: 'monitor', - xtype: 'pveQemuMonitor' - }); - } - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - xtype: 'pveQemuSnapshotTree', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var qmpstatus; - var spice = false; - var xtermjs = false; - var lock; - - if (!success) { - status = qmpstatus = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('qmpstatus'); - qmpstatus = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - - spice = s.data.get('spice') ? true : false; - xtermjs = s.data.get('serial') ? true : false; - - } - - if (template) { - return; - } - - var resume = (['prelaunch', 'paused', 'suspended'].indexOf(qmpstatus) !== -1); - - if (resume || lock === 'suspended') { - startBtn.setVisible(false); - resumeBtn.setVisible(true); - } else { - startBtn.setVisible(true); - resumeBtn.setVisible(false); - } - - consoleBtn.setEnableSpice(spice); - consoleBtn.setEnableXtermJS(xtermjs); - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.CreateWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveQemuCreateWizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - current: { - scsihw: '' - } - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('Virtual Machine'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'qm_general_settings', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', - guestType: 'qemu', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ], - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - ], - advancedColumn2: [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - labelWidth: 120, - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Shutdown timeout') - } - ], - onGetValues: function(values) { - - ['name', 'pool', 'onboot', 'agent'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - - var res = PVE.Parser.printStartup({ - order: values.order, - up: values.up, - down: values.down - }); - - if (res) { - values.startup = res; - } - - delete values.order; - delete values.up; - delete values.down; - - return values; - } - }, - { - xtype: 'container', - layout: 'hbox', - defaults: { - flex: 1, - padding: '0 10' - }, - title: gettext('OS'), - items: [ - { - xtype: 'pveQemuCDInputPanel', - bind: { - nodename: '{nodename}' - }, - confid: 'ide2', - insideWizard: true - }, - { - xtype: 'pveQemuOSTypePanel', - insideWizard: true - } - ] - }, - { - xtype: 'pveQemuSystemPanel', - title: gettext('System'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuHDInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Hard Disk'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuProcessorPanel', - insideWizard: true, - title: gettext('CPU') - }, - { - xtype: 'pveQemuMemoryPanel', - insideWizard: true, - title: gettext('Memory') - }, - { - xtype: 'pveQemuNetworkInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Network'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var kv = this.up('window').getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete') { // ignore - return; - } - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response){ - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - - -Ext.define('PVE.qemu.USBInputPanel', { - extend: 'Proxmox.panel.InputPanel', - mixins: ['Proxmox.Mixin.CBind' ], - - autoComplete: false, - onlineHelp: 'qm_usb_passthrough', - - controller: { - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=usb]': { - change: function(field, newValue, oldValue) { - var hwidfield = this.lookupReference('hwid'); - var portfield = this.lookupReference('port'); - var usb3field = this.lookupReference('usb3'); - if (field.inputValue === 'hostdevice') { - hwidfield.setDisabled(!newValue); - } else if(field.inputValue === 'port') { - portfield.setDisabled(!newValue); - } else if(field.inputValue === 'spice') { - usb3field.setDisabled(newValue); - } - } - }, - 'pveUSBSelector': { - change: function(field, newValue, oldValue) { - var usbval = field.getUSBValue(); - var usb3field = this.lookupReference('usb3'); - var usb3 = /usb3/.test(usbval); - if(usb3 && !usb3field.isDisabled()) { - usb3field.savedVal = usb3field.getValue(); - usb3field.setValue(true); - usb3field.setDisabled(true); - } else if(!usb3 && usb3field.isDisabled()){ - var val = (usb3field.savedVal === undefined)?usb3field.originalValue:usb3field.savedVal; - usb3field.setValue(val); - usb3field.setDisabled(false); - } - } - } - } - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - }, - - onGetValues: function(values) { - var me = this; - if(!me.confid) { - var i; - for (i = 0; i < 6; i++) { - if (!me.vmconfig['usb' + i.toString()]) { - me.confid = 'usb' + i.toString(); - break; - } - } - } - var val = ""; - var type = me.down('radiofield').getGroupValue(); - switch (type) { - case 'spice': - val = 'spice'; break; - case 'hostdevice': - case 'port': - val = me.down('pveUSBSelector[name=' + type + ']').getUSBValue(); - if (!/usb3/.test(val) && me.down('field[name=usb3]').getValue() === true) { - val += ',usb3=1'; - } - break; - default: - throw "invalid type selected"; - } - - values[me.confid] = val; - return values; - }, - - items: [ - { - xtype: 'fieldcontainer', - defaultType: 'radiofield', - items:[ - { - name: 'usb', - inputValue: 'spice', - boxLabel: gettext('Spice Port'), - submitValue: false, - checked: true - }, - { - name: 'usb', - inputValue: 'hostdevice', - boxLabel: gettext('Use USB Vendor/Device ID'), - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - type: 'device', - name: 'hostdevice', - cbind: { pveSelNode: '{pveSelNode}' }, - editable: true, - reference: 'hwid', - allowBlank: false, - fieldLabel: 'Choose Device', - labelAlign: 'right', - submitValue: false - }, - { - name: 'usb', - inputValue: 'port', - boxLabel: gettext('Use USB Port'), - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - name: 'port', - cbind: { pveSelNode: '{pveSelNode}' }, - editable: true, - type: 'port', - reference: 'port', - allowBlank: false, - fieldLabel: gettext('Choose Port'), - labelAlign: 'right', - submitValue: false - }, - { - xtype: 'checkbox', - name: 'usb3', - submitValue: false, - reference: 'usb3', - fieldLabel: gettext('Use USB3') - } - ] - } - ] -}); - -Ext.define('PVE.qemu.USBEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('USB Device'), - - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.USBInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var data = response.result.data[me.confid].split(','); - var port, hostdevice, usb3 = false; - var type = 'spice'; - var i; - for (i = 0; i < data.length; i++) { - if (/^(host=)?(0x)?[a-zA-Z0-9]{4}\:(0x)?[a-zA-Z0-9]{4}$/.test(data[i])) { - hostdevice = data[i]; - hostdevice = hostdevice.replace('host=', '').replace('0x',''); - type = 'hostdevice'; - } else if (/^(host=)?(\d+)\-(\d+(\.\d+)*)$/.test(data[i])) { - port = data[i]; - port = port.replace('host=',''); - type = 'port'; - } - - if (/^usb3=(1|on|true)$/.test(data[i])) { - usb3 = true; - } - } - var values = { - usb : type, - hostdevice: hostdevice, - port: port, - usb3: usb3 - }; - - ipanel.setValues(values); - } - } - }); - } -}); -Ext.define('PVE.qemu.PCIInputPanel', { - extend: 'Proxmox.panel.InputPanel', - - onlineHelp: 'qm_pci_passthrough', - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - - var hostpci = me.vmconfig[me.confid] || ''; - - var values = PVE.Parser.parsePropertyString(hostpci, 'host'); - if (values.host && values.host.length < 6) { // 00:00 format not 00:00.0 - values.host += ".0"; - values.multifunction = true; - } - values['x-vga'] = PVE.Parser.parseBoolean(values['x-vga'], 0); - values.pcie = PVE.Parser.parseBoolean(values.pcie, 0); - values.rombar = PVE.Parser.parseBoolean(values.rombar, 1); - - me.setValues(values); - if (!me.vmconfig.machine || me.vmconfig.machine.indexOf('q35') === -1) { - // machine is not set to some variant of q35, so we disable pcie - var pcie = me.down('field[name=pcie]'); - pcie.setDisabled(true); - pcie.setBoxLabel(gettext('Q35 only')); - } - - if (values.romfile) { - me.down('field[name=romfile]').setVisible(true); - } - }, - - onGetValues: function(values) { - var me = this; - var ret = {}; - if(!me.confid) { - var i; - for (i = 0; i < 5; i++) { - if (!me.vmconfig['hostpci' + i.toString()]) { - me.confid = 'hostpci' + i.toString(); - break; - } - } - } - if (values.multifunction) { - // modify host to skip the '.X' - values.host = values.host.substring(0,5); - delete values.multifunction; - } - - if (values.rombar) { - delete values.rombar; - } else { - values.rombar = 0; - } - - if (!values.romfile) { - delete values.romfile; - } - - ret[me.confid] = PVE.Parser.printPropertyString(values, 'host'); - return ret; - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.column1 = [ - { - xtype: 'pvePCISelector', - fieldLabel: gettext('Device'), - name: 'host', - nodename: me.nodename, - allowBlank: false, - onLoadCallBack: function(store, records, success) { - if (!success || !records.length) { - return; - } - - var first = records[0]; - if (first.data.iommugroup === -1) { - // no iommu groups - var warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - value: 'No IOMMU detected, please activate it.' + - 'See Documentation for further information.', - userCls: 'pve-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } - }, - listeners: { - change: function(pcisel, value) { - if (!value) { - return; - } - var pcidev = pcisel.getStore().getById(value); - var mdevfield = me.down('field[name=mdev]'); - mdevfield.setDisabled(!pcidev || !pcidev.data.mdev); - if (!pcidev) { - return; - } - var id = pcidev.data.id.substring(0,5); // 00:00 - var iommu = pcidev.data.iommugroup; - // try to find out if there are more devices - // in that iommu group - if (iommu !== -1) { - var count = 0; - pcisel.getStore().each(function(record) { - if (record.data.iommugroup === iommu && - record.data.id.substring(0,5) !== id) - { - count++; - return false; - } - }); - var warning = me.down('#iommuwarning'); - if (count && !warning) { - warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - itemId: 'iommuwarning', - value: 'The selected Device is not in a seperate' + - 'IOMMU group, make sure this is intended.', - userCls: 'pve-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } else if (!count && warning) { - me.remove(warning); - } - } - if (pcidev.data.mdev) { - mdevfield.setPciID(value); - } - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('All Functions'), - name: 'multifunction' - } - ]; - - me.column2 = [ - { - xtype: 'pveMDevSelector', - name: 'mdev', - disabled: true, - fieldLabel: gettext('MDev Type'), - nodename: me.nodename, - listeners: { - change: function(field, value) { - var mf = me.down('field[name=multifunction]'); - if (!!value) { - mf.setValue(false); - } - mf.setDisabled(!!value); - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Primary GPU'), - name: 'x-vga' - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'ROM-Bar', - name: 'rombar' - }, - { - xtype: 'displayfield', - submitValue: true, - hidden: true, - fieldLabel: 'ROM-File', - name: 'romfile' - } - ]; - - me.advancedColumn2 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'PCI-Express', - name: 'pcie' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.PCIEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('PCI Device'), - - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.PCIInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.SerialnputPanel', { - extend: 'Proxmox.panel.InputPanel', - - autoComplete: false, - - setVMConfig: function(vmconfig) { - var me = this, i; - me.vmconfig = vmconfig; - - for (i = 0; i < 4; i++) { - var port = 'serial' + i.toString(); - if (!me.vmconfig[port]) { - me.down('field[name=serialid]').setValue(i); - break; - } - } - - }, - - onGetValues: function(values) { - var me = this; - - var id = 'serial' + values.serialid; - delete values.serialid; - values[id] = 'socket'; - return values; - }, - - items: [ - { - xtype: 'proxmoxintegerfield', - name: 'serialid', - fieldLabel: gettext('Serial Port'), - minValue: 0, - maxValue: 3, - allowBlank: false, - validator: function(id) { - if (!this.rendered) { - return true; - } - var me = this.up('panel'); - if (me.vmconfig !== undefined && Ext.isDefined(me.vmconfig['serial' + id])) { - return "This device is already in use."; - } - return true; - } - } - ] -}); - -Ext.define('PVE.qemu.SerialEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('Serial Port'), - - initComponent : function() { - var me = this; - - // for now create of (socket) serial port only - me.isCreate = true; - - var ipanel = Ext.create('PVE.qemu.SerialnputPanel', {}); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.window.IPInfo', { - extend: 'Ext.window.Window', - width: 600, - title: gettext('Guest Agent Network Information'), - height: 300, - layout: { - type: 'fit' - }, - modal: true, - items: [ - { - xtype: 'grid', - emptyText: gettext('No network information'), - columns: [ - { - dataIndex: 'name', - text: gettext('Name'), - flex: 3 - }, - { - dataIndex: 'hardware-address', - text: gettext('MAC address'), - width: 140 - }, - { - dataIndex: 'ip-addresses', - text: gettext('IP address'), - align: 'right', - flex: 4, - renderer: function(val) { - if (!Ext.isArray(val)) { - return ''; - } - var ips = []; - val.forEach(function(ip) { - var addr = ip['ip-address']; - var pref = ip.prefix; - if (addr && pref) { - ips.push(addr + '/' + pref); - } - }); - return ips.join('
'); - } - } - ] - } - ] -}); - -Ext.define('PVE.qemu.AgentIPView', { - extend: 'Ext.container.Container', - xtype: 'pveAgentIPView', - - layout: { - type: 'hbox', - align: 'top' - }, - - nics: [], - - items: [ - { - xtype: 'box', - html: ' IPs' - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'right', - pack: 'end' - }, - items: [ - { - xtype: 'label', - flex: 1, - itemId: 'ipBox', - style: { - 'text-align': 'right' - } - }, - { - xtype: 'button', - itemId: 'moreBtn', - hidden: true, - ui: 'default-toolbar', - handler: function(btn) { - var me = this.up('pveAgentIPView'); - - var win = Ext.create('PVE.window.IPInfo'); - win.down('grid').getStore().setData(me.nics); - win.show(); - }, - text: gettext('More') - } - ] - } - ], - - getDefaultIps: function(nics) { - var me = this; - var ips = []; - nics.forEach(function(nic) { - if (nic['hardware-address'] && - nic['hardware-address'] != '00:00:00:00:00:00') { - - var nic_ips = nic['ip-addresses'] || []; - nic_ips.forEach(function(ip) { - var p = ip['ip-address']; - // show 2 ips at maximum - if (ips.length < 2) { - ips.push(p); - } - }); - } - }); - - return ips; - }, - - startIPStore: function(store, records, success) { - var me = this; - var agentRec = store.getById('agent'); - /*jslint confusion: true*/ - /* value is number and string */ - me.agent = (agentRec && agentRec.data.value === 1); - me.running = (store.getById('status').data.value === 'running'); - /*jslint confusion: false*/ - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!caps.vms['VM.Monitor']) { - var errorText = gettext("Requires '{0}' Privileges"); - me.updateStatus(false, Ext.String.format(errorText, 'VM.Monitor')); - return; - } - - if (me.agent && me.running && me.ipStore.isStopped) { - me.ipStore.startUpdate(); - } else if (me.ipStore.isStopped) { - me.updateStatus(); - } - }, - - updateStatus: function(unsuccessful, defaulttext) { - var me = this; - var text = defaulttext || gettext('No network information'); - var more = false; - if (unsuccessful) { - text = gettext('Guest Agent not running'); - } else if (me.agent && me.running) { - if (Ext.isArray(me.nics) && me.nics.length) { - more = true; - var ips = me.getDefaultIps(me.nics); - if (ips.length !== 0) { - text = ips.join('
'); - } - } else if (me.nics && me.nics.error) { - var msg = gettext('Cannot get info from Guest Agent
Error: {0}'); - text = Ext.String.format(text, me.nics.error.desc); - } - } else if (me.agent) { - text = gettext('Guest Agent not running'); - } else { - text = gettext('No Guest Agent configured'); - } - - var ipBox = me.down('#ipBox'); - ipBox.update(text); - - var moreBtn = me.down('#moreBtn'); - moreBtn.setVisible(more); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw 'rstore not given'; - } - - if (!me.pveSelNode) { - throw 'pveSelNode not given'; - } - - var nodename = me.pveSelNode.data.node; - var vmid = me.pveSelNode.data.vmid; - - me.ipStore = Ext.create('Proxmox.data.UpdateStore', { - interval: 10000, - storeid: 'pve-qemu-agent-' + vmid, - method: 'POST', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/qemu/' + vmid + '/agent/network-get-interfaces' - } - }); - - me.callParent(); - - me.mon(me.ipStore, 'load', function(store, records, success) { - if (records && records.length) { - me.nics = records[0].data.result; - } else { - me.nics = undefined; - } - me.updateStatus(!success); - }); - - me.on('destroy', me.ipStore.stopUpdate); - - // if we already have info about the vm, use it immediately - if (me.rstore.getCount()) { - me.startIPStore(me.rstore, me.rstore.getData(), false); - } - - // check if the guest agent is there on every statusstore load - me.mon(me.rstore, 'load', me.startIPStore, me); - } -}); -Ext.define('PVE.qemu.CloudInit', { - extend: 'Proxmox.grid.PendingObjectGrid', - xtype: 'pveCiPanel', - - onlineHelp: 'qm_cloud_init', - - tbar: [ - { - xtype: 'proxmoxButton', - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var me = this.up('grid'); - var warn = gettext('Are you sure you want to remove entry {0}'); - - var entry = rec.data.key; - var msg = Ext.String.format(warn, "'" - + me.renderKey(entry, {}, rec) + "'"); - - return msg; - }, - enableFn: function(record) { - var me = this.up('grid'); - var caps = Ext.state.Manager.get('GuiCap'); - if (me.rows[record.data.key].never_delete || - !caps.vms['VM.Config.Network']) { - return false; - } - - if (record.data.key === 'cipassword' && !record.data.value) { - return false; - } - return true; - }, - handler: function() { - var me = this.up('grid'); - var records = me.getSelection(); - if (!records || !records.length) { - return; - } - - var id = records[0].data.key; - var match = id.match(/^net(\d+)$/); - if (match) { - id = 'ipconfig' + match[1]; - } - - var params = {}; - params['delete'] = id; - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: params, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - callback: function() { - me.reload(); - } - }); - }, - text: gettext('Remove') - }, - { - xtype: 'proxmoxButton', - disabled: true, - handler: function() { - var me = this.up('grid'); - me.run_editor(); - }, - text: gettext('Edit') - }, - '-', - { - xtype: 'button', - itemId: 'savebtn', - text: gettext('Regenerate Image'), - handler: function() { - var me = this.up('grid'); - var eject_params = {}; - var insert_params = {}; - var disk = PVE.Parser.parseQemuDrive(me.ciDriveId, me.ciDrive); - var storage = ''; - var stormatch = disk.file.match(/^([^\:]+)\:/); - if (stormatch) { - storage = stormatch[1]; - } - eject_params[me.ciDriveId] = 'none,media=cdrom'; - insert_params[me.ciDriveId] = storage + ':cloudinit'; - - var failure = function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }; - - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: eject_params, - failure: failure, - callback: function() { - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: insert_params, - failure: failure, - callback: function() { - me.reload(); - } - }); - } - }); - } - } - ], - - border: false, - - set_button_status: function(rstore, records, success) { - if (!success || records.length < 1) { - return; - } - var me = this; - var found; - records.forEach(function(record) { - if (found) { - return; - } - var id = record.data.key; - var value = record.data.value; - var ciregex = new RegExp("vm-" + me.pveSelNode.data.vmid + "-cloudinit"); - if (id.match(/^(ide|scsi|sata)\d+$/) && ciregex.test(value)) { - found = id; - me.ciDriveId = found; - me.ciDrive = value; - } - }); - - me.down('#savebtn').setDisabled(!found); - me.setDisabled(!found); - if (!found) { - me.getView().mask(gettext('No CloudInit Drive found'), ['pve-static-mask']); - } else { - me.getView().unmask(); - } - }, - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - - var icon = ""; - if (rowdef.iconCls) { - icon = ' '; - } - return icon + (rowdef.header || key); - }, - - listeners: { - activate: function () { - var me = this; - me.rstore.startUpdate(); - }, - itemdblclick: function() { - var me = this; - me.run_editor(); - } - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - var caps = Ext.state.Manager.get('GuiCap'); - me.baseurl = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid; - me.url = me.baseurl + '/pending'; - me.editorConfig.url = me.baseurl + '/config'; - me.editorConfig.pveSelNode = me.pveSelNode; - - /*jslint confusion: true*/ - /* editor is string and object */ - me.rows = { - ciuser: { - header: gettext('User'), - iconCls: 'fa fa-user', - never_delete: true, - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('User'), - items: [ - { - xtype: 'proxmoxtextfield', - deleteEmpty: true, - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('User'), - name: 'ciuser' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.defaultText; - } - }, - cipassword: { - header: gettext('Password'), - iconCls: 'fa fa-unlock', - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Password'), - items: [ - { - xtype: 'proxmoxtextfield', - inputType: 'password', - deleteEmpty: true, - emptyText: Proxmox.Utils.noneText, - fieldLabel: gettext('Password'), - name: 'cipassword' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.noneText; - } - }, - searchdomain: { - header: gettext('DNS domain'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - nameserver: { - header: gettext('DNS servers'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - sshkeys: { - header: gettext('SSH public key'), - iconCls: 'fa fa-key', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.SSHKeyEdit' : undefined, - never_delete: true, - renderer: function(value) { - value = decodeURIComponent(value); - var keys = value.split('\n'); - var text = []; - keys.forEach(function(key) { - if (key.length) { - // First erase all quoted strings (eg. command="foo" - var v = key.replace(/"(?:\\.|[^"\\])*"/g, ''); - // Now try to detect the comment: - var res = v.match(/^\s*(\S+\s+)?(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)\s+\S+\s+(.*?)\s*$/, ''); - if (res) { - key = Ext.String.htmlEncode(res[2]); - if (res[1]) { - key += ' (' + gettext('with options') + ')'; - } - text.push(key); - return; - } - // Most likely invalid at this point, so just stick to - // the old value. - text.push(Ext.String.htmlEncode(key)); - } - }); - if (text.length) { - return text.join('
'); - } else { - return Proxmox.Utils.noneText; - } - }, - defaultValue: '' - } - }; - var i; - var ipconfig_renderer = function(value, md, record, ri, ci, store, pending) { - var id = record.data.key; - var match = id.match(/^net(\d+)$/); - var val = ''; - if (match) { - val = me.getObjectValue('ipconfig'+match[1], '', pending); - } - return val; - }; - for (i = 0; i < 32; i++) { - // we want to show an entry for every network device - // even if it is empty - me.rows['net' + i.toString()] = { - multiKey: ['ipconfig' + i.toString(), 'net' + i.toString()], - header: gettext('IP Config') + ' (net' + i.toString() +')', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.IPConfigEdit' : undefined, - iconCls: 'fa fa-exchange', - renderer: ipconfig_renderer - }; - me.rows['ipconfig' + i.toString()] = { - visible: false - }; - } - /*jslint confusion: false*/ - - PVE.Utils.forEachBus(['ide', 'scsi', 'sata'], function(type, id) { - me.rows[type+id] = { - visible: false - }; - }); - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - } -}); -Ext.define('PVE.qemu.CIDriveInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveCIDriveInputPanel', - - insideWizard: false, - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var drive = {}; - var params = {}; - drive.file = values.hdstorage + ":cloudinit"; - drive.format = values.diskformat; - params[values.controller + values.deviceid] = PVE.Parser.printQemuDrive(drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - setVMConfig: function(config) { - var me = this; - me.down('#drive').setVMConfig(config, 'cdrom'); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items = [ - { - xtype: 'pveControllerSelector', - noVirtIO: true, - itemId: 'drive', - fieldLabel: gettext('CloudInit Drive'), - name: 'drive' - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'storselector', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - } - ]; - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CIDriveEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCIDriveEdit', - - isCreate: true, - subject: gettext('CloudInit Drive'), - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveCIDriveInputPanel', - itemId: 'cipanel', - nodename: nodename - }]; - - me.callParent(); - - me.load({ - success: function(response, opts) { - me.down('#cipanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.SSHKeyInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSSHKeyInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - if (values.sshkeys) { - values.sshkeys.trim(); - } - if (!values.sshkeys.length) { - values = {}; - values['delete'] = 'sshkeys'; - return values; - } else { - values.sshkeys = encodeURIComponent(values.sshkeys); - } - return values; - }, - - items: [ - { - xtype: 'textarea', - itemId: 'sshkeys', - name: 'sshkeys', - height: 250 - }, - { - xtype: 'filebutton', - itemId: 'filebutton', - name: 'file', - text: gettext('Load SSH Key File'), - fieldLabel: 'test', - listeners: { - change: function(btn, e, value) { - var me = this.up('inputpanel'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - var keysField = me.down('#sshkeys'); - var old = keysField.getValue(); - keysField.setValue(old + res); - }); - }); - btn.reset(); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - if (!window.FileReader) { - me.down('#filebutton').setVisible(false); - } - - } -}); - -Ext.define('PVE.qemu.SSHKeyEdit', { - extend: 'Proxmox.window.Edit', - - width: 800, - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.SSHKeyInputPanel'); - - Ext.apply(me, { - subject: gettext('SSH Keys'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.create) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.sshkeys) { - data.sshkeys = decodeURIComponent(data.sshkeys); - ipanel.setValues(data); - } - } - }); - } - } -}); -Ext.define('PVE.qemu.IPConfigPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveIPConfigPanel', - - insideWizard: false, - - vmconfig: {}, - - onGetValues: function(values) { - var me = this; - - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - - var params = {}; - - var cfg = PVE.Parser.printIPConfig(values); - if (cfg === '') { - params['delete'] = [me.confid]; - } else { - params[me.confid] = cfg; - } - return params; - }, - - setVMConfig: function(config) { - var me = this; - me.vmconfig = config; - }, - - setIPConfig: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data.ip === 'dhcp') { - data.ipv4mode = data.ip; - data.ip = ''; - } else { - data.ipv4mode = 'static'; - } - if (data.ip6 === 'dhcp' || data.ip6 === 'auto') { - data.ipv6mode = data.ip6; - data.ip6 = ''; - } else { - data.ipv6mode = 'static'; - } - - me.ipconfig = data; - me.setValues(me.ipconfig); - }, - - initComponent : function() { - var me = this; - - me.ipconfig = {}; - - me.column1 = [ - { - xtype: 'displayfield', - fieldLabel: gettext('Network Device'), - value: me.netid - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv4') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv4mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: '', - disabled: true, - fieldLabel: gettext('IPv4/CIDR') - }, - { - xtype: 'textfield', - name: 'gw', - value: '', - vtype: 'IPAddress', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv4') +')' - } - ]; - - me.column2 = [ - { - xtype: 'displayfield' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv6') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv6mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: '', - vtype: 'IP6CIDRAddress', - disabled: true, - fieldLabel: gettext('IPv6/CIDR') - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: '', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv6') +')' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.IPConfigEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - // convert confid from netX to ipconfigX - var match = me.confid.match(/^net(\d+)$/); - if (match) { - me.netid = me.confid; - me.confid = 'ipconfig' + match[1]; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.IPConfigPanel', { - confid: me.confid, - netid: me.netid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: gettext('Network Config'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - me.vmconfig = response.result.data; - var ipconfig = {}; - var value = me.vmconfig[me.confid]; - if (value) { - ipconfig = PVE.Parser.parseIPConfig(me.confid, value); - if (!ipconfig) { - Ext.Msg.alert(gettext('Error'), gettext('Unable to parse network configuration')); - me.close(); - return; - } - } - ipanel.setIPConfig(me.confid, ipconfig); - ipanel.setVMConfig(me.vmconfig); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.SystemInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSystemPanel', - - onlineHelp: 'qm_system_settings', - - viewModel: { - data: { - efi: false, - addefi: true - }, - - formulas: { - efidisk: function(get) { - return get('efi') && get('addefi'); - } - } - }, - - onGetValues: function(values) { - if (values.vga && values.vga.substr(0,6) === 'serial') { - values['serial' + values.vga.substr(6,1)] = 'socket'; - } - - var efidrive = {}; - if (values.hdimage) { - efidrive.file = values.hdimage; - } else if (values.hdstorage) { - efidrive.file = values.hdstorage + ":1"; - } - - if (values.diskformat) { - efidrive.format = values.diskformat; - } - - delete values.hdimage; - delete values.hdstorage; - delete values.diskformat; - - if (efidrive.file) { - values.efidisk0 = PVE.Parser.printQemuDrive(efidrive); - } - - return values; - }, - - controller: { - xclass: 'Ext.app.ViewController', - - scsihwChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('current.scsihw', value); - } - }, - - biosChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('efi', value === 'ovmf'); - } - }, - - control: { - 'pveScsiHwSelector': { - change: 'scsihwChange' - }, - 'pveQemuBiosSelector': { - change: 'biosChange' - } - } - }, - - column1: [ - { - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - name: 'vga', - comboItems: PVE.Utils.kvm_vga_driver_array() - }, - { - xtype: 'proxmoxcheckbox', - name: 'agent', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Qemu Agent') - } - ], - - column2: [ - { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - bind: { - value: '{current.scsihw}' - }, - fieldLabel: gettext('SCSI Controller') - } - ], - - advancedColumn1: [ - { - xtype: 'pveQemuBiosSelector', - name: 'bios', - value: '__default__', - fieldLabel: 'BIOS' - }, - { - xtype: 'proxmoxcheckbox', - bind: { - value: '{addefi}', - hidden: '{!efi}', - disabled: '{!efi}' - }, - hidden: true, - submitValue: false, - disabled: true, - fieldLabel: gettext('Add EFI Disk') - }, - { - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - bind: { - nodename: '{nodename}', - hidden: '{!efi}', - disabled: '{!efidisk}' - }, - autoSelect: false, - disabled: true, - hidden: true, - hideSize: true - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - } - ] - -}); -Ext.define('PVE.lxc.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveLxcSummary', - - scrollable: true, - bodyPadding: 5, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - if (!me.workspace) { - throw "no workspace specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var template = !!me.pveSelNode.data.template; - var rstore = me.statusStore; - - var width = template ? 1 : 0.5; - var items = [ - { - xtype: template ? 'pveTemplateStatusView' : 'pveGuestStatusView', - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - }, - itemId: 'gueststatus', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'pveNotesView', - maxHeight: 320, - itemId: 'notesview', - pveSelNode: me.pveSelNode, - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - } - } - ]; - - var rrdstore; - if (!template) { - - rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/rrddata", - model: 'pve-rrd-guest' - }); - - items.push( - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - pveSelNode: me.pveSelNode, - fields: ['cpu'], - fieldTitles: [gettext('CPU usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - pveSelNode: me.pveSelNode, - fields: ['maxmem', 'mem'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - pveSelNode: me.pveSelNode, - fields: ['netin','netout'], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Disk IO'), - pveSelNode: me.pveSelNode, - fields: ['diskread','diskwrite'], - store: rrdstore - } - ); - - } - - Ext.apply(me, { - tbar: [ '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: { - type: 'column' - }, - defaults: { - minHeight: 320, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: items - } - ] - }); - - me.callParent(); - if (!template) { - rrdstore.startUpdate(); - me.on('destroy', rrdstore.stopUpdate); - } - } -}); -Ext.define('PVE.lxc.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcNetworkInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_network', - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - var bridgesel = me.query("[isFormField][name=bridge]")[0]; - bridgesel.setNodename(nodename); - }, - - onGetValues: function(values) { - var me = this; - - var id; - if (me.isCreate) { - id = values.id; - delete values.id; - } else { - id = me.ifname; - } - - if (!id) { - return {}; - } - - var newdata = {}; - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - newdata[id] = PVE.Parser.printLxcNetwork(values); - return newdata; - }, - - initComponent : function() { - var me = this; - - var cdata = {}; - - if (me.insideWizard) { - me.ifname = 'net0'; - cdata.name = 'eth0'; - me.dataCache = {}; - } - cdata.firewall = (me.insideWizard || me.isCreate); - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.isCreate) { - if (!me.ifname) { - throw "no interface name specified"; - } - if (!me.dataCache[me.ifname]) { - throw "no such interface '" + me.ifname + "'"; - } - - cdata = PVE.Parser.parseLxcNetwork(me.dataCache[me.ifname]); - } - - var i; - for (i = 0; i < 10; i++) { - if (me.isCreate && !me.dataCache['net'+i.toString()]) { - me.ifname = 'net' + i.toString(); - break; - } - } - - var idselector = { - xtype: 'hidden', - name: 'id', - value: me.ifname - }; - - me.column1 = [ - idselector, - { - xtype: 'textfield', - name: 'name', - fieldLabel: gettext('Name'), - emptyText: '(e.g., eth0)', - allowBlank: false, - value: cdata.name, - validator: function(value) { - var result = ''; - Ext.Object.each(me.dataCache, function(key, netstr) { - if (!key.match(/^net\d+/) || key === me.ifname) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(netstr); - if (net.name === value) { - result = "interface name already in use"; - return false; - } - }); - if (result !== '') { - return result; - } - // validator can return bool/string - /*jslint confusion:true*/ - return true; - } - }, - { - xtype: 'textfield', - name: 'hwaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - value: cdata.hwaddr, - allowBlank: true, - emptyText: 'auto' - }, - { - xtype: 'PVE.form.BridgeSelector', - name: 'bridge', - nodename: me.nodename, - fieldLabel: gettext('Bridge'), - value: cdata.bridge, - allowBlank: false - }, - { - xtype: 'pveVlanField', - name: 'tag', - value: cdata.tag - }, - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: cdata.rate, - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - value: cdata.firewall - } - ]; - - var dhcp4 = (cdata.ip === 'dhcp'); - if (dhcp4) { - cdata.ip = ''; - cdata.gw = ''; - } - - var auto6 = (cdata.ip6 === 'auto'); - var dhcp6 = (cdata.ip6 === 'dhcp'); - if (auto6 || dhcp6) { - cdata.ip6 = ''; - cdata.gw6 = ''; - } - - me.column2 = [ - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv4:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: !dhcp4, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv4mode', - inputValue: 'dhcp', - checked: dhcp4, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: cdata.ip, - disabled: dhcp4, - fieldLabel: 'IPv4/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw', - value: cdata.gw, - vtype: 'IPAddress', - disabled: dhcp4, - fieldLabel: gettext('Gateway') + ' (IPv4)', - margin: '0 0 3 0' // override bottom margin to account for the menuseparator - }, - { - xtype: 'menuseparator', - height: '3', - margin: '0' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv6:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: !(auto6 || dhcp6), - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv6mode', - inputValue: 'dhcp', - checked: dhcp6, - margin: '0 0 0 10' - }, - { - xtype: 'radiofield', - boxLabel: 'SLAAC', // do not localize - name: 'ipv6mode', - inputValue: 'auto', - checked: auto6, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: cdata.ip6, - vtype: 'IP6CIDRAddress', - disabled: (dhcp6 || auto6), - fieldLabel: 'IPv6/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: cdata.gw6, - disabled: (dhcp6 || auto6), - fieldLabel: gettext('Gateway') + ' (IPv6)' - } - ]; - - me.callParent(); - } -}); - - -Ext.define('PVE.lxc.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - var me = this; - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.nodename) { - throw "no node name specified"; - } - - var ipanel = Ext.create('PVE.lxc.NetworkInputPanel', { - ifname: me.ifname, - nodename: me.nodename, - dataCache: me.dataCache, - isCreate: me.isCreate - }); - - Ext.apply(me, { - subject: gettext('Network Device') + ' (veth)', - digest: me.dataCache.digest, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.NetworkView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveLxcNetworkView', - - onlineHelp: 'pct_container_network', - - dataCache: {}, // used to store result of last load - - stateful: true, - stateId: 'grid-lxc-network', - - load: function() { - var me = this; - - Proxmox.Utils.setErrorMask(me, true); - - Proxmox.Utils.API2Request({ - url: me.url, - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, gettext('Error') + ': ' + response.htmlStatus); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var result = Ext.decode(response.responseText); - var data = result.data || {}; - me.dataCache = data; - var records = []; - Ext.Object.each(data, function(key, value) { - if (!key.match(/^net\d+/)) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(value); - net.id = key; - records.push(net); - }); - me.store.loadData(records); - me.down('button[name=addButton]').setDisabled((records.length >= 10)); - } - }); - }, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.url = '/nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var store = new Ext.data.Store({ - model: 'pve-lxc-network', - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!caps.vms['VM.Config.Network']; - }, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - method: 'PUT', - params: { 'delete': rec.data.id, digest: me.dataCache.digest }, - callback: function() { - me.load(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (!caps.vms['VM.Config.Network']) { - return false; - } - - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - dataCache: me.dataCache, - ifname: rec.data.id - }); - win.on('destroy', me.load, me); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!caps.vms['VM.Config.Network']) { - return false; - } - return true; - }, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - name: 'addButton', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - isCreate: true, - dataCache: me.dataCache - }); - win.on('destroy', me.load, me); - win.show(); - } - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - width: 50, - dataIndex: 'id' - }, - { - header: gettext('Name'), - width: 80, - dataIndex: 'name' - }, - { - header: gettext('Bridge'), - width: 80, - dataIndex: 'bridge' - }, - { - header: gettext('Firewall'), - width: 80, - dataIndex: 'firewall', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('VLAN Tag'), - width: 80, - dataIndex: 'tag' - }, - { - header: gettext('MAC address'), - width: 110, - dataIndex: 'hwaddr' - }, - { - header: gettext('IP address'), - width: 150, - dataIndex: 'ip', - renderer: function(value, metaData, rec) { - if (rec.data.ip && rec.data.ip6) { - return rec.data.ip + "
" + rec.data.ip6; - } else if (rec.data.ip6) { - return rec.data.ip6; - } else { - return rec.data.ip; - } - } - }, - { - header: gettext('Gateway'), - width: 150, - dataIndex: 'gw', - renderer: function(value, metaData, rec) { - if (rec.data.gw && rec.data.gw6) { - return rec.data.gw + "
" + rec.data.gw6; - } else if (rec.data.gw6) { - return rec.data.gw6; - } else { - return rec.data.gw; - } - } - } - ], - listeners: { - activate: me.load, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-lxc-network', { - extend: "Ext.data.Model", - proxy: { type: 'memory' }, - fields: [ 'id', 'name', 'hwaddr', 'bridge', - 'ip', 'gw', 'ip6', 'gw6', 'tag', 'firewall' ] - }); - -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.RessourceView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcRessourceView'], - - onlineHelp: 'pct_configuration', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rowdef = me.rows[key] || {}; - - metaData.tdAttr = "valign=middle"; - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - } - return rowdef.header || key; - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - var mpeditor = caps.vms['VM.Config.Disk'] ? 'PVE.lxc.MountPointEdit' : undefined; - - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-memory', - group: 1, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - swap: { - header: gettext('Swap'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-swap', - group: 2, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - cores: { - header: gettext('Cores'), - editor: caps.vms['VM.Config.CPU'] ? 'PVE.lxc.CPUEdit' : undefined, - defaultValue: '', - tdCls: 'pve-itype-icon-processor', - group: 3, - renderer: function(value) { - var cpulimit = me.getObjectValue('cpulimit'); - var cpuunits = me.getObjectValue('cpuunits'); - var res; - if (value) { - res = value; - } else { - res = gettext('unlimited'); - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit + ']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits + ']'; - } - return res; - } - }, - rootfs: { - header: gettext('Root Disk'), - defaultValue: Proxmox.Utils.noneText, - editor: mpeditor, - tdCls: 'pve-itype-icon-storage', - group: 4 - }, - cpulimit: { - visible: false - }, - cpuunits: { - visible: false - }, - unprivileged: { - visible: false - } - }; - - PVE.Utils.forEachMP(function(bus, i) { - confid = bus + i; - var group = 5; - var header; - if (bus === 'mp') { - header = gettext('Mount Point') + ' (' + confid + ')'; - } else { - header = gettext('Unused Disk') + ' ' + i; - group += 1; - } - rows[confid] = { - group: group, - order: i, - tdCls: 'pve-itype-icon-storage', - editor: mpeditor, - header: header - }; - }, true); - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - var run_resize = function() { - var rec = me.selModel.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.MPResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - }; - - var run_remove = function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'delete': rec.data.key - }, - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var run_move = function(b, e, rec) { - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid, - type: 'lxc' - }); - - win.show(); - - win.on('destroy', me.reload, me); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: me.selModel, - disabled: true, - enableFn: function(rec) { - if (!rec) { - return false; - } - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: me.selModel, - disabled: true, - handler: run_resize - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - selModel: me.selModel, - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + me.renderKey(rec.data.key, {}, rec) + "'"); - if (rec.data.key.match(/^unused\d+$/)) { - msg += " " + gettext('This will permanently erase all data.'); - } - - return msg; - }, - handler: run_remove - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move Volume'), - selModel: me.selModel, - disabled: true, - dangerous: true, - handler: run_move - }); - - var set_button_status = function() { - var rec = me.selModel.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - remove_btn.disable(); - resize_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var isDisk = (rowdef.tdCls == 'pve-itype-icon-storage'); - - var noedit = rec.data['delete'] || !rowdef.editor; - if (!noedit && Proxmox.UserName !== 'root@pam' && key.match(/^mp\d+$/)) { - var mp = PVE.Parser.parseLxcMountPoint(value); - if (mp.type !== 'volume') { - noedit = true; - } - } - edit_btn.setDisabled(noedit); - - remove_btn.setDisabled(!isDisk || rec.data.key === 'rootfs' || !diskCap); - resize_btn.setDisabled(!isDisk || !diskCap); - move_btn.setDisabled(!isDisk || !diskCap); - - }; - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - Ext.apply(me, { - url: '/api2/json/' + baseurl, - selModel: me.selModel, - interval: 2000, - cwidth1: 170, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Mount Point'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.lxc.MountPointEdit', { - url: '/api2/extjs/' + baseurl, - unprivileged: me.getObjectValue('unprivileged'), - pveSelNode: me.pveSelNode - }); - win.show(); - } - } - ] - }) - }, - edit_btn, - remove_btn, - resize_btn, - move_btn - ], - rows: rows, - sorterFn: sorterFn, - editorConfig: { - pveSelNode: me.pveSelNode, - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - Ext.apply(me.editorConfig, { unprivileged: me.getObjectValue('unprivileged') }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.FeaturesInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcFeaturesInputPanel', - - // used to save the mounts fstypes until sending - mounts: [], - - fstypes: ['nfs', 'cifs'], - - viewModel: { - parent: null, - data: { - unprivileged: false - }, - formulas: { - privilegedOnly: function(get) { - return (get('unprivileged') ? gettext('privileged only') : ''); - }, - unprivilegedOnly: function(get) { - return (!get('unprivileged') ? gettext('unprivileged only') : ''); - } - } - }, - - items: [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('keyctl'), - name: 'keyctl', - bind: { - disabled: '{!unprivileged}', - boxLabel: '{unprivilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Nesting'), - name: 'nesting' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nfs', - fieldLabel: 'NFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'cifs', - fieldLabel: 'CIFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'fuse', - fieldLabel: 'FUSE' - } - ], - - onGetValues: function(values) { - var me = this; - var mounts = me.mounts; - me.fstypes.forEach(function(fs) { - if (values[fs]) { - mounts.push(fs); - } - delete values[fs]; - }); - - if (mounts.length) { - values.mount = mounts.join(';'); - } - - var featuresstring = PVE.Parser.printPropertyString(values, undefined); - if (featuresstring == '') { - return { 'delete': 'features' }; - } - return { features: featuresstring }; - }, - - setValues: function(values) { - var me = this; - - me.viewModel.set({ unprivileged: values.unprivileged }); - - if (values.features) { - var res = PVE.Parser.parsePropertyString(values.features); - me.mounts = []; - if (res.mount) { - res.mount.split(/[; ]/).forEach(function(item) { - if (me.fstypes.indexOf(item) === -1) { - me.mounts.push(item); - } else { - res[item] = 1; - } - }); - } - this.callParent([res]); - } - } -}); - -Ext.define('PVE.lxc.FeaturesEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveLxcFeaturesEdit', - - subject: gettext('Features'), - - items: [{ - xtype: 'pveLxcFeaturesInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load(); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.lxc.Options', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcOptions'], - - onlineHelp: 'pct_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'pct_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - defaultValue: Proxmox.Utils.unknownText - }, - arch: { - header: gettext('Architecture'), - defaultValue: Proxmox.Utils.unknownText - }, - console: { - header: '/dev/console', - defaultValue: 1, - renderer: Proxmox.Utils.format_enabled_toggle, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: '/dev/console', - items: { - xtype: 'proxmoxcheckbox', - name: 'console', - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - checked: true, - fieldLabel: '/dev/console' - } - } : undefined - }, - tty: { - header: gettext('TTY count'), - defaultValue: 2, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('TTY count'), - items: { - xtype: 'proxmoxintegerfield', - name: 'tty', - minValue: 0, - maxValue: 6, - value: 2, - fieldLabel: gettext('TTY count'), - emptyText: gettext('Default'), - deleteEmpty: true - } - } : undefined - }, - cmode: { - header: gettext('Console mode'), - defaultValue: 'tty', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Console mode'), - items: { - xtype: 'proxmoxKVComboBox', - name: 'cmode', - deleteEmpty: true, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (tty)"], - ['tty', "/dev/tty[X]"], - ['console', "/dev/console"], - ['shell', "shell"] - ], - fieldLabel: gettext('Console mode') - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - unprivileged: { - header: gettext('Unprivileged container'), - renderer: Proxmox.Utils.format_boolean, - defaultValue: 0 - }, - features: { - header: gettext('Features'), - defaultValue: Proxmox.Utils.noneText, - editor: Proxmox.UserName === 'root@pam' ? - 'PVE.lxc.FeaturesEdit' : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - Ext.apply(me, { - url: "/api2/json/" + baseurl, - selModel: sm, - interval: 5000, - tbar: [ edit_btn ], - rows: rows, - editorConfig: { - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - } -}); - -Ext.define('PVE.lxc.DNSInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcDNSInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var deletes = []; - if (!values.searchdomain && !me.insideWizard) { - deletes.push('searchdomain'); - } - - if (values.nameserver) { - var list = values.nameserver.split(/[\ \,\;]+/); - values.nameserver = list.join(' '); - } else if(!me.insideWizard) { - deletes.push('nameserver'); - } - - if (deletes.length) { - values['delete'] = deletes.join(','); - } - - return values; - }, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxtextfield', - name: 'searchdomain', - skipEmptyText: true, - fieldLabel: gettext('DNS domain'), - emptyText: gettext('use host settings'), - allowBlank: true - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS servers'), - vtype: 'IP64AddressList', - allowBlank: true, - emptyText: gettext('use host settings'), - name: 'nameserver', - itemId: 'nameserver' - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.DNSEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.lxc.DNSInputPanel'); - - Ext.apply(me, { - subject: gettext('Resources'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - if (values.nameserver) { - values.nameserver.replace(/[,;]/, ' '); - values.nameserver.replace(/^\s+/, ''); - } - - ipanel.setValues(values); - } - }); - } - } -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.DNS', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcDNS'], - - onlineHelp: 'pct_container_network', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - hostname: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Hostname'), - editor: caps.vms['VM.Config.Network'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hostname'), - items: { - xtype: 'inputpanel', - items:{ - fieldLabel: gettext('Hostname'), - xtype: 'textfield', - name: 'hostname', - vtype: 'DnsName', - allowBlank: true, - emptyText: 'CT' + vmid.toString() - }, - onGetValues: function(values) { - var params = values; - if (values.hostname === undefined || - values.hostname === null || - values.hostname === '') { - params = { hostname: 'CT'+vmid.toString()}; - } - return params; - } - } - } : undefined - }, - searchdomain: { - header: gettext('DNS domain'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - }, - nameserver: { - header: gettext('DNS server'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var reload = function() { - me.rstore.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - if (Ext.isString(rowdef.editor)) { - win = Ext.create(rowdef.editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - //win.load(); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: run_editor - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - var rowdef = rows[rec.data.key]; - edit_btn.setDisabled(!rowdef.editor); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/config", - selModel: sm, - cwidth1: 150, - run_editor: run_editor, - tbar: [ edit_btn ], - rows: rows, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status, - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.lxc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.lxc.Config', - - onlineHelp: 'chapter_pct', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!me.pveSelNode.data.template; - - var running = !!me.pveSelNode.data.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + '/lxc/' + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + "/status/" + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var stopBtn = Ext.create('Ext.menu.Item',{ - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('vzstop', vmid), - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - dangerous: true, - handler: function() { - vm_command("stop"); - }, - iconCls: 'fa fa-stop' - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('vzshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items:[stopBtn] - }, - iconCls: 'fa fa-power-off' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'lxc'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('vztemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = me.pveSelNode.data.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - guestType: 'ct', - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - disabled: !caps.vms['VM.Allocate'], - itemId: 'removeBtn', - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'CT', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var vm = me.pveSelNode.data; - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - consoleType: 'lxc', - consoleName: vm.name, - hidden: template, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - - Ext.apply(me, { - title: Ext.String.format(gettext("Container {0} on node '{1}'"), vm.text, nodename), - hstateid: 'lxctab', - tbarSpacing: false, - tbar: [ statusTxt, '->', startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveLxcSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push( - { - title: gettext('Console'), - itemId: 'consolejs', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'lxc', - xtermjs: true, - nodename: nodename - } - ); - } - - me.items.push( - { - title: gettext('Resources'), - itemId: 'resources', - expandedOnInit: true, - iconCls: 'fa fa-cube', - xtype: 'pveLxcRessourceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - xtype: 'pveLxcNetworkView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - itemId: 'dns', - xtype: 'pveLxcDNS' - }, - { - title: gettext('Options'), - itemId: 'options', - iconCls: 'fa fa-gear', - xtype: 'pveLxcOptions' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - iconCls: 'fa fa-list', - xtype: 'proxmoxNodeTasks', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - xtype: 'pveLxcSnapshotTree', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - itemId: 'permissions', - iconCls: 'fa fa-unlock', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var lock; - if (!success) { - status = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - } - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - stopBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'stopped'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.CreateWizard', { - extend: 'PVE.window.Wizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - storage: '', - unprivileged: true - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('LXC Container'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'pct_general', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', // backend only knows vmid - guestType: 'lxc', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'proxmoxtextfield', - name: 'hostname', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Hostname'), - skipEmptyText: true, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - bind: { - value: '{unprivileged}' - }, - fieldLabel: gettext('Unprivileged container') - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'password', - value: '', - fieldLabel: gettext('Password'), - allowBlank: false, - minLength: 5, - change: function(f, value) { - if (f.rendered) { - f.up().down('field[name=confirmpw]').validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'confirmpw', - value: '', - fieldLabel: gettext('Confirm password'), - allowBlank: true, - submitValue: false, - validator: function(value) { - var pw = this.up().down('field[name=password]').getValue(); - if (pw !== value) { - return "Passwords do not match!"; - } - return true; - } - }, - { - xtype: 'proxmoxtextfield', - name: 'ssh-public-keys', - value: '', - fieldLabel: gettext('SSH public key'), - allowBlank: true, - validator: function(value) { - var pwfield = this.up().down('field[name=password]'); - if (value.length) { - var key = PVE.Parser.parseSSHKey(value); - if (!key) { - return "Failed to recognize ssh key"; - } - pwfield.allowBlank = true; - } else { - pwfield.allowBlank = false; - } - pwfield.validate(); - return true; - }, - afterRender: function() { - if (!window.FileReader) { - // No FileReader support in this browser - return; - } - var cancel = function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - }; - var field = this; - field.inputEl.on('dragover', cancel); - field.inputEl.on('dragenter', cancel); - field.inputEl.on('drop', function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - var files = ev.dataTransfer.files; - PVE.Utils.loadSSHKeyFromFile(files[0], function(v) { - field.setValue(v); - }); - }); - } - }, - { - xtype: 'filebutton', - name: 'file', - hidden: !window.FileReader, - text: gettext('Load SSH Key File'), - listeners: { - change: function(btn, e, value) { - e = e.event; - var field = this.up().down('proxmoxtextfield[name=ssh-public-keys]'); - PVE.Utils.loadSSHKeyFromFile(e.target.files[0], function(v) { - field.setValue(v); - }); - btn.reset(); - } - } - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Template'), - onlineHelp: 'pct_container_images', - column1: [ - { - xtype: 'pveStorageSelector', - name: 'tmplstorage', - fieldLabel: gettext('Storage'), - storageContent: 'vztmpl', - autoSelect: true, - allowBlank: false, - bind: { - value: '{storage}', - nodename: '{nodename}' - } - }, - { - xtype: 'pveFileSelector', - name: 'ostemplate', - storageContent: 'vztmpl', - fieldLabel: gettext('Template'), - bind: { - storage: '{storage}', - nodename: '{nodename}' - }, - allowBlank: false - } - ] - }, - { - xtype: 'pveLxcMountPointInputPanel', - title: gettext('Root Disk'), - insideWizard: true, - isCreate: true, - unused: false, - bind: { - nodename: '{nodename}', - unprivileged: '{unprivileged}' - }, - confid: 'rootfs' - }, - { - xtype: 'pveLxcCPUInputPanel', - title: gettext('CPU'), - insideWizard: true - }, - { - xtype: 'pveLxcMemoryInputPanel', - title: gettext('Memory'), - insideWizard: true - }, - { - xtype: 'pveLxcNetworkInputPanel', - title: gettext('Network'), - insideWizard: true, - bind: { - nodename: '{nodename}' - }, - isCreate: true - }, - { - xtype: 'pveLxcDNSInputPanel', - title: gettext('DNS'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var wizard = this.up('window'); - var kv = wizard.getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete' || key === 'tmplstorage') { // ignore - return; - } - if (key === 'password') { // don't show pw - return; - } - var html = Ext.htmlEncode(Ext.JSON.encode(value)); - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - delete kv.tmplstorage; - - if (!kv.pool.length) { - delete kv.pool; - } - - if (!kv.password.length && kv['ssh-public-keys']) { - delete kv.password; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response, opts){ - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - -Ext.define('PVE.lxc.SnapshotTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveLxcSnapshotTree'], - - onlineHelp: 'pct_snapshots', - - load_delay: 3000, - - old_digest: 'invalid', - - stateful: true, - stateId: 'grid-lxc-snapshots', - - sorterFn: function(rec1, rec2) { - var v1 = rec1.data.snaptime; - var v2 = rec2.data.snaptime; - - if (rec1.data.name === 'current') { - return 1; - } - if (rec2.data.name === 'current') { - return -1; - } - - return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); - }, - - reload: function(repeat) { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot', - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - me.load_task.delay(me.load_delay); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var digest = 'invalid'; - var idhash = {}; - var root = { name: '__root', expanded: true, children: [] }; - Ext.Array.each(response.result.data, function(item) { - item.leaf = true; - item.children = []; - if (item.name === 'current') { - digest = item.digest + item.running; - if (item.running) { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running'; - } else { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree'; - } - } else { - item.iconCls = 'fa fa-fw fa-history x-fa-tree'; - } - idhash[item.name] = item; - }); - - if (digest !== me.old_digest) { - me.old_digest = digest; - - Ext.Array.each(response.result.data, function(item) { - if (item.parent && idhash[item.parent]) { - var parent_item = idhash[item.parent]; - parent_item.children.push(item); - parent_item.leaf = false; - parent_item.expanded = true; - parent_item.expandable = false; - } else { - root.children.push(item); - } - }); - - me.setRootNode(root); - } - - me.load_task.delay(me.load_delay); - } - }); - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/feature', - params: { feature: 'snapshot' }, - method: 'GET', - success: function(response, options) { - var res = response.result.data; - if (res.hasFeature) { - var snpBtns = Ext.ComponentQuery.query('#snapshotBtn'); - snpBtns.forEach(function(item){ - item.enable(); - }); - } - } - }); - - - }, - - listeners: { - beforestatesave: function(grid, state, eopts) { - // extjs cannot serialize functions, - // so a the sorter with only the sorterFn will - // not be a valid sorter when restoring the state - delete state.storeState.sorters; - } - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.vmid = me.pveSelNode.data.vmid; - if (!me.vmid) { - throw "no VM ID specified"; - } - - me.load_task = new Ext.util.DelayedTask(me.reload, me); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var valid_snapshot = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current'; - }; - - var valid_snapshot_rollback = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current' && !record.data.snapstate; - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (valid_snapshot(rec)) { - var win = Ext.create('PVE.window.LxcSnapshot', { - snapname: rec.data.name, - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - me.mon(win, 'close', me.reload, me); - } - }; - - var editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot, - handler: run_editor - }); - - var rollbackBtn = new Proxmox.button.Button({ - text: gettext('Rollback'), - disabled: true, - dangerous: true, - selModel: sm, - enableFn: valid_snapshot_rollback, - confirmMsg: function(rec) { - var taskdescription = Proxmox.Utils.format_task_description('vzrollback', me.vmid); - var snaptime = Ext.Date.format(rec.data.snaptime,'Y-m-d H:i:s'); - var snapname = rec.data.name; - - var msg = Ext.String.format(gettext('{0} to {1} ({2})'), - taskdescription, snapname, snaptime); - msg += '

' + gettext('Note: Rollback stops CT') + '

'; - - return msg; - }, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname + '/rollback', - method: 'POST', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var removeBtn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.name + "'"); - return msg; - }, - enableFn: valid_snapshot, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var snapshotBtn = Ext.create('Ext.Button', { - itemId: 'snapshotBtn', - text: gettext('Take Snapshot'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.window.LxcSnapshot', { - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - } - }); - - Ext.apply(me, { - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - selModel: sm, - tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ], - fields: [ - 'name', 'description', 'snapstate', 'vmstate', 'running', - { name: 'snaptime', type: 'date', dateFormat: 'timestamp' } - ], - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - width: 200, - renderer: function(value, metaData, record) { - if (value === 'current') { - return "NOW"; - } else { - return value; - } - } - }, -// { -// text: gettext('RAM'), -// align: 'center', -// resizable: false, -// dataIndex: 'vmstate', -// width: 50, -// renderer: function(value, metaData, record) { -// if (record.data.name !== 'current') { -// return Proxmox.Utils.format_boolean(value); -// } -// } -// }, - { - text: gettext('Date') + "/" + gettext("Status"), - dataIndex: 'snaptime', - resizable: false, - width: 150, - renderer: function(value, metaData, record) { - if (record.data.snapstate) { - return record.data.snapstate; - } - if (value) { - return Ext.Date.format(value,'Y-m-d H:i:s'); - } - } - }, - { - text: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value, metaData, record) { - if (record.data.name === 'current') { - return gettext("You are here!"); - } else { - return Ext.String.htmlEncode(value); - } - } - } - ], - columnLines: true, - listeners: { - activate: me.reload, - destroy: me.load_task.cancel, - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.store.sorters.add(new Ext.util.Sorter({ - sorterFn: me.sorterFn - })); - } -}); -Ext.define('PVE.window.LxcSnapshot', { - extend: 'Ext.window.Window', - - resizable: false, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - defaultFocus: 'field', - - take_snapshot: function(snapname, descr, vmstate) { - var me = this; - var params = { snapname: snapname }; - if (descr) { - params.description = descr; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot", - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - update_snapshot: function(snapname, descr) { - var me = this; - Proxmox.Utils.API2Request({ - params: { description: descr }, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot/" + - snapname + '/config', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var summarystore = Ext.create('Ext.data.Store', { - model: 'KeyValue', - sorters: [ - { - property : 'key', - direction: 'ASC' - } - ] - }); - - var items = [ - { - xtype: me.snapname ? 'displayfield' : 'textfield', - name: 'snapname', - value: me.snapname, - fieldLabel: gettext('Name'), - vtype: 'ConfigId', - allowBlank: false - } - ]; - - if (me.snapname) { - items.push({ - xtype: 'displayfield', - name: 'snaptime', - renderer: PVE.Utils.render_timestamp_human_readable, - fieldLabel: gettext('Timestamp') - }); - } - - items.push({ - xtype: 'textareafield', - grow: true, - name: 'description', - fieldLabel: gettext('Description') - }); - - if (me.snapname) { - items.push({ - title: gettext('Settings'), - xtype: 'grid', - height: 200, - store: summarystore, - columns: [ - {header: gettext('Key'), width: 150, dataIndex: 'key'}, - {header: gettext('Value'), flex: 1, dataIndex: 'value'} - ] - }); - } - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - if (me.snapname) { - me.title = gettext('Edit') + ': ' + gettext('Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Update'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.update_snapshot(me.snapname, values.description); - } - } - }); - } else { - me.title ="VM " + me.vmid + ': ' + gettext('Take Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Take Snapshot'), - reference: 'submitbutton', - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.take_snapshot(values.snapname, values.description); - } - } - }); - } - - Ext.apply(me, { - modal: true, - width: 450, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - if (me.snapname) { - Ext.apply(me, { - width: 620, - height: 420 - }); - } - - me.callParent(); - - if (!me.snapname) { - return; - } - - // else load data - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot/" + - me.snapname + '/config', - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.close(); - }, - success: function(response, options) { - var data = response.result.data; - var kvarray = []; - Ext.Object.each(data, function(key, value) { - if (key === 'description' || key === 'snaptime') { - return; - } - kvarray.push({ key: key, value: value }); - }); - - summarystore.suspendEvents(); - summarystore.add(kvarray); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh', summarystore); - - form.findField('snaptime').setValue(data.snaptime); - form.findField('description').setValue(data.description); - } - }); - } -}); -/*jslint confusion: true */ -var labelWidth = 120; - -Ext.define('PVE.lxc.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('Memory'), - items: Ext.create('PVE.lxc.MemoryInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - - -Ext.define('PVE.lxc.CPUEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('CPU'), - items: Ext.create('PVE.lxc.CPUInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.lxc.CPUInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcCPUInputPanel', - - onlineHelp: 'pct_cpu', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - PVE.Utils.delete_if_default(values, 'cores', '', me.insideWizard); - // cpu{limit,unit} aren't in the wizard so create is always false - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - return values; - }, - - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - value: 1024, - minValue: 8, - maxValue: 500000, - labelWidth: labelWidth, - allowBlank: false - } - ], - - initComponent: function() { - var me = this; - - me.column1 = [ - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: me.insideWizard ? 1 : '', - fieldLabel: gettext('Cores'), - allowBlank: true, - deleteEmpty: true, - emptyText: gettext('unlimited') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcMemoryInputPanel', - - onlineHelp: 'pct_memory', - - insideWizard: false, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxintegerfield', - name: 'memory', - minValue: 16, - value: '512', - step: 32, - fieldLabel: gettext('Memory') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'swap', - minValue: 0, - value: '512', - step: 32, - fieldLabel: gettext('Swap') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); -Ext.define('PVE.window.MPResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 120, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -/*jslint confusion: true*/ -/* hidden: boolean and string - * bind: function and object - * disabled: boolean and string - */ -Ext.define('PVE.lxc.MountPointInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcMountPointInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_storage', - - unused: false, // add unused disk imaged - - unprivileged: false, - - vmconfig: {}, // used to select unused disks - - setUnprivileged: function(unprivileged) { - var me = this; - var vm = me.getViewModel(); - me.unprivileged = unprivileged; - vm.set('unpriv', unprivileged); - }, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || "mp"+values.mpid; - values.file = me.down('field[name=file]').getValue(); - if (values.mountoptions) { - values.mountoptions = values.mountoptions.join(';'); - } - - if (me.unused) { - confid = "mp"+values.mpid; - } else if (me.isCreate) { - values.file = values.hdstorage + ':' + values.disksize; - } - - // delete unnecessary fields - delete values.mpid; - delete values.hdstorage; - delete values.disksize; - delete values.diskformat; - - var res = {}; - res[confid] = PVE.Parser.printLxcMountPoint(values); - return res; - }, - - - setMountPoint: function(mp) { - var me = this; - var vm = this.getViewModel(); - vm.set('mptype', mp.type); - if (mp.mountoptions) { - mp.mountoptions = mp.mountoptions.split(';'); - } - - if (this.confid === 'rootfs') { - var field = me.down('field[name=mountoptions]'); - var forbidden = ['nodev', 'noexec']; - var filtered = field.comboItems.filter(e => !forbidden.includes(e[0])); - field.setComboItems(filtered); - } - - me.setValues(mp); - }, - - setVMConfig: function(vmconfig) { - var me = this; - var vm = me.getViewModel(); - me.vmconfig = vmconfig; - vm.set('unpriv', vmconfig.unprivileged); - - PVE.Utils.forEachMP(function(bus, i) { - var name = "mp" + i.toString(); - if (!Ext.isDefined(vmconfig[name])) { - me.down('field[name=mpid]').setValue(i); - return false; - } - }); - }, - - setNodename: function(nodename) { - var me = this; - var vm = me.getViewModel(); - vm.set('node', nodename); - me.down('#diskstorage').setNodename(nodename); - }, - - controller: { - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=mpid]': { - change: function(field, value) { - field.validate(); - } - }, - '#hdstorage': { - change: function(field, newValue) { - var me = this; - if (!newValue) { - return; - } - - var rec = field.store.getById(newValue); - if (!rec) { - return; - } - - var vm = me.getViewModel(); - vm.set('type', rec.data.type); - } - } - }, - - init: function(view) { - var me = this; - var vm = this.getViewModel(); - vm.set('confid', view.confid); - vm.set('unused', view.unused); - vm.set('node', view.nodename); - vm.set('unpriv', view.unprivileged); - vm.set('hideStorSelector', view.unused || !view.isCreate); - } - }, - - viewModel: { - data: { - unpriv: false, - unused: false, - showStorageSelector: false, - mptype: '', - type: '', - confid: '', - node: '' - }, - - formulas: { - quota: function(get) { - return !(get('type') === 'zfs' || - get('type') === 'zfspool' || - get('unpriv') || - get('isBind')); - }, - hasMP: function(get) { - return !!get('confid') && !get('unused'); - }, - isRoot: function(get) { - return get('confid') === 'rootfs'; - }, - isBind: function(get) { - return get('mptype') === 'bind'; - }, - isBindOrRoot: function(get) { - return get('isBind') || get('isRoot'); - } - } - }, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'mpid', - fieldLabel: gettext('Mount Point ID'), - minValue: 0, - maxValue: PVE.Utils.mp_counts.mps - 1, - hidden: true, - allowBlank: false, - disabled: true, - bind: { - hidden: '{hasMP}', - disabled: '{hasMP}' - }, - validator: function(value) { - var me = this.up('inputpanel'); - if (!me.rendered) { - return; - } - if (Ext.isDefined(me.vmconfig["mp"+value])) { - return "Mount point is already in use."; - } - /*jslint confusion: true*/ - /* returns a string above */ - return true; - } - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'diskstorage', - storageContent: 'rootdir', - hidden: true, - autoSelect: true, - selectformat: false, - defaultSize: 8, - bind: { - hidden: '{hideStorSelector}', - disabled: '{hideStorSelector}', - nodename: '{node}' - } - }, - { - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'file', - bind: { - hidden: '{!hideStorSelector}' - } - } - ], - - column2: [ - { - xtype: 'textfield', - name: 'mp', - value: '', - emptyText: gettext('/some/path'), - allowBlank: false, - disabled: true, - fieldLabel: gettext('Path'), - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'backup', - fieldLabel: gettext('Backup'), - bind: { - hidden: '{isRoot}', - disabled: '{isBindOrRoot}' - } - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'quota', - defaultValue: 0, - bind: { - disabled: '{!quota}' - }, - fieldLabel: gettext('Enable quota'), - listeners: { - disable: function() { - this.reset(); - } - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'ro', - defaultValue: 0, - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - }, - fieldLabel: gettext('Read-only') - }, - { - xtype: 'proxmoxKVComboBox', - name: 'mountoptions', - fieldLabel: gettext('Mount options'), - deleteEmpty: false, - comboItems: [ - ['noatime', 'noatime'], - ['nodev', 'nodev'], - ['noexec', 'noexec'], - ['nosuid', 'nosuid'] - ], - multiSelect: true, - value: [], - allowBlank: true - }, - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'acl', - fieldLabel: 'ACLs', - deleteEmpty: false, - comboItems: [ - ['__default__', Proxmox.Utils.defaultText], - ['1', Proxmox.Utils.enabledText], - ['0', Proxmox.Utils.disabledText] - ], - value: '__default__', - bind: { - disabled: '{isBind}' - }, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - inputValue: '0', // reverses the logic - name: 'replicate', - fieldLabel: gettext('Skip replication') - } - ] -}); - -Ext.define('PVE.lxc.MountPointEdit', { - extend: 'Proxmox.window.Edit', - - unprivileged: false, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.lxc.MountPointInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - unprivileged: me.unprivileged, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - subject = gettext('Unused Disk'); - } else if (me.isCreate) { - subject = gettext('Mount Point'); - } else { - subject = gettext('Mount Point') + ' (' + me.confid + ')'; - } - - Ext.apply(me, { - subject: subject, - defaultFocus: me.confid !== 'rootfs' ? 'textfield[name=mp]' : 'tool', - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - /*jslint confusion: true*/ - /*data is defined as array above*/ - var value = response.result.data[me.confid]; - /*jslint confusion: false*/ - var mp = PVE.Parser.parseLxcMountPoint(value); - - if (!mp) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse mount point options'); - me.close(); - return; - } - - ipanel.setMountPoint(mp); - me.isValid(); // trigger validation - } - } - }); - } -}); -Ext.define('PVE.pool.StatusView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pvePoolStatusView'], - disabled: true, - - title: gettext('Status'), - cwidth1: 150, - interval: 30000, - //height: 195, - initComponent : function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var rows = { - comment: { - header: gettext('Comment'), - renderer: Ext.String.htmlEncode, - required: true - } - }; - - Ext.apply(me, { - url: "/api2/json/pools/" + pool, - rows: rows - }); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePoolSummary', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var statusview = Ext.create('PVE.pool.StatusView', { - pveSelNode: me.pveSelNode, - style: 'padding-top:0px' - }); - - var rstore = statusview.rstore; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - defaults: { - style: 'padding-top:10px', - width: 800 - }, - items: [ statusview ] - }); - - me.on('activate', rstore.startUpdate); - me.on('destroy', rstore.stopUpdate); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.pvePoolConfig', - - onlineHelp: 'pveum_pools', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - Ext.apply(me, { - title: Ext.String.format(gettext("Resource Pool") + ': ' + pool), - hstateid: 'pooltab', - items: [ - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - xtype: 'pvePoolSummary', - itemId: 'summary' - }, - { - title: gettext('Members'), - xtype: 'pvePoolMembers', - iconCls: 'fa fa-th', - pool: pool, - itemId: 'members' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/pool/' + pool - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.panel.StorageBase', { - extend: 'Proxmox.panel.InputPanel', - controller: 'storageEdit', - - type: '', - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = me.type; - } else { - delete values.storage; - } - - values.disable = values.enable ? 0 : 1; - delete values.enable; - - return values; - }, - - initComponent : function() { - var me = this; - - me.column1.unshift({ - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'storage', - value: me.storageId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }); - - me.column2.unshift( - { - xtype: 'pveNodeSelector', - name: 'nodes', - disabled: me.storageId === 'local', - fieldLabel: gettext('Nodes'), - emptyText: gettext('All') + ' (' + gettext('No restrictions') +')', - multiSelect: true, - autoSelect: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - me.isCreate = !me.storageId; - - if (me.isCreate) { - me.url = '/api2/extjs/storage'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/storage/' + me.storageId; - me.method = 'PUT'; - } - - var ipanel = Ext.create(me.paneltype, { - type: me.type, - isCreate: me.isCreate, - storageId: me.storageId - }); - - Ext.apply(me, { - subject: PVE.Utils.format_storage_type(me.type), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - var ctypes = values.content || ''; - - values.content = ctypes.split(','); - - if (values.nodes) { - values.nodes = values.nodes.split(','); - } - values.enable = values.disable ? 0 : 1; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.grid.TemplateSelector', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveTemplateSelector', - - stateful: true, - stateId: 'grid-template-selector', - viewConfig: { - trackOver: false - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/aplinfo"; - var store = new Ext.data.Store({ - model: 'pve-aplinfo', - groupField: 'section', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{[ "Section: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - '->', - gettext('Search'), - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - var value = field.getValue().toLowerCase(); - store.clearFilter(true); - store.filterBy(function(rec) { - return (rec.data['package'].toLowerCase().indexOf(value) !== -1) - || (rec.data.headline.toLowerCase().indexOf(value) !== -1); - }); - } - } - } - ], - features: [ groupingFeature ], - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Package'), - flex: 1, - dataIndex: 'package' - }, - { - header: gettext('Version'), - width: 80, - dataIndex: 'version' - }, - { - header: gettext('Description'), - flex: 1.5, - renderer: Ext.String.htmlEncode, - dataIndex: 'headline' - } - ], - listeners: { - afterRender: reload - } - }); - - me.callParent(); - } - -}, function() { - - Ext.define('pve-aplinfo', { - extend: 'Ext.data.Model', - fields: [ - 'template', 'type', 'package', 'version', 'headline', 'infopage', - 'description', 'os', 'section' - ], - idProperty: 'template' - }); - -}); - -Ext.define('PVE.storage.TemplateDownload', { - extend: 'Ext.window.Window', - alias: 'widget.pveTemplateDownload', - - modal: true, - title: gettext('Templates'), - layout: 'fit', - width: 900, - height: 600, - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var grid = Ext.create('PVE.grid.TemplateSelector', { - border: false, - scrollable: true, - nodename: me.nodename - }); - - var sm = grid.getSelectionModel(); - - var submitBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Download'), - disabled: true, - selModel: sm, - handler: function(button, event, rec) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/aplinfo', - params: { - storage: me.storage, - template: rec.data.template - }, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - destroy: me.reloadGrid - } - }).show(); - - me.close(); - } - }); - } - }); - - Ext.apply(me, { - items: grid, - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.Upload', { - extend: 'Ext.window.Window', - alias: 'widget.pveStorageUpload', - - resizable: false, - - modal: true, - - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var xhr; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/storage/" + me.storage + "/upload"; - - var pbar = Ext.create('Ext.ProgressBar', { - text: 'Ready', - hidden: true - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - method: 'POST', - waitMsgTarget: true, - bodyPadding: 10, - border: false, - width: 300, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - xtype: 'pveContentTypeSelector', - cts: me.contents, - fieldLabel: gettext('Content'), - name: 'content', - value: me.contents[0] || '', - allowBlank: false - }, - { - xtype: 'filefield', - name: 'filename', - buttonText: gettext('Select File...'), - allowBlank: false - }, - pbar - ] - }); - - var form = me.formPanel.getForm(); - - var doStandardSubmit = function() { - form.submit({ - url: "/api2/htmljs" + baseurl, - waitMsg: gettext('Uploading file...'), - success: function(f, action) { - me.close(); - }, - failure: function(f, action) { - var msg = PVE.Utils.extractFormActionError(action); - Ext.Msg.alert(gettext('Error'), msg); - } - }); - }; - - var updateProgress = function(per, bytes) { - var text = (per * 100).toFixed(2) + '%'; - if (bytes) { - text += " (" + Proxmox.Utils.format_size(bytes) + ')'; - } - pbar.updateProgress(per, text); - }; - - var abortBtn = Ext.create('Ext.Button', { - text: gettext('Abort'), - disabled: true, - handler: function() { - me.close(); - } - }); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Upload'), - disabled: true, - handler: function(button) { - var fd; - try { - fd = new FormData(); - } catch (err) { - doStandardSubmit(); - return; - } - - button.setDisabled(true); - abortBtn.setDisabled(false); - - var field = form.findField('content'); - fd.append("content", field.getValue()); - field.setDisabled(true); - - field = form.findField('filename'); - var file = field.fileInputEl.dom; - fd.append("filename", file.files[0]); - field.setDisabled(true); - - pbar.setVisible(true); - updateProgress(0); - - xhr = new XMLHttpRequest(); - - xhr.addEventListener("load", function(e) { - if (xhr.status == 200) { - me.close(); - } else { - var msg = gettext('Error') + " " + xhr.status.toString() + ": " + Ext.htmlEncode(xhr.statusText); - if (xhr.responseText !== "") { - var result = Ext.decode(xhr.responseText); - result.message = msg; - msg = Proxmox.Utils.extractRequestError(result, true); - } - Ext.Msg.alert(gettext('Error'), msg, function(btn) { - me.close(); - }); - } - }, false); - - xhr.addEventListener("error", function(e) { - var msg = "Error " + e.target.status.toString() + " occurred while receiving the document."; - Ext.Msg.alert(gettext('Error'), msg, function(btn) { - me.close(); - }); - }); - - xhr.upload.addEventListener("progress", function(evt) { - if (evt.lengthComputable) { - var percentComplete = evt.loaded / evt.total; - updateProgress(percentComplete, evt.loaded); - } - }, false); - - xhr.open("POST", "/api2/json" + baseurl, true); - xhr.send(fd); - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - Ext.apply(me, { - title: gettext('Upload'), - items: me.formPanel, - buttons: [ abortBtn, submitBtn ], - listeners: { - close: function() { - if (xhr) { - xhr.abort(); - } - } - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ContentView', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveStorageContentView', - - stateful: true, - stateId: 'grid-storage-content', - viewConfig: { - trackOver: false, - loadMask: false - }, - features: [ - { - ftype: 'grouping', - groupHeaderTpl: '{name} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - } - ], - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + nodename + "/storage/" + storage + "/content"; - var store = Ext.create('Ext.data.Store',{ - model: 'pve-storage-content', - groupField: 'content', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - }, - sorters: { - property: 'volid', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - me.statusStore.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var templateButton = Ext.create('Proxmox.button.Button',{ - itemId: 'tmpl-btn', - text: gettext('Templates'), - handler: function() { - var win = Ext.create('PVE.storage.TemplateDownload', { - nodename: nodename, - storage: storage, - reloadGrid: reload - }); - win.show(); - } - }); - - var uploadButton = Ext.create('Proxmox.button.Button', { - contents : ['iso','vztmpl'], - text: gettext('Upload'), - handler: function() { - var me = this; - var win = Ext.create('PVE.storage.Upload', { - nodename: nodename, - storage: storage, - contents: me.contents - }); - win.show(); - win.on('destroy', reload); - } - }); - - var imageRemoveButton; - var removeButton = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - enableFn: function(rec) { - if (rec && rec.data.content !== 'images') { - imageRemoveButton.setVisible(false); - removeButton.setVisible(true); - return true; - } - return false; - }, - callback: function() { - reload(); - }, - baseurl: baseurl + '/' - }); - - imageRemoveButton = Ext.create('Proxmox.button.Button',{ - selModel: sm, - hidden: true, - text: gettext('Remove'), - enableFn: function(rec) { - if (rec && rec.data.content === 'images') { - removeButton.setVisible(false); - imageRemoveButton.setVisible(true); - return true; - } - return false; - }, - handler: function(btn, event, rec) { - me = this; - - var url = baseurl + '/' + rec.data.volid; - var vmid = rec.data.vmid; - - var store = PVE.data.ResourceStore; - - if (vmid && store.findVMID(vmid)) { - var guest_node = store.guestNode(vmid); - var storage_path = 'storage/' + nodename + '/' + storage; - - // allow to delete local backed images if a VMID exists on another node. - if (store.storageIsShared(storage_path) || guest_node == nodename) { - var msg = Ext.String.format( - gettext("Cannot remove image, a guest with VMID '{0}' exists!"), vmid); - msg += '
' + gettext("You can delete the image from the guest's hardware pane"); - - Ext.Msg.show({ - title: gettext('Cannot remove disk image.'), - icon: Ext.Msg.ERROR, - msg: msg - }); - return; - } - } - var win = Ext.create('PVE.window.SafeDestroy', { - title: Ext.String.format(gettext("Destroy '{0}'"), rec.data.volid), - showProgress: true, - url: url, - item: { type: 'Image', id: vmid } - }).show(); - win.on('destroy', function() { - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - reload(); - - }); - } - }); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - xtype: 'proxmoxButton', - text: gettext('Restore'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b, e, rec) { - var vmtype; - if (rec.data.volid.match(/vzdump-qemu-/)) { - vmtype = 'qemu'; - } else if (rec.data.volid.match(/vzdump-openvz-/) || rec.data.volid.match(/vzdump-lxc-/)) { - vmtype = 'lxc'; - } else { - return; - } - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }, - removeButton, - imageRemoveButton, - templateButton, - uploadButton, - { - xtype: 'proxmoxButton', - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b,e,rec) { - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }, - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - store.clearFilter(true); - store.filter([ - { - property: 'text', - value: field.getValue(), - anyMatch: true, - caseSensitive: false - } - ]); - } - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'text' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ], - listeners: { - activate: reload - } - }); - - me.callParent(); - - // disable the buttons/restrict the upload window - // if templates or uploads are not allowed - me.mon(me.statusStore, 'load', function(s, records, success) { - var availcontent = []; - Ext.Array.each(records, function(item){ - if (item.id === 'content') { - availcontent = item.data.value.split(','); - } - }); - var templ = false; - var upload = false; - var cts = []; - - Ext.Array.each(availcontent, function(content) { - if (content === 'vztmpl') { - templ = true; - cts.push('vztmpl'); - } else if (content === 'iso') { - upload = true; - cts.push('iso'); - } - }); - - if (templ !== upload) { - uploadButton.contents = cts; - } - - templateButton.setDisabled(!templ); - uploadButton.setDisabled(!upload && !templ); - }); - } -}, function() { - - Ext.define('pve-storage-content', { - extend: 'Ext.data.Model', - fields: [ - 'volid', 'content', 'format', 'size', 'used', 'vmid', - 'channel', 'id', 'lun', - { - name: 'text', - convert: function(value, record) { - // check for volid, because if you click on a grouping header, - // it calls convert (but with an empty volid) - if (value || record.data.volid === null) { - return value; - } - return PVE.Utils.render_storage_content(value, {}, record); - } - } - ], - idProperty: 'volid' - }); - -}); -Ext.define('PVE.storage.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveStorageStatusView', - - height: 230, - title: gettext('Status'), - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 30 5 30' - }, - items: [ - { - xtype: 'box', - height: 30 - }, - { - itemId: 'enabled', - title: gettext('Enabled'), - printBar: false, - textField: 'disabled', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - itemId: 'active', - title: gettext('Active'), - printBar: false, - textField: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - itemId: 'content', - title: gettext('Content'), - printBar: false, - textField: 'content', - renderer: PVE.Utils.format_content_types - }, - { - itemId: 'type', - title: gettext('Type'), - printBar: false, - textField: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - xtype: 'box', - height: 10 - }, - { - itemId: 'usage', - title: gettext('Usage'), - valueField: 'used', - maxField: 'total' - } - ], - - updateTitle: function() { - return; - } -}); -Ext.define('PVE.storage.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStorageSummary', - scrollable: true, - bodyPadding: 5, - tbar: [ - '->', - { - xtype: 'proxmoxRRDTypeSelector' - } - ], - layout: { - type: 'column' - }, - defaults: { - padding: 5, - columnWidth: 1 - }, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var rstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/status", - interval: 1000 - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/rrddata", - model: 'pve-rrd-storage' - }); - - Ext.apply(me, { - items: [ - { - xtype: 'pveStorageStatusView', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Usage'), - fields: ['total','used'], - fieldTitles: ['Total Size', 'Used Size'], - store: rrdstore - } - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.storage.Browser', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.storage.Browser', - - onlineHelp: 'chapter_storage', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storeid = me.pveSelNode.data.storage; - if (!storeid) { - throw "no storage ID specified"; - } - - - me.items = [ - { - title: gettext('Summary'), - xtype: 'pveStorageSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ]; - - var caps = Ext.state.Manager.get('GuiCap'); - - Ext.apply(me, { - title: Ext.String.format(gettext("Storage {0} on node {1}"), - "'" + storeid + "'", "'" + nodename + "'"), - hstateid: 'storagetab' - }); - - if (caps.storage['Datastore.Allocate'] || - caps.storage['Datastore.AllocateSpace'] || - caps.storage['Datastore.Audit']) { - me.items.push({ - xtype: 'pveStorageContentView', - title: gettext('Content'), - iconCls: 'fa fa-th', - itemId: 'content' - }); - } - - if (caps.storage['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/storage/' + storeid - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.storage.DirInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_directory', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'path', - value: '', - fieldLabel: gettext('Directory'), - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.NFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveNFSScan', - - queryParam: 'server', - - valueField: 'path', - displayField: 'path', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.nfsServer) { - me.store.removeAll(); - } - - me.allQuery = me.nfsServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.nfsServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'path', 'options' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/nfs' - } - }); - - store.sort('path', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.NFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_nfs', - - options : [], - - onGetValues: function(values) { - var me = this; - - var i; - var res = []; - for (i = 0; i < me.options.length; i++) { - var item = me.options[i]; - if (!item.match(/^vers=(.*)$/)) { - res.push(item); - } - } - if (values.nfsversion && values.nfsversion !== '__default__') { - res.push('vers=' + values.nfsversion); - } - delete values.nfsversion; - values.options = res.join(','); - if (values.options === '') { - delete values.options; - if (!me.isCreate) { - values["delete"] = "options"; - } - } - - return me.callParent([values]); - }, - - setValues: function(values) { - var me = this; - if (values.options) { - var res = values.options; - me.options = values.options.split(','); - me.options.forEach(function(item) { - var match = item.match(/^vers=(.*)$/); - if (match) { - values.nfsversion = match[1]; - } - }); - } - return me.callParent([values]); - }, - - initComponent : function() { - var me = this; - - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=export]'); - exportField.setServer(value); - exportField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'pveNFSScan' : 'displayfield', - name: 'export', - value: '', - fieldLabel: 'Export', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('NFS Version'), - name: 'nfsversion', - value: '__default__', - deleteEmpty: false, - comboItems: [ - ['__default__', Proxmox.Utils.defaultText], - ['3', '3'], - ['4', '4'], - ['4.1', '4.1'], - ['4.2', '4.2'] - ] - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.CIFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCIFSScan', - - queryParam: 'server', - - valueField: 'share', - displayField: 'share', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.cifsServer) { - me.store.removeAll(); - } - - var params = {}; - if (me.cifsUsername && me.cifsPassword) { - params.username = me.cifsUsername; - params.password = me.cifsPassword; - } - - if (me.cifsDomain) { - params.domain = me.cifsDomain; - } - - me.store.getProxy().setExtraParams(params); - me.allQuery = me.cifsServer; - - me.callParent(); - }, - - setServer: function(server) { - this.cifsServer = server; - }, - - setUsername: function(username) { - this.cifsUsername = username; - }, - - setPassword: function(password) { - this.cifsPassword = password; - }, - - setDomain: function(domain) { - this.cifsDomain = domain; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['description', 'share'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/cifs' - } - }); - store.sort('share', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.CIFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_cifs', - - initComponent : function() { - var me = this; - - var passwordfield = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - inputType: 'password', - name: 'password', - value: me.isCreate ? '' : '********', - fieldLabel: gettext('Password'), - allowBlank: false, - disabled: me.isCreate, - minLength: 1, - listeners: { - change: function(f, value) { - - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setPassword(value); - } - } - } - }); - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setServer(value); - } - } - } - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: '', - fieldLabel: gettext('Username'), - emptyText: gettext('Guest user'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (!me.isCreate) { - return; - } - var exportField = me.down('field[name=share]'); - exportField.setUsername(value); - - if (value == "") { - passwordfield.disable(); - } else { - passwordfield.enable(); - } - passwordfield.validate(); - } - } - }, - passwordfield, - { - xtype: me.isCreate ? 'pveCIFSScan' : 'displayfield', - name: 'share', - value: '', - fieldLabel: 'Share', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'domain', - value: me.isCreate ? '' : undefined, - fieldLabel: gettext('Domain'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (me.isCreate) { - - var exportField = me.down('field[name=share]'); - exportField.setDomain(value); - } - } - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.GlusterFsScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveGlusterFsScan', - - queryParam: 'server', - - valueField: 'volname', - displayField: 'volname', - matchFieldWidth: false, - listConfig: { - loadingText: 'Scanning...', - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.glusterServer) { - me.store.removeAll(); - } - - me.allQuery = me.glusterServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.glusterServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'volname' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/glusterfs' - } - }); - - store.sort('volname', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.GlusterFsInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_glusterfs', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var volumeField = me.down('field[name=volume]'); - volumeField.setServer(value); - volumeField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'server2', - value: '', - fieldLabel: gettext('Second Server'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'pveGlusterFsScan' : 'displayfield', - name: 'volume', - value: '', - fieldLabel: 'Volume name', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'iso', 'backup', 'vztmpl', 'snippets'], - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.IScsiScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveIScsiScan', - - queryParam: 'portal', - valueField: 'target', - displayField: 'target', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.portal) { - me.store.removeAll(); - } - - me.allQuery = me.portal; - - me.callParent(); - }, - - setPortal: function(portal) { - var me = this; - - me.portal = portal; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'target', 'portal' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/iscsi' - } - }); - - store.sort('target', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.IScsiInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_open_iscsi', - - onGetValues: function(values) { - var me = this; - - values.content = values.luns ? 'images' : 'none'; - delete values.luns; - - return me.callParent([values]); - }, - - setValues: function(values) { - values.luns = (values.content.indexOf('images') !== -1) ? true : false; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: 'Portal', - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=target]'); - exportField.setPortal(value); - exportField.setValue(''); - } - } - } - }, - { - readOnly: !me.isCreate, - xtype: me.isCreate ? 'pveIScsiScan' : 'displayfield', - name: 'target', - value: '', - fieldLabel: 'Target', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'checkbox', - name: 'luns', - checked: true, - fieldLabel: gettext('Use LUNs directly') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.VgSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveVgSelector', - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'vg', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - store.sort('vg', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseStorageSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseStorageSelector', - - existingGroupsText: gettext("Existing volume groups"), - queryMode: 'local', - editable: false, - value: '', - valueField: 'storage', - displayField: 'text', - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: { - addRecords: true, - params: { - type: 'iscsi' - } - }, - fields: [ 'storage', 'type', 'content', - { - name: 'text', - convert: function(value, record) { - if (record.data.storage) { - return record.data.storage + " (iSCSI)"; - } else { - return me.existingGroupsText; - } - } - }], - proxy: { - type: 'proxmox', - url: '/api2/json/storage/' - } - }); - - store.loadData([{ storage: '' }], true); - - store.sort('storage', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LVMInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvm', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.VgSelector', { - name: 'vgname', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var baseField = Ext.createWidget('pveFileSelector', { - name: 'base', - hidden: true, - disabled: true, - nodename: 'localhost', - storageContent: 'images', - fieldLabel: gettext('Base volume'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseStorageSelector', - name: 'basesel', - fieldLabel: gettext('Base storage'), - submitValue: false, - listeners: { - change: function(f, value) { - if (value) { - vgnameField.setVisible(true); - vgnameField.setDisabled(false); - vgField.setVisible(false); - vgField.setDisabled(true); - baseField.setVisible(true); - baseField.setDisabled(false); - } else { - vgnameField.setVisible(false); - vgnameField.setDisabled(true); - vgField.setVisible(true); - vgField.setDisabled(false); - baseField.setVisible(false); - baseField.setDisabled(true); - } - baseField.setStorage(value); - } - } - }); - - me.column1.push(baseField); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.TPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveTPSelector', - - queryParam: 'vg', - valueField: 'lv', - displayField: 'lv', - editable: false, - - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.vg) { - me.store.removeAll(); - } - - me.allQuery = me.vg; - - me.callParent(); - }, - - setVG: function(myvg) { - var me = this; - - me.vg = myvg; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'lv' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvmthin' - } - }); - - store.sort('lv', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseVGSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseVGSelector', - - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, - fields: [ 'vg', 'size', 'free'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LvmThinInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvmthin', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var thinpoolField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'thinpool', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.TPoolSelector', { - name: 'thinpool', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseVGSelector', - name: 'vgname', - fieldLabel: gettext('Volume group'), - listeners: { - change: function(f, value) { - if (me.isCreate) { - vgField.setVG(value); - vgField.setValue(''); - } - } - } - }); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - me.column1.push(thinpoolField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = []; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.CephFSInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'storage_cephfs', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'cephfs'; - - me.column1 = []; - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - value: '', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: 'admin', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['backup', 'iso', 'vztmpl', 'snippets'], - fieldLabel: gettext('Content'), - name: 'content', - value: 'backup', - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged cephFS') - }]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.Ceph.Model', { - extend: 'Ext.app.ViewModel', - alias: 'viewmodel.cephstorage', - - data: { - pveceph: true, - pvecephPossible: true - } -}); - -Ext.define('PVE.storage.Ceph.Controller', { - extend: 'PVE.controller.StorageEdit', - alias: 'controller.cephstorage', - - control: { - '#': { - afterrender: 'queryMonitors' - }, - 'textfield[name=username]': { - disable: 'resetField' - }, - 'displayfield[name=monhost]': { - enable: 'queryMonitors' - }, - 'textfield[name=monhost]': { - disable: 'resetField', - enable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - queryMonitors: function(field, newVal, oldVal) { - // we get called with two signatures, the above one for a field - // change event and the afterrender from the view, this check only - // can be true for the field change one and omit the API request if - // pveceph got unchecked - as it's not needed there. - if (field && !newVal && oldVal) { - return; - } - var view = this.getView(); - var vm = this.getViewModel(); - if (!(view.isCreate || vm.get('pveceph'))) { - return; // only query on create or if editing a pveceph store - } - - var monhostField = this.lookupReference('monhost'); - - Proxmox.Utils.API2Request({ - url: '/api2/json/nodes/localhost/ceph/mon', - method: 'GET', - scope: this, - callback: function(options, success, response) { - var data = response.result.data; - if (response.status === 200) { - if (data.length > 0) { - var monhost = Ext.Array.pluck(data, 'name').sort().join(','); - monhostField.setValue(monhost); - monhostField.resetOriginalValue(); - if (view.isCreate) { - vm.set('pvecephPossible', true); - } - } else { - vm.set('pveceph', false); - } - } else { - vm.set('pveceph', false); - vm.set('pvecephPossible', false); - } - } - }); - } -}); - -Ext.define('PVE.storage.RBDInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'ceph_rados_block_devices', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'rbd'; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push({ - xtype: 'pveCephPoolSelector', - nodename: me.nodename, - name: 'pool', - bind: { - disabled: '{!pveceph}', - submitValue: '{pveceph}', - hidden: '{!pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - },{ - xtype: 'textfield', - name: 'pool', - value: 'rbd', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } else { - me.column1.push({ - xtype: 'displayfield', - nodename: me.nodename, - name: 'pool', - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - value: 'admin', - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images'], - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'krbd', - uncheckedValue: 0, - fieldLabel: 'KRBD' - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged ceph pool') - }]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.ZFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - viewModel: { - parent: null, - data: { - isLIO: false, - isComstar: true, - hasWriteCacheOption: true - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[name=iscsiprovider]': { - change: 'changeISCSIProvider' - } - }, - changeISCSIProvider: function(f, newVal, oldVal) { - var vm = this.getViewModel(); - vm.set('isLIO', newVal === 'LIO'); - vm.set('isComstar', newVal === 'comstar'); - vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt'); - } - }, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.content = 'images'; - } - - values.nowritecache = values.writecache ? 0 : 1; - delete values.writecache; - - return me.callParent([values]); - }, - - setValues: function diff(values) { - values.writecache = values.nowritecache ? 0 : 1; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: gettext('Portal'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'pool', - value: '', - fieldLabel: gettext('Pool'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'blocksize', - value: '4k', - fieldLabel: gettext('Block Size'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'target', - value: '', - fieldLabel: gettext('Target'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_tg', - value: '', - fieldLabel: gettext('Target group'), - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - allowBlank: true - } - ]; - - me.column2 = [ - { - xtype: me.isCreate ? 'pveiScsiProviderSelector' : 'displayfield', - name: 'iscsiprovider', - value: 'comstar', - fieldLabel: gettext('iSCSI Provider'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'proxmoxcheckbox', - name: 'writecache', - checked: true, - bind: me.isCreate ? { disabled: '{!hasWriteCacheOption}' } : { hidden: '{!hasWriteCacheOption}' }, - uncheckedValue: 0, - fieldLabel: gettext('Write cache') - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_hg', - value: '', - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - fieldLabel: gettext('Host group'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'lio_tpg', - value: '', - bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' }, - allowBlank: false, - fieldLabel: gettext('Target portal group') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.ZFSPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveZFSPoolSelector', - valueField: 'pool', - displayField: 'pool', - queryMode: 'local', - editable: false, - listConfig: { - loadingText: gettext('Scanning...') - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'pool', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/zfs' - } - }); - - store.sort('pool', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ZFSPoolInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_zfspool', - - initComponent : function() { - var me = this; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push(Ext.create('PVE.storage.ZFSPoolSelector', { - name: 'pool', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } else { - me.column1.push(Ext.createWidget('displayfield', { - name: 'pool', - value: '', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } - - // value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push( - {xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'textfield', - name: 'blocksize', - emptyText: '8k', - fieldLabel: gettext('Block Size'), - allowBlank: true - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.ha.StatusView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAStatusView'], - - onlineHelp: 'chapter_ha_manager', - - sortPriority: { - quorum: 1, - master: 2, - lrm: 3, - service: 4 - }, - - initComponent : function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sortAfterUpdate: true, - sorters: [{ - sorterFn: function(rec1, rec2) { - var p1 = me.sortPriority[rec1.data.type]; - var p2 = me.sortPriority[rec2.data.type]; - return (p1 !== p2) ? ((p1 > p2) ? 1 : -1) : 0; - } - }], - filters: { - property: 'type', - value: 'service', - operator: '!=' - } - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Status'), - width: 80, - flex: 1, - dataIndex: 'status' - } - ] - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - } -}, function() { - - Ext.define('pve-ha-status', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'type', 'node', 'status', 'sid', - 'state', 'group', 'comment', - 'max_restart', 'max_relocate', 'type', - 'crm_state', 'request_state' - ], - idProperty: 'id' - }); - -}); -Ext.define('PVE.ha.Status', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveHAStatus', - - onlineHelp: 'chapter_ha_manager', - layout: { - type: 'vbox', - align: 'stretch' - }, - - initComponent: function() { - var me = this; - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - interval: me.interval, - model: 'pve-ha-status', - storeid: 'pve-store-' + (++Ext.idSeed), - groupField: 'type', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/ha/status/current' - } - }); - - me.items = [{ - xtype: 'pveHAStatusView', - title: gettext('Status'), - rstore: me.rstore, - border: 0, - collapsible: true, - padding: '0 0 20 0' - },{ - xtype: 'pveHAResourcesView', - flex: 1, - collapsible: true, - title: gettext('Resources'), - border: 0, - rstore: me.rstore - }]; - - me.callParent(); - me.on('activate', me.rstore.startUpdate); - } -}); -Ext.define('PVE.ha.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveHAGroupSelector'], - - value: [], - autoSelect: false, - valueField: 'group', - displayField: 'group', - listConfig: { - columns: [ - { - header: gettext('Group'), - width: 100, - sortable: true, - dataIndex: 'group' - }, - { - header: gettext('Nodes'), - width: 100, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode - } - ] - }, - store: { - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }, - - initComponent: function() { - var me = this; - me.callParent(); - me.getStore().load(); - } - -}, function() { - - Ext.define('pve-ha-groups', { - extend: 'Ext.data.Model', - fields: [ - 'group', 'type', 'digest', 'nodes', 'comment', - { - name : 'restricted', - type: 'boolean' - }, - { - name : 'nofailback', - type: 'boolean' - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/ha/groups" - }, - idProperty: 'group' - }); -}); -Ext.define('PVE.ha.VMResourceInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_resource_config', - vmid: undefined, - - onGetValues: function(values) { - var me = this; - - if (values.vmid) { - values.sid = values.vmid; - } - delete values.vmid; - - PVE.Utils.delete_if_default(values, 'group', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_restart', '1', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_relocate', '1', me.isCreate); - - return values; - }, - - initComponent : function() { - var me = this; - var MIN_QUORUM_VOTES = 3; - - var disabledHint = Ext.createWidget({ - xtype: 'displayfield', // won't get submitted by default - userCls: 'pve-hint', - value: 'Disabling the resource will stop the guest system. ' + - 'See the online help for details.', - hidden: true - }); - - var fewVotesHint = Ext.createWidget({ - itemId: 'fewVotesHint', - xtype: 'displayfield', - userCls: 'pve-hint', - value: 'At least three quorum votes are recommended for reliable HA.', - hidden: true - }); - - Proxmox.Utils.API2Request({ - url: '/cluster/config/nodes', - method: 'GET', - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var nodes = response.result.data; - var votes = 0; - Ext.Array.forEach(nodes, function(node) { - var vote = parseInt(node.quorum_votes, 10); // parse as base 10 - votes += vote || 0; // parseInt might return NaN, which is false - }); - - if (votes < MIN_QUORUM_VOTES) { - fewVotesHint.setVisible(true); - } - } - }); - - /*jslint confusion: true */ - var vmidStore = (me.vmid) ? {} : { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [ - { - property: 'type', - value: /lxc|qemu/ - }, - { - property: 'hastate', - value: /unmanaged/ - } - ] - }; - - // value is a string above, but a number below - me.column1 = [ - { - xtype: me.vmid ? 'displayfield' : 'vmComboSelector', - submitValue: me.isCreate, - name: 'vmid', - fieldLabel: (me.vmid && me.guestType === 'ct') ? 'CT' : 'VM', - value: me.vmid, - store: vmidStore, - validateExists: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_restart', - fieldLabel: gettext('Max. Restart'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_relocate', - fieldLabel: gettext('Max. Relocate'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - } - ]; - /*jslint confusion: false */ - - me.column2 = [ - { - xtype: 'pveHAGroupSelector', - name: 'group', - fieldLabel: gettext('Group') - }, - { - xtype: 'proxmoxKVComboBox', - name: 'state', - value: 'started', - fieldLabel: gettext('Request State'), - comboItems: [ - ['started', 'started'], - ['stopped', 'stopped'], - ['ignored', 'ignored'], - ['disabled', 'disabled'] - ], - listeners: { - 'change': function(field, newValue) { - if (newValue === 'disabled') { - disabledHint.setVisible(true); - } - else { - if (disabledHint.isVisible()) { - disabledHint.setVisible(false); - } - } - } - } - }, - disabledHint - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - fewVotesHint - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.VMResourceEdit', { - extend: 'Proxmox.window.Edit', - - vmid: undefined, - guestType: undefined, - isCreate: undefined, - - initComponent : function() { - var me = this; - - if (me.isCreate === undefined) { - me.isCreate = !me.vmid; - } - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/resources'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/resources/' + me.vmid; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.VMResourceInputPanel', { - isCreate: me.isCreate, - vmid: me.vmid, - guestType: me.guestType - }); - - Ext.apply(me, { - subject: gettext('Resource') + ': ' + gettext('Container') + - '/' + gettext('Virtual Machine'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(values.sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - throw "got unexpected resource type"; - } - - values.vmid = res[2]; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.ResourcesView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAResourcesView'], - - onlineHelp: 'ha_manager_resources', - - stateful: true, - stateId: 'grid-ha-resources', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!me.rstore) { - throw "no store given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - filters: { - property: 'type', - value: 'service' - } - }); - - var reload = function() { - me.rstore.load(); - }; - - var render_error = function(dataIndex, value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors[dataIndex]; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - var sid = rec.data.sid; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - return; - } - var guestType = res[1]; - var vmid = res[2]; - - var win = Ext.create('PVE.ha.VMResourceEdit',{ - guestType: guestType, - vmid: vmid - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/resources/', - getUrl: function(rec) { - var me = this; - return me.baseurl + '/' + rec.get('sid'); - }, - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.VMResourceEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - - columns: [ - { - header: 'ID', - width: 100, - sortable: true, - dataIndex: 'sid' - }, - { - header: gettext('State'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Request State'), - width: 100, - hidden: true, - sortable: true, - renderer: function(v) { - return v || 'started'; - }, - dataIndex: 'request_state' - }, - { - header: gettext('CRM State'), - width: 100, - hidden: true, - sortable: true, - dataIndex: 'crm_state' - }, - { - header: gettext('Max. Restart'), - width: 100, - sortable: true, - renderer: (v) => v === undefined ? '1' : v, - dataIndex: 'max_restart' - }, - { - header: gettext('Max. Relocate'), - width: 100, - sortable: true, - renderer: (v) => v === undefined ? '1' : v, - dataIndex: 'max_relocate' - }, - { - header: gettext('Group'), - width: 200, - sortable: true, - renderer: function(value, metaData, record) { - return render_error('group', value, metaData, record); - }, - dataIndex: 'group' - }, - { - header: gettext('Description'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-ha-resources', { - extend: 'Ext.data.Model', - fields: [ - 'sid', 'state', 'digest', 'errors', 'group', 'comment', - 'max_restart', 'max_relocate', 'type', 'status', 'node', - 'crm_state', 'request_state' - ], - idProperty: 'sid' - }); - -}); -Ext.define('PVE.ha.GroupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_groups', - - groupId: undefined, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = 'group'; - } - - return values; - }, - - initComponent : function() { - var me = this; - - var update_nodefield, update_node_selection; - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - update_nodefield(selected); - } - } - }); - - // use already cached data to avoid an API call - var data = PVE.data.ResourceStore.getNodes(); - - var store = Ext.create('Ext.data.Store', { - fields: [ 'node', 'mem', 'cpu', 'priority' ], - data: data, - proxy: { - type: 'memory', - reader: {type: 'json'} - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - } - ] - }); - - var nodegrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - columns: [ - { - header: gettext('Node'), - flex: 1, - dataIndex: 'node' - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 150, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 150, - dataIndex: 'cpu' - }, - { - header: 'Priority', - xtype: 'widgetcolumn', - dataIndex: 'priority', - sortable: true, - stopSelection: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 0, - maxValue: 1000, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('priority', value); - update_nodefield(sm.getSelection()); - } - } - } - } - ] - }); - - var nodefield = Ext.create('Ext.form.field.Hidden', { - name: 'nodes', - value: '', - listeners: { - change: function (nodefield, value) { - update_node_selection(value); - } - }, - isValid: function () { - var value = nodefield.getValue(); - return (value && 0 !== value.length); - } - }); - - update_node_selection = function(string) { - sm.deselectAll(true); - - string.split(',').forEach(function (e, idx, array) { - var res = e.split(':'); - - store.each(function(record) { - var node = record.get('node'); - - if (node == res[0]) { - sm.select(record, true); - record.set('priority', res[1]); - record.commit(); - } - }); - }); - nodegrid.reconfigure(store); - - }; - - update_nodefield = function(selected) { - var nodes = ''; - var first_iteration = true; - Ext.Array.each(selected, function(record) { - if (!first_iteration) { - nodes += ','; - } - first_iteration = false; - - nodes += record.data.node; - if (record.data.priority) { - nodes += ':' + record.data.priority; - } - }); - - // nodefield change listener calls us again, which results in a - // endless recursion, suspend the event temporary to avoid this - nodefield.suspendEvent('change'); - nodefield.setValue(nodes); - nodefield.resumeEvent('change'); - }; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'group', - value: me.groupId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }, - nodefield - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'restricted', - uncheckedValue: 0, - fieldLabel: 'restricted' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nofailback', - uncheckedValue: 0, - fieldLabel: 'nofailback' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - nodegrid - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.GroupEdit', { - extend: 'Proxmox.window.Edit', - - groupId: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupId; - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/groups'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/groups/' + me.groupId; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.GroupInputPanel', { - isCreate: me.isCreate, - groupId: me.groupId - }); - - Ext.apply(me, { - subject: gettext('HA Group'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.GroupsView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAGroupsView'], - - onlineHelp: 'ha_manager_groups', - - stateful: true, - stateId: 'grid-ha-groups', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.ha.GroupEdit',{ - groupId: rec.data.group - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/groups/', - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Create'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.GroupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - columns: [ - { - header: gettext('Group'), - width: 150, - sortable: true, - dataIndex: 'group' - }, - { - header: 'restricted', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'restricted' - }, - { - header: 'nofailback', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'nofailback' - }, - { - header: gettext('Nodes'), - flex: 1, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - activate: reload, - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.ha.FencingView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveFencingView'], - - onlineHelp: 'ha_manager_fencing', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-ha-fencing', - data: [] - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false, - deferEmptyText: false, - emptyText: 'Use watchdog based fencing.' - }, - columns: [ - { - header: 'Node', - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Command'), - flex: 1, - dataIndex: 'command' - } - ] - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-ha-fencing', { - extend: 'Ext.data.Model', - fields: [ - 'node', 'command', 'digest' - ] - }); - -}); -Ext.define('PVE.dc.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSummary', - - scrollable: true, - - bodyPadding: 5, - - layout: 'column', - - defaults: { - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - - items: [ - { - itemId: 'dcHealth', - xtype: 'pveDcHealth' - }, - { - itemId: 'dcGuests', - xtype: 'pveDcGuests' - }, - { - title: gettext('Resources'), - xtype: 'panel', - minHeight: 250, - bodyPadding: 5, - layout: 'hbox', - defaults: { - xtype: 'proxmoxGauge', - flex: 1 - }, - items:[ - { - title: gettext('CPU'), - itemId: 'cpu' - }, - { - title: gettext('Memory'), - itemId: 'memory' - }, - { - title: gettext('Storage'), - itemId: 'storage' - } - ] - }, - { - itemId: 'nodeview', - xtype: 'pveDcNodeView', - height: 250 - }, - { - title: gettext('Subscriptions'), - height: 220, - items: [ - { - itemId: 'subscriptions', - xtype: 'pveHealthWidget', - userCls: 'pointer', - listeners: { - element: 'el', - click: function() { - if (this.component.userCls === 'pointer') { - window.open('https://www.proxmox.com/en/proxmox-ve/pricing', '_blank'); - } - } - } - } - ] - } - ], - - initComponent: function() { - var me = this; - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-status', - model: 'pve-dc-nodes', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/status" - } - }); - - var gridstore = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - filters: { - property: 'type', - value: 'node' - }, - sorters: { - property: 'id', - direction: 'ASC' - } - }); - - me.callParent(); - - me.getComponent('nodeview').setStore(gridstore); - - var gueststatus = me.getComponent('dcGuests'); - - var cpustat = me.down('#cpu'); - var memorystat = me.down('#memory'); - var storagestat = me.down('#storage'); - var sp = Ext.state.Manager.getProvider(); - - me.mon(PVE.data.ResourceStore, 'load', function(curstore, results) { - me.suspendLayout = true; - - var cpu = 0; - var maxcpu = 0; - - var nodes = 0; - - var memory = 0; - var maxmem = 0; - - var countedStorages = {}; - var used = 0; - var total = 0; - var usableStorages = {}; - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - if (storage !== '') { - usableStorages[storage] = true; - } - }); - - var qemu = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var lxc = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var error = 0; - - var i; - - for (i = 0; i < results.length; i++) { - var item = results[i]; - switch(item.data.type) { - case 'node': - cpu += (item.data.cpu * item.data.maxcpu); - maxcpu += item.data.maxcpu || 0; - memory += item.data.mem || 0; - maxmem += item.data.maxmem || 0; - nodes++; - - // update grid also - var griditem = gridstore.getById(item.data.id); - if (griditem) { - griditem.set('cpuusage', item.data.cpu); - var max = item.data.maxmem || 1; - var val = item.data.mem || 0; - griditem.set('memoryusage', val/max); - griditem.set('uptime', item.data.uptime); - griditem.commit(); //else it marks the fields as dirty - } - break; - case 'storage': - if (!Ext.Object.isEmpty(usableStorages)) { - if (usableStorages[item.data.id] === true) { - used += item.data.disk; - total += item.data.maxdisk; - } - break; - } - if (!countedStorages[item.data.storage] || - (item.data.storage === 'local' && - !countedStorages[item.data.id])) { - used += item.data.disk; - total += item.data.maxdisk; - - countedStorages[item.data.storage === 'local'?item.data.id:item.data.storage] = true; - } - break; - case 'qemu': - qemu[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - case 'lxc': - lxc[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - default: break; - } - } - - var text = Ext.String.format(gettext('of {0} CPU(s)'), maxcpu); - cpustat.updateValue((cpu/maxcpu), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(memory), PVE.Utils.render_size(maxmem)); - memorystat.updateValue((memory/maxmem), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(used), PVE.Utils.render_size(total)); - storagestat.updateValue((used/total), text); - - gueststatus.updateValues(qemu,lxc,error); - - me.suspendLayout = false; - me.updateLayout(true); - }); - - var dcHealth = me.getComponent('dcHealth'); - me.mon(rstore, 'load', dcHealth.updateStatus, dcHealth); - - var subs = me.down('#subscriptions'); - me.mon(rstore, 'load', function(store, records, success) { - var i; - var level; - var mixed = false; - for (i = 0; i < records.length; i++) { - if (records[i].get('type') !== 'node') { - continue; - } - var node = records[i]; - if (node.get('status') === 'offline') { - continue; - } - - var curlevel = node.get('level'); - - if (curlevel === '') { // no subscription trumps all, set and break - level = ''; - break; - } - - if (level === undefined) { // save level - level = curlevel; - } else if (level !== curlevel) { // detect different levels - mixed = true; - } - } - - var data = { - title: Proxmox.Utils.unknownText, - text: Proxmox.Utils.unknownText, - iconCls: PVE.Utils.get_health_icon(undefined, true) - }; - if (level === '') { - data = { - title: gettext('No Subscription'), - iconCls: PVE.Utils.get_health_icon('critical', true), - text: gettext('You have at least one node without subscription.') - }; - subs.setUserCls('pointer'); - } else if (mixed) { - data = { - title: gettext('Mixed Subscriptions'), - iconCls: PVE.Utils.get_health_icon('warning', true), - text: gettext('Warning: Your subscription levels are not the same.') - }; - subs.setUserCls('pointer'); - } else if (level) { - data = { - title: PVE.Utils.render_support_level(level), - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext('Your subscription status is valid.') - }; - subs.setUserCls(''); - } - - subs.setData(data); - }); - - me.on('destroy', function(){ - rstore.stopUpdate(); - }); - - rstore.startUpdate(); - } - -}); -Ext.define('PVE.window.ReplicaEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveReplicaEdit', - - subject: gettext('Replication Job'), - - - url: '/cluster/replication', - method: 'POST', - - initComponent: function() { - var me = this; - - var vmid = me.pveSelNode.data.vmid; - var nodename = me.pveSelNode.data.node; - - var items = []; - - items.push({ - xtype: (me.isCreate && !vmid)?'pveGuestIDSelector':'displayfield', - name: 'guest', - fieldLabel: 'CT/VM ID', - value: vmid || '' - }); - - items.push( - { - xtype: me.isCreate ? 'pveNodeSelector':'displayfield', - name: 'target', - disallowedNodes: [nodename], - allowBlank: false, - onlineValidator: true, - fieldLabel: gettext("Target") - }, - { - xtype: 'pveCalendarEvent', - fieldLabel: gettext('Schedule'), - emptyText: '*/15 - ' + Ext.String.format(gettext('Every {0} minutes'), 15), - name: 'schedule' - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - step: 1, - minValue: 1, - emptyText: gettext('unlimited'), - name: 'rate' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment' - }, - { - xtype: 'proxmoxcheckbox', - name: 'enabled', - defaultValue: 'on', - checked: true, - fieldLabel: gettext('Enabled') - } - ); - - me.items = [ - { - xtype: 'inputpanel', - itemId: 'ipanel', - onlineHelp: 'pvesr_schedule_time_format', - - onGetValues: function(values) { - var me = this.up('window'); - - values.disable = values.enabled ? 0 : 1; - delete values.enabled; - - PVE.Utils.delete_if_default(values, 'rate', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'disable', 0, me.isCreate); - PVE.Utils.delete_if_default(values, 'schedule', '*/15', me.isCreate); - PVE.Utils.delete_if_default(values, 'comment', '', me.isCreate); - - if (me.isCreate) { - values.type = 'local'; - var vm = vmid || values.guest; - var id = -1; - if (me.highestids[vm] !== undefined) { - id = me.highestids[vm]; - } - id++; - values.id = vm + '-' + id.toString(); - delete values.guest; - } - return values; - }, - items: items - } - ]; - - me.callParent(); - - if (me.isCreate) { - me.load({ - success: function(response) { - var jobs = response.result.data; - var highestids = {}; - Ext.Array.forEach(jobs, function(job) { - var match = /^([0-9]+)\-([0-9]+)$/.exec(job.id); - if (match) { - var vmid = parseInt(match[1],10); - var id = parseInt(match[2],10); - if (highestids[vmid] < id || - highestids[vmid] === undefined) { - highestids[vmid] = id; - } - } - }); - - me.highestids = highestids; - } - }); - - } else { - me.load({ - success: function(response, options) { - response.result.data.enabled = !response.result.data.disable; - me.setValues(response.result.data); - me.digest = response.result.data.digest; - } - }); - } - } -}); - -/*jslint confusion: true */ -/* callback is a function and string */ -Ext.define('PVE.grid.ReplicaView', { - extend: 'Ext.grid.Panel', - xtype: 'pveReplicaView', - - onlineHelp: 'chapter_pvesr', - - stateful: true, - stateId: 'grid-pve-replication-status', - - controller: { - xclass: 'Ext.app.ViewController', - - addJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var win = Ext.create('PVE.window.ReplicaEdit', { - isCreate: true, - method: 'POST', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - editJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var data = rec.data; - var win = Ext.create('PVE.window.ReplicaEdit', { - url: '/cluster/replication/' + data.id, - method: 'PUT', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - scheduleJobNow: function(button,event,rec) { - var me = this.getView(); - var controller = this; - - Proxmox.Utils.API2Request({ - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/schedule_now", - method: 'POST', - waitMsgTarget: me, - callback: function() { controller.reload(); }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - showLog: function(button, event, rec) { - var me = this.getView(); - var controller = this; - var logView = Ext.create('Proxmox.panel.LogView', { - border: false, - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/log" - }); - var win = Ext.create('Ext.window.Window', { - items: [ logView ], - layout: 'fit', - width: 800, - height: 400, - modal: true, - title: gettext("Replication Log") - }); - var task = { - run: function() { - logView.requestUpdate(); - }, - interval: 1000 - }; - Ext.TaskManager.start(task); - win.on('destroy', function() { - Ext.TaskManager.stop(task); - controller.reload(); - }); - win.show(); - }, - - reload: function() { - var me = this.getView(); - me.rstore.load(); - }, - - dblClick: function(grid, record, item) { - var me = this; - me.editJob(undefined, undefined, record); - }, - - // check for cluster - // currently replication is for cluster only, so we disable the whole - // component - checkPrerequisites: function() { - var me = this.getView(); - if (PVE.data.ResourceStore.getNodes().length < 2) { - me.mask(gettext("Replication needs at least two nodes"), ['pve-static-mask']); - } - }, - - control: { - '#': { - itemdblclick: 'dblClick', - afterlayout: 'checkPrerequisites' - } - } - }, - - tbar: [ - { - text: gettext('Add'), - itemId: 'addButton', - handler: 'addJob' - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - itemId: 'editButton', - handler: 'editJob', - disabled: true - }, - { - xtype: 'proxmoxStdRemoveButton', - itemId: 'removeButton', - baseurl: '/api2/extjs/cluster/replication/', - dangerous: true, - callback: 'reload' - }, - { - xtype: 'proxmoxButton', - text: gettext('Log'), - itemId: 'logButton', - handler: 'showLog', - disabled: true - }, - { - xtype: 'proxmoxButton', - text: gettext('Schedule now'), - itemId: 'scheduleNowButton', - handler: 'scheduleJobNow', - disabled: true - } - ], - - initComponent: function() { - var me = this; - var mode = ''; - var url = '/cluster/replication'; - - me.nodename = me.pveSelNode.data.node; - me.vmid = me.pveSelNode.data.vmid; - - me.columns = [ - { - text: gettext('Enabled'), - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true - }, - { - text: 'ID', - dataIndex: 'id', - width: 60, - hidden: true - }, - { - text: gettext('Guest'), - dataIndex: 'guest', - width: 75 - }, - { - text: gettext('Job'), - dataIndex: 'jobnum', - width: 60 - }, - { - text: gettext('Target'), - dataIndex: 'target' - } - ]; - - if (!me.nodename) { - mode = 'dc'; - me.stateId = 'grid-pve-replication-dc'; - } else if (!me.vmid) { - mode = 'node'; - url = '/nodes/' + me.nodename + '/replication'; - } else { - mode = 'vm'; - url = '/nodes/' + me.nodename + '/replication' + '?guest=' + me.vmid; - } - - if (mode !== 'dc') { - me.columns.push( - { - text: gettext('Status'), - dataIndex: 'state', - minWidth: 160, - flex: 1, - renderer: function(value, metadata, record) { - - if (record.data.pid) { - metadata.tdCls = 'x-grid-row-loading'; - return ''; - } - - var icons = []; - var states = []; - - if (record.data.remove_job) { - icons.push(''); - states.push(gettext("Removal Scheduled")); - } - - if (record.data.error) { - icons.push(''); - states.push(record.data.error); - } - - if (icons.length == 0) { - icons.push(''); - states.push(gettext('OK')); - } - - return icons.join(',') + ' ' + states.join(','); - } - }, - { - text: gettext('Last Sync'), - dataIndex: 'last_sync', - width: 150, - renderer: function(value, metadata, record) { - if (!value) { - return '-'; - } - - if (record.data.pid) { - return gettext('syncing'); - } - - return Proxmox.Utils.render_timestamp(value); - } - }, - { - text: gettext('Duration'), - dataIndex: 'duration', - width: 60, - renderer: PVE.Utils.render_duration - }, - { - text: gettext('Next Sync'), - dataIndex: 'next_sync', - width: 150, - renderer: function(value) { - if (!value) { - return '-'; - } - - var now = new Date(); - var next = new Date(value*1000); - - if (next < now) { - return gettext('pending'); - } - - return Proxmox.Utils.render_timestamp(value); - } - } - ); - } - - me.columns.push( - { - text: gettext('Schedule'), - width: 75, - dataIndex: 'schedule' - }, - { - text: gettext('Rate limit'), - dataIndex: 'rate', - renderer: function(value) { - if (!value) { - return gettext('unlimited'); - } - - return value.toString() + ' MB/s'; - }, - hidden: true - }, - { - text: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.htmlEncode - } - ); - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-replica-' + me.nodename + me.vmid, - model: (mode === 'dc')? 'pve-replication' : 'pve-replication-state', - interval: 3000, - proxy: { - type: 'proxmox', - url: "/api2/json" + url - } - }); - - me.store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sorters: [ - { - property: 'guest' - }, - { - property: 'jobnum' - } - ] - }); - - me.callParent(); - - // we cannot access the log and scheduleNow button - // in the datacenter, because - // we do not know where/if the jobs runs - if (mode === 'dc') { - me.down('#logButton').setHidden(true); - me.down('#scheduleNowButton').setHidden(true); - } - - // if we set the warning mask, we do not want to load - // or set the mask on store errors - if (PVE.data.ResourceStore.getNodes().length < 2) { - return; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.on('destroy', me.rstore.stopUpdate); - me.rstore.startUpdate(); - } -}, function() { - - Ext.define('pve-replication', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'target', 'comment', 'rate', 'type', - { name: 'guest', type: 'integer' }, - { name: 'jobnum', type: 'integer' }, - { name: 'schedule', defaultValue: '*/15' }, - { name: 'disable', defaultValue: '' }, - { name: 'enabled', calculate: function(data) { return !data.disable; } } - ] - }); - - Ext.define('pve-replication-state', { - extend: 'pve-replication', - fields: [ - 'last_sync', 'next_sync', 'error', 'duration', 'state', - 'fail_count', 'remove_job', 'pid' - ] - }); - -}); -Ext.define('PVE.dc.Health', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcHealth', - - title: gettext('Health'), - - bodyPadding: 10, - height: 220, - layout: { - type: 'hbox', - align: 'stretch' - }, - - defaults: { - flex: 1, - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - nodeList: [], - nodeIndex: 0, - - updateStatus: function(store, records, success) { - var me = this; - if (!success) { - return; - } - - var cluster = { - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext("Standalone node - no cluster defined") - }; - - var nodes = { - online: 0, - offline: 0 - }; - - // by default we have one node - var numNodes = 1; - var i; - - for (i = 0; i < records.length; i++) { - var item = records[i]; - if (item.data.type === 'node') { - nodes[item.data.online === 1 ? 'online':'offline']++; - } else if(item.data.type === 'cluster') { - cluster.text = gettext("Cluster") + ": "; - cluster.text += item.data.name + ", "; - cluster.text += gettext("Quorate") + ": "; - cluster.text += Proxmox.Utils.format_boolean(item.data.quorate); - if (item.data.quorate != 1) { - cluster.iconCls = PVE.Utils.get_health_icon('critical', true); - } - - numNodes = item.data.nodes; - } - } - - if (numNodes !== (nodes.online + nodes.offline)) { - nodes.offline = numNodes - nodes.online; - } - - me.getComponent('clusterstatus').updateHealth(cluster); - me.getComponent('nodestatus').update(nodes); - }, - - updateCeph: function(store, records, success) { - var me = this; - var cephstatus = me.getComponent('ceph'); - if (!success || records.length < 1) { - - // if ceph status is already visible - // don't stop to update - if (cephstatus.isVisible()) { - return; - } - - // try all nodes until we either get a successful api call, - // or we tried all nodes - if (++me.nodeIndex >= me.nodeList.length) { - me.cephstore.stopUpdate(); - } else { - store.getProxy().setUrl('/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status'); - } - - return; - } - - var state = PVE.Utils.render_ceph_health(records[0].data.health || {}); - cephstatus.updateHealth(state); - cephstatus.setVisible(true); - }, - - listeners: { - destroy: function() { - var me = this; - me.cephstore.stopUpdate(); - } - }, - - items: [ - { - itemId: 'clusterstatus', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - itemId: 'nodestatus', - data: { - online: 0, - offline: 0 - }, - tpl: [ - '

' + gettext('Nodes') + '


', - '
', - '
', - ' ', - gettext('Online'), - '
', - '
{online}
', - '

', - '
', - ' ', - gettext('Offline'), - '
', - '
{offline}
', - '
' - ] - }, - { - itemId: 'ceph', - width: 250, - columnWidth: undefined, - userCls: 'pointer', - title: 'Ceph', - xtype: 'pveHealthWidget', - hidden: true, - listeners: { - element: 'el', - click: function() { - var sp = Ext.state.Manager.getProvider(); - sp.set('dctab', {value:'ceph'}, true); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.nodeList = PVE.data.ResourceStore.getNodes(); - me.nodeIndex = 0; - me.cephstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-ceph', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status' - } - }); - me.callParent(); - me.mon(me.cephstore, 'load', me.updateCeph, me); - me.cephstore.startUpdate(); - } -}); -Ext.define('PVE.dc.Guests', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcGuests', - - - title: gettext('Guests'), - height: 220, - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - bodyPadding: '0 20 20 20', - - defaults: { - xtype: 'box', - padding: '0 50 0 50', - style: { - 'text-align':'center', - 'line-height':'1.2' - } - }, - items: [{ - itemId: 'qemu', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("Virtual Machines") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'lxc', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("LXC Container") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'error', - colspan: 2, - data: { - num: 0 - }, - columnWidth: 1, - padding: '10 250 0 250', - tpl: [ - '', - '
', - ' ', - gettext('Error'), - '
', - '
{num}
', - '
' - ] - }], - - updateValues: function(qemu, lxc, error) { - var me = this; - me.getComponent('qemu').update(qemu); - me.getComponent('lxc').update(lxc); - me.getComponent('error').update({num: error}); - } -}); - /*jslint confusion: true*/ -Ext.define('PVE.dc.OptionView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveDcOptionView'], - - onlineHelp: 'datacenter_configuration_file', - - monStoreErrors: true, - - add_inputpanel_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - var canEdit = (opts.caps === undefined || opts.caps); - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: canEdit ? { - xtype: 'proxmoxWindowEdit', - width: 350, - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - setValues: function(values) { - // FIXME: run through parsePropertyString if not an object? - var edit_value = values[name]; - Ext.Array.each(this.query('inputpanel'), function(panel) { - panel.setValues(edit_value); - }); - }, - url: opts.url, - items: [{ - xtype: 'inputpanel', - onGetValues: function(values) { - if (values === undefined || Object.keys(values).length === 0) { - return { 'delete': name }; - } - var ret_val = {}; - ret_val[name] = PVE.Parser.printPropertyString(values); - return ret_val; - }, - items: opts.items - }] - } : undefined - }; - }, - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.add_combobox_row('keyboard', gettext('Keyboard Layout'), { - renderer: PVE.Utils.render_kvm_language, - comboItems: PVE.Utils.kvm_keymap_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('http_proxy', gettext('HTTP proxy'), { - defaultValue: Proxmox.Utils.noneText, - vtype: 'HttpProxy', - deleteEmpty: true - }); - me.add_combobox_row('console', gettext('Console Viewer'), { - renderer: PVE.Utils.render_console_viewer, - comboItems: PVE.Utils.console_viewer_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('email_from', gettext('Email from address'), { - deleteEmpty: true, - vtype: 'proxmoxMail', - defaultValue: 'root@$hostname' - }); - me.add_text_row('mac_prefix', gettext('MAC address prefix'), { - deleteEmpty: true, - vtype: 'MacPrefix', - defaultValue: Proxmox.Utils.noneText - }); - me.add_inputpanel_row('migration', gettext('Migration Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.vms['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - defaultKey: 'type', - items: [{ - xtype: 'displayfield', - name: 'type', - fieldLabel: gettext('Type'), - value: 'secure', - submitValue: true, - }, { - xtype: 'proxmoxNetworkSelector', - name: 'network', - fieldLabel: gettext('Network'), - value: null, - emptyText: Proxmox.Utils.defaultText, - autoSelect: false, - skipEmptyText: true - }] - }); - me.add_inputpanel_row('ha', gettext('HA Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.vms['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'shutdown_policy', - fieldLabel: gettext('Shutdown Policy'), - deleteEmpty: false, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + ' (conditional)' ], - ['freeze', 'freeze'], - ['failover', 'failover'], - ['conditional', 'conditional'] - ], - defaultValue: '__default__' - }] - }); - - // TODO: bwlimits, u2f? - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - Ext.apply(me, { - tbar: [{ - text: gettext('Edit'), - xtype: 'proxmoxButton', - disabled: true, - handler: function() { me.run_editor(); }, - selModel: me.selModel - }], - url: "/api2/json/cluster/options", - editorConfig: { - url: "/api2/extjs/cluster/options" - }, - interval: 5000, - cwidth1: 200, - listeners: { - itemdblclick: me.run_editor - } - }); - - me.callParent(); - - // set the new value for the default console - me.mon(me.rstore, 'load', function(store, records, success) { - if (!success) { - return; - } - - var rec = store.getById('console'); - PVE.VersionInfo.console = rec.data.value; - if (rec.data.value === '__default__') { - delete PVE.VersionInfo.console; - } - }); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); -Ext.define('PVE.dc.StorageView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveStorageView'], - - onlineHelp: 'chapter_storage', - - stateful: true, - stateId: 'grid-dc-storage', - - createStorageEditWindow: function(type, sid) { - var schema = PVE.Utils.storageSchema[type]; - if (!schema || !schema.ipanel) { - throw "no editor registered for storage type: " + type; - } - - Ext.create('PVE.storage.BaseEdit', { - paneltype: 'PVE.storage.' + schema.ipanel, - type: type, - storageId: sid, - autoShow: true, - listeners: { - destroy: this.reloadStore - } - }); - }, - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-storage', - proxy: { - type: 'proxmox', - url: "/api2/json/storage" - }, - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type, - sid = rec.data.storage; - - me.createStorageEditWindow(type, sid); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/storage/', - callback: reload - }); - - // else we cannot dynamically generate the add menu handlers - var addHandleGenerator = function(type) { - return function() { me.createStorageEditWindow(type); }; - }; - var addMenuItems = [], type; - /*jslint forin: true */ - for (type in PVE.Utils.storageSchema) { - var storage = PVE.Utils.storageSchema[type]; - if (storage.hideAdd) { - continue; - } - addMenuItems.push({ - text: PVE.Utils.format_storage_type(type), - iconCls: 'fa fa-fw fa-' + storage.faIcon, - handler: addHandleGenerator(type) - }); - } - - Ext.apply(me, { - store: store, - reloadStore: reload, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: addMenuItems - }) - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - flex: 2, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Type'), - flex: 1, - sortable: true, - dataIndex: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - header: gettext('Content'), - flex: 3, - sortable: true, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Path') + '/' + gettext('Target'), - flex: 2, - sortable: true, - dataIndex: 'path', - renderer: function(value, metaData, record) { - if (record.data.target) { - return record.data.target; - } - return value; - } - }, - { - header: gettext('Shared'), - flex: 1, - sortable: true, - dataIndex: 'shared', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Enabled'), - flex: 1, - sortable: true, - dataIndex: 'disable', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - header: gettext('Bandwidth Limit'), - flex: 2, - sortable: true, - dataIndex: 'bwlimit' - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-storage', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'content', 'server', 'portal', 'target', 'export', 'storage', - { name: 'shared', type: 'boolean'}, - { name: 'disable', type: 'boolean'} - ], - idProperty: 'storage' - }); - -}); -/*global u2f,QRCode,Uint8Array*/ -/*jslint confusion: true*/ -Ext.define('PVE.window.TFAEdit', { - extend: 'Ext.window.Window', - mixins: ['Proxmox.Mixin.CBind'], - - onlineHelp: 'pveum_tfa_auth', // fake to ensure this gets a link target - - modal: true, - resizable: false, - title: gettext('Two Factor Authentication'), - subject: 'TFA', - url: '/api2/extjs/access/tfa', - width: 512, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - updateQrCode: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var algorithm = values.algorithm; - if (!algorithm) { - algorithm = 'SHA1'; - } - - me.qrcode.makeCode( - 'otpauth://totp/' + encodeURIComponent(me.userid) + - '?secret=' + values.secret + - '&period=' + values.step + - '&digits=' + values.digits + - '&algorithm=' + algorithm + - '&issuer=' + encodeURIComponent(values.issuer) - ); - - me.lookup('challenge').setVisible(true); - me.down('#qrbox').setVisible(true); - }, - - showError: function(error) { - Ext.Msg.alert( - gettext('Error'), - PVE.Utils.render_u2f_error(error) - ); - }, - - doU2FChallenge: function(response) { - var me = this; - - var data = response.result.data; - me.lookup('password').setDisabled(true); - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Setup'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - Ext.Function.defer(function() { - u2f.register(data.appId, [data], [], function(data) { - msg.close(); - if (data.errorCode) { - me.showError(data.errorCode); - } else { - me.respondToU2FChallenge(data); - } - }); - }, 500, me); - }, - - respondToU2FChallenge: function(data) { - var me = this; - var params = { - userid: me.userid, - action: 'confirm', - response: JSON.stringify(data) - }; - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - success: function() { - me.close(); - Ext.Msg.show({ - title: gettext('Success'), - message: gettext('U2F Device successfully connected.'), - buttons: Ext.Msg.OK - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - viewModel: { - data: { - in_totp_tab: true, - tfa_required: false, - tfa_type: null, // dependencies of formulas should not be undefined - valid: false, - u2f_available: true - }, - formulas: { - canDeleteTFA: function(get) { - return (get('tfa_type') !== null && !get('tfa_required')); - }, - canSetupTOTP: function(get) { - var tfa = get('tfa_type'); - return (tfa === null || tfa === 'totp' || tfa === 1); - }, - canSetupU2F: function(get) { - var tfa = get('tfa_type'); - return (get('u2f_available') && (tfa === null || tfa === 'u2f' || tfa === 1)); - } - } - }, - - afterLoading: function(realm_tfa_type, user_tfa_type) { - var me = this; - var viewmodel = me.getViewModel(); - if (user_tfa_type === 'oath') { - user_tfa_type = 'totp'; - } - viewmodel.set('tfa_type', user_tfa_type || null); - if (!realm_tfa_type) { - // There's no TFA enforced by the realm, everything works. - viewmodel.set('u2f_available', true); - viewmodel.set('tfa_required', false); - } else if (realm_tfa_type === 'oath') { - // The realm explicitly requires TOTP - if (user_tfa_type !== 'totp' && user_tfa_type !== null) { - // user had a different tfa method, so - // we have to change back to the totp tab and - // generate a secret - viewmodel.set('tfa_type', null); - me.lookup('tfatabs').setActiveTab(me.lookup('totp_panel')); - me.getController().randomizeSecret(); - } - viewmodel.set('tfa_required', true); - viewmodel.set('u2f_available', false); - } else { - // The realm enforces some other TFA type (yubico) - me.close(); - Ext.Msg.alert( - gettext('Error'), - Ext.String.format( - gettext("Custom 2nd factor configuration is not supported on realms with '{0}' TFA."), - realm_tfa_type - ) - ); - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[qrupdate=true]': { - change: function() { - var me = this.getView(); - me.updateQrCode(); - } - }, - 'field': { - validitychange: function(field, valid) { - var me = this; - var viewModel = me.getViewModel(); - var form = me.lookup('totp_form'); - var challenge = me.lookup('challenge'); - var password = me.lookup('password'); - viewModel.set('valid', form.isValid() && challenge.isValid() && password.isValid()); - } - }, - '#': { - show: function() { - var me = this.getView(); - var viewmodel = this.getViewModel(); - - var loadMaskContainer = me.down('#tfatabs'); - Proxmox.Utils.API2Request({ - url: '/access/users/' + encodeURIComponent(me.userid) + '/tfa', - waitMsgTarget: loadMaskContainer, - method: 'GET', - success: function(response, opts) { - var data = response.result.data; - me.afterLoading(data.realm, data.user); - }, - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(loadMaskContainer, response.htmlStatus); - } - }); - - me.qrdiv = document.createElement('center'); - me.qrcode = new QRCode(me.qrdiv, { - width: 256, - height: 256, - correctLevel: QRCode.CorrectLevel.M - }); - me.down('#qrbox').getEl().appendChild(me.qrdiv); - - viewmodel.set('tfa_type', me.tfa_type || null); - if (!me.tfa_type) { - this.randomizeSecret(); - } else { - me.down('#qrbox').setVisible(false); - me.lookup('challenge').setVisible(false); - if (me.tfa_type === 'u2f') { - var u2f_panel = me.lookup('u2f_panel'); - me.lookup('tfatabs').setActiveTab(u2f_panel); - } - } - - if (Proxmox.UserName === 'root@pam') { - me.lookup('password').setVisible(false); - me.lookup('password').setDisabled(true); - } - } - }, - '#tfatabs': { - tabchange: function(panel, newcard) { - var viewmodel = this.getViewModel(); - viewmodel.set('in_totp_tab', newcard.itemId === 'totp-panel'); - } - } - }, - - applySettings: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'new', - key: values.secret, - config: PVE.Parser.printPropertyString({ - type: 'oath', - digits: values.digits, - step: values.step - }), - // this is used to verify that the client generates the correct codes: - response: me.lookup('challenge').value - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - deleteTFA: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'delete' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - randomizeSecret: function() { - var me = this; - var rnd = new Uint8Array(16); - window.crypto.getRandomValues(rnd); - var data = ''; - rnd.forEach(function(b) { - // secret must be base32, so just use the first 5 bits - b = b & 0x1f; - if (b < 26) { - // A..Z - data += String.fromCharCode(b + 0x41); - } else { - // 2..7 - data += String.fromCharCode(b-26 + 0x32); - } - }); - me.lookup('tfa_secret').setValue(data); - }, - - startU2FRegistration: function() { - var me = this; - - var params = { - userid: me.getView().userid, - action: 'new' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response) { - me.getView().doU2FChallenge(response); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - - items: [ - { - xtype: 'tabpanel', - itemId: 'tfatabs', - reference: 'tfatabs', - border: false, - items: [ - { - xtype: 'panel', - title: 'TOTP', - itemId: 'totp-panel', - reference: 'totp_panel', - tfa_type: 'totp', - border: false, - bind: { - disabled: '{!canSetupTOTP}' - }, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'form', - layout: 'anchor', - border: false, - reference: 'totp_form', - fieldDefaults: { - anchor: '100%', - padding: '0 5' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('User name'), - cbind: { - value: '{userid}' - } - }, - { - layout: 'hbox', - border: false, - padding: '0 0 5 0', - items: [{ - xtype: 'textfield', - fieldLabel: gettext('Secret'), - emptyText: gettext('Unchanged'), - name: 'secret', - reference: 'tfa_secret', - regex: /^[A-Z2-7=]+$/, - regexText: 'Must be base32 [A-Z2-7=]', - maskRe: /[A-Z2-7=]/, - qrupdate: true, - flex: 4 - }, - { - xtype: 'button', - text: gettext('Randomize'), - reference: 'randomize_button', - handler: 'randomizeSecret', - flex: 1 - }] - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Time period'), - name: 'step', - // Google Authenticator ignores this and generates bogus data - hidden: true, - value: 30, - minValue: 10, - qrupdate: true - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Digits'), - name: 'digits', - value: 6, - // Google Authenticator ignores this and generates bogus data - hidden: true, - minValue: 6, - maxValue: 8, - qrupdate: true - }, - { - xtype: 'textfield', - fieldLabel: gettext('Issuer Name'), - name: 'issuer', - value: 'Proxmox Web UI', - qrupdate: true - } - ] - }, - { - xtype: 'box', - itemId: 'qrbox', - visible: false, // will be enabled when generating a qr code - style: { - 'background-color': '#23272a', - padding: '5px', - width: '266px', - height: '266px' - } - }, - { - xtype: 'textfield', - fieldLabel: gettext('Verification Code'), - allowBlank: false, - reference: 'challenge', - padding: '0 5', - emptyText: gettext('Scan QR code and enter TOTP auth. code to verify') - } - ] - }, - { - title: 'U2F', - itemId: 'u2f-panel', - reference: 'u2f_panel', - tfa_type: 'u2f', - border: false, - padding: '5 5', - layout: { - type: 'vbox', - align: 'middle' - }, - bind: { - disabled: '{!canSetupU2F}' - }, - items: [ - { - xtype: 'label', - width: 500, - text: gettext('To register a U2F device, connect the device, then click the button and follow the instructions.') - } - ] - } - ] - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - reference: 'password', - allowBlank: false, - validateBlank: true, - padding: '0 0 5 5', - emptyText: gettext('verify current password') - } - ], - - buttons: [ - { - xtype: 'proxmoxHelpButton' - }, - '->', - { - text: gettext('Apply'), - handler: 'applySettings', - bind: { - hidden: '{!in_totp_tab}', - disabled: '{!valid}' - } - }, - { - xtype: 'button', - text: gettext('Register U2F Device'), - handler: 'startU2FRegistration', - bind: { - hidden: '{in_totp_tab}', - disabled: '{tfa_type}' - } - }, - { - text: gettext('Delete'), - reference: 'delete_button', - disabled: true, - handler: 'deleteTFA', - bind: { - disabled: '{!canDeleteTFA}' - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.userid) { - throw "no userid given"; - } - - me.callParent(); - - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', 'pveum_tfa_auth'); - } -}); -Ext.define('PVE.dc.UserEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcUserEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.userid; - - var url; - var method; - var realm; - - if (me.isCreate) { - url = '/api2/extjs/access/users'; - method = 'POST'; - } else { - url = '/api2/extjs/access/users/' + encodeURIComponent(me.userid); - method = 'PUT'; - } - - var verifypw; - var pwfield; - - var validate_pw = function() { - if (verifypw.getValue() !== pwfield.getValue()) { - return gettext("Passwords do not match"); - } - return true; - }; - - verifypw = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - submitValue: false, - disabled: true, - hidden: true, - validator: validate_pw - }); - - pwfield = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - name: 'password', - disabled: true, - hidden: true, - validator: validate_pw - }); - - var update_passwd_field = function(realm) { - if (realm === 'pve') { - pwfield.setVisible(true); - pwfield.setDisabled(false); - verifypw.setVisible(true); - verifypw.setDisabled(false); - } else { - pwfield.setVisible(false); - pwfield.setDisabled(true); - verifypw.setVisible(false); - verifypw.setDisabled(true); - } - - }; - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'userid', - fieldLabel: gettext('User name'), - value: me.userid, - allowBlank: false, - submitValue: me.isCreate ? true : false - }, - pwfield, verifypw, - { - xtype: 'pveGroupSelector', - name: 'groups', - multiSelect: true, - allowBlank: true, - fieldLabel: gettext('Group') - }, - { - xtype: 'datefield', - name: 'expire', - emptyText: 'never', - format: 'Y-m-d', - submitFormat: 'U', - fieldLabel: gettext('Expire') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enabled'), - name: 'enable', - uncheckedValue: 0, - defaultValue: 1, - checked: true - } - ]; - - var column2 = [ - { - xtype: 'textfield', - name: 'firstname', - fieldLabel: gettext('First Name') - }, - { - xtype: 'textfield', - name: 'lastname', - fieldLabel: gettext('Last Name') - }, - { - xtype: 'textfield', - name: 'email', - fieldLabel: gettext('E-Mail'), - vtype: 'proxmoxMail' - } - ]; - - if (me.isCreate) { - column1.splice(1,0,{ - xtype: 'pveRealmComboBox', - name: 'realm', - fieldLabel: gettext('Realm'), - allowBlank: false, - matchFieldWidth: false, - listConfig: { width: 300 }, - listeners: { - change: function(combo, newValue){ - realm = newValue; - update_passwd_field(realm); - } - }, - submitValue: false - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ], - advancedItems: [ - { - xtype: 'textfield', - name: 'keys', - fieldLabel: gettext('Key IDs') - } - ], - onGetValues: function(values) { - // hack: ExtJS datefield does not submit 0, so we need to set that - if (!values.expire) { - values.expire = 0; - } - - if (realm) { - values.userid = values.userid + '@' + realm; - } - - if (!values.password) { - delete values.password; - } - - return values; - } - }); - - Ext.applyIf(me, { - subject: gettext('User'), - url: url, - method: method, - fieldDefaults: { - labelWidth: 110 // for spanish translation - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (Ext.isDefined(data.expire)) { - if (data.expire) { - data.expire = new Date(data.expire * 1000); - } else { - // display 'never' instead of '1970-01-01' - data.expire = null; - } - } - me.setValues(data); - } - }); - } - } -}); -/*jslint confusion: true */ -Ext.define('PVE.dc.UserView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveUserView'], - - onlineHelp: 'pveum_users', - - stateful: true, - stateId: 'grid-users', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - id: "users", - model: 'pve-users', - sorters: { - property: 'userid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/access/users/', - enableFn: function(rec) { - if (!caps.access['User.Modify']) { - return false; - } - return rec.data.userid !== 'root@pam'; - }, - callback: function() { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec || !caps.access['User.Modify']) { - return; - } - - var win = Ext.create('PVE.dc.UserEdit',{ - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - enableFn: function(rec) { - return !!caps.access['User.Modify']; - }, - selModel: sm, - handler: run_editor - }); - - var pwchange_btn = new Proxmox.button.Button({ - text: gettext('Password'), - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var win = Ext.create('Proxmox.window.PasswordEdit', { - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var tfachange_btn = new Proxmox.button.Button({ - text: 'TFA', - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var d = rec.data; - var tfa_type = PVE.Parser.parseTfaType(d.keys); - var win = Ext.create('PVE.window.TFAEdit',{ - tfa_type: tfa_type, - userid: d.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - disabled: !caps.access['User.Modify'], - handler: function() { - var win = Ext.create('PVE.dc.UserEdit',{ - }); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn, pwchange_btn, tfachange_btn - ]; - - var render_username = function(userid) { - return userid.match(/^(.+)(@[^@]+)$/)[1]; - }; - - var render_realm = function(userid) { - return userid.match(/@([^@]+)$/)[1]; - }; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('User name'), - width: 200, - sortable: true, - renderer: render_username, - dataIndex: 'userid' - }, - { - header: gettext('Realm'), - width: 100, - sortable: true, - renderer: render_realm, - dataIndex: 'userid' - }, - { - header: gettext('Enabled'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'enable' - }, - { - header: gettext('Expire'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_expire, - dataIndex: 'expire' - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname' - }, - { - header: 'TFA', - width: 50, - sortable: true, - renderer: function(v) { - var tfa_type = PVE.Parser.parseTfaType(v); - if (tfa_type === undefined) { - return Proxmox.Utils.noText; - } else if (tfa_type === 1) { - return Proxmox.Utils.yesText; - } else { - return tfa_type; - } - }, - dataIndex: 'keys' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.PoolView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pvePoolView'], - - onlineHelp: 'pveum_pools', - - stateful: true, - stateId: 'grid-pools', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: { - property: 'poolid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/pools/', - callback: function () { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.PoolEdit',{ - poolid: rec.data.poolid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.PoolEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'poolid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.PoolEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcPoolEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.poolid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/pools'; - method = 'POST'; - } else { - url = '/api2/extjs/pools/' + me.poolid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Pool'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'poolid', - value: me.poolid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.GroupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveGroupView'], - - onlineHelp: 'pveum_groups', - - stateful: true, - stateId: 'grid-groups', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: { - property: 'groupid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/groups/' - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.GroupEdit',{ - groupid: rec.data.groupid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.GroupEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'groupid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.GroupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcGroupEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/groups'; - method = 'POST'; - } else { - url = '/api2/extjs/access/groups/' + me.groupid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Group'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'groupid', - value: me.groupid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.RoleView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveRoleView'], - - onlineHelp: 'pveum_roles', - - stateful: true, - stateId: 'grid-roles', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: { - property: 'roleid', - order: 'DESC' - } - }); - - var render_privs = function(value, metaData) { - - if (!value) { - return '-'; - } - - // allow word wrap - metaData.style = 'white-space:normal;'; - - return value.replace(/\,/g, ' '); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (rec.data.special === "1") { - return; - } - - var win = Ext.create('PVE.dc.RoleEdit',{ - roleid: rec.data.roleid, - privs: rec.data.privs - }); - win.on('destroy', reload); - win.show(); - }; - - Ext.apply(me, { - store: store, - selModel: sm, - - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Built-In'), - width: 65, - sortable: true, - dataIndex: 'special', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - dataIndex: 'roleid' - }, - { - itemid: 'privs', - header: gettext('Privileges'), - sortable: false, - renderer: render_privs, - dataIndex: 'privs', - flex: 1 - } - ], - listeners: { - activate: function() { - store.load(); - }, - itemdblclick: run_editor - }, - tbar: [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.RoleEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor, - enableFn: function(record) { - return record.data.special !== '1'; - } - }, - { - xtype: 'proxmoxStdRemoveButton', - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/roles/', - enableFn: function(record) { - return record.data.special !== '1'; - } - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.RoleEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveDcRoleEdit', - - width: 400, - - initComponent : function() { - var me = this; - - me.isCreate = !me.roleid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/roles'; - method = 'POST'; - } else { - url = '/api2/extjs/access/roles/' + me.roleid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Role'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'roleid', - value: me.roleid, - allowBlank: false, - fieldLabel: gettext('Name') - }, - { - xtype: 'pvePrivilegesSelector', - name: 'privs', - value: me.privs, - allowBlank: false, - fieldLabel: gettext('Privileges') - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response) { - var data = response.result.data; - var keys = Ext.Object.getKeys(data); - - me.setValues({ - privs: keys, - roleid: me.roleid - }); - } - }); - } - } -}); -Ext.define('PVE.dc.ACLAdd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveACLAdd'], - url: '/access/acl', - method: 'PUT', - isAdd: true, - initComponent : function() { - - var me = this; - - me.isCreate = true; - - var items = [ - { - xtype: me.path ? 'hiddenfield' : 'pvePermPathSelector', - name: 'path', - value: me.path, - allowBlank: false, - fieldLabel: gettext('Path') - } - ]; - - if (me.aclType === 'group') { - me.subject = gettext("Group Permission"); - items.push({ - xtype: 'pveGroupSelector', - name: 'groups', - fieldLabel: gettext('Group') - }); - } else if (me.aclType === 'user') { - me.subject = gettext("User Permission"); - items.push({ - xtype: 'pveUserSelector', - name: 'users', - fieldLabel: gettext('User') - }); - } else { - throw "unknown ACL type"; - } - - items.push({ - xtype: 'pveRoleSelector', - name: 'roles', - value: 'NoAccess', - fieldLabel: gettext('Role') - }); - - if (!me.path) { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'propagate', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Propagate') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - items: items, - onlineHelp: 'pveum_permission_management' - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.dc.ACLView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveACLView'], - - onlineHelp: 'chapter_user_management', - - stateful: true, - stateId: 'grid-acls', - - // use fixed path - path: undefined, - - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-acl', - proxy: { - type: 'proxmox', - url: "/api2/json/access/acl" - }, - sorters: { - property: 'path', - order: 'DESC' - } - }); - - if (me.path) { - store.addFilter(Ext.create('Ext.util.Filter',{ - filterFn: function(item) { - if (item.data.path === me.path) { - return true; - } - } - })); - } - - var render_ugid = function(ugid, metaData, record) { - if (record.data.type == 'group') { - return '@' + ugid; - } - - return ugid; - }; - - var columns = [ - { - header: gettext('User') + '/' + gettext('Group'), - flex: 1, - sortable: true, - renderer: render_ugid, - dataIndex: 'ugid' - }, - { - header: gettext('Role'), - flex: 1, - sortable: true, - dataIndex: 'roleid' - } - ]; - - if (!me.path) { - columns.unshift({ - header: gettext('Path'), - flex: 1, - sortable: true, - dataIndex: 'path' - }); - columns.push({ - header: gettext('Propagate'), - width: 80, - sortable: true, - dataIndex: 'propagate' - }); - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: gettext('Are you sure you want to remove this entry'), - handler: function(btn, event, rec) { - var params = { - 'delete': 1, - path: rec.data.path, - roles: rec.data.roleid - }; - if (rec.data.type === 'group') { - params.groups = rec.data.ugid; - } else if (rec.data.type === 'user') { - params.users = rec.data.ugid; - } else { - throw 'unknown data type'; - } - - Proxmox.Utils.API2Request({ - url: '/access/acl', - params: params, - method: 'PUT', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: { - xtype: 'menu', - items: [ - { - text: gettext('Group Permission'), - iconCls: 'fa fa-fw fa-group', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'group', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('User Permission'), - iconCls: 'fa fa-fw fa-user', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'user', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - } - ] - } - }, - remove_btn - ], - viewConfig: { - trackOver: false - }, - columns: columns, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-acl', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'ugid', 'roleid', - { - name: 'propagate', - type: 'boolean' - } - ] - }); - -}); -Ext.define('PVE.dc.AuthView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveAuthView'], - - onlineHelp: 'pveum_authentication_realms', - - stateful: true, - stateId: 'grid-authrealms', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-domains', - sorters: { - property: 'realm', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.AuthEdit',{ - realm: rec.data.realm, - authType: rec.data.type - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - baseurl: '/access/domains/', - selModel: sm, - enableFn: function(rec) { - return !(rec.data.type === 'pve' || rec.data.type === 'pam'); - }, - callback: function() { - reload(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Active Directory Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit', { - authType: 'ad' - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('LDAP Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit',{ - authType: 'ldap' - }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - edit_btn, remove_btn - ]; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Realm'), - width: 100, - sortable: true, - dataIndex: 'realm' - }, - { - header: gettext('Type'), - width: 100, - sortable: true, - dataIndex: 'type' - }, - { - header: gettext('TFA'), - width: 100, - sortable: true, - dataIndex: 'tfa' - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.AuthEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcAuthEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.realm; - - var url; - var method; - var serverlist; - - if (me.isCreate) { - url = '/api2/extjs/access/domains'; - method = 'POST'; - } else { - url = '/api2/extjs/access/domains/' + me.realm; - method = 'PUT'; - } - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'realm', - fieldLabel: gettext('Realm'), - value: me.realm, - allowBlank: false - } - ]; - - if (me.authType === 'ad') { - - me.subject = gettext('Active Directory Server'); - - column1.push({ - xtype: 'textfield', - name: 'domain', - fieldLabel: gettext('Domain'), - emptyText: 'company.net', - allowBlank: false - }); - - } else if (me.authType === 'ldap') { - - me.subject = gettext('LDAP Server'); - - column1.push({ - xtype: 'textfield', - name: 'base_dn', - fieldLabel: gettext('Base Domain Name'), - emptyText: 'CN=Users,DC=Company,DC=net', - allowBlank: false - }); - - column1.push({ - xtype: 'textfield', - name: 'user_attr', - emptyText: 'uid / sAMAccountName', - fieldLabel: gettext('User Attribute Name'), - allowBlank: false - }); - } else if (me.authType === 'pve') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'Proxmox VE authentication server'; - - } else if (me.authType === 'pam') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'linux PAM'; - - } else { - throw 'unknown auth type '; - } - - column1.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Default'), - name: 'default', - uncheckedValue: 0 - }); - - var column2 = []; - - if (me.authType === 'ldap' || me.authType === 'ad') { - column2.push( - { - xtype: 'textfield', - fieldLabel: gettext('Server'), - name: 'server1', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Fallback Server'), - deleteEmpty: !me.isCreate, - name: 'server2' - }, - { - xtype: 'proxmoxintegerfield', - name: 'port', - fieldLabel: gettext('Port'), - minValue: 1, - maxValue: 65535, - emptyText: gettext('Default'), - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'SSL', - name: 'secure', - uncheckedValue: 0 - } - ); - } - - // Two Factor Auth settings - - column2.push({ - xtype: 'proxmoxKVComboBox', - name: 'tfa', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('TFA'), - comboItems: [ ['__default__', Proxmox.Utils.noneText], ['oath', 'OATH'], ['yubico', 'Yubico']], - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=oath_step]').setVisible(value === 'oath'); - me.down('field[name=oath_digits]').setVisible(value === 'oath'); - me.down('field[name=yubico_api_id]').setVisible(value === 'yubico'); - me.down('field[name=yubico_api_key]').setVisible(value === 'yubico'); - me.down('field[name=yubico_url]').setVisible(value === 'yubico'); - } - } - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_step', - value: '', - minValue: 10, - emptyText: Proxmox.Utils.defaultText + ' (30)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH time step' - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_digits', - value: '', - minValue: 6, - maxValue: 8, - emptyText: Proxmox.Utils.defaultText + ' (6)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH password length' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_id', - hidden: true, - fieldLabel: 'Yubico API Id' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_key', - hidden: true, - fieldLabel: 'Yubico API Key' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_url', - hidden: true, - fieldLabel: 'Yubico URL' - }); - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [{ - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }], - onGetValues: function(values) { - if (!values.port) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'port' }); - } - delete values.port; - } - - if (me.isCreate) { - values.type = me.authType; - } - - if (values.tfa === 'oath') { - values.tfa = "type=oath"; - if (values.oath_step) { - values.tfa += ",step=" + values.oath_step; - } - if (values.oath_digits) { - values.tfa += ",digits=" + values.oath_digits; - } - } else if (values.tfa === 'yubico') { - values.tfa = "type=yubico"; - values.tfa += ",id=" + values.yubico_api_id; - values.tfa += ",key=" + values.yubico_api_key; - if (values.yubico_url) { - values.tfa += ",url=" + values.yubico_url; - } - } else { - delete values.tfa; - } - - delete values.oath_step; - delete values.oath_digits; - delete values.yubico_api_id; - delete values.yubico_api_key; - delete values.yubico_url; - - return values; - } - }); - - Ext.applyIf(me, { - url: url, - method: method, - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data || {}; - // just to be sure (should not happen) - if (data.type !== me.authType) { - me.close(); - throw "got wrong auth type"; - } - - if (data.tfa) { - var tfacfg = PVE.Parser.parseTfaConfig(data.tfa); - data.tfa = tfacfg.type; - if (tfacfg.type === 'yubico') { - data.yubico_api_key = tfacfg.key; - data.yubico_api_id = tfacfg.id; - data.yubico_url = tfacfg.url; - } else if (tfacfg.type === 'oath') { - // step is a number before - /*jslint confusion: true*/ - data.oath_step = tfacfg.step; - data.oath_digits = tfacfg.digits; - /*jslint confusion: false*/ - } - } - - me.setValues(data); - } - }); - } - } -}); -Ext.define('PVE.dc.BackupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcBackupEdit'], - - defaultFocus: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.jobid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/cluster/backup'; - method = 'POST'; - } else { - url = '/api2/extjs/cluster/backup/' + me.jobid; - method = 'PUT'; - } - - var vmidField = Ext.create('Ext.form.field.Hidden', { - name: 'vmid' - }); - - /*jslint confusion: true*/ - // 'value' can be assigned a string or an array - var selModeField = Ext.create('Proxmox.form.KVComboBox', { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['include', gettext('Include selected VMs')], - ['all', gettext('All')], - ['exclude', gettext('Exclude selected VMs')], - ['pool', gettext('Pool based')] - ], - fieldLabel: gettext('Selection mode'), - name: 'selMode', - value: '' - }); - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - var sel = []; - Ext.Array.each(selected, function(record) { - sel.push(record.data.vmid); - }); - - // to avoid endless recursion suspend the vmidField change - // event temporary as it calls us again - vmidField.suspendEvent('change'); - vmidField.setValue(sel); - vmidField.resumeEvent('change'); - } - } - }); - - var storagesel = Ext.create('PVE.form.StorageSelector', { - fieldLabel: gettext('Storage'), - nodename: 'localhost', - storageContent: 'backup', - allowBlank: false, - name: 'storage' - }); - - var store = new Ext.data.Store({ - model: 'PVEResources', - sorters: { - property: 'vmid', - order: 'ASC' - } - }); - - var vmgrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - disabled: true, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - - var selectPoolMembers = function(poolid) { - if (!poolid) { - return; - } - sm.deselectAll(true); - store.filter([ - { - id: 'poolFilter', - property: 'pool', - value: poolid - } - ]); - sm.selectAll(true); - }; - - var selPool = Ext.create('PVE.form.PoolSelector', { - fieldLabel: gettext('Pool to backup'), - hidden: true, - allowBlank: true, - name: 'pool', - listeners: { - change: function( selpool, newValue, oldValue) { - selectPoolMembers(newValue); - } - } - }); - - var nodesel = Ext.create('PVE.form.NodeSelector', { - name: 'node', - fieldLabel: gettext('Node'), - allowBlank: true, - editable: true, - autoSelect: false, - emptyText: '-- ' + gettext('All') + ' --', - listeners: { - change: function(f, value) { - storagesel.setNodename(value || 'localhost'); - var mode = selModeField.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!value || rec.get('node') === value); - }); - if (mode === 'all') { - sm.selectAll(true); - } - - if (mode === 'pool') { - selectPoolMembers(selPool.value); - } - } - } - }); - - var column1 = [ - nodesel, - storagesel, - { - xtype: 'pveDayOfWeekSelector', - name: 'dow', - fieldLabel: gettext('Day of week'), - multiSelect: true, - value: ['sat'], - allowBlank: false - }, - { - xtype: 'timefield', - fieldLabel: gettext('Start Time'), - name: 'starttime', - format: 'H:i', - formatText: 'HH:MM', - value: '00:00', - allowBlank: false - }, - selModeField, - selPool - ]; - - var column2 = [ - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto' - }, - { - xtype: 'pveEmailNotificationSelector', - fieldLabel: gettext('Email notification'), - name: 'mailnotification', - deleteEmpty: me.isCreate ? false : true, - value: me.isCreate ? 'always' : '' - }, - { - xtype: 'pveCompressionSelector', - fieldLabel: gettext('Compression'), - name: 'compress', - deleteEmpty: me.isCreate ? false : true, - value: 'lzo' - }, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable'), - name: 'enabled', - uncheckedValue: 0, - defaultValue: 1, - checked: true - }, - vmidField - ]; - /*jslint confusion: false*/ - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - onlineHelp: 'chapter_vzdump', - column1: column1, - column2: column2, - onGetValues: function(values) { - if (!values.node) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'node' }); - } - delete values.node; - } - - var selMode = values.selMode; - delete values.selMode; - - if (selMode === 'all') { - values.all = 1; - values.exclude = ''; - delete values.vmid; - } else if (selMode === 'exclude') { - values.all = 1; - values.exclude = values.vmid; - delete values.vmid; - } else if (selMode === 'pool') { - delete values.vmid; - } - - if (selMode !== 'pool') { - delete values.pool; - } - return values; - } - }); - - var update_vmid_selection = function(list, mode) { - if (mode !== 'all' && mode !== 'pool') { - sm.deselectAll(true); - if (list) { - Ext.Array.each(list.split(','), function(vmid) { - var rec = store.findRecord('vmid', vmid); - if (rec) { - sm.select(rec, true); - } - }); - } - } - }; - - vmidField.on('change', function(f, value) { - var mode = selModeField.getValue(); - update_vmid_selection(value, mode); - }); - - selModeField.on('change', function(f, value, oldValue) { - if (oldValue === 'pool') { - store.removeFilter('poolFilter'); - } - - if (oldValue === 'all') { - sm.deselectAll(true); - vmidField.setValue(''); - } - - if (value === 'all') { - sm.selectAll(true); - vmgrid.setDisabled(true); - } else { - vmgrid.setDisabled(false); - } - - if (value === 'pool') { - vmgrid.setDisabled(true); - vmidField.setValue(''); - selPool.setVisible(true); - selPool.allowBlank = false; - selectPoolMembers(selPool.value); - - } else { - selPool.setVisible(false); - selPool.allowBlank = true; - } - var list = vmidField.getValue(); - update_vmid_selection(list, value); - }); - - var reload = function() { - store.load({ - params: { type: 'vm' }, - callback: function() { - var node = nodesel.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!node || node.length === 0 || rec.get('node') === node); - }); - var list = vmidField.getValue(); - var mode = selModeField.getValue(); - if (mode === 'all') { - sm.selectAll(true); - } else if (mode === 'pool'){ - selectPoolMembers(selPool.value); - } else { - update_vmid_selection(list, mode); - } - } - }); - }; - - Ext.applyIf(me, { - subject: gettext("Backup Job"), - url: url, - method: method, - items: [ ipanel, vmgrid ] - }); - - me.callParent(); - - if (me.isCreate) { - selModeField.setValue('include'); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - - data.dow = data.dow.split(','); - - if (data.all || data.exclude) { - if (data.exclude) { - data.vmid = data.exclude; - data.selMode = 'exclude'; - } else { - data.vmid = ''; - data.selMode = 'all'; - } - } else if (data.pool) { - data.selMode = 'pool'; - data.selPool = data.pool; - } else { - data.selMode = 'include'; - } - - me.setValues(data); - } - }); - } - - reload(); - } -}); - - -Ext.define('PVE.dc.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveDcBackupView'], - - onlineHelp: 'chapter_vzdump', - - allText: '-- ' + gettext('All') + ' --', - allExceptText: gettext('All except {0}'), - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-cluster-backup', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/backup" - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.BackupEdit',{ - jobid: rec.data.id - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/backup', - callback: function() { - reload(); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - stateful: true, - stateId: 'grid-dc-backup', - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.dc.BackupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: gettext('Enabled'), - width: 80, - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true, - disabledCls: 'x-item-enabled', - stopSelection: false - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node', - renderer: function(value) { - if (value) { - return value; - } - return me.allText; - } - }, - { - header: gettext('Day of week'), - width: 200, - sortable: false, - dataIndex: 'dow', - renderer: function(val) { - var dows = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; - var selected = []; - var cur = -1; - val.split(',').forEach(function(day){ - cur++; - var dow = (dows.indexOf(day)+6)%7; - if (cur === dow) { - if (selected.length === 0 || selected[selected.length-1] === 0) { - selected.push(1); - } else { - selected[selected.length-1]++; - } - } else { - while (cur < dow) { - cur++; - selected.push(0); - } - selected.push(1); - } - }); - - cur = -1; - var days = []; - selected.forEach(function(item) { - cur++; - if (item > 2) { - days.push(Ext.Date.dayNames[(cur+1)] + '-' + Ext.Date.dayNames[(cur+item)%7]); - cur += item-1; - } else if (item == 2) { - days.push(Ext.Date.dayNames[cur+1]); - days.push(Ext.Date.dayNames[(cur+2)%7]); - cur++; - } else if (item == 1) { - days.push(Ext.Date.dayNames[(cur+1)%7]); - } - }); - return days.join(', '); - } - }, - { - header: gettext('Start Time'), - width: 60, - sortable: true, - dataIndex: 'starttime' - }, - { - header: gettext('Storage'), - width: 100, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Selection'), - flex: 1, - sortable: false, - dataIndex: 'vmid', - renderer: function(value, metaData, record) { - /*jslint confusion: true */ - if (record.data.all) { - if (record.data.exclude) { - return Ext.String.format(me.allExceptText, record.data.exclude); - } - return me.allText; - } - if (record.data.vmid) { - return record.data.vmid; - } - - if (record.data.pool) { - return "Pool '"+ record.data.pool + "'"; - } - - return "-"; - } - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-cluster-backup', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'starttime', 'dow', - 'storage', 'node', 'vmid', 'exclude', - 'mailto', 'pool', - { name: 'enabled', type: 'boolean' }, - { name: 'all', type: 'boolean' }, - { name: 'snapshot', type: 'boolean' }, - { name: 'stop', type: 'boolean' }, - { name: 'suspend', type: 'boolean' }, - { name: 'compress', type: 'boolean' } - ] - }); -}); -Ext.define('PVE.dc.Support', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSupport', - pveGuidePath: '/pve-docs/index.html', - onlineHelp: 'getting_help', - - invalidHtml: '

No valid subscription

' + PVE.Utils.noSubKeyHtml, - - communityHtml: 'Please use the public community forum for any questions.', - - activeHtml: 'Please use our support portal for any questions. You can also use the public community forum to get additional information.', - - bugzillaHtml: '

Bug Tracking

Our bug tracking system is available here.', - - docuHtml: function() { - var me = this; - var guideUrl = window.location.origin + me.pveGuidePath; - var text = Ext.String.format('

Documentation

' - + 'The official Proxmox VE Administration Guide' - + ' is included with this installation and can be browsed at ' - + '{0}', guideUrl); - return text; - }, - - updateActive: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.activeHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateCommunity: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.communityHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateInactive: function(data) { - var me = this; - me.update(me.invalidHtml); - }, - - initComponent: function() { - var me = this; - - var reload = function() { - Proxmox.Utils.API2Request({ - url: '/nodes/localhost/subscription', - method: 'GET', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.update('Unable to load subscription status' + ": " + response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data; - - if (data.status === 'Active') { - if (data.level === 'c') { - me.updateCommunity(data); - } else { - me.updateActive(data); - } - } else { - me.updateInactive(data); - } - } - }); - }; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('pve-security-groups', { - extend: 'Ext.data.Model', - - fields: [ 'group', 'comment', 'digest' ], - idProperty: 'group' -}); - -Ext.define('PVE.SecurityGroupEdit', { - extend: 'Proxmox.window.Edit', - - base_url: "/cluster/firewall/groups", - - allow_iface: false, - - initComponent : function() { - var me = this; - - me.isCreate = (me.group_name === undefined); - - var subject; - - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - - var items = [ - { - xtype: 'textfield', - name: 'group', - value: me.group_name || '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: me.group_comment || '', - fieldLabel: gettext('Comment') - } - ]; - - if (me.isCreate) { - subject = gettext('Security Group'); - } else { - subject = gettext('Security Group') + " '" + me.group_name + "'"; - items.push({ - xtype: 'hiddenfield', - name: 'rename', - value: me.group_name - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - // InputPanel does not have a 'create' property, does it need a 'isCreate' - isCreate: me.isCreate, - items: items - }); - - - Ext.apply(me, { - subject: subject, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.SecurityGroupList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveSecurityGroupList', - - stateful: true, - stateId: 'grid-securitygroups', - - rule_panel: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - base_url: "/cluster/firewall/groups", - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (me.rule_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-security-groups', - proxy: { - type: 'proxmox', - url: '/api2/json' + me.base_url - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('group', oldrec.data.group); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.SecurityGroupEdit', { - digest: rec.data.digest, - group_name: rec.data.group, - group_comment: rec.data.comment - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('PVE.SecurityGroupEdit', {}); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - enableFn: function(rec) { - return (rec && me.base_url); - }, - callback: function() { - reload(); - } - }); - - Ext.apply(me, { - store: store, - tbar: [ '' + gettext('Group') + ':', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: gettext('Group'), dataIndex: 'group', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = '/cluster/firewall/groups/' + rec.data.group; - me.rule_panel.setBaseUrl(url); - }, - deselect: function() { - me.rule_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.SecurityGroups', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveSecurityGroups', - - title: 'Security Groups', - - initComponent: function() { - var me = this; - - var rule_panel = Ext.createWidget('pveFirewallRules', { - region: 'center', - allow_groups: false, - list_refs_url: '/cluster/firewall/refs', - tbar_prefix: '' + gettext('Rules') + ':', - border: false - }); - - var sglist = Ext.createWidget('pveSecurityGroupList', { - region: 'west', - rule_panel: rule_panel, - width: '25%', - border: false, - split: true - }); - - - Ext.apply(me, { - layout: 'border', - items: [ sglist, rule_panel ], - listeners: { - show: function() { - sglist.fireEvent('show', sglist); - } - } - }); - - me.callParent(); - } -}); -/* - * Datacenter config panel, located in the center of the ViewPort after the Datacenter view is selected - */ - -Ext.define('PVE.dc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.dc.Config', - - onlineHelp: 'pve_admin_guide', - - initComponent: function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.items = []; - - Ext.apply(me, { - title: gettext("Datacenter"), - hstateid: 'dctab' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - title: gettext('Summary'), - xtype: 'pveDcSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - }, - { - title: gettext('Cluster'), - xtype: 'pveClusterAdministration', - iconCls: 'fa fa-server', - itemId: 'cluster' - }, - { - title: 'Ceph', - itemId: 'ceph', - iconCls: 'fa fa-ceph', - xtype: 'pveNodeCephStatus' - }, - { - xtype: 'pveDcOptionView', - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options' - }); - } - - if (caps.storage['Datastore.Allocate'] || caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveStorageView', - title: gettext('Storage'), - iconCls: 'fa fa-database', - itemId: 'storage' - }); - } - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveDcBackupView', - iconCls: 'fa fa-floppy-o', - title: gettext('Backup'), - itemId: 'backup' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - expandedOnInit: true - }); - } - - me.items.push({ - xtype: 'pveUserView', - groups: ['permissions'], - iconCls: 'fa fa-user', - title: gettext('Users'), - itemId: 'users' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveGroupView', - title: gettext('Groups'), - iconCls: 'fa fa-users', - groups: ['permissions'], - itemId: 'groups' - }, - { - xtype: 'pvePoolView', - title: gettext('Pools'), - iconCls: 'fa fa-tags', - groups: ['permissions'], - itemId: 'pools' - }, - { - xtype: 'pveRoleView', - title: gettext('Roles'), - iconCls: 'fa fa-male', - groups: ['permissions'], - itemId: 'roles' - }, - { - xtype: 'pveAuthView', - title: gettext('Authentication'), - groups: ['permissions'], - iconCls: 'fa fa-key', - itemId: 'domains' - }, - { - xtype: 'pveHAStatus', - title: 'HA', - iconCls: 'fa fa-heartbeat', - itemId: 'ha' - }, - { - title: gettext('Groups'), - groups: ['ha'], - xtype: 'pveHAGroupsView', - iconCls: 'fa fa-object-group', - itemId: 'ha-groups' - }, - { - title: gettext('Fencing'), - groups: ['ha'], - iconCls: 'fa fa-bolt', - xtype: 'pveFencingView', - itemId: 'ha-fencing' - }, - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/cluster/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - iconCls: 'fa fa-shield', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - groups: ['firewall'], - iconCls: 'fa fa-gear', - base_url: '/cluster/firewall/options', - onlineHelp: 'pve_firewall_cluster_wide_setup', - fwtype: 'dc', - itemId: 'firewall-options' - }, - { - xtype: 'pveSecurityGroups', - title: gettext('Security Group'), - groups: ['firewall'], - iconCls: 'fa fa-group', - itemId: 'firewall-sg' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: '/cluster/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: 'IPSet', - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: '/cluster/firewall/ipset', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall-ipset' - }, - { - xtype: 'pveDcSupport', - title: gettext('Support'), - itemId: 'support', - iconCls: 'fa fa-comments-o' - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.dc.NodeView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveDcNodeView', - - title: gettext('Nodes'), - disableSelection: true, - scrollable: true, - - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: 'ID', - width: 40, - sortable: true, - dataIndex: 'nodeid' - }, - { - header: gettext('Online'), - width: 60, - sortable: true, - dataIndex: 'online', - renderer: function(value) { - var cls = (value)?'good':'critical'; - return ''; - } - }, - { - header: gettext('Support'), - width: 100, - sortable: true, - dataIndex: 'level', - renderer: PVE.Utils.render_support_level - }, - { - header: gettext('Server Address'), - width: 115, - sortable: true, - dataIndex: 'ip' - }, - { - header: gettext('CPU usage'), - sortable: true, - width: 110, - dataIndex: 'cpuusage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Memory usage'), - width: 110, - sortable: true, - tdCls: 'x-progressbar-default-cell', - dataIndex: 'memoryusage', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Uptime'), - sortable: true, - dataIndex: 'uptime', - align: 'right', - renderer: Proxmox.Utils.render_uptime - } - ], - - stateful: true, - stateId: 'grid-cluster-nodes', - tools: [ - { - type: 'up', - handler: function(){ - var me = this.up('grid'); - var height = Math.max(me.getHeight()-50, 250); - me.setHeight(height); - } - }, - { - type: 'down', - handler: function(){ - var me = this.up('grid'); - var height = me.getHeight()+50; - me.setHeight(height); - } - } - ] -}, function() { - - Ext.define('pve-dc-nodes', { - extend: 'Ext.data.Model', - fields: [ 'id', 'type', 'name', 'nodeid', 'ip', 'level', 'local', 'online'], - idProperty: 'id' - }); - -}); - -Ext.define('PVE.widget.ProgressBar',{ - extend: 'Ext.Progress', - alias: 'widget.pveProgressBar', - - animate: true, - textTpl: [ - '{percent}%' - ], - - setValue: function(value){ - var me = this; - me.callParent([value]); - - me.removeCls(['warning', 'critical']); - - if (value > 0.89) { - me.addCls('critical'); - } else if (value > 0.59) { - me.addCls('warning'); - } - } -}); -/*jslint confusion: true*/ -Ext.define('pve-cluster-nodes', { - extend: 'Ext.data.Model', - fields: [ - 'node', { type: 'integer', name: 'nodeid' }, 'ring0_addr', 'ring1_addr', - { type: 'integer', name: 'quorum_votes' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/nodes" - }, - idProperty: 'nodeid' -}); - -Ext.define('pve-cluster-info', { - extend: 'Ext.data.Model', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/join" - } -}); - -Ext.define('PVE.ClusterAdministration', { - extend: 'Ext.panel.Panel', - xtype: 'pveClusterAdministration', - - title: gettext('Cluster Administration'), - onlineHelp: 'chapter_pvecm', - - border: false, - defaults: { border: false }, - - viewModel: { - parent: null, - data: { - totem: {}, - nodelist: [], - preferred_node: { - name: '', - fp: '', - addr: '' - }, - isInCluster: false, - nodecount: 0 - } - }, - - items: [ - { - xtype: 'panel', - title: gettext('Cluster Information'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store = Ext.create('Proxmox.data.UpdateStore', { - autoStart: true, - interval: 15 * 1000, - storeid: 'pve-cluster-info', - model: 'pve-cluster-info' - }); - view.store.on('load', this.onLoad, this); - view.on('destroy', view.store.stopUpdate); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records || !records[0].data) { - vm.set('totem', {}); - vm.set('isInCluster', false); - vm.set('nodelist', []); - vm.set('preferred_node', { - name: '', - addr: '', - fp: '' - }); - return; - } - var data = records[0].data; - vm.set('totem', data.totem); - vm.set('isInCluster', !!data.totem.cluster_name); - vm.set('nodelist', data.nodelist); - - var nodeinfo = Ext.Array.findBy(data.nodelist, function (el) { - return el.name === data.preferred_node; - }); - - vm.set('preferred_node', { - name: data.preferred_node, - addr: nodeinfo.pve_addr, - ring_addr: [ nodeinfo.ring0_addr, nodeinfo.ring1_addr ], - fp: nodeinfo.pve_fp - }); - }, - - onCreate: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterCreateWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - }, - - onClusterInfo: function() { - var vm = this.getViewModel(); - var win = Ext.create('PVE.ClusterInfoWindow', { - joinInfo: { - ipAddress: vm.get('preferred_node.addr'), - fingerprint: vm.get('preferred_node.fp'), - ring_addr: vm.get('preferred_node.ring_addr'), - totem: vm.get('totem') - } - }); - win.show(); - }, - - onJoin: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterJoinNodeWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - } - }, - tbar: [ - { - text: gettext('Create Cluster'), - reference: 'createButton', - handler: 'onCreate', - bind: { - disabled: '{isInCluster}' - } - }, - { - text: gettext('Join Information'), - reference: 'addButton', - handler: 'onClusterInfo', - bind: { - disabled: '{!isInCluster}' - } - }, - { - text: gettext('Join Cluster'), - reference: 'joinButton', - handler: 'onJoin', - bind: { - disabled: '{isInCluster}' - } - } - ], - layout: 'hbox', - bodyPadding: 5, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Cluster Name'), - bind: { - value: '{totem.cluster_name}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Config Version'), - bind: { - value: '{totem.config_version}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Number of Nodes'), - labelWidth: 120, - bind: { - value: '{nodecount}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - value: gettext('Standalone node - no cluster defined'), - bind: { - hidden: '{isInCluster}' - }, - flex: 1 - } - ] - }, - { - xtype: 'grid', - title: gettext('Cluster Nodes'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-cluster-nodes', - model: 'pve-cluster-nodes' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'nodeid', - order: 'DESC' - } - })); - Proxmox.Utils.monStoreErrors(view, view.rstore); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records) { - vm.set('nodecount', 0); - return; - } - vm.set('nodecount', records.length); - } - }, - columns: [ - { - header: gettext('Nodename'), - flex: 2, - dataIndex: 'name' - }, - { - header: gettext('ID'), - flex: 1, - dataIndex: 'nodeid' - }, - { - header: gettext('Votes'), - flex: 1, - dataIndex: 'quorum_votes' - }, - { - header: Ext.String.format(gettext('Link {0}'), 0), - flex: 2, - dataIndex: 'ring0_addr' - }, - { - header: Ext.String.format(gettext('Link {0}'), 1), - flex: 2, - dataIndex: 'ring1_addr' - } - ] - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.ClusterCreateWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterCreateWindow', - - title: gettext('Create Cluster'), - width: 600, - - method: 'POST', - url: '/cluster/config', - - isCreate: true, - subject: gettext('Cluster'), - showTaskViewer: true, - - onlineHelp: 'pvecm_create_cluster', - - items: { - xtype: 'inputpanel', - items: [{ - xtype: 'textfield', - fieldLabel: gettext('Cluster Name'), - allowBlank: false, - name: 'clustername' - }, - { - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 0), - emptyText: gettext("Optional, defaults to IP resolved by node's hostname"), - name: 'link0', - autoSelect: false, - valueField: 'address', - displayField: 'address', - skipEmptyText: true - }], - advancedItems: [{ - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 1), - emptyText: gettext("Optional second link for redundancy"), - name: 'link1', - autoSelect: false, - valueField: 'address', - displayField: 'address', - skipEmptyText: true - }] - } -}); - -Ext.define('PVE.ClusterInfoWindow', { - extend: 'Ext.window.Window', - xtype: 'pveClusterInfoWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 800, - modal: true, - resizable: false, - title: gettext('Cluster Join Information'), - - joinInfo: { - ipAddress: undefined, - fingerprint: undefined, - totem: {} - }, - - items: [ - { - xtype: 'component', - border: false, - padding: '10 10 10 10', - html: gettext("Copy the Join Information here and use it on the node you want to add.") - }, - { - xtype: 'container', - layout: 'form', - border: false, - padding: '0 10 10 10', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('IP Address'), - cbind: { value: '{joinInfo.ipAddress}' }, - editable: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - cbind: { value: '{joinInfo.fingerprint}' }, - editable: false - }, - { - xtype: 'textarea', - inputId: 'pveSerializedClusterInfo', - fieldLabel: gettext('Join Information'), - grow: true, - cbind: { joinInfo: '{joinInfo}' }, - editable: false, - listeners: { - afterrender: function(field) { - if (!field.joinInfo) { - return; - } - var jsons = Ext.JSON.encode(field.joinInfo); - var base64s = Ext.util.Base64.encode(jsons); - field.setValue(base64s); - } - } - } - ] - } - ], - dockedItems: [{ - dock: 'bottom', - xtype: 'toolbar', - items: [{ - xtype: 'button', - handler: function(b) { - var el = document.getElementById('pveSerializedClusterInfo'); - el.select(); - document.execCommand("copy"); - }, - text: gettext('Copy Information') - }] - }] -}); - -Ext.define('PVE.ClusterJoinNodeWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterJoinNodeWindow', - - title: gettext('Cluster Join'), - width: 800, - - method: 'POST', - url: '/cluster/config/join', - - defaultFocus: 'textarea[name=serializedinfo]', - isCreate: true, - submitText: gettext('Join'), - showTaskViewer: true, - - onlineHelp: 'chapter_pvecm', - - viewModel: { - parent: null, - data: { - info: { - fp: '', - ip: '', - ring0Needed: false, - ring1Possible: false, - ring1Needed: false - } - }, - formulas: { - ring0EmptyText: function(get) { - if (get('info.ring0Needed')) { - return gettext("Cannot use default address safely"); - } else { - return gettext("Default: IP resolved by node's hostname"); - } - } - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - '#': { - close: function() { - delete PVE.Utils.silenceAuthFailures; - } - }, - 'proxmoxcheckbox[name=assistedEntry]': { - change: 'onInputTypeChange' - }, - 'textarea[name=serializedinfo]': { - change: 'recomputeSerializedInfo', - enable: 'resetField' - }, - 'proxmoxtextfield[name=ring1_addr]': { - enable: 'ring1Needed' - }, - 'textfield': { - disable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - ring1Needed: function(f) { - var vm = this.getViewModel(); - f.allowBlank = !vm.get('info.ring1Needed'); - }, - onInputTypeChange: function(field, assistedInput) { - var vm = this.getViewModel(); - if (!assistedInput) { - vm.set('info.ring1Possible', true); - } - }, - recomputeSerializedInfo: function(field, value) { - var vm = this.getViewModel(); - var jsons = Ext.util.Base64.decode(value); - var joinInfo = Ext.JSON.decode(jsons, true); - - var info = { - fp: '', - ring1Needed: false, - ring1Possible: false, - ip: '' - }; - - var totem = {}; - if (!(joinInfo && joinInfo.totem)) { - field.valid = false; - } else { - var ring0Needed = false; - if (joinInfo.ring_addr !== undefined) { - ring0Needed = joinInfo.ring_addr[0] !== joinInfo.ipAddress; - } - - info = { - ip: joinInfo.ipAddress, - fp: joinInfo.fingerprint, - ring0Needed: ring0Needed, - ring1Possible: !!joinInfo.totem['interface']['1'], - ring1Needed: !!joinInfo.totem['interface']['1'] - }; - totem = joinInfo.totem; - field.valid = true; - } - - vm.set('info', info); - } - }, - - submit: function() { - // joining may produce temporarily auth failures, ignore as long the task runs - PVE.Utils.silenceAuthFailures = true; - this.callParent(); - }, - - taskDone: function(success) { - delete PVE.Utils.silenceAuthFailures; - if (success) { - var txt = gettext('Cluster join task finished, node certificate may have changed, reload GUI!'); - // ensure user cannot do harm - Ext.getBody().mask(txt, ['pve-static-mask']); - // TaskView may hide above mask, so tell him directly - Ext.Msg.show({ - title: gettext('Join Task Finished'), - icon: Ext.Msg.INFO, - msg: txt - }); - // reload always (if user wasn't faster), but wait a bit for pveproxy - Ext.defer(function() { - window.location.reload(true); - }, 5000); - } - }, - - items: [{ - xtype: 'proxmoxcheckbox', - reference: 'assistedEntry', - name: 'assistedEntry', - submitValue: false, - value: true, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Select if join information should be extracted from pasted cluster information, deselect for manual entering') - }, - boxLabel: gettext('Assisted join: Paste encoded cluster join information and enter password.') - }, - { - xtype: 'textarea', - name: 'serializedinfo', - submitValue: false, - allowBlank: false, - fieldLabel: gettext('Information'), - emptyText: gettext('Paste encoded Cluster Information here'), - validator: function(val) { - return val === '' || this.valid || - gettext('Does not seem like a valid encoded Cluster Information!'); - }, - bind: { - disabled: '{!assistedEntry.checked}', - hidden: '{!assistedEntry.checked}' - }, - value: '' - }, - { - xtype: 'inputpanel', - column1: [ - { - xtype: 'textfield', - fieldLabel: gettext('Peer Address'), - allowBlank: false, - bind: { - value: '{info.ip}', - readOnly: '{assistedEntry.checked}' - }, - name: 'hostname' - }, - { - xtype: 'textfield', - inputType: 'password', - emptyText: gettext("Peer's root password"), - fieldLabel: gettext('Password'), - allowBlank: false, - name: 'password' - } - ], - column2: [ - { - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 0), - bind: { - emptyText: '{ring0EmptyText}', - allowBlank: '{!info.ring0Needed}' - }, - skipEmptyText: true, - autoSelect: false, - valueField: 'address', - displayField: 'address', - name: 'link0' - }, - { - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 1), - skipEmptyText: true, - autoSelect: false, - valueField: 'address', - displayField: 'address', - bind: { - disabled: '{!info.ring1Possible}', - allowBlank: '{!info.ring1Needed}', - }, - name: 'link1' - } - ], - columnB: [ - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - allowBlank: false, - bind: { - value: '{info.fp}', - readOnly: '{assistedEntry.checked}' - }, - name: 'fingerprint' - } - ] - }] -}); -/* - * Workspace base class - * - * popup login window when auth fails (call onLogin handler) - * update (re-login) ticket every 15 minutes - * - */ - -Ext.define('PVE.Workspace', { - extend: 'Ext.container.Viewport', - - title: 'Proxmox Virtual Environment', - - loginData: null, // Data from last login call - - onLogin: function(loginData) {}, - - // private - updateLoginData: function(loginData) { - var me = this; - me.loginData = loginData; - Proxmox.Utils.setAuthData(loginData); - - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(loginData.clustername); - - if (loginData.cap) { - Ext.state.Manager.set('GuiCap', loginData.cap); - } - me.response401count = 0; - - me.onLogin(loginData); - }, - - // private - showLogin: function() { - var me = this; - - Proxmox.Utils.authClear(); - Proxmox.UserName = null; - me.loginData = null; - - if (!me.login) { - me.login = Ext.create('PVE.window.LoginWindow', { - handler: function(data) { - me.login = null; - me.updateLoginData(data); - Proxmox.Utils.checked_command(function() {}); // display subscription status - } - }); - } - me.onLogin(null); - me.login.show(); - }, - - initComponent : function() { - var me = this; - - Ext.tip.QuickTipManager.init(); - - // fixme: what about other errors - Ext.Ajax.on('requestexception', function(conn, response, options) { - if (response.status == 401 && !PVE.Utils.silenceAuthFailures) { // auth failure - // don't immediately show as logged out to cope better with some big - // upgrades, which may temporarily produce a false positive 401 err - me.response401count++; - if (me.response401count > 5) { - me.showLogin(); - } - } - }); - - me.callParent(); - - if (!Proxmox.Utils.authOK()) { - me.showLogin(); - } else { - if (me.loginData) { - me.onLogin(me.loginData); - } - } - - Ext.TaskManager.start({ - run: function() { - var ticket = Proxmox.Utils.authOK(); - if (!ticket || !Proxmox.UserName) { - return; - } - - Ext.Ajax.request({ - params: { - username: Proxmox.UserName, - password: ticket - }, - url: '/api2/json/access/ticket', - method: 'POST', - success: function(response, opts) { - var obj = Ext.decode(response.responseText); - me.updateLoginData(obj.data); - } - }); - }, - interval: 15*60*1000 - }); - - } -}); - -Ext.define('PVE.StdWorkspace', { - extend: 'PVE.Workspace', - - alias: ['widget.pveStdWorkspace'], - - // private - setContent: function(comp) { - var me = this; - - var cont = me.child('#content'); - - var lay = cont.getLayout(); - - var cur = lay.getActiveItem(); - - if (comp) { - Proxmox.Utils.setErrorMask(cont, false); - comp.border = false; - cont.add(comp); - if (cur !== null && lay.getNext()) { - lay.next(); - var task = Ext.create('Ext.util.DelayedTask', function(){ - cont.remove(cur); - }); - task.delay(10); - } - } - else { - // helper for cleaning the content when logging out - cont.removeAll(); - } - }, - - selectById: function(nodeid) { - var me = this; - var tree = me.down('pveResourceTree'); - tree.selectById(nodeid); - }, - - onLogin: function(loginData) { - var me = this; - - me.updateUserInfo(); - - if (loginData) { - PVE.data.ResourceStore.startUpdate(); - - Proxmox.Utils.API2Request({ - url: '/version', - method: 'GET', - success: function(response) { - PVE.VersionInfo = response.result.data; - me.updateVersionInfo(); - } - }); - } - }, - - updateUserInfo: function() { - var me = this; - var ui = me.query('#userinfo')[0]; - ui.setText(Proxmox.UserName || ''); - ui.updateLayout(); - }, - - updateVersionInfo: function() { - var me = this; - - var ui = me.query('#versioninfo')[0]; - - if (PVE.VersionInfo) { - var version = PVE.VersionInfo.version; - ui.update('Virtual Environment ' + version); - } else { - ui.update('Virtual Environment'); - } - ui.updateLayout(); - }, - - initComponent : function() { - var me = this; - - Ext.History.init(); - - var sprovider = Ext.create('PVE.StateProvider'); - Ext.state.Manager.setProvider(sprovider); - - var selview = Ext.create('PVE.form.ViewSelector'); - - var rtree = Ext.createWidget('pveResourceTree', { - viewFilter: selview.getViewFilter(), - flex: 1, - selModel: { - selType: 'treemodel', - listeners: { - selectionchange: function(sm, selected) { - if (selected.length > 0) { - var n = selected[0]; - var tlckup = { - root: 'PVE.dc.Config', - node: 'PVE.node.Config', - qemu: 'PVE.qemu.Config', - lxc: 'PVE.lxc.Config', - storage: 'PVE.storage.Browser', - pool: 'pvePoolConfig' - }; - var comp = { - xtype: tlckup[n.data.type || 'root'] || - 'pvePanelConfig', - showSearch: (n.data.id === 'root') || - Ext.isDefined(n.data.groupbyid), - pveSelNode: n, - workspace: me, - viewFilter: selview.getViewFilter() - }; - PVE.curSelectedNode = n; - me.setContent(comp); - } - } - } - } - }); - - selview.on('select', function(combo, records) { - if (records) { - var view = combo.getViewFilter(); - rtree.setViewFilter(view); - } - }); - - var caps = sprovider.get('GuiCap'); - - var createVM = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-desktop', - text: gettext("Create VM"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.qemu.CreateWizard', {}); - wiz.show(); - } - }); - - var createCT = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-cube', - text: gettext("Create CT"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.lxc.CreateWizard', {}); - wiz.show(); - } - }); - - sprovider.on('statechange', function(sp, key, value) { - if (key === 'GuiCap' && value) { - caps = value; - createVM.setDisabled(!caps.vms['VM.Allocate']); - createCT.setDisabled(!caps.vms['VM.Allocate']); - } - }); - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - { - region: 'north', - layout: { - type: 'hbox', - align: 'middle' - }, - baseCls: 'x-plain', - defaults: { - baseCls: 'x-plain' - }, - border: false, - margin: '2 0 2 5', - items: [ - { - html: '' + - '' - }, - { - minWidth: 150, - id: 'versioninfo', - html: 'Virtual Environment' - }, - { - xtype: 'pveGlobalSearchField', - tree: rtree - }, - { - flex: 1 - }, - { - xtype: 'proxmoxHelpButton', - hidden: false, - baseCls: 'x-btn', - iconCls: 'fa fa-book x-btn-icon-el-default-toolbar-small ', - listenToGlobalEvent: false, - onlineHelp: 'pve_documentation_index', - text: gettext('Documentation'), - margin: '0 5 0 0' - }, - createVM, - createCT, - { - pack: 'end', - margin: '0 5 0 0', - id: 'userinfo', - xtype: 'button', - baseCls: 'x-btn', - style: { - // proxmox dark grey p light grey as border - backgroundColor: '#464d4d', - borderColor: '#ABBABA' - }, - iconCls: 'fa fa-user', - menu: [ - { - iconCls: 'fa fa-gear', - text: gettext('My Settings'), - handler: function() { - var win = Ext.create('PVE.window.Settings'); - win.show(); - } - }, - { - text: gettext('Password'), - iconCls: 'fa fa-fw fa-key', - handler: function() { - var win = Ext.create('Proxmox.window.PasswordEdit', { - userid: Proxmox.UserName - }); - win.show(); - } - }, - { - text: 'TFA', - iconCls: 'fa fa-fw fa-lock', - handler: function(btn, event, rec) { - var win = Ext.create('PVE.window.TFAEdit',{ - userid: Proxmox.UserName - }); - win.show(); - } - }, - '-', - { - iconCls: 'fa fa-fw fa-sign-out', - text: gettext("Logout"), - handler: function() { - PVE.data.ResourceStore.loadData([], false); - me.showLogin(); - me.setContent(null); - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(undefined); - rt.clearTree(); - - // empty the stores of the StatusPanel child items - var statusPanels = Ext.ComponentQuery.query('pveStatusPanel grid'); - Ext.Array.forEach(statusPanels, function(comp) { - if (comp.getStore()) { - comp.getStore().loadData([], false); - } - }); - } - } - ] - } - ] - }, - { - region: 'center', - stateful: true, - stateId: 'pvecenter', - minWidth: 100, - minHeight: 100, - id: 'content', - xtype: 'container', - layout: { type: 'card' }, - border: false, - margin: '0 5 0 0', - items: [] - }, - { - region: 'west', - stateful: true, - stateId: 'pvewest', - itemId: 'west', - xtype: 'container', - border: false, - layout: { type: 'vbox', align: 'stretch' }, - margin: '0 0 0 5', - split: true, - width: 200, - items: [ selview, rtree ], - listeners: { - resize: function(panel, width, height) { - var viewWidth = me.getSize().width; - if (width > viewWidth - 100) { - panel.setWidth(viewWidth - 100); - } - } - } - }, - { - xtype: 'pveStatusPanel', - stateful: true, - stateId: 'pvesouth', - itemId: 'south', - region: 'south', - margin:'0 5 5 5', - title: gettext('Logs'), - collapsible: true, - header: false, - height: 200, - split:true, - listeners: { - resize: function(panel, width, height) { - var viewHeight = me.getSize().height; - if (height > (viewHeight - 150)) { - panel.setHeight(viewHeight - 150); - } - } - } - } - ] - }); - - me.callParent(); - - me.updateUserInfo(); - - // on resize, center all modal windows - Ext.on('resize', function(){ - var wins = Ext.ComponentQuery.query('window[modal]'); - if (wins.length > 0) { - wins.forEach(function(win){ - win.alignTo(me, 'c-c'); - }); - } - }); - } -}); - diff --git a/serverside/jsmod/6.0-4/pvemanagerlib.js.original b/serverside/jsmod/6.0-4/pvemanagerlib.js.original deleted file mode 100644 index 0ba8b5c..0000000 --- a/serverside/jsmod/6.0-4/pvemanagerlib.js.original +++ /dev/null @@ -1,39779 +0,0 @@ -var pveOnlineHelpInfo = { - "ceph_rados_block_devices" : { - "link" : "/pve-docs/chapter-pvesm.html#ceph_rados_block_devices", - "title" : "Ceph RADOS Block Devices (RBD)" - }, - "chapter_ha_manager" : { - "link" : "/pve-docs/chapter-ha-manager.html#chapter_ha_manager", - "title" : "High Availability" - }, - "chapter_lvm" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_lvm", - "title" : "Logical Volume Manager (LVM)" - }, - "chapter_pct" : { - "link" : "/pve-docs/chapter-pct.html#chapter_pct", - "title" : "Proxmox Container Toolkit" - }, - "chapter_pve_firewall" : { - "link" : "/pve-docs/chapter-pve-firewall.html#chapter_pve_firewall", - "title" : "Proxmox VE Firewall" - }, - "chapter_pveceph" : { - "link" : "/pve-docs/chapter-pveceph.html#chapter_pveceph", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "chapter_pvecm" : { - "link" : "/pve-docs/chapter-pvecm.html#chapter_pvecm", - "title" : "Cluster Manager" - }, - "chapter_pvesr" : { - "link" : "/pve-docs/chapter-pvesr.html#chapter_pvesr", - "title" : "Storage Replication" - }, - "chapter_storage" : { - "link" : "/pve-docs/chapter-pvesm.html#chapter_storage", - "title" : "Proxmox VE Storage" - }, - "chapter_system_administration" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_system_administration", - "title" : "Host System Administration" - }, - "chapter_user_management" : { - "link" : "/pve-docs/chapter-pveum.html#chapter_user_management", - "title" : "User Management" - }, - "chapter_virtual_machines" : { - "link" : "/pve-docs/chapter-qm.html#chapter_virtual_machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "chapter_vzdump" : { - "link" : "/pve-docs/chapter-vzdump.html#chapter_vzdump", - "title" : "Backup and Restore" - }, - "chapter_zfs" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_zfs", - "title" : "ZFS on Linux" - }, - "datacenter_configuration_file" : { - "link" : "/pve-docs/pve-admin-guide.html#datacenter_configuration_file", - "title" : "Datacenter Configuration" - }, - "getting_help" : { - "link" : "/pve-docs/pve-admin-guide.html#getting_help", - "title" : "Getting Help" - }, - "gui_my_settings" : { - "link" : "/pve-docs/chapter-pve-gui.html#gui_my_settings", - "subtitle" : "My Settings", - "title" : "Graphical User Interface" - }, - "ha_manager_fencing" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_fencing", - "subtitle" : "Fencing", - "title" : "High Availability" - }, - "ha_manager_groups" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_groups", - "subtitle" : "Groups", - "title" : "High Availability" - }, - "ha_manager_resource_config" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resource_config", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "ha_manager_resources" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resources", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "pct_configuration" : { - "link" : "/pve-docs/chapter-pct.html#pct_configuration", - "subtitle" : "Configuration", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_images" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_images", - "subtitle" : "Container Images", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_network" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_network", - "subtitle" : "Network", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_storage" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_storage", - "subtitle" : "Container Storage", - "title" : "Proxmox Container Toolkit" - }, - "pct_cpu" : { - "link" : "/pve-docs/chapter-pct.html#pct_cpu", - "subtitle" : "CPU", - "title" : "Proxmox Container Toolkit" - }, - "pct_general" : { - "link" : "/pve-docs/chapter-pct.html#pct_general", - "subtitle" : "General Settings", - "title" : "Proxmox Container Toolkit" - }, - "pct_memory" : { - "link" : "/pve-docs/chapter-pct.html#pct_memory", - "subtitle" : "Memory", - "title" : "Proxmox Container Toolkit" - }, - "pct_migration" : { - "link" : "/pve-docs/chapter-pct.html#pct_migration", - "subtitle" : "Migration", - "title" : "Proxmox Container Toolkit" - }, - "pct_options" : { - "link" : "/pve-docs/chapter-pct.html#pct_options", - "subtitle" : "Options", - "title" : "Proxmox Container Toolkit" - }, - "pct_snapshots" : { - "link" : "/pve-docs/chapter-pct.html#pct_snapshots", - "subtitle" : "Snapshots", - "title" : "Proxmox Container Toolkit" - }, - "pct_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-pct.html#pct_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Containers", - "title" : "Proxmox Container Toolkit" - }, - "pve_admin_guide" : { - "link" : "/pve-docs/pve-admin-guide.html", - "title" : "Proxmox VE Administration Guide" - }, - "pve_ceph_install" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_install", - "subtitle" : "Installation of Ceph Packages", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_osds" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_osds", - "subtitle" : "Creating Ceph OSDs", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_ceph_pools" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_pools", - "subtitle" : "Creating Ceph Pools", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pve_documentation_index" : { - "link" : "/pve-docs/index.html", - "title" : "Proxmox VE Documentation Index" - }, - "pve_firewall_cluster_wide_setup" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_cluster_wide_setup", - "subtitle" : "Cluster Wide Setup", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_host_specific_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_host_specific_configuration", - "subtitle" : "Host Specific Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_aliases" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_aliases", - "subtitle" : "IP Aliases", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_sets" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_sets", - "subtitle" : "IP Sets", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_vm_container_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_vm_container_configuration", - "subtitle" : "VM/Container Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_service_daemons" : { - "link" : "/pve-docs/index.html#_service_daemons", - "title" : "Service Daemons" - }, - "pveceph_fs" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs", - "subtitle" : "CephFS", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pveceph_fs_create" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs_create", - "subtitle" : "Create a CephFS", - "title" : "Manage Ceph Services on Proxmox VE Nodes" - }, - "pvecm_create_cluster" : { - "link" : "/pve-docs/chapter-pvecm.html#pvecm_create_cluster", - "subtitle" : "Create the Cluster", - "title" : "Cluster Manager" - }, - "pvesr_schedule_time_format" : { - "link" : "/pve-docs/chapter-pvesr.html#pvesr_schedule_time_format", - "subtitle" : "Schedule Format", - "title" : "Storage Replication" - }, - "pveum_authentication_realms" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_authentication_realms", - "subtitle" : "Authentication Realms", - "title" : "User Management" - }, - "pveum_groups" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_groups", - "subtitle" : "Groups", - "title" : "User Management" - }, - "pveum_permission_management" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_permission_management", - "subtitle" : "Permission Management", - "title" : "User Management" - }, - "pveum_pools" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_pools", - "subtitle" : "Pools", - "title" : "User Management" - }, - "pveum_roles" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_roles", - "subtitle" : "Roles", - "title" : "User Management" - }, - "pveum_tfa_auth" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_tfa_auth", - "subtitle" : "Two factor authentication", - "title" : "User Management" - }, - "pveum_users" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_users", - "subtitle" : "Users", - "title" : "User Management" - }, - "qm_bios_and_uefi" : { - "link" : "/pve-docs/chapter-qm.html#qm_bios_and_uefi", - "subtitle" : "BIOS and UEFI", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cloud_init" : { - "link" : "/pve-docs/chapter-qm.html#qm_cloud_init", - "title" : "Cloud-Init Support" - }, - "qm_copy_and_clone" : { - "link" : "/pve-docs/chapter-qm.html#qm_copy_and_clone", - "subtitle" : "Copies and Clones", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cpu" : { - "link" : "/pve-docs/chapter-qm.html#qm_cpu", - "subtitle" : "CPU", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_general_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_general_settings", - "subtitle" : "General Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_hard_disk" : { - "link" : "/pve-docs/chapter-qm.html#qm_hard_disk", - "subtitle" : "Hard Disk", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_memory" : { - "link" : "/pve-docs/chapter-qm.html#qm_memory", - "subtitle" : "Memory", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_migration" : { - "link" : "/pve-docs/chapter-qm.html#qm_migration", - "subtitle" : "Migration", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_network_device" : { - "link" : "/pve-docs/chapter-qm.html#qm_network_device", - "subtitle" : "Network Device", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_options" : { - "link" : "/pve-docs/chapter-qm.html#qm_options", - "subtitle" : "Options", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_os_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_os_settings", - "subtitle" : "OS Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_pci_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_pci_passthrough", - "title" : "PCI(e) Passthrough" - }, - "qm_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-qm.html#qm_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Virtual Machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_system_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_system_settings", - "subtitle" : "System Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_usb_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_usb_passthrough", - "subtitle" : "USB Passthrough", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_virtual_machines_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_virtual_machines_settings", - "subtitle" : "Virtual Machines Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "storage_cephfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cephfs", - "title" : "Ceph Filesystem (CephFS)" - }, - "storage_cifs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cifs", - "title" : "CIFS Backend" - }, - "storage_directory" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_directory", - "title" : "Directory Backend" - }, - "storage_glusterfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_glusterfs", - "title" : "GlusterFS Backend" - }, - "storage_lvm" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvm", - "title" : "LVM Backend" - }, - "storage_lvmthin" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvmthin", - "title" : "LVM thin Backend" - }, - "storage_nfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_nfs", - "title" : "NFS Backend" - }, - "storage_open_iscsi" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_open_iscsi", - "title" : "Open-iSCSI initiator" - }, - "storage_zfspool" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_zfspool", - "title" : "Local ZFS Pool Backend" - }, - "sysadmin_certificate_management" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_certificate_management", - "title" : "Certificate Management" - }, - "sysadmin_network_configuration" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_network_configuration", - "title" : "Network Configuration" - } -}; -Ext.ns('PVE'); - -// 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)) { - var console = { - log: function() {} - }; -} -console.log("Starting PVE Manager"); - -Ext.Ajax.defaultHeaders = { - 'Accept': 'application/json' -}; - -/*jslint confusion: true */ -Ext.define('PVE.Utils', { utilities: { - - // this singleton contains miscellaneous utilities - - toolkit: undefined, // (extjs|touch), set inside Toolkit.js - - bus_match: /^(ide|sata|virtio|scsi)\d+$/, - - log_severity_hash: { - 0: "panic", - 1: "alert", - 2: "critical", - 3: "error", - 4: "warning", - 5: "notice", - 6: "info", - 7: "debug" - }, - - support_level_hash: { - 'c': gettext('Community'), - 'b': gettext('Basic'), - 's': gettext('Standard'), - 'p': gettext('Premium') - }, - - noSubKeyHtml: 'You do not have a valid subscription for this server. Please visit www.proxmox.com to get a list of available options.', - - kvm_ostypes: { - 'Linux': [ - { desc: '5.x - 2.6 Kernel', val: 'l26' }, - { desc: '2.4 Kernel', val: 'l24' } - ], - 'Microsoft Windows': [ - { desc: '10/2016', val: 'win10' }, - { desc: '8.x/2012/2012r2', val: 'win8' }, - { desc: '7/2008r2', val: 'win7' }, - { desc: 'Vista/2008', val: 'w2k8' }, - { desc: 'XP/2003', val: 'wxp' }, - { desc: '2000', val: 'w2k' } - ], - 'Solaris Kernel': [ - { desc: '-', val: 'solaris'} - ], - 'Other': [ - { desc: '-', val: 'other'} - ] - }, - - get_health_icon: function(state, circle) { - if (circle === undefined) { - circle = false; - } - - if (state === undefined) { - state = 'uknown'; - } - - var icon = 'faded fa-question'; - switch(state) { - case 'good': - icon = 'good fa-check'; - break; - case 'old': - icon = 'warning fa-refresh'; - break; - case 'warning': - icon = 'warning fa-exclamation'; - break; - case 'critical': - icon = 'critical fa-times'; - break; - default: break; - } - - if (circle) { - icon += '-circle'; - } - - return icon; - }, - - parse_ceph_version: function(service) { - if (service.ceph_version_short) { - return service.ceph_version_short; - } - - if (service.ceph_version) { - var match = service.ceph_version.match(/version (\d+(\.\d+)*)/); - if (match) { - return match[1]; - } - } - - return undefined; - }, - - compare_ceph_versions: function(a, b) { - if (a === b) { - return 0; - } - let avers = a.toString().split('.'); - let bvers = b.toString().split('.'); - - while (true) { - let av = avers.shift(); - let bv = bvers.shift(); - - if (av === undefined && bv === undefined) { - return 0; - } else if (av === undefined) { - return -1; - } else if (bv === undefined) { - return 1; - } else { - let diff = parseInt(av, 10) - parseInt(bv, 10); - if (diff != 0) return diff; - // else we need to look at the next parts - } - } - - }, - - get_ceph_icon_html: function(health, fw) { - var state = PVE.Utils.map_ceph_health[health]; - var cls = PVE.Utils.get_health_icon(state); - if (fw) { - cls += ' fa-fw'; - } - return " "; - }, - - map_ceph_health: { - 'HEALTH_OK':'good', - 'HEALTH_OLD':'old', - 'HEALTH_WARN':'warning', - 'HEALTH_ERR':'critical' - }, - - render_ceph_health: function(healthObj) { - var state = { - iconCls: PVE.Utils.get_health_icon(), - text: '' - }; - - if (!healthObj || !healthObj.status) { - return state; - } - - var health = PVE.Utils.map_ceph_health[healthObj.status]; - - state.iconCls = PVE.Utils.get_health_icon(health, true); - state.text = healthObj.status; - - return state; - }, - - render_zfs_health: function(value) { - if (typeof value == 'undefined'){ - return ""; - } - var iconCls = 'question-circle'; - switch (value) { - case 'AVAIL': - case 'ONLINE': - iconCls = 'check-circle good'; - break; - case 'REMOVED': - case 'DEGRADED': - iconCls = 'exclamation-circle warning'; - break; - case 'UNAVAIL': - case 'FAULTED': - case 'OFFLINE': - iconCls = 'times-circle critical'; - break; - default: //unknown - } - - return ' ' + value; - - }, - - get_kvm_osinfo: function(value) { - var info = { base: 'Other' }; // default - if (value) { - Ext.each(Object.keys(PVE.Utils.kvm_ostypes), function(k) { - Ext.each(PVE.Utils.kvm_ostypes[k], function(e) { - if (e.val === value) { - info = { desc: e.desc, base: k }; - } - }); - }); - } - return info; - }, - - render_kvm_ostype: function (value) { - var osinfo = PVE.Utils.get_kvm_osinfo(value); - if (osinfo.desc && osinfo.desc !== '-') { - return osinfo.base + ' ' + osinfo.desc; - } else { - return osinfo.base; - } - }, - - render_hotplug_features: function (value) { - var fa = []; - - if (!value || (value === '0')) { - return gettext('Disabled'); - } - - if (value === '1') { - value = 'disk,network,usb'; - } - - Ext.each(value.split(','), function(el) { - if (el === 'disk') { - fa.push(gettext('Disk')); - } else if (el === 'network') { - fa.push(gettext('Network')); - } else if (el === 'usb') { - fa.push('USB'); - } else if (el === 'memory') { - fa.push(gettext('Memory')); - } else if (el === 'cpu') { - fa.push(gettext('CPU')); - } else { - fa.push(el); - } - }); - - return fa.join(', '); - }, - - render_qga_features: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (' + Proxmox.Utils.disabledText + ')'; - } - var props = PVE.Parser.parsePropertyString(value, 'enabled'); - if (!PVE.Parser.parseBoolean(props.enabled)) { - return Proxmox.Utils.disabledText; - } - - delete props.enabled; - var agentstring = Proxmox.Utils.enabledText; - - Ext.Object.each(props, function(key, value) { - var keystring = '' ; - agentstring += ', ' + key + ': '; - - if (PVE.Parser.parseBoolean(value)) { - agentstring += Proxmox.Utils.enabledText; - } else { - agentstring += Proxmox.Utils.disabledText; - } - }); - - return agentstring; - }, - - render_qemu_machine: function(value) { - return value || (Proxmox.Utils.defaultText + ' (i440fx)'); - }, - - render_qemu_bios: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (SeaBIOS)'; - } else if (value === 'seabios') { - return "SeaBIOS"; - } else if (value === 'ovmf') { - return "OVMF (UEFI)"; - } else { - return value; - } - }, - - render_dc_ha_opts: function(value) { - if (!value) { - return Proxmox.Utils.defaultText; - } else { - return PVE.Parser.printPropertyString(value); - } - }, - render_as_property_string: function(value) { - return (!value) ? Proxmox.Utils.defaultText - : PVE.Parser.printPropertyString(value); - }, - - render_scsihw: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (LSI 53C895A)'; - } else if (value === 'lsi') { - return 'LSI 53C895A'; - } else if (value === 'lsi53c810') { - return 'LSI 53C810'; - } else if (value === 'megasas') { - return 'MegaRAID SAS 8708EM2'; - } else if (value === 'virtio-scsi-pci') { - return 'VirtIO SCSI'; - } else if (value === 'virtio-scsi-single') { - return 'VirtIO SCSI single'; - } else if (value === 'pvscsi') { - return 'VMware PVSCSI'; - } else { - return value; - } - }, - - // fixme: auto-generate this - // for now, please keep in sync with PVE::Tools::kvmkeymaps - kvm_keymaps: { - //ar: 'Arabic', - da: 'Danish', - de: 'German', - 'de-ch': 'German (Swiss)', - 'en-gb': 'English (UK)', - 'en-us': 'English (USA)', - es: 'Spanish', - //et: 'Estonia', - fi: 'Finnish', - //fo: 'Faroe Islands', - fr: 'French', - 'fr-be': 'French (Belgium)', - 'fr-ca': 'French (Canada)', - 'fr-ch': 'French (Swiss)', - //hr: 'Croatia', - hu: 'Hungarian', - is: 'Icelandic', - it: 'Italian', - ja: 'Japanese', - lt: 'Lithuanian', - //lv: 'Latvian', - mk: 'Macedonian', - nl: 'Dutch', - //'nl-be': 'Dutch (Belgium)', - no: 'Norwegian', - pl: 'Polish', - pt: 'Portuguese', - 'pt-br': 'Portuguese (Brazil)', - //ru: 'Russian', - sl: 'Slovenian', - sv: 'Swedish', - //th: 'Thai', - tr: 'Turkish' - }, - - kvm_vga_drivers: { - std: gettext('Standard VGA'), - vmware: gettext('VMware compatible'), - qxl: 'SPICE', - qxl2: 'SPICE dual monitor', - qxl3: 'SPICE three monitors', - qxl4: 'SPICE four monitors', - serial0: gettext('Serial terminal') + ' 0', - serial1: gettext('Serial terminal') + ' 1', - serial2: gettext('Serial terminal') + ' 2', - serial3: gettext('Serial terminal') + ' 3', - virtio: 'VirtIO-GPU', - none: Proxmox.Utils.noneText - }, - - render_kvm_language: function (value) { - if (!value || value === '__default__') { - return Proxmox.Utils.defaultText; - } - var text = PVE.Utils.kvm_keymaps[value]; - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_keymap_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_language('')]]; - Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) { - data.push([key, PVE.Utils.render_kvm_language(value)]); - }); - - return data; - }, - - console_map: { - '__default__': Proxmox.Utils.defaultText + ' (xterm.js)', - 'vv': 'SPICE (remote-viewer)', - 'html5': 'HTML5 (noVNC)', - 'xtermjs': 'xterm.js' - }, - - render_console_viewer: function(value) { - value = value || '__default__'; - if (PVE.Utils.console_map[value]) { - return PVE.Utils.console_map[value]; - } - return value; - }, - - console_viewer_array: function() { - return Ext.Array.map(Object.keys(PVE.Utils.console_map), function(v) { - return [v, PVE.Utils.render_console_viewer(v)]; - }); - }, - - render_kvm_vga_driver: function (value) { - if (!value) { - return Proxmox.Utils.defaultText; - } - var vga = PVE.Parser.parsePropertyString(value, 'type'); - var text = PVE.Utils.kvm_vga_drivers[vga.type]; - if (!vga.type) { - text = Proxmox.Utils.defaultText; - } - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_vga_driver_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_vga_driver('')]]; - Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) { - data.push([key, PVE.Utils.render_kvm_vga_driver(value)]); - }); - - return data; - }, - - render_kvm_startup: function(value) { - var startup = PVE.Parser.parseStartup(value); - - var res = 'order='; - if (startup.order === undefined) { - res += 'any'; - } else { - res += startup.order; - } - if (startup.up !== undefined) { - res += ',up=' + startup.up; - } - if (startup.down !== undefined) { - res += ',down=' + startup.down; - } - - return res; - }, - - extractFormActionError: function(action) { - var msg; - switch (action.failureType) { - case Ext.form.action.Action.CLIENT_INVALID: - msg = gettext('Form fields may not be submitted with invalid values'); - break; - case Ext.form.action.Action.CONNECT_FAILURE: - msg = gettext('Connection error'); - var resp = action.response; - if (resp.status && resp.statusText) { - msg += " " + resp.status + ": " + resp.statusText; - } - break; - case Ext.form.action.Action.LOAD_FAILURE: - case Ext.form.action.Action.SERVER_INVALID: - msg = Proxmox.Utils.extractRequestError(action.result, true); - break; - } - return msg; - }, - - format_duration_short: function(ut) { - - if (ut < 60) { - return ut.toFixed(1) + 's'; - } - - if (ut < 3600) { - var mins = ut / 60; - return mins.toFixed(1) + 'm'; - } - - if (ut < 86400) { - var hours = ut / 3600; - return hours.toFixed(1) + 'h'; - } - - var days = ut / 86400; - return days.toFixed(1) + 'd'; - }, - - contentTypes: { - 'images': gettext('Disk image'), - 'backup': gettext('VZDump backup file'), - 'vztmpl': gettext('Container template'), - 'iso': gettext('ISO image'), - 'rootdir': gettext('Container'), - 'snippets': gettext('Snippets') - }, - - storageSchema: { - dir: { - name: Proxmox.Utils.directoryText, - ipanel: 'DirInputPanel', - faIcon: 'folder' - }, - lvm: { - name: 'LVM', - ipanel: 'LVMInputPanel', - faIcon: 'folder' - }, - lvmthin: { - name: 'LVM-Thin', - ipanel: 'LvmThinInputPanel', - faIcon: 'folder' - }, - nfs: { - name: 'NFS', - ipanel: 'NFSInputPanel', - faIcon: 'building' - }, - cifs: { - name: 'CIFS', - ipanel: 'CIFSInputPanel', - faIcon: 'building' - }, - glusterfs: { - name: 'GlusterFS', - ipanel: 'GlusterFsInputPanel', - faIcon: 'building' - }, - iscsi: { - name: 'iSCSI', - ipanel: 'IScsiInputPanel', - faIcon: 'building' - }, - cephfs: { - name: 'CephFS', - ipanel: 'CephFSInputPanel', - faIcon: 'building' - }, - pvecephfs: { - name: 'CephFS (PVE)', - ipanel: 'CephFSInputPanel', - hideAdd: true, - faIcon: 'building' - }, - rbd: { - name: 'RBD', - ipanel: 'RBDInputPanel', - faIcon: 'building' - }, - pveceph: { - name: 'RBD (PVE)', - ipanel: 'RBDInputPanel', - hideAdd: true, - faIcon: 'building' - }, - zfs: { - name: 'ZFS over iSCSI', - ipanel: 'ZFSInputPanel', - faIcon: 'building' - }, - zfspool: { - name: 'ZFS', - ipanel: 'ZFSPoolInputPanel', - faIcon: 'folder' - }, - drbd: { - name: 'DRBD', - hideAdd: true - } - }, - - format_storage_type: function(value, md, record) { - if (value === 'rbd') { - value = (!record || record.get('monhost') ? 'rbd' : 'pveceph'); - } else if (value === 'cephfs') { - value = (!record || record.get('monhost') ? 'cephfs' : 'pvecephfs'); - } - - var schema = PVE.Utils.storageSchema[value]; - if (schema) { - return schema.name; - } - return Proxmox.Utils.unknownText; - }, - - format_ha: function(value) { - var text = Proxmox.Utils.noneText; - - if (value.managed) { - text = value.state || Proxmox.Utils.noneText; - - text += ', ' + Proxmox.Utils.groupText + ': '; - text += value.group || Proxmox.Utils.noneText; - } - - return text; - }, - - format_content_types: function(value) { - return value.split(',').sort().map(function(ct) { - return PVE.Utils.contentTypes[ct] || ct; - }).join(', '); - }, - - render_storage_content: function(value, metaData, record) { - var data = record.data; - if (Ext.isNumber(data.channel) && - Ext.isNumber(data.id) && - Ext.isNumber(data.lun)) { - return "CH " + - Ext.String.leftPad(data.channel,2, '0') + - " ID " + data.id + " LUN " + data.lun; - } - return data.volid.replace(/^.*:(.*\/)?/,''); - }, - - render_serverity: function (value) { - return PVE.Utils.log_severity_hash[value] || value; - }, - - render_cpu: function(value, metaData, record, rowIndex, colIndex, store) { - - if (!(record.data.uptime && Ext.isNumeric(value))) { - return ''; - } - - var maxcpu = record.data.maxcpu || 1; - - if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) { - return ''; - } - - var per = value * 100; - - return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU'); - }, - - render_size: function(value, metaData, record, rowIndex, colIndex, store) { - /*jslint confusion: true */ - - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value); - }, - - render_bandwidth: function(value) { - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value) + '/s'; - }, - - render_timestamp_human_readable: function(value) { - return Ext.Date.format(new Date(value * 1000), 'l d F Y H:i:s'); - }, - - render_duration: function(value) { - if (value === undefined) { - return '-'; - } - return PVE.Utils.format_duration_short(value); - }, - - calculate_mem_usage: function(data) { - if (!Ext.isNumeric(data.mem) || - data.maxmem === 0 || - data.uptime < 1) { - return -1; - } - - return (data.mem / data.maxmem); - }, - - render_mem_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - if (value > 1 ) { - // we got no percentage but bytes - var mem = value; - var maxmem = record.data.maxmem; - if (!record.data.uptime || - maxmem === 0 || - !Ext.isNumeric(mem)) { - return ''; - } - - return ((mem*100)/maxmem).toFixed(1) + " %"; - } - return (value*100).toFixed(1) + " %"; - }, - - render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var mem = value; - var maxmem = record.data.maxmem; - - if (!record.data.uptime) { - return ''; - } - - if (!(Ext.isNumeric(mem) && maxmem)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - calculate_disk_usage: function(data) { - - if (!Ext.isNumeric(data.disk) || - data.type === 'qemu' || - (data.type === 'lxc' && data.uptime === 0) || - data.maxdisk === 0) { - return -1; - } - - return (data.disk / data.maxdisk); - }, - - render_disk_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - - return (value * 100).toFixed(1) + " %"; - }, - - render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var disk = value; - var maxdisk = record.data.maxdisk; - var type = record.data.type; - - if (!Ext.isNumeric(disk) || - type === 'qemu' || - maxdisk === 0 || - (type === 'lxc' && record.data.uptime === 0)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - get_object_icon_class: function(type, record) { - var status = ''; - var objType = type; - - if (type === 'type') { - // for folder view - objType = record.groupbyid; - } else if (record.template) { - // templates - objType = 'template'; - status = type; - } else { - // everything else - status = record.status + ' ha-' + record.hastate; - } - - if (record.lock) { - status += ' locked lock-' + record.lock; - } - - var defaults = PVE.tree.ResourceTree.typeDefaults[objType]; - if (defaults && defaults.iconCls) { - var retVal = defaults.iconCls + ' ' + status; - return retVal; - } - - return ''; - }, - - render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) { - - var cls = PVE.Utils.get_object_icon_class(value,record.data); - - var fa = ' '; - return fa + value; - }, - - render_support_level: function(value, metaData, record) { - return PVE.Utils.support_level_hash[value] || '-'; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - /* render functions for new status panel */ - - render_usage: function(val) { - return (val*100).toFixed(2) + '%'; - }, - - render_cpu_usage: function(val, max) { - return Ext.String.format(gettext('{0}% of {1}') + - ' ' + gettext('CPU(s)'), (val*100).toFixed(2), max); - }, - - render_size_usage: function(val, max) { - if (max === 0) { - return gettext('N/A'); - } - return (val*100/max).toFixed(2) + '% '+ '(' + - Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(val), PVE.Utils.render_size(max)) + ')'; - }, - - /* this is different for nodes */ - render_node_cpu_usage: function(value, record) { - return PVE.Utils.render_cpu_usage(value, record.cpus); - }, - - /* this is different for nodes */ - render_node_size_usage: function(record) { - return PVE.Utils.render_size_usage(record.used, record.total); - }, - - render_optional_url: function(value) { - var match; - if (value && (match = value.match(/^https?:\/\//)) !== null) { - return '' + value + ''; - } - return value; - }, - - render_san: function(value) { - var names = []; - if (Ext.isArray(value)) { - value.forEach(function(val) { - if (!Ext.isNumber(val)) { - names.push(val); - } - }); - return names.join('
'); - } - return value; - }, - - render_full_name: function(firstname, metaData, record) { - var first = firstname || ''; - var last = record.data.lastname || ''; - return Ext.htmlEncode(first + " " + last); - }, - - render_u2f_error: function(error) { - var ErrorNames = { - '1': gettext('Other Error'), - '2': gettext('Bad Request'), - '3': gettext('Configuration Unsupported'), - '4': gettext('Device Ineligible'), - '5': gettext('Timeout') - }; - return "U2F Error: " + ErrorNames[error] || Proxmox.Utils.unknownText; - }, - - windowHostname: function() { - return window.location.hostname.replace(Proxmox.Utils.IP6_bracket_match, - function(m, addr, offset, original) { return addr; }); - }, - - openDefaultConsoleWindow: function(consoles, vmtype, vmid, nodename, vmname, cmd) { - var dv = PVE.Utils.defaultViewer(consoles); - PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname, cmd); - }, - - openConsoleWindow: function(viewer, vmtype, vmid, nodename, vmname, cmd) { - // kvm, lxc, shell, upgrade - - if (vmid == undefined && (vmtype === 'kvm' || vmtype === 'lxc')) { - throw "missing vmid"; - } - - if (!nodename) { - throw "no nodename specified"; - } - - if (viewer === 'html5') { - PVE.Utils.openVNCViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'xtermjs') { - Proxmox.Utils.openXtermJsViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'vv') { - var url; - var params = { proxy: PVE.Utils.windowHostname() }; - if (vmtype === 'kvm') { - url = '/nodes/' + nodename + '/qemu/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'lxc') { - url = '/nodes/' + nodename + '/lxc/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'shell') { - url = '/nodes/' + nodename + '/spiceshell'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'upgrade') { - url = '/nodes/' + nodename + '/spiceshell'; - params.upgrade = 1; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'cmd') { - url = '/nodes/' + nodename + '/spiceshell'; - params.cmd = cmd; - PVE.Utils.openSpiceViewer(url, params); - } - } else { - throw "unknown viewer type"; - } - }, - - defaultViewer: function(consoles) { - - var allowSpice, allowXtermjs; - - if (consoles === true) { - allowSpice = true; - allowXtermjs = true; - } else if (typeof consoles === 'object') { - allowSpice = consoles.spice; - allowXtermjs = !!consoles.xtermjs; - } - var dv = PVE.VersionInfo.console || 'xtermjs'; - if (dv === 'vv' && !allowSpice) { - dv = (allowXtermjs) ? 'xtermjs' : 'html5'; - } else if (dv === 'xtermjs' && !allowXtermjs) { - dv = (allowSpice) ? 'vv' : 'html5'; - } - - return dv; - }, - - openVNCViewer: function(vmtype, vmid, nodename, vmname, cmd) { - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - novnc: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - resize: 'off', - cmd: cmd - }); - var nw = window.open("?" + url, '_blank', "innerWidth=745,innerheight=427"); - if (nw) { - nw.focus(); - } - }, - - openSpiceViewer: function(url, params){ - - var downloadWithName = function(uri, name) { - var link = Ext.DomHelper.append(document.body, { - tag: 'a', - href: uri, - css : 'display:none;visibility:hidden;height:0px;' - }); - - // Note: we need to tell android the correct file name extension - // but we do not set 'download' tag for other environments, because - // It can have strange side effects (additional user prompt on firefox) - var andriod = navigator.userAgent.match(/Android/i) ? true : false; - if (andriod) { - link.download = name; - } - - if (link.fireEvent) { - link.fireEvent('onclick'); - } else { - var evt = document.createEvent("MouseEvents"); - evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null); - link.dispatchEvent(evt); - } - }; - - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - failure: function(response, opts){ - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts){ - var raw = "[virt-viewer]\n"; - Ext.Object.each(response.result.data, function(k, v) { - raw += k + "=" + v + "\n"; - }); - var url = 'data:application/x-virt-viewer;charset=UTF-8,' + - encodeURIComponent(raw); - - downloadWithName(url, "pve-spice.vv"); - } - }); - }, - - openTreeConsole: function(tree, record, item, index, e) { - e.stopEvent(); - var nodename = record.data.node; - var vmid = record.data.vmid; - var vmname = record.data.name; - if (record.data.type === 'qemu' && !record.data.template) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - let conf = response.result.data; - var consoles = { - spice: !!conf.spice, - xtermjs: !!conf.serial, - }; - PVE.Utils.openDefaultConsoleWindow(consoles, 'kvm', vmid, nodename, vmname); - } - }); - } else if (record.data.type === 'lxc' && !record.data.template) { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - }, - - // test automation helper - call_menu_handler: function(menu, text) { - - var list = menu.query('menuitem'); - - Ext.Array.each(list, function(item) { - if (item.text === text) { - if (item.handler) { - item.handler(); - return 1; - } else { - return undefined; - } - } - }); - }, - - createCmdMenu: function(v, record, item, index, event) { - event.stopEvent(); - if (!(v instanceof Ext.tree.View)) { - v.select(record); - } - var menu; - var template = !!record.data.template; - var type = record.data.type; - - if (template) { - if (type === 'qemu' || type == 'lxc') { - menu = Ext.create('PVE.menu.TemplateMenu', { - pveSelNode: record - }); - } - } else if (type === 'qemu' || - type === 'lxc' || - type === 'node') { - menu = Ext.create('PVE.' + type + '.CmdMenu', { - pveSelNode: record, - nodename: record.data.node - }); - } else { - return; - } - - menu.showAt(event.getXY()); - return menu; - }, - - // helper for deleting field which are set to there default values - delete_if_default: function(values, fieldname, default_val, create) { - if (values[fieldname] === '' || values[fieldname] === default_val) { - if (!create) { - if (values['delete']) { - values['delete'] += ',' + fieldname; - } else { - values['delete'] = fieldname; - } - } - - delete values[fieldname]; - } - }, - - loadSSHKeyFromFile: function(file, callback) { - // ssh-keygen produces 740 bytes for an average 4096 bit rsa key, with - // a user@host comment, 1420 for 8192 bits; current max is 16kbit - // assume: 740*8 for max. 32kbit (5920 byte file) - // round upwards to nearest nice number => 8192 bytes, leaves lots of comment space - if (file.size > 8192) { - Ext.Msg.alert(gettext('Error'), gettext("Invalid file size: ") + file.size); - return; - } - /*global - FileReader - */ - var reader = new FileReader(); - reader.onload = function(evt) { - callback(evt.target.result); - }; - reader.readAsText(file); - }, - - bus_counts: { ide: 4, sata: 6, scsi: 16, virtio: 16 }, - - // types is either undefined (all busses), an array of busses, or a single bus - forEachBus: function(types, func) { - var busses = Object.keys(PVE.Utils.bus_counts); - var i, j, count, cont; - - if (Ext.isArray(types)) { - busses = types; - } else if (Ext.isDefined(types)) { - busses = [ types ]; - } - - // check if we only have valid busses - for (i = 0; i < busses.length; i++) { - if (!PVE.Utils.bus_counts[busses[i]]) { - throw "invalid bus: '" + busses[i] + "'"; - } - } - - for (i = 0; i < busses.length; i++) { - count = PVE.Utils.bus_counts[busses[i]]; - for (j = 0; j < count; j++) { - cont = func(busses[i], j); - if (!cont && cont !== undefined) { - return; - } - } - } - }, - - mp_counts: { mps: 256, unused: 256 }, - - forEachMP: function(func, includeUnused) { - var i, cont; - for (i = 0; i < PVE.Utils.mp_counts.mps; i++) { - cont = func('mp', i); - if (!cont && cont !== undefined) { - return; - } - } - - if (!includeUnused) { - return; - } - - for (i = 0; i < PVE.Utils.mp_counts.unused; i++) { - cont = func('unused', i); - if (!cont && cont !== undefined) { - return; - } - } - }, - - cleanEmptyObjectKeys: function (obj) { - var propName; - for (propName in obj) { - if (obj.hasOwnProperty(propName)) { - if (obj[propName] === null || obj[propName] === undefined) { - delete obj[propName]; - } - } - } - }, - - handleStoreErrorOrMask: function(me, store, regex, callback) { - - me.mon(store, 'load', function (proxy, response, success, operation) { - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - var msg; - - if (operation.error.statusText) { - if (operation.error.statusText.match(regex)) { - callback(me, operation.error); - return; - } else { - msg = operation.error.statusText + ' (' + operation.error.status + ')'; - } - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - showCephInstallOrMask: function(container, msg, nodename, callback){ - var regex = new RegExp("not (installed|initialized)", "i"); - if (msg.match(regex)) { - if (Proxmox.UserName === 'root@pam') { - container.el.mask(); - if (!container.down('pveCephInstallWindow')){ - var isInstalled = msg.match(/not initialized/i) ? true : false; - var win = Ext.create('PVE.ceph.Install', { - nodename: nodename - }); - win.getViewModel().set('isInstalled', isInstalled); - container.add(win); - win.show(); - callback(win); - } - } else { - container.mask(Ext.String.format(gettext('{0} not installed.') + - ' ' + gettext('Log in as root to install.'), 'Ceph'), ['pve-static-mask']); - } - return true; - } else { - return false; - } - } -}, - - singleton: true, - constructor: function() { - var me = this; - Ext.apply(me, me.utilities); - } - -}); - -// ExtJS related things - -Proxmox.Utils.toolkit = 'extjs'; - -// custom PVE specific VTypes -Ext.apply(Ext.form.field.VTypes, { - - 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"', - IP64AddressList: function(v) { - var list = v.split(/[\ \,\;]+/); - var i; - for (i = 0; i < list.length; i++) { - if (list[i] == '') { - continue; - } - - if (!Proxmox.Utils.IP64_match.test(list[i])) { - return false; - } - } - - return true; - }, - IP64AddressListText: gettext('Example') + ': 192.168.1.1,192.168.1.2', - IP64AddressListMask: /[A-Fa-f0-9\,\:\.\;\ ]/ -}); - -Ext.define('PVE.form.field.Display', { - override: 'Ext.form.field.Display', - - setSubmitValue: function(value) { - // do nothing, this is only to allow generalized bindings for the: - // `me.isCreate ? 'textfield' : 'displayfield'` cases we have. - } -}); -// Some configuration values are complex strings - -// so we need parsers/generators for them. - -Ext.define('PVE.Parser', { statics: { - - // this class only contains static functions - - parseACME: function(value) { - if (!value) { - return; - } - - var res = {}; - var errors = false; - - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; //continue - } - - var match_res; - if ((match_res = p.match(/^(?:domains=)?((?:[a-zA-Z0-9\-\.]+[;, ]?)+)$/)) !== null) { - res.domains = match_res[1].split(/[;, ]/); - } else { - errors = true; - return false; - } - }); - - if (errors || !res) { - return; - } - - return res; - }, - - parseBoolean: function(value, default_value) { - if (!Ext.isDefined(value)) { - return default_value; - } - value = value.toLowerCase(); - return value === '1' || - value === 'on' || - value === 'yes' || - value === 'true'; - }, - - parsePropertyString: function(value, defaultKey) { - var res = {}, - error; - - Ext.Array.each(value.split(','), function(p) { - var kv = p.split('=', 2); - if (Ext.isDefined(kv[1])) { - res[kv[0]] = kv[1]; - } else if (Ext.isDefined(defaultKey)) { - if (Ext.isDefined(res[defaultKey])) { - error = 'defaultKey may be only defined once in propertyString'; - return false; // break - } - res[defaultKey] = kv[0]; - } else { - error = 'invalid propertyString, not a key=value pair and no defaultKey defined'; - return false; // break - } - }); - - if (error !== undefined) { - console.error(error); - return; - } - - return res; - }, - - printPropertyString: function(data, defaultKey) { - var stringparts = [], - gotDefaultKeyVal = false, - defaultKeyVal; - - Ext.Object.each(data, function(key, value) { - if (defaultKey !== undefined && key === defaultKey) { - gotDefaultKeyVal = true; - defaultKeyVal = value; - } else { - stringparts.push(key + '=' + value); - } - }); - - stringparts = stringparts.sort(); - if (gotDefaultKeyVal) { - stringparts.unshift(defaultKeyVal); - } - - return stringparts.join(','); - }, - - parseQemuNetwork: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(ne2k_pci|e1000|e1000-82540em|e1000-82544gc|e1000-82545em|vmxnet3|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i)) !== null) { - res.model = match_res[1].toLowerCase(); - if (match_res[3]) { - res.macaddr = match_res[3]; - } - } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) { - res.bridge = match_res[1]; - } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) { - res.rate = match_res[1]; - } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) { - res.tag = match_res[1]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - res.firewall = match_res[1]; - } else if ((match_res = p.match(/^link_down=(\d+)$/)) !== null) { - res.disconnect = match_res[1]; - } else if ((match_res = p.match(/^queues=(\d+)$/)) !== null) { - res.queues = match_res[1]; - } else if ((match_res = p.match(/^trunks=(\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*)$/)) !== null) { - res.trunks = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors || !res.model) { - return; - } - - return res; - }, - - printQemuNetwork: function(net) { - - var netstr = net.model; - if (net.macaddr) { - netstr += "=" + net.macaddr; - } - if (net.bridge) { - netstr += ",bridge=" + net.bridge; - if (net.tag) { - netstr += ",tag=" + net.tag; - } - if (net.firewall) { - netstr += ",firewall=" + net.firewall; - } - } - if (net.rate) { - netstr += ",rate=" + net.rate; - } - if (net.queues) { - netstr += ",queues=" + net.queues; - } - if (net.disconnect) { - netstr += ",link_down=" + net.disconnect; - } - if (net.trunks) { - netstr += ",trunks=" + net.trunks; - } - return netstr; - }, - - parseQemuDrive: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var match_res = key.match(/^([a-z]+)(\d+)$/); - if (!match_res) { - return; - } - res['interface'] = match_res[1]; - res.index = match_res[2]; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - if (k === 'cache' && v === 'off') { - v = 'none'; - } - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - return res; - }, - - printQemuDrive: function(drive) { - - var drivestr = drive.file; - - Ext.Object.each(drive, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'index' || key === 'interface') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseIPConfig: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - if ((match_res = p.match(/^ip=(\S+)$/)) !== null) { - res.ip = match_res[1]; - } else if ((match_res = p.match(/^gw=(\S+)$/)) !== null) { - res.gw = match_res[1]; - } else if ((match_res = p.match(/^ip6=(\S+)$/)) !== null) { - res.ip6 = match_res[1]; - } else if ((match_res = p.match(/^gw6=(\S+)$/)) !== null) { - res.gw6 = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printIPConfig: function(cfg) { - var c = ""; - var str = ""; - if (cfg.ip) { - str += "ip=" + cfg.ip; - c = ","; - } - if (cfg.gw) { - str += c + "gw=" + cfg.gw; - c = ","; - } - if (cfg.ip6) { - str += c + "ip6=" + cfg.ip6; - c = ","; - } - if (cfg.gw6) { - str += c + "gw6=" + cfg.gw6; - c = ","; - } - return str; - }, - - parseOpenVZNetIf: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(';'), function(item) { - if (!item || item.match(/^\s*$/)) { - return; // continue - } - - var data = {}; - Ext.Array.each(item.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - if (match_res[1] === 'bridge'){ - var bridgevlanf = match_res[2]; - var bridge_res = bridgevlanf.match(/^(vmbr(\d+))(v(\d+))?(f)?$/); - if (!bridge_res) { - errors = true; - return false; // break - } - data.bridge = bridge_res[1]; - data.tag = bridge_res[4]; - /*jslint confusion: true*/ - data.firewall = bridge_res[5] ? 1 : 0; - /*jslint confusion: false*/ - } else { - data[match_res[1]] = match_res[2]; - } - }); - - if (errors || !data.ifname) { - errors = true; - return false; // break - } - - data.raw = item; - - res[data.ifname] = data; - }); - - return errors ? undefined: res; - }, - - printOpenVZNetIf: function(netif) { - var netarray = []; - - Ext.Object.each(netif, function(iface, data) { - var tmparray = []; - Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname' , 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) { - var value = data[key]; - if (key === 'bridge'){ - if(data.tag){ - value = value + 'v' + data.tag; - } - if (data.firewall){ - value = value + 'f'; - } - } - if (value) { - tmparray.push(key + '=' + value); - } - - }); - netarray.push(tmparray.join(',')); - }); - - return netarray.join(';'); - }, - - parseLxcNetwork: function(value) { - if (!value) { - return; - } - - var data = {}; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|tag|rate)=(\S+)$/); - if (match_res) { - data[match_res[1]] = match_res[2]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - data.firewall = PVE.Parser.parseBoolean(match_res[1]); - } else { - // todo: simply ignore errors ? - return; // continue - } - }); - - return data; - }, - - printLxcNetwork: function(data) { - var tmparray = []; - Ext.Array.each(['bridge', 'hwaddr', 'mtu', 'name', 'ip', - 'gw', 'ip6', 'gw6', 'firewall', 'tag'], function(key) { - var value = data[key]; - if (value) { - tmparray.push(key + '=' + value); - } - }); - - /*jslint confusion: true*/ - if (data.rate > 0) { - tmparray.push('rate=' + data.rate); - } - /*jslint confusion: false*/ - return tmparray.join(','); - }, - - parseLxcMountPoint: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(.+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - var m = res.file.match(/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):/i); - if (m) { - res.storage = m[1]; - res.type = 'volume'; - } else if (res.file.match(/^\/dev\//)) { - res.type = 'device'; - } else { - res.type = 'bind'; - } - - return res; - }, - - printLxcMountPoint: function(mp) { - var drivestr = mp.file; - - Ext.Object.each(mp, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'type' || key === 'storage') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseStartup: function(value) { - if (value === undefined) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) { - res.order = match_res[2]; - } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) { - res.up = match_res[1]; - } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) { - res.down = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printStartup: function(startup) { - var arr = []; - if (startup.order !== undefined && startup.order !== '') { - arr.push('order=' + startup.order); - } - if (startup.up !== undefined && startup.up !== '') { - arr.push('up=' + startup.up); - } - if (startup.down !== undefined && startup.down !== '') { - arr.push('down=' + startup.down); - } - - return arr.join(','); - }, - - parseQemuSmbios1: function(value) { - var res = value.split(',').reduce(function (accumulator, currentValue) { - var splitted = currentValue.split(new RegExp("=(.+)")); - accumulator[splitted[0]] = splitted[1]; - return accumulator; - }, {}); - - if (PVE.Parser.parseBoolean(res.base64, false)) { - Ext.Object.each(res, function(key, value) { - if (key === 'uuid') { return; } - res[key] = Ext.util.Base64.decode(value); - }); - } - - return res; - }, - - printQemuSmbios1: function(data) { - - var datastr = ''; - var base64 = false; - Ext.Object.each(data, function(key, value) { - if (value === '') { return; } - if (key === 'uuid') { - datastr += (datastr !== '' ? ',' : '') + key + '=' + value; - } else { - // values should be base64 encoded from now on, mark config strings correspondingly - if (!base64) { - base64 = true; - datastr += (datastr !== '' ? ',' : '') + 'base64=1'; - } - datastr += (datastr !== '' ? ',' : '') + key + '=' + Ext.util.Base64.encode(value); - } - }); - - return datastr; - }, - - parseTfaConfig: function(value) { - var res = {}; - - Ext.Array.each(value.split(','), function(p) { - var kva = p.split('=', 2); - res[kva[0]] = kva[1]; - }); - - return res; - }, - - parseTfaType: function(value) { - /*jslint confusion: true*/ - var match; - if (!value || !value.length) { - return undefined; - } else if (value === 'x!oath') { - return 'totp'; - } else if (!!(match = value.match(/^x!(.+)$/))) { - return match[1]; - } else { - return 1; - } - }, - - parseQemuCpu: function(value) { - if (!value) { - return {}; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - if (!p.match(/\=/)) { - if (Ext.isDefined(res.cpu)) { - errors = true; - return false; // break - } - res.cputype = p; - return; // continue - } - - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - - var k = match_res[1]; - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - res[k] = match_res[2]; - }); - - if (errors || !res.cputype) { - return; - } - - return res; - }, - - printQemuCpu: function(cpu) { - var cpustr = cpu.cputype; - var optstr = ''; - - Ext.Object.each(cpu, function(key, value) { - if (!Ext.isDefined(value) || key === 'cputype') { - return; // continue - } - optstr += ',' + key + '=' + value; - }); - - if (!cpustr) { - if (optstr) { - return 'kvm64' + optstr; - } - return; - } - - return cpustr + optstr; - }, - - parseSSHKey: function(key) { - // |--- options can have quotes--| type key comment - var keyre = /^(?:((?:[^\s"]|\"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/; - var typere = /^(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)$/; - - var m = key.match(keyre); - if (!m) { - return null; - } - if (m.length < 3 || !m[2]) { // [2] is always either type or key - return null; - } - if (m[1] && m[1].match(typere)) { - return { - type: m[1], - key: m[2], - comment: m[3] - }; - } - if (m[2].match(typere)) { - return { - options: m[1], - type: m[2], - key: m[3], - comment: m[4] - }; - } - return null; - } -}}); -/* This state provider keeps part of the state inside - * the browser history. - * - * We compress (shorten) url using dictionary based compression - * i.e. use column separated list instead of url encoded hash: - * #v\d* version/format - * := indicates string values - * :\d+ lookup value in dictionary hash - * #v1:=value1:5:=value2:=value3:... -*/ - -Ext.define('PVE.StateProvider', { - extend: 'Ext.state.LocalStorageProvider', - - // private - setHV: function(name, newvalue, fireEvents) { - var me = this; - - var changes = false; - var oldtext = Ext.encode(me.UIState[name]); - var newtext = Ext.encode(newvalue); - if (newtext != oldtext) { - changes = true; - me.UIState[name] = newvalue; - //console.log("changed old " + name + " " + oldtext); - //console.log("changed new " + name + " " + newtext); - if (fireEvents) { - me.fireEvent("statechange", me, name, { value: newvalue }); - } - } - return changes; - }, - - // private - hslist: [ - // order is important for notifications - // [ name, default ] - ['view', 'server'], - ['rid', 'root'], - ['ltab', 'tasks'], - ['nodetab', ''], - ['storagetab', ''], - ['pooltab', ''], - ['kvmtab', ''], - ['lxctab', ''], - ['dctab', ''] - ], - - hprefix: 'v1', - - compDict: { - cloudinit: 52, - replication: 51, - system: 50, - monitor: 49, - 'ha-fencing': 48, - 'ha-groups': 47, - 'ha-resources': 46, - 'ceph-log': 45, - 'ceph-crushmap':44, - 'ceph-pools': 43, - 'ceph-osdtree': 42, - 'ceph-disklist': 41, - 'ceph-monlist': 40, - 'ceph-config': 39, - ceph: 38, - 'firewall-fwlog': 37, - 'firewall-options': 36, - 'firewall-ipset': 35, - 'firewall-aliases': 34, - 'firewall-sg': 33, - firewall: 32, - apt: 31, - members: 30, - snapshot: 29, - ha: 28, - support: 27, - pools: 26, - syslog: 25, - ubc: 24, - initlog: 23, - openvz: 22, - backup: 21, - resources: 20, - content: 19, - root: 18, - domains: 17, - roles: 16, - groups: 15, - users: 14, - time: 13, - dns: 12, - network: 11, - services: 10, - options: 9, - console: 8, - hardware: 7, - permissions: 6, - summary: 5, - tasks: 4, - clog: 3, - storage: 2, - folder: 1, - server: 0 - }, - - decodeHToken: function(token) { - var me = this; - - var state = {}; - if (!token) { - Ext.Array.each(me.hslist, function(rec) { - state[rec[0]] = rec[1]; - }); - return state; - } - - // return Ext.urlDecode(token); - - var items = token.split(':'); - var prefix = items.shift(); - - if (prefix != me.hprefix) { - return me.decodeHToken(); - } - - Ext.Array.each(me.hslist, function(rec) { - var value = items.shift(); - if (value) { - if (value[0] === '=') { - value = decodeURIComponent(value.slice(1)); - } else { - Ext.Object.each(me.compDict, function(key, cv) { - if (value == cv) { - value = key; - return false; - } - }); - } - } - state[rec[0]] = value; - }); - - return state; - }, - - encodeHToken: function(state) { - var me = this; - - // return Ext.urlEncode(state); - - var ctoken = me.hprefix; - Ext.Array.each(me.hslist, function(rec) { - var value = state[rec[0]]; - if (!Ext.isDefined(value)) { - value = rec[1]; - } - value = encodeURIComponent(value); - if (!value) { - ctoken += ':'; - } else { - var comp = me.compDict[value]; - if (Ext.isDefined(comp)) { - ctoken += ":" + comp; - } else { - ctoken += ":=" + value; - } - } - }); - - return ctoken; - }, - - constructor: function(config){ - var me = this; - - me.callParent([config]); - - me.UIState = me.decodeHToken(); // set default - - var history_change_cb = function(token) { - //console.log("HC " + token); - if (!token) { - var res = window.confirm(gettext('Are you sure you want to navigate away from this page?')); - if (res){ - // process text value and close... - Ext.History.back(); - } else { - Ext.History.forward(); - } - return; - } - - var newstate = me.decodeHToken(token); - Ext.Array.each(me.hslist, function(rec) { - if (typeof newstate[rec[0]] == "undefined") { - return; - } - me.setHV(rec[0], newstate[rec[0]], true); - }); - }; - - var start_token = Ext.History.getToken(); - if (start_token) { - history_change_cb(start_token); - } else { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - - Ext.History.on('change', history_change_cb); - }, - - get: function(name, defaultValue){ - /*jslint confusion: true */ - var me = this; - var data; - - if (typeof me.UIState[name] != "undefined") { - data = { value: me.UIState[name] }; - } else { - data = me.callParent(arguments); - if (!data && name === 'GuiCap') { - data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {} }; - } - } - - //console.log("GET " + name + " " + Ext.encode(data)); - return data; - }, - - clear: function(name){ - var me = this; - - if (typeof me.UIState[name] != "undefined") { - me.UIState[name] = null; - } - - me.callParent(arguments); - }, - - set: function(name, value, fireevent){ - var me = this; - - //console.log("SET " + name + " " + Ext.encode(value)); - if (typeof me.UIState[name] != "undefined") { - var newvalue = value ? value.value : null; - if (me.setHV(name, newvalue, fireevent)) { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - } else { - me.callParent(arguments); - } - } -}); -Ext.define('PVE.menu.Item', { - extend: 'Ext.menu.Item', - alias: 'widget.pveMenuItem', - - // set to wrap the handler callback in a confirm dialog showing this text - confirmMsg: false, - - // set to focus 'No' instead of 'Yes' button and show a warning symbol - dangerous: false, - - initComponent: function() { - var me = this; - - if (me.handler) { - me.setHandler(me.handler, me.scope); - } - - me.callParent(); - }, - - setHandler: function(fn, scope) { - var me = this; - me.scope = scope; - me.handler = function(button, e) { - var rec, msg; - if (me.confirmMsg) { - msg = me.confirmMsg; - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn === 'yes') { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - } - }); - } else { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - }; - } -}); -Ext.define('PVE.menu.TemplateMenu', { - extend: 'Ext.menu.Menu', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var guestType = me.pveSelNode.data.type; - if (guestType !== 'qemu' && guestType != 'lxc') { - throw "invalid guest type"; - } - - var vmname = me.pveSelNode.data.name; - - var template = me.pveSelNode.data.template; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - me.title = (guestType === 'qemu' ? 'VM ' : 'CT ') + vmid; - - me.items = [ - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: guestType, - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - handler: function() { - var win = Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: template - }); - win.show(); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.button.ConsoleButton', { - extend: 'Ext.button.Split', - alias: 'widget.pveConsoleButton', - - consoleType: 'shell', // one of 'shell', 'kvm', 'lxc', 'upgrade', 'cmd' - - cmd: undefined, - - consoleName: undefined, - - iconCls: 'fa fa-terminal', - - enableSpice: true, - enableXtermjs: true, - - nodename: undefined, - - vmid: 0, - - text: gettext('Console'), - - setEnableSpice: function(enable){ - var me = this; - - me.enableSpice = enable; - me.down('#spicemenu').setDisabled(!enable); - }, - - setEnableXtermJS: function(enable){ - var me = this; - - me.enableXtermjs = enable; - me.down('#xtermjs').setDisabled(!enable); - }, - - handler: function() { - var me = this; - var consoles = { - spice: me.enableSpice, - xtermjs: me.enableXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, me.consoleType, me.vmid, - me.nodename, me.consoleName, me.cmd); - }, - - menu: [ - { - xtype:'menuitem', - text: 'noVNC', - iconCls: 'pve-itype-icon-novnc', - type: 'html5', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - xterm: 'menuitem', - itemId: 'spicemenu', - text: 'SPICE', - type: 'vv', - iconCls: 'pve-itype-icon-virt-viewer', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - text: 'xterm.js', - itemId: 'xtermjs', - iconCls: 'pve-itype-icon-xtermjs', - type: 'xtermjs', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.callParent(); - } -}); -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - * - * does this for the button and every menu item - */ -Ext.define('PVE.button.Split', { - extend: 'Ext.button.Split', - alias: 'widget.pveSplitButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - handlerWrapper: function(button, event) { - var me = this; - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - // confirMsg can be boolean or function - /*jslint confusion: true*/ - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - /*jslint confusion: false*/ - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - callback: function(btn) { - if (btn !== 'yes') { - return; - } - me.realHandler(button, event, rec); - } - }); - } else { - me.realHandler(button, event, rec); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - me.realHandler = me.handler; - me.handler = me.handlerWrapper; - } - - if (me.menu && me.menu.items) { - me.menu.items.forEach(function(item) { - if (item.handler) { - item.realHandler = item.handler; - item.handler = me.handlerWrapper; - } - - if (item.selModel) { - me.mon(item.selModel, "selectionchange", function() { - var rec = item.selModel.getSelection()[0]; - if (!rec || (item.enableFn(rec) === false )) { - item.setDisabled(true); - } else { - item.setDisabled(false); - } - }); - } - }); - } - - me.callParent(); - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); -Ext.define('PVE.controller.StorageEdit', { - extend: 'Ext.app.ViewController', - alias: 'controller.storageEdit', - control: { - 'field[name=content]': { - change: function(field, value) { - var hasBackups = Ext.Array.contains(value, 'backup'); - var maxfiles = this.lookupReference('maxfiles'); - if (!maxfiles) { - return; - } - - if (!hasBackups) { - // clear values which will never be submitted - maxfiles.reset(); - } - maxfiles.setDisabled(!hasBackups); - } - } - } -}); -Ext.define('PVE.qemu.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'suspended': - stopped = false; - suspended = true; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = "VM " + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - hidden: running || suspended, - disabled: running || suspended, - handler: function() { - vm_command('start'); - } - }, - { - text: gettext('Pause'), - iconCls: 'fa fa-fw fa-pause', - hidden: stopped || suspended, - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmpause', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend'); - }); - } - }, - { - text: gettext('Hibernate'), - iconCls: 'fa fa-fw fa-download', - hidden: stopped || suspended, - disabled: stopped || suspended, - tooltip: gettext('Suspend to disk'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmsuspend', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend', { todisk: 1 }); - }); - } - }, - { - text: gettext('Resume'), - iconCls: 'fa fa-fw fa-play', - hidden: !suspended, - handler: function() { - vm_command('resume'); - } - }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: (standalone || !caps.vms['VM.Migrate']) && !caps.vms['VM.Allocate'] && !caps.vms['VM.Clone'] - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - hidden: !caps.vms['VM.Allocate'], - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmtemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var allowSpice = response.result.data.spice; - var allowXtermjs = response.result.data.serial; - var consoles = { - spice: allowSpice, - xtermjs: allowXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, 'kvm', vmid, nodename, vmname); - } - }); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.lxc.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no CT ID specified"; - } - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/lxc/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = 'CT ' + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - disabled: running, - handler: function() { - vm_command('start'); - } - }, -// { -// text: gettext('Suspend'), -// iconCls: 'fa fa-fw fa-pause', -// hidde: suspended, -// disabled: stopped || suspended, -// handler: function() { -// var msg = Proxmox.Utils.format_task_description('vzsuspend', vmid); -// Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { -// if (btn !== 'yes') { -// return; -// } -// -// vm_command('suspend'); -// }); -// } -// }, -// { -// text: gettext('Resume'), -// iconCls: 'fa fa-fw fa-play', -// hidden: !suspended, -// handler: function() { -// vm_command('resume'); -// } -// }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: standalone || !caps.vms['VM.Migrate'] - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'lxc'); - } - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - handler: function() { - var msg = Proxmox.Utils.format_task_description('vztemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.node.CmdMenu', { - extend: 'Ext.menu.Menu', - xtype: 'nodeCmdMenu', - - showSeparator: false, - - items: [ - { - text: gettext('Create VM'), - itemId: 'createvm', - iconCls: 'fa fa-desktop', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.qemu.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { - text: gettext('Create CT'), - itemId: 'createct', - iconCls: 'fa fa-cube', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.lxc.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Bulk Start'), - itemId: 'bulkstart', - iconCls: 'fa fa-fw fa-play', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - itemId: 'bulkstop', - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - itemId: 'bulkmigrate', - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Shell'), - itemId: 'shell', - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - var me = this.up('menu'); - PVE.Utils.openDefaultConsoleWindow(true, 'shell', undefined, me.nodename, undefined); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Wake-on-LAN'), - itemId: 'wakeonlan', - iconCls: 'fa fa-fw fa-power-off', - handler: function() { - var me = this.up('menu'); - Proxmox.Utils.API2Request({ - param: {}, - url: '/nodes/' + me.nodename + '/wakeonlan', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - Ext.Msg.show({ - title: 'Success', - icon: Ext.Msg.INFO, - msg: Ext.String.format(gettext("Wake on LAN packet send for '{0}': '{1}'"), me.nodename, response.result.data) - }); - } - }); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no nodename specified'; - } - - me.title = gettext('Node') + " '" + me.nodename + "'"; - me.callParent(); - - var caps = Ext.state.Manager.get('GuiCap'); - // disable not allowed options - if (!caps.vms['VM.Allocate']) { - me.getComponent('createct').setDisabled(true); - me.getComponent('createvm').setDisabled(true); - } - - if (!caps.nodes['Sys.PowerMgmt']) { - me.getComponent('bulkstart').setDisabled(true); - me.getComponent('bulkstop').setDisabled(true); - me.getComponent('bulkmigrate').setDisabled(true); - me.getComponent('wakeonlan').setDisabled(true); - } - - if (!caps.nodes['Sys.Console']) { - me.getComponent('shell').setDisabled(true); - } - - if (me.pveSelNode.data.running) { - me.getComponent('wakeonlan').setDisabled(true); - } - } -}); -Ext.define('PVE.noVncConsole', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNoVncConsole', - - nodename: undefined, - - vmid: undefined, - - cmd: undefined, - - consoleType: undefined, // lxc, kvm, shell, cmd - - layout: 'fit', - - xtermjs: false, - - border: false, - - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.consoleType) { - throw "no console type specified"; - } - - if (!me.vmid && me.consoleType !== 'shell' && me.consoleType !== 'cmd') { - throw "no VM ID specified"; - } - - // always use same iframe, to avoid running several noVnc clients - // at same time (to avoid performance problems) - var box = Ext.create('Ext.ux.IFrame', { itemid : "vncconsole" }); - - var type = me.xtermjs ? 'xtermjs' : 'novnc'; - Ext.apply(me, { - items: box, - listeners: { - activate: function() { - var queryDict = { - console: me.consoleType, // kvm, lxc, upgrade or shell - vmid: me.vmid, - node: me.nodename, - cmd: me.cmd, - resize: 'scale' - }; - queryDict[type] = 1; - PVE.Utils.cleanEmptyObjectKeys(queryDict); - var url = '/?' + Ext.Object.toQueryString(queryDict); - box.load(url); - } - } - }); - - me.callParent(); - - me.on('afterrender', function() { - me.focus(); - }); - } -}); - -Ext.define('PVE.data.PermPathStore', { - extend: 'Ext.data.Store', - alias: 'store.pvePermPath', - fields: [ 'value' ], - autoLoad: false, - data: [ - {'value': '/'}, - {'value': '/access'}, - {'value': '/nodes'}, - {'value': '/pool'}, - {'value': '/storage'}, - {'value': '/vms'} - ], - - constructor: function(config) { - var me = this; - - config = config || {}; - - me.callParent([config]); - - me.suspendEvents(); - PVE.data.ResourceStore.each(function(record) { - switch (record.get('type')) { - case 'node': - me.add({value: '/nodes/' + record.get('text')}); - break; - - case 'qemu': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'lxc': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'storage': - me.add({value: '/storage/' + record.get('storage')}); - break; - case 'pool': - me.add({value: '/pool/' + record.get('pool')}); - break; - } - }); - me.resumeEvents(); - - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - - me.sort({ - property: 'value', - direction: 'ASC' - }); - } -}); -Ext.define('PVE.data.ResourceStore', { - extend: 'Proxmox.data.UpdateStore', - singleton: true, - - findVMID: function(vmid) { - var me = this, i; - - return (me.findExact('vmid', parseInt(vmid, 10)) >= 0); - }, - - // returns the cached data from all nodes - getNodes: function() { - var me = this; - - var nodes = []; - me.each(function(record) { - if (record.get('type') == "node") { - nodes.push( record.getData() ); - } - }); - - return nodes; - }, - - storageIsShared: function(storage_path) { - var me = this; - - var index = me.findExact('id', storage_path); - - return me.getAt(index).data.shared; - }, - - guestNode: function(vmid) { - var me = this; - - var index = me.findExact('vmid', parseInt(vmid, 10)); - - return me.getAt(index).data.node; - }, - - constructor: function(config) { - // fixme: how to avoid those warnings - /*jslint confusion: true */ - - var me = this; - - config = config || {}; - - var field_defaults = { - type: { - header: gettext('Type'), - type: 'string', - renderer: PVE.Utils.render_resource_type, - sortable: true, - hideable: false, - width: 100 - }, - id: { - header: 'ID', - type: 'string', - hidden: true, - sortable: true, - width: 80 - }, - running: { - header: gettext('Online'), - type: 'boolean', - renderer: Proxmox.Utils.format_boolean, - hidden: true, - convert: function(value, record) { - var info = record.data; - return (Ext.isNumeric(info.uptime) && (info.uptime > 0)); - } - }, - text: { - header: gettext('Description'), - type: 'string', - sortable: true, - width: 200, - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - - if (Ext.isNumeric(info.vmid) && info.vmid > 0) { - text = String(info.vmid); - if (info.name) { - text += " (" + info.name + ')'; - } - } else { // node, pool, storage - text = info[info.type] || info.id; - if (info.node && info.type !== 'node') { - text += " (" + info.node + ")"; - } - } - - return text; - } - }, - vmid: { - header: 'VMID', - type: 'integer', - hidden: true, - sortable: true, - width: 80 - }, - name: { - header: gettext('Name'), - hidden: true, - sortable: true, - type: 'string' - }, - disk: { - header: gettext('Disk usage'), - type: 'integer', - renderer: PVE.Utils.render_disk_usage, - sortable: true, - width: 100, - hidden: true - }, - diskuse: { - header: gettext('Disk usage') + " %", - type: 'number', - sortable: true, - renderer: PVE.Utils.render_disk_usage_percent, - width: 100, - calculate: PVE.Utils.calculate_disk_usage, - sortType: 'asFloat' - }, - maxdisk: { - header: gettext('Disk size'), - type: 'integer', - renderer: PVE.Utils.render_size, - sortable: true, - hidden: true, - width: 100 - }, - mem: { - header: gettext('Memory usage'), - type: 'integer', - renderer: PVE.Utils.render_mem_usage, - sortable: true, - hidden: true, - width: 100 - }, - memuse: { - header: gettext('Memory usage') + " %", - type: 'number', - renderer: PVE.Utils.render_mem_usage_percent, - calculate: PVE.Utils.calculate_mem_usage, - sortType: 'asFloat', - sortable: true, - width: 100 - }, - maxmem: { - header: gettext('Memory size'), - type: 'integer', - renderer: PVE.Utils.render_size, - hidden: true, - sortable: true, - width: 100 - }, - cpu: { - header: gettext('CPU usage'), - type: 'float', - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100 - }, - maxcpu: { - header: gettext('maxcpu'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - diskread: { - header: gettext('Total Disk Read'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - diskwrite: { - header: gettext('Total Disk Write'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netin: { - header: gettext('Total NetIn'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netout: { - header: gettext('Total NetOut'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - template: { - header: gettext('Template'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - uptime: { - header: gettext('Uptime'), - type: 'integer', - renderer: Proxmox.Utils.render_uptime, - sortable: true, - width: 110 - }, - node: { - header: gettext('Node'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - storage: { - header: gettext('Storage'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - pool: { - header: gettext('Pool'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - hastate: { - header: gettext('HA State'), - type: 'string', - defaultValue: 'unmanaged', - hidden: true, - sortable: true - }, - status: { - header: gettext('Status'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - lock: { - header: gettext('Lock'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - } - }; - - var fields = []; - var fieldNames = []; - Ext.Object.each(field_defaults, function(key, value) { - var field = {name: key, type: value.type}; - if (Ext.isDefined(value.convert)) { - field.convert = value.convert; - } - - if (Ext.isDefined(value.calculate)) { - field.calculate = value.calculate; - } - - if (Ext.isDefined(value.defaultValue)) { - field.defaultValue = value.defaultValue; - } - - fields.push(field); - fieldNames.push(key); - }); - - Ext.define('PVEResources', { - extend: "Ext.data.Model", - fields: fields, - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/resources' - } - }); - - Ext.define('PVETree', { - extend: "Ext.data.Model", - fields: fields, - proxy: { type: 'memory' } - }); - - Ext.apply(config, { - storeid: 'PVEResources', - model: 'PVEResources', - defaultColumns: function() { - var res = []; - Ext.Object.each(field_defaults, function(field, info) { - var fi = Ext.apply({ dataIndex: field }, info); - res.push(fi); - }); - return res; - }, - fieldNames: fieldNames - }); - - me.callParent([config]); - } -}); -Ext.define('pve-domains', { - extend: "Ext.data.Model", - fields: [ - 'realm', 'type', 'comment', 'default', 'tfa', - { - name: 'descr', - // Note: We use this in the RealmComboBox.js (see Bug #125) - convert: function(value, record) { - if (value) { - return value; - } - - var info = record.data; - // return realm if there is no comment - var text = info.comment || info.realm; - - if (info.tfa) { - text += " (+ " + info.tfa + ")"; - } - - return Ext.String.htmlEncode(text); - } - } - ], - idProperty: 'realm', - proxy: { - type: 'proxmox', - url: "/api2/json/access/domains" - } -}); -Ext.define('pve-rrd-node', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - { - name:'iowait', - // percentage - convert: function(value) { - return value*100; - } - }, - 'loadavg', - 'maxcpu', - 'memtotal', - 'memused', - 'netin', - 'netout', - 'roottotal', - 'rootused', - 'swaptotal', - 'swapused', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-guest', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - 'maxcpu', - 'netin', - 'netout', - 'mem', - 'maxmem', - 'disk', - 'maxdisk', - 'diskread', - 'diskwrite', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-storage', { - extend: 'Ext.data.Model', - fields: [ - 'used', - 'total', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); -Ext.define('PVE.form.VlanField', { - extend: 'Ext.form.field.Number', - alias: ['widget.pveVlanField'], - - deleteEmpty: false, - - emptyText: 'no VLAN', - - fieldLabel: gettext('VLAN Tag'), - - allowBlank: true, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val) { - data = {}; - data[me.getName()] = val; - } else if (me.deleteEmpty) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - minValue: 1, - maxValue: 4094 - }); - - me.callParent(); - } -}); -// boolean type including 'Default' (delete property from file) -Ext.define('PVE.form.Boolean', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.booleanfield'], - comboItems: [ - ['__default__', gettext('Default')], - [1, gettext('Yes')], - [0, gettext('No')] - ] -}); -Ext.define('PVE.form.CompressionSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveCompressionSelector'], - comboItems: [ - ['0', Proxmox.Utils.noneText], - ['lzo', 'LZO (' + gettext('fast') + ')'], - ['gzip', 'GZIP (' + gettext('good') + ')'] - ] -}); -Ext.define('PVE.form.PoolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pvePoolSelector'], - - allowBlank: false, - valueField: 'poolid', - displayField: 'poolid', - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: 'poolid' - }); - - Ext.apply(me, { - store: store, - autoSelect: false, - listConfig: { - columns: [ - { - header: gettext('Pool'), - sortable: true, - dataIndex: 'poolid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-pools', { - extend: 'Ext.data.Model', - fields: [ 'poolid', 'comment' ], - proxy: { - type: 'proxmox', - url: "/api2/json/pools" - }, - idProperty: 'poolid' - }); - -}); -Ext.define('PVE.form.PrivilegesSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'pvePrivilegesSelector', - - multiSelect: true, - - initComponent: function() { - var me = this; - - // So me.store is available. - me.callParent(); - - Proxmox.Utils.API2Request({ - url: '/access/roles/Administrator', - method: 'GET', - success: function(response, options) { - var data = [], key; - /*jslint forin: true */ - for (key in response.result.data) { - data.push([key, key]); - } - /*jslint forin: false */ - - me.store.setData(data); - - me.store.sort({ - property: 'key', - direction: 'ASC' - }); - }, - - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -Ext.define('pve-groups', { - extend: 'Ext.data.Model', - fields: [ 'groupid', 'comment' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/groups" - }, - idProperty: 'groupid' -}); - -Ext.define('PVE.form.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveGroupSelector', - - allowBlank: false, - autoSelect: false, - valueField: 'groupid', - displayField: 'groupid', - listConfig: { - columns: [ - { - header: gettext('Group'), - sortable: true, - dataIndex: 'groupid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - }, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: [{ - property: 'groupid' - }] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}); -Ext.define('PVE.form.UserSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUserSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'userid', - displayField: 'userid', - - editable: true, - anyMatch: true, - forceSelection: true, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-users', - sorters: [{ - property: 'userid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('User'), - sortable: true, - dataIndex: 'userid', - flex: 1 - }, - { - header: gettext('Name'), - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load({ params: { enabled: 1 }}); - } - -}, function() { - - Ext.define('pve-users', { - extend: 'Ext.data.Model', - fields: [ - 'userid', 'firstname', 'lastname' , 'email', 'comment', - { type: 'boolean', name: 'enable' }, - { type: 'date', dateFormat: 'timestamp', name: 'expire' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/users" - }, - idProperty: 'userid' - }); - -}); - - -Ext.define('PVE.form.RoleSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveRoleSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'roleid', - displayField: 'roleid', - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: [{ - property: 'roleid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Role'), - sortable: true, - dataIndex: 'roleid', - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-roles', { - extend: 'Ext.data.Model', - fields: [ 'roleid', 'privs' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/roles" - }, - idProperty: 'roleid' - }); - -}); -Ext.define('PVE.form.GuestIDSelector', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveGuestIDSelector', - - allowBlank: false, - - minValue: 100, - - maxValue: 999999999, - - validateExists: undefined, - - loadNextFreeID: false, - - guestType: undefined, - - validator: function(value) { - var me = this; - - if (!Ext.isNumeric(value) || - value < me.minValue || - value > me.maxValue) { - // check is done by ExtJS - return true; - } - - if (me.validateExists === true && !me.exists) { - return me.unknownID; - } - - if (me.validateExists === false && me.exists) { - return me.inUseID; - } - - return true; - }, - - initComponent: function() { - var me = this; - var label = '{0} ID'; - var unknownID = gettext('This {0} ID does not exists'); - var inUseID = gettext('This {0} ID is already in use'); - var type = 'CT/VM'; - - if (me.guestType === 'lxc') { - type = 'CT'; - } else if (me.guestType === 'qemu') { - type = 'VM'; - } - - me.label = Ext.String.format(label, type); - me.unknownID = Ext.String.format(unknownID, type); - me.inUseID = Ext.String.format(inUseID, type); - - Ext.apply(me, { - fieldLabel: me.label, - listeners: { - 'change': function(field, newValue, oldValue) { - if (!Ext.isDefined(me.validateExists)) { - return; - } - Proxmox.Utils.API2Request({ - params: { vmid: newValue }, - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.exists = false; - me.validate(); - }, - failure: function(response, opts) { - me.exists = true; - me.validate(); - } - }); - } - } - }); - - me.callParent(); - - if (me.loadNextFreeID) { - Proxmox.Utils.API2Request({ - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.setRawValue(response.result.data); - } - }); - } - } -}); -Ext.define('PVE.form.MemoryField', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveMemoryField', - - allowBlank: false, - - hotplug: false, - - minValue: 32, - - maxValue: 4178944, - - step: 32, - - value: '512', // qm default - - allowDecimals: false, - - allowExponential: false, - - computeUpDown: function(value) { - var me = this; - - if (!me.hotplug) { - return { up: value + me.step, down: value - me.step }; - } - - var dimm_size = 512; - var prev_dimm_size = 0; - var min_size = 1024; - var current_size = min_size; - var value_up = min_size; - var value_down = min_size; - var value_start = min_size; - - var i, j; - for (j = 0; j < 9; j++) { - for (i = 0; i < 32; i++) { - if ((value >= current_size) && (value < (current_size + dimm_size))) { - value_start = current_size; - value_up = current_size + dimm_size; - value_down = current_size - ((i === 0) ? prev_dimm_size : dimm_size); - } - current_size += dimm_size; - } - prev_dimm_size = dimm_size; - dimm_size = dimm_size*2; - } - - return { up: value_up, down: value_down, start: value_start }; - }, - - onSpinUp: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.up, me.minValue, me.maxValue)); - } - }, - - onSpinDown: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.down, me.minValue, me.maxValue)); - } - }, - - initComponent: function() { - var me = this; - - if (me.hotplug) { - me.minValue = 1024; - - me.on('blur', function(field) { - var value = me.getValue(); - var res = me.computeUpDown(value); - if (value === res.start || value === res.up || value === res.down) { - return; - } - field.setValue(res.up); - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.form.NetworkCardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveNetworkCardSelector', - comboItems: [ - ['e1000', 'Intel E1000'], - ['virtio', 'VirtIO (' + gettext('paravirtualized') + ')'], - ['rtl8139', 'Realtek RTL8139'], - ['vmxnet3', 'VMware vmxnet3'] - ] -}); -Ext.define('PVE.form.DiskFormatSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveDiskFormatSelector', - comboItems: [ - ['raw', gettext('Raw disk image') + ' (raw)'], - ['qcow2', gettext('QEMU image format') + ' (qcow2)'], - ['vmdk', gettext('VMware image format') + ' (vmdk)'] - ] -}); -Ext.define('PVE.form.DiskSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveDiskSelector', - - // can be - // undefined: all - // unused: only unused - // journal_disk: all disks with gpt - diskType: undefined, - - valueField: 'devpath', - displayField: 'devpath', - emptyText: gettext('No Disks unused'), - listConfig: { - width: 600, - columns: [ - { - header: gettext('Device'), - flex: 3, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Size'), - flex: 2, - sortable: false, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Serial'), - flex: 5, - sortable: true, - dataIndex: 'serial' - } - ] - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - filterOnLoad: true, - model: 'pve-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list", - extraParams: { type: me.diskType } - }, - sorters: [ - { - property : 'devpath', - direction: 'ASC' - } - ] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}, function() { - - Ext.define('pve-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial'], - idProperty: 'devpath' - }); -}); -Ext.define('PVE.form.BusTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveBusSelector', - - noVirtIO: false, - - initComponent: function() { - var me = this; - - me.comboItems = [['ide', 'IDE'], ['sata', 'SATA']]; - - if (!me.noVirtIO) { - me.comboItems.push(['virtio', 'VirtIO Block']); - } - - me.comboItems.push(['scsi', 'SCSI']); - - me.callParent(); - } -}); -Ext.define('PVE.form.ControllerSelector', { - extend: 'Ext.form.FieldContainer', - alias: 'widget.pveControllerSelector', - - statics: { - maxIds: { - ide: 3, - sata: 5, - virtio: 15, - scsi: 13 - } - }, - - noVirtIO: false, - - vmconfig: {}, // used to check for existing devices - - sortByPreviousUsage: function(vmconfig, controllerList) { - - var usedControllers = Ext.clone(PVE.form.ControllerSelector.maxIds); - - var type; - for (type in usedControllers) { - if(usedControllers.hasOwnProperty(type)) { - usedControllers[type] = 0; - } - } - - var property; - for (property in vmconfig) { - if (vmconfig.hasOwnProperty(property)) { - if (property.match(PVE.Utils.bus_match) && !vmconfig[property].match(/media=cdrom/)) { - var foundController = property.match(PVE.Utils.bus_match)[1]; - usedControllers[foundController]++; - } - } - } - - var vmDefaults = PVE.qemu.OSDefaults[vmconfig.ostype]; - - var sortPriority = vmDefaults && vmDefaults.busPriority - ? vmDefaults.busPriority : PVE.qemu.OSDefaults.generic; - - var sortedList = Ext.clone(controllerList); - sortedList.sort(function(a,b) { - if (usedControllers[b] == usedControllers[a]) { - return sortPriority[b] - sortPriority[a]; - } - return usedControllers[b] - usedControllers[a]; - }); - - return sortedList; - }, - - setVMConfig: function(vmconfig, autoSelect) { - var me = this; - - me.vmconfig = Ext.apply({}, vmconfig); - - var clist = ['ide', 'virtio', 'scsi', 'sata']; - var bussel = me.down('field[name=controller]'); - var deviceid = me.down('field[name=deviceid]'); - - if (autoSelect === 'cdrom') { - clist = ['ide', 'scsi', 'sata']; - if (!Ext.isDefined(me.vmconfig.ide2)) { - bussel.setValue('ide'); - deviceid.setValue(2); - return; - } - } else { - // in most cases we want to add a disk to the same controller - // we previously used - clist = me.sortByPreviousUsage(me.vmconfig, clist); - } - - Ext.Array.each(clist, function(controller) { - var confid, i; - bussel.setValue(controller); - for (i = 0; i <= PVE.form.ControllerSelector.maxIds[controller]; i++) { - confid = controller + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - deviceid.setValue(i); - return false; // break - } - } - }); - deviceid.validate(); - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - fieldLabel: gettext('Bus/Device'), - layout: 'hbox', - defaults: { - hideLabel: true - }, - items: [ - { - xtype: 'pveBusSelector', - name: 'controller', - value: PVE.qemu.OSDefaults.generic.busType, - noVirtIO: me.noVirtIO, - allowBlank: false, - flex: 2, - listeners: { - change: function(t, value) { - if (!value) { - return; - } - var field = me.down('field[name=deviceid]'); - field.setMaxValue(PVE.form.ControllerSelector.maxIds[value]); - field.validate(); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'deviceid', - minValue: 0, - maxValue: PVE.form.ControllerSelector.maxIds.ide, - value: '0', - flex: 1, - allowBlank: false, - validator: function(value) { - /*jslint confusion: true */ - if (!me.rendered) { - return; - } - var field = me.down('field[name=controller]'); - var controller = field.getValue(); - var confid = controller + value; - if (Ext.isDefined(me.vmconfig[confid])) { - return "This device is already in use."; - } - return true; - } - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.EmailNotificationSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveEmailNotificationSelector'], - comboItems: [ - ['always', gettext('Always')], - ['failure', gettext('On failure only')] - ] -}); -/*global Proxmox*/ -Ext.define('PVE.form.RealmComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveRealmComboBox'], - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store.on('load', this.onLoad, view); - }, - - onLoad: function(store, records, success) { - if (!success) { - return; - } - var me = this; - var val = me.getValue(); - if (!val || !me.store.findRecord('realm', val)) { - var def = 'pam'; - Ext.each(records, function(rec) { - if (rec.data && rec.data['default']) { - def = rec.data.realm; - } - }); - me.setValue(def); - } - } - }, - - fieldLabel: gettext('Realm'), - name: 'realm', - queryMode: 'local', - allowBlank: false, - editable: false, - forceSelection: true, - autoSelect: false, - triggerAction: 'all', - valueField: 'realm', - displayField: 'descr', - getState: function() { - return { value: this.getValue() }; - }, - applyState : function(state) { - if (state && state.value) { - this.setValue(state.value); - } - }, - stateEvents: [ 'select' ], - stateful: true, // last chosen auth realm is saved between page reloads - id: 'pveloginrealm', // We need stable ids when using stateful, not autogenerated - stateID: 'pveloginrealm', - - needOTP: function(realm) { - var me = this; - // use exact match - var rec = me.store.findRecord('realm', realm, 0, false, false, true); - return rec && rec.data && rec.data.tfa ? rec.data.tfa : undefined; - }, - - store: { - model: 'pve-domains', - autoLoad: true - } -}); -/* - * Top left combobox, used to select a view of the underneath RessourceTree - */ -Ext.define('PVE.form.ViewSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveViewSelector'], - - editable: false, - allowBlank: false, - forceSelection: true, - autoSelect: false, - valueField: 'key', - displayField: 'value', - hideLabel: true, - queryMode: 'local', - - initComponent: function() { - var me = this; - - var default_views = { - server: { - text: gettext('Server View'), - groups: ['node'] - }, - folder: { - text: gettext('Folder View'), - groups: ['type'] - }, - storage: { - text: gettext('Storage View'), - groups: ['node'], - filterfn: function(node) { - return node.data.type === 'storage' || node.data.type === 'node'; - } - }, - pool: { - text: gettext('Pool View'), - groups: ['pool'], - // Pool View only lists VMs and Containers - filterfn: function(node) { - return node.data.type === 'qemu' || node.data.type === 'lxc' || node.data.type === 'openvz' || - node.data.type === 'pool'; - } - } - }; - - var groupdef = []; - Ext.Object.each(default_views, function(viewname, value) { - groupdef.push([viewname, value.text]); - }); - - var store = Ext.create('Ext.data.Store', { - model: 'KeyValue', - proxy: { - type: 'memory', - reader: 'array' - }, - data: groupdef, - autoload: true - }); - - Ext.apply(me, { - store: store, - value: groupdef[0][0], - getViewFilter: function() { - var view = me.getValue(); - return Ext.apply({ id: view }, default_views[view] || default_views.server); - }, - - getState: function() { - return { value: me.getValue() }; - }, - - applyState : function(state, doSelect) { - var view = me.getValue(); - if (state && state.value && (view != state.value)) { - var record = store.findRecord('key', state.value); - if (record) { - me.setValue(state.value, true); - if (doSelect) { - me.fireEvent('select', me, [record]); - } - } - } - }, - stateEvents: [ 'select' ], - stateful: true, - stateId: 'pveview', - id: 'view' - }); - - me.callParent(); - - var statechange = function(sp, key, value) { - if (key === me.id) { - me.applyState(value, true); - } - }; - - var sp = Ext.state.Manager.getProvider(); - me.mon(sp, 'statechange', statechange, me); - } -}); -Ext.define('PVE.form.NodeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveNodeSelector'], - - // invalidate nodes which are offline - onlineValidator: false, - - selectCurNode: false, - - // do not allow those nodes (array) - disallowedNodes: undefined, - - // only allow those nodes (array) - allowedNodes: undefined, - // set default value to empty array, else it inits it with - // null and after the store load it is an empty array, - // triggering dirtychange - value: [], - valueField: 'node', - displayField: 'node', - store: { - fields: [ 'node', 'cpu', 'maxcpu', 'mem', 'maxmem', 'uptime' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes' - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - }, - { - property : 'mem', - direction: 'DESC' - } - ] - }, - - listConfig: { - columns: [ - { - header: gettext('Node'), - dataIndex: 'node', - sortable: true, - hideable: false, - flex: 1 - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 100, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100, - dataIndex: 'cpu' - } - ] - }, - - validator: function(value) { - /*jslint confusion: true */ - var me = this; - if (!me.onlineValidator || (me.allowBlank && !value)) { - return true; - } - - var offline = []; - var notAllowed = []; - - Ext.Array.each(value.split(/\s*,\s*/), function(node) { - var rec = me.store.findRecord(me.valueField, node); - if (!(rec && rec.data) || rec.data.status !== 'online') { - offline.push(node); - } else if (me.allowedNodes && !Ext.Array.contains(me.allowedNodes, node)) { - notAllowed.push(node); - } - }); - - if (value && notAllowed.length !== 0) { - return "Node " + notAllowed.join(', ') + " is not allowed for this action!"; - } - - if (value && offline.length !== 0) { - return "Node " + offline.join(', ') + " seems to be offline!"; - } - return true; - }, - - initComponent: function() { - var me = this; - - if (me.selectCurNode && PVE.curSelectedNode && PVE.curSelectedNode.data.node) { - me.preferredValue = PVE.curSelectedNode.data.node; - } - - me.callParent(); - me.getStore().load(); - - // filter out disallowed nodes - me.getStore().addFilter(new Ext.util.Filter({ - filterFn: function(item) { - if (Ext.isArray(me.disallowedNodes)) { - return !Ext.Array.contains(me.disallowedNodes, item.data.node); - } else { - return true; - } - } - })); - - me.mon(me.getStore(), 'load', function(){ - me.isValid(); - }); - } -}); -Ext.define('PVE.form.FileSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFileSelector', - - editable: true, - anyMatch: true, - forceSelection: true, - - listeners: { - afterrender: function() { - var me = this; - if (!me.disabled) { - me.setStorage(me.storage, me.nodename); - } - } - }, - - setStorage: function(storage, nodename) { - var me = this; - - var change = false; - if (storage && (me.storage !== storage)) { - me.storage = storage; - change = true; - } - - if (nodename && (me.nodename !== nodename)) { - me.nodename = nodename; - change = true; - } - - if (!(me.storage && me.nodename && change)) { - return; - } - - var url = '/api2/json/nodes/' + me.nodename + '/storage/' + me.storage + '/content'; - if (me.storageContent) { - url += '?content=' + me.storageContent; - } - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - me.store.removeAll(); - me.store.load(); - }, - - setNodename: function(nodename) { - this.setStorage(undefined, nodename); - }, - - store: { - model: 'pve-storage-content' - }, - - allowBlank: false, - autoSelect: false, - valueField: 'volid', - displayField: 'text', - - listConfig: { - width: 600, - columns: [ - { - header: gettext('Name'), - dataIndex: 'text', - hideable: false, - flex: 1 - }, - { - header: gettext('Format'), - width: 60, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - dataIndex: 'size', - renderer: Proxmox.Utils.format_size - } - ] - } -}); -Ext.define('PVE.form.StorageSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveStorageSelector', - - allowBlank: false, - valueField: 'storage', - displayField: 'storage', - listConfig: { - width: 450, - columns: [ - { - header: gettext('Name'), - dataIndex: 'storage', - hideable: false, - flex: 1 - }, - { - header: gettext('Type'), - width: 75, - dataIndex: 'type' - }, - { - header: gettext('Avail'), - width: 90, - dataIndex: 'avail', - renderer: Proxmox.Utils.format_size - }, - { - header: gettext('Capacity'), - width: 90, - dataIndex: 'total', - renderer: Proxmox.Utils.format_size - } - ] - }, - - reloadStorageList: function() { - var me = this; - if (!me.nodename) { - return; - } - - var params = { - format: 1 - }; - var url = '/api2/json/nodes/' + me.nodename + '/storage'; - if (me.storageContent) { - params.content = me.storageContent; - } - if (me.targetNode) { - params.target = me.targetNode; - params.enabled = 1; // skip disabled storages - } - me.store.setProxy({ - type: 'proxmox', - url: url, - extraParams: params - }); - - me.store.load(); - - }, - - setTargetNode: function(targetNode) { - var me = this; - - if (!targetNode || (me.targetNode === targetNode)) { - return; - } - - me.targetNode = targetNode; - - me.reloadStorageList(); - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.reloadStorageList(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - var store = Ext.create('Ext.data.Store', { - model: 'pve-storage-status', - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - if (nodename) { - me.setNodename(nodename); - } - } -}, function() { - - Ext.define('pve-storage-status', { - extend: 'Ext.data.Model', - fields: [ 'storage', 'active', 'type', 'avail', 'total' ], - idProperty: 'storage' - }); - -}); -Ext.define('PVE.form.DiskStorageSelector', { - extend: 'Ext.container.Container', - alias: 'widget.pveDiskStorageSelector', - - layout: 'fit', - defaults: { - margin: '0 0 5 0' - }, - - // the fieldLabel for the storageselector - storageLabel: gettext('Storage'), - - // the content to show (e.g., images or rootdir) - storageContent: undefined, - - // if true, selects the first available storage - autoSelect: false, - - allowBlank: false, - emptyText: '', - - // hides the selection field - // this is always hidden on creation, - // and only shown when the storage needs a selection and - // hideSelection is not true - hideSelection: undefined, - - // hides the size field (e.g, for the efi disk dialog) - hideSize: false, - - // sets the initial size value - // string because else we get a type confusion - defaultSize: '32', - - changeStorage: function(f, value) { - var me = this; - var formatsel = me.getComponent('diskformat'); - var hdfilesel = me.getComponent('hdimage'); - var hdsizesel = me.getComponent('disksize'); - - // initial store load, and reset/deletion of the storage - if (!value) { - hdfilesel.setDisabled(true); - hdfilesel.setVisible(false); - - formatsel.setDisabled(true); - return; - } - - var rec = f.store.getById(value); - // if the storage is not defined, or valid, - // we cannot know what to enable/disable - if (!rec) { - return; - } - - var selectformat = false; - if (rec.data.format) { - var format = rec.data.format[0]; // 0 is the formats, 1 the default in the backend - delete format.subvol; // we never need subvol in the gui - selectformat = (Ext.Object.getSize(format) > 1); - } - - var select = !!rec.data.select_existing && !me.hideSelection; - - formatsel.setDisabled(!selectformat); - formatsel.setValue(selectformat ? 'qcow2' : 'raw'); - - hdfilesel.setDisabled(!select); - hdfilesel.setVisible(select); - if (select) { - hdfilesel.setStorage(value); - } - - hdsizesel.setDisabled(select || me.hideSize); - hdsizesel.setVisible(!select && !me.hideSize); - }, - - setNodename: function(nodename) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - var hdfilesel = me.getComponent('hdimage'); - - hdstorage.setNodename(nodename); - hdfilesel.setNodename(nodename); - }, - - setDisabled: function(value) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - - // reset on disable - if (value) { - hdstorage.setValue(); - } - hdstorage.setDisabled(value); - - // disabling does not always fire this event and we do not need - // the value of the validity - hdstorage.fireEvent('validitychange'); - }, - - initComponent: function() { - var me = this; - - me.items = [ - { - xtype: 'pveStorageSelector', - itemId: 'hdstorage', - name: 'hdstorage', - reference: 'hdstorage', - fieldLabel: me.storageLabel, - nodename: me.nodename, - storageContent: me.storageContent, - disabled: me.disabled, - autoSelect: me.autoSelect, - allowBlank: me.allowBlank, - emptyText: me.emptyText, - listeners: { - change: { - fn: me.changeStorage, - scope: me - } - } - }, - { - xtype: 'pveFileSelector', - name: 'hdimage', - reference: 'hdimage', - itemId: 'hdimage', - fieldLabel: gettext('Disk image'), - nodename: me.nodename, - disabled: true, - hidden: true - }, - { - xtype: 'numberfield', - itemId: 'disksize', - reference: 'disksize', - name: 'disksize', - fieldLabel: gettext('Disk size') + ' (GiB)', - hidden: me.hideSize, - disabled: me.hideSize, - minValue: 0.001, - maxValue: 128*1024, - decimalPrecision: 3, - value: me.defaultSize, - allowBlank: false - }, - { - xtype: 'pveDiskFormatSelector', - itemId: 'diskformat', - reference: 'diskformat', - name: 'diskformat', - fieldLabel: gettext('Format'), - nodename: me.nodename, - disabled: true, - hidden: me.storageContent === 'rootdir', - value: 'qcow2', - allowBlank: false - } - ]; - - // use it to disable the children but not ourself - me.disabled = false; - - me.callParent(); - } -}); -Ext.define('PVE.form.BridgeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.BridgeSelector'], - - bridgeType: 'any_bridge', // bridge, OVSBridge or any_bridge - - store: { - fields: [ 'iface', 'active', 'type' ], - filterOnLoad: true, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }, - valueField: 'iface', - displayField: 'iface', - listConfig: { - columns: [ - { - header: gettext('Bridge'), - dataIndex: 'iface', - hideable: false, - width: 100 - }, - { - header: gettext('Active'), - width: 60, - dataIndex: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/network?type=' + - me.bridgeType - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.PCISelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pvePCISelector', - - store: { - fields: [ 'id','vendor_name', 'device_name', 'vendor', 'device', 'iommugroup', 'mdev' ], - filterOnLoad: true, - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }, - - autoSelect: false, - valueField: 'id', - displayField: 'id', - - // can contain a load callback for the store - // useful to determine the state of the IOMMU - onLoadCallBack: undefined, - - listConfig: { - width: 800, - columns: [ - { - header: 'ID', - dataIndex: 'id', - width: 80 - }, - { - header: gettext('IOMMU Group'), - dataIndex: 'iommugroup', - width: 50 - }, - { - header: gettext('Vendor'), - dataIndex: 'vendor_name', - flex: 2 - }, - { - header: gettext('Device'), - dataIndex: 'device_name', - flex: 6 - }, - { - header: gettext('Mediated Devices'), - dataIndex: 'mdev', - flex: 1, - renderer: function(val) { - return Proxmox.Utils.format_boolean(!!val); - } - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - if (me.onLoadCallBack !== undefined) { - me.mon(me.getStore(), 'load', me.onLoadCallBack); - } - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.MDevSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveMDevSelector', - - store: { - fields: [ 'type','available', 'description' ], - filterOnLoad: true, - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ] - }, - autoSelect: false, - valueField: 'type', - displayField: 'type', - listConfig: { - columns: [ - { - header: gettext('Type'), - dataIndex: 'type', - flex: 1 - }, - { - header: gettext('Available'), - dataIndex: 'available', - width: 80 - }, - { - header: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value) { - if (!value) { - return ''; - } - - return value.split('\n').join('
'); - } - } - ] - }, - - setPciID: function(pciid, force) { - var me = this; - - if (!force && (!pciid || (me.pciid === pciid))) { - return; - } - - me.pciid = pciid; - me.updateProxy(); - }, - - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - me.updateProxy(); - }, - - updateProxy: function() { - var me = this; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci/' + me.pciid + '/mdev' - }); - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no node name specified'; - } - - me.callParent(); - - if (me.pciid) { - me.setPciID(me.pciid, true); - } - } -}); - -Ext.define('PVE.form.SecurityGroupsSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveSecurityGroupsSelector'], - - valueField: 'group', - displayField: 'group', - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'group', 'comment' ], - idProperty: 'group', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/groups" - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Security Group'), - dataIndex: 'group', - hideable: false, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPRefSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPRefSelector'], - - base_url: undefined, - - preferredValue: '', // hack: else Form sets dirty flag? - - ref_type: undefined, // undefined = any [undefined, 'ipset' or 'alias'] - - valueField: 'ref', - displayField: 'ref', - - initComponent: function() { - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - - var url = "/api2/json" + me.base_url; - if (me.ref_type) { - url += "?type=" + me.ref_type; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'type', 'name', 'ref', 'comment' ], - idProperty: 'ref', - proxy: { - type: 'proxmox', - url: url - }, - sorters: { - property: 'ref', - order: 'DESC' - } - }); - - var disable_query_for_ips = function(f, value) { - if (value === null || - value.match(/^\d/)) { // IP address starts with \d - f.queryDelay = 9999999999; // hack: disable with long delay - } else { - f.queryDelay = 10; - } - }; - - var columns = []; - - if (!me.ref_type) { - columns.push({ - header: gettext('Type'), - dataIndex: 'type', - hideable: false, - width: 60 - }); - } - - columns.push( - { - header: gettext('Name'), - dataIndex: 'ref', - hideable: false, - width: 140 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ); - - Ext.apply(me, { - store: store, - listConfig: { columns: columns } - }); - - me.on('change', disable_query_for_ips); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPProtocolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPProtocolSelector'], - valueField: 'p', - displayField: 'p', - listConfig: { - columns: [ - { - header: gettext('Protocol'), - dataIndex: 'p', - hideable: false, - sortable: false, - width: 100 - }, - { - header: gettext('Number'), - dataIndex: 'n', - hideable: false, - sortable: false, - width: 50 - }, - { - header: gettext('Description'), - dataIndex: 'd', - hideable: false, - sortable: false, - flex: 1 - } - ] - }, - store: { - fields: [ 'p', 'd', 'n'], - data: [ - { p: 'tcp', n: 6, d: 'Transmission Control Protocol' }, - { p: 'udp', n: 17, d: 'User Datagram Protocol' }, - { p: 'icmp', n: 1, d: 'Internet Control Message Protocol' }, - { p: 'igmp', n: 2, d: 'Internet Group Management' }, - { p: 'ggp', n: 3, d: 'gateway-gateway protocol' }, - { p: 'ipencap', n: 4, d: 'IP encapsulated in IP' }, - { p: 'st', n: 5, d: 'ST datagram mode' }, - { p: 'egp', n: 8, d: 'exterior gateway protocol' }, - { p: 'igp', n: 9, d: 'any private interior gateway (Cisco)' }, - { p: 'pup', n: 12, d: 'PARC universal packet protocol' }, - { p: 'hmp', n: 20, d: 'host monitoring protocol' }, - { p: 'xns-idp', n: 22, d: 'Xerox NS IDP' }, - { p: 'rdp', n: 27, d: '"reliable datagram" protocol' }, - { p: 'iso-tp4', n: 29, d: 'ISO Transport Protocol class 4 [RFC905]' }, - { p: 'dccp', n: 33, d: 'Datagram Congestion Control Prot. [RFC4340]' }, - { p: 'xtp', n: 36, d: 'Xpress Transfer Protocol' }, - { p: 'ddp', n: 37, d: 'Datagram Delivery Protocol' }, - { p: 'idpr-cmtp', n: 38, d: 'IDPR Control Message Transport' }, - { p: 'ipv6', n: 41, d: 'Internet Protocol, version 6' }, - { p: 'ipv6-route', n: 43, d: 'Routing Header for IPv6' }, - { p: 'ipv6-frag', n: 44, d: 'Fragment Header for IPv6' }, - { p: 'idrp', n: 45, d: 'Inter-Domain Routing Protocol' }, - { p: 'rsvp', n: 46, d: 'Reservation Protocol' }, - { p: 'gre', n: 47, d: 'General Routing Encapsulation' }, - { p: 'esp', n: 50, d: 'Encap Security Payload [RFC2406]' }, - { p: 'ah', n: 51, d: 'Authentication Header [RFC2402]' }, - { p: 'skip', n: 57, d: 'SKIP' }, - { p: 'ipv6-icmp', n: 58, d: 'ICMP for IPv6' }, - { p: 'ipv6-nonxt', n: 59, d: 'No Next Header for IPv6' }, - { p: 'ipv6-opts', n: 60, d: 'Destination Options for IPv6' }, - { p: 'vmtp', n: 81, d: 'Versatile Message Transport' }, - { p: 'eigrp', n: 88, d: 'Enhanced Interior Routing Protocol (Cisco)' }, - { p: 'ospf', n: 89, d: 'Open Shortest Path First IGP' }, - { p: 'ax.25', n: 93, d: 'AX.25 frames' }, - { p: 'ipip', n: 94, d: 'IP-within-IP Encapsulation Protocol' }, - { p: 'etherip', n: 97, d: 'Ethernet-within-IP Encapsulation [RFC3378]' }, - { p: 'encap', n: 98, d: 'Yet Another IP encapsulation [RFC1241]' }, - { p: 'pim', n: 103, d: 'Protocol Independent Multicast' }, - { p: 'ipcomp', n: 108, d: 'IP Payload Compression Protocol' }, - { p: 'vrrp', n: 112, d: 'Virtual Router Redundancy Protocol [RFC5798]' }, - { p: 'l2tp', n: 115, d: 'Layer Two Tunneling Protocol [RFC2661]' }, - { p: 'isis', n: 124, d: 'IS-IS over IPv4' }, - { p: 'sctp', n: 132, d: 'Stream Control Transmission Protocol' }, - { p: 'fc', n: 133, d: 'Fibre Channel' }, - { p: 'mobility-header', n: 135, d: 'Mobility Support for IPv6 [RFC3775]' }, - { p: 'udplite', n: 136, d: 'UDP-Lite [RFC3828]' }, - { p: 'mpls-in-ip', n: 137, d: 'MPLS-in-IP [RFC4023]' }, - { p: 'hip', n: 139, d: 'Host Identity Protocol' }, - { p: 'shim6', n: 140, d: 'Shim6 Protocol [RFC5533]' }, - { p: 'wesp', n: 141, d: 'Wrapped Encapsulating Security Payload' }, - { p: 'rohc', n: 142, d: 'Robust Header Compression' } - ] - } -}); -Ext.define('PVE.form.CPUModelSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.CPUModelSelector'], - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + ' (kvm64)'], - ['486', '486'], - ['athlon', 'athlon'], - ['core2duo', 'core2duo'], - ['coreduo', 'coreduo'], - ['kvm32', 'kvm32'], - ['kvm64', 'kvm64'], - ['pentium', 'pentium'], - ['pentium2', 'pentium2'], - ['pentium3', 'pentium3'], - ['phenom', 'phenom'], - ['qemu32', 'qemu32'], - ['qemu64', 'qemu64'], - ['Conroe', 'Conroe'], - ['Penryn', 'Penryn'], - ['Nehalem', 'Nehalem'], - ['Westmere', 'Westmere'], - ['SandyBridge', 'SandyBridge'], - ['IvyBridge', 'IvyBridge'], - ['Haswell', 'Haswell'], - ['Haswell-noTSX','Haswell-noTSX'], - ['Broadwell', 'Broadwell'], - ['Broadwell-noTSX','Broadwell-noTSX'], - ['Skylake-Client','Skylake-Client'], - ['Opteron_G1', 'Opteron_G1'], - ['Opteron_G2', 'Opteron_G2'], - ['Opteron_G3', 'Opteron_G3'], - ['Opteron_G4', 'Opteron_G4'], - ['Opteron_G5', 'Opteron_G5'], - ['EPYC', 'EPYC'], - ['host', 'host'] - - ] -}); -Ext.define('PVE.form.VNCKeyboardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.VNCKeyboardSelector'], - comboItems: PVE.Utils.kvm_keymap_array() -}); -Ext.define('PVE.form.CacheTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.CacheTypeSelector'], - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (" + gettext('No cache') + ")"], - ['directsync', 'Direct sync'], - ['writethrough', 'Write through'], - ['writeback', 'Write back'], - ['unsafe', 'Write back (' + gettext('unsafe') + ')'], - ['none', gettext('No cache')] - ] -}); -Ext.define('PVE.form.SnapshotSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.SnapshotSelector'], - - valueField: 'name', - displayField: 'name', - - loadStore: function(nodename, vmid) { - var me = this; - - if (!nodename) { - return; - } - - me.nodename = nodename; - - if (!vmid) { - return; - } - - me.vmid = vmid; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid +'/snapshot' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.guestType) { - throw "no guest type specified"; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'name'], - filterOnLoad: true - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Snapshot'), - dataIndex: 'name', - hideable: false, - flex: 1 - } - ] - } - }); - - me.callParent(); - - me.loadStore(me.nodename, me.vmid); - } -}); -Ext.define('PVE.form.ContentTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveContentTypeSelector'], - - cts: undefined, - - initComponent: function() { - var me = this; - - me.comboItems = []; - - if (me.cts === undefined) { - me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir', 'snippets']; - } - - Ext.Array.each(me.cts, function(ct) { - me.comboItems.push([ct, PVE.Utils.format_content_types(ct)]); - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.HotplugFeatureSelector', { - extend: 'Ext.form.CheckboxGroup', - alias: 'widget.pveHotplugFeatureSelector', - - columns: 1, - vertical: true, - - defaults: { - name: 'hotplug', - submitValue: false - }, - items: [ - { - boxLabel: gettext('Disk'), - inputValue: 'disk', - checked: true - }, - { - boxLabel: gettext('Network'), - inputValue: 'network', - checked: true - }, - { - boxLabel: 'USB', - inputValue: 'usb', - checked: true - }, - { - boxLabel: gettext('Memory'), - inputValue: 'memory' - }, - { - boxLabel: gettext('CPU'), - inputValue: 'cpu' - } - ], - - setValue: function(value) { - var me = this; - var newVal = []; - if (value === '1') { - newVal = ['disk', 'network', 'usb']; - } else if (value !== '0') { - newVal = value.split(','); - } - me.callParent([{ hotplug: newVal }]); - }, - - // override framework function to - // assemble the hotplug value - getSubmitData: function() { - var me = this, - boxes = me.getBoxes(), - data = []; - Ext.Array.forEach(boxes, function(box){ - if (box.getValue()) { - data.push(box.inputValue); - } - }); - - /* because above is hotplug an array */ - /*jslint confusion: true*/ - if (data.length === 0) { - return { 'hotplug':'0' }; - } else { - return { 'hotplug': data.join(',') }; - } - } - -}); -Ext.define('PVE.form.AgentFeatureSelector', { - extend: 'Proxmox.panel.InputPanel', - alias: ['widget.pveAgentFeatureSelector'], - - initComponent: function() { - var me = this; - me.items= [ - { - xtype: 'proxmoxcheckbox', - boxLabel: gettext('Qemu Agent'), - name: 'enabled', - uncheckedValue: 0, - listeners: { - change: function(f, value, old) { - var gtcb = me.down('proxmoxcheckbox[name=fstrim_cloned_disks]'); - if (value) { - gtcb.setDisabled(false); - } else { - gtcb.setDisabled(true); - } - } - } - }, - { - xtype: 'proxmoxcheckbox', - boxLabel: gettext('Run guest-trim after clone disk'), - name: 'fstrim_cloned_disks', - disabled: true - } - ]; - me.callParent(); - }, - - onGetValues: function(values) { - var agentstr = PVE.Parser.printPropertyString(values, 'enabled'); - return { agent: agentstr }; - }, - - setValues: function(values) { - var agent = values.agent || ''; - var res = PVE.Parser.parsePropertyString(agent, 'enabled'); - this.callParent([res]); - } -}); -Ext.define('PVE.form.iScsiProviderSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveiScsiProviderSelector'], - comboItems: [ - ['comstar', 'Comstar'], - [ 'istgt', 'istgt'], - [ 'iet', 'IET'], - [ 'LIO', 'LIO'] - ] -}); -Ext.define('PVE.form.DayOfWeekSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveDayOfWeekSelector'], - comboItems:[], - initComponent: function(){ - var me = this; - me.comboItems = [ - ['mon', Ext.util.Format.htmlDecode(Ext.Date.dayNames[1])], - ['tue', Ext.util.Format.htmlDecode(Ext.Date.dayNames[2])], - ['wed', Ext.util.Format.htmlDecode(Ext.Date.dayNames[3])], - ['thu', Ext.util.Format.htmlDecode(Ext.Date.dayNames[4])], - ['fri', Ext.util.Format.htmlDecode(Ext.Date.dayNames[5])], - ['sat', Ext.util.Format.htmlDecode(Ext.Date.dayNames[6])], - ['sun', Ext.util.Format.htmlDecode(Ext.Date.dayNames[0])] - ]; - this.callParent(); - } -}); -Ext.define('PVE.form.BackupModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveBackupModeSelector'], - comboItems: [ - ['snapshot', gettext('Snapshot')], - ['suspend', gettext('Suspend')], - ['stop', gettext('Stop')] - ] -}); -Ext.define('PVE.form.ScsiHwSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveScsiHwSelector'], - comboItems: [ - ['__default__', PVE.Utils.render_scsihw('')], - ['lsi', PVE.Utils.render_scsihw('lsi')], - ['lsi53c810', PVE.Utils.render_scsihw('lsi53c810')], - ['megasas', PVE.Utils.render_scsihw('megasas')], - ['virtio-scsi-pci', PVE.Utils.render_scsihw('virtio-scsi-pci')], - ['virtio-scsi-single', PVE.Utils.render_scsihw('virtio-scsi-single')], - ['pvscsi', PVE.Utils.render_scsihw('pvscsi')] - ] -}); -Ext.define('PVE.form.FirewallPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallPolicySelector'], - comboItems: [ - ['ACCEPT', 'ACCEPT'], - ['REJECT', 'REJECT'], - [ 'DROP', 'DROP'] - ] -}); -/* - * This is a global search field - * it loads the /cluster/resources on focus - * and displays the result in a floating grid - * - * it filters and sorts the objects by the algorithm in - * the customFilter function - * - * also it does accept key up/down and enter for input - * and it opens to ctrl+shift+f and ctrl+space - */ -Ext.define('PVE.form.GlobalSearchField', { - extend: 'Ext.form.field.Text', - alias: 'widget.pveGlobalSearchField', - - emptyText: gettext('Search'), - enableKeyEvents: true, - selectOnFocus: true, - padding: '0 5 0 5', - - grid: { - xtype: 'gridpanel', - focusOnToFront: false, - floating: true, - emptyText: Proxmox.Utils.noneText, - width: 600, - height: 400, - scrollable: { - xtype: 'scroller', - y: true, - x:false - }, - store: { - model: 'PVEResources', - proxy:{ - type: 'proxmox', - url: '/api2/extjs/cluster/resources' - } - }, - plugins: { - ptype: 'bufferedrenderer', - trailingBufferZone: 20, - leadingBufferZone: 20 - }, - - hideMe: function() { - var me = this; - if (typeof me.ctxMenu !== 'undefined' && me.ctxMenu.isVisible()) { - return; - } - me.hasFocus = false; - if (!me.textfield.hasFocus) { - me.hide(); - } - }, - - setFocus: function() { - var me = this; - me.hasFocus = true; - }, - - listeners: { - rowclick: function(grid, record) { - var me = this; - me.textfield.selectAndHide(record.id); - }, - itemcontextmenu: function(v, record, item, index, event) { - var me = this; - me.ctxMenu = PVE.Utils.createCmdMenu(v, record, item, index, event); - }, - /* because of lint */ - focusleave: { - fn: 'hideMe' - }, - focusenter: 'setFocus' - }, - - columns: [ - { - text: gettext('Type'), - dataIndex: 'type', - width: 100, - renderer: PVE.Utils.render_resource_type - }, - { - text: gettext('Description'), - flex: 1, - dataIndex: 'text' - }, - { - text: gettext('Node'), - dataIndex: 'node' - }, - { - text: gettext('Pool'), - dataIndex: 'pool' - } - ] - }, - - customFilter: function(item) { - var me = this; - var match = 0; - var fieldArr = []; - var i,j, fields; - - // different types of objects have different fields to search - // for example, a node will never have a pool and vice versa - switch (item.data.type) { - case 'pool': fieldArr = ['type', 'pool', 'text']; break; - case 'node': fieldArr = ['type', 'node', 'text']; break; - case 'storage': fieldArr = ['type', 'pool', 'node', 'storage']; break; - default: fieldArr = ['name', 'type', 'node', 'pool', 'vmid']; - } - if (me.filterVal === '') { - item.data.relevance = 0; - return true; - } - - // all text is case insensitive and each word is - // searched alone - // for every partial match, the row gets - // 1 match point, for every exact match - // it gets 2 points - // - // results gets sorted by points (descending) - fields = me.filterVal.split(/\s+/); - for(i = 0; i < fieldArr.length; i++) { - var v = item.data[fieldArr[i]]; - if (v !== undefined) { - v = v.toString().toLowerCase(); - for(j = 0; j < fields.length; j++) { - if (v.indexOf(fields[j]) !== -1) { - match++; - if(v === fields[j]) { - match++; - } - } - } - } - } - // give the row the 'relevance' value - item.data.relevance = match; - return (match > 0); - }, - - updateFilter: function(field, newValue, oldValue) { - var me = this; - // parse input and filter store, - // show grid - me.grid.store.filterVal = newValue.toLowerCase().trim(); - me.grid.store.clearFilter(true); - me.grid.store.filterBy(me.customFilter); - me.grid.getSelectionModel().select(0); - }, - - selectAndHide: function(id) { - var me = this; - me.tree.selectById(id); - me.grid.hide(); - me.setValue(''); - me.blur(); - }, - - onKey: function(field, e) { - var me = this; - var key = e.getKey(); - - switch(key) { - case Ext.event.Event.ENTER: - // go to first entry if there is one - if (me.grid.store.getCount() > 0) { - me.selectAndHide(me.grid.getSelection()[0].data.id); - } - break; - case Ext.event.Event.UP: - me.grid.getSelectionModel().selectPrevious(); - break; - case Ext.event.Event.DOWN: - me.grid.getSelectionModel().selectNext(); - break; - case Ext.event.Event.ESC: - me.grid.hide(); - me.blur(); - break; - } - }, - - loadValues: function(field) { - var me = this; - var records = []; - - me.hasFocus = true; - me.grid.textfield = me; - me.grid.store.load(); - me.grid.showBy(me, 'tl-bl'); - }, - - hideGrid: function() { - var me = this; - - me.hasFocus = false; - if (!me.grid.hasFocus) { - me.grid.hide(); - } - }, - - listeners: { - change: { - fn: 'updateFilter', - buffer: 250 - }, - specialkey: 'onKey', - focusenter: 'loadValues', - focusleave: { - fn: 'hideGrid', - delay: 100 - } - }, - - toggleFocus: function() { - var me = this; - if (!me.hasFocus) { - me.focus(); - } else { - me.blur(); - } - }, - - initComponent: function() { - var me = this; - - if (!me.tree) { - throw "no tree given"; - } - - me.grid = Ext.create(me.grid); - - me.callParent(); - - /*jslint confusion: true*/ - /*because shift is also a function*/ - // bind ctrl+shift+f and ctrl+space - // to open/close the search - me.keymap = new Ext.KeyMap({ - target: Ext.get(document), - binding: [{ - key:'F', - ctrl: true, - shift: true, - fn: me.toggleFocus, - scope: me - },{ - key:' ', - ctrl: true, - fn: me.toggleFocus, - scope: me - }] - }); - - // always select first item and - // sort by relevance after load - me.mon(me.grid.store, 'load', function() { - me.grid.getSelectionModel().select(0); - me.grid.store.sort({ - property: 'relevance', - direction: 'DESC' - }); - }); - } - -}); -Ext.define('PVE.form.QemuBiosSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveQemuBiosSelector'], - - initComponent: function() { - var me = this; - - me.comboItems = [ - ['__default__', PVE.Utils.render_qemu_bios('')], - ['seabios', PVE.Utils.render_qemu_bios('seabios')], - ['ovmf', PVE.Utils.render_qemu_bios('ovmf')] - ]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -/* filter is a javascript builtin, but extjs calls it also filter */ -Ext.define('PVE.form.VMSelector', { - extend: 'Ext.grid.Panel', - alias: 'widget.vmselector', - - mixins: { - field: 'Ext.form.field.Field' - }, - - allowBlank: true, - selectAll: false, - isFormField: true, - - plugins: 'gridfilters', - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - // due to EXTJS-18711 - // we have to do a static list via a store - // but to avoid creating an object, - // we have to have a pseudo un function - un: function(){} - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - flex: 1, - filter: { - type: 'list' - } - } - ], - - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE' - }, - - checkChangeEvents: [ - 'selectionchange', - 'change' - ], - - listeners: { - selectionchange: function() { - // to trigger validity and error checks - this.checkChange(); - } - }, - - getValue: function() { - var me = this; - var sm = me.getSelectionModel(); - var selection = sm.getSelection(); - var values = []; - var store = me.getStore(); - selection.forEach(function(item) { - // only add if not filtered - if (store.findExact('vmid', item.data.vmid) !== -1) { - values.push(item.data.vmid); - } - }); - return values; - }, - - setValue: function(value) { - console.log(value); - var me = this; - var sm = me.getSelectionModel(); - if (!Ext.isArray(value)) { - value = value.split(','); - } - var selection = []; - var store = me.getStore(); - - value.forEach(function(item) { - var rec = store.findRecord('vmid',item, 0, false, true, true); - console.log(store); - - if (rec) { - console.log(rec); - selection.push(rec); - } - }); - - sm.select(selection); - - return me.mixins.field.setValue.call(me, value); - }, - - getErrors: function(value) { - var me = this; - if (me.allowBlank === false && - me.getSelectionModel().getCount() === 0) { - me.addBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return [gettext('No VM selected')]; - } - - me.removeBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return []; - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.nodename) { - me.store.filters.add({ - property: 'node', - exactMatch: true, - value: me.nodename - }); - } - - // only show the relevant guests by default - if (me.action) { - var statusfilter = ''; - switch (me.action) { - case 'startall': - statusfilter = 'stopped'; - break; - case 'stopall': - statusfilter = 'running'; - break; - } - if (statusfilter !== '') { - me.store.filters.add({ - property: 'template', - value: 0 - },{ - id: 'x-gridfilter-status', - operator: 'in', - property: 'status', - value: [statusfilter] - }); - } - } - - var store = me.getStore(); - var sm = me.getSelectionModel(); - - if (me.selectAll) { - me.mon(store,'load', function(){ - me.getSelectionModel().selectAll(false); - }); - } - } -}); - - -Ext.define('PVE.form.VMComboSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.vmComboSelector', - - valueField: 'vmid', - displayField: 'vmid', - - autoSelect: false, - editable: true, - anyMatch: true, - forceSelection: true, - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - - listConfig: { - width: 600, - plugins: 'gridfilters', - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - hidden: true, - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - un: function(){} // due to EXTJS-18711 - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - hidden: true, - flex: 1, - filter: { - type: 'list' - } - } - ] - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.form.VMCPUFlagSelector', { - extend: 'Ext.grid.Panel', - alias: 'widget.vmcpuflagselector', - - mixins: { - field: 'Ext.form.field.Field' - }, - - disableSelection: true, - columnLines: false, - selectable: false, - hideHeaders: true, - - scrollable: 'y', - height: 200, - - unkownFlags: [], - - store: { - type: 'store', - fields: ['flag', { name: 'state', defaultValue: '=' }, 'desc'], - data: [ - // FIXME: let qemu-server host this and autogenerate or get from API call?? - { flag: 'md-clear', desc: 'Required to let the guest OS know if MDS is mitigated correctly' }, - { flag: 'pcid', desc: 'Meltdown fix cost reduction on Westmere, Sandy-, and IvyBridge Intel CPUs' }, - { flag: 'spec-ctrl', desc: 'Allows improved Spectre mitigation with Intel CPUs' }, - { flag: 'ssbd', desc: 'Protection for "Speculative Store Bypass" for Intel models' }, - { flag: 'ibpb', desc: 'Allows improved Spectre mitigation with AMD CPUs' }, - { flag: 'virt-ssbd', desc: 'Basis for "Speculative Store Bypass" protection for AMD models' }, - { flag: 'amd-ssbd', desc: 'Improves Spectre mitigation performance with AMD CPUs, best used with "virt-ssbd"' }, - { flag: 'amd-no-ssb', desc: 'Notifies guest OS that host is not vulnerable for Spectre on AMD CPUs' }, - { flag: 'pdpe1gb', desc: 'Allow guest OS to use 1GB size pages, if host HW supports it' } - ], - listeners: { - update: function() { - this.commitChanges(); - } - } - }, - - getValue: function() { - var me = this; - var store = me.getStore(); - var flags = ''; - - // ExtJS does not has a nice getAllRecords interface for stores :/ - store.queryBy(Ext.returnTrue).each(function(rec) { - var s = rec.get('state'); - if (s && s !== '=') { - var f = rec.get('flag'); - if (flags === '') { - flags = s + f; - } else { - flags += ';' + s + f; - } - } - }); - - flags += me.unkownFlags.join(';'); - - return flags; - }, - - setValue: function(value) { - var me = this; - var store = me.getStore(); - - me.value = value || ''; - - me.unkownFlags = []; - - me.getStore().queryBy(Ext.returnTrue).each(function(rec) { - rec.set('state', '='); - }); - - var flags = value ? value.split(';') : []; - flags.forEach(function(flag) { - var sign = flag.substr(0, 1); - flag = flag.substr(1); - - var rec = store.findRecord('flag', flag); - if (rec !== null) { - rec.set('state', sign); - } else { - me.unkownFlags.push(flag); - } - }); - store.reload(); - - var res = me.mixins.field.setValue.call(me, value); - - return res; - }, - columns: [ - { - dataIndex: 'state', - renderer: function(v) { - switch(v) { - case '=': return 'Default'; - case '-': return 'Off'; - case '+': return 'On'; - default: return 'Unknown'; - } - }, - width: 65 - }, - { - xtype: 'widgetcolumn', - dataIndex: 'state', - width: 95, - onWidgetAttach: function (column, widget, record) { - var val = record.get('state') || '='; - widget.down('[inputValue=' + val + ']').setValue(true); - // TODO: disable if selected CPU model and flag are incompatible - }, - widget: { - xtype: 'radiogroup', - hideLabel: true, - layout: 'hbox', - validateOnChange: false, - value: '=', - listeners: { - change: function(f, value) { - var v = Object.values(value)[0]; - f.getWidgetRecord().set('state', v); - - var view = this.up('grid'); - view.dirty = view.getValue() !== view.originalValue; - view.checkDirty(); - //view.checkChange(); - } - }, - items: [ - { - boxLabel: '-', - boxLabelAlign: 'before', - inputValue: '-' - }, - { - checked: true, - inputValue: '=' - }, - { - boxLabel: '+', - inputValue: '+' - } - ] - } - }, - { - dataIndex: 'flag', - width: 100 - }, - { - dataIndex: 'desc', - cellWrap: true, - flex: 1 - } - ], - - initComponent: function() { - var me = this; - - // static class store, thus gets not recreated, so ensure defaults are set! - me.getStore().data.forEach(function(v) { - v.state = '='; - }); - - me.value = me.originalValue = ''; - - me.callParent(arguments); - } -}); -Ext.define('PVE.form.USBSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUSBSelector'], - allowBlank: false, - autoSelect: false, - displayField: 'usbid', - valueField: 'usbid', - editable: true, - - getUSBValue: function() { - var me = this; - var rec = me.store.findRecord('usbid', me.value); - var val = 'host='+ me.value; - if (rec && rec.data.speed === "5000") { - val = 'host=' + me.value + ",usb3=1"; - } - return val; - }, - - validator: function(value) { - var me = this; - if (me.type === 'device') { - return (/^[a-f0-9]{4}\:[a-f0-9]{4}$/i).test(value); - } else if (me.type === 'port') { - return (/^[0-9]+\-[0-9]+(\.[0-9]+)*$/).test(value); - } - return false; - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - - if (!nodename) { - throw "no nodename specified"; - } - - if (me.type !== 'device' && me.type !== 'port') { - throw "no valid type specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-usb-' + me.type, - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/scan/usb" - }, - filters: [ - function (item) { - return !!item.data.usbpath && !!item.data.prodid && item.data['class'] != 9; - } - ] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: (me.type === 'device')?gettext('Device'):gettext('Port'), - sortable: true, - dataIndex: 'usbid', - width: 80 - }, - { - header: gettext('Manufacturer'), - sortable: true, - dataIndex: 'manufacturer', - width: 100 - }, - { - header: gettext('Product'), - sortable: true, - dataIndex: 'product', - flex: 1 - }, - { - header: gettext('Speed'), - width: 70, - sortable: true, - dataIndex: 'speed', - renderer: function(value) { - if (value === "5000") { - return "USB 3.0"; - } else if (value === "480") { - return "USB 2.0"; - } else { - return "USB 1.x"; - } - } - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-usb-device', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val, data) { - if (val) { - return val; - } - return data.get('vendid') + ':' + data.get('prodid'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' } - ] - }); - - Ext.define('pve-usb-port', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val,data) { - if (val) { - return val; - } - return data.get('busnum') + '-' + data.get('usbpath'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' } - ] - }); -}); -Ext.define('PVE.form.CalendarEvent', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pveCalendarEvent', - - editable: true, - - valueField: 'value', - displayField: 'text', - queryMode: 'local', - - store: { - field: [ 'value', 'text'], - data: [ - { value: '*/30', text: Ext.String.format(gettext("Every {0} minutes"), 30) }, - { value: '*/2:00', text: gettext("Every two hours")}, - { value: '2,22:30', text: gettext("Every day") + " 02:30, 22:30"}, - { value: 'mon..fri', text: gettext("Monday to Friday") + " 00:00"}, - { value: 'mon..fri */1:00', text: gettext("Monday to Friday") + ': ' + gettext("hourly")}, - { value: 'sun 01:00', text: gettext("Sunday") + " 01:00"} - ] - }, - - tpl: [ - '
    ', - '
  • {text}
  • ', - '
' - ], - - displayTpl: [ - '', - '{value}', - '' - ] - -}); -Ext.define('PVE.form.CephPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephPoolSelector', - - allowBlank: false, - valueField: 'pool_name', - displayField: 'pool_name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/pools' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.form.PermPathSelector', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pvePermPathSelector', - - valueField: 'value', - displayField: 'value', - typeAhead: true, - queryMode: 'local', - store: { - type: 'pvePermPath' - } -}); -/* This class defines the "Tasks" tab of the bottom status panel - * Tasks are jobs with a start, end and log output - */ - -Ext.define('PVE.dc.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterTasks'], - - initComponent : function() { - var me = this; - - var taskstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-tasks', - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/tasks' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: taskstore, - sortAfterUpdate: true, - appendAtStart: true, - sorters: [ - { - property : 'pid', - direction: 'DESC' - }, - { - property : 'starttime', - direction: 'DESC' - } - ] - - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type == "vncproxy" || - record.data.type == "vncshell" || - record.data.type == "spiceproxy") { - metaData.tdCls = "x-grid-row-console"; - } else { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type != "vncproxy") { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return Proxmox.Utils.errorText + ': ' + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - show: taskstore.startUpdate, - destroy: taskstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* This class defines the "Cluster log" tab of the bottom status panel - * A log entry is a timestamp associated with an action on a cluster - */ - -Ext.define('PVE.dc.Log', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterLog'], - - initComponent : function() { - var me = this; - - var logstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-log', - model: 'proxmox-cluster-log', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/log' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: logstore, - appendAtStart: true - }); - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, - - getRowClass: function(record, index) { - var pri = record.get('pri'); - - if (pri && pri <= 3) { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Time"), - dataIndex: 'time', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 150 - }, - { - header: gettext("Service"), - dataIndex: 'tag', - width: 100 - }, - { - header: "PID", - dataIndex: 'pid', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Severity"), - dataIndex: 'pri', - renderer: PVE.Utils.render_serverity, - width: 100 - }, - { - header: gettext("Message"), - dataIndex: 'msg', - flex: 1 - } - ], - listeners: { - activate: logstore.startUpdate, - deactivate: logstore.stopUpdate, - destroy: logstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* - * This class describes the bottom panel - */ -Ext.define('PVE.panel.StatusPanel', { - extend: 'Ext.tab.Panel', - alias: 'widget.pveStatusPanel', - - - //title: "Logs", - //tabPosition: 'bottom', - - initComponent: function() { - var me = this; - - var stateid = 'ltab'; - var sp = Ext.state.Manager.getProvider(); - - var state = sp.get(stateid); - if (state && state.value) { - me.activeTab = state.value; - } - - Ext.apply(me, { - listeners: { - tabchange: function() { - var atab = me.getActiveTab().itemId; - var state = { value: atab }; - sp.set(stateid, state); - } - }, - items: [ - { - itemId: 'tasks', - title: gettext('Tasks'), - xtype: 'pveClusterTasks' - }, - { - itemId: 'clog', - title: gettext('Cluster log'), - xtype: 'pveClusterLog' - } - ] - }); - - me.callParent(); - - me.items.get(0).fireEvent('show', me.items.get(0)); - - var statechange = function(sp, key, state) { - if (key === stateid) { - var atab = me.getActiveTab().itemId; - var ntab = state.value; - if (state && ntab && (atab != ntab)) { - me.setActiveTab(ntab); - } - } - }; - - sp.on('statechange', statechange); - me.on('destroy', function() { - sp.un('statechange', statechange); - }); - - } -}); -Ext.define('PVE.panel.StatusView', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStatusView', - - layout: { - type: 'column' - }, - - title: gettext('Status'), - - getRecordValue: function(key, store) { - if (!key) { - throw "no key given"; - } - var me = this; - - if (store === undefined) { - store = me.getStore(); - } - - var rec = store.getById(key); - if (rec) { - return rec.data.value; - } - - return ''; - }, - - fieldRenderer: function(val,max) { - if (max === undefined) { - return val; - } - - if (!Ext.isNumeric(max) || max === 1) { - return PVE.Utils.render_usage(val); - } - return PVE.Utils.render_size_usage(val,max); - }, - - fieldCalculator: function(used, max) { - if (!Ext.isNumeric(max) && Ext.isNumeric(used)) { - return used; - } else if(!Ext.isNumeric(used)) { - /* we come here if the field is from a node - * where the records are not mem and maxmem - * but mem.used and mem.total - */ - if (used.used !== undefined && - used.total !== undefined) { - return used.used/used.total; - } - } - - return used/max; - }, - - updateField: function(field) { - var me = this; - var text = ''; - var renderer = me.fieldRenderer; - if (Ext.isFunction(field.renderer)) { - renderer = field.renderer; - } - if (field.multiField === true) { - field.updateValue(renderer.call(field, me.getStore().getRecord())); - } else if (field.textField !== undefined) { - field.updateValue(renderer.call(field, me.getRecordValue(field.textField))); - } else if(field.valueField !== undefined) { - var used = me.getRecordValue(field.valueField); - /*jslint confusion: true*/ - /* string and int */ - var max = field.maxField !== undefined ? me.getRecordValue(field.maxField) : 1; - - var calculate = me.fieldCalculator; - - if (Ext.isFunction(field.calculate)) { - calculate = field.calculate; - } - field.updateValue(renderer.call(field, used,max), calculate(used,max)); - } - }, - - getStore: function() { - var me = this; - if (!me.rstore) { - throw "there is no rstore"; - } - - return me.rstore; - }, - - updateTitle: function() { - var me = this; - me.setTitle(me.getRecordValue('name')); - }, - - updateValues: function(store, records, success) { - if (!success) { - return; // do not update if store load was not successful - } - var me = this; - var itemsToUpdate = me.query('pveInfoWidget'); - - itemsToUpdate.forEach(me.updateField, me); - - me.updateTitle(store); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - if (!me.title) { - throw "no title given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.callParent(); - - me.mon(me.rstore, 'load', 'updateValues'); - } - -}); -Ext.define('PVE.panel.GuestStatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveGuestStatusView', - mixins: ['Proxmox.Mixin.CBind'], - - height: 300, - - cbindData: function (initialConfig) { - var me = this; - return { - isQemu: me.pveSelNode.data.type === 'qemu', - isLxc: me.pveSelNode.data.type === 'lxc' - }; - }, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'status', - title: gettext('Status'), - iconCls: 'fa fa-info fa-fw', - printBar: false, - multiField: true, - renderer: function(record) { - var me = this; - var text = record.data.status; - var qmpstatus = record.data.qmpstatus; - if (qmpstatus && qmpstatus !== record.data.status) { - text += ' (' + qmpstatus + ')'; - } - return text; - } - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - xtype: 'pveInfoWidget', - itemId: 'node', - iconCls: 'fa fa-building fa-fw', - title: gettext('Node'), - cbind: { - text: '{pveSelNode.data.node}' - }, - printBar: false - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpus', - renderer: PVE.Utils.render_cpu_usage, - // in this specific api call - // we already have the correct value for the usage - calculate: Ext.identityFn - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory usage'), - valueField: 'mem', - maxField: 'maxmem' - }, - { - itemId: 'swap', - xtype: 'pveInfoWidget', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'maxswap', - cbind: { - hidden: '{isQemu}', - disabled: '{isQemu}' - } - }, - { - itemId: 'rootfs', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - valueField: 'disk', - maxField: 'maxdisk', - printBar: false, - renderer: function(used, max) { - var me = this; - me.setPrintBar(used > 0); - if (used === 0) { - return PVE.Utils.render_size(max); - } else { - return PVE.Utils.render_size_usage(used,max); - } - } - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'ips', - xtype: 'pveAgentIPView', - cbind: { - rstore: '{rstore}', - pveSelNode: '{pveSelNode}', - hidden: '{isLxc}', - disabled: '{isLxc}' - } - } - ], - - updateTitle: function() { - var me = this; - var uptime = me.getRecordValue('uptime'); - - var text = ""; - if (Number(uptime) > 0) { - text = " (" + gettext('Uptime') + ': ' + Proxmox.Utils.format_duration_long(uptime) - + ')'; - } - - me.setTitle(me.getRecordValue('name') + text); - } -}); -/* - * This is a running chart widget - * you add time datapoints to it, - * and we only show the last x of it - * used for ceph performance charts - */ -Ext.define('PVE.widget.RunningChart', { - extend: 'Ext.container.Container', - alias: 'widget.pveRunningChart', - - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - width: 80, - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}:

' - }, - { - flex: 1, - xtype: 'cartesian', - height: '100%', - itemId: 'chart', - border: false, - axes: [ - { - type: 'numeric', - position: 'left', - hidden: true, - minimum: 0 - }, - { - type: 'numeric', - position: 'bottom', - hidden: true - } - ], - - store: { - data: {} - }, - - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '0 B/s', - textAlign: 'end', - textBaseline: 'middle', - fontSize: 14 - }], - - series: [{ - type: 'line', - xField: 'time', - yField: 'val', - fill: 'true', - colors: ['#cfcfcf'], - tooltip: { - trackMouse: true, - renderer: function( tooltip, record, ctx) { - var me = this.getChart(); - var date = new Date(record.data.time); - var value = me.up().renderer(record.data.val); - tooltip.setHtml( - me.up().title + ': ' + value + '
' + - Ext.Date.format(date, 'H:i:s') - ); - } - }, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - } - }] - } - ], - - // the renderer for the tooltip and last value, - // default just the value - renderer: Ext.identityFn, - - // show the last x seconds - // default is 5 minutes - timeFrame: 5*60, - - addDataPoint: function(value, time) { - var me = this.chart; - var panel = me.up(); - var now = new Date(); - var begin = new Date(now.getTime() - (1000*panel.timeFrame)); - - me.store.add({ - time: time || now.getTime(), - val: value || 0 - }); - - // delete all old records when we have 20 times more datapoints - // than seconds in our timeframe (so even a subsecond graph does - // not trigger this often) - // - // records in the store do not take much space, but like this, - // we prevent a memory leak when someone has the site open for a long time - // with minimal graphical glitches - if (me.store.count() > panel.timeFrame * 20) { - var oldData = me.store.getData().createFiltered(function(item) { - return item.data.time < begin.getTime(); - }); - - me.store.remove(oldData.getRange()); - } - - me.timeaxis.setMinimum(begin.getTime()); - me.timeaxis.setMaximum(now.getTime()); - me.valuesprite.setText(panel.renderer(value || 0).toString()); - me.valuesprite.setAttributes({ - x: me.getWidth() - 15, - y: me.getHeight()/2 - }, true); - me.redraw(); - }, - - setTitle: function(title) { - this.title = title; - var me = this.getComponent('title'); - me.update({title: title}); - }, - - initComponent: function(){ - var me = this; - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.chart = me.getComponent('chart'); - me.chart.timeaxis = me.chart.getAxes()[1]; - me.chart.valuesprite = me.chart.getSurface('chart').get('valueSprite'); - if (me.color) { - me.chart.series[0].setStyle({ - fill: me.color, - stroke: me.color - }); - } - } -}); -Ext.define('PVE.widget.Info',{ - extend: 'Ext.container.Container', - alias: 'widget.pveInfoWidget', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - value: 0, - maximum: 1, - printBar: true, - items: [ - { - xtype: 'component', - itemId: 'label', - data: { - title: '', - usage: '', - iconCls: undefined - }, - tpl: [ - '
', - '', - ' ', - '', - '{title}
 
{usage}
' - ] - }, - { - height: 2, - border: 0 - }, - { - xtype: 'progressbar', - itemId: 'progress', - height: 5, - value: 0, - animate: true - } - ], - - warningThreshold: 0.6, - criticalThreshold: 0.9, - - setPrintBar: function(enable) { - var me = this; - me.printBar = enable; - me.getComponent('progress').setVisible(enable); - }, - - setIconCls: function(iconCls) { - var me = this; - me.getComponent('label').data.iconCls = iconCls; - }, - - updateValue: function(text, usage) { - var me = this; - var label = me.getComponent('label'); - label.update(Ext.apply(label.data, {title: me.title, usage:text})); - - if (usage !== undefined && - me.printBar && - Ext.isNumeric(usage) && - usage >= 0) { - var progressBar = me.getComponent('progress'); - progressBar.updateProgress(usage, ''); - if (usage > me.criticalThreshold) { - progressBar.removeCls('warning'); - progressBar.addCls('critical'); - } else if (usage > me.warningThreshold) { - progressBar.removeCls('critical'); - progressBar.addCls('warning'); - } else { - progressBar.removeCls('warning'); - progressBar.removeCls('critical'); - } - } - }, - - initComponent: function() { - var me = this; - - if (!me.title) { - throw "no title defined"; - } - - me.callParent(); - - me.getComponent('progress').setVisible(me.printBar); - - me.updateValue(me.text, me.value); - me.setIconCls(me.iconCls); - } - -}); -Ext.define('PVE.panel.TemplateStatusView',{ - extend: 'PVE.panel.StatusView', - alias: 'widget.pveTemplateStatusView', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - printBar: false, - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - itemId: 'node', - iconCls: 'fa fa-fw fa-building', - title: gettext('Node') - }, - { - xtype: 'box', - height: 20 - }, - { - itemId: 'cpus', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('Processors'), - textField: 'cpus' - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory'), - textField: 'maxmem', - renderer: PVE.Utils.render_size - }, - { - itemId: 'swap', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('Swap'), - textField: 'maxswap', - renderer: PVE.Utils.render_size - }, - { - itemId: 'disk', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - textField: 'maxdisk', - renderer: PVE.Utils.render_size - }, - { - xtype: 'box', - height: 20 - } - ], - - initComponent: function() { - var me = this; - - var name = me.pveSelNode.data.name; - if (!name) { - throw "no name specified"; - } - - me.title = name; - - me.callParent(); - if (me.pveSelNode.data.type !== 'lxc') { - me.remove(me.getComponent('swap')); - } - me.getComponent('node').updateValue(me.pveSelNode.data.node); - } -}); -Ext.define('PVE.widget.HealthWidget', { - extend: 'Ext.Component', - alias: 'widget.pveHealthWidget', - - data: { - iconCls: PVE.Utils.get_health_icon(undefined, true), - text: '', - title: '' - }, - - style: { - 'text-align':'center' - }, - - tpl: [ - '

{title}

', - '', - '

', - '{text}' - ], - - updateHealth: function(data) { - var me = this; - me.update(Ext.apply(me.data, data)); - }, - - initComponent: function(){ - var me = this; - - if (me.title) { - me.config.data.title = me.title; - } - - me.callParent(); - } - -}); -/*global u2f*/ -Ext.define('PVE.window.LoginWindow', { - extend: 'Ext.window.Window', - - controller: { - - xclass: 'Ext.app.ViewController', - - onLogon: function() { - var me = this; - - var form = this.lookupReference('loginForm'); - var unField = this.lookupReference('usernameField'); - var saveunField = this.lookupReference('saveunField'); - var view = this.getView(); - - if (!form.isValid()) { - return; - } - - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - - // set or clear username - var sp = Ext.state.Manager.getProvider(); - if (saveunField.getValue() === true) { - sp.set(unField.getStateId(), unField.getValue()); - } else { - sp.clear(unField.getStateId()); - } - sp.set(saveunField.getStateId(), saveunField.getValue()); - - form.submit({ - failure: function(f, resp){ - me.failure(resp); - }, - success: function(f, resp){ - view.el.unmask(); - - var data = resp.result.data; - if (Ext.isDefined(data.NeedTFA)) { - // Store first factor login information first: - data.LoggedOut = true; - Proxmox.Utils.setAuthData(data); - - if (Ext.isDefined(data.U2FChallenge)) { - me.perform_u2f(data); - } else { - me.perform_otp(); - } - } else { - me.success(data); - } - } - }); - - }, - failure: function(resp) { - var me = this; - var view = me.getView(); - view.el.unmask(); - var handler = function() { - var uf = me.lookupReference('usernameField'); - uf.focus(true, true); - }; - - Ext.MessageBox.alert(gettext('Error'), - gettext("Login failed. Please try again"), - handler); - }, - success: function(data) { - var me = this; - var view = me.getView(); - var handler = view.handler || Ext.emptyFn; - handler.call(me, data); - view.close(); - }, - - perform_otp: function() { - var me = this; - var win = Ext.create('PVE.window.TFALoginWindow', { - onLogin: function(value) { - me.finish_tfa(value); - }, - onCancel: function() { - Proxmox.LoggedOut = false; - Proxmox.Utils.authClear(); - me.getView().show(); - } - }); - win.show(); - }, - - perform_u2f: function(data) { - var me = this; - // Show the message: - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Verification'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - var chlg = data.U2FChallenge; - var key = { - version: chlg.version, - keyHandle: chlg.keyHandle - }; - u2f.sign(chlg.appId, chlg.challenge, [key], function(res) { - msg.close(); - if (res.errorCode) { - Proxmox.Utils.authClear(); - Ext.Msg.alert(gettext('Error'), PVE.Utils.render_u2f_error(res.errorCode)); - return; - } - delete res.errorCode; - me.finish_tfa(JSON.stringify(res)); - }); - }, - finish_tfa: function(res) { - var me = this; - var view = me.getView(); - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - var params = { response: res }; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'POST', - timeout: 5000, // it'll delay both success & failure - success: function(resp, opts) { - view.el.unmask(); - // Fill in what we copy over from the 1st factor: - var data = resp.result.data; - data.CSRFPreventionToken = Proxmox.CSRFPreventionToken; - data.username = Proxmox.UserName; - // Finish logging in: - me.success(data); - }, - failure: function(resp, opts) { - Proxmox.Utils.authClear(); - me.failure(resp); - } - }); - }, - - control: { - 'field[name=username]': { - specialkey: function(f, e) { - if (e.getKey() === e.ENTER) { - var pf = this.lookupReference('passwordField'); - if (!pf.getValue()) { - pf.focus(false); - } - } - } - }, - 'field[name=lang]': { - change: function(f, value) { - var dt = Ext.Date.add(new Date(), Ext.Date.YEAR, 10); - Ext.util.Cookies.set('PVELangCookie', value, dt); - this.getView().mask(gettext('Please wait...'), 'x-mask-loading'); - window.location.reload(); - } - }, - 'button[reference=loginButton]': { - click: 'onLogon' - }, - '#': { - show: function() { - var sp = Ext.state.Manager.getProvider(); - var checkboxField = this.lookupReference('saveunField'); - var unField = this.lookupReference('usernameField'); - - var checked = sp.get(checkboxField.getStateId()); - checkboxField.setValue(checked); - - if(checked === true) { - var username = sp.get(unField.getStateId()); - unField.setValue(username); - var pwField = this.lookupReference('passwordField'); - pwField.focus(); - } - } - } - } - }, - - width: 400, - - modal: true, - - border: false, - - draggable: true, - - closable: false, - - resizable: false, - - layout: 'auto', - - title: gettext('Proxmox VE Login'), - - defaultFocus: 'usernameField', - - defaultButton: 'loginButton', - - items: [{ - xtype: 'form', - layout: 'form', - url: '/api2/extjs/access/ticket', - reference: 'loginForm', - - fieldDefaults: { - labelAlign: 'right', - allowBlank: false - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('User name'), - name: 'username', - itemId: 'usernameField', - reference: 'usernameField', - stateId: 'login-username' - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - name: 'password', - reference: 'passwordField' - }, - { - xtype: 'pveRealmComboBox', - name: 'realm' - }, - { - xtype: 'proxmoxLanguageSelector', - fieldLabel: gettext('Language'), - value: Ext.util.Cookies.get('PVELangCookie') || Proxmox.defaultLang || 'en', - name: 'lang', - reference: 'langField', - submitValue: false - } - ], - buttons: [ - { - xtype: 'checkbox', - fieldLabel: gettext('Save User name'), - name: 'saveusername', - reference: 'saveunField', - stateId: 'login-saveusername', - labelWidth: 'auto', - labelAlign: 'right', - submitValue: false - }, - { - text: gettext('Login'), - reference: 'loginButton' - } - ] - }] - }); -Ext.define('PVE.window.TFALoginWindow', { - extend: 'Ext.window.Window', - - modal: true, - resizable: false, - title: 'Two-Factor Authentication', - layout: 'form', - defaultButton: 'loginButton', - defaultFocus: 'otpField', - - controller: { - xclass: 'Ext.app.ViewController', - login: function() { - var me = this; - var view = me.getView(); - view.onLogin(me.lookup('otpField').getValue()); - view.close(); - }, - cancel: function() { - var me = this; - var view = me.getView(); - view.onCancel(); - view.close(); - } - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Please enter your OTP verification code:'), - name: 'otp', - itemId: 'otpField', - reference: 'otpField', - allowBlank: false - } - ], - - buttons: [ - { - text: gettext('Login'), - reference: 'loginButton', - handler: 'login' - }, - { - text: gettext('Cancel'), - handler: 'cancel' - } - ] -}); -Ext.define('PVE.window.Wizard', { - extend: 'Ext.window.Window', - - activeTitle: '', // used for automated testing - - width: 700, - height: 510, - - modal: true, - border: false, - - draggable: true, - closable: true, - resizable: false, - - layout: 'border', - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.down('form').getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - initComponent: function() { - var me = this; - - var tabs = me.items || []; - delete me.items; - - /* - * Items may have the following functions: - * validator(): per tab custom validation - * onSubmit(): submit handler - * onGetValues(): overwrite getValues results - */ - - Ext.Array.each(tabs, function(tab) { - tab.disabled = true; - }); - tabs[0].disabled = false; - - var maxidx = 0; - var curidx = 0; - - var check_card = function(card) { - var valid = true; - var fields = card.query('field, fieldcontainer'); - if (card.isXType('fieldcontainer')) { - fields.unshift(card); - } - Ext.Array.each(fields, function(field) { - // Note: not all fielcontainer have isValid() - if (Ext.isFunction(field.isValid) && !field.isValid()) { - valid = false; - } - }); - - if (Ext.isFunction(card.validator)) { - return card.validator(); - } - - return valid; - }; - - var disable_at = function(card) { - var tp = me.down('#wizcontent'); - var idx = tp.items.indexOf(card); - for(;idx < tp.items.getCount();idx++) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }; - - var tabchange = function(tp, newcard, oldcard) { - if (newcard.onSubmit) { - me.down('#next').setVisible(false); - me.down('#submit').setVisible(true); - } else { - me.down('#next').setVisible(true); - me.down('#submit').setVisible(false); - } - var valid = check_card(newcard); - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - me.down('#back').setDisabled(tp.items.indexOf(newcard) == 0); - - var idx = tp.items.indexOf(newcard); - if (idx > maxidx) { - maxidx = idx; - } - curidx = idx; - - var next = idx + 1; - var ntab = tp.items.getAt(next); - if (valid && ntab && !newcard.onSubmit) { - ntab.enable(); - } - }; - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, true, false); - } - - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - - Ext.apply(me, { - items: [ - { - xtype: 'form', - region: 'center', - layout: 'fit', - border: false, - margins: '5 5 0 5', - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [{ - itemId: 'wizcontent', - xtype: 'tabpanel', - activeItem: 0, - bodyPadding: 10, - listeners: { - afterrender: function(tp) { - var atab = this.getActiveTab(); - tabchange(tp, atab); - }, - tabchange: function(tp, newcard, oldcard) { - tabchange(tp, newcard, oldcard); - } - }, - items: tabs - }] - } - ], - fbar: [ - { - xtype: 'proxmoxHelpButton', - itemId: 'help' - }, - '->', - { - xtype: 'proxmoxcheckbox', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - value: advchecked, - listeners: { - change: function(cb, val) { - var tp = me.down('#wizcontent'); - tp.query('inputpanel').forEach(function(ip) { - ip.setAdvancedVisible(val); - }); - - sp.set('proxmox-advanced-cb', val); - } - } - }, - { - text: gettext('Back'), - disabled: true, - itemId: 'back', - minWidth: 60, - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - var prev = tp.items.indexOf(atab) - 1; - if (prev < 0) { - return; - } - var ntab = tp.items.getAt(prev); - if (ntab) { - tp.setActiveTab(ntab); - } - } - }, - { - text: gettext('Next'), - disabled: true, - itemId: 'next', - minWidth: 60, - handler: function() { - - var form = me.down('form').getForm(); - - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - if (!check_card(atab)) { - return; - } - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - - } - }, - { - text: gettext('Finish'), - minWidth: 60, - hidden: true, - itemId: 'submit', - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - atab.onSubmit(); - } - } - ] - }); - me.callParent(); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setAdvancedVisible(advchecked); - }); - - Ext.Array.each(me.query('field'), function(field) { - var validcheck = function() { - var tp = me.down('#wizcontent'); - - // check tabs from current to the last enabled for validity - // since we might have changed a validity on a later one - var i; - for (i = curidx; i <= maxidx && i < tp.items.getCount(); i++) { - var tab = tp.items.getAt(i); - var valid = check_card(tab); - - // only set the buttons on the current panel - if (i === curidx) { - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - } - - // if a panel is invalid, then disable it and all following, - // else enable it and go to the next - var ntab = tp.items.getAt(i + 1); - if (!valid) { - disable_at(ntab); - return; - } else if (ntab && !tab.onSubmit) { - ntab.enable(); - } - } - }; - field.on('change', validcheck); - field.on('validitychange', validcheck); - }); - } -}); -Ext.define('PVE.window.NotesEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - title: gettext('Notes'), - width: 600, - height: '400px', - resizable: true, - layout: 'fit', - defaultButton: undefined, - items: { - xtype: 'textarea', - name: 'description', - height: '100%', - value: '', - hideLabel: true - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.window.Backup', { - extend: 'Ext.window.Window', - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.vmtype) { - throw "no VM type specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: me.storage, - fieldLabel: gettext('Storage'), - storageContent: 'backup', - allowBlank: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - storagesel, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'pveCompressionSelector', - name: 'compress', - value: 'lzo', - fieldLabel: gettext('Compression') - }, - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto', - emptyText: Proxmox.Utils.noneText - } - ] - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Backup'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - var params = { - storage: storage, - vmid: me.vmid, - mode: values.mode, - remove: 0 - }; - - if ( values.mailto ) { - params.mailto = values.mailto; - } - - if (values.compress) { - params.compress = values.compress; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/vzdump', - params: params, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - }, - success: function(response, options) { - // close later so we reload the grid - // after the task has completed - me.hide(); - - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - close: function() { - me.close(); - } - } - }); - win.show(); - } - }); - } - }); - - var helpBtn = Ext.create('Proxmox.button.Help', { - onlineHelp: 'chapter_vzdump', - listenToGlobalEvent: false, - hidden: false - }); - - var title = gettext('Backup') + " " + - ((me.vmtype === 'lxc') ? "CT" : "VM") + - " " + me.vmid; - - Ext.apply(me, { - title: title, - width: 350, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ helpBtn, '->', submitBtn ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.window.Restore', { - extend: 'Ext.window.Window', // fixme: Proxmox.window.Edit? - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.volid) { - throw "no volume ID specified"; - } - - if (!me.vmtype) { - throw "no vmtype specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: '', - fieldLabel: gettext('Storage'), - storageContent: (me.vmtype === 'lxc') ? 'rootdir' : 'images', - allowBlank: true - }); - - var IDfield; - if (me.vmid) { - IDfield = Ext.create('Ext.form.field.Display', { - name: 'vmid', - value: me.vmid, - fieldLabel: (me.vmtype === 'lxc') ? 'CT' : 'VM' - }); - } else { - IDfield = Ext.create('PVE.form.GuestIDSelector', { - name: 'vmid', - guestType: me.vmtype, - loadNextFreeID: true, - validateExists: false - }); - } - - var items = [ - { - xtype: 'displayfield', - value: me.volidText || me.volid, - fieldLabel: gettext('Source') - }, - storagesel, - IDfield, - { - xtype: 'proxmoxintegerfield', - name: 'bwlimit', - fieldLabel: gettext('Read Limit (MiB/s)'), - minValue: 0, - emptyText: gettext('Defaults to target storage restore limit'), - autoEl: { - tag: 'div', - 'data-qtip': gettext("Use '0' to disable all bandwidth limits.") - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'unique', - fieldLabel: gettext('Unique'), - hidden: !!me.vmid, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses') - }, - checked: false - } - ]; - - /*jslint confusion: true*/ - if (me.vmtype === 'lxc') { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - fieldLabel: gettext('Unprivileged container') - }); - } - /*jslint confusion: false*/ - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var doRestore = function(url, params) { - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - waitMsgTarget: me, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.close(); - } - }); - }; - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Restore'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - - var params = { - storage: storage, - vmid: me.vmid || values.vmid, - force: me.vmid ? 1 : 0 - }; - if (values.unique) { params.unique = 1; } - - if (values.bwlimit !== undefined) { - params.bwlimit = values.bwlimit * 1024; - } - - var url; - var msg; - if (me.vmtype === 'lxc') { - url = '/nodes/' + me.nodename + '/lxc'; - params.ostemplate = me.volid; - params.restore = 1; - if (values.unprivileged) { params.unprivileged = 1; } - msg = Proxmox.Utils.format_task_description('vzrestore', params.vmid); - } else if (me.vmtype === 'qemu') { - url = '/nodes/' + me.nodename + '/qemu'; - params.archive = me.volid; - msg = Proxmox.Utils.format_task_description('qmrestore', params.vmid); - } else { - throw 'unknown VM type'; - } - - if (me.vmid) { - msg += '. ' + gettext('This will permanently erase current VM data.'); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - doRestore(url, params); - }); - } else { - doRestore(url, params); - } - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - var title = gettext('Restore') + ": " + ( - (me.vmtype === 'lxc') ? 'CT' : 'VM'); - - if (me.vmid) { - title += " " + me.vmid; - } - - Ext.apply(me, { - title: title, - width: 500, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); -/* Popup a message window - * where the user has to manually enter the resource ID - * to enable the destroy button - */ -Ext.define('PVE.window.SafeDestroy', { - extend: 'Ext.window.Window', - alias: 'widget.pveSafeDestroy', - - title: gettext('Confirm'), - modal: true, - buttonAlign: 'center', - bodyPadding: 10, - width: 450, - layout: { type:'hbox' }, - defaultFocus: 'confirmField', - showProgress: false, - - config: { - item: { - id: undefined, - type: undefined - }, - url: undefined, - params: {} - }, - - getParams: function() { - var me = this; - if (Ext.Object.isEmpty(me.params)) { - return ''; - } - return '?' + Ext.Object.toQueryString(me.params); - }, - - controller: { - - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=confirm]': { - change: function(f, value) { - var view = this.getView(); - var removeButton = this.lookupReference('removeButton'); - if (value === view.getItem().id.toString()) { - removeButton.enable(); - } else { - removeButton.disable(); - } - }, - specialkey: function (field, event) { - var removeButton = this.lookupReference('removeButton'); - if (!removeButton.isDisabled() && event.getKey() == event.ENTER) { - removeButton.fireEvent('click', removeButton, event); - } - } - }, - 'button[reference=removeButton]': { - click: function() { - var view = this.getView(); - Proxmox.Utils.API2Request({ - url: view.getUrl() + view.getParams(), - method: 'DELETE', - waitMsgTarget: view, - failure: function(response, opts) { - view.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = view.showProgress && - response.result.data ? true : false; - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - view.hide(); - - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: function () { - view.close(); - } - } - }); - win.show(); - } else { - view.close(); - } - } - }); - } - } - } - }, - - items: [ - { - xtype: 'component', - cls: [ Ext.baseCSSPrefix + 'message-box-icon', - Ext.baseCSSPrefix + 'message-box-warning', - Ext.baseCSSPrefix + 'dlg-icon'] - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'component', - reference: 'messageCmp' - }, - { - itemId: 'confirmField', - reference: 'confirmField', - xtype: 'textfield', - name: 'confirm', - labelWidth: 300, - hideTrigger: true, - allowBlank: false - } - ] - } - ], - buttons: [ - { - reference: 'removeButton', - text: gettext('Remove'), - disabled: true - } - ], - - initComponent : function() { - var me = this; - - me.callParent(); - - var item = me.getItem(); - - if (!Ext.isDefined(item.id)) { - throw "no ID specified"; - } - - if (!Ext.isDefined(item.type)) { - throw "no VM type specified"; - } - - var messageCmp = me.lookupReference('messageCmp'); - var msg; - - if (item.type === 'VM') { - msg = Proxmox.Utils.format_task_description('qmdestroy', item.id); - } else if (item.type === 'CT') { - msg = Proxmox.Utils.format_task_description('vzdestroy', item.id); - } else if (item.type === 'CephPool') { - msg = Proxmox.Utils.format_task_description('cephdestroypool', item.id); - } else if (item.type === 'Image') { - msg = Proxmox.Utils.format_task_description('unknownimgdel', item.id); - } else { - throw "unknown item type specified"; - } - - messageCmp.setHtml(msg); - - var confirmField = me.lookupReference('confirmField'); - msg = gettext('Please enter the ID to confirm') + - ' (' + item.id + ')'; - confirmField.setFieldLabel(msg); - } -}); -Ext.define('PVE.window.BackupConfig', { - extend: 'Ext.window.Window', - title: gettext('Configuration'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: { - xtype: 'component', - itemId: 'configtext', - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }, - - initComponent: function() { - var me = this; - - if (!me.volume) { - throw "no volume specified"; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.callParent(); - - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/vzdump/extractconfig", - method: 'GET', - params: { - volume: me.volume - }, - failure: function(response, opts) { - me.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response,options) { - me.show(); - me.down('#configtext').update(Ext.htmlEncode(response.result.data)); - } - }); - } -}); -Ext.define('PVE.window.Settings', { - extend: 'Ext.window.Window', - - width: '800px', - title: gettext('My Settings'), - iconCls: 'fa fa-gear', - modal: true, - bodyPadding: 10, - resizable: false, - - buttons: [ - { - xtype: 'proxmoxHelpButton', - onlineHelp: 'gui_my_settings', - hidden: false - }, - '->', - { - text: gettext('Close'), - handler: function() { - this.up('window').close(); - } - } - ], - - layout: { - type: 'hbox', - align: 'top' - }, - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - var username = sp.get('login-username') || Proxmox.Utils.noneText; - me.lookupReference('savedUserName').setValue(username); - - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var val = localStorage.getItem('pve-xterm-' + setting); - if (val !== undefined && val !== null) { - var field = me.lookup(setting); - field.setValue(val); - field.resetOriginalValue(); - } - }); - }, - - set_button_status: function() { - var me = this; - - var form = me.lookup('xtermform'); - var valid = form.isValid(); - var dirty = form.isDirty(); - - var hasvalues = false; - var values = form.getValues(); - Ext.Object.eachValue(values, function(value) { - if (value) { - hasvalues = true; - return false; - } - }); - - me.lookup('xtermsave').setDisabled(!dirty || !valid); - me.lookup('xtermreset').setDisabled(!hasvalues); - }, - - control: { - '#xtermjs form': { - dirtychange: 'set_button_status', - validitychange: 'set_button_status' - }, - '#xtermjs button': { - click: function(button) { - var me = this; - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var field = me.lookup(setting); - if (button.reference === 'xtermsave') { - var value = field.getValue(); - if (value) { - localStorage.setItem('pve-xterm-' + setting, value); - } else { - localStorage.removeItem('pve-xterm-' + setting); - } - } else if (button.reference === 'xtermreset') { - field.setValue(undefined); - localStorage.removeItem('pve-xterm-' + setting); - } - field.resetOriginalValue(); - }); - me.set_button_status(); - } - }, - 'button[name=reset]': { - click: function () { - var blacklist = ['GuiCap', 'login-username', 'dash-storages']; - var sp = Ext.state.Manager.getProvider(); - var state; - for (state in sp.state) { - if (sp.state.hasOwnProperty(state)) { - if (blacklist.indexOf(state) !== -1) { - continue; - } - - sp.clear(state); - } - } - - window.location.reload(); - } - }, - 'button[name=clear-username]': { - click: function () { - var me = this; - var usernamefield = me.lookupReference('savedUserName'); - var sp = Ext.state.Manager.getProvider(); - - usernamefield.setValue(Proxmox.Utils.noneText); - sp.clear('login-username'); - } - }, - 'grid[reference=dashboard-storages]': { - selectionchange: function(grid, selected) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - // saves the selected storageids as - // "id1,id2,id3,..." - // or clears the variable - if (selected.length > 0) { - sp.set('dash-storages', - Ext.Array.pluck(selected, 'id').join(',')); - } else { - sp.clear('dash-storages'); - } - }, - afterrender: function(grid) { - var me = grid; - var sp = Ext.state.Manager.getProvider(); - var store = me.getStore(); - var items = []; - me.suspendEvent('selectionchange'); - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - // we have to get the records - // to be able to select them - if (storage !== '') { - var item = store.getById(storage); - if (item) { - items.push(item); - } - } - }); - me.getSelectionModel().select(items); - me.resumeEvent('selectionchange'); - } - } - } - }, - - items: [{ - xtype: 'fieldset', - width: '50%', - title: gettext('Webinterface Settings'), - margin: '5', - layout: { - type: 'vbox', - align: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Dashboard Storages'), - labelAlign: 'left', - labelWidth: '50%' - }, - { - xtype: 'grid', - maxHeight: 150, - reference: 'dashboard-storages', - selModel: { - selType: 'checkboxmodel' - }, - columns: [{ - header: gettext('Name'), - dataIndex: 'storage', - flex: 1 - },{ - header: gettext('Node'), - dataIndex: 'node', - flex: 1 - }], - store: { - type: 'diff', - field: ['type', 'storage', 'id', 'node'], - rstore: PVE.data.ResourceStore, - filters: [{ - property: 'type', - value: 'storage' - }], - sorters: [ 'node','storage'] - } - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Saved User name'), - labelAlign: 'left', - labelWidth: '50%', - stateId: 'login-username', - reference: 'savedUserName', - value: '' - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Clear User name'), - width: 'auto', - name: 'clear-username' - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Layout'), - labelAlign: 'left', - labelWidth: '50%' - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Reset Layout'), - width: 'auto', - name: 'reset' - } - ] - },{ - xtype: 'fieldset', - itemId: 'xtermjs', - width: '50%', - margin: '5', - title: gettext('xterm.js Settings'), - items: [{ - xtype: 'form', - reference: 'xtermform', - border: false, - layout: { - type: 'vbox', - algin: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0' - }, - items: [ - { - xtype: 'textfield', - name: 'fontFamily', - reference: 'fontFamily', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Font-Family') - }, - { - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - name: 'fontSize', - reference: 'fontSize', - minValue: 1, - fieldLabel: gettext('Font-Size') - }, - { - xtype: 'numberfield', - name: 'letterSpacing', - reference: 'letterSpacing', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Letter Spacing') - }, - { - xtype: 'numberfield', - name: 'lineHeight', - minValue: 0.1, - reference: 'lineHeight', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Line Height') - }, - { - xtype: 'container', - layout: { - type: 'hbox', - pack: 'end' - }, - items: [ - { - xtype: 'button', - reference: 'xtermreset', - disabled: true, - text: gettext('Reset') - }, - { - xtype: 'button', - reference: 'xtermsave', - disabled: true, - text: gettext('Save') - } - ] - } - ] - }] - }], - - onShow: function() { - var me = this; - me.callParent(); - } -}); -Ext.define('PVE.panel.StartupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'qm_startup_and_shutdown', - - onGetValues: function(values) { - var me = this; - - var res = PVE.Parser.printStartup(values); - - if (res === undefined || res === '') { - return { 'delete': 'startup' }; - } - - return { startup: res }; - }, - - setStartup: function(value) { - var me = this; - - var startup = PVE.Parser.parseStartup(value); - if (startup) { - me.setValues(startup); - } - }, - - initComponent : function() { - var me = this; - - me.items = [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Shutdown timeout') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.window.StartupEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveWindowStartupEdit', - onlineHelp: undefined, - - initComponent : function() { - - var me = this; - var ipanelConfig = me.onlineHelp ? {onlineHelp: me.onlineHelp} : {}; - var ipanel = Ext.create('PVE.panel.StartupInputPanel', ipanelConfig); - - Ext.applyIf(me, { - subject: gettext('Start/Shutdown order'), - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - ipanel.setStartup(me.vmconfig.startup); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.Install', { - extend: 'Ext.window.Window', - xtype: 'pveCephInstallWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 220, - header: false, - resizable: false, - draggable: false, - modal: true, - nodename: undefined, - shadow: false, - border: false, - bodyBorder: false, - closable: false, - cls: 'install-mask', - bodyCls: 'install-mask', - layout: { - align: 'stretch', - pack: 'center', - type: 'vbox' - }, - viewModel: { - data: { - cephVersion: 'nautilus', - isInstalled: false - }, - formulas: { - buttonText: function (get){ - if (get('isInstalled')) { - return gettext('Configure Ceph'); - } else { - return gettext('Install Ceph-') + get('cephVersion'); - } - }, - windowText: function (get) { - if (get('isInstalled')) { - return '

' + - Ext.String.format(gettext('{0} is not initialized.'), 'Ceph') + ' '+ - gettext('You need to create a initial config once.') + '

'; - } else { - return '

' + - Ext.String.format(gettext('{0} is not installed on this node.'), 'Ceph') + '
' + - gettext('Would you like to install it now?') + '

'; - } - } - } - }, - items: [ - { - bind: { - html: '{windowText}' - }, - border: false, - padding: 5, - bodyCls: 'install-mask' - - }, - { - xtype: 'button', - bind: { - text: '{buttonText}' - }, - viewModel: {}, - cbind: { - nodename: '{nodename}' - }, - handler: function() { - var me = this.up('pveCephInstallWindow'); - var win = Ext.create('PVE.ceph.CephInstallWizard',{ - nodename: me.nodename - }); - win.getViewModel().set('isInstalled', this.getViewModel().get('isInstalled')); - win.show(); - me.mon(win,'beforeClose', function(){ - me.fireEvent("cephInstallWindowClosed"); - me.close(); - }); - - } - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallEnableEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveFirewallEnableEdit'], - mixins: ['Proxmox.Mixin.CBind'], - - subject: gettext('Firewall'), - cbindData: { - defaultValue: 0 - }, - width: 350, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - uncheckedValue: 0, - cbind: { - defaultValue: '{defaultValue}', - checked: '{defaultValue}' - }, - deleteDefaultValue: false, - fieldLabel: gettext('Firewall') - }, - { - xtype: 'displayfield', - name: 'warning', - userCls: 'pve-hint', - value: gettext('Warning: Firewall still disabled at datacenter level!'), - hidden: true - } - ], - - beforeShow: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/cluster/firewall/options', - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - if (!response.result.data.enable) { - me.down('displayfield[name=warning]').setVisible(true); - } - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallLograteInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveFirewallLograteInputPanel', - - viewModel: {}, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - reference: 'enable', - fieldLabel: gettext('Enable'), - value: true - }, - { - layout: 'hbox', - border: false, - items: [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Log rate limit'), - minValue: 1, - maxValue: 99, - allowBlank: false, - flex: 2, - value: 1 - }, - { - xtype: 'box', - html: '
/
' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'unit', - comboItems: [['second', 'second'], ['minute', 'minute'], - ['hour', 'hour'], ['day', 'day']], - allowBlank: false, - flex: 1, - value: 'second' - } - ] - }, - { - xtype: 'numberfield', - name: 'burst', - fieldLabel: gettext('Log burst limit'), - minValue: 1, - maxValue: 99, - value: 5 - } - ], - - onGetValues: function(values) { - var me = this; - - var vals = {}; - vals.enable = values.enable !== undefined ? 1 : 0; - vals.rate = values.rate + '/' + values.unit; - vals.burst = values.burst; - var properties = PVE.Parser.printPropertyString(vals, undefined); - if (properties == '') { - return { 'delete': 'log_ratelimit' }; - } - return { log_ratelimit: properties }; - }, - - setValues: function(values) { - var me = this; - - var properties = {}; - if (values.log_ratelimit !== undefined) { - properties = PVE.Parser.parsePropertyString(values.log_ratelimit, 'enable'); - if (properties.rate) { - var matches = properties.rate.match(/^(\d+)\/(second|minute|hour|day)$/); - if (matches) { - properties.rate = matches[1]; - properties.unit = matches[2]; - } - } - } - me.callParent([properties]); - } -}); - -Ext.define('PVE.FirewallLograteEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveFirewallLograteEdit', - - subject: gettext('Log rate limit'), - - items: [{ - xtype: 'pveFirewallLograteInputPanel' - }], - autoLoad: true -}); -Ext.define('PVE.panel.NotesView', { - extend: 'Ext.panel.Panel', - xtype: 'pveNotesView', - - title: gettext("Notes"), - bodyStyle: 'white-space:pre', - bodyPadding: 10, - scrollable: true, - - tbar: { - itemId: 'tbar', - hidden: true, - items: [ - { - text: gettext('Edit'), - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - } - ] - }, - - run_editor: function() { - var me = this; - var win = Ext.create('PVE.window.NotesEdit', { - pveSelNode: me.pveSelNode, - url: me.url - }); - win.show(); - win.on('destroy', me.load, me); - }, - - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data.description || ''; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - listeners: { - render: function(c) { - var me = this; - me.getEl().on('dblclick', me.run_editor, me); - } - }, - - tools: [{ - type: 'gear', - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - }], - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var type = me.pveSelNode.data.type; - if (!Ext.Array.contains(['node', 'qemu', 'lxc'], type)) { - throw 'invalid type specified'; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid && type !== 'node') { - throw "no VM ID specified"; - } - - me.url = '/api2/extjs/nodes/' + nodename + '/'; - - // add the type specific path if qemu/lxc - if (type === 'qemu' || type === 'lxc') { - me.url += type + '/' + vmid + '/'; - } - - me.url += 'config'; - - me.callParent(); - if (type === 'node') { - me.down('#tbar').setVisible(true); - } - me.load(); - } -}); -Ext.define('PVE.grid.ResourceGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveResourceGrid'], - - border: false, - defaultSorter: { - property: 'type', - direction: 'ASC' - }, - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - var coldef = rstore.defaultColumns(); - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: me.defaultSorter, - proxy: { type: 'memory' } - }); - - var textfilter = ''; - - var textfilter_match = function(item) { - var match = false; - Ext.each(['name', 'storage', 'node', 'type', 'text'], function(field) { - var v = item.data[field]; - if (v !== undefined) { - v = v.toLowerCase(); - if (v.indexOf(textfilter) >= 0) { - match = true; - return false; - } - } - }); - return match; - }; - - var updateGrid = function() { - - var filterfn = me.viewFilter ? me.viewFilter.filterfn : null; - - //console.log("START GRID UPDATE " + me.viewFilter); - - store.suspendEvents(); - - var nodeidx = {}; - var gather_child_nodes = function(cn) { - if (!cn) { - return; - } - var cs = cn.childNodes; - if (!cs) { - return; - } - var len = cs.length, i = 0, n, res; - - for (; i < len; i++) { - var child = cs[i]; - var orgnode = rstore.data.get(child.data.id); - if (orgnode) { - if ((!filterfn || filterfn(child)) && - (!textfilter || textfilter_match(child))) { - nodeidx[child.data.id] = orgnode; - } - } - gather_child_nodes(child); - } - }; - gather_child_nodes(me.pveSelNode); - - // remove vanished items - var rmlist = []; - store.each(function(olditem) { - var item = nodeidx[olditem.data.id]; - if (!item) { - //console.log("GRID REM UID: " + olditem.data.id); - rmlist.push(olditem); - } - }); - - if (rmlist.length) { - store.remove(rmlist); - } - - // add new items - var addlist = []; - var key; - for (key in nodeidx) { - if (nodeidx.hasOwnProperty(key)) { - var item = nodeidx[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var olditem = store.getById(item.data.id); - var olditem = store.data.get(item.data.id); - - if (!olditem) { - //console.log("GRID ADD UID: " + item.data.id); - var info = Ext.apply({}, item.data); - var child = Ext.create(store.model, info); - addlist.push(item); - continue; - } - // try to detect changes - var changes = false; - var fieldkeys = PVE.data.ResourceStore.fieldNames; - var fieldcount = fieldkeys.length; - var fieldind; - for (fieldind = 0; fieldind < fieldcount; fieldind++) { - var field = fieldkeys[fieldind]; - if (field != 'id' && item.data[field] != olditem.data[field]) { - changes = true; - //console.log("changed item " + item.id + " " + field + " " + item.data[field] + " != " + olditem.data[field]); - olditem.beginEdit(); - olditem.set(field, item.data[field]); - } - } - if (changes) { - olditem.endEdit(true); - olditem.commit(true); - } - } - } - - if (addlist.length) { - store.add(addlist); - } - - store.sort(); - - store.resumeEvents(); - - store.fireEvent('refresh', store); - - //console.log("END GRID UPDATE"); - }; - - var filter_task = new Ext.util.DelayedTask(function(){ - updateGrid(); - }); - - var load_cb = function() { - updateGrid(); - }; - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-resource', - tbar: [ - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - value: textfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - var v = field.getValue(); - textfilter = v.toLowerCase(); - filter_task.delay(500); - } - } - } - ], - viewConfig: { - stripeRows: true - }, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - destroy: function() { - rstore.un("load", load_cb); - } - }, - columns: coldef - }); - me.callParent(); - updateGrid(); - rstore.on("load", load_cb); - } -}); -Ext.define('PVE.pool.AddVM', { - extend: 'Proxmox.window.Edit', - width: 600, - height: 400, - isAdd: true, - isCreate: true, - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - var vmsField = Ext.create('Ext.form.field.Text', { - name: 'vms', - hidden: true, - allowBlank: false - }); - - var vmStore = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property: 'vmid', - order: 'ASC' - } - ], - filters: [ - function(item) { - return ((item.data.type === 'lxc' || item.data.type === 'qemu') && item.data.pool === ''); - } - ] - }); - - var vmGrid = Ext.create('widget.grid',{ - store: vmStore, - border: true, - height: 300, - scrollable: true, - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected, opts) { - var selectedVms = []; - selected.forEach(function(vm) { - selectedVms.push(vm.data.vmid); - }); - vmsField.setValue(selectedVms); - } - } - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - Ext.apply(me, { - subject: gettext('Virtual Machine'), - items: [ vmsField, vmGrid ] - }); - - me.callParent(); - vmStore.load(); - } -}); - -Ext.define('PVE.pool.AddStorage', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.isCreate = true; - me.isAdd = true; - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - Ext.apply(me, { - subject: gettext('Storage'), - width: 350, - items: [ - { - xtype: 'pveStorageSelector', - name: 'storage', - nodename: 'localhost', - autoSelect: false, - value: '', - fieldLabel: gettext("Storage") - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.grid.PoolMembers', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pvePoolMembers'], - - // fixme: dynamic status update ? - - stateful: true, - stateId: 'grid-pool-members', - - initComponent : function() { - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ], - proxy: { - type: 'proxmox', - root: 'data.members', - url: "/api2/json/pools/" + me.pool - } - }); - - var coldef = PVE.data.ResourceStore.defaultColumns(); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - var params = { 'delete': 1 }; - if (rec.data.type === 'storage') { - params.storage = rec.data.storage; - } else if (rec.data.type === 'qemu' || rec.data.type === 'lxc' || rec.data.type === 'openvz') { - params.vms = rec.data.vmid; - } else { - throw "unknown resource type"; - } - - Proxmox.Utils.API2Request({ - url: '/pools/' + me.pool, - method: 'PUT', - params: params, - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Virtual Machine'), - iconCls: 'pve-itype-icon-qemu', - handler: function() { - var win = Ext.create('PVE.pool.AddVM', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Storage'), - iconCls: 'pve-itype-icon-storage', - handler: function() { - var win = Ext.create('PVE.pool.AddStorage', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - remove_btn - ], - viewConfig: { - stripeRows: true - }, - columns: coldef, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.FWMacroSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFWMacroSelector', - allowBlank: true, - autoSelect: false, - valueField: 'macro', - displayField: 'macro', - listConfig: { - columns: [ - { - header: gettext('Macro'), - dataIndex: 'macro', - hideable: false, - width: 100 - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - flex: 1, - dataIndex: 'descr' - } - ] - }, - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'macro', 'descr' ], - idProperty: 'macro', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/macros" - }, - sorters: { - property: 'macro', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRulePanel', { - extend: 'Proxmox.panel.InputPanel', - - allow_iface: false, - - list_refs_url: undefined, - - onGetValues: function(values) { - var me = this; - - // hack: editable ComboGrid returns nothing when empty, so we need to set '' - // Also, disabled text fields return nothing, so we need to set '' - - Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'log'], function(key) { - if (values[key] === undefined) { - values[key] = ''; - } - }); - - delete values.modified_marker; - - return values; - }, - - initComponent : function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.column1 = [ - { - // hack: we use this field to mark the form 'dirty' when the - // record has errors- so that the user can safe the unmodified - // form again. - xtype: 'hiddenfield', - name: 'modified_marker', - value: '' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'type', - value: 'in', - comboItems: [['in', 'in'], ['out', 'out']], - fieldLabel: gettext('Direction'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - name: 'action', - value: 'ACCEPT', - comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']], - fieldLabel: gettext('Action'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - me.column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } else { - me.column1.push({ - xtype: 'displayfield', - fieldLabel: '', - value: '' - }); - } - - me.column1.push( - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'pveIPRefSelector', - name: 'source', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Source') - - }, - { - xtype: 'pveIPRefSelector', - name: 'dest', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Destination') - } - ); - - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - }, - { - xtype: 'pveFWMacroSelector', - name: 'macro', - fieldLabel: gettext('Macro'), - editable: true, - allowBlank: true, - listeners: { - change: function(f, value) { - if (value === null) { - me.down('field[name=proto]').setDisabled(false); - me.down('field[name=sport]').setDisabled(false); - me.down('field[name=dport]').setDisabled(false); - } else { - me.down('field[name=proto]').setDisabled(true); - me.down('field[name=proto]').setValue(''); - me.down('field[name=sport]').setDisabled(true); - me.down('field[name=sport]').setValue(''); - me.down('field[name=dport]').setDisabled(true); - me.down('field[name=dport]').setValue(''); - } - } - } - }, - { - xtype: 'pveIPProtocolSelector', - name: 'proto', - autoSelect: false, - editable: true, - value: '', - fieldLabel: gettext('Protocol') - }, - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'textfield', - name: 'sport', - value: '', - fieldLabel: gettext('Source port') - }, - { - xtype: 'textfield', - name: 'dport', - value: '', - fieldLabel: gettext('Dest. port') - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'pveFirewallLogLevels' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - list_refs_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.FirewallRulePanel', { - isCreate: me.isCreate, - list_refs_url: me.list_refs_url, - allow_iface: me.allow_iface, - rule_pos: me.rule_pos - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - if (values.errors) { - var field = me.query('[isFormField][name=modified_marker]')[0]; - field.setValue(1); - Ext.Function.defer(function() { - var form = ipanel.up('form').getForm(); - form.markInvalid(values.errors); - }, 100); - } - } - }); - } else if (me.rec) { - ipanel.setValues(me.rec.data); - } - } -}); - -Ext.define('PVE.FirewallGroupRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var column1 = [ - { - xtype: 'hiddenfield', - name: 'type', - value: 'group' - }, - { - xtype: 'pveSecurityGroupsSelector', - name: 'action', - value: '', - fieldLabel: gettext('Security Group'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.FirewallRules', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveFirewallRules', - - onlineHelp: 'chapter_pve_firewall', - - stateful: true, - stateId: 'grid-firewall-rules', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - groupBtn: undefined, - - tbar_prefix: undefined, - - allow_groups: true, - allow_iface: false, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - if (me.groupBtn) { - me.groupBtn.setDisabled(true); - } - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - if (me.groupBtn) { - me.groupBtn.setDisabled(false); - } - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - moveRule: function(from, to) { - var me = this; - - if (!me.base_url) { - return; - } - - Proxmox.Utils.API2Request({ - url: me.base_url + "/" + from, - method: 'PUT', - params: { moveto: to }, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - updateRule: function(rule) { - var me = this; - - if (!me.base_url) { - return; - } - - rule.enable = rule.enable ? 1 : 0; - - var pos = rule.pos; - delete rule.pos; - delete rule.errors; - - Proxmox.Utils.API2Request({ - url: me.base_url + '/' + pos.toString(), - method: 'PUT', - params: rule, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-fw-rule' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type; - - var editor; - if (type === 'in' || type === 'out') { - editor = 'PVE.FirewallRuleEdit'; - } else if (type === 'group') { - editor = 'PVE.FirewallGroupRuleEdit'; - } else { - return; - } - - var win = Ext.create(editor, { - digest: rec.data.digest, - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rule_pos: rec.data.pos - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - var run_copy_editor = function() { - var rec = sm.getSelection()[0]; - - if (!rec) { - return; - } - var type = rec.data.type; - - - if (!(type === 'in' || type === 'out')) { - return; - } - - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rec: rec - }); - - win.show(); - win.on('destroy', reload); - }; - - me.copyBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Copy'), - selModel: sm, - enableFn: function(rec) { - return (rec.data.type === 'in' || rec.data.type === 'out'); - }, - disabled: true, - handler: run_copy_editor - }); - - if (me.allow_groups) { - me.groupBtn = Ext.create('Ext.Button', { - text: gettext('Insert') + ': ' + - gettext('Security Group'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallGroupRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - baseurl: me.base_url + '/', - confirmMsg: false, - getRecordName: function(rec) { - var rule = rec.data; - return rule.pos.toString() + - '?digest=' + encodeURIComponent(rule.digest); - }, - callback: function() { - me.store.load(); - } - }); - - var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : []; - tbar.push(me.addBtn, me.copyBtn); - if (me.groupBtn) { - tbar.push(me.groupBtn); - } - tbar.push(me.removeBtn, me.editBtn); - - var render_errors = function(name, value, metaData, record) { - var errors = record.data.errors; - if (errors && errors[name]) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(errors[name]) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - return value; - }; - - var columns = [ - { - // similar to xtype: 'rownumberer', - dataIndex: 'pos', - resizable: false, - width: 23, - sortable: false, - align: 'right', - hideable: false, - menuDisabled: true, - renderer: function(value, metaData, record, rowIdx, colIdx, store) { - metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special'; - if (value >= 0) { - return value; - } - return ''; - } - }, - { - xtype: 'checkcolumn', - header: gettext('Enable'), - dataIndex: 'enable', - listeners: { - checkchange: function(column, recordIndex, checked) { - var record = me.getStore().getData().items[recordIndex]; - record.commit(); - var data = {}; - Ext.Array.forEach(record.getFields(), function(field) { - data[field.name] = record.get(field.name); - }); - if (!me.allow_iface || !data.iface) { - delete data.iface; - } - me.updateRule(data); - } - }, - width: 50 - }, - { - header: gettext('Type'), - dataIndex: 'type', - renderer: function(value, metaData, record) { - return render_errors('type', value, metaData, record); - }, - width: 50 - }, - { - header: gettext('Action'), - dataIndex: 'action', - renderer: function(value, metaData, record) { - return render_errors('action', value, metaData, record); - }, - width: 80 - }, - { - header: gettext('Macro'), - dataIndex: 'macro', - renderer: function(value, metaData, record) { - return render_errors('macro', value, metaData, record); - }, - width: 80 - } - ]; - - if (me.allow_iface) { - columns.push({ - header: gettext('Interface'), - dataIndex: 'iface', - renderer: function(value, metaData, record) { - return render_errors('iface', value, metaData, record); - }, - width: 80 - }); - } - - columns.push( - { - header: gettext('Source'), - dataIndex: 'source', - renderer: function(value, metaData, record) { - return render_errors('source', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Destination'), - dataIndex: 'dest', - renderer: function(value, metaData, record) { - return render_errors('dest', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Protocol'), - dataIndex: 'proto', - renderer: function(value, metaData, record) { - return render_errors('proto', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Dest. port'), - dataIndex: 'dport', - renderer: function(value, metaData, record) { - return render_errors('dport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Source port'), - dataIndex: 'sport', - renderer: function(value, metaData, record) { - return render_errors('sport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Log level'), - dataIndex: 'log', - renderer: function(value, metaData, record) { - return render_errors('log', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value, metaData, record) { - return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record); - } - } - ); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - plugins: [ - { - ptype: 'gridviewdragdrop', - dragGroup: 'FWRuleDDGroup', - dropGroup: 'FWRuleDDGroup' - } - ], - listeners: { - beforedrop: function(node, data, dropRec, dropPosition) { - if (!dropRec) { - return false; // empty view - } - var moveto = dropRec.get('pos'); - if (dropPosition === 'after') { - moveto++; - } - var pos = data.records[0].get('pos'); - me.moveRule(pos, moveto); - return 0; - }, - itemdblclick: run_editor - } - }, - sortableColumns: false, - columns: columns - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-fw-rule', { - extend: 'Ext.data.Model', - fields: [ { name: 'enable', type: 'boolean' }, - 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface', - 'dport', 'sport', 'comment', 'pos', 'digest', 'errors' ], - idProperty: 'pos' - }); - -}); -Ext.define('PVE.FirewallAliasEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - alias_name: undefined, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.alias_name === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.alias_name; - me.method = 'PUT'; - } - - var items = [ - { - xtype: 'textfield', - name: me.isCreate ? 'name' : 'rename', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'cidr', - fieldLabel: gettext('IP/CIDR'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ]; - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - items: items - }); - - Ext.apply(me, { - subject: gettext('Alias'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - values.rename = values.name; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('pve-fw-aliases', { - extend: 'Ext.data.Model', - - fields: [ 'name', 'cidr', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.FirewallAliases', { - extend: 'Ext.grid.Panel', - alias: ['widget.pveFirewallAliases'], - - onlineHelp: 'pve_firewall_ip_aliases', - - stateful: true, - stateId: 'grid-firewall-aliases', - - base_url: undefined, - - title: gettext('Alias'), - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-aliases', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url, - alias_name: rec.data.name - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - - Ext.apply(me, { - store: store, - tbar: [ me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: gettext('Name'), dataIndex: 'name', width: 100 }, - { header: gettext('IP/CIDR'), dataIndex: 'cidr', width: 100 }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - me.on('activate', reload); - } -}); -Ext.define('PVE.FirewallOptions', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveFirewallOptions'], - - fwtype: undefined, // 'dc', 'node' or 'vm' - - base_url: undefined, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - if (me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm') { - if (me.fwtype === 'node') { - me.cwidth1 = 250; - } - } else { - throw "unknown firewall option type"; - } - - me.rows = {}; - - var add_boolean_row = function(name, text, defaultValue) { - me.add_boolean_row(name, text, { defaultValue: defaultValue }); - }; - var add_integer_row = function(name, text, minValue, labelWidth) { - me.add_integer_row(name, text, { - minValue: minValue, - deleteEmpty: true, - labelWidth: labelWidth, - renderer: function(value) { - if (value === undefined) { - return Proxmox.Utils.defaultText; - } - - return value; - } - }); - }; - - var add_log_row = function(name, labelWidth) { - me.rows[name] = { - header: name, - required: true, - defaultValue: 'nolog', - editor: { - xtype: 'proxmoxWindowEdit', - subject: name, - fieldDefaults: { labelWidth: labelWidth || 100 }, - items: { - xtype: 'pveFirewallLogLevels', - name: name, - fieldLabel: name - } - } - }; - }; - - if (me.fwtype === 'node') { - me.rows.enable = { - required: true, - defaultValue: 1, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 1 - } - }; - add_boolean_row('nosmurfs', gettext('SMURFS filter'), 1); - add_boolean_row('tcpflags', gettext('TCP flags filter'), 0); - add_boolean_row('ndp', 'NDP', 1); - add_integer_row('nf_conntrack_max', 'nf_conntrack_max', 32768, 120); - add_integer_row('nf_conntrack_tcp_timeout_established', - 'nf_conntrack_tcp_timeout_established', 7875, 250); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - add_log_row('tcp_flags_log_level', 120); - add_log_row('smurf_log_level'); - } else if (me.fwtype === 'vm') { - me.rows.enable = { - required: true, - defaultValue: 0, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 0 - } - }; - add_boolean_row('dhcp', 'DHCP', 1); - add_boolean_row('ndp', 'NDP', 1); - add_boolean_row('radv', gettext('Router Advertisement'), 0); - add_boolean_row('macfilter', gettext('MAC filter'), 1); - add_boolean_row('ipfilter', gettext('IP filter'), 0); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - } else if (me.fwtype === 'dc') { - add_boolean_row('enable', gettext('Firewall'), 0); - add_boolean_row('ebtables', 'ebtables', 1); - me.rows.log_ratelimit = { - header: gettext('Log rate limit'), - required: true, - defaultValue: gettext('Default') + ' (enable=1,rate1/second,burst=5)', - editor: { - xtype: 'pveFirewallLograteEdit', - defaultValue: 'enable=1' - } - }; - } - - if (me.fwtype === 'dc' || me.fwtype === 'vm') { - me.rows.policy_in = { - header: gettext('Input Policy'), - required: true, - defaultValue: 'DROP', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Input Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_in', - value: 'DROP', - fieldLabel: gettext('Input Policy') - } - } - }; - - me.rows.policy_out = { - header: gettext('Output Policy'), - required: true, - defaultValue: 'ACCEPT', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Output Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_out', - value: 'ACCEPT', - fieldLabel: gettext('Output Policy') - } - } - }; - } - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - var rowdef = me.rows[rec.data.key]; - edit_btn.setDisabled(!rowdef.editor); - }; - - Ext.apply(me, { - url: "/api2/json" + me.base_url, - tbar: [ edit_btn ], - editorConfig: { - url: '/api2/extjs/' + me.base_url - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); - - -Ext.define('PVE.FirewallLogLevels', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallLogLevels'], - - name: 'log', - fieldLabel: gettext('Log level'), - value: 'nolog', - comboItems: [['nolog', 'nolog'], ['emerg', 'emerg'], ['alert', 'alert'], - ['crit', 'crit'], ['err', 'err'], ['warning', 'warning'], - ['notice', 'notice'], ['info', 'info'], ['debug', 'debug']] -}); -/* - * Left Treepanel, containing all the resources we manage in this datacenter: server nodes, server storages, VMs and Containers - */ -Ext.define('PVE.tree.ResourceTree', { - extend: 'Ext.tree.TreePanel', - alias: ['widget.pveResourceTree'], - - statics: { - typeDefaults: { - node: { - iconCls: 'fa fa-building', - text: gettext('Nodes') - }, - pool: { - iconCls: 'fa fa-tags', - text: gettext('Resource Pool') - }, - storage: { - iconCls: 'fa fa-database', - text: gettext('Storage') - }, - qemu: { - iconCls: 'fa fa-desktop', - text: gettext('Virtual Machine') - }, - lxc: { - //iconCls: 'x-tree-node-lxc', - iconCls: 'fa fa-cube', - text: gettext('LXC Container') - }, - template: { - iconCls: 'fa fa-file-o' - } - } - }, - - useArrows: true, - - // private - nodeSortFn: function(node1, node2) { - var n1 = node1.data; - var n2 = node2.data; - - if ((n1.groupbyid && n2.groupbyid) || - !(n1.groupbyid || n2.groupbyid)) { - - var tcmp; - - var v1 = n1.type; - var v2 = n2.type; - - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - - // numeric compare for VM IDs - // sort templates after regular VMs - if (v1 === 'qemu' || v1 === 'lxc') { - if (n1.template && !n2.template) { - return 1; - } else if (n2.template && !n1.template) { - return -1; - } - v1 = n1.vmid; - v2 = n2.vmid; - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - } - - return n1.id > n2.id ? 1 : (n1.id < n2.id ? -1 : 0); - } else if (n1.groupbyid) { - return -1; - } else if (n2.groupbyid) { - return 1; - } - }, - - // private: fast binary search - findInsertIndex: function(node, child, start, end) { - var me = this; - - var diff = end - start; - - var mid = start + (diff>>1); - - if (diff <= 0) { - return start; - } - - var res = me.nodeSortFn(child, node.childNodes[mid]); - if (res <= 0) { - return me.findInsertIndex(node, child, start, mid); - } else { - return me.findInsertIndex(node, child, mid + 1, end); - } - }, - - setIconCls: function(info) { - var me = this; - - var cls = PVE.Utils.get_object_icon_class(info.type, info); - - if (cls !== '') { - info.iconCls = cls; - } - }, - - // add additional elements to text - // at the moment only the usage indicator for storages - setText: function(info) { - var me = this; - - var status = ''; - if (info.type === 'storage') { - var maxdisk = info.maxdisk; - var disk = info.disk; - var usage = disk/maxdisk; - var cls = ''; - if (usage <= 1.0 && usage >= 0.0) { - var height = (usage*100).toFixed(0); - var neg_height = (100-usage*100).toFixed(0); - status = '
'; - status += '
'; - status += '
'; - status += '
'; - } - } - - info.text = status + info.text; - }, - - setToolTip: function(info) { - if (info.type === 'pool' || info.groupbyid !== undefined) { - return; - } - - var qtips = [gettext('Status') + ': ' + (info.qmpstatus || info.status)]; - if (info.lock) { - qtips.push('Config locked (' + info.lock + ')'); - } - if (info.hastate != 'unmanaged') { - qtips.push(gettext('HA State') + ": " + info.hastate); - } - - info.qtip = qtips.join(', '); - }, - - // private - addChildSorted: function(node, info) { - var me = this; - - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - - var defaults; - if (info.groupbyid) { - info.text = info.groupbyid; - if (info.type === 'type') { - defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid]; - if (defaults && defaults.text) { - info.text = defaults.text; - } - } - } - var child = Ext.create('PVETree', info); - - var cs = node.childNodes; - var pos; - if (cs) { - pos = cs[me.findInsertIndex(node, child, 0, cs.length)]; - } - - node.insertBefore(child, pos); - - return child; - }, - - // private - groupChild: function(node, info, groups, level) { - var me = this; - - var groupby = groups[level]; - var v = info[groupby]; - - if (v) { - var group = node.findChild('groupbyid', v); - if (!group) { - var groupinfo; - if (info.type === groupby) { - groupinfo = info; - } else { - groupinfo = { - type: groupby, - id : groupby + "/" + v - }; - if (groupby !== 'type') { - groupinfo[groupby] = v; - } - } - groupinfo.leaf = false; - groupinfo.groupbyid = v; - group = me.addChildSorted(node, groupinfo); - } - if (info.type === groupby) { - return group; - } - if (group) { - return me.groupChild(group, info, groups, level + 1); - } - } - - return me.addChildSorted(node, info); - }, - - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - if (!me.viewFilter) { - me.viewFilter = {}; - } - - var pdata = { - dataIndex: {}, - updateCount: 0 - }; - - var store = Ext.create('Ext.data.TreeStore', { - model: 'PVETree', - root: { - expanded: true, - id: 'root', - text: gettext('Datacenter'), - iconCls: 'fa fa-server' - } - }); - - var stateid = 'rid'; - - var updateTree = function() { - var tmp; - - store.suspendEvents(); - - var rootnode = me.store.getRootNode(); - // remember selected node (and all parents) - var sm = me.getSelectionModel(); - - var lastsel = sm.getSelection()[0]; - var reselect = false; - var parents = []; - var p = lastsel; - while (p && !!(p = p.parentNode)) { - parents.push(p); - } - - var index = pdata.dataIndex; - - var groups = me.viewFilter.groups || []; - var filterfn = me.viewFilter.filterfn; - - // remove vanished or moved items - // update in place changed items - var key; - for (key in index) { - if (index.hasOwnProperty(key)) { - var olditem = index[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var item = rstore.getById(olditem.data.id); - var item = rstore.data.get(olditem.data.id); - - var changed = false; - var moved = false; - if (item) { - // test if any grouping attributes changed - // this will also catch migrated nodes - // in server view - var i, len; - for (i = 0, len = groups.length; i < len; i++) { - var attr = groups[i]; - if (item.data[attr] != olditem.data[attr]) { - //console.log("changed " + attr); - moved = true; - break; - } - } - - // explicitly check for node, since - // in some views, node is not a grouping - // attribute - if (!moved && item.data.node !== olditem.data.node) { - moved = true; - } - - // tree item has been updated - var fields = [ - 'text', 'running', 'template', 'status', - 'qmpstatus', 'hastate', 'lock' - ]; - - var field; - for (i = 0; i < fields.length; i++) { - field = fields[i]; - if (item.data[field] !== olditem.data[field]) { - changed = true; - break; - } - } - - // fixme: also test filterfn()? - } - - if (changed) { - olditem.beginEdit(); - //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running); - var info = olditem.data; - Ext.apply(info, item.data); - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - olditem.commit(); - } - if ((!item || moved) && olditem.isLeaf()) { - //console.log("REM UID: " + key + " ITEM " + olditem.data.id); - delete index[key]; - var parentNode = olditem.parentNode; - // when the selected item disappears, - // we have to deselect it here, and reselect it - // later - if (lastsel && olditem.data.id === lastsel.data.id) { - reselect = true; - sm.deselect(olditem); - } - // since the store events are suspended, we - // manually remove the item from the store also - store.remove(olditem); - parentNode.removeChild(olditem, true); - } - } - } - - // add new items - rstore.each(function(item) { - var olditem = index[item.data.id]; - if (olditem) { - return; - } - - if (filterfn && !filterfn(item)) { - return; - } - - //console.log("ADD UID: " + item.data.id); - - var info = Ext.apply({ leaf: true }, item.data); - - var child = me.groupChild(rootnode, info, groups, 0); - if (child) { - index[item.data.id] = child; - } - }); - - store.resumeEvents(); - store.fireEvent('refresh', store); - - // select parent node is selection vanished - if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) { - lastsel = rootnode; - while (!!(p = parents.shift())) { - if (!!(tmp = rootnode.findChild('id', p.data.id, true))) { - lastsel = tmp; - break; - } - } - me.selectById(lastsel.data.id); - } else if (lastsel && reselect) { - me.selectById(lastsel.data.id); - } - - // on first tree load set the selection from the stateful provider - if (!pdata.updateCount) { - rootnode.expand(); - me.applyState(sp.get(stateid)); - } - - pdata.updateCount++; - }; - - var statechange = function(sp, key, value) { - if (key === stateid) { - me.applyState(value); - } - }; - - sp.on('statechange', statechange); - - Ext.apply(me, { - allowSelection: true, - store: store, - viewConfig: { - // note: animate cause problems with applyState - animate: false - }, - //useArrows: true, - //rootVisible: false, - //title: 'Resource Tree', - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - destroy: function() { - rstore.un("load", updateTree); - }, - beforecellmousedown: function (tree, td, cellIndex, record, tr, rowIndex, ev) { - var sm = me.getSelectionModel(); - // disable selection when right clicking - // except the record is already selected - me.allowSelection = (ev.button !== 2) || sm.isSelected(record); - }, - beforeselect: function (tree, record, index, eopts) { - var allow = me.allowSelection; - me.allowSelection = true; - return allow; - }, - itemdblclick: PVE.Utils.openTreeConsole - }, - setViewFilter: function(view) { - me.viewFilter = view; - me.clearTree(); - updateTree(); - }, - setDatacenterText: function(clustername) { - var rootnode = me.store.getRootNode(); - - var rnodeText = gettext('Datacenter'); - if (clustername !== undefined) { - rnodeText += ' (' + clustername + ')'; - } - - rootnode.beginEdit(); - rootnode.data.text = rnodeText; - rootnode.commit(); - }, - clearTree: function() { - pdata.updateCount = 0; - var rootnode = me.store.getRootNode(); - rootnode.collapse(); - rootnode.removeAll(); - pdata.dataIndex = {}; - me.getSelectionModel().deselectAll(); - }, - selectExpand: function(node) { - var sm = me.getSelectionModel(); - if (!sm.isSelected(node)) { - sm.select(node); - var cn = node; - while (!!(cn = cn.parentNode)) { - if (!cn.isExpanded()) { - cn.expand(); - } - } - me.getView().focusRow(node); - } - }, - selectById: function(nodeid) { - var rootnode = me.store.getRootNode(); - var sm = me.getSelectionModel(); - var node; - if (nodeid === 'root') { - node = rootnode; - } else { - node = rootnode.findChild('id', nodeid, true); - } - if (node) { - me.selectExpand(node); - } - return node; - }, - applyState : function(state) { - var sm = me.getSelectionModel(); - if (state && state.value) { - me.selectById(state.value); - } else { - sm.deselectAll(); - } - } - }); - - me.callParent(); - - var sm = me.getSelectionModel(); - sm.on('select', function(sm, n) { - sp.set(stateid, { value: n.data.id}); - }); - - rstore.on("load", updateTree); - rstore.startUpdate(); - //rstore.stopUpdate(); - } - -}); -Ext.define('pve-fw-ipsets', { - extend: 'Ext.data.Model', - fields: [ 'name', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.IPSetList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetList', - - stateful: true, - stateId: 'grid-firewall-ipsetlist', - - ipset_panel: undefined, - - base_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - initComponent: function() { - - var me = this; - - if (me.ipset_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-ipsets', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('Proxmox.window.Edit', { - subject: "IPSet '" + rec.data.name + "'", - url: me.base_url, - method: 'POST', - digest: rec.data.digest, - items: [ - { - xtype: 'hiddenfield', - name: 'rename', - value: rec.data.name - }, - { - xtype: 'textfield', - name: 'name', - value: rec.data.name, - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: rec.data.comment, - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('Proxmox.window.Edit', { - subject: 'IPSet', - url: me.base_url, - method: 'POST', - items: [ - { - xtype: 'textfield', - name: 'name', - value: '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - Ext.apply(me, { - store: store, - tbar: [ 'IPSet:', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: 'IPSet', dataIndex: 'name', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = me.base_url + '/' + rec.data.name; - me.ipset_panel.setBaseUrl(url); - }, - deselect: function() { - me.ipset_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.IPSetCidrEdit', { - extend: 'Proxmox.window.Edit', - - cidr: undefined, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.cidr === undefined); - - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.cidr; - me.method = 'PUT'; - } - - var column1 = []; - - if (me.isCreate) { - if (!me.list_refs_url) { - throw "no alias_base_url specified"; - } - - column1.push({ - xtype: 'pveIPRefSelector', - name: 'cidr', - ref_type: 'alias', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } else { - column1.push({ - xtype: 'displayfield', - name: 'cidr', - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'nomatch', - checked: false, - uncheckedValue: 0, - fieldLabel: 'nomatch' - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('IP/CIDR'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.IPSetGrid', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetGrid', - - stateful: true, - stateId: 'grid-firewall-ipsets', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no1 list_refs_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-ipset' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - cidr: rec.data.cidr - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Add'), - disabled: true, - handler: function() { - if (!me.base_url) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - var render_errors = function(value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors.cidr || errors.nomatch; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - Ext.apply(me, { - tbar: [ 'IP/CIDR:', me.addBtn, me.removeBtn, me.editBtn ], - store: store, - selModel: sm, - listeners: { - itemdblclick: run_editor - }, - columns: [ - { - xtype: 'rownumberer' - }, - { - header: gettext('IP/CIDR'), - dataIndex: 'cidr', - width: 150, - renderer: function(value, metaData, record) { - value = render_errors(value, metaData, record); - if (record.data.nomatch) { - return '! ' + value; - } - return value; - } - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value) { - return Ext.util.Format.htmlEncode(value); - } - } - ] - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-ipset', { - extend: 'Ext.data.Model', - fields: [ { name: 'nomatch', type: 'boolean' }, - 'cidr', 'comment', 'errors' ], - idProperty: 'cidr' - }); - -}); - -Ext.define('PVE.IPSet', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveIPSet', - - title: 'IPSet', - - onlineHelp: 'pve_firewall_ip_sets', - - list_refs_url: undefined, - - initComponent: function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var ipset_panel = Ext.createWidget('pveIPSetGrid', { - region: 'center', - list_refs_url: me.list_refs_url, - border: false - }); - - var ipset_list = Ext.createWidget('pveIPSetList', { - region: 'west', - ipset_panel: ipset_panel, - base_url: me.base_url, - width: '50%', - border: false, - split: true - }); - - Ext.apply(me, { - layout: 'border', - items: [ ipset_list, ipset_panel ], - listeners: { - show: function() { - ipset_list.fireEvent('show', ipset_list); - } - } - }); - - me.callParent(); - } -}); -/* - * Base class for all the multitab config panels - * - * How to use this: - * - * You create a subclass of this, and then define your wanted tabs - * as items like this: - * - * items: [{ - * title: "myTitle", - * xytpe: "somextype", - * iconCls: 'fa fa-icon', - * groups: ['somegroup'], - * expandedOnInit: true, - * itemId: 'someId' - * }] - * - * this has to be in the declarative syntax, else we - * cannot save them for later - * (so no Ext.create or Ext.apply of an item in the subclass) - * - * the groups array expects the itemids of the items - * which are the parents, which have to come before they - * are used - * - * if you want following the tree: - * - * Option1 - * Option2 - * -> SubOption1 - * -> SubSubOption1 - * - * the suboption1 group array has to look like this: - * groups: ['itemid-of-option2'] - * - * and of subsuboption1: - * groups: ['itemid-of-option2', 'itemid-of-suboption1'] - * - * setting the expandedOnInit determines if the item/group is expanded - * initially (false by default) - */ -Ext.define('PVE.panel.Config', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePanelConfig', - - showSearch: true, // add a resource grid with a search button as first tab - viewFilter: undefined, // a filter to pass to that resource grid - - tbarSpacing: true, // if true, adds a spacer after the title in tbar - - dockedItems: [{ - // this is needed for the overflow handler - xtype: 'toolbar', - overflowHandler: 'scroller', - dock: 'left', - style: { - backgroundColor: '#f5f5f5', - padding: 0, - margin: 0 - }, - items: { - xtype: 'treelist', - itemId: 'menu', - ui: 'nav', - expanderOnly: true, - expanderFirst: false, - animation: false, - singleExpand: false, - listeners: { - selectionchange: function(treeList, selection) { - var me = this.up('panel'); - me.suspendLayout = true; - me.activateCard(selection.data.id); - me.suspendLayout = false; - me.updateLayout(); - }, - itemclick: function(treelist, info) { - var olditem = treelist.getSelection(); - var newitem = info.node; - - // when clicking on the expand arrow, - // we don't select items, but still want - // the original behaviour - if (info.select === false) { - return; - } - - // if you click on a different item which is open, - // leave it open - // else toggle the clicked item - if (olditem.data.id !== newitem.data.id && - newitem.data.expanded === true) { - info.toggle = false; - } else { - info.toggle = true; - } - } - } - } - }, - { - xtype: 'toolbar', - itemId: 'toolbar', - dock: 'top', - height: 36, - overflowHandler: 'scroller' - }], - - firstItem: '', - layout: 'card', - border: 0, - - // used for automated test - selectById: function(cardid) { - var me = this; - - var root = me.store.getRoot(); - var selection = root.findChild('id', cardid, true); - - if (selection) { - selection.expand(); - var menu = me.down('#menu'); - menu.setSelection(selection); - return cardid; - } - }, - - activateCard: function(cardid) { - var me = this; - if (me.savedItems[cardid]) { - var curcard = me.getLayout().getActiveItem(); - var newcard = me.add(me.savedItems[cardid]); - me.helpButton.setOnlineHelp(newcard.onlineHelp || me.onlineHelp); - if (curcard) { - me.setActiveItem(cardid); - me.remove(curcard, true); - - // trigger state change - - var ncard = cardid; - // Note: '' is alias for first tab. - // First tab can be 'search' or something else - if (cardid === me.firstItem) { - ncard = ''; - } - if (me.hstateid) { - me.sp.set(me.hstateid, { value: ncard }); - } - } - } - }, - - initComponent: function() { - var me = this; - - var stateid = me.hstateid; - - me.sp = Ext.state.Manager.getProvider(); - - var activeTab; // leaving this undefined means items[0] will be the default tab - - if (stateid) { - var state = me.sp.get(stateid); - if (state && state.value) { - // if this tab does not exists, it chooses the first - activeTab = state.value; - } - } - - // get title - var title = me.title || me.pveSelNode.data.text; - me.title = undefined; - - // create toolbar - var tbar = me.tbar || []; - me.tbar = undefined; - - if (!me.onlineHelp) { - switch(me.pveSelNode.data.id) { - case 'type/storage':me.onlineHelp = 'chapter-pvesm.html'; break; - case 'type/qemu':me.onlineHelp = 'chapter-qm.html'; break; - case 'type/lxc':me.onlineHelp = 'chapter-pct.html'; break; - case 'type/pool':me.onlineHelp = 'chapter-pveum.html#_pools'; break; - case 'type/node':me.onlineHelp = 'chapter-sysadmin.html'; break; - } - } - - if (me.tbarSpacing) { - tbar.unshift('->'); - } - tbar.unshift({ - xtype: 'tbtext', - text: title, - baseCls: 'x-panel-header-text' - }); - - me.helpButton = Ext.create('Proxmox.button.Help', { - hidden: false, - listenToGlobalEvent: false, - onlineHelp: me.onlineHelp || undefined - }); - - tbar.push(me.helpButton); - - me.dockedItems[1].items = tbar; - - // include search tab - me.items = me.items || []; - if (me.showSearch) { - me.items.unshift({ - itemId: 'search', - title: gettext('Search'), - iconCls: 'fa fa-search', - xtype: 'pveResourceGrid', - pveSelNode: me.pveSelNode - }); - } - - me.savedItems = {}; - /*jslint confusion:true*/ - if (me.items[0]) { - me.firstItem = me.items[0].itemId; - } - /*jslint confusion:false*/ - - me.store = Ext.create('Ext.data.TreeStore', { - root: { - expanded: true - } - }); - var root = me.store.getRoot(); - me.items.forEach(function(item){ - var treeitem = Ext.create('Ext.data.TreeModel',{ - id: item.itemId, - text: item.title, - iconCls: item.iconCls, - leaf: true, - expanded: item.expandedOnInit - }); - item.header = false; - if (me.savedItems[item.itemId] !== undefined) { - throw "itemId already exists, please use another"; - } - me.savedItems[item.itemId] = item; - - var group; - var curnode = root; - - // get/create the group items - while (Ext.isArray(item.groups) && item.groups.length > 0) { - group = item.groups.shift(); - - var child = curnode.findChild('id', group); - if (child === null) { - // did not find the group item - // so add it where we are - break; - } - curnode = child; - } - - // insert the item - - // lets see if it already exists - var node = curnode.findChild('id', item.itemId); - - if (node === null) { - curnode.appendChild(treeitem); - } else { - // should not happen! - throw "id already exists"; - } - }); - - delete me.items; - me.defaults = me.defaults || {}; - Ext.apply(me.defaults, { - pveSelNode: me.pveSelNode, - viewFilter: me.viewFilter, - workspace: me.workspace, - border: 0 - }); - - me.callParent(); - - var menu = me.down('#menu'); - var selection = root.findChild('id', activeTab, true) || root.firstChild; - var node = selection; - while (node !== root) { - node.expand(); - node = node.parentNode; - } - menu.setStore(me.store); - menu.setSelection(selection); - - // on a state change, - // select the new item - var statechange = function(sp, key, state) { - // it the state change is for this panel - if (stateid && (key === stateid) && state) { - // get active item - var acard = me.getLayout().getActiveItem().itemId; - // get the itemid of the new value - var ncard = state.value || me.firstItem; - if (ncard && (acard != ncard)) { - // select the chosen item - menu.setSelection(root.findChild('id', ncard, true) || root.firstChild); - } - } - }; - - if (stateid) { - me.mon(me.sp, 'statechange', statechange); - } - } -}); -Ext.define('PVE.grid.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveBackupView'], - - onlineHelp: 'chapter_vzdump', - - stateful: true, - stateId: 'grid-guest-backup', - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmtype = me.pveSelNode.data.type; - if (!vmtype) { - throw "no VM type specified"; - } - - var vmtypeFilter; - if (vmtype === 'openvz') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-openvz-'); - }; - } else if (vmtype === 'lxc') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-lxc-'); - }; - } else if (vmtype === 'qemu') { - vmtypeFilter = function(item) { - return item.data.volid.match(':backup/vzdump-qemu-'); - }; - } else { - throw "unsupported VM type '" + vmtype + "'"; - } - - var searchFilter = { - property: 'volid', - // on initial store display only our vmid backups - // surround with minus sign to prevent the 2016 VMID bug - value: vmtype + '-' + vmid + '-', - anyMatch: true, - caseSensitive: false - }; - - me.store = Ext.create('Ext.data.Store', { - model: 'pve-storage-content', - sorters: { - property: 'volid', - order: 'DESC' - }, - filters: [ - vmtypeFilter, - searchFilter - ] - }); - - var reload = Ext.Function.createBuffered(function() { - if (me.store) { - me.store.load(); - } - }, 100); - - var setStorage = function(storage) { - var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content'; - url += '?content=backup'; - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - reload(); - }; - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'backup', - allowBlank: false, - listeners: { - change: function(f, value) { - setStorage(value); - } - } - }); - - var storagefilter = Ext.create('Ext.form.field.Text', { - fieldLabel: gettext('Search'), - labelWidth: 50, - labelAlign: 'right', - enableKeyEvents: true, - value: searchFilter.value, - listeners: { - buffer: 500, - keyup: function(field) { - me.store.clearFilter(true); - searchFilter.value = field.getValue(); - me.store.filter([ - vmtypeFilter, - searchFilter - ]); - } - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var backup_btn = Ext.create('Ext.button.Button', { - text: gettext('Backup now'), - handler: function() { - var win = Ext.create('PVE.window.Backup', { - nodename: nodename, - vmid: vmid, - vmtype: vmtype, - storage: storagesel.getValue(), - listeners : { - close: function() { - reload(); - } - } - }); - win.show(); - } - }); - - var restore_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Restore'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var volid = rec.data.volid; - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - vmid: vmid, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }); - - var delete_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - dangerous: true, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.volid + "'"); - msg += " " + gettext('This will permanently erase all data.'); - - return msg; - }, - getUrl: function(rec) { - var storage = storagesel.getValue(); - return '/nodes/' + nodename + '/storage/' + storage + '/content/' + rec.data.volid; - }, - callback: function() { - reload(); - } - }); - - var config_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var storage = storagesel.getValue(); - if (!storage) { - return; - } - - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }); - - Ext.apply(me, { - selModel: sm, - tbar: [ backup_btn, restore_btn, delete_btn,config_btn, '->', storagesel, storagefilter ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'volid' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.CephCreateService', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCephCreateService', - - showProgress: true, - - setNode: function(nodename) { - var me = this; - - me.nodename = nodename; - me.url = "/nodes/" + nodename + "/ceph/" + me.type + "/" + nodename; - }, - - method: 'POST', - isCreate: true, - - items: [ - { - xtype: 'pveNodeSelector', - submitValue: false, - fieldLabel: gettext('Host'), - selectCurNode: true, - allowBlank: false, - listeners: { - change: function(f, value) { - var me = this.up('pveCephCreateService'); - me.setNode(value); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.type) { - throw "no type specified"; - } - - me.setNode(me.nodename); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephServiceList', { - extend: 'Ext.grid.GridPanel', - xtype: 'pveNodeCephServiceList', - - onlineHelp: 'chapter_pveceph', - emptyText: gettext('No such service configured.'), - - stateful: true, - - // will be called when the store loads - storeLoadCallback: Ext.emptyFn, - - // if set to true, does shows the ceph install mask if needed - showCephInstallMask: false, - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - if (view.pveSelNode) { - view.nodename = view.pveSelNode.data.node; - } - if (!view.nodename) { - throw "no node name specified"; - } - - if (!view.type) { - throw "no type specified"; - } - - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - autoStart: true, - interval: 3000, - storeid: 'ceph-' + view.type + '-list' + view.nodename, - model: 'ceph-service-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + view.nodename + "/ceph/" + view.type - } - }); - - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: [{ property: 'name' }] - })); - - if (view.storeLoadCallback) { - view.rstore.on('load', view.storeLoadCallback, this); - } - view.on('destroy', view.rstore.stopUpdate); - - if (view.showCephInstallMask) { - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error) { - view.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(view.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - view.rstore.startUpdate(); - }); - } - ); - }); - } - }, - - service_cmd: function(rec, cmd) { - var view = this.getView(); - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/" + cmd, - method: 'POST', - params: { service: view.type + '.' + rec.data.name }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - taskDone: function() { - view.rstore.load(); - } - }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - onChangeService: function(btn) { - var me = this; - var view = this.getView(); - var cmd = btn.action; - var rec = view.getSelection()[0]; - me.service_cmd(rec, cmd); - }, - - showSyslog: function() { - var view = this.getView(); - var rec = view.getSelection()[0]; - var servicename = 'ceph-' + view.type + '@' + rec.data.name; - var url = "/api2/extjs/nodes/" + rec.data.host + "/syslog?service=" + encodeURIComponent(servicename); - var win = Ext.create('Ext.window.Window', { - title: gettext('Syslog') + ': ' + servicename, - modal: true, - width: 800, - height: 400, - layout: 'fit', - items: [{ - xtype: 'proxmoxLogView', - url: url, - log_select_timespan: 1 - }] - }); - win.show(); - }, - - onCreate: function() { - var view = this.getView(); - var win = Ext.create('PVE.CephCreateService', { - autoShow: true, - nodename: view.nodename, - subject: view.getTitle(), - type: view.type, - taskDone: function() { - view.rstore.load(); - } - }); - } - }, - - tbar: [ - { - xtype: 'proxmoxButton', - text: gettext('Start'), - iconCls: 'fa fa-play', - action: 'start', - disabled: true, - enableFn: function(rec) { - return rec.data.state === 'stopped' || - rec.data.state === 'unknown'; - }, - handler: 'onChangeService' - }, - { - xtype: 'proxmoxButton', - text: gettext('Stop'), - iconCls: 'fa fa-stop', - action: 'stop', - enableFn: function(rec) { - return rec.data.state !== 'stopped'; - }, - disabled: true, - handler: 'onChangeService' - }, - { - xtype: 'proxmoxButton', - text: gettext('Restart'), - iconCls: 'fa fa-refresh', - action: 'restart', - disabled: true, - enableFn: function(rec) { - return rec.data.state !== 'stopped'; - }, - handler: 'onChangeService' - }, - '-', - { - text: gettext('Create'), - reference: 'createButton', - handler: 'onCreate' - }, - { - text: gettext('Destroy'), - xtype: 'proxmoxStdRemoveButton', - getUrl: function(rec) { - var view = this.up('grid'); - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - return "/nodes/" + rec.data.host + "/ceph/" + view.type + "/" + rec.data.name; - }, - callback: function(options, success, response) { - var view = this.up('grid'); - if (!success) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - return; - } - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - taskDone: function() { - view.rstore.load(); - } - }); - win.show(); - } - }, - '-', - { - xtype: 'proxmoxButton', - text: gettext('Syslog'), - disabled: true, - handler: 'showSyslog' - } - ], - - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: function(v) { - return this.type + '.' + v; - }, - dataIndex: 'name' - }, - { - header: gettext('Host'), - flex: 1, - sortable: true, - renderer: function(v) { - return v || Proxmox.Utils.unknownText; - }, - dataIndex: 'host' - }, - { - header: gettext('Status'), - flex: 1, - sortable: false, - dataIndex: 'state' - }, - { - header: gettext('Address'), - flex: 3, - sortable: true, - renderer: function(v) { - return v || Proxmox.Utils.unknownText; - }, - dataIndex: 'addr' - }, - { - header: gettext('Version'), - flex: 3, - sortable: true, - dataIndex: 'version' - } - ], - - initComponent: function() { - var me = this; - - if (me.additionalColumns) { - me.columns = me.columns.concat(me.additionalColumns); - } - - me.callParent(); - } - -}, function() { - - Ext.define('ceph-service-list', { - extend: 'Ext.data.Model', - fields: [ 'addr', 'name', 'rank', 'host', 'quorum', 'state', - 'ceph_version', 'ceph_version_short', - { type: 'string', name: 'version', calculate: function(data) { - return PVE.Utils.parse_ceph_version(data); - } } - ], - idProperty: 'name' - }); -}); -/*jslint confusion: true */ -Ext.define('PVE.CephCreateFS', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreateFS', - - showTaskViewer: true, - onlineHelp: 'pveceph_fs_create', - - subject: 'Ceph FS', - isCreate: true, - method: 'POST', - - setFSName: function(fsName) { - var me = this; - - if (fsName === '' || fsName === undefined) { - fsName = 'cephfs'; - } - - me.url = "/nodes/" + me.nodename + "/ceph/fs/" + fsName; - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - value: 'cephfs', - listeners: { - change: function(f, value) { - this.up('pveCephCreateFS').setFSName(value); - } - }, - submitValue: false, // already encoded in apicall URL - emptyText: 'cephfs' - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'Placement Groups', - name: 'pg_num', - value: 128, - emptyText: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add as Storage'), - value: true, - name: 'add-storage', - autoEl: { - tag: 'div', - 'data-qtip': gettext('Add the new CephFS to the cluster storage configuration.'), - }, - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.setFSName(); - - me.callParent(); - } -}); - -Ext.define('PVE.NodeCephFSPanel', { - extend: 'Ext.panel.Panel', - xtype: 'pveNodeCephFSPanel', - mixins: ['Proxmox.Mixin.CBind'], - - title: gettext('CephFS'), - onlineHelp: 'pveceph_fs', - - border: false, - defaults: { - border: false, - cbind: { - nodename: '{nodename}' - } - }, - - viewModel: { - parent: null, - data: { - cephfsConfigured: false, - mdsCount: 0 - }, - formulas: { - canCreateFS: function(get) { - return (!get('cephfsConfigured') && get('mdsCount') > 0); - } - } - }, - - items: [ - { - xtype: 'grid', - emptyText: Ext.String.format(gettext('No {0} configured.'), 'CephFS'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-ceph-fs', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + view.nodename + '/ceph/fs' - }, - model: 'pve-ceph-fs' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'name', - order: 'DESC' - } - })); - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){ - me.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.rstore.startUpdate(); - }); - } - ); - }); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onCreate: function() { - var view = this.getView(); - view.rstore.stopUpdate(); - var win = Ext.create('PVE.CephCreateFS', { - autoShow: true, - nodename: view.nodename, - listeners: { - destroy: function() { - view.rstore.startUpdate(); - } - } - }); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!(success && records && records.length > 0)) { - vm.set('cephfsConfigured', false); - return; - } - vm.set('cephfsConfigured', true); - } - }, - tbar: [ - { - text: gettext('Create CephFS'), - reference: 'createButton', - handler: 'onCreate', - bind: { - // only one CephFS per Ceph cluster makes sense for now - disabled: '{!canCreateFS}' - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - dataIndex: 'name' - }, - { - header: 'Data Pool', - flex: 1, - dataIndex: 'data_pool' - }, - { - header: 'Metadata Pool', - flex: 1, - dataIndex: 'metadata_pool' - } - ], - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'pveNodeCephServiceList', - title: gettext('Metadata Servers'), - stateId: 'grid-ceph-mds', - type: 'mds', - storeLoadCallback: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records) { - vm.set('mdsCount', 0); - return; - } - vm.set('mdsCount', records.length); - }, - cbind: { - nodename: '{nodename}' - } - } - ] -}, function() { - Ext.define('pve-ceph-fs', { - extend: 'Ext.data.Model', - fields: [ 'name', 'data_pool', 'metadata_pool' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/localhost/ceph/fs" - }, - idProperty: 'name' - }); -}); -Ext.define('PVE.CephCreatePool', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreatePool', - - showProgress: true, - onlineHelp: 'pve_ceph_pools', - - subject: 'Ceph Pool', - isCreate: true, - method: 'POST', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Size'), - name: 'size', - value: 3, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Min. Size'), - name: 'min_size', - value: 2, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'pveCephRuleSelector', - fieldLabel: 'Crush Rule', // do not localize - name: 'crush_rule', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'pg_num', - name: 'pg_num', - value: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add as Storage'), - value: true, - name: 'add_storages', - autoEl: { - tag: 'div', - 'data-qtip': gettext('Add the new pool to the cluster storage configuration.'), - }, - } - ], - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: "/nodes/" + me.nodename + "/ceph/pools", - defaults: { - nodename: me.nodename - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephPoolList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeCephPoolList', - - onlineHelp: 'chapter_pveceph', - - stateful: true, - stateId: 'grid-ceph-pools', - bufferedRenderer: false, - - features: [ { ftype: 'summary'} ], - - columns: [ - { - header: gettext('Name'), - width: 120, - sortable: true, - dataIndex: 'pool_name' - }, - { - header: gettext('Size') + '/min', - width: 100, - align: 'right', - renderer: function(v, meta, rec) { - return v + '/' + rec.data.min_size; - }, - dataIndex: 'size' - }, - { - text: '# Placement Groups', // pg_num', - width: 180, - align: 'right', - dataIndex: 'pg_num' - }, - { - text: 'CRUSH Rule', - columns: [ - { - text: 'ID', - align: 'right', - width: 50, - dataIndex: 'crush_rule' - }, - { - text: gettext('Name'), - width: 150, - dataIndex: 'crush_rule_name', - }, - ] - }, - { - text: gettext('Used'), - columns: [ - { - text: '%', - width: 100, - sortable: true, - align: 'right', - renderer: function(val) { - return Ext.util.Format.percent(val, '0.00'); - }, - dataIndex: 'percent_used', - summaryType: 'sum', - summaryRenderer: function(val) { - return Ext.util.Format.percent(val, '0.00'); - }, - }, - { - text: gettext('Total'), - width: 100, - sortable: true, - renderer: PVE.Utils.render_size, - align: 'right', - dataIndex: 'bytes_used', - summaryType: 'sum', - summaryRenderer: PVE.Utils.render_size - } - ] - } - ], - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'ceph-pool-list' + nodename, - model: 'ceph-pool-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/ceph/pools" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore }); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ - me.store.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.rstore.startUpdate(); - }); - } - ); - }); - - var create_btn = new Ext.Button({ - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.CephCreatePool', { - nodename: nodename - }); - win.show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - var destroy_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Destroy'), - selModel: sm, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - if (!rec.data.pool_name) { - return; - } - var base_url = '/nodes/' + nodename + '/ceph/pools/' + - rec.data.pool_name; - - var win = Ext.create('PVE.window.SafeDestroy', { - showProgress: true, - url: base_url, - params: { - remove_storages: 1 - }, - item: { type: 'CephPool', id: rec.data.pool_name } - }).show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ create_btn, destroy_btn ], - listeners: { - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('ceph-pool-list', { - extend: 'Ext.data.Model', - fields: [ 'pool_name', - { name: 'pool', type: 'integer'}, - { name: 'size', type: 'integer'}, - { name: 'min_size', type: 'integer'}, - { name: 'pg_num', type: 'integer'}, - { name: 'bytes_used', type: 'integer'}, - { name: 'percent_used', type: 'number'}, - { name: 'crush_rule', type: 'integer'}, - { name: 'crush_rule_name', type: 'string'} - ], - idProperty: 'pool_name' - }); -}); - -Ext.define('PVE.form.CephRuleSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephRuleSelector', - - allowBlank: false, - valueField: 'name', - displayField: 'name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/rules' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.CephCreateOsd', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCephCreateOsd', - - subject: 'Ceph OSD', - - showProgress: true, - - onlineHelp: 'pve_ceph_osds', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd", - method: 'POST', - items: [ - { - xtype: 'inputpanel', - onGetValues: function(values) { - Object.keys(values || {}).forEach(function(name) { - if (values[name] === '') { - delete values[name]; - } - }); - - return values; - }, - column1: [ - { - xtype: 'pveDiskSelector', - name: 'dev', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - } - ], - column2: [ - { - xtype: 'pveDiskSelector', - name: 'db_dev', - nodename: me.nodename, - diskType: 'journal_disks', - fieldLabel: gettext('DB Disk'), - value: '', - autoSelect: false, - allowBlank: true, - emptyText: 'use OSD disk', - listeners: { - change: function(field, val) { - me.down('field[name=db_size]').setDisabled(!val); - } - } - }, - { - xtype: 'numberfield', - name: 'db_size', - fieldLabel: gettext('DB size') + ' (GiB)', - minValue: 1, - maxValue: 128*1024, - decimalPrecision: 2, - allowBlank: true, - disabled: true, - emptyText: gettext('Automatic') - } - ], - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'encrypted', - fieldLabel: gettext('Encrypt OSD') - }, - ], - advancedColumn2: [ - { - xtype: 'pveDiskSelector', - name: 'wal_dev', - nodename: me.nodename, - diskType: 'journal_disks', - fieldLabel: gettext('WAL Disk'), - value: '', - autoSelect: false, - allowBlank: true, - emptyText: 'use OSD/DB disk', - listeners: { - change: function(field, val) { - me.down('field[name=wal_size]').setDisabled(!val); - } - } - }, - { - xtype: 'numberfield', - name: 'wal_size', - fieldLabel: gettext('WAL size') + ' (GiB)', - minValue: 0.5, - maxValue: 128*1024, - decimalPrecision: 2, - allowBlank: true, - disabled: true, - emptyText: gettext('Automatic') - } - ] - }, - { - xtype: 'displayfield', - padding: '5 0 0 0', - userCls: 'pve-hint', - value: 'Note: Ceph is not compatible with disks backed by a hardware ' + - 'RAID controller. For details see ' + - 'the reference documentation.', - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.CephRemoveOsd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephRemoveOsd'], - - isRemove: true, - - showProgress: true, - method: 'DELETE', - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'cleanup', - checked: true, - labelWidth: 130, - fieldLabel: gettext('Cleanup Disks') - } - ], - initComponent : function() { - - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - if (me.osdid === undefined || me.osdid < 0) { - throw "no osdid specified"; - } - - me.isCreate = true; - - me.title = gettext('Destroy') + ': Ceph OSD osd.' + me.osdid.toString(); - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd/" + me.osdid.toString() - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephOsdTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveNodeCephOsdTree'], - onlineHelp: 'chapter_pveceph', - - viewModel: { - data: { - nodename: '', - flags: [], - maxversion: '0', - versions: {}, - isOsd: false, - downOsd: false, - upOsd: false, - inOsd: false, - outOsd: false, - osdid: '', - osdhost: '', - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - - reload: function() { - var me = this.getView(); - var vm = this.getViewModel(); - var nodename = vm.get('nodename'); - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/ceph/osd", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me, msg, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', this.reload); - } - ); - }, - success: function(response, opts) { - var data = response.result.data; - var selected = me.getSelection(); - var name; - if (selected.length) { - name = selected[0].data.name; - } - vm.set('versions', data.versions); - // extract max version - var maxversion = vm.get('maxversion'); - Object.values(data.versions || {}).forEach(function(version) { - if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) { - maxversion = version; - } - }); - vm.set('maxversion', maxversion); - sm.deselectAll(); - me.setRootNode(data.root); - me.expandAll(); - if (name) { - var node = me.getRootNode().findChild('name', name, true); - if (node) { - me.setSelection([node]); - } - } - - var flags = data.flags.split(','); - vm.set('flags', flags); - var noout = flags.includes('noout'); - me.down('#nooutBtn').setText(noout ? gettext("Unset noout") : gettext("Set noout")); - } - }); - }, - - osd_cmd: function(comp) { - var me = this; - var vm = this.getViewModel(); - var cmd = comp.cmd; - var params = comp.params || {}; - var osdid = vm.get('osdid'); - - var doRequest = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + vm.get('osdhost') + "/ceph/osd/" + osdid + '/' + cmd, - waitMsgTarget: me.getView(), - method: 'POST', - params: params, - success: () => { me.reload(); }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - if (cmd === 'scrub') { - Ext.MessageBox.defaultButton = params.deep === 1 ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: params.deep === 1 ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: params.deep !== 1 ? - Ext.String.format(gettext("Scrub OSD.{0}"), osdid) : - Ext.String.format(gettext("Deep Scrub OSD.{0}"), osdid) + - "
Caution: This can reduce performance while it is running.", - buttons: Ext.Msg.YESNO, - callback: function(btn) { - if (btn !== 'yes') { - return; - } - doRequest(); - } - }); - } else { - doRequest(); - } - }, - - create_osd: function() { - var me = this; - var vm = this.getViewModel(); - Ext.create('PVE.CephCreateOsd', { - nodename: vm.get('nodename'), - taskDone: () => { me.reload(); } - }).show(); - }, - - destroy_osd: function() { - var me = this; - var vm = this.getViewModel(); - Ext.create('PVE.CephRemoveOsd', { - nodename: vm.get('osdhost'), - osdid: vm.get('osdid'), - taskDone: () => { me.reload(); } - }).show(); - }, - - set_flag: function() { - var me = this; - var vm = this.getViewModel(); - var flags = vm.get('flags'); - Proxmox.Utils.API2Request({ - url: "/nodes/" + vm.get('nodename') + "/ceph/flags/noout", - waitMsgTarget: me.getView(), - method: flags.includes('noout') ? 'DELETE' : 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: () => { me.reload(); } - }); - }, - - service_cmd: function(comp) { - var me = this; - var vm = this.getViewModel(); - var cmd = comp.cmd || comp; - Proxmox.Utils.API2Request({ - url: "/nodes/" + vm.get('osdhost') + "/ceph/" + cmd, - params: { service: "osd." + vm.get('osdid') }, - waitMsgTarget: me.getView(), - method: 'POST', - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - taskDone: () => { me.reload(); } - }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - set_selection_status: function(tp, selection) { - if (selection.length < 1) { - return; - } - var rec = selection[0]; - var vm = this.getViewModel(); - - var isOsd = (rec.data.host && (rec.data.type === 'osd') && (rec.data.id >= 0)); - - vm.set('isOsd', isOsd); - vm.set('downOsd', isOsd && rec.data.status === 'down'); - vm.set('upOsd', isOsd && rec.data.status !== 'down'); - vm.set('inOsd', isOsd && rec.data.in); - vm.set('outOsd', isOsd && !rec.data.in); - vm.set('osdid', isOsd ? rec.data.id : undefined); - vm.set('osdhost', isOsd ? rec.data.host : undefined); - }, - - render_status: function(value, metaData, rec) { - if (!value) { - return value; - } - var inout = rec.data['in'] ? 'in' : 'out'; - var updownicon = value === 'up' ? 'good fa-arrow-circle-up' : - 'critical fa-arrow-circle-down'; - - var inouticon = rec.data['in'] ? 'good fa-circle' : - 'warning fa-circle-o'; - - var text = value + ' / ' + - inout + ' '; - - return text; - }, - - render_wal: function(value, metaData, rec) { - if (!value && - rec.data.osdtype === 'bluestore' && - rec.data.type === 'osd') { - return 'N/A'; - } - return value; - }, - - render_version: function(value, metadata, rec) { - var vm = this.getViewModel(); - var versions = vm.get('versions'); - var icon = ""; - var version = value || ""; - if (value && value != vm.get('maxversion')) { - icon = PVE.Utils.get_ceph_icon_html('HEALTH_OLD'); - } - - if (!value && rec.data.type == 'host') { - version = versions[rec.data.name] || Proxmox.Utils.unknownText; - } - - return icon + version; - }, - - render_osd_val: function(value, metaData, rec) { - return (rec.data.type === 'osd') ? value : ''; - }, - render_osd_weight: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return Ext.util.Format.number(value, '0.00###'); - }, - - render_osd_latency: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - let commit_ms = rec.data.commit_latency_ms, - apply_ms = rec.data.apply_latency_ms; - return apply_ms + ' / ' + commit_ms; - }, - - render_osd_size: function(value, metaData, rec) { - return this.render_osd_val(PVE.Utils.render_size(value), metaData, rec); - }, - - control: { - '#': { - selectionchange: 'set_selection_status' - } - }, - - init: function(view) { - var me = this; - var vm = this.getViewModel(); - - if (!view.pveSelNode.data.node) { - throw "no node name specified"; - } - - vm.set('nodename', view.pveSelNode.data.node); - - me.callParent(); - me.reload(); - } - }, - - stateful: true, - stateId: 'grid-ceph-osd', - rootVisible: false, - useArrows: true, - - columns: [ - { - xtype: 'treecolumn', - text: 'Name', - dataIndex: 'name', - width: 150 - }, - { - text: 'Type', - dataIndex: 'type', - hidden: true, - align: 'right', - width: 75 - }, - { - text: gettext("Class"), - dataIndex: 'device_class', - align: 'right', - width: 75 - }, - { - text: "OSD Type", - dataIndex: 'osdtype', - align: 'right', - width: 100 - }, - { - text: "Bluestore Device", - dataIndex: 'blfsdev', - align: 'right', - width: 75, - hidden: true - }, - { - text: "DB Device", - dataIndex: 'dbdev', - align: 'right', - width: 75, - hidden: true - }, - { - text: "WAL Device", - dataIndex: 'waldev', - align: 'right', - renderer: 'render_wal', - width: 75, - hidden: true - }, - { - text: 'Status', - dataIndex: 'status', - align: 'right', - renderer: 'render_status', - width: 120 - }, - { - text: gettext('Version'), - dataIndex: 'version', - align: 'right', - renderer: 'render_version' - }, - { - text: 'weight', - dataIndex: 'crush_weight', - align: 'right', - renderer: 'render_osd_weight', - width: 90 - }, - { - text: 'reweight', - dataIndex: 'reweight', - align: 'right', - renderer: 'render_osd_weight', - width: 90 - }, - { - text: gettext('Used') + ' (%)', - dataIndex: 'percent_used', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return Ext.util.Format.number(value, '0.00'); - }, - width: 100 - }, - { - text: gettext('Total'), - dataIndex: 'total_space', - align: 'right', - renderer: 'render_osd_size', - width: 100 - }, - { - text: 'Apply/Commit
Latency (ms)', - dataIndex: 'apply_latency_ms', - align: 'right', - renderer: 'render_osd_latency', - width: 120 - } - ], - - - tbar: { - items: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: 'reload' - }, - '-', - { - text: gettext('Create') + ': OSD', - handler: 'create_osd', - }, - { - text: gettext('Set noout'), - itemId: 'nooutBtn', - handler: 'set_flag', - }, - '->', - { - xtype: 'tbtext', - data: { - osd: undefined - }, - bind: { - data: { - osd: "{osdid}" - } - }, - tpl: [ - '', - 'osd.{osd}:', - '', - gettext('No OSD selected'), - '' - ] - }, - { - text: gettext('Start'), - iconCls: 'fa fa-play', - disabled: true, - bind: { - disabled: '{!downOsd}' - }, - cmd: 'start', - handler: 'service_cmd' - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-stop', - disabled: true, - bind: { - disabled: '{!upOsd}' - }, - cmd: 'stop', - handler: 'service_cmd' - }, - { - text: gettext('Restart'), - iconCls: 'fa fa-refresh', - disabled: true, - bind: { - disabled: '{!upOsd}' - }, - cmd: 'restart', - handler: 'service_cmd' - }, - '-', - { - text: 'Out', - iconCls: 'fa fa-circle-o', - disabled: true, - bind: { - disabled: '{!inOsd}' - }, - cmd: 'out', - handler: 'osd_cmd' - }, - { - text: 'In', - iconCls: 'fa fa-circle', - disabled: true, - bind: { - disabled: '{!outOsd}' - }, - cmd: 'in', - handler: 'osd_cmd' - }, - '-', - { - text: gettext('More'), - iconCls: 'fa fa-bars', - disabled: true, - bind: { - disabled: '{!isOsd}' - }, - menu: [ - { - text: gettext('Scrub'), - iconCls: 'fa fa-shower', - cmd: 'scrub', - handler: 'osd_cmd' - }, - { - text: gettext('Deep Scrub'), - iconCls: 'fa fa-bath', - cmd: 'scrub', - params: { - deep: 1, - }, - handler: 'osd_cmd' - }, - { - text: gettext('Destroy'), - itemId: 'remove', - iconCls: 'fa fa-fw fa-trash-o', - bind: { - disabled: '{!downOsd}' - }, - handler: 'destroy_osd' - } - ], - } - ] - }, - - fields: [ - 'name', 'type', 'status', 'host', 'in', 'id' , - { type: 'number', name: 'reweight' }, - { type: 'number', name: 'percent_used' }, - { type: 'integer', name: 'bytes_used' }, - { type: 'integer', name: 'total_space' }, - { type: 'integer', name: 'apply_latency_ms' }, - { type: 'integer', name: 'commit_latency_ms' }, - { type: 'string', name: 'device_class' }, - { type: 'string', name: 'osdtype' }, - { type: 'string', name: 'blfsdev' }, - { type: 'string', name: 'dbdev' }, - { type: 'string', name: 'waldev' }, - { type: 'string', name: 'version', calculate: function(data) { - return PVE.Utils.parse_ceph_version(data); - } }, - { type: 'string', name: 'iconCls', calculate: function(data) { - var iconMap = { - host: 'fa-building', - osd: 'fa-hdd-o', - root: 'fa-server', - }; - return 'fa x-fa-tree ' + iconMap[data.type]; - } }, - { type: 'number', name: 'crush_weight' } - ], -}); -Ext.define('PVE.node.CephMonMgrList', { - extend: 'Ext.container.Container', - xtype: 'pveNodeCephMonMgr', - - mixins: ['Proxmox.Mixin.CBind' ], - - onlineHelp: 'chapter_pveceph', - - defaults: { - border: false, - onlineHelp: 'chapter_pveceph', - flex: 1 - }, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - items: [ - { - xtype: 'pveNodeCephServiceList', - cbind: { pveSelNode: '{pveSelNode}' }, - type: 'mon', - additionalColumns: [ - { - header: gettext('Quorum'), - width: 70, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'quorum' - } - ], - stateId: 'grid-ceph-monitor', - showCephInstallMask: true, - title: gettext('Monitor') - }, - { - xtype: 'pveNodeCephServiceList', - type: 'mgr', - stateId: 'grid-ceph-manager', - cbind: { pveSelNode: '{pveSelNode}' }, - title: gettext('Manager') - } - ] -}); -Ext.define('PVE.node.CephCrushMap', { - extend: 'Ext.panel.Panel', - alias: ['widget.pveNodeCephCrushMap'], - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - stateful: true, - stateId: 'layout-ceph-crush', - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/crush', - - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.node.CephStatus', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephStatus', - - onlineHelp: 'chapter_pveceph', - - scrollable: true, - - bodyPadding: 5, - - layout: { - type: 'column' - }, - - defaults: { - padding: 5 - }, - - items: [ - { - xtype: 'panel', - title: gettext('Health'), - bodyPadding: 10, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - minHeight: 230, - columnWidth: 1 - }, - 'width >= 1900': { - minHeight: 500, - columnWidth: 0.5 - } - }, - layout: { - type: 'hbox', - align: 'stretch' - }, - items: [ - { - flex: 1, - itemId: 'overallhealth', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - flex: 2, - itemId: 'warnings', - stateful: true, - stateId: 'ceph-status-warnings', - xtype: 'grid', - // since we load the store manually, - // to show the emptytext, we have to - // specify an empty store - store: { data:[] }, - emptyText: gettext('No Warnings/Errors'), - columns: [ - { - dataIndex: 'severity', - header: gettext('Severity'), - align: 'center', - width: 70, - renderer: function(value) { - var health = PVE.Utils.map_ceph_health[value]; - var classes = PVE.Utils.get_health_icon(health); - - return ''; - }, - sorter: { - sorterFn: function(a,b) { - var healthArr = ['HEALTH_ERR', 'HEALTH_WARN', 'HEALTH_OK']; - return healthArr.indexOf(b.data.severity) - healthArr.indexOf(a.data.severity); - } - } - }, - { - dataIndex: 'summary', - header: gettext('Summary'), - flex: 1 - }, - { - xtype: 'actioncolumn', - width: 40, - align: 'center', - tooltip: gettext('Detail'), - items: [ - { - iconCls: 'x-fa fa-info-circle', - handler: function(grid, rowindex, colindex, item, e, record) { - var win = Ext.create('Ext.window.Window', { - title: gettext('Detail'), - resizable: true, - modal: true, - width: 650, - height: 400, - layout: { - type: 'fit' - }, - items: [{ - scrollable: true, - padding: 10, - xtype: 'box', - html: [ - '' + Ext.htmlEncode(record.data.summary) + '', - '
' + Ext.htmlEncode(record.data.detail) + '
' - ] - }] - }); - win.show(); - } - } - ] - } - ] - } - ] - }, - { - xtype: 'pveCephStatusDetail', - itemId: 'statusdetail', - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1, - minHeight: 250 - }, - 'width >= 1900': { - columnWidth: 0.5, - minHeight: 300 - } - }, - title: gettext('Status') - }, - { - title: gettext('Services'), - xtype: 'pveCephServices', - itemId: 'services', - plugins: 'responsive', - layout: { - type: 'hbox', - align: 'stretch' - }, - responsiveConfig: { - 'width < 1900': { - columnWidth: 1, - minHeight: 200 - }, - 'width >= 1900': { - columnWidth: 0.5, - minHeight: 200 - } - } - }, - { - xtype: 'panel', - title: gettext('Performance'), - columnWidth: 1, - bodyPadding: 5, - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - flex: 1, - xtype: 'proxmoxGauge', - itemId: 'space', - title: gettext('Usage') - }, - { - flex: 2, - xtype: 'container', - defaults: { - padding: 0, - height: 100 - }, - items: [ - { - itemId: 'reads', - xtype: 'pveRunningChart', - title: gettext('Reads'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'writes', - xtype: 'pveRunningChart', - title: gettext('Writes'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'iops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS', // do not localize - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'readiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Reads'), - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'writeiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Writes'), - renderer: Ext.util.Format.numberRenderer('0,000') - } - ] - } - ] - } - ], - - generateCheckData: function(health) { - var result = []; - var checks = health.checks || {}; - var keys = Ext.Object.getKeys(checks).sort(); - - Ext.Array.forEach(keys, function(key) { - var details = checks[key].detail || []; - result.push({ - id: key, - summary: checks[key].summary.message, - detail: Ext.Array.reduce( - checks[key].detail, - function(first, second) { - return first + '\n' + second.message; - }, - '' - ), - severity: checks[key].severity - }); - }); - - return result; - }, - - updateAll: function(store, records, success) { - if (!success || records.length === 0) { - return; - } - - var me = this; - var rec = records[0]; - me.status = rec.data; - - // add health panel - me.down('#overallhealth').updateHealth(PVE.Utils.render_ceph_health(rec.data.health || {})); - // add errors to gridstore - me.down('#warnings').getStore().loadRawData(me.generateCheckData(rec.data.health || {}), false); - - // update services - me.getComponent('services').updateAll(me.metadata || {}, rec.data); - - // update detailstatus panel - me.getComponent('statusdetail').updateAll(me.metadata || {}, rec.data); - - // add performance data - var used = rec.data.pgmap.bytes_used; - var total = rec.data.pgmap.bytes_total; - - var text = Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(used), - PVE.Utils.render_size(total) - ); - - // update the usage widget - me.down('#space').updateValue(used/total, text); - - // TODO: logic for jewel (iops split in read/write) - - var iops = rec.data.pgmap.op_per_sec; - var readiops = rec.data.pgmap.read_op_per_sec; - var writeiops = rec.data.pgmap.write_op_per_sec; - var reads = rec.data.pgmap.read_bytes_sec || 0; - var writes = rec.data.pgmap.write_bytes_sec || 0; - - if (iops !== undefined && me.version !== 'hammer') { - me.change_version('hammer'); - } else if((readiops !== undefined || writeiops !== undefined) && me.version !== 'jewel') { - me.change_version('jewel'); - } - // update the graphs - me.reads.addDataPoint(reads); - me.writes.addDataPoint(writes); - me.iops.addDataPoint(iops); - me.readiops.addDataPoint(readiops); - me.writeiops.addDataPoint(writeiops); - }, - - change_version: function(version) { - var me = this; - me.version = version; - me.sp.set('ceph-version', version); - me.iops.setVisible(version === 'hammer'); - me.readiops.setVisible(version === 'jewel'); - me.writeiops.setVisible(version === 'jewel'); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - - me.callParent(); - var baseurl = '/api2/json' + (nodename ? '/nodes/' + nodename : '/cluster') + '/ceph'; - me.store = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + (nodename || 'cluster'), - interval: 5000, - proxy: { - type: 'proxmox', - url: baseurl + '/status' - } - }); - - me.metadatastore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-metadata-' + (nodename || 'cluster'), - interval: 15*1000, - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/ceph/metadata' - } - }); - - // save references for the updatefunction - me.iops = me.down('#iops'); - me.readiops = me.down('#readiops'); - me.writeiops = me.down('#writeiops'); - me.reads = me.down('#reads'); - me.writes = me.down('#writes'); - - // get ceph version - me.sp = Ext.state.Manager.getProvider(); - me.version = me.sp.get('ceph-version'); - me.change_version(me.version); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, me.store, regex, function(me, error){ - me.store.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, (nodename || 'localhost'), - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.startUpdate(); - }); - } - ); - }); - - me.mon(me.store, 'load', me.updateAll, me); - me.mon(me.metadatastore, 'load', function(store, records, success) { - if (!success || records.length < 1) { - return; - } - var rec = records[0]; - me.metadata = rec.data; - - // update services - me.getComponent('services').updateAll(rec.data, me.status || {}); - - // update detailstatus panel - me.getComponent('statusdetail').updateAll(rec.data, me.status || {}); - - }, me); - - me.on('destroy', me.store.stopUpdate); - me.on('destroy', me.metadatastore.stopUpdate); - me.store.startUpdate(); - me.metadatastore.startUpdate(); - } - -}); -Ext.define('PVE.ceph.StatusDetail', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveCephStatusDetail', - - layout: { - type: 'hbox', - align: 'stretch' - }, - - bodyPadding: '0 5', - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [{ - flex: 1, - itemId: 'osds', - maxHeight: 250, - scrollable: true, - padding: '0 10 5 10', - data: { - total: 0, - upin: 0, - upout: 0, - downin: 0, - downout: 0, - oldosds: [] - }, - tpl: [ - '

' + 'OSDs' + '

', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '
', - gettext('In'), - '', - gettext('Out'), - '
', - gettext('Up'), - '{upin}{upout}
', - gettext('Down'), - '{downin}{downout}
', - '
', - gettext('Total'), - ': {total}', - '

', - '', - ' ' + gettext('Outdated OSDs') + "
", - '
', - '', - '
osd.{id}:
', - '
{version}

', - '
', - '
', - '
', - '
' - ] - }, - { - flex: 1, - border: false, - itemId: 'pgchart', - xtype: 'polar', - height: 184, - innerPadding: 5, - insetPadding: 5, - colors: [ - '#CFCFCF', - '#21BF4B', - '#FFCC00', - '#FF6C59' - ], - store: { }, - series: [ - { - type: 'pie', - donut: 60, - angleField: 'count', - tooltip: { - trackMouse: true, - renderer: function(tooltip, record, ctx) { - var html = record.get('text'); - html += '
'; - record.get('states').forEach(function(state) { - html += '
' + - state.state_name + ': ' + state.count.toString(); - }); - tooltip.setHtml(html); - } - }, - subStyle: { - strokeStyle: false - } - } - ] - }, - { - flex: 1.6, - itemId: 'pgs', - padding: '0 10', - maxHeight: 250, - scrollable: true, - data: { - states: [] - }, - tpl: [ - '

' + 'PGs' + '

', - '', - '
{state_name}:
', - '
{count}

', - '
', - '
' - ] - }], - - // similar to mgr dashboard - pgstates: { - // clean - clean: 1, - active: 1, - - // working - activating: 2, - backfill_wait: 2, - backfilling: 2, - creating: 2, - deep: 2, - degraded: 2, - forced_backfill: 2, - forced_recovery: 2, - peered: 2, - peering: 2, - recovering: 2, - recovery_wait: 2, - repair: 2, - scrubbing: 2, - snaptrim: 2, - snaptrim_wait: 2, - - // error - backfill_toofull: 3, - backfill_unfound: 3, - down: 3, - incomplete: 3, - inconsistent: 3, - recovery_toofull: 3, - recovery_unfound: 3, - remapped: 3, - snaptrim_error: 3, - stale: 3, - undersized: 3 - }, - - statecategories: [ - { - text: gettext('Unknown'), - count: 0, - states: [], - cls: 'faded' - }, - { - text: gettext('Clean'), - cls: 'good' - }, - { - text: gettext('Working'), - cls: 'warning' - }, - { - text: gettext('Error'), - cls: 'critical' - } - ], - - updateAll: function(metadata, status) { - var me = this; - me.suspendLayout = true; - - var maxversion = "0"; - Object.values(metadata.version || {}).forEach(function(version) { - if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) { - maxversion = version; - } - }); - - var oldosds = []; - - if (metadata.osd) { - metadata.osd.forEach(function(osd) { - var version = PVE.Utils.parse_ceph_version(osd); - if (version != maxversion) { - oldosds.push({ - id: osd.id, - version: version - }); - } - }); - } - - var pgmap = status.pgmap || {}; - var health = status.health || {}; - var osdmap = status.osdmap || { osdmap: {} }; - - - // update pgs sorted - var pgs_by_state = pgmap.pgs_by_state || []; - pgs_by_state.sort(function(a,b){ - return (a.state_name < b.state_name)?-1:(a.state_name === b.state_name)?0:1; - }); - - me.statecategories.forEach(function(cat) { - cat.count = 0; - cat.states = []; - }); - - pgs_by_state.forEach(function(state) { - var i; - var states = state.state_name.split(/[^a-z]+/); - var result = 0; - for (i = 0; i < states.length; i++) { - if (me.pgstates[states[i]] > result) { - result = me.pgstates[states[i]]; - } - } - // for the list - state.cls = me.statecategories[result].cls; - - me.statecategories[result].count += state.count; - me.statecategories[result].states.push(state); - }); - - me.getComponent('pgchart').getStore().setData(me.statecategories); - me.getComponent('pgs').update({states: pgs_by_state}); - - var downinregex = /(\d+) osds down/; - var downin_osds = 0; - - // we collect monitor/osd information from the checks - Ext.Object.each(health.checks, function(key, value, obj) { - var found = null; - if (key === 'OSD_DOWN') { - found = value.summary.message.match(downinregex); - if (found !== null) { - downin_osds = parseInt(found[1],10); - } - } - }); - - // update osds counts - - var total_osds = osdmap.osdmap.num_osds || 0; - var in_osds = osdmap.osdmap.num_in_osds || 0; - var up_osds = osdmap.osdmap.num_up_osds || 0; - var out_osds = total_osds - in_osds; - var down_osds = total_osds - up_osds; - - var downout_osds = down_osds - downin_osds; - var upin_osds = in_osds - downin_osds; - var upout_osds = up_osds - upin_osds; - var osds = { - total: total_osds, - upin: upin_osds, - upout: upout_osds, - downin: downin_osds, - downout: downout_osds, - oldosds: oldosds - }; - var osdcomponent = me.getComponent('osds'); - osdcomponent.update(Ext.apply(osdcomponent.data, osds)); - - me.suspendLayout = false; - me.updateLayout(); - } -}); - -Ext.define('PVE.ceph.Services', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveCephServices', - - layout: { - type: 'hbox', - align: 'stretch' - }, - - bodyPadding: '0 5 20', - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [ - { - flex: 1, - xtype: 'pveCephServiceList', - itemId: 'mons', - title: gettext('Monitors') - }, - { - flex: 1, - xtype: 'pveCephServiceList', - itemId: 'mgrs', - title: gettext('Managers') - }, - { - flex: 1, - xtype: 'pveCephServiceList', - itemId: 'mdss', - title: gettext('Meta Data Servers') - } - ], - - updateAll: function(metadata, status) { - var me = this; - - var healthstates = { - 'HEALTH_UNKNOWN': 0, - 'HEALTH_ERR': 1, - 'HEALTH_WARN': 2, - 'HEALTH_OLD': 3, - 'HEALTH_OK': 4 - }; - var healthmap = [ - 'HEALTH_UNKNOWN', - 'HEALTH_ERR', - 'HEALTH_WARN', - 'HEALTH_OLD', - 'HEALTH_OK' - ]; - var reduceFn = function(first, second) { - return first + '\n' + second.message; - }; - var services = ['mon','mgr','mds']; - var maxversion = "00.0.00"; - Object.values(metadata.version || {}).forEach(function(version) { - if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) { - maxversion = version; - } - }); - var i; - var quorummap = (status && status.quorum_names) ? status.quorum_names : []; - var monmessages = {}; - var mgrmessages = {}; - var mdsmessages = {}; - if (status) { - if (status.health) { - Ext.Object.each(status.health.checks, function(key, value, obj) { - if (!Ext.String.startsWith(key, "MON_")) { - return; - } - - var i; - for (i = 0; i < value.detail.length; i++) { - var match = value.detail[i].message.match(/mon.([a-zA-Z0-9\-\.]+)/); - if (!match) { - continue; - } - var monid = match[1]; - - if (!monmessages[monid]) { - monmessages[monid] = { - worstSeverity: healthstates.HEALTH_OK, - messages: [] - }; - } - - - monmessages[monid].messages.push( - PVE.Utils.get_ceph_icon_html(value.severity, true) + - Ext.Array.reduce(value.detail, reduceFn, '') - ); - if (healthstates[value.severity] < monmessages[monid].worstSeverity) { - monmessages[monid].worstSeverity = healthstates[value.severity]; - } - } - }); - } - - if (status.mgrmap) { - mgrmessages[status.mgrmap.active_name] = "active"; - status.mgrmap.standbys.forEach(function(mgr) { - mgrmessages[mgr.name] = "standby"; - }); - } - - if (status.fsmap) { - status.fsmap.by_rank.forEach(function(mds) { - mdsmessages[mds.name] = 'rank: ' + mds.rank + "; " + mds.status; - }); - } - } - - var checks = { - mon: function(mon) { - if (quorummap.indexOf(mon.name) !== -1) { - mon.health = healthstates.HEALTH_OK; - } else { - mon.health = healthstates.HEALTH_ERR; - } - if (monmessages[mon.name]) { - if (monmessages[mon.name].worstSeverity < mon.health) { - mon.health = monmessages[mon.name].worstSeverity; - } - Array.prototype.push.apply(mon.messages, monmessages[mon.name].messages); - } - return mon; - }, - mgr: function(mgr) { - if (mgrmessages[mgr.name] === 'active') { - mgr.title = '' + mgr.title + ''; - mgr.statuses.push(gettext('Status') + ': active'); - } else if (mgrmessages[mgr.name] === 'standby') { - mgr.statuses.push(gettext('Status') + ': standby'); - } else if (mgr.health > healthstates.HEALTH_WARN) { - mgr.health = healthstates.HEALTH_WARN; - } - - return mgr; - }, - mds: function(mds) { - if (mdsmessages[mds.name]) { - mds.title = '' + mds.title + ''; - mds.statuses.push(gettext('Status') + ': ' + mdsmessages[mds.name]+""); - } else if (mds.addr !== Proxmox.Utils.unknownText) { - mds.statuses.push(gettext('Status') + ': standby'); - } - - return mds; - } - }; - - for (i = 0; i < services.length; i++) { - var type = services[i]; - var ids = Object.keys(metadata[type] || {}); - me[type] = {}; - - var j; - for (j = 0; j < ids.length; j++) { - var id = ids[j]; - var tmp = id.split('@'); - var name = tmp[0]; - var host = tmp[1]; - var result = { - id: id, - health: healthstates.HEALTH_OK, - statuses: [], - messages: [], - name: name, - title: metadata[type][id].name || name, - host: host, - version: PVE.Utils.parse_ceph_version(metadata[type][id]), - service: metadata[type][id].service, - addr: metadata[type][id].addr || metadata[type][id].addrs || Proxmox.Utils.unknownText - }; - - result.statuses = [ - gettext('Host') + ": " + result.host, - gettext('Address') + ": " + result.addr - ]; - - if (checks[type]) { - result = checks[type](result); - } - - if (result.service && !result.version) { - result.messages.push( - PVE.Utils.get_ceph_icon_html('HEALTH_UNKNOWN', true) + - gettext('Stopped') - ); - result.health = healthstates.HEALTH_UNKNOWN; - } - - if (!result.version && result.addr === Proxmox.Utils.unknownText) { - result.health = healthstates.HEALTH_UNKNOWN; - } - - if (result.version) { - result.statuses.push(gettext('Version') + ": " + result.version); - - if (result.version != maxversion) { - if (result.health > healthstates.HEALTH_OLD) { - result.health = healthstates.HEALTH_OLD; - } - result.messages.push( - PVE.Utils.get_ceph_icon_html('HEALTH_OLD', true) + - gettext('Not Current Version, please upgrade') - ); - } - } - - result.statuses.push(''); // empty line - result.text = result.statuses.concat(result.messages).join('
'); - - result.health = healthmap[result.health]; - - me[type][id] = result; - } - } - - me.getComponent('mons').updateAll(Object.values(me.mon)); - me.getComponent('mgrs').updateAll(Object.values(me.mgr)); - me.getComponent('mdss').updateAll(Object.values(me.mds)); - } -}); - -Ext.define('PVE.ceph.ServiceList', { - extend: 'Ext.container.Container', - xtype: 'pveCephServiceList', - - style: { - 'text-align':'center' - }, - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [ - { - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}

' - } - ], - - updateAll: function(list) { - var me = this; - me.suspendLayout = true; - - var i; - list.sort(function(a,b) { - return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; - }); - var ids = {}; - if (me.ids) { - me.ids.forEach(function(id) { - ids[id] = true; - }); - } - for (i = 0; i < list.length; i++) { - var service = me.getComponent(list[i].id); - if (!service) { - // since services are already sorted, and - // we always have a sorted list - // we can add it at the service+1 position (because of the title) - service = me.insert(i+1, { - xtype: 'pveCephServiceWidget', - itemId: list[i].id - }); - if (!me.ids) { - me.ids = []; - } - me.ids.push(list[i].id); - } else { - delete ids[list[i].id]; - } - service.updateService(list[i].title, list[i].text, list[i].health); - } - - Object.keys(ids).forEach(function(id) { - me.remove(id); - }); - me.suspendLayout = false; - me.updateLayout(); - }, - - initComponent: function() { - var me = this; - me.callParent(); - me.getComponent('title').update({ - title: me.title - }); - } -}); - -/*jslint confusion: true*/ -Ext.define('PVE.ceph.ServiceWidget', { - extend: 'Ext.Component', - alias: 'widget.pveCephServiceWidget', - - userCls: 'monitor inline-block', - data: { - title: '0', - health: 'HEALTH_ERR', - text: '', - iconCls: PVE.Utils.get_health_icon() - }, - - tpl: [ - '{title}: ', - '' - ], - - updateService: function(title, text, health) { - var me = this; - - me.update(Ext.apply(me.data, { - health: health, - text: text, - title: title, - iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[health]) - })); - - if (me.tooltip) { - me.tooltip.setHtml(text); - } - }, - - listeners: { - destroy: function() { - var me = this; - if (me.tooltip) { - me.tooltip.destroy(); - delete me.tooltip; - } - }, - mouseenter: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (!me) { - return; - } - if (!me.tooltip) { - me.tooltip = Ext.create('Ext.tip.ToolTip', { - target: me.el, - trackMouse: true, - dismissDelay: 0, - renderTo: Ext.getBody(), - html: me.data.text - }); - } - me.tooltip.show(); - } - }, - mouseleave: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (me.tooltip) { - me.tooltip.destroy(); - delete me.tooltip; - } - } - } - } -}); -Ext.define('PVE.node.CephConfigDb', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveNodeCephConfigDb', - - border: false, - store: { - proxy: { - type: 'proxmox' - } - }, - - columns: [ - { - dataIndex: 'section', - text: 'WHO', - width: 100, - }, - { - dataIndex: 'mask', - text: 'MASK', - hidden: true, - width: 80, - }, - { - dataIndex: 'level', - hidden: true, - text: 'LEVEL', - }, - { - dataIndex: 'name', - flex: 1, - text: 'OPTION', - }, - { - dataIndex: 'value', - flex: 1, - text: 'VALUE', - }, - { - dataIndex: 'can_update_at_runtime', - text: 'Runtime Updatable', - hidden: true, - width: 80, - renderer: Proxmox.Utils.format_boolean - }, - ], - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.store.proxy.url = '/api2/json/nodes/' + nodename + '/ceph/configdb'; - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore()); - me.getStore().load(); - } -}); -Ext.define('PVE.node.CephConfig', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfig', - - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/config', - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.CephConfigCrush', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfigCrush', - - onlineHelp: 'chapter_pveceph', - - layout: 'border', - items: [{ - title: gettext('Configuration'), - xtype: 'pveNodeCephConfig', - region: 'center' - }, - { - title: 'Crush Map', // do not localize - xtype: 'pveNodeCephCrushMap', - region: 'east', - split: true, - width: '50%' - }, - { - title: gettext('Configuration Database'), - xtype: 'pveNodeCephConfigDb', - region: 'south', - split: true, - weight: -30, - height: '50%' - }], - - initComponent: function() { - var me = this; - me.defaults = { - pveSelNode: me.pveSelNode - }; - me.callParent(); - } -}); -Ext.define('PVE.ceph.Log', { - extend: 'Proxmox.panel.LogView', - xtype: 'cephLogView', - nodename: undefined, - failCallback: function(response) { - var me = this; - var msg = response.htmlStatus; - var windowShow = PVE.Utils.showCephInstallOrMask(me, msg, me.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.loadTask.delay(200); - }); - } - ); - if (!windowShow) { - Proxmox.Utils.setErrorMask(me, msg); - } - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.CephInstallWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveCephInstallWizard', - mixins: ['Proxmox.Mixin.CBind'], - resizable: false, - nodename: undefined, - viewModel: { - data: { - nodename: '', - configuration: true, - isInstalled: false - } - }, - cbindData: { - nodename: undefined - }, - title: gettext('Setup'), - navigateNext: function() { - var tp = this.down('#wizcontent'); - var atab = tp.getActiveTab(); - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - }, - setInitialTab: function (index) { - var tp = this.down('#wizcontent'); - var initialTab = tp.items.getAt(index); - initialTab.enable(); - tp.setActiveTab(initialTab); - }, - onShow: function() { - this.callParent(arguments); - var isInstalled = this.getViewModel().get('isInstalled'); - if (isInstalled) { - this.getViewModel().set('configuration', false); - this.setInitialTab(2); - } - }, - items: [ - { - title: gettext('Info'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'chapter_pveceph', - html: '

Ceph?

'+ - '

"Ceph is a unified, distributed storage system designed for excellent performance, reliability and scalability."

'+ - '

Ceph is currently not installed on this node, click on the next button below to start the installation.'+ - ' This wizard will guide you through the necessary steps, after the initial installation you will be offered to create an initial configuration.'+ - ' The configuration step is only needed once per cluster and will be skipped if a config is already present.

'+ - '

Please take a look at our documentation, by clicking the help button below, before starting the installation, '+ - 'if you want to gain deeper knowledge about Ceph visit ceph.com.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#back').hide(true); - this.up('pveCephInstallWizard').down('#next').setText(gettext('Start installation')); - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#next').setText(gettext('Next')); - } - } - }, - { - title: gettext('Installation'), - xtype: 'panel', - layout: 'fit', - cbind:{ - nodename: '{nodename}' - }, - viewModel: {}, // needed to inherit parent viewModel data - listeners: { - afterrender: function() { - var me = this; - if (this.getViewModel().get('isInstalled')) { - this.mask("Ceph is already installed, click next to create your configuration.",['pve-static-mask']); - } else { - me.down('pveNoVncConsole').fireEvent('activate'); - } - }, - activate: function() { - var me = this; - var nodename = me.nodename; - me.updateStore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + nodename, - interval: 1000, - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/ceph/status' - }, - listeners: { - load: function(rec, response, success, operation) { - - if (success) { - me.updateStore.stopUpdate(); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("not initialized", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',false); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("rados_connect failed", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',true); - me.down('textfield').setValue('success'); - } else if (!operation.error.statusText.match("not installed", "i")) { - Proxmox.Utils.setErrorMask(me, operation.error.statusText); - } - } - } - }); - me.updateStore.startUpdate(); - }, - destroy: function() { - var me = this; - if (me.updateStore) { - me.updateStore.stopUpdate(); - } - } - }, - items: [ - { - itemId: 'jsconsole', - consoleType: 'cmd', - xtermjs: true, - xtype: 'pveNoVncConsole', - cbind:{ - nodename: '{nodename}' - }, - cmd: 'ceph_install' - }, - { - xtype: 'textfield', - name: 'installSuccess', - value: '', - allowBlank: false, - submitValue: false, - hidden: true - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Configuration'), - onlineHelp: 'chapter_pveceph', - cbind: { - nodename: '{nodename}' - }, - viewModel: { - data: { - replicas: undefined, - minreplicas: undefined - } - }, - listeners: { - activate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Next')); - }, - beforeshow: function() { - if (this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - this.mask("Coniguration already initialized",['pve-static-mask']); - } else { - this.unmask(); - } - }, - deactivate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Finish')); - } - }, - column1: [ - { - xtype: 'displayfield', - value: gettext('Ceph cluster configuration') + ':' - }, - { - xtype: 'proxmoxNetworkSelector', - name: 'network', - value: '', - fieldLabel: 'Public Network IP/CIDR', - bind: { - allowBlank: '{configuration}' - } - }, - { - xtype: 'proxmoxNetworkSelector', - name: 'cluster-network', - fieldLabel: 'Cluster Network IP/CIDR', - allowBlank: true, - autoSelect: false, - emptyText: gettext('Same as Public Network') - } - // FIXME: add hint about cluster network and/or reference user to docs?? - ], - column2: [ - { - xtype: 'displayfield', - value: gettext('First Ceph monitor') + ':' - }, - { - xtype: 'pveNodeSelector', - fieldLabel: gettext('Monitor node'), - name: 'mon-node', - selectCurNode: true, - allowBlank: false - }, - { - xtype: 'displayfield', - value: gettext('Additional monitors are recommended. They can be created at any time in the Monitor tab.'), - userCls: 'pve-hint' - } - ], - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'size', - fieldLabel: 'Number of replicas', - bind: { - value: '{replicas}' - }, - maxValue: 7, - minValue: 2, - emptyText: '3' - }, - { - xtype: 'numberfield', - name: 'min_size', - fieldLabel: 'Minimum replicas', - bind: { - maxValue: '{replicas}', - value: '{minreplicas}' - }, - minValue: 2, - maxValue: 3, - setMaxValue: function(value) { - this.maxValue = Ext.Number.from(value, 2); - // allow enough to avoid split brains with max 'size', but more makes simply no sense - if (this.maxValue > 4) { - this.maxValue = 4; - } - this.toggleSpinners(); - this.validate(); - }, - emptyText: '2' - } - ], - onGetValues: function(values) { - ['cluster-network', 'size', 'min_size'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - return values; - }, - onSubmit: function() { - var me = this; - if (!this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - var wizard = me.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - var monNode = kv['mon-node']; - delete kv['mon-node']; - var nodename = me.nodename; - delete kv.nodename; - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/ceph/init', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + monNode + '/ceph/mon/' + monNode, - waitMsgTarget: wizard, - method: 'POST', - success: function() { - me.up('pveCephInstallWizard').navigateNext(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - - } else { - me.up('pveCephInstallWizard').navigateNext(); - } - } - }, - { - title: gettext('Success'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'pve_ceph_install', - html: '

Installation successful!

'+ - '

The basic installation and configuration is completed, depending on your setup some of the following steps are required to start using Ceph:

'+ - '
  1. Install Ceph on other nodes
  2. '+ - '
  3. Create additional Ceph Monitors
  4. '+ - '
  5. Create Ceph OSDs
  6. '+ - '
  7. Create Ceph Pools
'+ - '

To learn more click on the help button below.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - - var tp = this.up('#wizcontent'); - var idx = tp.items.indexOf(this)-1; - for(;idx >= 0;idx--) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - onSubmit: function() { - var wizard = this.up('pveCephInstallWizard'); - wizard.close(); - } - } - ] - }); -Ext.define('PVE.node.DiskList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeDiskList', - - emptyText: gettext('No Disks found'), - - stateful: true, - stateId: 'grid-node-disks', - - columns: [ - { - header: gettext('Device'), - width: 150, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Type'), - width: 80, - sortable: true, - dataIndex: 'type', - renderer: function(v) { - if (v === 'ssd') { - return 'SSD'; - } else if (v === 'hdd') { - return 'Hard Disk'; - } else if (v === 'usb'){ - return 'USB'; - } else { - return gettext('Unknown'); - } - } - }, - { - header: gettext('Usage'), - width: 150, - sortable: false, - renderer: function(v, metaData, rec) { - if (rec) { - if (rec.data.osdid >= 0) { - var bluestore = ''; - if (rec.data.bluestore === 1) { - bluestore = ' (Bluestore)'; - } - return "Ceph osd." + rec.data.osdid.toString() + bluestore; - } - - var types = []; - if (rec.data.journals > 0) { - types.push('Journal'); - } - - if (rec.data.db > 0) { - types.push('DB'); - } - - if (rec.data.wal > 0) { - types.push('WAL'); - } - - if (types.length > 0) { - return 'Ceph (' + types.join(', ') + ')'; - } - } - - return v || Proxmox.Utils.noText; - }, - dataIndex: 'used' - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: 'GPT', - width: 60, - align: 'right', - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'gpt' - }, - { - header: gettext('Vendor'), - width: 100, - sortable: true, - hidden: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'vendor' - }, - { - header: gettext('Model'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'model' - }, - { - header: gettext('Serial'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'serial' - }, - { - header: 'S.M.A.R.T.', - width: 100, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'health' - }, - { - header: 'Wearout', - width: 90, - sortable: true, - align: 'right', - dataIndex: 'wearout', - renderer: function(value) { - if (Ext.isNumeric(value)) { - return (100 - value).toString() + '%'; - } - return 'N/A'; - } - } - ], - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var store = Ext.create('Ext.data.Store', { - storeid: 'node-disk-list' + nodename, - model: 'node-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list" - }, - sorters: [ - { - property : 'dev', - direction: 'ASC' - } - ] - }); - - var reloadButton = Ext.create('Proxmox.button.Button', { - text: gettext('Reload'), - handler: function() { - me.store.load(); - } - }); - - var smartButton = Ext.create('Proxmox.button.Button', { - text: gettext('Show S.M.A.R.T. values'), - selModel: sm, - enableFn: function() { - return !!sm.getSelection().length; - }, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - }); - - var initButton = Ext.create('Proxmox.button.Button', { - text: gettext('Initialize Disk with GPT'), - selModel: sm, - enableFn: function() { - var selection = sm.getSelection(); - - if (!selection.length || selection[0].data.used) { - return false; - } else { - return true; - } - }, - disabled: true, - - handler: function() { - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + nodename + '/disks/initgpt', - waitMsgTarget: me, - method: 'POST', - params: { disk: rec.data.devpath}, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - } - }); - - me.loadCount = 1; // avoid duplicate loadmask - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ reloadButton, smartButton, initButton ], - listeners: { - itemdblclick: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - } - }); - - - me.callParent(); - me.store.load(); - } -}, function() { - - Ext.define('node-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial', 'rpm', 'type', 'health', 'wearout' ], - idProperty: 'devpath' - }); -}); - -Ext.define('PVE.DiskSmartWindow', { - extend: 'Ext.window.Window', - alias: 'widget.pveSmartWindow', - - modal: true, - - items: [ - { - xtype: 'gridpanel', - layout: { - type: 'fit' - }, - emptyText: gettext('No S.M.A.R.T. Values'), - scrollable: true, - flex: 1, - itemId: 'smarts', - reserveScrollbar: true, - columns: [ - { text: 'ID', dataIndex: 'id', width: 50 }, - { text: gettext('Attribute'), flex: 1, dataIndex: 'name', renderer: Ext.String.htmlEncode }, - { text: gettext('Value'), dataIndex: 'raw', renderer: Ext.String.htmlEncode }, - { text: gettext('Normalized'), dataIndex: 'value', width: 60}, - { text: gettext('Threshold'), dataIndex: 'threshold', width: 60}, - { text: gettext('Worst'), dataIndex: 'worst', width: 60}, - { text: gettext('Flags'), dataIndex: 'flags'}, - { text: gettext('Failing'), dataIndex: 'fail', renderer: Ext.String.htmlEncode } - ] - }, - { - xtype: 'component', - itemId: 'text', - layout: { - type: 'fit' - }, - hidden: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace' - } - } - ], - - buttons: [ - { - text: gettext('Reload'), - name: 'reload', - handler: function() { - var me = this; - me.up('window').store.reload(); - } - }, - { - text: gettext('Close'), - name: 'close', - handler: function() { - var me = this; - me.up('window').close(); - } - } - ], - - layout: { - type: 'vbox', - align: 'stretch' - }, - width: 800, - height: 500, - minWidth: 600, - minHeight: 400, - bodyPadding: 5, - title: gettext('S.M.A.R.T. Values'), - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var dev = me.dev; - if (!dev) { - throw "no device specified"; - } - - me.store = Ext.create('Ext.data.Store', { - model: 'disk-smart', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/smart?disk=" + dev - } - }); - - me.callParent(); - var grid = me.down('#smarts'); - var text = me.down('#text'); - - Proxmox.Utils.monStoreErrors(grid, me.store); - me.mon(me.store, 'load', function(s, records, success) { - if (success && records.length > 0) { - var rec = records[0]; - switch (rec.data.type) { - case 'text': - grid.setVisible(false); - text.setVisible(true); - text.setHtml(Ext.String.htmlEncode(rec.data.text)); - break; - default: - // includes 'ata' - // cannot use empty case because - // of jslint - grid.setVisible(true); - text.setVisible(false); - grid.setStore(rec.attributes()); - break; - } - } - }); - - me.store.load(); - } -}, function() { - - Ext.define('disk-smart', { - extend: 'Ext.data.Model', - fields: [ - { name:'health'}, - { name:'type'}, - { name:'text'} - ], - hasMany: {model: 'smart-attribute', name: 'attributes'} - }); - Ext.define('smart-attribute', { - extend: 'Ext.data.Model', - fields: [ - { name:'id', type:'number' }, 'name', 'value', 'worst', 'threshold', 'flags', 'fail', 'raw' - ] - }); -}); -Ext.define('PVE.node.CreateLVM', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVM', - - subject: 'LVM Volume Group', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvm", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMList', { - extend: 'Ext.tree.Panel', - xtype: 'pveLVMList', - emptyText: gettext('No Volume Groups found'), - stateful: true, - stateId: 'grid-node-lvm', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Number of LVs'), - dataIndex: 'lvcount', - width: 150, - align: 'right' - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Volume Group', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVM', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/lvm", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'size', 'free', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - txt += (data.leaf) ? 'hdd-o' : 'object-group'; - return txt; - } - }, - { - type: 'number', - name: 'usage', - calculate: function(data) { - return ((data.size-data.free)/data.size); - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.CreateLVMThin', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVMThin', - - subject: 'LVM Thinpool', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvmthin", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMThinList', { - extend: 'Ext.grid.Panel', - xtype: 'pveLVMThinList', - - emptyText: gettext('No thinpools found'), - stateful: true, - stateId: 'grid-node-lvmthin', - columns: [ - { - text: gettext('Name'), - dataIndex: 'lv', - flex: 1 - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'lv_size' - }, - { - header: gettext('Used'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'used' - }, - { - header: gettext('Metadata Usage'), - width: 120, - dataIndex: 'metadata_usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Metadata Size'), - width: 120, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_size' - }, - { - header: gettext('Metadata Used'), - width: 125, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_used' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Thinpool', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVMThin', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['lv', 'lv_size', 'used', 'metadata_size', 'metadata_used', - { - type: 'number', - name: 'usage', - calculate: function(data) { - return data.used/data.lv_size; - } - }, - { - type: 'number', - name: 'metadata_usage', - calculate: function(data) { - return data.metadata_used/data.metadata_size; - } - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/lvmthin' - }, - sorters: 'lv' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.CreateDirectory', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateDirectory', - - subject: Proxmox.Utils.directoryText, - - showProgress: true, - - onlineHelp: 'chapter_storage', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/directory", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['ext4', 'ext4'], - ['xfs', 'xfs'] - ], - fieldLabel: gettext('Filesystem'), - name: 'filesystem', - value: '', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.Directorylist', { - extend: 'Ext.grid.Panel', - xtype: 'pveDirectoryList', - - stateful: true, - stateId: 'grid-node-directory', - columns: [ - { - text: gettext('Path'), - dataIndex: 'path', - flex: 1 - }, - { - header: gettext('Device'), - flex: 1, - dataIndex: 'device' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'type' - }, - { - header: gettext('Options'), - width: 100, - dataIndex: 'options' - }, - { - header: gettext('Unit File'), - hidden: true, - dataIndex: 'unitfile' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Directory', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateDirectory', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['path', 'device', 'type', 'options', 'unitfile' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/directory' - }, - sorters: 'path' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -/*jslint confusion: true*/ -Ext.define('PVE.node.CreateZFS', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateZFS', - - subject: 'ZFS', - - showProgress: true, - - onlineHelp: 'chapter_zfs', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - var update_disklist = function() { - var grid = me.down('#disklist'); - var disks = grid.getSelection(); - - var val = []; - disks.sort(function(a,b) { - var aorder = a.get('order') || 0; - var border = b.get('order') || 0; - return (aorder - border); - }); - - disks.forEach(function(disk) { - val.push(disk.get('devpath')); - }); - - me.down('field[name=devices]').setValue(val.join(',')); - }; - - Ext.apply(me, { - url: '/nodes/' + me.nodename + '/disks/zfs', - method: 'POST', - items: [ - { - xtype: 'inputpanel', - onGetValues: function(values) { - return values; - }, - column1: [ - { - xtype: 'textfield', - hidden: true, - name: 'devices', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ], - column2: [ - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('RAID Level'), - name: 'raidlevel', - value: 'single', - comboItems: [ - ['single', gettext('Single Disk')], - ['mirror', 'Mirror'], - ['raid10', 'RAID10'], - ['raidz', 'RAIDZ'], - ['raidz2', 'RAIDZ2'], - ['raidz3', 'RAIDZ3'] - ] - }, - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Compression'), - name: 'compression', - value: 'on', - comboItems: [ - ['on', 'on'], - ['off', 'off'], - ['gzip', 'gzip'], - ['lz4', 'lz4'], - ['lzjb', 'lzjb'], - ['zle', 'zle'] - ] - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('ashift'), - minValue: 9, - maxValue: 16, - value: '12', - name: 'ashift' - } - ], - columnB: [ - { - xtype: 'grid', - height: 200, - emptyText: gettext('No Disks unused'), - itemId: 'disklist', - selModel: 'checkboxmodel', - listeners: { - selectionchange: update_disklist - }, - store: { - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/disks/list?type=unused' - } - }, - columns: [ - { - text: gettext('Device'), - dataIndex: 'devpath', - flex: 1 - }, - { - text: gettext('Serial'), - dataIndex: 'serial' - }, - { - text: gettext('Size'), - dataIndex: 'size', - renderer: PVE.Utils.render_size - }, - { - header: gettext('Order'), - xtype: 'widgetcolumn', - dataIndex: 'order', - sortable: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 1, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('order', value); - update_disklist(record); - } - } - } - } - ] - } - ] - }, - { - xtype: 'displayfield', - padding: '5 0 0 0', - userCls: 'pve-hint', - value: 'Note: ZFS is not compatible with disks backed by a hardware ' + - 'RAID controller. For details see ' + - 'the reference documentation.', - } - ] - }); - - me.callParent(); - me.down('#disklist').getStore().load(); - } -}); - -Ext.define('PVE.node.ZFSDevices', { - extend: 'Ext.tree.Panel', - xtype: 'pveZFSDevices', - stateful: true, - stateId: 'grid-node-zfsstatus', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'state' - }, - { - text: 'READ', - dataIndex: 'read' - }, - { - text: 'WRITE', - dataIndex: 'write' - }, - { - text: 'CKSUM', - dataIndex: 'cksum' - }, - { - text: gettext('Message'), - dataIndex: 'msg' - } - ], - - rootVisible: true, - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/zfs/" + me.zpool, - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'status', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - if (data.leaf) { - return txt + 'hdd-o'; - } - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSStatus', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveZFSStatus', - layout: 'fit', - border: false, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - me.url = "/api2/extjs/nodes/" + me.nodename + "/disks/zfs/" + me.zpool; - - me.rows = { - scan: { - header: gettext('Scan') - }, - status: { - header: gettext('Status') - }, - action: { - header: gettext('Action') - }, - errors: { - header: gettext('Errors') - } - }; - - me.callParent(); - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSList', { - extend: 'Ext.grid.Panel', - xtype: 'pveZFSList', - - stateful: true, - stateId: 'grid-node-zfs', - columns: [ - { - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Size'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - }, - { - header: gettext('Allocated'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'alloc' - }, - { - header: gettext('Fragmentation'), - renderer: function(value) { - return value.toString() + '%'; - }, - dataIndex: 'frag' - }, - { - header: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'health' - }, - { - header: gettext('Deduplication'), - hidden: true, - renderer: function(value) { - return value.toFixed(2).toString() + 'x'; - }, - dataIndex: 'dedup' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': ZFS', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateZFS', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - }, - { - text: gettext('Detail'), - itemId: 'detailbtn', - disabled: true, - handler: function() { - var me = this.up('panel'); - var selection = me.getSelection(); - if (selection.length < 1) { - return; - } - me.show_detail(selection[0].get('name')); - } - } - ], - - show_detail: function(zpool) { - var me = this; - - var detailsgrid = Ext.create('PVE.node.ZFSStatus', { - layout: 'fit', - nodename: me.nodename, - flex: 0, - zpool: zpool - }); - - var devicetree = Ext.create('PVE.node.ZFSDevices', { - title: gettext('Devices'), - nodename: me.nodename, - flex: 1, - zpool: zpool - }); - - - var win = Ext.create('Ext.window.Window', { - modal: true, - width: 800, - height: 400, - resizable: true, - layout: 'fit', - title: gettext('Status') + ': ' + zpool, - items:[{ - xtype: 'panel', - region: 'center', - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [detailsgrid, devicetree], - tbar: [{ - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - - devicetree.reload(); - detailsgrid.reload(); - } - }] - }] - }).show(); - }, - - set_button_status: function() { - var me = this; - var selection = me.getSelection(); - me.down('#detailbtn').setDisabled(selection.length === 0); - }, - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - }, - selectionchange: function() { - this.set_button_status(); - }, - itemdblclick: function(grid, record) { - var me = this; - me.show_detail(record.get('name')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['name', 'size', 'free', 'alloc', 'dedup', 'frag', 'health'], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/zfs' - }, - sorters: 'name' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveNodeStatus', - - height: 300, - bodyPadding: '20 15 20 15', - - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 15 5 15' - }, - - items: [ - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpuinfo', - renderer: PVE.Utils.render_node_cpu_usage - }, - { - itemId: 'wait', - iconCls: 'fa fa-fw fa-clock-o', - title: gettext('IO delay'), - valueField: 'wait', - rowspan: 2 - }, - { - itemId: 'load', - iconCls: 'fa fa-fw fa-tasks', - title: gettext('Load average'), - printBar: false, - textField: 'loadavg' - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - itemId: 'memory', - title: gettext('RAM usage'), - valueField: 'memory', - maxField: 'memory', - renderer: PVE.Utils.render_node_size_usage - }, - { - itemId: 'ksm', - printBar: false, - title: gettext('KSM sharing'), - textField: 'ksm', - renderer: function(record) { - return PVE.Utils.render_size(record.shared); - }, - padding: '0 15 10 15' - }, - { - iconCls: 'fa fa-fw fa-hdd-o', - itemId: 'rootfs', - title: gettext('HD space') + '(root)', - valueField: 'rootfs', - maxField: 'rootfs', - renderer: PVE.Utils.render_node_size_usage - }, - { - iconCls: 'fa fa-fw fa-refresh', - itemId: 'swap', - printSize: true, - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'swap', - renderer: PVE.Utils.render_node_size_usage - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - itemId: 'cpus', - colspan: 2, - printBar: false, - title: gettext('CPU(s)'), - textField: 'cpuinfo', - renderer: function(cpuinfo) { - return cpuinfo.cpus + " x " + cpuinfo.model + " (" + - cpuinfo.sockets.toString() + " " + - (cpuinfo.sockets > 1 ? - gettext('Sockets') : - gettext('Socket') - ) + ")"; - }, - value: '' - }, - { - itemId: 'kversion', - colspan: 2, - title: gettext('Kernel Version'), - printBar: false, - textField: 'kversion', - value: '' - }, - { - itemId: 'version', - colspan: 2, - printBar: false, - title: gettext('PVE Manager Version'), - textField: 'pveversion', - value: '' - } - ], - - updateTitle: function() { - var me = this; - var uptime = Proxmox.Utils.render_uptime(me.getRecordValue('uptime')); - me.setTitle(me.pveSelNode.data.node + ' (' + gettext('Uptime') + ': ' + uptime + ')'); - } - -}); -Ext.define('PVE.node.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeSummary', - - scrollable: true, - bodyPadding: 5, - - showVersions: function() { - var me = this; - - // Note: we use simply text/html here, because ExtJS grid has problems - // with cut&paste - - var nodename = me.pveSelNode.data.node; - - var view = Ext.createWidget('component', { - autoScroll: true, - padding: 5, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Package versions'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + nodename + "/apt/versions", - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - var text = ''; - - Ext.Array.each(response.result.data, function(rec) { - var version = "not correctly installed"; - var pkg = rec.Package; - if (rec.OldVersion && rec.CurrentState === 'Installed') { - version = rec.OldVersion; - } - if (rec.RunningKernel) { - text += pkg + ': ' + version + ' (running kernel: ' + - rec.RunningKernel + ')\n'; - } else if (rec.ManagerVersion) { - text += pkg + ': ' + version + ' (running version: ' + - rec.ManagerVersion + ')\n'; - } else { - text += pkg + ': ' + version + '\n'; - } - }); - - view.update(Ext.htmlEncode(text)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var rstore = me.statusStore; - - var version_btn = new Ext.Button({ - text: gettext('Package versions'), - handler: function(){ - Proxmox.Utils.checked_command(function() { me.showVersions(); }); - } - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/rrddata", - model: 'pve-rrd-node' - }); - - Ext.apply(me, { - tbar: [version_btn, '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: 'column', - defaults: { - minHeight: 320, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: [ - { - xtype: 'pveNodeStatus', - rstore: rstore, - width: 770, - pveSelNode: me.pveSelNode - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - fields: ['cpu','iowait'], - fieldTitles: [gettext('CPU usage'), gettext('IO delay')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Server load'), - fields: ['loadavg'], - fieldTitles: [gettext('Load average')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - fields: ['memtotal','memused'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - fields: ['netin','netout'], - store: rrdstore - } - ] - } - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - } -}); -/*global Blob*/ -Ext.define('PVE.node.SubscriptionKeyEdit', { - extend: 'Proxmox.window.Edit', - title: gettext('Upload Subscription Key'), - width: 300, - items: { - xtype: 'textfield', - name: 'key', - value: '', - fieldLabel: gettext('Subscription Key') - }, - initComponent : function() { - var me = this; - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.Subscription', { - extend: 'Proxmox.grid.ObjectGrid', - - alias: ['widget.pveNodeSubscription'], - - onlineHelp: 'getting_help', - - viewConfig: { - enableTextSelection: true - }, - - showReport: function() { - var me = this; - var nodename = me.pveSelNode.data.node; - - var getReportFileName = function() { - var now = Ext.Date.format(new Date(), 'D-d-F-Y-G-i'); - return me.nodename + '-report-' + now + '.txt'; - }; - - var view = Ext.createWidget('component', { - itemId: 'system-report-view', - scrollable: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var reportWindow = Ext.create('Ext.window.Window', { - title: gettext('System Report'), - width: 1024, - height: 600, - layout: 'fit', - modal: true, - buttons: [ - '->', - { - text: gettext('Download'), - handler: function() { - var fileContent = reportWindow.getComponent('system-report-view').html; - var fileName = getReportFileName(); - - // Internet Explorer - if (window.navigator.msSaveOrOpenBlob) { - navigator.msSaveOrOpenBlob(new Blob([fileContent]), fileName); - } else { - var element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset=utf-8,' - + encodeURIComponent(fileContent)); - element.setAttribute('download', fileName); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - } - } - } - ], - items: view - }); - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + me.nodename + '/report', - method: 'GET', - waitMsgTarget: me, - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var report = Ext.htmlEncode(response.result.data); - reportWindow.show(); - view.update(report); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var reload = function() { - me.rstore.load(); - }; - - var baseurl = '/nodes/' + me.nodename + '/subscription'; - - var render_status = function(value) { - - var message = me.getObjectValue('message'); - - if (message) { - return value + ": " + message; - } - return value; - }; - - var rows = { - productname: { - header: gettext('Type') - }, - key: { - header: gettext('Subscription Key') - }, - status: { - header: gettext('Status'), - renderer: render_status - }, - message: { - visible: false - }, - serverid: { - header: gettext('Server ID') - }, - sockets: { - header: gettext('Sockets') - }, - checktime: { - header: gettext('Last checked'), - renderer: Proxmox.Utils.render_timestamp - }, - nextduedate: { - header: gettext('Next due date') - } - }; - - Ext.apply(me, { - url: '/api2/json' + baseurl, - cwidth1: 170, - tbar: [ - { - text: gettext('Upload Subscription Key'), - handler: function() { - var win = Ext.create('PVE.node.SubscriptionKeyEdit', { - url: '/api2/extjs/' + baseurl - }); - win.show(); - win.on('destroy', reload); - } - }, - { - text: gettext('Check'), - handler: function() { - Proxmox.Utils.API2Request({ - params: { force: 1 }, - url: baseurl, - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: reload - }); - } - }, - { - text: gettext('System Report'), - handler: function() { - Proxmox.Utils.checked_command(function (){ me.showReport(); }); - } - } - ], - rows: rows, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.node.CertificateView', { - extend: 'Ext.container.Container', - xtype: 'pveCertificatesView', - - onlineHelp: 'sysadmin_certificate_management', - - mixins: ['Proxmox.Mixin.CBind' ], - - items: [ - { - xtype: 'pveCertView', - border: 0, - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'pveACMEView', - border: 0, - cbind: { - nodename: '{nodename}' - } - } - ] - -}); - -Ext.define('PVE.node.CertificateViewer', { - extend: 'Proxmox.window.Edit', - - title: gettext('Certificate'), - - fieldDefaults: { - labelWidth: 120 - }, - width: 800, - resizable: true, - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Name'), - name: 'filename' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Fingerprint'), - name: 'fingerprint' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Issuer'), - name: 'issuer' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject'), - name: 'subject' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Valid Since'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notbefore' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Expires'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notafter' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject Alternative Names'), - name: 'san', - renderer: PVE.Utils.render_san - }, - { - xtype: 'textarea', - editable: false, - grow: true, - growMax: 200, - fieldLabel: gettext('Certificate'), - name: 'pem' - } - ], - - initComponent: function() { - var me = this; - - if (!me.cert) { - throw "no cert given"; - } - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/info'; - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - if (Ext.isArray(response.result.data)) { - Ext.Array.each(response.result.data, function(item) { - if (item.filename === me.cert) { - me.setValues(item); - return false; - } - }); - } - } - }); - } -}); - -Ext.define('PVE.node.CertUpload', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCertUpload', - - title: gettext('Upload Custom Certificate'), - resizable: false, - isCreate: true, - submitText: gettext('Upload'), - method: 'POST', - width: 600, - - apiCallDone: function(success, response, options) { - if (!success) { - return; - } - - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - items: [ - { - fieldLabel: gettext('Private Key (Optional)'), - labelAlign: 'top', - emptyText: gettext('No change'), - name: 'key', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=key]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'box', - autoEl: 'hr' - }, - { - fieldLabel: gettext('Certificate Chain'), - labelAlign: 'top', - allowBlank: false, - name: 'certificates', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=certificates]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'hidden', - name: 'restart', - value: '1' - }, - { - xtype: 'hidden', - name: 'force', - value: '1' - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/custom'; - - me.callParent(); - } -}); - -Ext.define('pve-certificate', { - extend: 'Ext.data.Model', - - fields: [ 'filename', 'fingerprint', 'issuer', 'notafter', 'notbefore', 'subject', 'san' ], - idProperty: 'filename' -}); - -Ext.define('PVE.node.Certificates', { - extend: 'Ext.grid.Panel', - xtype: 'pveCertView', - - tbar: [ - { - xtype: 'button', - text: gettext('Upload Custom Certificate'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.CertUpload', { - nodename: me.nodename - }); - win.show(); - win.on('destroy', me.reload, me); - } - }, - { - xtype: 'button', - itemId: 'deletebtn', - text: gettext('Delete Custom Certificate'), - handler: function() { - var me = this.up('grid'); - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/certificates/custom?restart=1', - method: 'DELETE', - success: function(response, opt) { - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - '-', - { - xtype: 'proxmoxButton', - itemId: 'viewbtn', - disabled: true, - text: gettext('View Certificate'), - handler: function() { - var me = this.up('grid'); - me.view_certificate(); - } - } - ], - - columns: [ - { - header: gettext('File'), - width: 150, - dataIndex: 'filename' - }, - { - header: gettext('Issuer'), - flex: 1, - dataIndex: 'issuer' - }, - { - header: gettext('Subject'), - flex: 1, - dataIndex: 'subject' - }, - { - header: gettext('Valid Since'), - width: 150, - dataIndex: 'notbefore', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Expires'), - width: 150, - dataIndex: 'notafter', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Subject Alternative Names'), - flex: 1, - dataIndex: 'san', - renderer: PVE.Utils.render_san - }, - { - header: gettext('Fingerprint'), - dataIndex: 'fingerprint', - hidden: true - }, - { - header: gettext('PEM'), - dataIndex: 'pem', - hidden: true - } - ], - - reload: function() { - var me = this; - me.rstore.load(); - }, - - set_button_status: function() { - var me = this; - var rec = me.rstore.getById('pveproxy-ssl.pem'); - - me.down('#deletebtn').setDisabled(!rec); - }, - - view_certificate: function() { - var me = this; - var selection = me.getSelection(); - if (!selection || selection.length < 1) { - return; - } - var win = Ext.create('PVE.node.CertificateViewer', { - cert: selection[0].data.filename, - nodename : me.nodename - }); - win.show(); - }, - - listeners: { - itemdblclick: 'view_certificate' - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'certs-' + me.nodename, - model: 'pve-certificate', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/certificates/info' - } - }); - - me.store = { - type: 'diff', - rstore: me.rstore - }; - - me.callParent(); - - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - } -}); -Ext.define('PVE.node.ACMEEditor', { - extend: 'Proxmox.window.Edit', - xtype: 'pveACMEEditor', - - subject: gettext('Domains'), - items: [ - { - xtype: 'inputpanel', - items: [ - { - xtype: 'textarea', - fieldLabel: gettext('Domains'), - emptyText: "domain1.example.com\ndomain2.example.com", - name: 'domains' - } - ], - onGetValues: function(values) { - if (!values.domains) { - return { - 'delete': 'acme' - }; - } - var domains = values.domains.split(/\n/).join(';'); - return { - 'acme': 'domains=' + domains - }; - } - } - ], - - initComponent: function() { - var me = this; - me.callParent(); - - me.load({ - success: function(response, opts) { - var res = PVE.Parser.parseACME(response.result.data.acme); - if (res) { - res.domains = res.domains.join(' '); - me.setValues(res); - } - } - }); - } -}); - -Ext.define('PVE.node.ACMEAccountCreate', { - extend: 'Proxmox.window.Edit', - - width: 400, - title: gettext('Register Account'), - isCreate: true, - method: 'POST', - submitText: gettext('Register'), - url: '/cluster/acme/account', - showTaskViewer: true, - - items: [ - { - xtype: 'proxmoxComboGrid', - name: 'directory', - allowBlank: false, - valueField: 'url', - displayField: 'name', - fieldLabel: gettext('ACME Directory'), - store: { - autoLoad: true, - fields: ['name', 'url'], - idProperty: ['name'], - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/acme/directories' - }, - sorters: { - property: 'name', - order: 'ASC' - } - }, - listConfig: { - columns: [ - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('URL'), - dataIndex: 'url', - flex: 1 - } - ] - }, - listeners: { - change: function(combogrid, value) { - var me = this; - if (!value) { - return; - } - - var disp = me.up('window').down('#tos_url_display'); - var field = me.up('window').down('#tos_url'); - var checkbox = me.up('window').down('#tos_checkbox'); - - disp.setValue(gettext('Loading')); - field.setValue(undefined); - checkbox.setValue(undefined); - - Proxmox.Utils.API2Request({ - url: '/cluster/acme/tos', - method: 'GET', - params: { - directory: value - }, - success: function(response, opt) { - me.up('window').down('#tos_url').setValue(response.result.data); - me.up('window').down('#tos_url_display').setValue(response.result.data); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - }, - { - xtype: 'displayfield', - itemId: 'tos_url_display', - fieldLabel: gettext('Terms of Service'), - renderer: PVE.Utils.render_optional_url, - name: 'tos_url_display' - }, - { - xtype: 'hidden', - itemId: 'tos_url', - name: 'tos_url' - }, - { - xtype: 'proxmoxcheckbox', - itemId: 'tos_checkbox', - fieldLabel: gettext('Accept TOS'), - submitValue: false, - validateValue: function(value) { - if (value && this.checked) { - return true; - } - return false; - } - }, - { - xtype: 'textfield', - name: 'contact', - vtype: 'email', - allowBlank: false, - fieldLabel: gettext('E-Mail') - } - ] - -}); - -Ext.define('PVE.node.ACMEAccountView', { - extend: 'Proxmox.window.Edit', - - width: 600, - fieldDefaults: { - labelWidth: 140 - }, - - title: gettext('Account'), - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('E-Mail'), - name: 'email' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Created'), - name: 'createdAt' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Status'), - name: 'status' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Directory'), - renderer: PVE.Utils.render_optional_url, - name: 'directory' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Terms of Services'), - renderer: PVE.Utils.render_optional_url, - name: 'tos' - } - ], - - initComponent: function() { - var me = this; - - if (!me.accountname) { - throw "no account name defined"; - } - - me.url = '/cluster/acme/account/' + me.accountname; - - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - var data = response.result.data; - data.email = data.account.contact[0]; - data.createdAt = data.account.createdAt; - data.status = data.account.status; - me.setValues(data); - } - }); - } -}); - -Ext.define('PVE.node.ACME', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveACMEView', - - margin: '10 0 0 0', - title: 'ACME', - - tbar: [ - { - xtype: 'button', - itemId: 'edit', - text: gettext('Edit Domains'), - handler: function() { - this.up('grid').run_editor(); - } - }, - { - xtype: 'button', - itemId: 'createaccount', - text: gettext('Register Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountCreate', { - taskDone: function() { - me.load_account(); - me.reload(); - } - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'viewaccount', - text: gettext('View Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountView', { - accountname: 'default' - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'order', - text: gettext('Order Certificate'), - handler: function() { - var me = this.up('grid'); - - Proxmox.Utils.API2Request({ - method: 'POST', - params: { - force: 1 - }, - url: '/nodes/' + me.nodename + '/certificates/acme/certificate', - success: function(response, opt) { - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: response.result.data, - taskDone: function(success) { - me.certificate_order_finished(success); - } - }); - win.show(); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ], - - certificate_order_finished: function(success) { - if (!success) { - return; - } - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - set_button_status: function() { - var me = this; - - var account = !!me.account; - var acmeObj = PVE.Parser.parseACME(me.getObjectValue('acme')); - var domains = acmeObj ? acmeObj.domains.length : 0; - - var order = me.down('#order'); - order.setVisible(account); - order.setDisabled(!account || !domains); - - me.down('#createaccount').setVisible(!account); - me.down('#viewaccount').setVisible(account); - }, - - load_account: function() { - var me = this; - - // for now we only use the 'default' account - Proxmox.Utils.API2Request({ - url: '/cluster/acme/account/default', - success: function(response, opt) { - me.account = response.result.data; - me.set_button_status(); - }, - failure: function(response, opt) { - me.account = undefined; - me.set_button_status(); - } - }); - }, - - run_editor: function() { - var me = this; - var win = Ext.create(me.rows.acme.editor, me.editorConfig); - win.show(); - win.on('destroy', me.reload, me); - }, - - listeners: { - itemdblclick: 'run_editor' - }, - - // account data gets loaded here - account: undefined, - - disableSelection: true, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/api2/json/nodes/' + me.nodename + '/config'; - - me.editorConfig = { - url: '/api2/extjs/nodes/' + me.nodename + '/config' - }; - /*jslint confusion: true*/ - /*acme is a string above*/ - me.rows = { - acme: { - defaultValue: '', - header: gettext('Domains'), - editor: 'PVE.node.ACMEEditor', - renderer: function(value) { - var acmeObj = PVE.Parser.parseACME(value); - if (acmeObj) { - return acmeObj.domains.join('
'); - } - return Proxmox.Utils.noneText; - } - } - }; - /*jslint confusion: false*/ - - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - me.load_account(); - } -}); -Ext.define('PVE.node.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.node.Config', - - onlineHelp: 'chapter_system_administration', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/status", - interval: 1000 - }); - - var node_command = function(cmd) { - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/status', - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var actionBtn = Ext.create('Ext.Button', { - text: gettext('Bulk Actions'), - iconCls: 'fa fa-fw fa-ellipsis-v', - disabled: !caps.nodes['Sys.PowerMgmt'], - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Bulk Start'), - iconCls: 'fa fa-fw fa-play', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - } - ] - }) - }); - - var restartBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Reboot'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Reboot node '{0}'?"), nodename), - handler: function() { - node_command('reboot'); - }, - iconCls: 'fa fa-undo' - }); - - var shutdownBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Shutdown'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Shutdown node '{0}'?"), nodename), - handler: function() { - node_command('shutdown'); - }, - iconCls: 'fa fa-power-off' - }); - - var shellBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.nodes['Sys.Console'], - text: gettext('Shell'), - consoleType: 'shell', - nodename: nodename - }); - - me.items = []; - - Ext.apply(me, { - title: gettext('Node') + " '" + nodename + "'", - hstateid: 'nodetab', - defaults: { statusStore: me.statusStore }, - tbar: [ restartBtn, shutdownBtn, shellBtn, actionBtn] - }); - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - itemId: 'summary', - xtype: 'pveNodeSummary' - }, - { - title: gettext('Notes'), - iconCls: 'fa fa-sticky-note-o', - itemId: 'notes', - xtype: 'pveNotesView' - } - ); - } - - if (caps.nodes['Sys.Console']) { - me.items.push( - { - title: gettext('Shell'), - iconCls: 'fa fa-terminal', - itemId: 'jsconsole', - xtype: 'pveNoVncConsole', - consoleType: 'shell', - xtermjs: true, - nodename: nodename - } - ); - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('System'), - iconCls: 'fa fa-cogs', - itemId: 'services', - expandedOnInit: true, - startOnlyServices: { - 'pveproxy': true, - 'pvedaemon': true, - 'pve-cluster': true - }, - nodename: nodename, - onlineHelp: 'pve_service_daemons', - xtype: 'proxmoxNodeServiceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - groups: ['services'], - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeNetworkView' - }, - { - title: gettext('Certificates'), - iconCls: 'fa fa-certificate', - itemId: 'certificates', - groups: ['services'], - nodename: nodename, - xtype: 'pveCertificatesView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'dns', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeDNSView' - }, - { - title: gettext('Hosts'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'hosts', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeHostsView' - }, - { - title: gettext('Time'), - itemId: 'time', - groups: ['services'], - nodename: nodename, - xtype: 'proxmoxNodeTimeView', - iconCls: 'fa fa-clock-o' - }); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push({ - title: 'Syslog', - iconCls: 'fa fa-list', - groups: ['services'], - disabled: !caps.nodes['Sys.Syslog'], - itemId: 'syslog', - xtype: 'proxmoxJournalView', - url: "/api2/extjs/nodes/" + nodename + "/journal" - }); - - if (caps.nodes['Sys.Modify']) { - me.items.push({ - title: gettext('Updates'), - iconCls: 'fa fa-refresh', - disabled: !caps.nodes['Sys.Console'], - // do we want to link to system updates instead? - itemId: 'apt', - xtype: 'proxmoxNodeAPT', - upgradeBtn: { - xtype: 'pveConsoleButton', - disabled: Proxmox.UserName !== 'root@pam', - text: gettext('Upgrade'), - consoleType: 'upgrade', - nodename: nodename - }, - nodename: nodename - }); - } - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - xtype: 'pveFirewallRules', - iconCls: 'fa fa-shield', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/nodes/' + nodename + '/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_host_specific_configuration', - groups: ['firewall'], - base_url: '/nodes/' + nodename + '/firewall/options', - fwtype: 'node', - itemId: 'firewall-options' - }); - } - - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Disks'), - itemId: 'storage', - expandedOnInit: true, - iconCls: 'fa fa-hdd-o', - xtype: 'pveNodeDiskList' - }, - { - title: 'LVM', - itemId: 'lvm', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square', - groups: ['storage'], - xtype: 'pveLVMList' - }, - { - title: 'LVM-Thin', - itemId: 'lvmthin', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square-o', - groups: ['storage'], - xtype: 'pveLVMThinList' - }, - { - title: Proxmox.Utils.directoryText, - itemId: 'directory', - onlineHelp: 'chapter_storage', - iconCls: 'fa fa-folder', - groups: ['storage'], - xtype: 'pveDirectoryList' - }, - { - title: 'ZFS', - itemId: 'zfs', - onlineHelp: 'chapter_zfs', - iconCls: 'fa fa-th-large', - groups: ['storage'], - xtype: 'pveZFSList' - }, - { - title: 'Ceph', - itemId: 'ceph', - iconCls: 'fa fa-ceph', - xtype: 'pveNodeCephStatus' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveNodeCephConfigCrush', - title: gettext('Configuration'), - iconCls: 'fa fa-gear', - groups: ['ceph'], - itemId: 'ceph-config' - }, - { - xtype: 'pveNodeCephMonMgr', - title: gettext('Monitor'), - iconCls: 'fa fa-tv', - groups: ['ceph'], - itemId: 'ceph-monlist' - }, - { - xtype: 'pveNodeCephOsdTree', - title: 'OSD', - iconCls: 'fa fa-hdd-o', - groups: ['ceph'], - itemId: 'ceph-osdtree' - }, - { - xtype: 'pveNodeCephFSPanel', - title: 'CephFS', - iconCls: 'fa fa-folder', - groups: ['ceph'], - nodename: nodename, - itemId: 'ceph-cephfspanel' - }, - { - xtype: 'pveNodeCephPoolList', - title: 'Pools', - iconCls: 'fa fa-sitemap', - groups: ['ceph'], - itemId: 'ceph-pools' - } - ); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push( - { - xtype: 'proxmoxLogView', - title: gettext('Log'), - iconCls: 'fa fa-list', - groups: ['firewall'], - onlineHelp: 'chapter_pve_firewall', - url: '/api2/extjs/nodes/' + nodename + '/firewall/log', - itemId: 'firewall-fwlog' - }, - { - title: gettext('Log'), - itemId: 'ceph-log', - iconCls: 'fa fa-list', - groups: ['ceph'], - onlineHelp: 'chapter_pveceph', - xtype: 'cephLogView', - url: "/api2/extjs/nodes/" + nodename + "/ceph/log", - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Task History'), - iconCls: 'fa fa-list', - itemId: 'tasks', - nodename: nodename, - xtype: 'proxmoxNodeTasks' - }, - { - title: gettext('Subscription'), - iconCls: 'fa fa-support', - itemId: 'support', - xtype: 'pveNodeSubscription', - nodename: nodename - } - ); - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var uptimerec = s.data.get('uptime'); - var powermgmt = uptimerec ? uptimerec.data.value : false; - if (!caps.nodes['Sys.PowerMgmt']) { - powermgmt = false; - } - restartBtn.setDisabled(!powermgmt); - shutdownBtn.setDisabled(!powermgmt); - shellBtn.setDisabled(!powermgmt); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.window.Migrate', { - extend: 'Ext.window.Window', - - vmtype: undefined, - nodename: undefined, - vmid: undefined, - - viewModel: { - data: { - vmid: undefined, - nodename: undefined, - vmtype: undefined, - running: false, - qemu: { - onlineHelp: 'qm_migration', - commonName: 'VM' - }, - lxc: { - onlineHelp: 'pct_migration', - commonName: 'CT' - }, - migration: { - possible: true, - preconditions: [], - 'with-local-disks': 0, - mode: undefined, - allowedNodes: undefined - } - - }, - - formulas: { - setMigrationMode: function(get) { - if (get('running')){ - if (get('vmtype') === 'qemu') { - return gettext('Online'); - } else { - return gettext('Restart Mode'); - } - } else { - return gettext('Offline'); - } - }, - setStorageselectorHidden: function(get) { - if (get('migration.with-local-disks') && get('running')) { - return false; - } else { - return true; - } - } - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=formPanel]': { - validityChange: function(panel, isValid) { - this.getViewModel().set('migration.possible', isValid); - this.checkMigratePreconditions(); - } - } - }, - - init: function(view) { - var me = this, - vm = view.getViewModel(); - - if (!view.nodename) { - throw "missing custom view config: nodename"; - } - vm.set('nodename', view.nodename); - - if (!view.vmid) { - throw "missing custom view config: vmid"; - } - vm.set('vmid', view.vmid); - - if (!view.vmtype) { - throw "missing custom view config: vmtype"; - } - vm.set('vmtype', view.vmtype); - - - view.setTitle( - Ext.String.format('{0} {1}{2}', gettext('Migrate'), vm.get(view.vmtype).commonName, view.vmid) - ); - me.lookup('proxmoxHelpButton').setHelpConfig({ - onlineHelp: vm.get(view.vmtype).onlineHelp - }); - me.checkMigratePreconditions(); - me.lookup('formPanel').isValid(); - - }, - - onTargetChange: function (nodeSelector) { - //Always display the storages of the currently seleceted migration target - this.lookup('pveDiskStorageSelector').setNodename(nodeSelector.value); - this.checkMigratePreconditions(); - }, - - startMigration: function() { - var me = this, - view = me.getView(), - vm = me.getViewModel(); - - var values = me.lookup('formPanel').getValues(); - var params = { - target: values.target - }; - - if (vm.get('migration.mode')) { - params[vm.get('migration.mode')] = 1; - } - if (vm.get('migration.with-local-disks')) { - params['with-local-disks'] = 1; - } - //only submit targetstorage if vm is running, storage migration to different storage is only possible online - if (vm.get('migration.with-local-disks') && vm.get('running')) { - params.targetstorage = values.targetstorage; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate', - waitMsgTarget: view, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var extraTitle = Ext.String.format(' ({0} ---> {1})', vm.get('nodename'), params.target); - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - extraTitle: extraTitle - }).show(); - - view.close(); - } - }); - - }, - - checkMigratePreconditions: function() { - var me = this, - vm = me.getViewModel(); - - - var vmrec = PVE.data.ResourceStore.findRecord('vmid', vm.get('vmid'), - 0, false, false, true); - if (vmrec && vmrec.data && vmrec.data.running) { - vm.set('running', true); - } - - if (vm.get('vmtype') === 'qemu') { - me.checkQemuPreconditions(); - } else { - me.checkLxcPreconditions(); - } - me.lookup('pveNodeSelector').disallowedNodes = [vm.get('nodename')]; - - // Only allow nodes where the local storage is available in case of offline migration - // where storage migration is not possible - me.lookup('pveNodeSelector').allowedNodes = vm.get('migration.allowedNodes'); - - me.lookup('formPanel').isValid(); - - }, - - checkQemuPreconditions: function() { - var me = this, - vm = me.getViewModel(), - migrateStats; - - if (vm.get('running')) { - vm.set('migration.mode', 'online'); - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate', - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - migrateStats = response.result.data; - if (migrateStats.running) { - vm.set('running', true); - } - // Get migration object from viewmodel to prevent - // to many bind callbacks - var migration = vm.get('migration'); - migration.preconditions = []; - - if (migrateStats.allowed_nodes) { - migration.allowedNodes = migrateStats.allowed_nodes; - var target = me.lookup('pveNodeSelector').value; - if (target.length && !migrateStats.allowed_nodes.includes(target)) { - let disallowed = migrateStats.not_allowed_nodes[target]; - let missing_storages = disallowed.unavailable_storages.join(', '); - - migration.possible = false; - migration.preconditions.push({ - text: 'Storage (' + missing_storages + ') not available on selected target. ' + - 'Start VM to use live storage migration or select other target node', - severity: 'error' - }); - } - } - - if (migrateStats.local_resources.length) { - migration.possible = false; - migration.preconditions.push({ - text: 'Can\'t migrate VM with local resources: '+ migrateStats.local_resources.join(', '), - severity: 'error' - }); - } - - if (migrateStats.local_disks.length) { - - migrateStats.local_disks.forEach(function (disk) { - if (disk.cdrom && disk.cdrom === 1) { - migration.possible = false; - migration.preconditions.push({ - text: "Can't migrate VM with local CD/DVD", - severity: 'error' - }); - - } else if (!disk.referenced_in_config) { - migration.possible = false; - migration.preconditions.push({ - text: 'Found not referenced/unused disk via storage: '+ disk.volid, - severity: 'error' - }); - } else { - migration['with-local-disks'] = 1; - migration.preconditions.push({ - text:'Migration with local disk might take long: ' + disk.volid - +' (' + PVE.Utils.render_size(disk.size) + ')', - severity: 'warning' - }); - } - }); - - } - - vm.set('migration', migration); - - } - }); - }, - checkLxcPreconditions: function() { - var me = this, - vm = me.getViewModel(); - if (vm.get('running')) { - vm.set('migration.mode', 'restart'); - } - } - - - }, - - width: 600, - modal: true, - layout: { - type: 'vbox', - align: 'stretch' - }, - border: false, - items: [ - { - xtype: 'form', - reference: 'formPanel', - bodyPadding: 10, - border: false, - layout: { - type: 'column' - }, - items: [ - { - xtype: 'container', - columnWidth: 0.5, - items: [{ - xtype: 'displayfield', - name: 'source', - fieldLabel: gettext('Source node'), - bind: { - value: '{nodename}' - } - }, - { - xtype: 'displayfield', - reference: 'migrationMode', - fieldLabel: gettext('Mode'), - bind: { - value: '{setMigrationMode}' - } - }] - }, - { - xtype: 'container', - columnWidth: 0.5, - items: [{ - xtype: 'pveNodeSelector', - reference: 'pveNodeSelector', - name: 'target', - fieldLabel: gettext('Target node'), - allowBlank: false, - disallowedNodes: undefined, - onlineValidator: true, - listeners: { - change: 'onTargetChange' - } - }, - { - xtype: 'pveStorageSelector', - reference: 'pveDiskStorageSelector', - name: 'targetstorage', - fieldLabel: gettext('Target storage'), - storageContent: 'images', - bind: { - hidden: '{setStorageselectorHidden}' - } - }] - } - ] - }, - { - xtype: 'gridpanel', - reference: 'preconditionGrid', - selectable: false, - flex: 1, - columns: [{ - text: '', - dataIndex: 'severity', - renderer: function(v) { - switch (v) { - case 'warning': - return ' '; - case 'error': - return ''; - default: - return v; - } - }, - width: 35 - }, - { - text: 'Info', - dataIndex: 'text', - cellWrap: true, - flex: 1 - }], - bind: { - hidden: '{!migration.preconditions.length}', - store: { - fields: ['severity','text'], - data: '{migration.preconditions}' - } - } - } - - ], - buttons: [ - { - xtype: 'proxmoxHelpButton', - reference: 'proxmoxHelpButton', - onlineHelp: 'pct_migration', - listenToGlobalEvent: false, - hidden: false - }, - '->', - { - xtype: 'button', - reference: 'submitButton', - text: gettext('Migrate'), - handler: 'startMigration', - bind: { - disabled: '{!migration.possible}' - } - } - ] -}); -Ext.define('PVE.window.BulkAction', { - extend: 'Ext.window.Window', - - resizable: true, - width: 800, - modal: true, - layout: { - type: 'fit' - }, - border: false, - - // the action to be set - // currently there are - // startall - // migrateall - // stopall - action: undefined, - - submit: function(params) { - var me = this; - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.action, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.hide(); - win.on('destroy', function() { - me.close(); - }); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.action) { - throw "no action specified"; - } - - if (!me.btnText) { - throw "no button text specified"; - } - - if (!me.title) { - throw "no title specified"; - } - - var items = []; - - if (me.action === 'migrateall') { - /*jslint confusion: true*/ - /*value is string and number*/ - items.push( - { - xtype: 'pveNodeSelector', - name: 'target', - disallowedNodes: [me.nodename], - fieldLabel: gettext('Target node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'maxworkers', - minValue: 1, - maxValue: 100, - value: 1, - fieldLabel: gettext('Parallel jobs'), - allowBlank: false - }, - { - itemId: 'lxcwarning', - xtype: 'displayfield', - userCls: 'pve-hint', - value: 'Warning: Running CTs will be migrated in Restart Mode.', - hidden: true // only visible if running container chosen - } - ); - /*jslint confusion: false*/ - } else if (me.action === 'startall') { - items.push({ - xtype: 'hiddenfield', - name: 'force', - value: 1 - }); - } - - items.push({ - xtype: 'vmselector', - itemId: 'vms', - name: 'vms', - flex: 1, - height: 300, - selectAll: true, - allowBlank: false, - nodename: me.nodename, - action: me.action, - listeners: { - selectionchange: function(vmselector, records) { - if (me.action == 'migrateall') { - var showWarning = records.some(function(item) { - return (item.data.type == 'lxc' && - item.data.status == 'running'); - }); - me.down('#lxcwarning').setVisible(showWarning); - } - } - } - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - layout: { - type: 'vbox', - align: 'stretch' - }, - fieldDefaults: { - labelWidth: 300, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: me.btnText, - handler: function() { - form.isValid(); - me.submit(form.getValues()); - } - }); - - Ext.apply(me, { - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - - form.on('validitychange', function() { - var valid = form.isValid(); - submitBtn.setDisabled(!valid); - }); - form.isValid(); - } -}); -Ext.define('PVE.window.Clone', { - extend: 'Ext.window.Window', - - resizable: false, - - isTemplate: false, - - onlineHelp: 'qm_copy_and_clone', - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=cloneform]': { - validitychange: 'disableSubmit' - } - }, - disableSubmit: function(form) { - this.lookupReference('submitBtn').setDisabled(!form.isValid()); - } - }, - - statics: { - // display a snapshot selector only if needed - wrap: function(nodename, vmid, isTemplate, guestType) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid +'/snapshot', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var snapshotList = response.result.data; - var hasSnapshots = snapshotList.length === 1 && - snapshotList[0].name === 'current' ? false : true; - - Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: isTemplate, - hasSnapshots: hasSnapshots - }).show(); - } - }); - } - }, - - create_clone: function(values) { - var me = this; - - var params = { newid: values.newvmid }; - - if (values.snapname && values.snapname !== 'current') { - params.snapname = values.snapname; - } - - if (values.pool) { - params.pool = values.pool; - } - - if (values.name) { - if (me.guestType === 'lxc') { - params.hostname = values.name; - } else { - params.name = values.name; - } - } - - if (values.target) { - params.target = values.target; - } - - if (values.clonemode === 'copy') { - params.full = 1; - if (values.hdstorage) { - params.storage = values.hdstorage; - if (values.diskformat && me.guestType !== 'lxc') { - params.format = values.diskformat; - } - } - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/clone', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - - }, - - // disable the Storage selector when clone mode is linked clone - updateVisibility: function() { - var me = this; - var clonemode = me.lookupReference('clonemodesel').getValue(); - var disksel = me.lookup('diskselector'); - disksel.setDisabled(clonemode === 'clone'); - }, - - // add to the list of valid nodes each node where - // all the VM disks are available - verifyFeature: function() { - var me = this; - - var snapname = me.lookupReference('snapshotsel').getValue(); - var clonemode = me.lookupReference('clonemodesel').getValue(); - - var params = { feature: clonemode }; - if (snapname !== 'current') { - params.snapname = snapname; - } - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/feature', - params: params, - method: 'GET', - failure: function(response, opts) { - me.lookupReference('submitBtn').setDisabled(true); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var res = response.result.data; - - me.lookupReference('targetsel').allowedNodes = res.nodes; - me.lookupReference('targetsel').validate(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.snapname) { - me.snapname = 'current'; - } - - if (!me.guestType) { - throw "no Guest Type specified"; - } - - var titletext = me.guestType === 'lxc' ? 'CT' : 'VM'; - if (me.isTemplate) { - titletext += ' Template'; - } - me.title = "Clone " + titletext + " " + me.vmid; - - var col1 = []; - var col2 = []; - - col1.push({ - xtype: 'pveNodeSelector', - name: 'target', - reference: 'targetsel', - fieldLabel: gettext('Target node'), - selectCurNode: true, - allowBlank: false, - onlineValidator: true, - listeners: { - change: function(f, value) { - me.lookupReference('hdstorage').setTargetNode(value); - } - } - }); - - var modelist = [['copy', gettext('Full Clone')]]; - if (me.isTemplate) { - modelist.push(['clone', gettext('Linked Clone')]); - } - - col1.push({ - xtype: 'pveGuestIDSelector', - name: 'newvmid', - guestType: me.guestType, - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - allowBlank: true, - fieldLabel: me.guestType === 'lxc' ? gettext('Hostname') : gettext('Name') - }, - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ); - - col2.push({ - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Mode'), - name: 'clonemode', - reference: 'clonemodesel', - allowBlank: false, - hidden: !me.isTemplate, - value: me.isTemplate ? 'clone' : 'copy', - comboItems: modelist, - listeners: { - change: function(t, value) { - me.updateVisibility(); - me.verifyFeature(); - } - } - }, - { - xtype: 'PVE.form.SnapshotSelector', - name: 'snapname', - reference: 'snapshotsel', - fieldLabel: gettext('Snapshot'), - nodename: me.nodename, - guestType: me.guestType, - vmid: me.vmid, - hidden: me.isTemplate || !me.hasSnapshots ? true : false, - disabled: false, - allowBlank: false, - value : me.snapname, - listeners: { - change: function(f, value) { - me.verifyFeature(); - } - } - }, - { - xtype: 'pveDiskStorageSelector', - reference: 'diskselector', - nodename: me.nodename, - autoSelect: false, - hideSize: true, - hideSelection: true, - storageLabel: gettext('Target Storage'), - allowBlank: true, - storageContent: me.guestType === 'qemu' ? 'images' : 'rootdir', - emptyText: gettext('Same as source'), - disabled: me.isTemplate ? true : false // because default mode is clone for templates - }); - - var formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - reference: 'cloneform', - border: false, - layout: 'column', - defaultType: 'container', - columns: 2, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: col1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: col2 - } - ] - }); - - Ext.apply(me, { - modal: true, - width: 600, - height: 250, - border: false, - layout: 'fit', - buttons: [ { - xtype: 'proxmoxHelpButton', - listenToGlobalEvent: false, - hidden: false, - onlineHelp: me.onlineHelp - }, - '->', - { - reference: 'submitBtn', - text: gettext('Clone'), - disabled: true, - handler: function() { - var cloneForm = me.lookupReference('cloneform'); - if (cloneForm.isValid()) { - me.create_clone(cloneForm.getValues()); - } - } - } ], - items: [ formPanel ] - }); - - me.callParent(); - - me.verifyFeature(); - } -}); -Ext.define('PVE.qemu.Monitor', { - extend: 'Ext.panel.Panel', - - alias: 'widget.pveQemuMonitor', - - maxLines: 500, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var history = []; - var histNum = -1; - var lines = []; - - var textbox = Ext.createWidget('panel', { - region: 'center', - xtype: 'panel', - autoScroll: true, - border: true, - margins: '5 5 5 5', - bodyStyle: 'font-family: monospace;' - }); - - var scrollToEnd = function() { - var el = textbox.getTargetEl(); - var dom = Ext.getDom(el); - - var clientHeight = dom.clientHeight; - // BrowserBug: clientHeight reports 0 in IE9 StrictMode - // Instead we are using offsetHeight and hardcoding borders - if (Ext.isIE9 && Ext.isStrict) { - clientHeight = dom.offsetHeight + 2; - } - dom.scrollTop = dom.scrollHeight - clientHeight; - }; - - var refresh = function() { - textbox.update('
' + lines.join('\n') + '
'); - scrollToEnd(); - }; - - var addLine = function(line) { - lines.push(line); - if (lines.length > me.maxLines) { - lines.shift(); - } - }; - - var executeCmd = function(cmd) { - addLine("# " + Ext.htmlEncode(cmd)); - if (cmd) { - history.unshift(cmd); - if (history.length > 20) { - history.splice(20); - } - } - histNum = -1; - - refresh(); - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/monitor", - method: 'POST', - waitMsgTarget: me, - success: function(response, opts) { - var res = response.result.data; - Ext.Array.each(res.split('\n'), function(line) { - addLine(Ext.htmlEncode(line)); - }); - refresh(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - textbox, - { - region: 'south', - margins:'0 5 5 5', - border: false, - xtype: 'textfield', - name: 'cmd', - value: '', - fieldStyle: 'font-family: monospace;', - allowBlank: true, - listeners: { - afterrender: function(f) { - f.focus(false); - addLine("Type 'help' for help."); - refresh(); - }, - specialkey: function(f, e) { - var key = e.getKey(); - switch (key) { - case e.ENTER: - var cmd = f.getValue(); - f.setValue(''); - executeCmd(cmd); - break; - case e.PAGE_UP: - textbox.scrollBy(0, -0.9*textbox.getHeight(), false); - break; - case e.PAGE_DOWN: - textbox.scrollBy(0, 0.9*textbox.getHeight(), false); - break; - case e.UP: - if (histNum + 1 < history.length) { - f.setValue(history[++histNum]); - } - e.preventDefault(); - break; - case e.DOWN: - if (histNum > 0) { - f.setValue(history[--histNum]); - } - e.preventDefault(); - break; - default: - break; - } - } - } - } - ], - listeners: { - show: function() { - var field = me.query('textfield[name="cmd"]')[0]; - field.focus(false, true); - } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.qemu.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveQemuSummary', - - scrollable: true, - bodyPadding: 5, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - if (!me.workspace) { - throw "no workspace specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var template = !!me.pveSelNode.data.template; - var rstore = me.statusStore; - - var width = template ? 1 : 0.5; - var items = [ - { - xtype: template ? 'pveTemplateStatusView' : 'pveGuestStatusView', - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - }, - itemId: 'gueststatus', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'pveNotesView', - maxHeight: 330, - itemId: 'notesview', - pveSelNode: me.pveSelNode, - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - } - } - ]; - - var rrdstore; - if (!template) { - - rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/qemu/" + vmid + "/rrddata", - model: 'pve-rrd-guest' - }); - - items.push( - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - pveSelNode: me.pveSelNode, - fields: ['cpu'], - fieldTitles: [gettext('CPU usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - pveSelNode: me.pveSelNode, - fields: ['maxmem', 'mem'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - pveSelNode: me.pveSelNode, - fields: ['netin','netout'], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Disk IO'), - pveSelNode: me.pveSelNode, - fields: ['diskread','diskwrite'], - store: rrdstore - } - ); - - } - - Ext.apply(me, { - tbar: [ '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: { - type: 'column' - }, - defaults: { - minHeight: 330, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: items - } - ] - }); - - me.callParent(); - if (!template) { - rrdstore.startUpdate(); - me.on('destroy', rrdstore.stopUpdate); - } - } -}); -Ext.define('PVE.qemu.OSTypeInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuOSTypePanel', - onlineHelp: 'qm_os_settings', - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'combobox[name=osbase]': { - change: 'onOSBaseChange' - }, - 'combobox[name=ostype]': { - afterrender: 'onOSTypeChange', - change: 'onOSTypeChange' - } - }, - onOSBaseChange: function(field, value) { - this.lookup('ostype').getStore().setData(PVE.Utils.kvm_ostypes[value]); - }, - onOSTypeChange: function(field) { - var me = this, ostype = field.getValue(); - if (!me.getView().insideWizard) { - return; - } - var targetValues = PVE.qemu.OSDefaults.getDefaults(ostype); - - me.setWidget('pveBusSelector', targetValues.busType); - me.setWidget('pveNetworkCardSelector', targetValues.networkCard); - var scsihw = targetValues.scsihw || '__default__'; - this.getViewModel().set('current.scsihw', scsihw); - }, - setWidget: function(widget, newValue) { - // changing a widget is safe only if ComponentQuery.query returns us - // a single value array - var widgets = Ext.ComponentQuery.query('pveQemuCreateWizard ' + widget); - if (widgets.length === 1) { - widgets[0].setValue(newValue); - } else { - throw 'non unique widget :' + widget + ' in Wizard'; - } - } - }, - - initComponent : function() { - var me = this; - - /*jslint confusion: true */ - me.items = [ - { - xtype: 'displayfield', - value: gettext('Guest OS') + ':', - hidden: !me.insideWizard - }, - { - xtype: 'combobox', - submitValue: false, - name: 'osbase', - fieldLabel: gettext('Type'), - editable: false, - queryMode: 'local', - value: 'Linux', - store: Object.keys(PVE.Utils.kvm_ostypes) - }, - { - xtype: 'combobox', - name: 'ostype', - reference: 'ostype', - fieldLabel: gettext('Version'), - value: 'l26', - allowBlank : false, - editable: false, - queryMode: 'local', - valueField: 'val', - displayField: 'desc', - store: { - fields: ['desc', 'val'], - data: PVE.Utils.kvm_ostypes.Linux, - listeners: { - datachanged: function (store) { - var ostype = me.lookup('ostype'); - var old_val = ostype.getValue(); - if (!me.insideWizard && old_val && store.find('val', old_val) != -1) { - ostype.setValue(old_val); - } else { - ostype.setValue(store.getAt(0)); - } - } - } - } - } - ]; - /*jslint confusion: false */ - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.OSTypeEdit', { - extend: 'Proxmox.window.Edit', - - subject: 'OS Type', - - items: [{ xtype: 'pveQemuOSTypePanel' }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response, options) { - var value = response.result.data.ostype || 'other'; - var osinfo = PVE.Utils.get_kvm_osinfo(value); - me.setValues({ ostype: value, osbase: osinfo.base }); - } - }); - } -}); -/* - * This class holds performance *recommended* settings for the PVE Qemu wizards - * the *mandatory* settings are set in the PVE::QemuServer - * config_to_command sub - * We store this here until we get the data from the API server -*/ - -// this is how you would add an hypothetic FreeBSD > 10 entry -// -//virtio-blk is stable but virtIO net still -// problematic as of 10.3 -// see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059 -// addOS({ -// parent: 'generic', // inherits defaults -// pveOS: 'freebsd10', // must match a radiofield in OSTypeEdit.js -// busType: 'virtio' // must match a pveBusController value -// // networkCard muss match a pveNetworkCardSelector - - -Ext.define('PVE.qemu.OSDefaults', { - singleton: true, // will also force creation when loaded - - constructor: function() { - var me = this; - - var addOS = function(settings) { - if (me.hasOwnProperty(settings.parent)) { - var child = Ext.clone(me[settings.parent]); - me[settings.pveOS] = Ext.apply(child, settings); - - } else { - throw("Could not find your genitor"); - } - }; - - // default values - me.generic = { - busType: 'ide', - networkCard: 'e1000', - busPriority: { - ide: 4, - sata: 3, - scsi: 2, - virtio: 1 - }, - scsihw: 'virtio-scsi-pci' - }; - - // virtio-net is in kernel since 2.6.25 - // virtio-scsi since 3.2 but backported in RHEL with 2.6 kernel - addOS({ - pveOS: 'l26', - parent : 'generic', - busType: 'scsi', - busPriority: { - scsi: 4, - virtio: 3, - sata: 2, - ide: 1 - }, - networkCard: 'virtio' - }); - - // recommandation from http://wiki.qemu.org/Windows2000 - addOS({ - pveOS: 'w2k', - parent : 'generic', - networkCard: 'rtl8139', - scsihw: '' - }); - // https://pve.proxmox.com/wiki/Windows_XP_Guest_Notes - addOS({ - pveOS: 'wxp', - parent : 'w2k' - }); - - me.getDefaults = function(ostype) { - if (PVE.qemu.OSDefaults[ostype]) { - return PVE.qemu.OSDefaults[ostype]; - } else { - return PVE.qemu.OSDefaults.generic; - } - }; - } -}); -Ext.define('PVE.qemu.ProcessorInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuProcessorPanel', - onlineHelp: 'qm_cpu', - - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - - updateCores: function() { - var me = this.getView(); - var sockets = me.down('field[name=sockets]').getValue(); - var cores = me.down('field[name=cores]').getValue(); - me.down('field[name=totalcores]').setValue(sockets*cores); - var vcpus = me.down('field[name=vcpus]'); - vcpus.setMaxValue(sockets*cores); - vcpus.setEmptyText(sockets*cores); - vcpus.validate(); - }, - - control: { - 'field[name=sockets]': { - change: 'updateCores' - }, - 'field[name=cores]': { - change: 'updateCores' - } - } - }, - - onGetValues: function(values) { - var me = this; - - if (Array.isArray(values['delete'])) { - values['delete'] = values['delete'].join(','); - } - - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - // build the cpu options: - me.cpu.cputype = values.cputype; - - if (values.flags) { - me.cpu.flags = values.flags; - } else { - delete me.cpu.flags; - } - - delete values.cputype; - delete values.flags; - var cpustring = PVE.Parser.printQemuCpu(me.cpu); - - // remove cputype delete request: - var del = values['delete']; - delete values['delete']; - if (del) { - del = del.split(','); - Ext.Array.remove(del, 'cputype'); - } else { - del = []; - } - - if (cpustring) { - values.cpu = cpustring; - } else { - del.push('cpu'); - } - - var delarr = del.join(','); - if (delarr) { - values['delete'] = delarr; - } - - return values; - }, - - cpu: {}, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'sockets', - minValue: 1, - maxValue: 4, - value: '1', - fieldLabel: gettext('Sockets'), - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: '1', - fieldLabel: gettext('Cores'), - allowBlank: false - } - ], - - column2: [ - { - xtype: 'CPUModelSelector', - name: 'cputype', - value: '__default__', - fieldLabel: gettext('Type') - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Total cores'), - name: 'totalcores', - value: '1' - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxintegerfield', - name: 'vcpus', - minValue: 1, - maxValue: 1, - value: '', - fieldLabel: gettext('VCPUs'), - deleteEmpty: true, - allowBlank: true, - emptyText: '1' - }, - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - maxValue: 128, // api maximum - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - minValue: 8, - maxValue: 500000, - value: '1024', - deleteEmpty: true, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable NUMA'), - name: 'numa', - uncheckedValue: 0 - } - ], - advancedColumnB: [ - { - xtype: 'label', - text: 'Extra CPU Flags:' - }, - { - xtype: 'vmcpuflagselector', - name: 'flags' - } - ] -}); - -Ext.define('PVE.qemu.ProcessorEdit', { - extend: 'Proxmox.window.Edit', - - width: 700, - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.ProcessorInputPanel'); - - Ext.apply(me, { - subject: gettext('Processors'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - var value = data.cpu; - if (value) { - var cpu = PVE.Parser.parseQemuCpu(value); - ipanel.cpu = cpu; - data.cputype = cpu.cputype; - if (cpu.flags) { - data.flags = cpu.flags; - } - } - me.setValues(data); - } - }); - } -}); -Ext.define('PVE.qemu.BootOrderPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuBootOrderPanel', - vmconfig: {}, // store loaded vm config - - bootdisk: undefined, - selection: [], - list: [], - comboboxes: [], - - isBootDisk: function(value) { - return PVE.Utils.bus_match.test(value); - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - var order = me.vmconfig.boot || 'cdn'; - me.bootdisk = me.vmconfig.bootdisk || undefined; - - // get the first 3 characters - // ignore the rest (there should never be more than 3) - me.selection = order.split('').slice(0,3); - - // build bootdev list - me.list = []; - Ext.Object.each(me.vmconfig, function(key, value) { - if (me.isBootDisk(key) && - !(/media=cdrom/).test(value)) { - me.list.push([key, "Disk '" + key + "'"]); - } - }); - - me.list.push(['d', 'CD-ROM']); - me.list.push(['n', gettext('Network')]); - me.list.push(['__none__', Proxmox.Utils.noneText]); - - me.recomputeList(); - - me.comboboxes.forEach(function(box) { - box.resetOriginalValue(); - }); - }, - - onGetValues: function(values) { - var me = this; - var order = me.selection.join(''); - var res = { boot: order }; - - if (me.bootdisk && order.indexOf('c') !== -1) { - res.bootdisk = me.bootdisk; - } else { - res['delete'] = 'bootdisk'; - } - - return res; - }, - - recomputeSelection: function(combobox, newVal, oldVal) { - var me = this.up('#inputpanel'); - me.selection = []; - me.comboboxes.forEach(function(item) { - var val = item.getValue(); - - // when selecting an already selected item, - // switch it around - if ((val === newVal || (me.isBootDisk(val) && me.isBootDisk(newVal))) && - item.name !== combobox.name && - newVal !== '__none__') { - // swap items - val = oldVal; - } - - // push 'c','d' or 'n' in the array - if (me.isBootDisk(val)) { - me.selection.push('c'); - me.bootdisk = val; - } else if (val === 'd' || - val === 'n') { - me.selection.push(val); - } - }); - - me.recomputeList(); - }, - - recomputeList: function(){ - var me = this; - // set the correct values in the kvcomboboxes - var cnt = 0; - me.comboboxes.forEach(function(item) { - if (cnt === 0) { - // never show 'none' on first combobox - item.store.loadData(me.list.slice(0, me.list.length-1)); - } else { - item.store.loadData(me.list); - } - item.suspendEvent('change'); - if (cnt < me.selection.length) { - item.setValue((me.selection[cnt] !== 'c')?me.selection[cnt]:me.bootdisk); - } else if (cnt === 0){ - item.setValue(''); - } else { - item.setValue('__none__'); - } - cnt++; - item.resumeEvent('change'); - item.validate(); - }); - }, - - initComponent : function() { - var me = this; - - // this has to be done here, because of - // the way our inputPanel class handles items - me.comboboxes = [ - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 1", - labelWidth: 120, - name: 'bd1', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 2", - labelWidth: 120, - name: 'bd2', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 3", - labelWidth: 120, - name: 'bd3', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }) - ]; - Ext.apply(me, { items: me.comboboxes }); - me.callParent(); - } -}); - -Ext.define('PVE.qemu.BootOrderEdit', { - extend: 'Proxmox.window.Edit', - - items: [{ - xtype: 'pveQemuBootOrderPanel', - itemId: 'inputpanel' - }], - - subject: gettext('Boot Order'), - - initComponent : function() { - var me = this; - me.callParent(); - me.load({ - success: function(response, options) { - me.down('#inputpanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuMemoryPanel', - onlineHelp: 'qm_memory', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var res = {}; - - res.memory = values.memory; - res.balloon = values.balloon; - - if (!values.ballooning) { - res.balloon = 0; - res['delete'] = 'shares'; - } else if (values.memory === values.balloon) { - delete res.balloon; - res['delete'] = 'balloon,shares'; - } else if (Ext.isDefined(values.shares) && (values.shares !== "")) { - res.shares = values.shares; - } else { - res['delete'] = "shares"; - } - - return res; - }, - - initComponent: function() { - var me = this; - var labelWidth = 160; - - me.items= [ - { - xtype: 'pveMemoryField', - labelWidth: labelWidth, - fieldLabel: gettext('Memory') + ' (MiB)', - name: 'memory', - minValue: 1, - step: 32, - hotplug: me.hotplug, - listeners: { - change: function(f, value, old) { - var bf = me.down('field[name=balloon]'); - var balloon = bf.getValue(); - bf.setMaxValue(value); - if (balloon === old) { - bf.setValue(value); - } - bf.validate(); - } - } - } - ]; - - me.advancedItems= [ - { - xtype: 'pveMemoryField', - name: 'balloon', - minValue: 1, - step: 32, - fieldLabel: gettext('Minimum memory') + ' (MiB)', - hotplug: me.hotplug, - labelWidth: labelWidth, - allowBlank: false, - listeners: { - change: function(f, value) { - var memory = me.down('field[name=memory]').getValue(); - var shares = me.down('field[name=shares]'); - shares.setDisabled(value === memory); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'shares', - disabled: true, - minValue: 0, - maxValue: 50000, - value: '', - step: 10, - fieldLabel: gettext('Shares'), - labelWidth: labelWidth, - allowBlank: true, - emptyText: Proxmox.Utils.defaultText + ' (1000)', - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - labelWidth: labelWidth, - value: '1', - name: 'ballooning', - fieldLabel: gettext('Ballooning Device'), - listeners: { - change: function(f, value) { - var bf = me.down('field[name=balloon]'); - var shares = me.down('field[name=shares]'); - var memory = me.down('field[name=memory]'); - bf.setDisabled(!value); - shares.setDisabled(!value || (bf.getValue() === memory.getValue())); - } - } - } - ]; - - if (me.insideWizard) { - me.column1 = me.items; - me.items = undefined; - me.advancedColumn1 = me.advancedItems; - me.advancedItems = undefined; - } - me.callParent(); - } - -}); - -Ext.define('PVE.qemu.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent: function() { - var me = this; - - var memoryhotplug; - if(me.hotplug) { - Ext.each(me.hotplug.split(','), function(el) { - if (el === 'memory') { - memoryhotplug = 1; - } - }); - } - - var ipanel = Ext.create('PVE.qemu.MemoryInputPanel', { - hotplug: memoryhotplug - }); - - Ext.apply(me, { - subject: gettext('Memory'), - items: [ ipanel ], - // uncomment the following to use the async configiguration API - // backgroundDelay: 5, - width: 400 - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - - var values = { - ballooning: data.balloon === 0 ? '0' : '1', - shares: data.shares, - memory: data.memory || '512', - balloon: data.balloon > 0 ? data.balloon : (data.memory || '512') - }; - - ipanel.setValues(values); - } - }); - } -}); -Ext.define('PVE.qemu.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuNetworkInputPanel', - onlineHelp: 'qm_network_device', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - me.network.model = values.model; - if (values.nonetwork) { - return {}; - } else { - me.network.bridge = values.bridge; - me.network.tag = values.tag; - me.network.firewall = values.firewall; - } - me.network.macaddr = values.macaddr; - me.network.disconnect = values.disconnect; - me.network.queues = values.queues; - - if (values.rate) { - me.network.rate = values.rate; - } else { - delete me.network.rate; - } - - var params = {}; - - params[me.confid] = PVE.Parser.printQemuNetwork(me.network); - - return params; - }, - - setNetwork: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data) { - data.networkmode = data.bridge ? 'bridge' : 'nat'; - } else { - data = {}; - data.networkmode = 'bridge'; - } - me.network = data; - - me.setValues(me.network); - }, - - setNodename: function(nodename) { - var me = this; - - me.bridgesel.setNodename(nodename); - }, - - initComponent : function() { - var me = this; - - me.network = {}; - me.confid = 'net0'; - - me.column1 = []; - me.column2 = []; - - me.bridgesel = Ext.create('PVE.form.BridgeSelector', { - name: 'bridge', - fieldLabel: gettext('Bridge'), - nodename: me.nodename, - autoSelect: true, - allowBlank: false - }); - - me.column1 = [ - me.bridgesel, - { - xtype: 'pveVlanField', - name: 'tag', - value: '' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - checked: (me.insideWizard || me.isCreate) - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Disconnect'), - name: 'disconnect' - } - ]; - - if (me.insideWizard) { - me.column1.unshift({ - xtype: 'checkbox', - name: 'nonetwork', - inputValue: 'none', - boxLabel: gettext('No network device'), - listeners: { - change: function(cb, value) { - var fields = [ - 'disconnect', - 'bridge', - 'tag', - 'firewall', - 'model', - 'macaddr', - 'rate', - 'queues' - ]; - fields.forEach(function(fieldname) { - me.down('field[name='+fieldname+']').setDisabled(value); - }); - me.down('field[name=bridge]').validate(); - } - } - }); - me.column2.unshift({ - xtype: 'displayfield' - }); - } - - me.column2.push( - { - xtype: 'pveNetworkCardSelector', - name: 'model', - fieldLabel: gettext('Model'), - value: PVE.qemu.OSDefaults.generic.networkCard, - allowBlank: false - }, - { - xtype: 'textfield', - name: 'macaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - allowBlank: true, - emptyText: 'auto' - }); - me.advancedColumn2 = [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: '', - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'queues', - fieldLabel: 'Multiqueue', - minValue: 1, - maxValue: 8, - value: '', - allowBlank: true - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.NetworkInputPanel', { - confid: me.confid, - nodename: nodename, - isCreate: me.isCreate - }); - - Ext.applyIf(me, { - subject: gettext('Network Device'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - if (!me.isCreate) { - var value = me.vmconfig[me.confid]; - var network = PVE.Parser.parseQemuNetwork(me.confid, value); - if (!network) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse network options'); - me.close(); - return; - } - ipanel.setNetwork(me.confid, network); - } else { - for (i = 0; i < 100; i++) { - confid = 'net' + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - me.confid = confid; - break; - } - } - ipanel.setNetwork(me.confid); - } - } - }); - } -}); -Ext.define('PVE.qemu.Smbios1InputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.PVE.qemu.Smbios1InputPanel', - - insideWizard: false, - - smbios1: {}, - - onGetValues: function(values) { - var me = this; - - var params = { - smbios1: PVE.Parser.printQemuSmbios1(values) - }; - - return params; - }, - - setSmbios1: function(data) { - var me = this; - - me.smbios1 = data; - - me.setValues(me.smbios1); - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: 'UUID', - regex: /^[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$/, - name: 'uuid' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Manufacturer'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'manufacturer' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Product'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'product' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Version'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'version' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Serial'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'serial' - }, - { - xtype: 'textareafield', - fieldLabel: 'SKU', - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'sku' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Family'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'family' - } - ] -}); - -Ext.define('PVE.qemu.Smbios1Edit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var ipanel = Ext.create('PVE.qemu.Smbios1InputPanel', {}); - - Ext.applyIf(me, { - subject: gettext('SMBIOS settings (type1)'), - width: 450, - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - var value = me.vmconfig.smbios1; - if (value) { - var data = PVE.Parser.parseQemuSmbios1(value); - if (!data) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse smbios options'); - me.close(); - return; - } - ipanel.setSmbios1(data); - } - } - }); - } -}); -Ext.define('PVE.qemu.CDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuCDInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || (values.controller + values.deviceid); - - me.drive.media = 'cdrom'; - if (values.mediaType === 'iso') { - me.drive.file = values.cdimage; - } else if (values.mediaType === 'cdrom') { - me.drive.file = 'cdrom'; - } else { - me.drive.file = 'none'; - } - - var params = {}; - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig, 'cdrom'); - } - }, - - setDrive: function(drive) { - var me = this; - - var values = {}; - if (drive.file === 'cdrom') { - values.mediaType = 'cdrom'; - } else if (drive.file === 'none') { - values.mediaType = 'none'; - } else { - values.mediaType = 'iso'; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.cdstorage = match[1]; - values.cdimage = drive.file; - } - } - - me.drive = drive; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - - me.cdstoragesel.setNodename(nodename); - me.cdfilesel.setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - var items = []; - - if (!me.confid) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - noVirtIO: true - }); - items.push(me.bussel); - } - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'iso', - boxLabel: gettext('Use CD/DVD disc image file (iso)'), - checked: true, - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=cdstorage]').setDisabled(!value); - me.down('field[name=cdimage]').setDisabled(!value); - me.down('field[name=cdimage]').validate(); - } - } - }); - - me.cdfilesel = Ext.create('PVE.form.FileSelector', { - name: 'cdimage', - nodename: me.nodename, - storageContent: 'iso', - fieldLabel: gettext('ISO image'), - labelAlign: 'right', - allowBlank: false - }); - - me.cdstoragesel = Ext.create('PVE.form.StorageSelector', { - name: 'cdstorage', - nodename: me.nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'iso', - allowBlank: false, - autoSelect: me.insideWizard, - listeners: { - change: function(f, value) { - me.cdfilesel.setStorage(value); - } - } - }); - - items.push(me.cdstoragesel); - items.push(me.cdfilesel); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'cdrom', - boxLabel: gettext('Use physical CD/DVD Drive') - }); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'none', - boxLabel: gettext('Do not use any media') - }); - - me.items = items; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CDEdit', { - extend: 'Proxmox.window.Edit', - - width: 400, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.CDInputPanel', { - confid: me.confid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: 'CD/DVD Drive', - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert('Error', 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - } - } - }); - } -}); -/*jslint confusion: true */ -/* 'change' property is assigned a string and then a function */ -Ext.define('PVE.qemu.HDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuHDInputPanel', - onlineHelp: 'qm_hard_disk', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - controller: { - - xclass: 'Ext.app.ViewController', - - onControllerChange: function(field) { - var value = field.getValue(); - - var allowIOthread = value.match(/^(virtio|scsi)/); - this.lookup('iothread').setDisabled(!allowIOthread); - if (!allowIOthread) { - this.lookup('iothread').setValue(false); - } - - var virtio = value.match(/^virtio/); - this.lookup('discard').setDisabled(virtio); - this.lookup('ssd').setDisabled(virtio); - if (virtio) { - this.lookup('discard').setValue(false); - this.lookup('ssd').setValue(false); - } - - this.lookup('scsiController').setVisible(value.match(/^scsi/)); - }, - - control: { - 'field[name=controller]': { - change: 'onControllerChange', - afterrender: 'onControllerChange' - }, - 'field[name=iothread]' : { - change: function(f, value) { - if (!this.getView().insideWizard) { - return; - } - var vmScsiType = value ? 'virtio-scsi-single': 'virtio-scsi-pci'; - this.lookupReference('scsiController').setValue(vmScsiType); - } - } - } - }, - - onGetValues: function(values) { - var me = this; - - var params = {}; - var confid = me.confid || (values.controller + values.deviceid); - - if (me.unused) { - me.drive.file = me.vmconfig[values.unusedId]; - confid = values.controller + values.deviceid; - } else if (me.isCreate) { - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - me.drive.file = values.hdstorage + ":" + values.disksize; - } - me.drive.format = values.diskformat; - } - - if (values.nobackup) { - me.drive.backup = 'no'; - } else { - delete me.drive.backup; - } - - if (values.noreplicate) { - me.drive.replicate = 'no'; - } else { - delete me.drive.replicate; - } - - if (values.discard) { - me.drive.discard = 'on'; - } else { - delete me.drive.discard; - } - - if (values.ssd) { - me.drive.ssd = 'on'; - } else { - delete me.drive.ssd; - } - - if (values.iothread) { - me.drive.iothread = 'on'; - } else { - delete me.drive.iothread; - } - - if (values.cache) { - me.drive.cache = values.cache; - } else { - delete me.drive.cache; - } - - var names = ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr']; - Ext.Array.each(names, function(name) { - if (values[name]) { - me.drive[name] = values[name]; - } else { - delete me.drive[name]; - } - var burst_name = name + '_max'; - if (values[burst_name] && values[name]) { - me.drive[burst_name] = values[burst_name]; - } else { - delete me.drive[burst_name]; - } - }); - - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - me.vmconfig = vmconfig; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig); - me.scsiController.setValue(vmconfig.scsihw); - } - if (me.unusedDisks) { - var disklist = []; - Ext.Object.each(vmconfig, function(key, value) { - if (key.match(/^unused\d+$/)) { - disklist.push([key, value]); - } - }); - me.unusedDisks.store.loadData(disklist); - me.unusedDisks.setValue(me.confid); - } - }, - - setDrive: function(drive) { - var me = this; - - me.drive = drive; - - var values = {}; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.hdstorage = match[1]; - } - - values.hdimage = drive.file; - values.nobackup = !PVE.Parser.parseBoolean(drive.backup, 1); - values.noreplicate = !PVE.Parser.parseBoolean(drive.replicate, 1); - values.diskformat = drive.format || 'raw'; - values.cache = drive.cache || '__default__'; - values.discard = (drive.discard === 'on'); - values.ssd = PVE.Parser.parseBoolean(drive.ssd); - values.iothread = PVE.Parser.parseBoolean(drive.iothread); - - values.mbps_rd = drive.mbps_rd; - values.mbps_wr = drive.mbps_wr; - values.iops_rd = drive.iops_rd; - values.iops_wr = drive.iops_wr; - values.mbps_rd_max = drive.mbps_rd_max; - values.mbps_wr_max = drive.mbps_wr_max; - values.iops_rd_max = drive.iops_rd_max; - values.iops_wr_max = drive.iops_wr_max; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - var labelWidth = 140; - - me.drive = {}; - - me.column1 = []; - me.column2 = []; - - me.advancedColumn1 = []; - me.advancedColumn2 = []; - - if (!me.confid || me.unused) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - vmconfig: me.insideWizard ? {ide2: 'cdrom'} : {} - }); - me.column1.push(me.bussel); - - me.scsiController = Ext.create('Ext.form.field.Display', { - fieldLabel: gettext('SCSI Controller'), - reference: 'scsiController', - bind: me.insideWizard ? { - value: '{current.scsihw}' - } : undefined, - renderer: PVE.Utils.render_scsihw, - submitValue: false, - hidden: true - }); - me.column1.push(me.scsiController); - } - - if (me.unused) { - me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', { - name: 'unusedId', - fieldLabel: gettext('Disk image'), - matchFieldWidth: false, - listConfig: { - width: 350 - }, - data: [], - allowBlank: false - }); - me.column1.push(me.unusedDisks); - } else if (me.isCreate) { - me.column1.push({ - xtype: 'pveDiskStorageSelector', - storageContent: 'images', - name: 'disk', - nodename: me.nodename, - autoSelect: me.insideWizard - }); - } else { - me.column1.push({ - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'hdimage' - }); - } - - me.column2.push( - { - xtype: 'CacheTypeSelector', - name: 'cache', - value: '__default__', - fieldLabel: gettext('Cache') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Discard'), - disabled: me.confid && me.confid.match(/^virtio/), - reference: 'discard', - name: 'discard' - } - ); - - me.advancedColumn1.push( - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && me.confid.match(/^virtio/), - fieldLabel: gettext('SSD emulation'), - labelWidth: labelWidth, - name: 'ssd', - reference: 'ssd' - }, - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && !me.confid.match(/^(virtio|scsi)/), - fieldLabel: 'IO thread', - labelWidth: labelWidth, - reference: 'iothread', - name: 'iothread' - }, - { - xtype: 'numberfield', - name: 'mbps_rd', - minValue: 1, - step: 1, - fieldLabel: gettext('Read limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'numberfield', - name: 'mbps_wr', - minValue: 1, - step: 1, - fieldLabel: gettext('Write limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd', - minValue: 10, - step: 10, - fieldLabel: gettext('Read limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr', - minValue: 10, - step: 10, - fieldLabel: gettext('Write limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - } - ); - - me.advancedColumn2.push( - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('No backup'), - labelWidth: labelWidth, - name: 'nobackup' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Skip replication'), - labelWidth: labelWidth, - name: 'noreplicate' - }, - { - xtype: 'numberfield', - name: 'mbps_rd_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Read max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'numberfield', - name: 'mbps_wr_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Write max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Read max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Write max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - } - ); - - me.callParent(); - } -}); -/*jslint confusion: false */ - -Ext.define('PVE.qemu.HDEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - backgroundDelay: 5, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.qemu.HDInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - me.subject = gettext('Unused Disk'); - } else if (me.isCreate) { - me.subject = gettext('Hard Disk'); - } else { - me.subject = gettext('Hard Disk') + ' (' + me.confid + ')'; - } - - me.items = [ ipanel ]; - - me.callParent(); - /*jslint confusion: true*/ - /* 'data' is assigned an empty array in same file, and here we - * use it like an object - */ - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - me.isValid(); // trigger validation - } - } - }); - /*jslint confusion: false*/ - } -}); -Ext.define('PVE.window.HDResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 140, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 250, - height: 150, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -Ext.define('PVE.window.HDMove', { - extend: 'Ext.window.Window', - - resizable: false, - - - move_disk: function(disk, storage, format, delete_disk) { - var me = this; - var qemu = (me.type === 'qemu'); - var params = {}; - params.storage = storage; - params[qemu ? 'disk':'volume'] = disk; - - if (format && qemu) { - params.format = format; - } - - if (delete_disk) { - params['delete'] = 1; - } - - var url = '/nodes/' + me.nodename + '/' + me.type + '/' + me.vmid + '/'; - url += qemu ? 'move_disk' : 'move_volume'; - - Proxmox.Utils.API2Request({ - params: params, - url: url, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - win.on('destroy', function() { me.close(); }); - } - }); - - }, - - initComponent : function() { - var me = this; - - var diskarray = []; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.type) { - me.type = 'qemu'; - } - - var qemu = (me.type === 'qemu'); - - var items = [ - { - xtype: 'displayfield', - name: qemu ? 'disk' : 'volume', - value: me.disk, - fieldLabel: qemu ? gettext('Disk') : gettext('Mount Point'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - items.push({ - xtype: 'pveDiskStorageSelector', - storageLabel: gettext('Target Storage'), - nodename: me.nodename, - storageContent: qemu ? 'images' : 'rootdir', - hideSize: true - }); - - items.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Delete source'), - name: 'deleteDisk', - uncheckedValue: 0, - checked: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = qemu ? gettext("Move disk") : gettext('Move Volume'); - submitBtn = Ext.create('Ext.Button', { - text: me.title, - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.move_disk(me.disk, values.hdstorage, values.diskformat, - values.deleteDisk); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 350, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - me.mon(me.formPanel, 'validitychange', function(fp, isValid) { - submitBtn.setDisabled(!isValid); - }); - - me.formPanel.isValid(); - } -}); -Ext.define('PVE.qemu.EFIDiskInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveEFIDiskInputPanel', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var confid = 'efidisk0'; - - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - // we use 1 here, because for efi the size gets overridden from the backend - me.drive.file = values.hdstorage + ":1"; - } - - me.drive.format = values.diskformat; - var params = {}; - params[confid] = PVE.Parser.printQemuDrive(me.drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items= []; - - me.items.push({ - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - }); - me.callParent(); - } -}); - -Ext.define('PVE.qemu.EFIDiskEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - subject: gettext('EFI Disk'), - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveEFIDiskInputPanel', - onlineHelp: 'qm_bios_and_uefi', - confid: me.confid, - nodename: nodename, - isCreate: true - }]; - - me.callParent(); - } -}); -Ext.define('PVE.qemu.DisplayInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveDisplayInputPanel', - - onGetValues: function(values) { - var ret = PVE.Parser.printPropertyString(values, 'type'); - if (ret === '') { - return { - 'delete': 'vga' - }; - } - return { - vga: ret - }; - }, - - items: [{ - name: 'type', - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - comboItems: PVE.Utils.kvm_vga_driver_array(), - validator: function() { - var v = this.getValue(); - var cfg = this.up('proxmoxWindowEdit').vmconfig || {}; - - if (v.match(/^serial\d+$/) && (!cfg[v] || cfg[v] !== 'socket')) { - var fmt = gettext("Serial interface '{0}' is not correctly configured."); - return Ext.String.format(fmt, v); - } - return true; - }, - listeners: { - change: function(cb, val) { - var me = this.up('panel'); - if (!val) { - return; - } - var disable = false; - var emptyText = Proxmox.Utils.defaultText; - switch (val) { - case "cirrus": - emptyText = "4"; - break; - case "std": - emptyText = "16"; - break; - case "qxl": - case "qxl2": - case "qxl3": - case "qxl4": - emptyText = "16"; - break; - case "vmware": - emptyText = "16"; - break; - case "none": - case "serial0": - case "serial1": - case "serial2": - case "serial3": - emptyText = 'N/A'; - disable = true; - break; - case "virtio": - emptyText = "256"; - break; - default: - break; - } - var memoryfield = me.down('field[name=memory]'); - memoryfield.setEmptyText(emptyText); - memoryfield.setDisabled(disable); - } - } - },{ - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Memory') + ' (MiB)', - minValue: 4, - maxValue: 512, - step: 4, - name: 'memory' - }] -}); - -Ext.define('PVE.qemu.DisplayEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - subject: gettext('Display'), - width: 350, - - items: [{ - xtype: 'pveDisplayInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response) { - me.vmconfig = response.result.data; - var vga = me.vmconfig.vga || '__default__'; - me.setValues(PVE.Parser.parsePropertyString(vga, 'type')); - } - }); - } -}); -Ext.define('PVE.qemu.KeyboardEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('Keyboard Layout'), - items: { - xtype: 'VNCKeyboardSelector', - name: 'keyboard', - value: '__default__', - fieldLabel: gettext('Keyboard Layout') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.HardwareView', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.HardwareView'], - - onlineHelp: 'qm_virtual_machines_settings', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - var iconCls = rowdef.iconCls; - var icon = ''; - var txt = (rowdef.header || key); - - metaData.tdAttr = "valign=middle"; - - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - if (rowdef.tdCls == 'pve-itype-icon-storage') { - var value = me.getObjectValue(key, '', false); - if (value === '') { - value = me.getObjectValue(key, '', true); - } - if (value.match(/vm-.*-cloudinit/)) { - metaData.tdCls = 'pve-itype-icon-cloud'; - return rowdef.cloudheader; - } else if (value.match(/media=cdrom/)) { - metaData.tdCls = 'pve-itype-icon-cdrom'; - return rowdef.cdheader; - } - } - } else if (iconCls) { - icon = ""; - metaData.tdCls += " pve-itype-fa"; - } - return icon + txt; - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - /*jslint confusion: true */ - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined, - never_delete: true, - defaultValue: '512', - tdCls: 'pve-itype-icon-memory', - group: 2, - multiKey: ['memory', 'balloon', 'shares'], - renderer: function(value, metaData, record, ri, ci, store, pending) { - var res = ''; - - var max = me.getObjectValue('memory', 512, pending); - var balloon = me.getObjectValue('balloon', undefined, pending); - var shares = me.getObjectValue('shares', undefined, pending); - - res = Proxmox.Utils.format_size(max*1024*1024); - - if (balloon !== undefined && balloon > 0) { - res = Proxmox.Utils.format_size(balloon*1024*1024) + "/" + res; - - if (shares) { - res += ' [shares=' + shares +']'; - } - } else if (balloon === 0) { - res += ' [balloon=0]'; - } - return res; - } - }, - sockets: { - header: gettext('Processors'), - never_delete: true, - editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ? - 'PVE.qemu.ProcessorEdit' : undefined, - tdCls: 'pve-itype-icon-processor', - group: 3, - defaultValue: '1', - multiKey: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'], - renderer: function(value, metaData, record, rowIndex, colIndex, store, pending) { - - var sockets = me.getObjectValue('sockets', 1, pending); - var model = me.getObjectValue('cpu', undefined, pending); - var cores = me.getObjectValue('cores', 1, pending); - var numa = me.getObjectValue('numa', undefined, pending); - var vcpus = me.getObjectValue('vcpus', undefined, pending); - var cpulimit = me.getObjectValue('cpulimit', undefined, pending); - var cpuunits = me.getObjectValue('cpuunits', undefined, pending); - - var res = Ext.String.format('{0} ({1} sockets, {2} cores)', - sockets*cores, sockets, cores); - - if (model) { - res += ' [' + model + ']'; - } - - if (numa) { - res += ' [numa=' + numa +']'; - } - - if (vcpus) { - res += ' [vcpus=' + vcpus +']'; - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit +']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits +']'; - } - - return res; - } - }, - bios: { - header: 'BIOS', - group: 4, - never_delete: true, - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.BiosEdit' : undefined, - defaultValue: '', - iconCls: 'microchip', - renderer: PVE.Utils.render_qemu_bios - }, - vga: { - header: gettext('Display'), - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined, - never_delete: true, - tdCls: 'pve-itype-icon-display', - group:5, - defaultValue: '', - renderer: PVE.Utils.render_kvm_vga_driver - }, - machine: { - header: gettext('Machine'), - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Machine'), - width: 350, - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - }]} : undefined, - iconCls: 'cogs', - never_delete: true, - group: 6, - defaultValue: '', - renderer: PVE.Utils.render_qemu_machine - }, - scsihw: { - header: gettext('SCSI Controller'), - iconCls: 'database', - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.ScsiHwEdit' : undefined, - renderer: PVE.Utils.render_scsihw, - group: 7, - never_delete: true, - defaultValue: '' - }, - cores: { - visible: false - }, - cpu: { - visible: false - }, - numa: { - visible: false - }, - balloon: { - visible: false - }, - hotplug: { - visible: false - }, - vcpus: { - visible: false - }, - cpuunits: { - visible: false - }, - cpulimit: { - visible: false - }, - shares: { - visible: false - } - }; - /*jslint confusion: false */ - - PVE.Utils.forEachBus(undefined, function(type, id) { - var confid = type + id; - rows[confid] = { - group: 10, - tdCls: 'pve-itype-icon-storage', - editor: 'PVE.qemu.HDEdit', - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - header: gettext('Hard Disk') + ' (' + confid +')', - cdheader: gettext('CD/DVD Drive') + ' (' + confid +')', - cloudheader: gettext('CloudInit Drive') + ' (' + confid + ')' - }; - }); - for (i = 0; i < 32; i++) { - confid = "net" + i.toString(); - rows[confid] = { - group: 15, - order: i, - tdCls: 'pve-itype-icon-network', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined, - never_delete: caps.vms['VM.Config.Network'] ? false : true, - header: gettext('Network Device') + ' (' + confid +')' - }; - } - rows.efidisk0 = { - group: 20, - tdCls: 'pve-itype-icon-storage', - editor: null, - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - header: gettext('EFI Disk') - }; - for (i = 0; i < 5; i++) { - confid = "usb" + i.toString(); - rows[confid] = { - group: 25, - order: i, - tdCls: 'pve-itype-icon-usb', - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined, - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('USB Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < 4; i++) { - confid = "hostpci" + i.toString(); - rows[confid] = { - group: 30, - order: i, - tdCls: 'pve-itype-icon-pci', - never_delete: caps.nodes['Sys.Console'] ? false : true, - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.PCIEdit' : undefined, - header: gettext('PCI Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < 4; i++) { - confid = "serial" + i.toString(); - rows[confid] = { - group: 35, - order: i, - tdCls: 'pve-itype-icon-serial', - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('Serial Port') + ' (' + confid + ')' - }; - } - for (i = 0; i < 256; i++) { - rows["unused" + i.toString()] = { - group: 99, - order: i, - tdCls: 'pve-itype-icon-storage', - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined, - header: gettext('Unused Disk') + ' ' + i.toString() - }; - } - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - var reload = function() { - me.rstore.load(); - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var editor = rowdef.editor; - if (rowdef.tdCls == 'pve-itype-icon-storage') { - var value = me.getObjectValue(rec.data.key, '', true); - if (value.match(/vm-.*-cloudinit/)) { - return; - } else if (value.match(/media=cdrom/)) { - editor = 'PVE.qemu.CDEdit'; - } else if (!diskCap) { - return; - } - } - - var win; - - if (Ext.isString(editor)) { - win = Ext.create(editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', reload); - }; - - var run_resize = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', reload); - }; - - var run_move = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', reload); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - handler: run_editor - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: sm, - disabled: true, - handler: run_resize - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move disk'), - selModel: sm, - disabled: true, - handler: run_move - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - defaultText: gettext('Remove'), - altText: gettext('Detach'), - selModel: sm, - disabled: true, - dangerous: true, - RESTMethod: 'PUT', - confirmMsg: function(rec) { - var warn = gettext('Are you sure you want to remove entry {0}'); - if (this.text === this.altText) { - warn = gettext('Are you sure you want to detach entry {0}'); - } - - var entry = rec.data.key; - var msg = Ext.String.format(warn, "'" - + me.renderKey(entry, {}, rec) + "'"); - - if (entry.match(/^unused\d+$/)) { - msg += " " + gettext('This will permanently erase all data.'); - } - - return msg; - }, - handler: function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: b.RESTMethod, - params: { - 'delete': rec.data.key - }, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - if (b.RESTMethod === 'POST') { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: function () { - me.reload(); - } - } - }); - win.show(); - } - } - }); - }, - listeners: { - render: function(btn) { - // hack: calculate an optimal button width on first display - // to prevent the whole toolbar to move when we switch - // between the "Remove" and "Detach" labels - var def = btn.getSize().width; - - btn.setText(btn.altText); - var alt = btn.getSize().width; - - btn.setText(btn.defaultText); - - var optimal = alt > def ? alt : def; - btn.setSize({ width: optimal }); - } - } - }); - - var revert_btn = new Proxmox.button.Button({ - text: gettext('Revert'), - selModel: sm, - disabled: true, - handler: function(b, e, rec) { - var rowdef = me.rows[rec.data.key] || {}; - var keys = rowdef.multiKey || [ rec.data.key ]; - var revert = keys.join(','); - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'revert': revert - }, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - } - }); - } - }); - - var efidisk_menuitem = Ext.create('Ext.menu.Item',{ - text: gettext('EFI Disk'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - - var rstoredata = me.rstore.getData().map; - // check if ovmf is configured - if (rstoredata.bios && rstoredata.bios.data.value === 'ovmf') { - var win = Ext.create('PVE.qemu.EFIDiskEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } else { - Ext.Msg.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.')); - } - - } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - // disable button when we have an efidisk already - // disable is ok in this case, because you can instantly - // see that there is already one - efidisk_menuitem.setDisabled(me.rstore.getData().map.efidisk0 !== undefined); - // en/disable usb add button - var usbcount = 0; - var pcicount = 0; - var hasCloudInit = false; - me.rstore.getData().items.forEach(function(item){ - if (/^usb\d+/.test(item.id)) { - usbcount++; - } else if (/^hostpci\d+/.test(item.id)) { - pcicount++; - } - if (!hasCloudInit && /vm-.*-cloudinit/.test(item.data.value)) { - hasCloudInit = true; - } - }); - - // heuristic only for disabling some stuff, the backend has the final word. - var noSysConsolePerm = !caps.nodes['Sys.Console']; - - me.down('#addusb').setDisabled(noSysConsolePerm || (usbcount >= 5)); - me.down('#addpci').setDisabled(noSysConsolePerm || (pcicount >= 4)); - me.down('#addci').setDisabled(noSysConsolePerm || hasCloudInit); - - if (!rec) { - remove_btn.disable(); - edit_btn.disable(); - resize_btn.disable(); - move_btn.disable(); - revert_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var isCDRom = (value && !!value.toString().match(/media=cdrom/)); - var isUnusedDisk = key.match(/^unused\d+/); - var isUsedDisk = !isUnusedDisk && - rowdef.tdCls == 'pve-itype-icon-storage' && - !isCDRom; - - var isCloudInit = (value && value.toString().match(/vm-.*-cloudinit/)); - - var isEfi = (key === 'efidisk0'); - - remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete === true) || (isUnusedDisk && !diskCap)); - remove_btn.setText((isUsedDisk && !isCloudInit) ? remove_btn.altText : remove_btn.defaultText); - remove_btn.RESTMethod = isUnusedDisk ? 'POST':'PUT'; - - edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor || isCloudInit || (!isCDRom && !diskCap)); - - resize_btn.setDisabled(pending || !isUsedDisk || !diskCap); - - move_btn.setDisabled(pending || !isUsedDisk || !diskCap); - - revert_btn.setDisabled(!pending); - - }; - - Ext.apply(me, { - url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + '/pending', - interval: 5000, - selModel: sm, - run_editor: run_editor, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Hard Disk'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.HDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('CD/DVD Drive'), - iconCls: 'pve-itype-icon-cdrom', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.CDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Network Device'), - iconCls: 'pve-itype-icon-network', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.qemu.NetworkEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode, - isCreate: true - }); - win.on('destroy', reload); - win.show(); - } - }, - efidisk_menuitem, - { - text: gettext('USB Device'), - itemId: 'addusb', - iconCls: 'pve-itype-icon-usb', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.USBEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('PCI Device'), - itemId: 'addpci', - iconCls: 'pve-itype-icon-pci', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.PCIEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Serial Port'), - itemId: 'addserial', - iconCls: 'pve-itype-icon-serial', - disabled: !caps.vms['VM.Config.Options'], - handler: function() { - var win = Ext.create('PVE.qemu.SerialEdit', { - url: '/api2/extjs/' + baseurl - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('CloudInit Drive'), - itemId: 'addci', - iconCls: 'pve-itype-icon-cloud', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.CIDriveEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - remove_btn, - edit_btn, - resize_btn, - move_btn, - revert_btn - ], - rows: rows, - sorterFn: sorterFn, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - me.mon(me.rstore, 'refresh', function() { - set_button_status(); - }); - } -}); -Ext.define('PVE.qemu.ScsiHwEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('SCSI Controller Type'), - items: { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - fieldLabel: gettext('Type') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.BiosEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveQemuBiosEdit', - - initComponent : function() { - var me = this; - - var EFIHint = Ext.createWidget({ - xtype: 'displayfield', //submitValue is false, so we don't get submitted - userCls: 'pve-hint', - value: 'You need to add an EFI disk for storing the ' + - 'EFI settings. See the online help for details.', - hidden: true - }); - - Ext.applyIf(me, { - subject: 'BIOS', - items: [ { - xtype: 'pveQemuBiosSelector', - onlineHelp: 'qm_bios_and_uefi', - name: 'bios', - value: '__default__', - fieldLabel: 'BIOS', - listeners: { - 'change' : function(field, newValue) { - if (newValue == 'ovmf') { - Proxmox.Utils.API2Request({ - url : me.url, - method : 'GET', - failure : function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success : function(response, opts) { - var vmConfig = response.result.data; - // there can be only one - if (!vmConfig.efidisk0) { - EFIHint.setVisible(true); - } - } - }); - } else { - if (EFIHint.isVisible()) { - EFIHint.setVisible(false); - } - } - } - } - }, - EFIHint - ] }); - - me.callParent(); - - me.load(); - - } -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.Options', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.Options'], - - onlineHelp: 'qm_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - name: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Name'), - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Name'), - items: { - xtype: 'inputpanel', - items:{ - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - }, - onGetValues: function(values) { - var params = values; - if (values.name === undefined || - values.name === null || - values.name === '') { - params = { 'delete':'name'}; - } - return params; - } - } - } : undefined - }, - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'qm_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.OSTypeEdit' : undefined, - renderer: PVE.Utils.render_kvm_ostype, - defaultValue: 'other' - }, - bootdisk: { - visible: false - }, - boot: { - header: gettext('Boot Order'), - defaultValue: 'cdn', - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.BootOrderEdit' : undefined, - multiKey: ['boot', 'bootdisk'], - renderer: function(order, metaData, record, rowIndex, colIndex, store, pending) { - var i; - var text = ''; - var bootdisk = me.getObjectValue('bootdisk', undefined, pending); - order = order || 'cdn'; - for (i = 0; i < order.length; i++) { - var sel = order.substring(i, i + 1); - if (text) { - text += ', '; - } - if (sel === 'c') { - if (bootdisk) { - text += "Disk '" + bootdisk + "'"; - } else { - text += "Disk"; - } - } else if (sel === 'n') { - text += 'Network'; - } else if (sel === 'a') { - text += 'Floppy'; - } else if (sel === 'd') { - text += 'CD-ROM'; - } else { - text += sel; - } - } - return text; - } - }, - tablet: { - header: gettext('Use tablet for pointer'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use tablet for pointer'), - items: { - xtype: 'proxmoxcheckbox', - name: 'tablet', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - hotplug: { - header: gettext('Hotplug'), - defaultValue: 'disk,network,usb', - renderer: PVE.Utils.render_hotplug_features, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hotplug'), - items: { - xtype: 'pveHotplugFeatureSelector', - name: 'hotplug', - value: '', - multiSelect: true, - fieldLabel: gettext('Hotplug'), - allowBlank: true - } - } : undefined - }, - acpi: { - header: gettext('ACPI support'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('ACPI support'), - items: { - xtype: 'proxmoxcheckbox', - name: 'acpi', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - kvm: { - header: gettext('KVM hardware virtualization'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('KVM hardware virtualization'), - items: { - xtype: 'proxmoxcheckbox', - name: 'kvm', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - freeze: { - header: gettext('Freeze CPU at startup'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.PowerMgmt'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Freeze CPU at startup'), - items: { - xtype: 'proxmoxcheckbox', - name: 'freeze', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Freeze CPU at startup') - } - } : undefined - }, - localtime: { - header: gettext('Use local time for RTC'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use local time for RTC'), - items: { - xtype: 'proxmoxcheckbox', - name: 'localtime', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Use local time for RTC') - } - } : undefined - }, - startdate: { - header: gettext('RTC start date'), - defaultValue: 'now', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('RTC start date'), - items: { - xtype: 'proxmoxtextfield', - name: 'startdate', - deleteEmpty: true, - value: 'now', - fieldLabel: gettext('RTC start date'), - vtype: 'QemuStartDate', - allowBlank: true - } - } : undefined - }, - smbios1: { - header: gettext('SMBIOS settings (type1)'), - defaultValue: '', - renderer: Ext.String.htmlEncode, - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.Smbios1Edit' : undefined - }, - agent: { - header: gettext('Qemu Agent'), - defaultValue: false, - renderer: PVE.Utils.render_qga_features, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Qemu Agent'), - items: { - xtype: 'pveAgentFeatureSelector', - name: 'agent' - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var revert_btn = new Proxmox.button.Button({ - text: gettext('Revert'), - disabled: true, - handler: function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = me.rows[rec.data.key] || {}; - var keys = rowdef.multiKey || [ rec.data.key ]; - var revert = keys.join(','); - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'revert': revert - }, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - } - }); - } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - - var key = rec.data.key; - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var rowdef = rows[key]; - - edit_btn.setDisabled(!rowdef.editor); - revert_btn.setDisabled(!pending); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/qemu/" + vmid + "/pending", - interval: 5000, - cwidth1: 250, - tbar: [ edit_btn, revert_btn ], - rows: rows, - editorConfig: { - url: "/api2/extjs/" + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - me.rstore.on('datachanged', function() { - set_button_status(); - }); - } -}); - -Ext.define('PVE.window.Snapshot', { - extend: 'Ext.window.Window', - - resizable: false, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - defaultFocus: 'field', - - take_snapshot: function(snapname, descr, vmstate) { - var me = this; - var params = { snapname: snapname, vmstate: vmstate ? 1 : 0 }; - if (descr) { - params.description = descr; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot", - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - update_snapshot: function(snapname, descr) { - var me = this; - Proxmox.Utils.API2Request({ - params: { description: descr }, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot/" + - snapname + '/config', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var summarystore = Ext.create('Ext.data.Store', { - model: 'KeyValue', - sorters: [ - { - property : 'key', - direction: 'ASC' - } - ] - }); - - var items = [ - { - xtype: me.snapname ? 'displayfield' : 'textfield', - name: 'snapname', - value: me.snapname, - fieldLabel: gettext('Name'), - vtype: 'ConfigId', - allowBlank: false - } - ]; - - if (me.snapname) { - items.push({ - xtype: 'displayfield', - name: 'snaptime', - renderer: PVE.Utils.render_timestamp_human_readable, - fieldLabel: gettext('Timestamp') - }); - } else { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'vmstate', - uncheckedValue: 0, - defaultValue: 0, - checked: 1, - fieldLabel: gettext('Include RAM') - }); - } - - items.push({ - xtype: 'textareafield', - grow: true, - name: 'description', - fieldLabel: gettext('Description') - }); - - if (me.snapname) { - items.push({ - title: gettext('Settings'), - xtype: 'grid', - height: 200, - store: summarystore, - columns: [ - {header: gettext('Key'), width: 150, dataIndex: 'key'}, - {header: gettext('Value'), flex: 1, dataIndex: 'value'} - ] - }); - } - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - if (me.snapname) { - me.title = gettext('Edit') + ': ' + gettext('Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Update'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.update_snapshot(me.snapname, values.description); - } - } - }); - } else { - me.title ="VM " + me.vmid + ': ' + gettext('Take Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Take Snapshot'), - reference: 'submitbutton', - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.take_snapshot(values.snapname, values.description, values.vmstate); - } - } - }); - } - - Ext.apply(me, { - modal: true, - width: 450, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - if (me.snapname) { - Ext.apply(me, { - width: 620, - height: 420 - }); - } - - me.callParent(); - - if (!me.snapname) { - return; - } - - // else load data - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/snapshot/" + - me.snapname + '/config', - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.close(); - }, - success: function(response, options) { - var data = response.result.data; - var kvarray = []; - Ext.Object.each(data, function(key, value) { - if (key === 'description' || key === 'snaptime') { - return; - } - kvarray.push({ key: key, value: value }); - }); - - summarystore.suspendEvents(); - summarystore.add(kvarray); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh', summarystore); - - form.findField('snaptime').setValue(data.snaptime); - form.findField('description').setValue(data.description); - } - }); - } -}); -Ext.define('PVE.qemu.SnapshotTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveQemuSnapshotTree'], - - load_delay: 3000, - - old_digest: 'invalid', - - stateful: true, - stateId: 'grid-qemu-snapshots', - - sorterFn: function(rec1, rec2) { - var v1 = rec1.data.snaptime; - var v2 = rec2.data.snaptime; - - if (rec1.data.name === 'current') { - return 1; - } - if (rec2.data.name === 'current') { - return -1; - } - - return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); - }, - - reload: function(repeat) { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot', - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - me.load_task.delay(me.load_delay); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var digest = 'invalid'; - var idhash = {}; - var root = { name: '__root', expanded: true, children: [] }; - Ext.Array.each(response.result.data, function(item) { - item.leaf = true; - item.children = []; - if (item.name === 'current') { - digest = item.digest + item.running; - if (item.running) { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running'; - } else { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree'; - } - } else { - item.iconCls = 'fa fa-fw fa-history x-fa-tree'; - } - idhash[item.name] = item; - }); - - if (digest !== me.old_digest) { - me.old_digest = digest; - - Ext.Array.each(response.result.data, function(item) { - if (item.parent && idhash[item.parent]) { - var parent_item = idhash[item.parent]; - parent_item.children.push(item); - parent_item.leaf = false; - parent_item.expanded = true; - parent_item.expandable = false; - } else { - root.children.push(item); - } - }); - - me.setRootNode(root); - } - - me.load_task.delay(me.load_delay); - } - }); - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/feature', - params: { feature: 'snapshot' }, - method: 'GET', - success: function(response, options) { - var res = response.result.data; - if (res.hasFeature) { - var snpBtns = Ext.ComponentQuery.query('#snapshotBtn'); - snpBtns.forEach(function(item){ - item.enable(); - }); - } - } - }); - - - }, - - listeners: { - beforestatesave: function(grid, state, eopts) { - // extjs cannot serialize functions, - // so a the sorter with only the sorterFn will - // not be a valid sorter when restoring the state - delete state.storeState.sorters; - } - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.vmid = me.pveSelNode.data.vmid; - if (!me.vmid) { - throw "no VM ID specified"; - } - - me.load_task = new Ext.util.DelayedTask(me.reload, me); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var valid_snapshot = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current'; - }; - - var valid_snapshot_rollback = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current' && !record.data.snapstate; - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (valid_snapshot(rec)) { - var win = Ext.create('PVE.window.Snapshot', { - snapname: rec.data.name, - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - me.mon(win, 'close', me.reload, me); - } - }; - - var editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot, - handler: run_editor - }); - - var rollbackBtn = new Proxmox.button.Button({ - text: gettext('Rollback'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot_rollback, - confirmMsg: function(rec) { - return Proxmox.Utils.format_task_description('qmrollback', me.vmid) + - " '" + rec.data.name + "'"; - }, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname + '/rollback', - method: 'POST', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var removeBtn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.name + "'"); - return msg; - }, - enableFn: valid_snapshot, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var snapshotBtn = Ext.create('Ext.Button', { - itemId: 'snapshotBtn', - text: gettext('Take Snapshot'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.window.Snapshot', { - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - } - }); - - Ext.apply(me, { - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - selModel: sm, - tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ], - fields: [ - 'name', 'description', 'snapstate', 'vmstate', 'running', - { name: 'snaptime', type: 'date', dateFormat: 'timestamp' } - ], - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - width: 200, - renderer: function(value, metaData, record) { - if (value === 'current') { - return "NOW"; - } else { - return value; - } - } - }, - { - text: gettext('RAM'), - align: 'center', - resizable: false, - dataIndex: 'vmstate', - width: 50, - renderer: function(value, metaData, record) { - if (record.data.name !== 'current') { - return Proxmox.Utils.format_boolean(value); - } - } - }, - { - text: gettext('Date') + "/" + gettext("Status"), - dataIndex: 'snaptime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.snapstate) { - return record.data.snapstate; - } - if (value) { - return Ext.Date.format(value,'Y-m-d H:i:s'); - } - } - }, - { - text: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value, metaData, record) { - if (record.data.name === 'current') { - return gettext("You are here!"); - } else { - return Ext.String.htmlEncode(value); - } - } - } - ], - columnLines: true, // will work in 4.1? - listeners: { - activate: me.reload, - destroy: me.load_task.cancel, - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.store.sorters.add(new Ext.util.Sorter({ - sorterFn: me.sorterFn - })); - } -}); - -Ext.define('PVE.qemu.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.qemu.Config', - - onlineHelp: 'chapter_virtual_machines', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!me.pveSelNode.data.template; - - var running = !!me.pveSelNode.data.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + "/qemu/" + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + '/status/' + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var resumeBtn = Ext.create('Ext.Button', { - text: gettext('Resume'), - disabled: !caps.vms['VM.PowerMgmt'], - hidden: true, - handler: function() { - vm_command('resume'); - }, - iconCls: 'fa fa-play' - }); - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('qmtemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = me.pveSelNode.data.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - itemId: 'removeBtn', - disabled: !caps.vms['VM.Allocate'], - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'VM', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('qmshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items: [{ - text: gettext('Pause'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmpause', vmid), - handler: function() { - vm_command("suspend"); - }, - iconCls: 'fa fa-pause' - },{ - text: gettext('Hibernate'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmsuspend', vmid), - tooltip: gettext('Suspend to disk'), - handler: function() { - vm_command("suspend", { todisk: 1 }); - }, - iconCls: 'fa fa-download' - },{ - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - dangerous: true, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - confirmMsg: Proxmox.Utils.format_task_description('qmstop', vmid), - handler: function() { - vm_command("stop", { timeout: 30 }); - }, - iconCls: 'fa fa-stop' - },{ - text: gettext('Reset'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmreset', vmid), - handler: function() { - vm_command("reset"); - }, - iconCls: 'fa fa-bolt' - }] - }, - iconCls: 'fa fa-power-off' - }); - - var vm = me.pveSelNode.data; - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - hidden: template, - consoleType: 'kvm', - consoleName: vm.name, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - Ext.apply(me, { - title: Ext.String.format(gettext("Virtual Machine {0} on node '{1}'"), vm.text, nodename), - hstateid: 'kvmtab', - tbarSpacing: false, - tbar: [ statusTxt, '->', resumeBtn, startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveQemuSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push({ - title: gettext('Console'), - itemId: 'console', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'kvm', - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Hardware'), - itemId: 'hardware', - iconCls: 'fa fa-desktop', - xtype: 'PVE.qemu.HardwareView' - }, - { - title: 'Cloud-Init', - itemId: 'cloudinit', - iconCls: 'fa fa-cloud', - xtype: 'pveCiPanel' - }, - { - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options', - xtype: 'PVE.qemu.Options' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - xtype: 'proxmoxNodeTasks', - iconCls: 'fa fa-list', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Monitor'] && !template) { - me.items.push({ - title: gettext('Monitor'), - iconCls: 'fa fa-eye', - itemId: 'monitor', - xtype: 'pveQemuMonitor' - }); - } - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - xtype: 'pveQemuSnapshotTree', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var qmpstatus; - var spice = false; - var xtermjs = false; - var lock; - - if (!success) { - status = qmpstatus = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('qmpstatus'); - qmpstatus = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - - spice = s.data.get('spice') ? true : false; - xtermjs = s.data.get('serial') ? true : false; - - } - - if (template) { - return; - } - - var resume = (['prelaunch', 'paused', 'suspended'].indexOf(qmpstatus) !== -1); - - if (resume || lock === 'suspended') { - startBtn.setVisible(false); - resumeBtn.setVisible(true); - } else { - startBtn.setVisible(true); - resumeBtn.setVisible(false); - } - - consoleBtn.setEnableSpice(spice); - consoleBtn.setEnableXtermJS(xtermjs); - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.CreateWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveQemuCreateWizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - current: { - scsihw: '' - } - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('Virtual Machine'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'qm_general_settings', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', - guestType: 'qemu', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ], - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - ], - advancedColumn2: [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - labelWidth: 120, - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Shutdown timeout') - } - ], - onGetValues: function(values) { - - ['name', 'pool', 'onboot', 'agent'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - - var res = PVE.Parser.printStartup({ - order: values.order, - up: values.up, - down: values.down - }); - - if (res) { - values.startup = res; - } - - delete values.order; - delete values.up; - delete values.down; - - return values; - } - }, - { - xtype: 'container', - layout: 'hbox', - defaults: { - flex: 1, - padding: '0 10' - }, - title: gettext('OS'), - items: [ - { - xtype: 'pveQemuCDInputPanel', - bind: { - nodename: '{nodename}' - }, - confid: 'ide2', - insideWizard: true - }, - { - xtype: 'pveQemuOSTypePanel', - insideWizard: true - } - ] - }, - { - xtype: 'pveQemuSystemPanel', - title: gettext('System'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuHDInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Hard Disk'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuProcessorPanel', - insideWizard: true, - title: gettext('CPU') - }, - { - xtype: 'pveQemuMemoryPanel', - insideWizard: true, - title: gettext('Memory') - }, - { - xtype: 'pveQemuNetworkInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Network'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var kv = this.up('window').getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete') { // ignore - return; - } - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response){ - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - - -Ext.define('PVE.qemu.USBInputPanel', { - extend: 'Proxmox.panel.InputPanel', - mixins: ['Proxmox.Mixin.CBind' ], - - autoComplete: false, - onlineHelp: 'qm_usb_passthrough', - - controller: { - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=usb]': { - change: function(field, newValue, oldValue) { - var hwidfield = this.lookupReference('hwid'); - var portfield = this.lookupReference('port'); - var usb3field = this.lookupReference('usb3'); - if (field.inputValue === 'hostdevice') { - hwidfield.setDisabled(!newValue); - } else if(field.inputValue === 'port') { - portfield.setDisabled(!newValue); - } else if(field.inputValue === 'spice') { - usb3field.setDisabled(newValue); - } - } - }, - 'pveUSBSelector': { - change: function(field, newValue, oldValue) { - var usbval = field.getUSBValue(); - var usb3field = this.lookupReference('usb3'); - var usb3 = /usb3/.test(usbval); - if(usb3 && !usb3field.isDisabled()) { - usb3field.savedVal = usb3field.getValue(); - usb3field.setValue(true); - usb3field.setDisabled(true); - } else if(!usb3 && usb3field.isDisabled()){ - var val = (usb3field.savedVal === undefined)?usb3field.originalValue:usb3field.savedVal; - usb3field.setValue(val); - usb3field.setDisabled(false); - } - } - } - } - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - }, - - onGetValues: function(values) { - var me = this; - if(!me.confid) { - var i; - for (i = 0; i < 6; i++) { - if (!me.vmconfig['usb' + i.toString()]) { - me.confid = 'usb' + i.toString(); - break; - } - } - } - var val = ""; - var type = me.down('radiofield').getGroupValue(); - switch (type) { - case 'spice': - val = 'spice'; break; - case 'hostdevice': - case 'port': - val = me.down('pveUSBSelector[name=' + type + ']').getUSBValue(); - if (!/usb3/.test(val) && me.down('field[name=usb3]').getValue() === true) { - val += ',usb3=1'; - } - break; - default: - throw "invalid type selected"; - } - - values[me.confid] = val; - return values; - }, - - items: [ - { - xtype: 'fieldcontainer', - defaultType: 'radiofield', - items:[ - { - name: 'usb', - inputValue: 'spice', - boxLabel: gettext('Spice Port'), - submitValue: false, - checked: true - }, - { - name: 'usb', - inputValue: 'hostdevice', - boxLabel: gettext('Use USB Vendor/Device ID'), - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - type: 'device', - name: 'hostdevice', - cbind: { pveSelNode: '{pveSelNode}' }, - editable: true, - reference: 'hwid', - allowBlank: false, - fieldLabel: 'Choose Device', - labelAlign: 'right', - submitValue: false - }, - { - name: 'usb', - inputValue: 'port', - boxLabel: gettext('Use USB Port'), - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - name: 'port', - cbind: { pveSelNode: '{pveSelNode}' }, - editable: true, - type: 'port', - reference: 'port', - allowBlank: false, - fieldLabel: gettext('Choose Port'), - labelAlign: 'right', - submitValue: false - }, - { - xtype: 'checkbox', - name: 'usb3', - submitValue: false, - reference: 'usb3', - fieldLabel: gettext('Use USB3') - } - ] - } - ] -}); - -Ext.define('PVE.qemu.USBEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('USB Device'), - - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.USBInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var data = response.result.data[me.confid].split(','); - var port, hostdevice, usb3 = false; - var type = 'spice'; - var i; - for (i = 0; i < data.length; i++) { - if (/^(host=)?(0x)?[a-zA-Z0-9]{4}\:(0x)?[a-zA-Z0-9]{4}$/.test(data[i])) { - hostdevice = data[i]; - hostdevice = hostdevice.replace('host=', '').replace('0x',''); - type = 'hostdevice'; - } else if (/^(host=)?(\d+)\-(\d+(\.\d+)*)$/.test(data[i])) { - port = data[i]; - port = port.replace('host=',''); - type = 'port'; - } - - if (/^usb3=(1|on|true)$/.test(data[i])) { - usb3 = true; - } - } - var values = { - usb : type, - hostdevice: hostdevice, - port: port, - usb3: usb3 - }; - - ipanel.setValues(values); - } - } - }); - } -}); -Ext.define('PVE.qemu.PCIInputPanel', { - extend: 'Proxmox.panel.InputPanel', - - onlineHelp: 'qm_pci_passthrough', - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - - var hostpci = me.vmconfig[me.confid] || ''; - - var values = PVE.Parser.parsePropertyString(hostpci, 'host'); - if (values.host && values.host.length < 6) { // 00:00 format not 00:00.0 - values.host += ".0"; - values.multifunction = true; - } - values['x-vga'] = PVE.Parser.parseBoolean(values['x-vga'], 0); - values.pcie = PVE.Parser.parseBoolean(values.pcie, 0); - values.rombar = PVE.Parser.parseBoolean(values.rombar, 1); - - me.setValues(values); - if (!me.vmconfig.machine || me.vmconfig.machine.indexOf('q35') === -1) { - // machine is not set to some variant of q35, so we disable pcie - var pcie = me.down('field[name=pcie]'); - pcie.setDisabled(true); - pcie.setBoxLabel(gettext('Q35 only')); - } - - if (values.romfile) { - me.down('field[name=romfile]').setVisible(true); - } - }, - - onGetValues: function(values) { - var me = this; - var ret = {}; - if(!me.confid) { - var i; - for (i = 0; i < 5; i++) { - if (!me.vmconfig['hostpci' + i.toString()]) { - me.confid = 'hostpci' + i.toString(); - break; - } - } - } - if (values.multifunction) { - // modify host to skip the '.X' - values.host = values.host.substring(0,5); - delete values.multifunction; - } - - if (values.rombar) { - delete values.rombar; - } else { - values.rombar = 0; - } - - if (!values.romfile) { - delete values.romfile; - } - - ret[me.confid] = PVE.Parser.printPropertyString(values, 'host'); - return ret; - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.column1 = [ - { - xtype: 'pvePCISelector', - fieldLabel: gettext('Device'), - name: 'host', - nodename: me.nodename, - allowBlank: false, - onLoadCallBack: function(store, records, success) { - if (!success || !records.length) { - return; - } - - var first = records[0]; - if (first.data.iommugroup === -1) { - // no iommu groups - var warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - value: 'No IOMMU detected, please activate it.' + - 'See Documentation for further information.', - userCls: 'pve-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } - }, - listeners: { - change: function(pcisel, value) { - if (!value) { - return; - } - var pcidev = pcisel.getStore().getById(value); - var mdevfield = me.down('field[name=mdev]'); - mdevfield.setDisabled(!pcidev || !pcidev.data.mdev); - if (!pcidev) { - return; - } - var id = pcidev.data.id.substring(0,5); // 00:00 - var iommu = pcidev.data.iommugroup; - // try to find out if there are more devices - // in that iommu group - if (iommu !== -1) { - var count = 0; - pcisel.getStore().each(function(record) { - if (record.data.iommugroup === iommu && - record.data.id.substring(0,5) !== id) - { - count++; - return false; - } - }); - var warning = me.down('#iommuwarning'); - if (count && !warning) { - warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - itemId: 'iommuwarning', - value: 'The selected Device is not in a seperate' + - 'IOMMU group, make sure this is intended.', - userCls: 'pve-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } else if (!count && warning) { - me.remove(warning); - } - } - if (pcidev.data.mdev) { - mdevfield.setPciID(value); - } - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('All Functions'), - name: 'multifunction' - } - ]; - - me.column2 = [ - { - xtype: 'pveMDevSelector', - name: 'mdev', - disabled: true, - fieldLabel: gettext('MDev Type'), - nodename: me.nodename, - listeners: { - change: function(field, value) { - var mf = me.down('field[name=multifunction]'); - if (!!value) { - mf.setValue(false); - } - mf.setDisabled(!!value); - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Primary GPU'), - name: 'x-vga' - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'ROM-Bar', - name: 'rombar' - }, - { - xtype: 'displayfield', - submitValue: true, - hidden: true, - fieldLabel: 'ROM-File', - name: 'romfile' - } - ]; - - me.advancedColumn2 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'PCI-Express', - name: 'pcie' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.PCIEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('PCI Device'), - - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.PCIInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.SerialnputPanel', { - extend: 'Proxmox.panel.InputPanel', - - autoComplete: false, - - setVMConfig: function(vmconfig) { - var me = this, i; - me.vmconfig = vmconfig; - - for (i = 0; i < 4; i++) { - var port = 'serial' + i.toString(); - if (!me.vmconfig[port]) { - me.down('field[name=serialid]').setValue(i); - break; - } - } - - }, - - onGetValues: function(values) { - var me = this; - - var id = 'serial' + values.serialid; - delete values.serialid; - values[id] = 'socket'; - return values; - }, - - items: [ - { - xtype: 'proxmoxintegerfield', - name: 'serialid', - fieldLabel: gettext('Serial Port'), - minValue: 0, - maxValue: 3, - allowBlank: false, - validator: function(id) { - if (!this.rendered) { - return true; - } - var me = this.up('panel'); - if (me.vmconfig !== undefined && Ext.isDefined(me.vmconfig['serial' + id])) { - return "This device is already in use."; - } - return true; - } - } - ] -}); - -Ext.define('PVE.qemu.SerialEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('Serial Port'), - - initComponent : function() { - var me = this; - - // for now create of (socket) serial port only - me.isCreate = true; - - var ipanel = Ext.create('PVE.qemu.SerialnputPanel', {}); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.window.IPInfo', { - extend: 'Ext.window.Window', - width: 600, - title: gettext('Guest Agent Network Information'), - height: 300, - layout: { - type: 'fit' - }, - modal: true, - items: [ - { - xtype: 'grid', - emptyText: gettext('No network information'), - columns: [ - { - dataIndex: 'name', - text: gettext('Name'), - flex: 3 - }, - { - dataIndex: 'hardware-address', - text: gettext('MAC address'), - width: 140 - }, - { - dataIndex: 'ip-addresses', - text: gettext('IP address'), - align: 'right', - flex: 4, - renderer: function(val) { - if (!Ext.isArray(val)) { - return ''; - } - var ips = []; - val.forEach(function(ip) { - var addr = ip['ip-address']; - var pref = ip.prefix; - if (addr && pref) { - ips.push(addr + '/' + pref); - } - }); - return ips.join('
'); - } - } - ] - } - ] -}); - -Ext.define('PVE.qemu.AgentIPView', { - extend: 'Ext.container.Container', - xtype: 'pveAgentIPView', - - layout: { - type: 'hbox', - align: 'top' - }, - - nics: [], - - items: [ - { - xtype: 'box', - html: ' IPs' - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'right', - pack: 'end' - }, - items: [ - { - xtype: 'label', - flex: 1, - itemId: 'ipBox', - style: { - 'text-align': 'right' - } - }, - { - xtype: 'button', - itemId: 'moreBtn', - hidden: true, - ui: 'default-toolbar', - handler: function(btn) { - var me = this.up('pveAgentIPView'); - - var win = Ext.create('PVE.window.IPInfo'); - win.down('grid').getStore().setData(me.nics); - win.show(); - }, - text: gettext('More') - } - ] - } - ], - - getDefaultIps: function(nics) { - var me = this; - var ips = []; - nics.forEach(function(nic) { - if (nic['hardware-address'] && - nic['hardware-address'] != '00:00:00:00:00:00') { - - var nic_ips = nic['ip-addresses'] || []; - nic_ips.forEach(function(ip) { - var p = ip['ip-address']; - // show 2 ips at maximum - if (ips.length < 2) { - ips.push(p); - } - }); - } - }); - - return ips; - }, - - startIPStore: function(store, records, success) { - var me = this; - var agentRec = store.getById('agent'); - /*jslint confusion: true*/ - /* value is number and string */ - me.agent = (agentRec && agentRec.data.value === 1); - me.running = (store.getById('status').data.value === 'running'); - /*jslint confusion: false*/ - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!caps.vms['VM.Monitor']) { - var errorText = gettext("Requires '{0}' Privileges"); - me.updateStatus(false, Ext.String.format(errorText, 'VM.Monitor')); - return; - } - - if (me.agent && me.running && me.ipStore.isStopped) { - me.ipStore.startUpdate(); - } else if (me.ipStore.isStopped) { - me.updateStatus(); - } - }, - - updateStatus: function(unsuccessful, defaulttext) { - var me = this; - var text = defaulttext || gettext('No network information'); - var more = false; - if (unsuccessful) { - text = gettext('Guest Agent not running'); - } else if (me.agent && me.running) { - if (Ext.isArray(me.nics) && me.nics.length) { - more = true; - var ips = me.getDefaultIps(me.nics); - if (ips.length !== 0) { - text = ips.join('
'); - } - } else if (me.nics && me.nics.error) { - var msg = gettext('Cannot get info from Guest Agent
Error: {0}'); - text = Ext.String.format(text, me.nics.error.desc); - } - } else if (me.agent) { - text = gettext('Guest Agent not running'); - } else { - text = gettext('No Guest Agent configured'); - } - - var ipBox = me.down('#ipBox'); - ipBox.update(text); - - var moreBtn = me.down('#moreBtn'); - moreBtn.setVisible(more); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw 'rstore not given'; - } - - if (!me.pveSelNode) { - throw 'pveSelNode not given'; - } - - var nodename = me.pveSelNode.data.node; - var vmid = me.pveSelNode.data.vmid; - - me.ipStore = Ext.create('Proxmox.data.UpdateStore', { - interval: 10000, - storeid: 'pve-qemu-agent-' + vmid, - method: 'POST', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/qemu/' + vmid + '/agent/network-get-interfaces' - } - }); - - me.callParent(); - - me.mon(me.ipStore, 'load', function(store, records, success) { - if (records && records.length) { - me.nics = records[0].data.result; - } else { - me.nics = undefined; - } - me.updateStatus(!success); - }); - - me.on('destroy', me.ipStore.stopUpdate); - - // if we already have info about the vm, use it immediately - if (me.rstore.getCount()) { - me.startIPStore(me.rstore, me.rstore.getData(), false); - } - - // check if the guest agent is there on every statusstore load - me.mon(me.rstore, 'load', me.startIPStore, me); - } -}); -Ext.define('PVE.qemu.CloudInit', { - extend: 'Proxmox.grid.PendingObjectGrid', - xtype: 'pveCiPanel', - - onlineHelp: 'qm_cloud_init', - - tbar: [ - { - xtype: 'proxmoxButton', - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var me = this.up('grid'); - var warn = gettext('Are you sure you want to remove entry {0}'); - - var entry = rec.data.key; - var msg = Ext.String.format(warn, "'" - + me.renderKey(entry, {}, rec) + "'"); - - return msg; - }, - enableFn: function(record) { - var me = this.up('grid'); - var caps = Ext.state.Manager.get('GuiCap'); - if (me.rows[record.data.key].never_delete || - !caps.vms['VM.Config.Network']) { - return false; - } - - if (record.data.key === 'cipassword' && !record.data.value) { - return false; - } - return true; - }, - handler: function() { - var me = this.up('grid'); - var records = me.getSelection(); - if (!records || !records.length) { - return; - } - - var id = records[0].data.key; - var match = id.match(/^net(\d+)$/); - if (match) { - id = 'ipconfig' + match[1]; - } - - var params = {}; - params['delete'] = id; - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: params, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - callback: function() { - me.reload(); - } - }); - }, - text: gettext('Remove') - }, - { - xtype: 'proxmoxButton', - disabled: true, - handler: function() { - var me = this.up('grid'); - me.run_editor(); - }, - text: gettext('Edit') - }, - '-', - { - xtype: 'button', - itemId: 'savebtn', - text: gettext('Regenerate Image'), - handler: function() { - var me = this.up('grid'); - var eject_params = {}; - var insert_params = {}; - var disk = PVE.Parser.parseQemuDrive(me.ciDriveId, me.ciDrive); - var storage = ''; - var stormatch = disk.file.match(/^([^\:]+)\:/); - if (stormatch) { - storage = stormatch[1]; - } - eject_params[me.ciDriveId] = 'none,media=cdrom'; - insert_params[me.ciDriveId] = storage + ':cloudinit'; - - var failure = function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }; - - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: eject_params, - failure: failure, - callback: function() { - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: insert_params, - failure: failure, - callback: function() { - me.reload(); - } - }); - } - }); - } - } - ], - - border: false, - - set_button_status: function(rstore, records, success) { - if (!success || records.length < 1) { - return; - } - var me = this; - var found; - records.forEach(function(record) { - if (found) { - return; - } - var id = record.data.key; - var value = record.data.value; - var ciregex = new RegExp("vm-" + me.pveSelNode.data.vmid + "-cloudinit"); - if (id.match(/^(ide|scsi|sata)\d+$/) && ciregex.test(value)) { - found = id; - me.ciDriveId = found; - me.ciDrive = value; - } - }); - - me.down('#savebtn').setDisabled(!found); - me.setDisabled(!found); - if (!found) { - me.getView().mask(gettext('No CloudInit Drive found'), ['pve-static-mask']); - } else { - me.getView().unmask(); - } - }, - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - - var icon = ""; - if (rowdef.iconCls) { - icon = ' '; - } - return icon + (rowdef.header || key); - }, - - listeners: { - activate: function () { - var me = this; - me.rstore.startUpdate(); - }, - itemdblclick: function() { - var me = this; - me.run_editor(); - } - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - var caps = Ext.state.Manager.get('GuiCap'); - me.baseurl = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid; - me.url = me.baseurl + '/pending'; - me.editorConfig.url = me.baseurl + '/config'; - me.editorConfig.pveSelNode = me.pveSelNode; - - /*jslint confusion: true*/ - /* editor is string and object */ - me.rows = { - ciuser: { - header: gettext('User'), - iconCls: 'fa fa-user', - never_delete: true, - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('User'), - items: [ - { - xtype: 'proxmoxtextfield', - deleteEmpty: true, - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('User'), - name: 'ciuser' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.defaultText; - } - }, - cipassword: { - header: gettext('Password'), - iconCls: 'fa fa-unlock', - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Password'), - items: [ - { - xtype: 'proxmoxtextfield', - inputType: 'password', - deleteEmpty: true, - emptyText: Proxmox.Utils.noneText, - fieldLabel: gettext('Password'), - name: 'cipassword' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.noneText; - } - }, - searchdomain: { - header: gettext('DNS domain'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - nameserver: { - header: gettext('DNS servers'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - sshkeys: { - header: gettext('SSH public key'), - iconCls: 'fa fa-key', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.SSHKeyEdit' : undefined, - never_delete: true, - renderer: function(value) { - value = decodeURIComponent(value); - var keys = value.split('\n'); - var text = []; - keys.forEach(function(key) { - if (key.length) { - // First erase all quoted strings (eg. command="foo" - var v = key.replace(/"(?:\\.|[^"\\])*"/g, ''); - // Now try to detect the comment: - var res = v.match(/^\s*(\S+\s+)?(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)\s+\S+\s+(.*?)\s*$/, ''); - if (res) { - key = Ext.String.htmlEncode(res[2]); - if (res[1]) { - key += ' (' + gettext('with options') + ')'; - } - text.push(key); - return; - } - // Most likely invalid at this point, so just stick to - // the old value. - text.push(Ext.String.htmlEncode(key)); - } - }); - if (text.length) { - return text.join('
'); - } else { - return Proxmox.Utils.noneText; - } - }, - defaultValue: '' - } - }; - var i; - var ipconfig_renderer = function(value, md, record, ri, ci, store, pending) { - var id = record.data.key; - var match = id.match(/^net(\d+)$/); - var val = ''; - if (match) { - val = me.getObjectValue('ipconfig'+match[1], '', pending); - } - return val; - }; - for (i = 0; i < 32; i++) { - // we want to show an entry for every network device - // even if it is empty - me.rows['net' + i.toString()] = { - multiKey: ['ipconfig' + i.toString(), 'net' + i.toString()], - header: gettext('IP Config') + ' (net' + i.toString() +')', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.IPConfigEdit' : undefined, - iconCls: 'fa fa-exchange', - renderer: ipconfig_renderer - }; - me.rows['ipconfig' + i.toString()] = { - visible: false - }; - } - /*jslint confusion: false*/ - - PVE.Utils.forEachBus(['ide', 'scsi', 'sata'], function(type, id) { - me.rows[type+id] = { - visible: false - }; - }); - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - } -}); -Ext.define('PVE.qemu.CIDriveInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveCIDriveInputPanel', - - insideWizard: false, - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var drive = {}; - var params = {}; - drive.file = values.hdstorage + ":cloudinit"; - drive.format = values.diskformat; - params[values.controller + values.deviceid] = PVE.Parser.printQemuDrive(drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - setVMConfig: function(config) { - var me = this; - me.down('#drive').setVMConfig(config, 'cdrom'); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items = [ - { - xtype: 'pveControllerSelector', - noVirtIO: true, - itemId: 'drive', - fieldLabel: gettext('CloudInit Drive'), - name: 'drive' - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'storselector', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - } - ]; - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CIDriveEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCIDriveEdit', - - isCreate: true, - subject: gettext('CloudInit Drive'), - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveCIDriveInputPanel', - itemId: 'cipanel', - nodename: nodename - }]; - - me.callParent(); - - me.load({ - success: function(response, opts) { - me.down('#cipanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.SSHKeyInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSSHKeyInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - if (values.sshkeys) { - values.sshkeys.trim(); - } - if (!values.sshkeys.length) { - values = {}; - values['delete'] = 'sshkeys'; - return values; - } else { - values.sshkeys = encodeURIComponent(values.sshkeys); - } - return values; - }, - - items: [ - { - xtype: 'textarea', - itemId: 'sshkeys', - name: 'sshkeys', - height: 250 - }, - { - xtype: 'filebutton', - itemId: 'filebutton', - name: 'file', - text: gettext('Load SSH Key File'), - fieldLabel: 'test', - listeners: { - change: function(btn, e, value) { - var me = this.up('inputpanel'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - var keysField = me.down('#sshkeys'); - var old = keysField.getValue(); - keysField.setValue(old + res); - }); - }); - btn.reset(); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - if (!window.FileReader) { - me.down('#filebutton').setVisible(false); - } - - } -}); - -Ext.define('PVE.qemu.SSHKeyEdit', { - extend: 'Proxmox.window.Edit', - - width: 800, - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.SSHKeyInputPanel'); - - Ext.apply(me, { - subject: gettext('SSH Keys'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.create) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.sshkeys) { - data.sshkeys = decodeURIComponent(data.sshkeys); - ipanel.setValues(data); - } - } - }); - } - } -}); -Ext.define('PVE.qemu.IPConfigPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveIPConfigPanel', - - insideWizard: false, - - vmconfig: {}, - - onGetValues: function(values) { - var me = this; - - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - - var params = {}; - - var cfg = PVE.Parser.printIPConfig(values); - if (cfg === '') { - params['delete'] = [me.confid]; - } else { - params[me.confid] = cfg; - } - return params; - }, - - setVMConfig: function(config) { - var me = this; - me.vmconfig = config; - }, - - setIPConfig: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data.ip === 'dhcp') { - data.ipv4mode = data.ip; - data.ip = ''; - } else { - data.ipv4mode = 'static'; - } - if (data.ip6 === 'dhcp' || data.ip6 === 'auto') { - data.ipv6mode = data.ip6; - data.ip6 = ''; - } else { - data.ipv6mode = 'static'; - } - - me.ipconfig = data; - me.setValues(me.ipconfig); - }, - - initComponent : function() { - var me = this; - - me.ipconfig = {}; - - me.column1 = [ - { - xtype: 'displayfield', - fieldLabel: gettext('Network Device'), - value: me.netid - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv4') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv4mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: '', - disabled: true, - fieldLabel: gettext('IPv4/CIDR') - }, - { - xtype: 'textfield', - name: 'gw', - value: '', - vtype: 'IPAddress', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv4') +')' - } - ]; - - me.column2 = [ - { - xtype: 'displayfield' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv6') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv6mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: '', - vtype: 'IP6CIDRAddress', - disabled: true, - fieldLabel: gettext('IPv6/CIDR') - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: '', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv6') +')' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.IPConfigEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - // convert confid from netX to ipconfigX - var match = me.confid.match(/^net(\d+)$/); - if (match) { - me.netid = me.confid; - me.confid = 'ipconfig' + match[1]; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.IPConfigPanel', { - confid: me.confid, - netid: me.netid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: gettext('Network Config'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - me.vmconfig = response.result.data; - var ipconfig = {}; - var value = me.vmconfig[me.confid]; - if (value) { - ipconfig = PVE.Parser.parseIPConfig(me.confid, value); - if (!ipconfig) { - Ext.Msg.alert(gettext('Error'), gettext('Unable to parse network configuration')); - me.close(); - return; - } - } - ipanel.setIPConfig(me.confid, ipconfig); - ipanel.setVMConfig(me.vmconfig); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.SystemInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSystemPanel', - - onlineHelp: 'qm_system_settings', - - viewModel: { - data: { - efi: false, - addefi: true - }, - - formulas: { - efidisk: function(get) { - return get('efi') && get('addefi'); - } - } - }, - - onGetValues: function(values) { - if (values.vga && values.vga.substr(0,6) === 'serial') { - values['serial' + values.vga.substr(6,1)] = 'socket'; - } - - var efidrive = {}; - if (values.hdimage) { - efidrive.file = values.hdimage; - } else if (values.hdstorage) { - efidrive.file = values.hdstorage + ":1"; - } - - if (values.diskformat) { - efidrive.format = values.diskformat; - } - - delete values.hdimage; - delete values.hdstorage; - delete values.diskformat; - - if (efidrive.file) { - values.efidisk0 = PVE.Parser.printQemuDrive(efidrive); - } - - return values; - }, - - controller: { - xclass: 'Ext.app.ViewController', - - scsihwChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('current.scsihw', value); - } - }, - - biosChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('efi', value === 'ovmf'); - } - }, - - control: { - 'pveScsiHwSelector': { - change: 'scsihwChange' - }, - 'pveQemuBiosSelector': { - change: 'biosChange' - } - } - }, - - column1: [ - { - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - name: 'vga', - comboItems: PVE.Utils.kvm_vga_driver_array() - }, - { - xtype: 'proxmoxcheckbox', - name: 'agent', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Qemu Agent') - } - ], - - column2: [ - { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - bind: { - value: '{current.scsihw}' - }, - fieldLabel: gettext('SCSI Controller') - } - ], - - advancedColumn1: [ - { - xtype: 'pveQemuBiosSelector', - name: 'bios', - value: '__default__', - fieldLabel: 'BIOS' - }, - { - xtype: 'proxmoxcheckbox', - bind: { - value: '{addefi}', - hidden: '{!efi}', - disabled: '{!efi}' - }, - hidden: true, - submitValue: false, - disabled: true, - fieldLabel: gettext('Add EFI Disk') - }, - { - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - bind: { - nodename: '{nodename}', - hidden: '{!efi}', - disabled: '{!efidisk}' - }, - autoSelect: false, - disabled: true, - hidden: true, - hideSize: true - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - } - ] - -}); -Ext.define('PVE.lxc.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveLxcSummary', - - scrollable: true, - bodyPadding: 5, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - if (!me.workspace) { - throw "no workspace specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var template = !!me.pveSelNode.data.template; - var rstore = me.statusStore; - - var width = template ? 1 : 0.5; - var items = [ - { - xtype: template ? 'pveTemplateStatusView' : 'pveGuestStatusView', - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - }, - itemId: 'gueststatus', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'pveNotesView', - maxHeight: 320, - itemId: 'notesview', - pveSelNode: me.pveSelNode, - responsiveConfig: { - 'width < 1900': { - columnWidth: width - }, - 'width >= 1900': { - columnWidth: width / 2 - } - } - } - ]; - - var rrdstore; - if (!template) { - - rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/rrddata", - model: 'pve-rrd-guest' - }); - - items.push( - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - pveSelNode: me.pveSelNode, - fields: ['cpu'], - fieldTitles: [gettext('CPU usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - pveSelNode: me.pveSelNode, - fields: ['maxmem', 'mem'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - pveSelNode: me.pveSelNode, - fields: ['netin','netout'], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Disk IO'), - pveSelNode: me.pveSelNode, - fields: ['diskread','diskwrite'], - store: rrdstore - } - ); - - } - - Ext.apply(me, { - tbar: [ '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - layout: { - type: 'column' - }, - defaults: { - minHeight: 320, - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - items: items - } - ] - }); - - me.callParent(); - if (!template) { - rrdstore.startUpdate(); - me.on('destroy', rrdstore.stopUpdate); - } - } -}); -Ext.define('PVE.lxc.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcNetworkInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_network', - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - var bridgesel = me.query("[isFormField][name=bridge]")[0]; - bridgesel.setNodename(nodename); - }, - - onGetValues: function(values) { - var me = this; - - var id; - if (me.isCreate) { - id = values.id; - delete values.id; - } else { - id = me.ifname; - } - - if (!id) { - return {}; - } - - var newdata = {}; - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - newdata[id] = PVE.Parser.printLxcNetwork(values); - return newdata; - }, - - initComponent : function() { - var me = this; - - var cdata = {}; - - if (me.insideWizard) { - me.ifname = 'net0'; - cdata.name = 'eth0'; - me.dataCache = {}; - } - cdata.firewall = (me.insideWizard || me.isCreate); - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.isCreate) { - if (!me.ifname) { - throw "no interface name specified"; - } - if (!me.dataCache[me.ifname]) { - throw "no such interface '" + me.ifname + "'"; - } - - cdata = PVE.Parser.parseLxcNetwork(me.dataCache[me.ifname]); - } - - var i; - for (i = 0; i < 10; i++) { - if (me.isCreate && !me.dataCache['net'+i.toString()]) { - me.ifname = 'net' + i.toString(); - break; - } - } - - var idselector = { - xtype: 'hidden', - name: 'id', - value: me.ifname - }; - - me.column1 = [ - idselector, - { - xtype: 'textfield', - name: 'name', - fieldLabel: gettext('Name'), - emptyText: '(e.g., eth0)', - allowBlank: false, - value: cdata.name, - validator: function(value) { - var result = ''; - Ext.Object.each(me.dataCache, function(key, netstr) { - if (!key.match(/^net\d+/) || key === me.ifname) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(netstr); - if (net.name === value) { - result = "interface name already in use"; - return false; - } - }); - if (result !== '') { - return result; - } - // validator can return bool/string - /*jslint confusion:true*/ - return true; - } - }, - { - xtype: 'textfield', - name: 'hwaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - value: cdata.hwaddr, - allowBlank: true, - emptyText: 'auto' - }, - { - xtype: 'PVE.form.BridgeSelector', - name: 'bridge', - nodename: me.nodename, - fieldLabel: gettext('Bridge'), - value: cdata.bridge, - allowBlank: false - }, - { - xtype: 'pveVlanField', - name: 'tag', - value: cdata.tag - }, - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: cdata.rate, - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - value: cdata.firewall - } - ]; - - var dhcp4 = (cdata.ip === 'dhcp'); - if (dhcp4) { - cdata.ip = ''; - cdata.gw = ''; - } - - var auto6 = (cdata.ip6 === 'auto'); - var dhcp6 = (cdata.ip6 === 'dhcp'); - if (auto6 || dhcp6) { - cdata.ip6 = ''; - cdata.gw6 = ''; - } - - me.column2 = [ - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv4:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: !dhcp4, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv4mode', - inputValue: 'dhcp', - checked: dhcp4, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: cdata.ip, - disabled: dhcp4, - fieldLabel: 'IPv4/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw', - value: cdata.gw, - vtype: 'IPAddress', - disabled: dhcp4, - fieldLabel: gettext('Gateway') + ' (IPv4)', - margin: '0 0 3 0' // override bottom margin to account for the menuseparator - }, - { - xtype: 'menuseparator', - height: '3', - margin: '0' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv6:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: !(auto6 || dhcp6), - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv6mode', - inputValue: 'dhcp', - checked: dhcp6, - margin: '0 0 0 10' - }, - { - xtype: 'radiofield', - boxLabel: 'SLAAC', // do not localize - name: 'ipv6mode', - inputValue: 'auto', - checked: auto6, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: cdata.ip6, - vtype: 'IP6CIDRAddress', - disabled: (dhcp6 || auto6), - fieldLabel: 'IPv6/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: cdata.gw6, - disabled: (dhcp6 || auto6), - fieldLabel: gettext('Gateway') + ' (IPv6)' - } - ]; - - me.callParent(); - } -}); - - -Ext.define('PVE.lxc.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - var me = this; - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.nodename) { - throw "no node name specified"; - } - - var ipanel = Ext.create('PVE.lxc.NetworkInputPanel', { - ifname: me.ifname, - nodename: me.nodename, - dataCache: me.dataCache, - isCreate: me.isCreate - }); - - Ext.apply(me, { - subject: gettext('Network Device') + ' (veth)', - digest: me.dataCache.digest, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.NetworkView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveLxcNetworkView', - - onlineHelp: 'pct_container_network', - - dataCache: {}, // used to store result of last load - - stateful: true, - stateId: 'grid-lxc-network', - - load: function() { - var me = this; - - Proxmox.Utils.setErrorMask(me, true); - - Proxmox.Utils.API2Request({ - url: me.url, - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, gettext('Error') + ': ' + response.htmlStatus); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var result = Ext.decode(response.responseText); - var data = result.data || {}; - me.dataCache = data; - var records = []; - Ext.Object.each(data, function(key, value) { - if (!key.match(/^net\d+/)) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(value); - net.id = key; - records.push(net); - }); - me.store.loadData(records); - me.down('button[name=addButton]').setDisabled((records.length >= 10)); - } - }); - }, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.url = '/nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var store = new Ext.data.Store({ - model: 'pve-lxc-network', - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!caps.vms['VM.Config.Network']; - }, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - method: 'PUT', - params: { 'delete': rec.data.id, digest: me.dataCache.digest }, - callback: function() { - me.load(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (!caps.vms['VM.Config.Network']) { - return false; - } - - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - dataCache: me.dataCache, - ifname: rec.data.id - }); - win.on('destroy', me.load, me); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!caps.vms['VM.Config.Network']) { - return false; - } - return true; - }, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - name: 'addButton', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - isCreate: true, - dataCache: me.dataCache - }); - win.on('destroy', me.load, me); - win.show(); - } - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - width: 50, - dataIndex: 'id' - }, - { - header: gettext('Name'), - width: 80, - dataIndex: 'name' - }, - { - header: gettext('Bridge'), - width: 80, - dataIndex: 'bridge' - }, - { - header: gettext('Firewall'), - width: 80, - dataIndex: 'firewall', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('VLAN Tag'), - width: 80, - dataIndex: 'tag' - }, - { - header: gettext('MAC address'), - width: 110, - dataIndex: 'hwaddr' - }, - { - header: gettext('IP address'), - width: 150, - dataIndex: 'ip', - renderer: function(value, metaData, rec) { - if (rec.data.ip && rec.data.ip6) { - return rec.data.ip + "
" + rec.data.ip6; - } else if (rec.data.ip6) { - return rec.data.ip6; - } else { - return rec.data.ip; - } - } - }, - { - header: gettext('Gateway'), - width: 150, - dataIndex: 'gw', - renderer: function(value, metaData, rec) { - if (rec.data.gw && rec.data.gw6) { - return rec.data.gw + "
" + rec.data.gw6; - } else if (rec.data.gw6) { - return rec.data.gw6; - } else { - return rec.data.gw; - } - } - } - ], - listeners: { - activate: me.load, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-lxc-network', { - extend: "Ext.data.Model", - proxy: { type: 'memory' }, - fields: [ 'id', 'name', 'hwaddr', 'bridge', - 'ip', 'gw', 'ip6', 'gw6', 'tag', 'firewall' ] - }); - -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.RessourceView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcRessourceView'], - - onlineHelp: 'pct_configuration', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rowdef = me.rows[key] || {}; - - metaData.tdAttr = "valign=middle"; - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - } - return rowdef.header || key; - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - var mpeditor = caps.vms['VM.Config.Disk'] ? 'PVE.lxc.MountPointEdit' : undefined; - - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-memory', - group: 1, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - swap: { - header: gettext('Swap'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-swap', - group: 2, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - cores: { - header: gettext('Cores'), - editor: caps.vms['VM.Config.CPU'] ? 'PVE.lxc.CPUEdit' : undefined, - defaultValue: '', - tdCls: 'pve-itype-icon-processor', - group: 3, - renderer: function(value) { - var cpulimit = me.getObjectValue('cpulimit'); - var cpuunits = me.getObjectValue('cpuunits'); - var res; - if (value) { - res = value; - } else { - res = gettext('unlimited'); - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit + ']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits + ']'; - } - return res; - } - }, - rootfs: { - header: gettext('Root Disk'), - defaultValue: Proxmox.Utils.noneText, - editor: mpeditor, - tdCls: 'pve-itype-icon-storage', - group: 4 - }, - cpulimit: { - visible: false - }, - cpuunits: { - visible: false - }, - unprivileged: { - visible: false - } - }; - - PVE.Utils.forEachMP(function(bus, i) { - confid = bus + i; - var group = 5; - var header; - if (bus === 'mp') { - header = gettext('Mount Point') + ' (' + confid + ')'; - } else { - header = gettext('Unused Disk') + ' ' + i; - group += 1; - } - rows[confid] = { - group: group, - order: i, - tdCls: 'pve-itype-icon-storage', - editor: mpeditor, - header: header - }; - }, true); - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - var run_resize = function() { - var rec = me.selModel.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.MPResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - }; - - var run_remove = function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'delete': rec.data.key - }, - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var run_move = function(b, e, rec) { - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid, - type: 'lxc' - }); - - win.show(); - - win.on('destroy', me.reload, me); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: me.selModel, - disabled: true, - enableFn: function(rec) { - if (!rec) { - return false; - } - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: me.selModel, - disabled: true, - handler: run_resize - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - selModel: me.selModel, - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + me.renderKey(rec.data.key, {}, rec) + "'"); - if (rec.data.key.match(/^unused\d+$/)) { - msg += " " + gettext('This will permanently erase all data.'); - } - - return msg; - }, - handler: run_remove - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move Volume'), - selModel: me.selModel, - disabled: true, - dangerous: true, - handler: run_move - }); - - var set_button_status = function() { - var rec = me.selModel.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - remove_btn.disable(); - resize_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var isDisk = (rowdef.tdCls == 'pve-itype-icon-storage'); - - var noedit = rec.data['delete'] || !rowdef.editor; - if (!noedit && Proxmox.UserName !== 'root@pam' && key.match(/^mp\d+$/)) { - var mp = PVE.Parser.parseLxcMountPoint(value); - if (mp.type !== 'volume') { - noedit = true; - } - } - edit_btn.setDisabled(noedit); - - remove_btn.setDisabled(!isDisk || rec.data.key === 'rootfs' || !diskCap); - resize_btn.setDisabled(!isDisk || !diskCap); - move_btn.setDisabled(!isDisk || !diskCap); - - }; - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - Ext.apply(me, { - url: '/api2/json/' + baseurl, - selModel: me.selModel, - interval: 2000, - cwidth1: 170, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Mount Point'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.lxc.MountPointEdit', { - url: '/api2/extjs/' + baseurl, - unprivileged: me.getObjectValue('unprivileged'), - pveSelNode: me.pveSelNode - }); - win.show(); - } - } - ] - }) - }, - edit_btn, - remove_btn, - resize_btn, - move_btn - ], - rows: rows, - sorterFn: sorterFn, - editorConfig: { - pveSelNode: me.pveSelNode, - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - Ext.apply(me.editorConfig, { unprivileged: me.getObjectValue('unprivileged') }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.FeaturesInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcFeaturesInputPanel', - - // used to save the mounts fstypes until sending - mounts: [], - - fstypes: ['nfs', 'cifs'], - - viewModel: { - parent: null, - data: { - unprivileged: false - }, - formulas: { - privilegedOnly: function(get) { - return (get('unprivileged') ? gettext('privileged only') : ''); - }, - unprivilegedOnly: function(get) { - return (!get('unprivileged') ? gettext('unprivileged only') : ''); - } - } - }, - - items: [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('keyctl'), - name: 'keyctl', - bind: { - disabled: '{!unprivileged}', - boxLabel: '{unprivilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Nesting'), - name: 'nesting' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nfs', - fieldLabel: 'NFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'cifs', - fieldLabel: 'CIFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'fuse', - fieldLabel: 'FUSE' - } - ], - - onGetValues: function(values) { - var me = this; - var mounts = me.mounts; - me.fstypes.forEach(function(fs) { - if (values[fs]) { - mounts.push(fs); - } - delete values[fs]; - }); - - if (mounts.length) { - values.mount = mounts.join(';'); - } - - var featuresstring = PVE.Parser.printPropertyString(values, undefined); - if (featuresstring == '') { - return { 'delete': 'features' }; - } - return { features: featuresstring }; - }, - - setValues: function(values) { - var me = this; - - me.viewModel.set({ unprivileged: values.unprivileged }); - - if (values.features) { - var res = PVE.Parser.parsePropertyString(values.features); - me.mounts = []; - if (res.mount) { - res.mount.split(/[; ]/).forEach(function(item) { - if (me.fstypes.indexOf(item) === -1) { - me.mounts.push(item); - } else { - res[item] = 1; - } - }); - } - this.callParent([res]); - } - } -}); - -Ext.define('PVE.lxc.FeaturesEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveLxcFeaturesEdit', - - subject: gettext('Features'), - - items: [{ - xtype: 'pveLxcFeaturesInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load(); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.lxc.Options', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcOptions'], - - onlineHelp: 'pct_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'pct_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - defaultValue: Proxmox.Utils.unknownText - }, - arch: { - header: gettext('Architecture'), - defaultValue: Proxmox.Utils.unknownText - }, - console: { - header: '/dev/console', - defaultValue: 1, - renderer: Proxmox.Utils.format_enabled_toggle, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: '/dev/console', - items: { - xtype: 'proxmoxcheckbox', - name: 'console', - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - checked: true, - fieldLabel: '/dev/console' - } - } : undefined - }, - tty: { - header: gettext('TTY count'), - defaultValue: 2, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('TTY count'), - items: { - xtype: 'proxmoxintegerfield', - name: 'tty', - minValue: 0, - maxValue: 6, - value: 2, - fieldLabel: gettext('TTY count'), - emptyText: gettext('Default'), - deleteEmpty: true - } - } : undefined - }, - cmode: { - header: gettext('Console mode'), - defaultValue: 'tty', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Console mode'), - items: { - xtype: 'proxmoxKVComboBox', - name: 'cmode', - deleteEmpty: true, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (tty)"], - ['tty', "/dev/tty[X]"], - ['console', "/dev/console"], - ['shell', "shell"] - ], - fieldLabel: gettext('Console mode') - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - unprivileged: { - header: gettext('Unprivileged container'), - renderer: Proxmox.Utils.format_boolean, - defaultValue: 0 - }, - features: { - header: gettext('Features'), - defaultValue: Proxmox.Utils.noneText, - editor: Proxmox.UserName === 'root@pam' ? - 'PVE.lxc.FeaturesEdit' : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - Ext.apply(me, { - url: "/api2/json/" + baseurl, - selModel: sm, - interval: 5000, - tbar: [ edit_btn ], - rows: rows, - editorConfig: { - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - } -}); - -Ext.define('PVE.lxc.DNSInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcDNSInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var deletes = []; - if (!values.searchdomain && !me.insideWizard) { - deletes.push('searchdomain'); - } - - if (values.nameserver) { - var list = values.nameserver.split(/[\ \,\;]+/); - values.nameserver = list.join(' '); - } else if(!me.insideWizard) { - deletes.push('nameserver'); - } - - if (deletes.length) { - values['delete'] = deletes.join(','); - } - - return values; - }, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxtextfield', - name: 'searchdomain', - skipEmptyText: true, - fieldLabel: gettext('DNS domain'), - emptyText: gettext('use host settings'), - allowBlank: true - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS servers'), - vtype: 'IP64AddressList', - allowBlank: true, - emptyText: gettext('use host settings'), - name: 'nameserver', - itemId: 'nameserver' - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.DNSEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.lxc.DNSInputPanel'); - - Ext.apply(me, { - subject: gettext('Resources'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - if (values.nameserver) { - values.nameserver.replace(/[,;]/, ' '); - values.nameserver.replace(/^\s+/, ''); - } - - ipanel.setValues(values); - } - }); - } - } -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.DNS', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveLxcDNS'], - - onlineHelp: 'pct_container_network', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - hostname: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Hostname'), - editor: caps.vms['VM.Config.Network'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hostname'), - items: { - xtype: 'inputpanel', - items:{ - fieldLabel: gettext('Hostname'), - xtype: 'textfield', - name: 'hostname', - vtype: 'DnsName', - allowBlank: true, - emptyText: 'CT' + vmid.toString() - }, - onGetValues: function(values) { - var params = values; - if (values.hostname === undefined || - values.hostname === null || - values.hostname === '') { - params = { hostname: 'CT'+vmid.toString()}; - } - return params; - } - } - } : undefined - }, - searchdomain: { - header: gettext('DNS domain'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - }, - nameserver: { - header: gettext('DNS server'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var reload = function() { - me.rstore.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - if (Ext.isString(rowdef.editor)) { - win = Ext.create(rowdef.editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - //win.load(); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: run_editor - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - var rowdef = rows[rec.data.key]; - edit_btn.setDisabled(!rowdef.editor); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/config", - selModel: sm, - cwidth1: 150, - run_editor: run_editor, - tbar: [ edit_btn ], - rows: rows, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status, - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.lxc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.lxc.Config', - - onlineHelp: 'chapter_pct', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!me.pveSelNode.data.template; - - var running = !!me.pveSelNode.data.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + '/lxc/' + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + "/status/" + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var stopBtn = Ext.create('Ext.menu.Item',{ - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('vzstop', vmid), - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - dangerous: true, - handler: function() { - vm_command("stop"); - }, - iconCls: 'fa fa-stop' - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('vzshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items:[stopBtn] - }, - iconCls: 'fa fa-power-off' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'lxc'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('vztemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = me.pveSelNode.data.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - guestType: 'ct', - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - disabled: !caps.vms['VM.Allocate'], - itemId: 'removeBtn', - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'CT', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var vm = me.pveSelNode.data; - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - consoleType: 'lxc', - consoleName: vm.name, - hidden: template, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - - Ext.apply(me, { - title: Ext.String.format(gettext("Container {0} on node '{1}'"), vm.text, nodename), - hstateid: 'lxctab', - tbarSpacing: false, - tbar: [ statusTxt, '->', startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveLxcSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push( - { - title: gettext('Console'), - itemId: 'consolejs', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'lxc', - xtermjs: true, - nodename: nodename - } - ); - } - - me.items.push( - { - title: gettext('Resources'), - itemId: 'resources', - expandedOnInit: true, - iconCls: 'fa fa-cube', - xtype: 'pveLxcRessourceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - xtype: 'pveLxcNetworkView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - itemId: 'dns', - xtype: 'pveLxcDNS' - }, - { - title: gettext('Options'), - itemId: 'options', - iconCls: 'fa fa-gear', - xtype: 'pveLxcOptions' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - iconCls: 'fa fa-list', - xtype: 'proxmoxNodeTasks', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - xtype: 'pveLxcSnapshotTree', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - itemId: 'permissions', - iconCls: 'fa fa-unlock', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var lock; - if (!success) { - status = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - } - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - stopBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'stopped'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.CreateWizard', { - extend: 'PVE.window.Wizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - storage: '', - unprivileged: true - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('LXC Container'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'pct_general', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', // backend only knows vmid - guestType: 'lxc', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'proxmoxtextfield', - name: 'hostname', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Hostname'), - skipEmptyText: true, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - bind: { - value: '{unprivileged}' - }, - fieldLabel: gettext('Unprivileged container') - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'password', - value: '', - fieldLabel: gettext('Password'), - allowBlank: false, - minLength: 5, - change: function(f, value) { - if (f.rendered) { - f.up().down('field[name=confirmpw]').validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'confirmpw', - value: '', - fieldLabel: gettext('Confirm password'), - allowBlank: true, - submitValue: false, - validator: function(value) { - var pw = this.up().down('field[name=password]').getValue(); - if (pw !== value) { - return "Passwords do not match!"; - } - return true; - } - }, - { - xtype: 'proxmoxtextfield', - name: 'ssh-public-keys', - value: '', - fieldLabel: gettext('SSH public key'), - allowBlank: true, - validator: function(value) { - var pwfield = this.up().down('field[name=password]'); - if (value.length) { - var key = PVE.Parser.parseSSHKey(value); - if (!key) { - return "Failed to recognize ssh key"; - } - pwfield.allowBlank = true; - } else { - pwfield.allowBlank = false; - } - pwfield.validate(); - return true; - }, - afterRender: function() { - if (!window.FileReader) { - // No FileReader support in this browser - return; - } - var cancel = function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - }; - var field = this; - field.inputEl.on('dragover', cancel); - field.inputEl.on('dragenter', cancel); - field.inputEl.on('drop', function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - var files = ev.dataTransfer.files; - PVE.Utils.loadSSHKeyFromFile(files[0], function(v) { - field.setValue(v); - }); - }); - } - }, - { - xtype: 'filebutton', - name: 'file', - hidden: !window.FileReader, - text: gettext('Load SSH Key File'), - listeners: { - change: function(btn, e, value) { - e = e.event; - var field = this.up().down('proxmoxtextfield[name=ssh-public-keys]'); - PVE.Utils.loadSSHKeyFromFile(e.target.files[0], function(v) { - field.setValue(v); - }); - btn.reset(); - } - } - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Template'), - onlineHelp: 'pct_container_images', - column1: [ - { - xtype: 'pveStorageSelector', - name: 'tmplstorage', - fieldLabel: gettext('Storage'), - storageContent: 'vztmpl', - autoSelect: true, - allowBlank: false, - bind: { - value: '{storage}', - nodename: '{nodename}' - } - }, - { - xtype: 'pveFileSelector', - name: 'ostemplate', - storageContent: 'vztmpl', - fieldLabel: gettext('Template'), - bind: { - storage: '{storage}', - nodename: '{nodename}' - }, - allowBlank: false - } - ] - }, - { - xtype: 'pveLxcMountPointInputPanel', - title: gettext('Root Disk'), - insideWizard: true, - isCreate: true, - unused: false, - bind: { - nodename: '{nodename}', - unprivileged: '{unprivileged}' - }, - confid: 'rootfs' - }, - { - xtype: 'pveLxcCPUInputPanel', - title: gettext('CPU'), - insideWizard: true - }, - { - xtype: 'pveLxcMemoryInputPanel', - title: gettext('Memory'), - insideWizard: true - }, - { - xtype: 'pveLxcNetworkInputPanel', - title: gettext('Network'), - insideWizard: true, - bind: { - nodename: '{nodename}' - }, - isCreate: true - }, - { - xtype: 'pveLxcDNSInputPanel', - title: gettext('DNS'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var wizard = this.up('window'); - var kv = wizard.getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete' || key === 'tmplstorage') { // ignore - return; - } - if (key === 'password') { // don't show pw - return; - } - var html = Ext.htmlEncode(Ext.JSON.encode(value)); - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - delete kv.tmplstorage; - - if (!kv.pool.length) { - delete kv.pool; - } - - if (!kv.password.length && kv['ssh-public-keys']) { - delete kv.password; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response, opts){ - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - -Ext.define('PVE.lxc.SnapshotTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveLxcSnapshotTree'], - - onlineHelp: 'pct_snapshots', - - load_delay: 3000, - - old_digest: 'invalid', - - stateful: true, - stateId: 'grid-lxc-snapshots', - - sorterFn: function(rec1, rec2) { - var v1 = rec1.data.snaptime; - var v2 = rec2.data.snaptime; - - if (rec1.data.name === 'current') { - return 1; - } - if (rec2.data.name === 'current') { - return -1; - } - - return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); - }, - - reload: function(repeat) { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot', - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - me.load_task.delay(me.load_delay); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var digest = 'invalid'; - var idhash = {}; - var root = { name: '__root', expanded: true, children: [] }; - Ext.Array.each(response.result.data, function(item) { - item.leaf = true; - item.children = []; - if (item.name === 'current') { - digest = item.digest + item.running; - if (item.running) { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running'; - } else { - item.iconCls = 'fa fa-fw fa-desktop x-fa-tree'; - } - } else { - item.iconCls = 'fa fa-fw fa-history x-fa-tree'; - } - idhash[item.name] = item; - }); - - if (digest !== me.old_digest) { - me.old_digest = digest; - - Ext.Array.each(response.result.data, function(item) { - if (item.parent && idhash[item.parent]) { - var parent_item = idhash[item.parent]; - parent_item.children.push(item); - parent_item.leaf = false; - parent_item.expanded = true; - parent_item.expandable = false; - } else { - root.children.push(item); - } - }); - - me.setRootNode(root); - } - - me.load_task.delay(me.load_delay); - } - }); - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/feature', - params: { feature: 'snapshot' }, - method: 'GET', - success: function(response, options) { - var res = response.result.data; - if (res.hasFeature) { - var snpBtns = Ext.ComponentQuery.query('#snapshotBtn'); - snpBtns.forEach(function(item){ - item.enable(); - }); - } - } - }); - - - }, - - listeners: { - beforestatesave: function(grid, state, eopts) { - // extjs cannot serialize functions, - // so a the sorter with only the sorterFn will - // not be a valid sorter when restoring the state - delete state.storeState.sorters; - } - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.vmid = me.pveSelNode.data.vmid; - if (!me.vmid) { - throw "no VM ID specified"; - } - - me.load_task = new Ext.util.DelayedTask(me.reload, me); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var valid_snapshot = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current'; - }; - - var valid_snapshot_rollback = function(record) { - return record && record.data && record.data.name && - record.data.name !== 'current' && !record.data.snapstate; - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (valid_snapshot(rec)) { - var win = Ext.create('PVE.window.LxcSnapshot', { - snapname: rec.data.name, - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - me.mon(win, 'close', me.reload, me); - } - }; - - var editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: valid_snapshot, - handler: run_editor - }); - - var rollbackBtn = new Proxmox.button.Button({ - text: gettext('Rollback'), - disabled: true, - dangerous: true, - selModel: sm, - enableFn: valid_snapshot_rollback, - confirmMsg: function(rec) { - var taskdescription = Proxmox.Utils.format_task_description('vzrollback', me.vmid); - var snaptime = Ext.Date.format(rec.data.snaptime,'Y-m-d H:i:s'); - var snapname = rec.data.name; - - var msg = Ext.String.format(gettext('{0} to {1} ({2})'), - taskdescription, snapname, snaptime); - msg += '

' + gettext('Note: Rollback stops CT') + '

'; - - return msg; - }, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname + '/rollback', - method: 'POST', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var removeBtn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.name + "'"); - return msg; - }, - enableFn: valid_snapshot, - handler: function(btn, event) { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var snapname = rec.data.name; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - } - }); - - var snapshotBtn = Ext.create('Ext.Button', { - itemId: 'snapshotBtn', - text: gettext('Take Snapshot'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.window.LxcSnapshot', { - nodename: me.nodename, - vmid: me.vmid - }); - win.show(); - } - }); - - Ext.apply(me, { - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - selModel: sm, - tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ], - fields: [ - 'name', 'description', 'snapstate', 'vmstate', 'running', - { name: 'snaptime', type: 'date', dateFormat: 'timestamp' } - ], - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - width: 200, - renderer: function(value, metaData, record) { - if (value === 'current') { - return "NOW"; - } else { - return value; - } - } - }, -// { -// text: gettext('RAM'), -// align: 'center', -// resizable: false, -// dataIndex: 'vmstate', -// width: 50, -// renderer: function(value, metaData, record) { -// if (record.data.name !== 'current') { -// return Proxmox.Utils.format_boolean(value); -// } -// } -// }, - { - text: gettext('Date') + "/" + gettext("Status"), - dataIndex: 'snaptime', - resizable: false, - width: 150, - renderer: function(value, metaData, record) { - if (record.data.snapstate) { - return record.data.snapstate; - } - if (value) { - return Ext.Date.format(value,'Y-m-d H:i:s'); - } - } - }, - { - text: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value, metaData, record) { - if (record.data.name === 'current') { - return gettext("You are here!"); - } else { - return Ext.String.htmlEncode(value); - } - } - } - ], - columnLines: true, - listeners: { - activate: me.reload, - destroy: me.load_task.cancel, - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.store.sorters.add(new Ext.util.Sorter({ - sorterFn: me.sorterFn - })); - } -}); -Ext.define('PVE.window.LxcSnapshot', { - extend: 'Ext.window.Window', - - resizable: false, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - defaultFocus: 'field', - - take_snapshot: function(snapname, descr, vmstate) { - var me = this; - var params = { snapname: snapname }; - if (descr) { - params.description = descr; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot", - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - update_snapshot: function(snapname, descr) { - var me = this; - Proxmox.Utils.API2Request({ - params: { description: descr }, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot/" + - snapname + '/config', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var summarystore = Ext.create('Ext.data.Store', { - model: 'KeyValue', - sorters: [ - { - property : 'key', - direction: 'ASC' - } - ] - }); - - var items = [ - { - xtype: me.snapname ? 'displayfield' : 'textfield', - name: 'snapname', - value: me.snapname, - fieldLabel: gettext('Name'), - vtype: 'ConfigId', - allowBlank: false - } - ]; - - if (me.snapname) { - items.push({ - xtype: 'displayfield', - name: 'snaptime', - renderer: PVE.Utils.render_timestamp_human_readable, - fieldLabel: gettext('Timestamp') - }); - } - - items.push({ - xtype: 'textareafield', - grow: true, - name: 'description', - fieldLabel: gettext('Description') - }); - - if (me.snapname) { - items.push({ - title: gettext('Settings'), - xtype: 'grid', - height: 200, - store: summarystore, - columns: [ - {header: gettext('Key'), width: 150, dataIndex: 'key'}, - {header: gettext('Value'), flex: 1, dataIndex: 'value'} - ] - }); - } - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - if (me.snapname) { - me.title = gettext('Edit') + ': ' + gettext('Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Update'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.update_snapshot(me.snapname, values.description); - } - } - }); - } else { - me.title ="VM " + me.vmid + ': ' + gettext('Take Snapshot'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Take Snapshot'), - reference: 'submitbutton', - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.take_snapshot(values.snapname, values.description); - } - } - }); - } - - Ext.apply(me, { - modal: true, - width: 450, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - if (me.snapname) { - Ext.apply(me, { - width: 620, - height: 420 - }); - } - - me.callParent(); - - if (!me.snapname) { - return; - } - - // else load data - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + "/snapshot/" + - me.snapname + '/config', - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.close(); - }, - success: function(response, options) { - var data = response.result.data; - var kvarray = []; - Ext.Object.each(data, function(key, value) { - if (key === 'description' || key === 'snaptime') { - return; - } - kvarray.push({ key: key, value: value }); - }); - - summarystore.suspendEvents(); - summarystore.add(kvarray); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh', summarystore); - - form.findField('snaptime').setValue(data.snaptime); - form.findField('description').setValue(data.description); - } - }); - } -}); -/*jslint confusion: true */ -var labelWidth = 120; - -Ext.define('PVE.lxc.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('Memory'), - items: Ext.create('PVE.lxc.MemoryInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - - -Ext.define('PVE.lxc.CPUEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('CPU'), - items: Ext.create('PVE.lxc.CPUInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.lxc.CPUInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcCPUInputPanel', - - onlineHelp: 'pct_cpu', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - PVE.Utils.delete_if_default(values, 'cores', '', me.insideWizard); - // cpu{limit,unit} aren't in the wizard so create is always false - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - return values; - }, - - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - value: 1024, - minValue: 8, - maxValue: 500000, - labelWidth: labelWidth, - allowBlank: false - } - ], - - initComponent: function() { - var me = this; - - me.column1 = [ - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: me.insideWizard ? 1 : '', - fieldLabel: gettext('Cores'), - allowBlank: true, - deleteEmpty: true, - emptyText: gettext('unlimited') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcMemoryInputPanel', - - onlineHelp: 'pct_memory', - - insideWizard: false, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxintegerfield', - name: 'memory', - minValue: 16, - value: '512', - step: 32, - fieldLabel: gettext('Memory') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'swap', - minValue: 0, - value: '512', - step: 32, - fieldLabel: gettext('Swap') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); -Ext.define('PVE.window.MPResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 120, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -/*jslint confusion: true*/ -/* hidden: boolean and string - * bind: function and object - * disabled: boolean and string - */ -Ext.define('PVE.lxc.MountPointInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcMountPointInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_storage', - - unused: false, // add unused disk imaged - - unprivileged: false, - - vmconfig: {}, // used to select unused disks - - setUnprivileged: function(unprivileged) { - var me = this; - var vm = me.getViewModel(); - me.unprivileged = unprivileged; - vm.set('unpriv', unprivileged); - }, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || "mp"+values.mpid; - values.file = me.down('field[name=file]').getValue(); - if (values.mountoptions) { - values.mountoptions = values.mountoptions.join(';'); - } - - if (me.unused) { - confid = "mp"+values.mpid; - } else if (me.isCreate) { - values.file = values.hdstorage + ':' + values.disksize; - } - - // delete unnecessary fields - delete values.mpid; - delete values.hdstorage; - delete values.disksize; - delete values.diskformat; - - var res = {}; - res[confid] = PVE.Parser.printLxcMountPoint(values); - return res; - }, - - - setMountPoint: function(mp) { - var me = this; - var vm = this.getViewModel(); - vm.set('mptype', mp.type); - if (mp.mountoptions) { - mp.mountoptions = mp.mountoptions.split(';'); - } - - if (this.confid === 'rootfs') { - var field = me.down('field[name=mountoptions]'); - var forbidden = ['nodev', 'noexec']; - var filtered = field.comboItems.filter(e => !forbidden.includes(e[0])); - field.setComboItems(filtered); - } - - me.setValues(mp); - }, - - setVMConfig: function(vmconfig) { - var me = this; - var vm = me.getViewModel(); - me.vmconfig = vmconfig; - vm.set('unpriv', vmconfig.unprivileged); - - PVE.Utils.forEachMP(function(bus, i) { - var name = "mp" + i.toString(); - if (!Ext.isDefined(vmconfig[name])) { - me.down('field[name=mpid]').setValue(i); - return false; - } - }); - }, - - setNodename: function(nodename) { - var me = this; - var vm = me.getViewModel(); - vm.set('node', nodename); - me.down('#diskstorage').setNodename(nodename); - }, - - controller: { - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=mpid]': { - change: function(field, value) { - field.validate(); - } - }, - '#hdstorage': { - change: function(field, newValue) { - var me = this; - if (!newValue) { - return; - } - - var rec = field.store.getById(newValue); - if (!rec) { - return; - } - - var vm = me.getViewModel(); - vm.set('type', rec.data.type); - } - } - }, - - init: function(view) { - var me = this; - var vm = this.getViewModel(); - vm.set('confid', view.confid); - vm.set('unused', view.unused); - vm.set('node', view.nodename); - vm.set('unpriv', view.unprivileged); - vm.set('hideStorSelector', view.unused || !view.isCreate); - } - }, - - viewModel: { - data: { - unpriv: false, - unused: false, - showStorageSelector: false, - mptype: '', - type: '', - confid: '', - node: '' - }, - - formulas: { - quota: function(get) { - return !(get('type') === 'zfs' || - get('type') === 'zfspool' || - get('unpriv') || - get('isBind')); - }, - hasMP: function(get) { - return !!get('confid') && !get('unused'); - }, - isRoot: function(get) { - return get('confid') === 'rootfs'; - }, - isBind: function(get) { - return get('mptype') === 'bind'; - }, - isBindOrRoot: function(get) { - return get('isBind') || get('isRoot'); - } - } - }, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'mpid', - fieldLabel: gettext('Mount Point ID'), - minValue: 0, - maxValue: PVE.Utils.mp_counts.mps - 1, - hidden: true, - allowBlank: false, - disabled: true, - bind: { - hidden: '{hasMP}', - disabled: '{hasMP}' - }, - validator: function(value) { - var me = this.up('inputpanel'); - if (!me.rendered) { - return; - } - if (Ext.isDefined(me.vmconfig["mp"+value])) { - return "Mount point is already in use."; - } - /*jslint confusion: true*/ - /* returns a string above */ - return true; - } - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'diskstorage', - storageContent: 'rootdir', - hidden: true, - autoSelect: true, - selectformat: false, - defaultSize: 8, - bind: { - hidden: '{hideStorSelector}', - disabled: '{hideStorSelector}', - nodename: '{node}' - } - }, - { - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'file', - bind: { - hidden: '{!hideStorSelector}' - } - } - ], - - column2: [ - { - xtype: 'textfield', - name: 'mp', - value: '', - emptyText: gettext('/some/path'), - allowBlank: false, - disabled: true, - fieldLabel: gettext('Path'), - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'backup', - fieldLabel: gettext('Backup'), - bind: { - hidden: '{isRoot}', - disabled: '{isBindOrRoot}' - } - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'quota', - defaultValue: 0, - bind: { - disabled: '{!quota}' - }, - fieldLabel: gettext('Enable quota'), - listeners: { - disable: function() { - this.reset(); - } - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'ro', - defaultValue: 0, - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - }, - fieldLabel: gettext('Read-only') - }, - { - xtype: 'proxmoxKVComboBox', - name: 'mountoptions', - fieldLabel: gettext('Mount options'), - deleteEmpty: false, - comboItems: [ - ['noatime', 'noatime'], - ['nodev', 'nodev'], - ['noexec', 'noexec'], - ['nosuid', 'nosuid'] - ], - multiSelect: true, - value: [], - allowBlank: true - }, - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'acl', - fieldLabel: 'ACLs', - deleteEmpty: false, - comboItems: [ - ['__default__', Proxmox.Utils.defaultText], - ['1', Proxmox.Utils.enabledText], - ['0', Proxmox.Utils.disabledText] - ], - value: '__default__', - bind: { - disabled: '{isBind}' - }, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - inputValue: '0', // reverses the logic - name: 'replicate', - fieldLabel: gettext('Skip replication') - } - ] -}); - -Ext.define('PVE.lxc.MountPointEdit', { - extend: 'Proxmox.window.Edit', - - unprivileged: false, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.lxc.MountPointInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - unprivileged: me.unprivileged, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - subject = gettext('Unused Disk'); - } else if (me.isCreate) { - subject = gettext('Mount Point'); - } else { - subject = gettext('Mount Point') + ' (' + me.confid + ')'; - } - - Ext.apply(me, { - subject: subject, - defaultFocus: me.confid !== 'rootfs' ? 'textfield[name=mp]' : 'tool', - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - /*jslint confusion: true*/ - /*data is defined as array above*/ - var value = response.result.data[me.confid]; - /*jslint confusion: false*/ - var mp = PVE.Parser.parseLxcMountPoint(value); - - if (!mp) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse mount point options'); - me.close(); - return; - } - - ipanel.setMountPoint(mp); - me.isValid(); // trigger validation - } - } - }); - } -}); -Ext.define('PVE.pool.StatusView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pvePoolStatusView'], - disabled: true, - - title: gettext('Status'), - cwidth1: 150, - interval: 30000, - //height: 195, - initComponent : function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var rows = { - comment: { - header: gettext('Comment'), - renderer: Ext.String.htmlEncode, - required: true - } - }; - - Ext.apply(me, { - url: "/api2/json/pools/" + pool, - rows: rows - }); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePoolSummary', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var statusview = Ext.create('PVE.pool.StatusView', { - pveSelNode: me.pveSelNode, - style: 'padding-top:0px' - }); - - var rstore = statusview.rstore; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - defaults: { - style: 'padding-top:10px', - width: 800 - }, - items: [ statusview ] - }); - - me.on('activate', rstore.startUpdate); - me.on('destroy', rstore.stopUpdate); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.pvePoolConfig', - - onlineHelp: 'pveum_pools', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - Ext.apply(me, { - title: Ext.String.format(gettext("Resource Pool") + ': ' + pool), - hstateid: 'pooltab', - items: [ - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - xtype: 'pvePoolSummary', - itemId: 'summary' - }, - { - title: gettext('Members'), - xtype: 'pvePoolMembers', - iconCls: 'fa fa-th', - pool: pool, - itemId: 'members' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/pool/' + pool - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.panel.StorageBase', { - extend: 'Proxmox.panel.InputPanel', - controller: 'storageEdit', - - type: '', - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = me.type; - } else { - delete values.storage; - } - - values.disable = values.enable ? 0 : 1; - delete values.enable; - - return values; - }, - - initComponent : function() { - var me = this; - - me.column1.unshift({ - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'storage', - value: me.storageId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }); - - me.column2.unshift( - { - xtype: 'pveNodeSelector', - name: 'nodes', - disabled: me.storageId === 'local', - fieldLabel: gettext('Nodes'), - emptyText: gettext('All') + ' (' + gettext('No restrictions') +')', - multiSelect: true, - autoSelect: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - me.isCreate = !me.storageId; - - if (me.isCreate) { - me.url = '/api2/extjs/storage'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/storage/' + me.storageId; - me.method = 'PUT'; - } - - var ipanel = Ext.create(me.paneltype, { - type: me.type, - isCreate: me.isCreate, - storageId: me.storageId - }); - - Ext.apply(me, { - subject: PVE.Utils.format_storage_type(me.type), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - var ctypes = values.content || ''; - - values.content = ctypes.split(','); - - if (values.nodes) { - values.nodes = values.nodes.split(','); - } - values.enable = values.disable ? 0 : 1; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.grid.TemplateSelector', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveTemplateSelector', - - stateful: true, - stateId: 'grid-template-selector', - viewConfig: { - trackOver: false - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/aplinfo"; - var store = new Ext.data.Store({ - model: 'pve-aplinfo', - groupField: 'section', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{[ "Section: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - '->', - gettext('Search'), - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - var value = field.getValue().toLowerCase(); - store.clearFilter(true); - store.filterBy(function(rec) { - return (rec.data['package'].toLowerCase().indexOf(value) !== -1) - || (rec.data.headline.toLowerCase().indexOf(value) !== -1); - }); - } - } - } - ], - features: [ groupingFeature ], - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Package'), - flex: 1, - dataIndex: 'package' - }, - { - header: gettext('Version'), - width: 80, - dataIndex: 'version' - }, - { - header: gettext('Description'), - flex: 1.5, - renderer: Ext.String.htmlEncode, - dataIndex: 'headline' - } - ], - listeners: { - afterRender: reload - } - }); - - me.callParent(); - } - -}, function() { - - Ext.define('pve-aplinfo', { - extend: 'Ext.data.Model', - fields: [ - 'template', 'type', 'package', 'version', 'headline', 'infopage', - 'description', 'os', 'section' - ], - idProperty: 'template' - }); - -}); - -Ext.define('PVE.storage.TemplateDownload', { - extend: 'Ext.window.Window', - alias: 'widget.pveTemplateDownload', - - modal: true, - title: gettext('Templates'), - layout: 'fit', - width: 900, - height: 600, - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var grid = Ext.create('PVE.grid.TemplateSelector', { - border: false, - scrollable: true, - nodename: me.nodename - }); - - var sm = grid.getSelectionModel(); - - var submitBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Download'), - disabled: true, - selModel: sm, - handler: function(button, event, rec) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/aplinfo', - params: { - storage: me.storage, - template: rec.data.template - }, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - destroy: me.reloadGrid - } - }).show(); - - me.close(); - } - }); - } - }); - - Ext.apply(me, { - items: grid, - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.Upload', { - extend: 'Ext.window.Window', - alias: 'widget.pveStorageUpload', - - resizable: false, - - modal: true, - - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var xhr; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/storage/" + me.storage + "/upload"; - - var pbar = Ext.create('Ext.ProgressBar', { - text: 'Ready', - hidden: true - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - method: 'POST', - waitMsgTarget: true, - bodyPadding: 10, - border: false, - width: 300, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - xtype: 'pveContentTypeSelector', - cts: me.contents, - fieldLabel: gettext('Content'), - name: 'content', - value: me.contents[0] || '', - allowBlank: false - }, - { - xtype: 'filefield', - name: 'filename', - buttonText: gettext('Select File...'), - allowBlank: false - }, - pbar - ] - }); - - var form = me.formPanel.getForm(); - - var doStandardSubmit = function() { - form.submit({ - url: "/api2/htmljs" + baseurl, - waitMsg: gettext('Uploading file...'), - success: function(f, action) { - me.close(); - }, - failure: function(f, action) { - var msg = PVE.Utils.extractFormActionError(action); - Ext.Msg.alert(gettext('Error'), msg); - } - }); - }; - - var updateProgress = function(per, bytes) { - var text = (per * 100).toFixed(2) + '%'; - if (bytes) { - text += " (" + Proxmox.Utils.format_size(bytes) + ')'; - } - pbar.updateProgress(per, text); - }; - - var abortBtn = Ext.create('Ext.Button', { - text: gettext('Abort'), - disabled: true, - handler: function() { - me.close(); - } - }); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Upload'), - disabled: true, - handler: function(button) { - var fd; - try { - fd = new FormData(); - } catch (err) { - doStandardSubmit(); - return; - } - - button.setDisabled(true); - abortBtn.setDisabled(false); - - var field = form.findField('content'); - fd.append("content", field.getValue()); - field.setDisabled(true); - - field = form.findField('filename'); - var file = field.fileInputEl.dom; - fd.append("filename", file.files[0]); - field.setDisabled(true); - - pbar.setVisible(true); - updateProgress(0); - - xhr = new XMLHttpRequest(); - - xhr.addEventListener("load", function(e) { - if (xhr.status == 200) { - me.close(); - } else { - var msg = gettext('Error') + " " + xhr.status.toString() + ": " + Ext.htmlEncode(xhr.statusText); - if (xhr.responseText !== "") { - var result = Ext.decode(xhr.responseText); - result.message = msg; - msg = Proxmox.Utils.extractRequestError(result, true); - } - Ext.Msg.alert(gettext('Error'), msg, function(btn) { - me.close(); - }); - } - }, false); - - xhr.addEventListener("error", function(e) { - var msg = "Error " + e.target.status.toString() + " occurred while receiving the document."; - Ext.Msg.alert(gettext('Error'), msg, function(btn) { - me.close(); - }); - }); - - xhr.upload.addEventListener("progress", function(evt) { - if (evt.lengthComputable) { - var percentComplete = evt.loaded / evt.total; - updateProgress(percentComplete, evt.loaded); - } - }, false); - - xhr.open("POST", "/api2/json" + baseurl, true); - xhr.send(fd); - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - Ext.apply(me, { - title: gettext('Upload'), - items: me.formPanel, - buttons: [ abortBtn, submitBtn ], - listeners: { - close: function() { - if (xhr) { - xhr.abort(); - } - } - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ContentView', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveStorageContentView', - - stateful: true, - stateId: 'grid-storage-content', - viewConfig: { - trackOver: false, - loadMask: false - }, - features: [ - { - ftype: 'grouping', - groupHeaderTpl: '{name} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - } - ], - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + nodename + "/storage/" + storage + "/content"; - var store = Ext.create('Ext.data.Store',{ - model: 'pve-storage-content', - groupField: 'content', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - }, - sorters: { - property: 'volid', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - me.statusStore.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var templateButton = Ext.create('Proxmox.button.Button',{ - itemId: 'tmpl-btn', - text: gettext('Templates'), - handler: function() { - var win = Ext.create('PVE.storage.TemplateDownload', { - nodename: nodename, - storage: storage, - reloadGrid: reload - }); - win.show(); - } - }); - - var uploadButton = Ext.create('Proxmox.button.Button', { - contents : ['iso','vztmpl'], - text: gettext('Upload'), - handler: function() { - var me = this; - var win = Ext.create('PVE.storage.Upload', { - nodename: nodename, - storage: storage, - contents: me.contents - }); - win.show(); - win.on('destroy', reload); - } - }); - - var imageRemoveButton; - var removeButton = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - enableFn: function(rec) { - if (rec && rec.data.content !== 'images') { - imageRemoveButton.setVisible(false); - removeButton.setVisible(true); - return true; - } - return false; - }, - callback: function() { - reload(); - }, - baseurl: baseurl + '/' - }); - - imageRemoveButton = Ext.create('Proxmox.button.Button',{ - selModel: sm, - hidden: true, - text: gettext('Remove'), - enableFn: function(rec) { - if (rec && rec.data.content === 'images') { - removeButton.setVisible(false); - imageRemoveButton.setVisible(true); - return true; - } - return false; - }, - handler: function(btn, event, rec) { - me = this; - - var url = baseurl + '/' + rec.data.volid; - var vmid = rec.data.vmid; - - var store = PVE.data.ResourceStore; - - if (vmid && store.findVMID(vmid)) { - var guest_node = store.guestNode(vmid); - var storage_path = 'storage/' + nodename + '/' + storage; - - // allow to delete local backed images if a VMID exists on another node. - if (store.storageIsShared(storage_path) || guest_node == nodename) { - var msg = Ext.String.format( - gettext("Cannot remove image, a guest with VMID '{0}' exists!"), vmid); - msg += '
' + gettext("You can delete the image from the guest's hardware pane"); - - Ext.Msg.show({ - title: gettext('Cannot remove disk image.'), - icon: Ext.Msg.ERROR, - msg: msg - }); - return; - } - } - var win = Ext.create('PVE.window.SafeDestroy', { - title: Ext.String.format(gettext("Destroy '{0}'"), rec.data.volid), - showProgress: true, - url: url, - item: { type: 'Image', id: vmid } - }).show(); - win.on('destroy', function() { - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - reload(); - - }); - } - }); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - xtype: 'proxmoxButton', - text: gettext('Restore'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b, e, rec) { - var vmtype; - if (rec.data.volid.match(/vzdump-qemu-/)) { - vmtype = 'qemu'; - } else if (rec.data.volid.match(/vzdump-openvz-/) || rec.data.volid.match(/vzdump-lxc-/)) { - vmtype = 'lxc'; - } else { - return; - } - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }, - removeButton, - imageRemoveButton, - templateButton, - uploadButton, - { - xtype: 'proxmoxButton', - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b,e,rec) { - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }, - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - store.clearFilter(true); - store.filter([ - { - property: 'text', - value: field.getValue(), - anyMatch: true, - caseSensitive: false - } - ]); - } - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'text' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ], - listeners: { - activate: reload - } - }); - - me.callParent(); - - // disable the buttons/restrict the upload window - // if templates or uploads are not allowed - me.mon(me.statusStore, 'load', function(s, records, success) { - var availcontent = []; - Ext.Array.each(records, function(item){ - if (item.id === 'content') { - availcontent = item.data.value.split(','); - } - }); - var templ = false; - var upload = false; - var cts = []; - - Ext.Array.each(availcontent, function(content) { - if (content === 'vztmpl') { - templ = true; - cts.push('vztmpl'); - } else if (content === 'iso') { - upload = true; - cts.push('iso'); - } - }); - - if (templ !== upload) { - uploadButton.contents = cts; - } - - templateButton.setDisabled(!templ); - uploadButton.setDisabled(!upload && !templ); - }); - } -}, function() { - - Ext.define('pve-storage-content', { - extend: 'Ext.data.Model', - fields: [ - 'volid', 'content', 'format', 'size', 'used', 'vmid', - 'channel', 'id', 'lun', - { - name: 'text', - convert: function(value, record) { - // check for volid, because if you click on a grouping header, - // it calls convert (but with an empty volid) - if (value || record.data.volid === null) { - return value; - } - return PVE.Utils.render_storage_content(value, {}, record); - } - } - ], - idProperty: 'volid' - }); - -}); -Ext.define('PVE.storage.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveStorageStatusView', - - height: 230, - title: gettext('Status'), - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 30 5 30' - }, - items: [ - { - xtype: 'box', - height: 30 - }, - { - itemId: 'enabled', - title: gettext('Enabled'), - printBar: false, - textField: 'disabled', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - itemId: 'active', - title: gettext('Active'), - printBar: false, - textField: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - itemId: 'content', - title: gettext('Content'), - printBar: false, - textField: 'content', - renderer: PVE.Utils.format_content_types - }, - { - itemId: 'type', - title: gettext('Type'), - printBar: false, - textField: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - xtype: 'box', - height: 10 - }, - { - itemId: 'usage', - title: gettext('Usage'), - valueField: 'used', - maxField: 'total' - } - ], - - updateTitle: function() { - return; - } -}); -Ext.define('PVE.storage.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStorageSummary', - scrollable: true, - bodyPadding: 5, - tbar: [ - '->', - { - xtype: 'proxmoxRRDTypeSelector' - } - ], - layout: { - type: 'column' - }, - defaults: { - padding: 5, - columnWidth: 1 - }, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var rstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/status", - interval: 1000 - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/rrddata", - model: 'pve-rrd-storage' - }); - - Ext.apply(me, { - items: [ - { - xtype: 'pveStorageStatusView', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Usage'), - fields: ['total','used'], - fieldTitles: ['Total Size', 'Used Size'], - store: rrdstore - } - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.storage.Browser', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.storage.Browser', - - onlineHelp: 'chapter_storage', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storeid = me.pveSelNode.data.storage; - if (!storeid) { - throw "no storage ID specified"; - } - - - me.items = [ - { - title: gettext('Summary'), - xtype: 'pveStorageSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ]; - - var caps = Ext.state.Manager.get('GuiCap'); - - Ext.apply(me, { - title: Ext.String.format(gettext("Storage {0} on node {1}"), - "'" + storeid + "'", "'" + nodename + "'"), - hstateid: 'storagetab' - }); - - if (caps.storage['Datastore.Allocate'] || - caps.storage['Datastore.AllocateSpace'] || - caps.storage['Datastore.Audit']) { - me.items.push({ - xtype: 'pveStorageContentView', - title: gettext('Content'), - iconCls: 'fa fa-th', - itemId: 'content' - }); - } - - if (caps.storage['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/storage/' + storeid - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.storage.DirInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_directory', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'path', - value: '', - fieldLabel: gettext('Directory'), - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.NFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveNFSScan', - - queryParam: 'server', - - valueField: 'path', - displayField: 'path', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.nfsServer) { - me.store.removeAll(); - } - - me.allQuery = me.nfsServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.nfsServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'path', 'options' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/nfs' - } - }); - - store.sort('path', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.NFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_nfs', - - options : [], - - onGetValues: function(values) { - var me = this; - - var i; - var res = []; - for (i = 0; i < me.options.length; i++) { - var item = me.options[i]; - if (!item.match(/^vers=(.*)$/)) { - res.push(item); - } - } - if (values.nfsversion && values.nfsversion !== '__default__') { - res.push('vers=' + values.nfsversion); - } - delete values.nfsversion; - values.options = res.join(','); - if (values.options === '') { - delete values.options; - if (!me.isCreate) { - values["delete"] = "options"; - } - } - - return me.callParent([values]); - }, - - setValues: function(values) { - var me = this; - if (values.options) { - var res = values.options; - me.options = values.options.split(','); - me.options.forEach(function(item) { - var match = item.match(/^vers=(.*)$/); - if (match) { - values.nfsversion = match[1]; - } - }); - } - return me.callParent([values]); - }, - - initComponent : function() { - var me = this; - - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=export]'); - exportField.setServer(value); - exportField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'pveNFSScan' : 'displayfield', - name: 'export', - value: '', - fieldLabel: 'Export', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('NFS Version'), - name: 'nfsversion', - value: '__default__', - deleteEmpty: false, - comboItems: [ - ['__default__', Proxmox.Utils.defaultText], - ['3', '3'], - ['4', '4'], - ['4.1', '4.1'], - ['4.2', '4.2'] - ] - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.CIFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCIFSScan', - - queryParam: 'server', - - valueField: 'share', - displayField: 'share', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.cifsServer) { - me.store.removeAll(); - } - - var params = {}; - if (me.cifsUsername && me.cifsPassword) { - params.username = me.cifsUsername; - params.password = me.cifsPassword; - } - - if (me.cifsDomain) { - params.domain = me.cifsDomain; - } - - me.store.getProxy().setExtraParams(params); - me.allQuery = me.cifsServer; - - me.callParent(); - }, - - setServer: function(server) { - this.cifsServer = server; - }, - - setUsername: function(username) { - this.cifsUsername = username; - }, - - setPassword: function(password) { - this.cifsPassword = password; - }, - - setDomain: function(domain) { - this.cifsDomain = domain; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['description', 'share'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/cifs' - } - }); - store.sort('share', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.CIFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_cifs', - - initComponent : function() { - var me = this; - - var passwordfield = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - inputType: 'password', - name: 'password', - value: me.isCreate ? '' : '********', - fieldLabel: gettext('Password'), - allowBlank: false, - disabled: me.isCreate, - minLength: 1, - listeners: { - change: function(f, value) { - - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setPassword(value); - } - } - } - }); - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setServer(value); - } - } - } - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: '', - fieldLabel: gettext('Username'), - emptyText: gettext('Guest user'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (!me.isCreate) { - return; - } - var exportField = me.down('field[name=share]'); - exportField.setUsername(value); - - if (value == "") { - passwordfield.disable(); - } else { - passwordfield.enable(); - } - passwordfield.validate(); - } - } - }, - passwordfield, - { - xtype: me.isCreate ? 'pveCIFSScan' : 'displayfield', - name: 'share', - value: '', - fieldLabel: 'Share', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'domain', - value: me.isCreate ? '' : undefined, - fieldLabel: gettext('Domain'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (me.isCreate) { - - var exportField = me.down('field[name=share]'); - exportField.setDomain(value); - } - } - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.GlusterFsScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveGlusterFsScan', - - queryParam: 'server', - - valueField: 'volname', - displayField: 'volname', - matchFieldWidth: false, - listConfig: { - loadingText: 'Scanning...', - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.glusterServer) { - me.store.removeAll(); - } - - me.allQuery = me.glusterServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.glusterServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'volname' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/glusterfs' - } - }); - - store.sort('volname', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.GlusterFsInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_glusterfs', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var volumeField = me.down('field[name=volume]'); - volumeField.setServer(value); - volumeField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'server2', - value: '', - fieldLabel: gettext('Second Server'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'pveGlusterFsScan' : 'displayfield', - name: 'volume', - value: '', - fieldLabel: 'Volume name', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'iso', 'backup', 'vztmpl', 'snippets'], - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.IScsiScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveIScsiScan', - - queryParam: 'portal', - valueField: 'target', - displayField: 'target', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.portal) { - me.store.removeAll(); - } - - me.allQuery = me.portal; - - me.callParent(); - }, - - setPortal: function(portal) { - var me = this; - - me.portal = portal; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'target', 'portal' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/iscsi' - } - }); - - store.sort('target', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.IScsiInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_open_iscsi', - - onGetValues: function(values) { - var me = this; - - values.content = values.luns ? 'images' : 'none'; - delete values.luns; - - return me.callParent([values]); - }, - - setValues: function(values) { - values.luns = (values.content.indexOf('images') !== -1) ? true : false; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: 'Portal', - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=target]'); - exportField.setPortal(value); - exportField.setValue(''); - } - } - } - }, - { - readOnly: !me.isCreate, - xtype: me.isCreate ? 'pveIScsiScan' : 'displayfield', - name: 'target', - value: '', - fieldLabel: 'Target', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'checkbox', - name: 'luns', - checked: true, - fieldLabel: gettext('Use LUNs directly') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.VgSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveVgSelector', - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'vg', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - store.sort('vg', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseStorageSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseStorageSelector', - - existingGroupsText: gettext("Existing volume groups"), - queryMode: 'local', - editable: false, - value: '', - valueField: 'storage', - displayField: 'text', - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: { - addRecords: true, - params: { - type: 'iscsi' - } - }, - fields: [ 'storage', 'type', 'content', - { - name: 'text', - convert: function(value, record) { - if (record.data.storage) { - return record.data.storage + " (iSCSI)"; - } else { - return me.existingGroupsText; - } - } - }], - proxy: { - type: 'proxmox', - url: '/api2/json/storage/' - } - }); - - store.loadData([{ storage: '' }], true); - - store.sort('storage', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LVMInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvm', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.VgSelector', { - name: 'vgname', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var baseField = Ext.createWidget('pveFileSelector', { - name: 'base', - hidden: true, - disabled: true, - nodename: 'localhost', - storageContent: 'images', - fieldLabel: gettext('Base volume'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseStorageSelector', - name: 'basesel', - fieldLabel: gettext('Base storage'), - submitValue: false, - listeners: { - change: function(f, value) { - if (value) { - vgnameField.setVisible(true); - vgnameField.setDisabled(false); - vgField.setVisible(false); - vgField.setDisabled(true); - baseField.setVisible(true); - baseField.setDisabled(false); - } else { - vgnameField.setVisible(false); - vgnameField.setDisabled(true); - vgField.setVisible(true); - vgField.setDisabled(false); - baseField.setVisible(false); - baseField.setDisabled(true); - } - baseField.setStorage(value); - } - } - }); - - me.column1.push(baseField); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.TPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveTPSelector', - - queryParam: 'vg', - valueField: 'lv', - displayField: 'lv', - editable: false, - - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.vg) { - me.store.removeAll(); - } - - me.allQuery = me.vg; - - me.callParent(); - }, - - setVG: function(myvg) { - var me = this; - - me.vg = myvg; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'lv' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvmthin' - } - }); - - store.sort('lv', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseVGSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseVGSelector', - - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, - fields: [ 'vg', 'size', 'free'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LvmThinInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvmthin', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var thinpoolField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'thinpool', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.TPoolSelector', { - name: 'thinpool', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseVGSelector', - name: 'vgname', - fieldLabel: gettext('Volume group'), - listeners: { - change: function(f, value) { - if (me.isCreate) { - vgField.setVG(value); - vgField.setValue(''); - } - } - } - }); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - me.column1.push(thinpoolField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = []; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.CephFSInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'storage_cephfs', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'cephfs'; - - me.column1 = []; - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - value: '', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: 'admin', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['backup', 'iso', 'vztmpl', 'snippets'], - fieldLabel: gettext('Content'), - name: 'content', - value: 'backup', - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged cephFS') - }]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.Ceph.Model', { - extend: 'Ext.app.ViewModel', - alias: 'viewmodel.cephstorage', - - data: { - pveceph: true, - pvecephPossible: true - } -}); - -Ext.define('PVE.storage.Ceph.Controller', { - extend: 'PVE.controller.StorageEdit', - alias: 'controller.cephstorage', - - control: { - '#': { - afterrender: 'queryMonitors' - }, - 'textfield[name=username]': { - disable: 'resetField' - }, - 'displayfield[name=monhost]': { - enable: 'queryMonitors' - }, - 'textfield[name=monhost]': { - disable: 'resetField', - enable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - queryMonitors: function(field, newVal, oldVal) { - // we get called with two signatures, the above one for a field - // change event and the afterrender from the view, this check only - // can be true for the field change one and omit the API request if - // pveceph got unchecked - as it's not needed there. - if (field && !newVal && oldVal) { - return; - } - var view = this.getView(); - var vm = this.getViewModel(); - if (!(view.isCreate || vm.get('pveceph'))) { - return; // only query on create or if editing a pveceph store - } - - var monhostField = this.lookupReference('monhost'); - - Proxmox.Utils.API2Request({ - url: '/api2/json/nodes/localhost/ceph/mon', - method: 'GET', - scope: this, - callback: function(options, success, response) { - var data = response.result.data; - if (response.status === 200) { - if (data.length > 0) { - var monhost = Ext.Array.pluck(data, 'name').sort().join(','); - monhostField.setValue(monhost); - monhostField.resetOriginalValue(); - if (view.isCreate) { - vm.set('pvecephPossible', true); - } - } else { - vm.set('pveceph', false); - } - } else { - vm.set('pveceph', false); - vm.set('pvecephPossible', false); - } - } - }); - } -}); - -Ext.define('PVE.storage.RBDInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'ceph_rados_block_devices', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'rbd'; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push({ - xtype: 'pveCephPoolSelector', - nodename: me.nodename, - name: 'pool', - bind: { - disabled: '{!pveceph}', - submitValue: '{pveceph}', - hidden: '{!pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - },{ - xtype: 'textfield', - name: 'pool', - value: 'rbd', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } else { - me.column1.push({ - xtype: 'displayfield', - nodename: me.nodename, - name: 'pool', - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - value: 'admin', - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images'], - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'krbd', - uncheckedValue: 0, - fieldLabel: 'KRBD' - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged ceph pool') - }]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.ZFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - viewModel: { - parent: null, - data: { - isLIO: false, - isComstar: true, - hasWriteCacheOption: true - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[name=iscsiprovider]': { - change: 'changeISCSIProvider' - } - }, - changeISCSIProvider: function(f, newVal, oldVal) { - var vm = this.getViewModel(); - vm.set('isLIO', newVal === 'LIO'); - vm.set('isComstar', newVal === 'comstar'); - vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt'); - } - }, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.content = 'images'; - } - - values.nowritecache = values.writecache ? 0 : 1; - delete values.writecache; - - return me.callParent([values]); - }, - - setValues: function diff(values) { - values.writecache = values.nowritecache ? 0 : 1; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: gettext('Portal'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'pool', - value: '', - fieldLabel: gettext('Pool'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'blocksize', - value: '4k', - fieldLabel: gettext('Block Size'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'target', - value: '', - fieldLabel: gettext('Target'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_tg', - value: '', - fieldLabel: gettext('Target group'), - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - allowBlank: true - } - ]; - - me.column2 = [ - { - xtype: me.isCreate ? 'pveiScsiProviderSelector' : 'displayfield', - name: 'iscsiprovider', - value: 'comstar', - fieldLabel: gettext('iSCSI Provider'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'proxmoxcheckbox', - name: 'writecache', - checked: true, - bind: me.isCreate ? { disabled: '{!hasWriteCacheOption}' } : { hidden: '{!hasWriteCacheOption}' }, - uncheckedValue: 0, - fieldLabel: gettext('Write cache') - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_hg', - value: '', - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - fieldLabel: gettext('Host group'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'lio_tpg', - value: '', - bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' }, - allowBlank: false, - fieldLabel: gettext('Target portal group') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.ZFSPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveZFSPoolSelector', - valueField: 'pool', - displayField: 'pool', - queryMode: 'local', - editable: false, - listConfig: { - loadingText: gettext('Scanning...') - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'pool', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/zfs' - } - }); - - store.sort('pool', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ZFSPoolInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_zfspool', - - initComponent : function() { - var me = this; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push(Ext.create('PVE.storage.ZFSPoolSelector', { - name: 'pool', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } else { - me.column1.push(Ext.createWidget('displayfield', { - name: 'pool', - value: '', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } - - // value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push( - {xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'textfield', - name: 'blocksize', - emptyText: '8k', - fieldLabel: gettext('Block Size'), - allowBlank: true - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.ha.StatusView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAStatusView'], - - onlineHelp: 'chapter_ha_manager', - - sortPriority: { - quorum: 1, - master: 2, - lrm: 3, - service: 4 - }, - - initComponent : function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sortAfterUpdate: true, - sorters: [{ - sorterFn: function(rec1, rec2) { - var p1 = me.sortPriority[rec1.data.type]; - var p2 = me.sortPriority[rec2.data.type]; - return (p1 !== p2) ? ((p1 > p2) ? 1 : -1) : 0; - } - }], - filters: { - property: 'type', - value: 'service', - operator: '!=' - } - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Status'), - width: 80, - flex: 1, - dataIndex: 'status' - } - ] - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - } -}, function() { - - Ext.define('pve-ha-status', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'type', 'node', 'status', 'sid', - 'state', 'group', 'comment', - 'max_restart', 'max_relocate', 'type', - 'crm_state', 'request_state' - ], - idProperty: 'id' - }); - -}); -Ext.define('PVE.ha.Status', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveHAStatus', - - onlineHelp: 'chapter_ha_manager', - layout: { - type: 'vbox', - align: 'stretch' - }, - - initComponent: function() { - var me = this; - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - interval: me.interval, - model: 'pve-ha-status', - storeid: 'pve-store-' + (++Ext.idSeed), - groupField: 'type', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/ha/status/current' - } - }); - - me.items = [{ - xtype: 'pveHAStatusView', - title: gettext('Status'), - rstore: me.rstore, - border: 0, - collapsible: true, - padding: '0 0 20 0' - },{ - xtype: 'pveHAResourcesView', - flex: 1, - collapsible: true, - title: gettext('Resources'), - border: 0, - rstore: me.rstore - }]; - - me.callParent(); - me.on('activate', me.rstore.startUpdate); - } -}); -Ext.define('PVE.ha.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveHAGroupSelector'], - - value: [], - autoSelect: false, - valueField: 'group', - displayField: 'group', - listConfig: { - columns: [ - { - header: gettext('Group'), - width: 100, - sortable: true, - dataIndex: 'group' - }, - { - header: gettext('Nodes'), - width: 100, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode - } - ] - }, - store: { - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }, - - initComponent: function() { - var me = this; - me.callParent(); - me.getStore().load(); - } - -}, function() { - - Ext.define('pve-ha-groups', { - extend: 'Ext.data.Model', - fields: [ - 'group', 'type', 'digest', 'nodes', 'comment', - { - name : 'restricted', - type: 'boolean' - }, - { - name : 'nofailback', - type: 'boolean' - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/ha/groups" - }, - idProperty: 'group' - }); -}); -Ext.define('PVE.ha.VMResourceInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_resource_config', - vmid: undefined, - - onGetValues: function(values) { - var me = this; - - if (values.vmid) { - values.sid = values.vmid; - } - delete values.vmid; - - PVE.Utils.delete_if_default(values, 'group', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_restart', '1', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_relocate', '1', me.isCreate); - - return values; - }, - - initComponent : function() { - var me = this; - var MIN_QUORUM_VOTES = 3; - - var disabledHint = Ext.createWidget({ - xtype: 'displayfield', // won't get submitted by default - userCls: 'pve-hint', - value: 'Disabling the resource will stop the guest system. ' + - 'See the online help for details.', - hidden: true - }); - - var fewVotesHint = Ext.createWidget({ - itemId: 'fewVotesHint', - xtype: 'displayfield', - userCls: 'pve-hint', - value: 'At least three quorum votes are recommended for reliable HA.', - hidden: true - }); - - Proxmox.Utils.API2Request({ - url: '/cluster/config/nodes', - method: 'GET', - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var nodes = response.result.data; - var votes = 0; - Ext.Array.forEach(nodes, function(node) { - var vote = parseInt(node.quorum_votes, 10); // parse as base 10 - votes += vote || 0; // parseInt might return NaN, which is false - }); - - if (votes < MIN_QUORUM_VOTES) { - fewVotesHint.setVisible(true); - } - } - }); - - /*jslint confusion: true */ - var vmidStore = (me.vmid) ? {} : { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [ - { - property: 'type', - value: /lxc|qemu/ - }, - { - property: 'hastate', - value: /unmanaged/ - } - ] - }; - - // value is a string above, but a number below - me.column1 = [ - { - xtype: me.vmid ? 'displayfield' : 'vmComboSelector', - submitValue: me.isCreate, - name: 'vmid', - fieldLabel: (me.vmid && me.guestType === 'ct') ? 'CT' : 'VM', - value: me.vmid, - store: vmidStore, - validateExists: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_restart', - fieldLabel: gettext('Max. Restart'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_relocate', - fieldLabel: gettext('Max. Relocate'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - } - ]; - /*jslint confusion: false */ - - me.column2 = [ - { - xtype: 'pveHAGroupSelector', - name: 'group', - fieldLabel: gettext('Group') - }, - { - xtype: 'proxmoxKVComboBox', - name: 'state', - value: 'started', - fieldLabel: gettext('Request State'), - comboItems: [ - ['started', 'started'], - ['stopped', 'stopped'], - ['ignored', 'ignored'], - ['disabled', 'disabled'] - ], - listeners: { - 'change': function(field, newValue) { - if (newValue === 'disabled') { - disabledHint.setVisible(true); - } - else { - if (disabledHint.isVisible()) { - disabledHint.setVisible(false); - } - } - } - } - }, - disabledHint - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - fewVotesHint - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.VMResourceEdit', { - extend: 'Proxmox.window.Edit', - - vmid: undefined, - guestType: undefined, - isCreate: undefined, - - initComponent : function() { - var me = this; - - if (me.isCreate === undefined) { - me.isCreate = !me.vmid; - } - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/resources'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/resources/' + me.vmid; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.VMResourceInputPanel', { - isCreate: me.isCreate, - vmid: me.vmid, - guestType: me.guestType - }); - - Ext.apply(me, { - subject: gettext('Resource') + ': ' + gettext('Container') + - '/' + gettext('Virtual Machine'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(values.sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - throw "got unexpected resource type"; - } - - values.vmid = res[2]; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.ResourcesView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAResourcesView'], - - onlineHelp: 'ha_manager_resources', - - stateful: true, - stateId: 'grid-ha-resources', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!me.rstore) { - throw "no store given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - filters: { - property: 'type', - value: 'service' - } - }); - - var reload = function() { - me.rstore.load(); - }; - - var render_error = function(dataIndex, value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors[dataIndex]; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - var sid = rec.data.sid; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - return; - } - var guestType = res[1]; - var vmid = res[2]; - - var win = Ext.create('PVE.ha.VMResourceEdit',{ - guestType: guestType, - vmid: vmid - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/resources/', - getUrl: function(rec) { - var me = this; - return me.baseurl + '/' + rec.get('sid'); - }, - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.VMResourceEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - - columns: [ - { - header: 'ID', - width: 100, - sortable: true, - dataIndex: 'sid' - }, - { - header: gettext('State'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Request State'), - width: 100, - hidden: true, - sortable: true, - renderer: function(v) { - return v || 'started'; - }, - dataIndex: 'request_state' - }, - { - header: gettext('CRM State'), - width: 100, - hidden: true, - sortable: true, - dataIndex: 'crm_state' - }, - { - header: gettext('Max. Restart'), - width: 100, - sortable: true, - renderer: (v) => v === undefined ? '1' : v, - dataIndex: 'max_restart' - }, - { - header: gettext('Max. Relocate'), - width: 100, - sortable: true, - renderer: (v) => v === undefined ? '1' : v, - dataIndex: 'max_relocate' - }, - { - header: gettext('Group'), - width: 200, - sortable: true, - renderer: function(value, metaData, record) { - return render_error('group', value, metaData, record); - }, - dataIndex: 'group' - }, - { - header: gettext('Description'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-ha-resources', { - extend: 'Ext.data.Model', - fields: [ - 'sid', 'state', 'digest', 'errors', 'group', 'comment', - 'max_restart', 'max_relocate', 'type', 'status', 'node', - 'crm_state', 'request_state' - ], - idProperty: 'sid' - }); - -}); -Ext.define('PVE.ha.GroupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_groups', - - groupId: undefined, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = 'group'; - } - - return values; - }, - - initComponent : function() { - var me = this; - - var update_nodefield, update_node_selection; - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - update_nodefield(selected); - } - } - }); - - // use already cached data to avoid an API call - var data = PVE.data.ResourceStore.getNodes(); - - var store = Ext.create('Ext.data.Store', { - fields: [ 'node', 'mem', 'cpu', 'priority' ], - data: data, - proxy: { - type: 'memory', - reader: {type: 'json'} - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - } - ] - }); - - var nodegrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - columns: [ - { - header: gettext('Node'), - flex: 1, - dataIndex: 'node' - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 150, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 150, - dataIndex: 'cpu' - }, - { - header: 'Priority', - xtype: 'widgetcolumn', - dataIndex: 'priority', - sortable: true, - stopSelection: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 0, - maxValue: 1000, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('priority', value); - update_nodefield(sm.getSelection()); - } - } - } - } - ] - }); - - var nodefield = Ext.create('Ext.form.field.Hidden', { - name: 'nodes', - value: '', - listeners: { - change: function (nodefield, value) { - update_node_selection(value); - } - }, - isValid: function () { - var value = nodefield.getValue(); - return (value && 0 !== value.length); - } - }); - - update_node_selection = function(string) { - sm.deselectAll(true); - - string.split(',').forEach(function (e, idx, array) { - var res = e.split(':'); - - store.each(function(record) { - var node = record.get('node'); - - if (node == res[0]) { - sm.select(record, true); - record.set('priority', res[1]); - record.commit(); - } - }); - }); - nodegrid.reconfigure(store); - - }; - - update_nodefield = function(selected) { - var nodes = ''; - var first_iteration = true; - Ext.Array.each(selected, function(record) { - if (!first_iteration) { - nodes += ','; - } - first_iteration = false; - - nodes += record.data.node; - if (record.data.priority) { - nodes += ':' + record.data.priority; - } - }); - - // nodefield change listener calls us again, which results in a - // endless recursion, suspend the event temporary to avoid this - nodefield.suspendEvent('change'); - nodefield.setValue(nodes); - nodefield.resumeEvent('change'); - }; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'group', - value: me.groupId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }, - nodefield - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'restricted', - uncheckedValue: 0, - fieldLabel: 'restricted' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nofailback', - uncheckedValue: 0, - fieldLabel: 'nofailback' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - nodegrid - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.GroupEdit', { - extend: 'Proxmox.window.Edit', - - groupId: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupId; - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/groups'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/groups/' + me.groupId; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.GroupInputPanel', { - isCreate: me.isCreate, - groupId: me.groupId - }); - - Ext.apply(me, { - subject: gettext('HA Group'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.GroupsView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAGroupsView'], - - onlineHelp: 'ha_manager_groups', - - stateful: true, - stateId: 'grid-ha-groups', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.ha.GroupEdit',{ - groupId: rec.data.group - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/groups/', - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Create'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.GroupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - columns: [ - { - header: gettext('Group'), - width: 150, - sortable: true, - dataIndex: 'group' - }, - { - header: 'restricted', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'restricted' - }, - { - header: 'nofailback', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'nofailback' - }, - { - header: gettext('Nodes'), - flex: 1, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - activate: reload, - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.ha.FencingView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveFencingView'], - - onlineHelp: 'ha_manager_fencing', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-ha-fencing', - data: [] - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false, - deferEmptyText: false, - emptyText: 'Use watchdog based fencing.' - }, - columns: [ - { - header: 'Node', - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Command'), - flex: 1, - dataIndex: 'command' - } - ] - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-ha-fencing', { - extend: 'Ext.data.Model', - fields: [ - 'node', 'command', 'digest' - ] - }); - -}); -Ext.define('PVE.dc.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSummary', - - scrollable: true, - - bodyPadding: 5, - - layout: 'column', - - defaults: { - padding: 5, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1 - }, - 'width >= 1900': { - columnWidth: 0.5 - } - } - }, - - items: [ - { - itemId: 'dcHealth', - xtype: 'pveDcHealth' - }, - { - itemId: 'dcGuests', - xtype: 'pveDcGuests' - }, - { - title: gettext('Resources'), - xtype: 'panel', - minHeight: 250, - bodyPadding: 5, - layout: 'hbox', - defaults: { - xtype: 'proxmoxGauge', - flex: 1 - }, - items:[ - { - title: gettext('CPU'), - itemId: 'cpu' - }, - { - title: gettext('Memory'), - itemId: 'memory' - }, - { - title: gettext('Storage'), - itemId: 'storage' - } - ] - }, - { - itemId: 'nodeview', - xtype: 'pveDcNodeView', - height: 250 - }, - { - title: gettext('Subscriptions'), - height: 220, - items: [ - { - itemId: 'subscriptions', - xtype: 'pveHealthWidget', - userCls: 'pointer', - listeners: { - element: 'el', - click: function() { - if (this.component.userCls === 'pointer') { - window.open('https://www.proxmox.com/en/proxmox-ve/pricing', '_blank'); - } - } - } - } - ] - } - ], - - initComponent: function() { - var me = this; - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-status', - model: 'pve-dc-nodes', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/status" - } - }); - - var gridstore = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - filters: { - property: 'type', - value: 'node' - }, - sorters: { - property: 'id', - direction: 'ASC' - } - }); - - me.callParent(); - - me.getComponent('nodeview').setStore(gridstore); - - var gueststatus = me.getComponent('dcGuests'); - - var cpustat = me.down('#cpu'); - var memorystat = me.down('#memory'); - var storagestat = me.down('#storage'); - var sp = Ext.state.Manager.getProvider(); - - me.mon(PVE.data.ResourceStore, 'load', function(curstore, results) { - me.suspendLayout = true; - - var cpu = 0; - var maxcpu = 0; - - var nodes = 0; - - var memory = 0; - var maxmem = 0; - - var countedStorages = {}; - var used = 0; - var total = 0; - var usableStorages = {}; - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - if (storage !== '') { - usableStorages[storage] = true; - } - }); - - var qemu = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var lxc = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var error = 0; - - var i; - - for (i = 0; i < results.length; i++) { - var item = results[i]; - switch(item.data.type) { - case 'node': - cpu += (item.data.cpu * item.data.maxcpu); - maxcpu += item.data.maxcpu || 0; - memory += item.data.mem || 0; - maxmem += item.data.maxmem || 0; - nodes++; - - // update grid also - var griditem = gridstore.getById(item.data.id); - if (griditem) { - griditem.set('cpuusage', item.data.cpu); - var max = item.data.maxmem || 1; - var val = item.data.mem || 0; - griditem.set('memoryusage', val/max); - griditem.set('uptime', item.data.uptime); - griditem.commit(); //else it marks the fields as dirty - } - break; - case 'storage': - if (!Ext.Object.isEmpty(usableStorages)) { - if (usableStorages[item.data.id] === true) { - used += item.data.disk; - total += item.data.maxdisk; - } - break; - } - if (!countedStorages[item.data.storage] || - (item.data.storage === 'local' && - !countedStorages[item.data.id])) { - used += item.data.disk; - total += item.data.maxdisk; - - countedStorages[item.data.storage === 'local'?item.data.id:item.data.storage] = true; - } - break; - case 'qemu': - qemu[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - case 'lxc': - lxc[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - default: break; - } - } - - var text = Ext.String.format(gettext('of {0} CPU(s)'), maxcpu); - cpustat.updateValue((cpu/maxcpu), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(memory), PVE.Utils.render_size(maxmem)); - memorystat.updateValue((memory/maxmem), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(used), PVE.Utils.render_size(total)); - storagestat.updateValue((used/total), text); - - gueststatus.updateValues(qemu,lxc,error); - - me.suspendLayout = false; - me.updateLayout(true); - }); - - var dcHealth = me.getComponent('dcHealth'); - me.mon(rstore, 'load', dcHealth.updateStatus, dcHealth); - - var subs = me.down('#subscriptions'); - me.mon(rstore, 'load', function(store, records, success) { - var i; - var level; - var mixed = false; - for (i = 0; i < records.length; i++) { - if (records[i].get('type') !== 'node') { - continue; - } - var node = records[i]; - if (node.get('status') === 'offline') { - continue; - } - - var curlevel = node.get('level'); - - if (curlevel === '') { // no subscription trumps all, set and break - level = ''; - break; - } - - if (level === undefined) { // save level - level = curlevel; - } else if (level !== curlevel) { // detect different levels - mixed = true; - } - } - - var data = { - title: Proxmox.Utils.unknownText, - text: Proxmox.Utils.unknownText, - iconCls: PVE.Utils.get_health_icon(undefined, true) - }; - if (level === '') { - data = { - title: gettext('No Subscription'), - iconCls: PVE.Utils.get_health_icon('critical', true), - text: gettext('You have at least one node without subscription.') - }; - subs.setUserCls('pointer'); - } else if (mixed) { - data = { - title: gettext('Mixed Subscriptions'), - iconCls: PVE.Utils.get_health_icon('warning', true), - text: gettext('Warning: Your subscription levels are not the same.') - }; - subs.setUserCls('pointer'); - } else if (level) { - data = { - title: PVE.Utils.render_support_level(level), - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext('Your subscription status is valid.') - }; - subs.setUserCls(''); - } - - subs.setData(data); - }); - - me.on('destroy', function(){ - rstore.stopUpdate(); - }); - - rstore.startUpdate(); - } - -}); -Ext.define('PVE.window.ReplicaEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveReplicaEdit', - - subject: gettext('Replication Job'), - - - url: '/cluster/replication', - method: 'POST', - - initComponent: function() { - var me = this; - - var vmid = me.pveSelNode.data.vmid; - var nodename = me.pveSelNode.data.node; - - var items = []; - - items.push({ - xtype: (me.isCreate && !vmid)?'pveGuestIDSelector':'displayfield', - name: 'guest', - fieldLabel: 'CT/VM ID', - value: vmid || '' - }); - - items.push( - { - xtype: me.isCreate ? 'pveNodeSelector':'displayfield', - name: 'target', - disallowedNodes: [nodename], - allowBlank: false, - onlineValidator: true, - fieldLabel: gettext("Target") - }, - { - xtype: 'pveCalendarEvent', - fieldLabel: gettext('Schedule'), - emptyText: '*/15 - ' + Ext.String.format(gettext('Every {0} minutes'), 15), - name: 'schedule' - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - step: 1, - minValue: 1, - emptyText: gettext('unlimited'), - name: 'rate' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment' - }, - { - xtype: 'proxmoxcheckbox', - name: 'enabled', - defaultValue: 'on', - checked: true, - fieldLabel: gettext('Enabled') - } - ); - - me.items = [ - { - xtype: 'inputpanel', - itemId: 'ipanel', - onlineHelp: 'pvesr_schedule_time_format', - - onGetValues: function(values) { - var me = this.up('window'); - - values.disable = values.enabled ? 0 : 1; - delete values.enabled; - - PVE.Utils.delete_if_default(values, 'rate', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'disable', 0, me.isCreate); - PVE.Utils.delete_if_default(values, 'schedule', '*/15', me.isCreate); - PVE.Utils.delete_if_default(values, 'comment', '', me.isCreate); - - if (me.isCreate) { - values.type = 'local'; - var vm = vmid || values.guest; - var id = -1; - if (me.highestids[vm] !== undefined) { - id = me.highestids[vm]; - } - id++; - values.id = vm + '-' + id.toString(); - delete values.guest; - } - return values; - }, - items: items - } - ]; - - me.callParent(); - - if (me.isCreate) { - me.load({ - success: function(response) { - var jobs = response.result.data; - var highestids = {}; - Ext.Array.forEach(jobs, function(job) { - var match = /^([0-9]+)\-([0-9]+)$/.exec(job.id); - if (match) { - var vmid = parseInt(match[1],10); - var id = parseInt(match[2],10); - if (highestids[vmid] < id || - highestids[vmid] === undefined) { - highestids[vmid] = id; - } - } - }); - - me.highestids = highestids; - } - }); - - } else { - me.load({ - success: function(response, options) { - response.result.data.enabled = !response.result.data.disable; - me.setValues(response.result.data); - me.digest = response.result.data.digest; - } - }); - } - } -}); - -/*jslint confusion: true */ -/* callback is a function and string */ -Ext.define('PVE.grid.ReplicaView', { - extend: 'Ext.grid.Panel', - xtype: 'pveReplicaView', - - onlineHelp: 'chapter_pvesr', - - stateful: true, - stateId: 'grid-pve-replication-status', - - controller: { - xclass: 'Ext.app.ViewController', - - addJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var win = Ext.create('PVE.window.ReplicaEdit', { - isCreate: true, - method: 'POST', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - editJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var data = rec.data; - var win = Ext.create('PVE.window.ReplicaEdit', { - url: '/cluster/replication/' + data.id, - method: 'PUT', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - scheduleJobNow: function(button,event,rec) { - var me = this.getView(); - var controller = this; - - Proxmox.Utils.API2Request({ - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/schedule_now", - method: 'POST', - waitMsgTarget: me, - callback: function() { controller.reload(); }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - showLog: function(button, event, rec) { - var me = this.getView(); - var controller = this; - var logView = Ext.create('Proxmox.panel.LogView', { - border: false, - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/log" - }); - var win = Ext.create('Ext.window.Window', { - items: [ logView ], - layout: 'fit', - width: 800, - height: 400, - modal: true, - title: gettext("Replication Log") - }); - var task = { - run: function() { - logView.requestUpdate(); - }, - interval: 1000 - }; - Ext.TaskManager.start(task); - win.on('destroy', function() { - Ext.TaskManager.stop(task); - controller.reload(); - }); - win.show(); - }, - - reload: function() { - var me = this.getView(); - me.rstore.load(); - }, - - dblClick: function(grid, record, item) { - var me = this; - me.editJob(undefined, undefined, record); - }, - - // check for cluster - // currently replication is for cluster only, so we disable the whole - // component - checkPrerequisites: function() { - var me = this.getView(); - if (PVE.data.ResourceStore.getNodes().length < 2) { - me.mask(gettext("Replication needs at least two nodes"), ['pve-static-mask']); - } - }, - - control: { - '#': { - itemdblclick: 'dblClick', - afterlayout: 'checkPrerequisites' - } - } - }, - - tbar: [ - { - text: gettext('Add'), - itemId: 'addButton', - handler: 'addJob' - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - itemId: 'editButton', - handler: 'editJob', - disabled: true - }, - { - xtype: 'proxmoxStdRemoveButton', - itemId: 'removeButton', - baseurl: '/api2/extjs/cluster/replication/', - dangerous: true, - callback: 'reload' - }, - { - xtype: 'proxmoxButton', - text: gettext('Log'), - itemId: 'logButton', - handler: 'showLog', - disabled: true - }, - { - xtype: 'proxmoxButton', - text: gettext('Schedule now'), - itemId: 'scheduleNowButton', - handler: 'scheduleJobNow', - disabled: true - } - ], - - initComponent: function() { - var me = this; - var mode = ''; - var url = '/cluster/replication'; - - me.nodename = me.pveSelNode.data.node; - me.vmid = me.pveSelNode.data.vmid; - - me.columns = [ - { - text: gettext('Enabled'), - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true - }, - { - text: 'ID', - dataIndex: 'id', - width: 60, - hidden: true - }, - { - text: gettext('Guest'), - dataIndex: 'guest', - width: 75 - }, - { - text: gettext('Job'), - dataIndex: 'jobnum', - width: 60 - }, - { - text: gettext('Target'), - dataIndex: 'target' - } - ]; - - if (!me.nodename) { - mode = 'dc'; - me.stateId = 'grid-pve-replication-dc'; - } else if (!me.vmid) { - mode = 'node'; - url = '/nodes/' + me.nodename + '/replication'; - } else { - mode = 'vm'; - url = '/nodes/' + me.nodename + '/replication' + '?guest=' + me.vmid; - } - - if (mode !== 'dc') { - me.columns.push( - { - text: gettext('Status'), - dataIndex: 'state', - minWidth: 160, - flex: 1, - renderer: function(value, metadata, record) { - - if (record.data.pid) { - metadata.tdCls = 'x-grid-row-loading'; - return ''; - } - - var icons = []; - var states = []; - - if (record.data.remove_job) { - icons.push(''); - states.push(gettext("Removal Scheduled")); - } - - if (record.data.error) { - icons.push(''); - states.push(record.data.error); - } - - if (icons.length == 0) { - icons.push(''); - states.push(gettext('OK')); - } - - return icons.join(',') + ' ' + states.join(','); - } - }, - { - text: gettext('Last Sync'), - dataIndex: 'last_sync', - width: 150, - renderer: function(value, metadata, record) { - if (!value) { - return '-'; - } - - if (record.data.pid) { - return gettext('syncing'); - } - - return Proxmox.Utils.render_timestamp(value); - } - }, - { - text: gettext('Duration'), - dataIndex: 'duration', - width: 60, - renderer: PVE.Utils.render_duration - }, - { - text: gettext('Next Sync'), - dataIndex: 'next_sync', - width: 150, - renderer: function(value) { - if (!value) { - return '-'; - } - - var now = new Date(); - var next = new Date(value*1000); - - if (next < now) { - return gettext('pending'); - } - - return Proxmox.Utils.render_timestamp(value); - } - } - ); - } - - me.columns.push( - { - text: gettext('Schedule'), - width: 75, - dataIndex: 'schedule' - }, - { - text: gettext('Rate limit'), - dataIndex: 'rate', - renderer: function(value) { - if (!value) { - return gettext('unlimited'); - } - - return value.toString() + ' MB/s'; - }, - hidden: true - }, - { - text: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.htmlEncode - } - ); - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-replica-' + me.nodename + me.vmid, - model: (mode === 'dc')? 'pve-replication' : 'pve-replication-state', - interval: 3000, - proxy: { - type: 'proxmox', - url: "/api2/json" + url - } - }); - - me.store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sorters: [ - { - property: 'guest' - }, - { - property: 'jobnum' - } - ] - }); - - me.callParent(); - - // we cannot access the log and scheduleNow button - // in the datacenter, because - // we do not know where/if the jobs runs - if (mode === 'dc') { - me.down('#logButton').setHidden(true); - me.down('#scheduleNowButton').setHidden(true); - } - - // if we set the warning mask, we do not want to load - // or set the mask on store errors - if (PVE.data.ResourceStore.getNodes().length < 2) { - return; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.on('destroy', me.rstore.stopUpdate); - me.rstore.startUpdate(); - } -}, function() { - - Ext.define('pve-replication', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'target', 'comment', 'rate', 'type', - { name: 'guest', type: 'integer' }, - { name: 'jobnum', type: 'integer' }, - { name: 'schedule', defaultValue: '*/15' }, - { name: 'disable', defaultValue: '' }, - { name: 'enabled', calculate: function(data) { return !data.disable; } } - ] - }); - - Ext.define('pve-replication-state', { - extend: 'pve-replication', - fields: [ - 'last_sync', 'next_sync', 'error', 'duration', 'state', - 'fail_count', 'remove_job', 'pid' - ] - }); - -}); -Ext.define('PVE.dc.Health', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcHealth', - - title: gettext('Health'), - - bodyPadding: 10, - height: 220, - layout: { - type: 'hbox', - align: 'stretch' - }, - - defaults: { - flex: 1, - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - nodeList: [], - nodeIndex: 0, - - updateStatus: function(store, records, success) { - var me = this; - if (!success) { - return; - } - - var cluster = { - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext("Standalone node - no cluster defined") - }; - - var nodes = { - online: 0, - offline: 0 - }; - - // by default we have one node - var numNodes = 1; - var i; - - for (i = 0; i < records.length; i++) { - var item = records[i]; - if (item.data.type === 'node') { - nodes[item.data.online === 1 ? 'online':'offline']++; - } else if(item.data.type === 'cluster') { - cluster.text = gettext("Cluster") + ": "; - cluster.text += item.data.name + ", "; - cluster.text += gettext("Quorate") + ": "; - cluster.text += Proxmox.Utils.format_boolean(item.data.quorate); - if (item.data.quorate != 1) { - cluster.iconCls = PVE.Utils.get_health_icon('critical', true); - } - - numNodes = item.data.nodes; - } - } - - if (numNodes !== (nodes.online + nodes.offline)) { - nodes.offline = numNodes - nodes.online; - } - - me.getComponent('clusterstatus').updateHealth(cluster); - me.getComponent('nodestatus').update(nodes); - }, - - updateCeph: function(store, records, success) { - var me = this; - var cephstatus = me.getComponent('ceph'); - if (!success || records.length < 1) { - - // if ceph status is already visible - // don't stop to update - if (cephstatus.isVisible()) { - return; - } - - // try all nodes until we either get a successful api call, - // or we tried all nodes - if (++me.nodeIndex >= me.nodeList.length) { - me.cephstore.stopUpdate(); - } else { - store.getProxy().setUrl('/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status'); - } - - return; - } - - var state = PVE.Utils.render_ceph_health(records[0].data.health || {}); - cephstatus.updateHealth(state); - cephstatus.setVisible(true); - }, - - listeners: { - destroy: function() { - var me = this; - me.cephstore.stopUpdate(); - } - }, - - items: [ - { - itemId: 'clusterstatus', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - itemId: 'nodestatus', - data: { - online: 0, - offline: 0 - }, - tpl: [ - '

' + gettext('Nodes') + '


', - '
', - '
', - ' ', - gettext('Online'), - '
', - '
{online}
', - '

', - '
', - ' ', - gettext('Offline'), - '
', - '
{offline}
', - '
' - ] - }, - { - itemId: 'ceph', - width: 250, - columnWidth: undefined, - userCls: 'pointer', - title: 'Ceph', - xtype: 'pveHealthWidget', - hidden: true, - listeners: { - element: 'el', - click: function() { - var sp = Ext.state.Manager.getProvider(); - sp.set('dctab', {value:'ceph'}, true); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.nodeList = PVE.data.ResourceStore.getNodes(); - me.nodeIndex = 0; - me.cephstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-ceph', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status' - } - }); - me.callParent(); - me.mon(me.cephstore, 'load', me.updateCeph, me); - me.cephstore.startUpdate(); - } -}); -Ext.define('PVE.dc.Guests', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcGuests', - - - title: gettext('Guests'), - height: 220, - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - bodyPadding: '0 20 20 20', - - defaults: { - xtype: 'box', - padding: '0 50 0 50', - style: { - 'text-align':'center', - 'line-height':'1.2' - } - }, - items: [{ - itemId: 'qemu', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("Virtual Machines") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'lxc', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("LXC Container") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'error', - colspan: 2, - data: { - num: 0 - }, - columnWidth: 1, - padding: '10 250 0 250', - tpl: [ - '', - '
', - ' ', - gettext('Error'), - '
', - '
{num}
', - '
' - ] - }], - - updateValues: function(qemu, lxc, error) { - var me = this; - me.getComponent('qemu').update(qemu); - me.getComponent('lxc').update(lxc); - me.getComponent('error').update({num: error}); - } -}); - /*jslint confusion: true*/ -Ext.define('PVE.dc.OptionView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveDcOptionView'], - - onlineHelp: 'datacenter_configuration_file', - - monStoreErrors: true, - - add_inputpanel_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - var canEdit = (opts.caps === undefined || opts.caps); - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: canEdit ? { - xtype: 'proxmoxWindowEdit', - width: 350, - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - setValues: function(values) { - // FIXME: run through parsePropertyString if not an object? - var edit_value = values[name]; - Ext.Array.each(this.query('inputpanel'), function(panel) { - panel.setValues(edit_value); - }); - }, - url: opts.url, - items: [{ - xtype: 'inputpanel', - onGetValues: function(values) { - if (values === undefined || Object.keys(values).length === 0) { - return { 'delete': name }; - } - var ret_val = {}; - ret_val[name] = PVE.Parser.printPropertyString(values); - return ret_val; - }, - items: opts.items - }] - } : undefined - }; - }, - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.add_combobox_row('keyboard', gettext('Keyboard Layout'), { - renderer: PVE.Utils.render_kvm_language, - comboItems: PVE.Utils.kvm_keymap_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('http_proxy', gettext('HTTP proxy'), { - defaultValue: Proxmox.Utils.noneText, - vtype: 'HttpProxy', - deleteEmpty: true - }); - me.add_combobox_row('console', gettext('Console Viewer'), { - renderer: PVE.Utils.render_console_viewer, - comboItems: PVE.Utils.console_viewer_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('email_from', gettext('Email from address'), { - deleteEmpty: true, - vtype: 'proxmoxMail', - defaultValue: 'root@$hostname' - }); - me.add_text_row('mac_prefix', gettext('MAC address prefix'), { - deleteEmpty: true, - vtype: 'MacPrefix', - defaultValue: Proxmox.Utils.noneText - }); - me.add_inputpanel_row('migration', gettext('Migration Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.vms['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - defaultKey: 'type', - items: [{ - xtype: 'displayfield', - name: 'type', - fieldLabel: gettext('Type'), - value: 'secure', - submitValue: true, - }, { - xtype: 'proxmoxNetworkSelector', - name: 'network', - fieldLabel: gettext('Network'), - value: null, - emptyText: Proxmox.Utils.defaultText, - autoSelect: false, - skipEmptyText: true - }] - }); - me.add_inputpanel_row('ha', gettext('HA Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.vms['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'shutdown_policy', - fieldLabel: gettext('Shutdown Policy'), - deleteEmpty: false, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + ' (conditional)' ], - ['freeze', 'freeze'], - ['failover', 'failover'], - ['conditional', 'conditional'] - ], - defaultValue: '__default__' - }] - }); - - // TODO: bwlimits, u2f? - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - Ext.apply(me, { - tbar: [{ - text: gettext('Edit'), - xtype: 'proxmoxButton', - disabled: true, - handler: function() { me.run_editor(); }, - selModel: me.selModel - }], - url: "/api2/json/cluster/options", - editorConfig: { - url: "/api2/extjs/cluster/options" - }, - interval: 5000, - cwidth1: 200, - listeners: { - itemdblclick: me.run_editor - } - }); - - me.callParent(); - - // set the new value for the default console - me.mon(me.rstore, 'load', function(store, records, success) { - if (!success) { - return; - } - - var rec = store.getById('console'); - PVE.VersionInfo.console = rec.data.value; - if (rec.data.value === '__default__') { - delete PVE.VersionInfo.console; - } - }); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); -Ext.define('PVE.dc.StorageView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveStorageView'], - - onlineHelp: 'chapter_storage', - - stateful: true, - stateId: 'grid-dc-storage', - - createStorageEditWindow: function(type, sid) { - var schema = PVE.Utils.storageSchema[type]; - if (!schema || !schema.ipanel) { - throw "no editor registered for storage type: " + type; - } - - Ext.create('PVE.storage.BaseEdit', { - paneltype: 'PVE.storage.' + schema.ipanel, - type: type, - storageId: sid, - autoShow: true, - listeners: { - destroy: this.reloadStore - } - }); - }, - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-storage', - proxy: { - type: 'proxmox', - url: "/api2/json/storage" - }, - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type, - sid = rec.data.storage; - - me.createStorageEditWindow(type, sid); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/storage/', - callback: reload - }); - - // else we cannot dynamically generate the add menu handlers - var addHandleGenerator = function(type) { - return function() { me.createStorageEditWindow(type); }; - }; - var addMenuItems = [], type; - /*jslint forin: true */ - for (type in PVE.Utils.storageSchema) { - var storage = PVE.Utils.storageSchema[type]; - if (storage.hideAdd) { - continue; - } - addMenuItems.push({ - text: PVE.Utils.format_storage_type(type), - iconCls: 'fa fa-fw fa-' + storage.faIcon, - handler: addHandleGenerator(type) - }); - } - - Ext.apply(me, { - store: store, - reloadStore: reload, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: addMenuItems - }) - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - flex: 2, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Type'), - flex: 1, - sortable: true, - dataIndex: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - header: gettext('Content'), - flex: 3, - sortable: true, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Path') + '/' + gettext('Target'), - flex: 2, - sortable: true, - dataIndex: 'path', - renderer: function(value, metaData, record) { - if (record.data.target) { - return record.data.target; - } - return value; - } - }, - { - header: gettext('Shared'), - flex: 1, - sortable: true, - dataIndex: 'shared', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Enabled'), - flex: 1, - sortable: true, - dataIndex: 'disable', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - header: gettext('Bandwidth Limit'), - flex: 2, - sortable: true, - dataIndex: 'bwlimit' - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-storage', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'content', 'server', 'portal', 'target', 'export', 'storage', - { name: 'shared', type: 'boolean'}, - { name: 'disable', type: 'boolean'} - ], - idProperty: 'storage' - }); - -}); -/*global u2f,QRCode,Uint8Array*/ -/*jslint confusion: true*/ -Ext.define('PVE.window.TFAEdit', { - extend: 'Ext.window.Window', - mixins: ['Proxmox.Mixin.CBind'], - - onlineHelp: 'pveum_tfa_auth', // fake to ensure this gets a link target - - modal: true, - resizable: false, - title: gettext('Two Factor Authentication'), - subject: 'TFA', - url: '/api2/extjs/access/tfa', - width: 512, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - updateQrCode: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var algorithm = values.algorithm; - if (!algorithm) { - algorithm = 'SHA1'; - } - - me.qrcode.makeCode( - 'otpauth://totp/' + encodeURIComponent(me.userid) + - '?secret=' + values.secret + - '&period=' + values.step + - '&digits=' + values.digits + - '&algorithm=' + algorithm + - '&issuer=' + encodeURIComponent(values.issuer) - ); - - me.lookup('challenge').setVisible(true); - me.down('#qrbox').setVisible(true); - }, - - showError: function(error) { - Ext.Msg.alert( - gettext('Error'), - PVE.Utils.render_u2f_error(error) - ); - }, - - doU2FChallenge: function(response) { - var me = this; - - var data = response.result.data; - me.lookup('password').setDisabled(true); - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Setup'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - Ext.Function.defer(function() { - u2f.register(data.appId, [data], [], function(data) { - msg.close(); - if (data.errorCode) { - me.showError(data.errorCode); - } else { - me.respondToU2FChallenge(data); - } - }); - }, 500, me); - }, - - respondToU2FChallenge: function(data) { - var me = this; - var params = { - userid: me.userid, - action: 'confirm', - response: JSON.stringify(data) - }; - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - success: function() { - me.close(); - Ext.Msg.show({ - title: gettext('Success'), - message: gettext('U2F Device successfully connected.'), - buttons: Ext.Msg.OK - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - viewModel: { - data: { - in_totp_tab: true, - tfa_required: false, - tfa_type: null, // dependencies of formulas should not be undefined - valid: false, - u2f_available: true - }, - formulas: { - canDeleteTFA: function(get) { - return (get('tfa_type') !== null && !get('tfa_required')); - }, - canSetupTOTP: function(get) { - var tfa = get('tfa_type'); - return (tfa === null || tfa === 'totp' || tfa === 1); - }, - canSetupU2F: function(get) { - var tfa = get('tfa_type'); - return (get('u2f_available') && (tfa === null || tfa === 'u2f' || tfa === 1)); - } - } - }, - - afterLoading: function(realm_tfa_type, user_tfa_type) { - var me = this; - var viewmodel = me.getViewModel(); - if (user_tfa_type === 'oath') { - user_tfa_type = 'totp'; - } - viewmodel.set('tfa_type', user_tfa_type || null); - if (!realm_tfa_type) { - // There's no TFA enforced by the realm, everything works. - viewmodel.set('u2f_available', true); - viewmodel.set('tfa_required', false); - } else if (realm_tfa_type === 'oath') { - // The realm explicitly requires TOTP - if (user_tfa_type !== 'totp' && user_tfa_type !== null) { - // user had a different tfa method, so - // we have to change back to the totp tab and - // generate a secret - viewmodel.set('tfa_type', null); - me.lookup('tfatabs').setActiveTab(me.lookup('totp_panel')); - me.getController().randomizeSecret(); - } - viewmodel.set('tfa_required', true); - viewmodel.set('u2f_available', false); - } else { - // The realm enforces some other TFA type (yubico) - me.close(); - Ext.Msg.alert( - gettext('Error'), - Ext.String.format( - gettext("Custom 2nd factor configuration is not supported on realms with '{0}' TFA."), - realm_tfa_type - ) - ); - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[qrupdate=true]': { - change: function() { - var me = this.getView(); - me.updateQrCode(); - } - }, - 'field': { - validitychange: function(field, valid) { - var me = this; - var viewModel = me.getViewModel(); - var form = me.lookup('totp_form'); - var challenge = me.lookup('challenge'); - var password = me.lookup('password'); - viewModel.set('valid', form.isValid() && challenge.isValid() && password.isValid()); - } - }, - '#': { - show: function() { - var me = this.getView(); - var viewmodel = this.getViewModel(); - - var loadMaskContainer = me.down('#tfatabs'); - Proxmox.Utils.API2Request({ - url: '/access/users/' + encodeURIComponent(me.userid) + '/tfa', - waitMsgTarget: loadMaskContainer, - method: 'GET', - success: function(response, opts) { - var data = response.result.data; - me.afterLoading(data.realm, data.user); - }, - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(loadMaskContainer, response.htmlStatus); - } - }); - - me.qrdiv = document.createElement('center'); - me.qrcode = new QRCode(me.qrdiv, { - width: 256, - height: 256, - correctLevel: QRCode.CorrectLevel.M - }); - me.down('#qrbox').getEl().appendChild(me.qrdiv); - - viewmodel.set('tfa_type', me.tfa_type || null); - if (!me.tfa_type) { - this.randomizeSecret(); - } else { - me.down('#qrbox').setVisible(false); - me.lookup('challenge').setVisible(false); - if (me.tfa_type === 'u2f') { - var u2f_panel = me.lookup('u2f_panel'); - me.lookup('tfatabs').setActiveTab(u2f_panel); - } - } - - if (Proxmox.UserName === 'root@pam') { - me.lookup('password').setVisible(false); - me.lookup('password').setDisabled(true); - } - } - }, - '#tfatabs': { - tabchange: function(panel, newcard) { - var viewmodel = this.getViewModel(); - viewmodel.set('in_totp_tab', newcard.itemId === 'totp-panel'); - } - } - }, - - applySettings: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'new', - key: values.secret, - config: PVE.Parser.printPropertyString({ - type: 'oath', - digits: values.digits, - step: values.step - }), - // this is used to verify that the client generates the correct codes: - response: me.lookup('challenge').value - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - deleteTFA: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'delete' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - randomizeSecret: function() { - var me = this; - var rnd = new Uint8Array(16); - window.crypto.getRandomValues(rnd); - var data = ''; - rnd.forEach(function(b) { - // secret must be base32, so just use the first 5 bits - b = b & 0x1f; - if (b < 26) { - // A..Z - data += String.fromCharCode(b + 0x41); - } else { - // 2..7 - data += String.fromCharCode(b-26 + 0x32); - } - }); - me.lookup('tfa_secret').setValue(data); - }, - - startU2FRegistration: function() { - var me = this; - - var params = { - userid: me.getView().userid, - action: 'new' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response) { - me.getView().doU2FChallenge(response); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - - items: [ - { - xtype: 'tabpanel', - itemId: 'tfatabs', - reference: 'tfatabs', - border: false, - items: [ - { - xtype: 'panel', - title: 'TOTP', - itemId: 'totp-panel', - reference: 'totp_panel', - tfa_type: 'totp', - border: false, - bind: { - disabled: '{!canSetupTOTP}' - }, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'form', - layout: 'anchor', - border: false, - reference: 'totp_form', - fieldDefaults: { - anchor: '100%', - padding: '0 5' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('User name'), - cbind: { - value: '{userid}' - } - }, - { - layout: 'hbox', - border: false, - padding: '0 0 5 0', - items: [{ - xtype: 'textfield', - fieldLabel: gettext('Secret'), - emptyText: gettext('Unchanged'), - name: 'secret', - reference: 'tfa_secret', - regex: /^[A-Z2-7=]+$/, - regexText: 'Must be base32 [A-Z2-7=]', - maskRe: /[A-Z2-7=]/, - qrupdate: true, - flex: 4 - }, - { - xtype: 'button', - text: gettext('Randomize'), - reference: 'randomize_button', - handler: 'randomizeSecret', - flex: 1 - }] - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Time period'), - name: 'step', - // Google Authenticator ignores this and generates bogus data - hidden: true, - value: 30, - minValue: 10, - qrupdate: true - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Digits'), - name: 'digits', - value: 6, - // Google Authenticator ignores this and generates bogus data - hidden: true, - minValue: 6, - maxValue: 8, - qrupdate: true - }, - { - xtype: 'textfield', - fieldLabel: gettext('Issuer Name'), - name: 'issuer', - value: 'Proxmox Web UI', - qrupdate: true - } - ] - }, - { - xtype: 'box', - itemId: 'qrbox', - visible: false, // will be enabled when generating a qr code - style: { - 'background-color': 'white', - padding: '5px', - width: '266px', - height: '266px' - } - }, - { - xtype: 'textfield', - fieldLabel: gettext('Verification Code'), - allowBlank: false, - reference: 'challenge', - padding: '0 5', - emptyText: gettext('Scan QR code and enter TOTP auth. code to verify') - } - ] - }, - { - title: 'U2F', - itemId: 'u2f-panel', - reference: 'u2f_panel', - tfa_type: 'u2f', - border: false, - padding: '5 5', - layout: { - type: 'vbox', - align: 'middle' - }, - bind: { - disabled: '{!canSetupU2F}' - }, - items: [ - { - xtype: 'label', - width: 500, - text: gettext('To register a U2F device, connect the device, then click the button and follow the instructions.') - } - ] - } - ] - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - reference: 'password', - allowBlank: false, - validateBlank: true, - padding: '0 0 5 5', - emptyText: gettext('verify current password') - } - ], - - buttons: [ - { - xtype: 'proxmoxHelpButton' - }, - '->', - { - text: gettext('Apply'), - handler: 'applySettings', - bind: { - hidden: '{!in_totp_tab}', - disabled: '{!valid}' - } - }, - { - xtype: 'button', - text: gettext('Register U2F Device'), - handler: 'startU2FRegistration', - bind: { - hidden: '{in_totp_tab}', - disabled: '{tfa_type}' - } - }, - { - text: gettext('Delete'), - reference: 'delete_button', - disabled: true, - handler: 'deleteTFA', - bind: { - disabled: '{!canDeleteTFA}' - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.userid) { - throw "no userid given"; - } - - me.callParent(); - - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', 'pveum_tfa_auth'); - } -}); -Ext.define('PVE.dc.UserEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcUserEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.userid; - - var url; - var method; - var realm; - - if (me.isCreate) { - url = '/api2/extjs/access/users'; - method = 'POST'; - } else { - url = '/api2/extjs/access/users/' + encodeURIComponent(me.userid); - method = 'PUT'; - } - - var verifypw; - var pwfield; - - var validate_pw = function() { - if (verifypw.getValue() !== pwfield.getValue()) { - return gettext("Passwords do not match"); - } - return true; - }; - - verifypw = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - submitValue: false, - disabled: true, - hidden: true, - validator: validate_pw - }); - - pwfield = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - name: 'password', - disabled: true, - hidden: true, - validator: validate_pw - }); - - var update_passwd_field = function(realm) { - if (realm === 'pve') { - pwfield.setVisible(true); - pwfield.setDisabled(false); - verifypw.setVisible(true); - verifypw.setDisabled(false); - } else { - pwfield.setVisible(false); - pwfield.setDisabled(true); - verifypw.setVisible(false); - verifypw.setDisabled(true); - } - - }; - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'userid', - fieldLabel: gettext('User name'), - value: me.userid, - allowBlank: false, - submitValue: me.isCreate ? true : false - }, - pwfield, verifypw, - { - xtype: 'pveGroupSelector', - name: 'groups', - multiSelect: true, - allowBlank: true, - fieldLabel: gettext('Group') - }, - { - xtype: 'datefield', - name: 'expire', - emptyText: 'never', - format: 'Y-m-d', - submitFormat: 'U', - fieldLabel: gettext('Expire') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enabled'), - name: 'enable', - uncheckedValue: 0, - defaultValue: 1, - checked: true - } - ]; - - var column2 = [ - { - xtype: 'textfield', - name: 'firstname', - fieldLabel: gettext('First Name') - }, - { - xtype: 'textfield', - name: 'lastname', - fieldLabel: gettext('Last Name') - }, - { - xtype: 'textfield', - name: 'email', - fieldLabel: gettext('E-Mail'), - vtype: 'proxmoxMail' - } - ]; - - if (me.isCreate) { - column1.splice(1,0,{ - xtype: 'pveRealmComboBox', - name: 'realm', - fieldLabel: gettext('Realm'), - allowBlank: false, - matchFieldWidth: false, - listConfig: { width: 300 }, - listeners: { - change: function(combo, newValue){ - realm = newValue; - update_passwd_field(realm); - } - }, - submitValue: false - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ], - advancedItems: [ - { - xtype: 'textfield', - name: 'keys', - fieldLabel: gettext('Key IDs') - } - ], - onGetValues: function(values) { - // hack: ExtJS datefield does not submit 0, so we need to set that - if (!values.expire) { - values.expire = 0; - } - - if (realm) { - values.userid = values.userid + '@' + realm; - } - - if (!values.password) { - delete values.password; - } - - return values; - } - }); - - Ext.applyIf(me, { - subject: gettext('User'), - url: url, - method: method, - fieldDefaults: { - labelWidth: 110 // for spanish translation - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (Ext.isDefined(data.expire)) { - if (data.expire) { - data.expire = new Date(data.expire * 1000); - } else { - // display 'never' instead of '1970-01-01' - data.expire = null; - } - } - me.setValues(data); - } - }); - } - } -}); -/*jslint confusion: true */ -Ext.define('PVE.dc.UserView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveUserView'], - - onlineHelp: 'pveum_users', - - stateful: true, - stateId: 'grid-users', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - id: "users", - model: 'pve-users', - sorters: { - property: 'userid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/access/users/', - enableFn: function(rec) { - if (!caps.access['User.Modify']) { - return false; - } - return rec.data.userid !== 'root@pam'; - }, - callback: function() { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec || !caps.access['User.Modify']) { - return; - } - - var win = Ext.create('PVE.dc.UserEdit',{ - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - enableFn: function(rec) { - return !!caps.access['User.Modify']; - }, - selModel: sm, - handler: run_editor - }); - - var pwchange_btn = new Proxmox.button.Button({ - text: gettext('Password'), - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var win = Ext.create('Proxmox.window.PasswordEdit', { - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var tfachange_btn = new Proxmox.button.Button({ - text: 'TFA', - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var d = rec.data; - var tfa_type = PVE.Parser.parseTfaType(d.keys); - var win = Ext.create('PVE.window.TFAEdit',{ - tfa_type: tfa_type, - userid: d.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - disabled: !caps.access['User.Modify'], - handler: function() { - var win = Ext.create('PVE.dc.UserEdit',{ - }); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn, pwchange_btn, tfachange_btn - ]; - - var render_username = function(userid) { - return userid.match(/^(.+)(@[^@]+)$/)[1]; - }; - - var render_realm = function(userid) { - return userid.match(/@([^@]+)$/)[1]; - }; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('User name'), - width: 200, - sortable: true, - renderer: render_username, - dataIndex: 'userid' - }, - { - header: gettext('Realm'), - width: 100, - sortable: true, - renderer: render_realm, - dataIndex: 'userid' - }, - { - header: gettext('Enabled'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'enable' - }, - { - header: gettext('Expire'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_expire, - dataIndex: 'expire' - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname' - }, - { - header: 'TFA', - width: 50, - sortable: true, - renderer: function(v) { - var tfa_type = PVE.Parser.parseTfaType(v); - if (tfa_type === undefined) { - return Proxmox.Utils.noText; - } else if (tfa_type === 1) { - return Proxmox.Utils.yesText; - } else { - return tfa_type; - } - }, - dataIndex: 'keys' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.PoolView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pvePoolView'], - - onlineHelp: 'pveum_pools', - - stateful: true, - stateId: 'grid-pools', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: { - property: 'poolid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/pools/', - callback: function () { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.PoolEdit',{ - poolid: rec.data.poolid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.PoolEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'poolid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.PoolEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcPoolEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.poolid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/pools'; - method = 'POST'; - } else { - url = '/api2/extjs/pools/' + me.poolid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Pool'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'poolid', - value: me.poolid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.GroupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveGroupView'], - - onlineHelp: 'pveum_groups', - - stateful: true, - stateId: 'grid-groups', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: { - property: 'groupid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/groups/' - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.GroupEdit',{ - groupid: rec.data.groupid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.GroupEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'groupid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.GroupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcGroupEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/groups'; - method = 'POST'; - } else { - url = '/api2/extjs/access/groups/' + me.groupid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Group'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'groupid', - value: me.groupid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.RoleView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveRoleView'], - - onlineHelp: 'pveum_roles', - - stateful: true, - stateId: 'grid-roles', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: { - property: 'roleid', - order: 'DESC' - } - }); - - var render_privs = function(value, metaData) { - - if (!value) { - return '-'; - } - - // allow word wrap - metaData.style = 'white-space:normal;'; - - return value.replace(/\,/g, ' '); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (rec.data.special === "1") { - return; - } - - var win = Ext.create('PVE.dc.RoleEdit',{ - roleid: rec.data.roleid, - privs: rec.data.privs - }); - win.on('destroy', reload); - win.show(); - }; - - Ext.apply(me, { - store: store, - selModel: sm, - - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Built-In'), - width: 65, - sortable: true, - dataIndex: 'special', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - dataIndex: 'roleid' - }, - { - itemid: 'privs', - header: gettext('Privileges'), - sortable: false, - renderer: render_privs, - dataIndex: 'privs', - flex: 1 - } - ], - listeners: { - activate: function() { - store.load(); - }, - itemdblclick: run_editor - }, - tbar: [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.RoleEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor, - enableFn: function(record) { - return record.data.special !== '1'; - } - }, - { - xtype: 'proxmoxStdRemoveButton', - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/roles/', - enableFn: function(record) { - return record.data.special !== '1'; - } - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.RoleEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveDcRoleEdit', - - width: 400, - - initComponent : function() { - var me = this; - - me.isCreate = !me.roleid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/roles'; - method = 'POST'; - } else { - url = '/api2/extjs/access/roles/' + me.roleid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Role'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'roleid', - value: me.roleid, - allowBlank: false, - fieldLabel: gettext('Name') - }, - { - xtype: 'pvePrivilegesSelector', - name: 'privs', - value: me.privs, - allowBlank: false, - fieldLabel: gettext('Privileges') - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response) { - var data = response.result.data; - var keys = Ext.Object.getKeys(data); - - me.setValues({ - privs: keys, - roleid: me.roleid - }); - } - }); - } - } -}); -Ext.define('PVE.dc.ACLAdd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveACLAdd'], - url: '/access/acl', - method: 'PUT', - isAdd: true, - initComponent : function() { - - var me = this; - - me.isCreate = true; - - var items = [ - { - xtype: me.path ? 'hiddenfield' : 'pvePermPathSelector', - name: 'path', - value: me.path, - allowBlank: false, - fieldLabel: gettext('Path') - } - ]; - - if (me.aclType === 'group') { - me.subject = gettext("Group Permission"); - items.push({ - xtype: 'pveGroupSelector', - name: 'groups', - fieldLabel: gettext('Group') - }); - } else if (me.aclType === 'user') { - me.subject = gettext("User Permission"); - items.push({ - xtype: 'pveUserSelector', - name: 'users', - fieldLabel: gettext('User') - }); - } else { - throw "unknown ACL type"; - } - - items.push({ - xtype: 'pveRoleSelector', - name: 'roles', - value: 'NoAccess', - fieldLabel: gettext('Role') - }); - - if (!me.path) { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'propagate', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Propagate') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - items: items, - onlineHelp: 'pveum_permission_management' - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.dc.ACLView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveACLView'], - - onlineHelp: 'chapter_user_management', - - stateful: true, - stateId: 'grid-acls', - - // use fixed path - path: undefined, - - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-acl', - proxy: { - type: 'proxmox', - url: "/api2/json/access/acl" - }, - sorters: { - property: 'path', - order: 'DESC' - } - }); - - if (me.path) { - store.addFilter(Ext.create('Ext.util.Filter',{ - filterFn: function(item) { - if (item.data.path === me.path) { - return true; - } - } - })); - } - - var render_ugid = function(ugid, metaData, record) { - if (record.data.type == 'group') { - return '@' + ugid; - } - - return ugid; - }; - - var columns = [ - { - header: gettext('User') + '/' + gettext('Group'), - flex: 1, - sortable: true, - renderer: render_ugid, - dataIndex: 'ugid' - }, - { - header: gettext('Role'), - flex: 1, - sortable: true, - dataIndex: 'roleid' - } - ]; - - if (!me.path) { - columns.unshift({ - header: gettext('Path'), - flex: 1, - sortable: true, - dataIndex: 'path' - }); - columns.push({ - header: gettext('Propagate'), - width: 80, - sortable: true, - dataIndex: 'propagate' - }); - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: gettext('Are you sure you want to remove this entry'), - handler: function(btn, event, rec) { - var params = { - 'delete': 1, - path: rec.data.path, - roles: rec.data.roleid - }; - if (rec.data.type === 'group') { - params.groups = rec.data.ugid; - } else if (rec.data.type === 'user') { - params.users = rec.data.ugid; - } else { - throw 'unknown data type'; - } - - Proxmox.Utils.API2Request({ - url: '/access/acl', - params: params, - method: 'PUT', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: { - xtype: 'menu', - items: [ - { - text: gettext('Group Permission'), - iconCls: 'fa fa-fw fa-group', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'group', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('User Permission'), - iconCls: 'fa fa-fw fa-user', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'user', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - } - ] - } - }, - remove_btn - ], - viewConfig: { - trackOver: false - }, - columns: columns, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-acl', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'ugid', 'roleid', - { - name: 'propagate', - type: 'boolean' - } - ] - }); - -}); -Ext.define('PVE.dc.AuthView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveAuthView'], - - onlineHelp: 'pveum_authentication_realms', - - stateful: true, - stateId: 'grid-authrealms', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-domains', - sorters: { - property: 'realm', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.AuthEdit',{ - realm: rec.data.realm, - authType: rec.data.type - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - baseurl: '/access/domains/', - selModel: sm, - enableFn: function(rec) { - return !(rec.data.type === 'pve' || rec.data.type === 'pam'); - }, - callback: function() { - reload(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Active Directory Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit', { - authType: 'ad' - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('LDAP Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit',{ - authType: 'ldap' - }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - edit_btn, remove_btn - ]; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Realm'), - width: 100, - sortable: true, - dataIndex: 'realm' - }, - { - header: gettext('Type'), - width: 100, - sortable: true, - dataIndex: 'type' - }, - { - header: gettext('TFA'), - width: 100, - sortable: true, - dataIndex: 'tfa' - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.AuthEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcAuthEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.realm; - - var url; - var method; - var serverlist; - - if (me.isCreate) { - url = '/api2/extjs/access/domains'; - method = 'POST'; - } else { - url = '/api2/extjs/access/domains/' + me.realm; - method = 'PUT'; - } - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'realm', - fieldLabel: gettext('Realm'), - value: me.realm, - allowBlank: false - } - ]; - - if (me.authType === 'ad') { - - me.subject = gettext('Active Directory Server'); - - column1.push({ - xtype: 'textfield', - name: 'domain', - fieldLabel: gettext('Domain'), - emptyText: 'company.net', - allowBlank: false - }); - - } else if (me.authType === 'ldap') { - - me.subject = gettext('LDAP Server'); - - column1.push({ - xtype: 'textfield', - name: 'base_dn', - fieldLabel: gettext('Base Domain Name'), - emptyText: 'CN=Users,DC=Company,DC=net', - allowBlank: false - }); - - column1.push({ - xtype: 'textfield', - name: 'user_attr', - emptyText: 'uid / sAMAccountName', - fieldLabel: gettext('User Attribute Name'), - allowBlank: false - }); - } else if (me.authType === 'pve') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'Proxmox VE authentication server'; - - } else if (me.authType === 'pam') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'linux PAM'; - - } else { - throw 'unknown auth type '; - } - - column1.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Default'), - name: 'default', - uncheckedValue: 0 - }); - - var column2 = []; - - if (me.authType === 'ldap' || me.authType === 'ad') { - column2.push( - { - xtype: 'textfield', - fieldLabel: gettext('Server'), - name: 'server1', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Fallback Server'), - deleteEmpty: !me.isCreate, - name: 'server2' - }, - { - xtype: 'proxmoxintegerfield', - name: 'port', - fieldLabel: gettext('Port'), - minValue: 1, - maxValue: 65535, - emptyText: gettext('Default'), - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'SSL', - name: 'secure', - uncheckedValue: 0 - } - ); - } - - // Two Factor Auth settings - - column2.push({ - xtype: 'proxmoxKVComboBox', - name: 'tfa', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('TFA'), - comboItems: [ ['__default__', Proxmox.Utils.noneText], ['oath', 'OATH'], ['yubico', 'Yubico']], - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=oath_step]').setVisible(value === 'oath'); - me.down('field[name=oath_digits]').setVisible(value === 'oath'); - me.down('field[name=yubico_api_id]').setVisible(value === 'yubico'); - me.down('field[name=yubico_api_key]').setVisible(value === 'yubico'); - me.down('field[name=yubico_url]').setVisible(value === 'yubico'); - } - } - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_step', - value: '', - minValue: 10, - emptyText: Proxmox.Utils.defaultText + ' (30)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH time step' - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_digits', - value: '', - minValue: 6, - maxValue: 8, - emptyText: Proxmox.Utils.defaultText + ' (6)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH password length' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_id', - hidden: true, - fieldLabel: 'Yubico API Id' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_key', - hidden: true, - fieldLabel: 'Yubico API Key' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_url', - hidden: true, - fieldLabel: 'Yubico URL' - }); - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [{ - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }], - onGetValues: function(values) { - if (!values.port) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'port' }); - } - delete values.port; - } - - if (me.isCreate) { - values.type = me.authType; - } - - if (values.tfa === 'oath') { - values.tfa = "type=oath"; - if (values.oath_step) { - values.tfa += ",step=" + values.oath_step; - } - if (values.oath_digits) { - values.tfa += ",digits=" + values.oath_digits; - } - } else if (values.tfa === 'yubico') { - values.tfa = "type=yubico"; - values.tfa += ",id=" + values.yubico_api_id; - values.tfa += ",key=" + values.yubico_api_key; - if (values.yubico_url) { - values.tfa += ",url=" + values.yubico_url; - } - } else { - delete values.tfa; - } - - delete values.oath_step; - delete values.oath_digits; - delete values.yubico_api_id; - delete values.yubico_api_key; - delete values.yubico_url; - - return values; - } - }); - - Ext.applyIf(me, { - url: url, - method: method, - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data || {}; - // just to be sure (should not happen) - if (data.type !== me.authType) { - me.close(); - throw "got wrong auth type"; - } - - if (data.tfa) { - var tfacfg = PVE.Parser.parseTfaConfig(data.tfa); - data.tfa = tfacfg.type; - if (tfacfg.type === 'yubico') { - data.yubico_api_key = tfacfg.key; - data.yubico_api_id = tfacfg.id; - data.yubico_url = tfacfg.url; - } else if (tfacfg.type === 'oath') { - // step is a number before - /*jslint confusion: true*/ - data.oath_step = tfacfg.step; - data.oath_digits = tfacfg.digits; - /*jslint confusion: false*/ - } - } - - me.setValues(data); - } - }); - } - } -}); -Ext.define('PVE.dc.BackupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcBackupEdit'], - - defaultFocus: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.jobid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/cluster/backup'; - method = 'POST'; - } else { - url = '/api2/extjs/cluster/backup/' + me.jobid; - method = 'PUT'; - } - - var vmidField = Ext.create('Ext.form.field.Hidden', { - name: 'vmid' - }); - - /*jslint confusion: true*/ - // 'value' can be assigned a string or an array - var selModeField = Ext.create('Proxmox.form.KVComboBox', { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['include', gettext('Include selected VMs')], - ['all', gettext('All')], - ['exclude', gettext('Exclude selected VMs')], - ['pool', gettext('Pool based')] - ], - fieldLabel: gettext('Selection mode'), - name: 'selMode', - value: '' - }); - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - var sel = []; - Ext.Array.each(selected, function(record) { - sel.push(record.data.vmid); - }); - - // to avoid endless recursion suspend the vmidField change - // event temporary as it calls us again - vmidField.suspendEvent('change'); - vmidField.setValue(sel); - vmidField.resumeEvent('change'); - } - } - }); - - var storagesel = Ext.create('PVE.form.StorageSelector', { - fieldLabel: gettext('Storage'), - nodename: 'localhost', - storageContent: 'backup', - allowBlank: false, - name: 'storage' - }); - - var store = new Ext.data.Store({ - model: 'PVEResources', - sorters: { - property: 'vmid', - order: 'ASC' - } - }); - - var vmgrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - disabled: true, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - - var selectPoolMembers = function(poolid) { - if (!poolid) { - return; - } - sm.deselectAll(true); - store.filter([ - { - id: 'poolFilter', - property: 'pool', - value: poolid - } - ]); - sm.selectAll(true); - }; - - var selPool = Ext.create('PVE.form.PoolSelector', { - fieldLabel: gettext('Pool to backup'), - hidden: true, - allowBlank: true, - name: 'pool', - listeners: { - change: function( selpool, newValue, oldValue) { - selectPoolMembers(newValue); - } - } - }); - - var nodesel = Ext.create('PVE.form.NodeSelector', { - name: 'node', - fieldLabel: gettext('Node'), - allowBlank: true, - editable: true, - autoSelect: false, - emptyText: '-- ' + gettext('All') + ' --', - listeners: { - change: function(f, value) { - storagesel.setNodename(value || 'localhost'); - var mode = selModeField.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!value || rec.get('node') === value); - }); - if (mode === 'all') { - sm.selectAll(true); - } - - if (mode === 'pool') { - selectPoolMembers(selPool.value); - } - } - } - }); - - var column1 = [ - nodesel, - storagesel, - { - xtype: 'pveDayOfWeekSelector', - name: 'dow', - fieldLabel: gettext('Day of week'), - multiSelect: true, - value: ['sat'], - allowBlank: false - }, - { - xtype: 'timefield', - fieldLabel: gettext('Start Time'), - name: 'starttime', - format: 'H:i', - formatText: 'HH:MM', - value: '00:00', - allowBlank: false - }, - selModeField, - selPool - ]; - - var column2 = [ - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto' - }, - { - xtype: 'pveEmailNotificationSelector', - fieldLabel: gettext('Email notification'), - name: 'mailnotification', - deleteEmpty: me.isCreate ? false : true, - value: me.isCreate ? 'always' : '' - }, - { - xtype: 'pveCompressionSelector', - fieldLabel: gettext('Compression'), - name: 'compress', - deleteEmpty: me.isCreate ? false : true, - value: 'lzo' - }, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable'), - name: 'enabled', - uncheckedValue: 0, - defaultValue: 1, - checked: true - }, - vmidField - ]; - /*jslint confusion: false*/ - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - onlineHelp: 'chapter_vzdump', - column1: column1, - column2: column2, - onGetValues: function(values) { - if (!values.node) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'node' }); - } - delete values.node; - } - - var selMode = values.selMode; - delete values.selMode; - - if (selMode === 'all') { - values.all = 1; - values.exclude = ''; - delete values.vmid; - } else if (selMode === 'exclude') { - values.all = 1; - values.exclude = values.vmid; - delete values.vmid; - } else if (selMode === 'pool') { - delete values.vmid; - } - - if (selMode !== 'pool') { - delete values.pool; - } - return values; - } - }); - - var update_vmid_selection = function(list, mode) { - if (mode !== 'all' && mode !== 'pool') { - sm.deselectAll(true); - if (list) { - Ext.Array.each(list.split(','), function(vmid) { - var rec = store.findRecord('vmid', vmid); - if (rec) { - sm.select(rec, true); - } - }); - } - } - }; - - vmidField.on('change', function(f, value) { - var mode = selModeField.getValue(); - update_vmid_selection(value, mode); - }); - - selModeField.on('change', function(f, value, oldValue) { - if (oldValue === 'pool') { - store.removeFilter('poolFilter'); - } - - if (oldValue === 'all') { - sm.deselectAll(true); - vmidField.setValue(''); - } - - if (value === 'all') { - sm.selectAll(true); - vmgrid.setDisabled(true); - } else { - vmgrid.setDisabled(false); - } - - if (value === 'pool') { - vmgrid.setDisabled(true); - vmidField.setValue(''); - selPool.setVisible(true); - selPool.allowBlank = false; - selectPoolMembers(selPool.value); - - } else { - selPool.setVisible(false); - selPool.allowBlank = true; - } - var list = vmidField.getValue(); - update_vmid_selection(list, value); - }); - - var reload = function() { - store.load({ - params: { type: 'vm' }, - callback: function() { - var node = nodesel.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!node || node.length === 0 || rec.get('node') === node); - }); - var list = vmidField.getValue(); - var mode = selModeField.getValue(); - if (mode === 'all') { - sm.selectAll(true); - } else if (mode === 'pool'){ - selectPoolMembers(selPool.value); - } else { - update_vmid_selection(list, mode); - } - } - }); - }; - - Ext.applyIf(me, { - subject: gettext("Backup Job"), - url: url, - method: method, - items: [ ipanel, vmgrid ] - }); - - me.callParent(); - - if (me.isCreate) { - selModeField.setValue('include'); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - - data.dow = data.dow.split(','); - - if (data.all || data.exclude) { - if (data.exclude) { - data.vmid = data.exclude; - data.selMode = 'exclude'; - } else { - data.vmid = ''; - data.selMode = 'all'; - } - } else if (data.pool) { - data.selMode = 'pool'; - data.selPool = data.pool; - } else { - data.selMode = 'include'; - } - - me.setValues(data); - } - }); - } - - reload(); - } -}); - - -Ext.define('PVE.dc.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveDcBackupView'], - - onlineHelp: 'chapter_vzdump', - - allText: '-- ' + gettext('All') + ' --', - allExceptText: gettext('All except {0}'), - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-cluster-backup', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/backup" - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.BackupEdit',{ - jobid: rec.data.id - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/backup', - callback: function() { - reload(); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - stateful: true, - stateId: 'grid-dc-backup', - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.dc.BackupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: gettext('Enabled'), - width: 80, - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true, - disabledCls: 'x-item-enabled', - stopSelection: false - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node', - renderer: function(value) { - if (value) { - return value; - } - return me.allText; - } - }, - { - header: gettext('Day of week'), - width: 200, - sortable: false, - dataIndex: 'dow', - renderer: function(val) { - var dows = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; - var selected = []; - var cur = -1; - val.split(',').forEach(function(day){ - cur++; - var dow = (dows.indexOf(day)+6)%7; - if (cur === dow) { - if (selected.length === 0 || selected[selected.length-1] === 0) { - selected.push(1); - } else { - selected[selected.length-1]++; - } - } else { - while (cur < dow) { - cur++; - selected.push(0); - } - selected.push(1); - } - }); - - cur = -1; - var days = []; - selected.forEach(function(item) { - cur++; - if (item > 2) { - days.push(Ext.Date.dayNames[(cur+1)] + '-' + Ext.Date.dayNames[(cur+item)%7]); - cur += item-1; - } else if (item == 2) { - days.push(Ext.Date.dayNames[cur+1]); - days.push(Ext.Date.dayNames[(cur+2)%7]); - cur++; - } else if (item == 1) { - days.push(Ext.Date.dayNames[(cur+1)%7]); - } - }); - return days.join(', '); - } - }, - { - header: gettext('Start Time'), - width: 60, - sortable: true, - dataIndex: 'starttime' - }, - { - header: gettext('Storage'), - width: 100, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Selection'), - flex: 1, - sortable: false, - dataIndex: 'vmid', - renderer: function(value, metaData, record) { - /*jslint confusion: true */ - if (record.data.all) { - if (record.data.exclude) { - return Ext.String.format(me.allExceptText, record.data.exclude); - } - return me.allText; - } - if (record.data.vmid) { - return record.data.vmid; - } - - if (record.data.pool) { - return "Pool '"+ record.data.pool + "'"; - } - - return "-"; - } - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-cluster-backup', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'starttime', 'dow', - 'storage', 'node', 'vmid', 'exclude', - 'mailto', 'pool', - { name: 'enabled', type: 'boolean' }, - { name: 'all', type: 'boolean' }, - { name: 'snapshot', type: 'boolean' }, - { name: 'stop', type: 'boolean' }, - { name: 'suspend', type: 'boolean' }, - { name: 'compress', type: 'boolean' } - ] - }); -}); -Ext.define('PVE.dc.Support', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSupport', - pveGuidePath: '/pve-docs/index.html', - onlineHelp: 'getting_help', - - invalidHtml: '

No valid subscription

' + PVE.Utils.noSubKeyHtml, - - communityHtml: 'Please use the public community forum for any questions.', - - activeHtml: 'Please use our support portal for any questions. You can also use the public community forum to get additional information.', - - bugzillaHtml: '

Bug Tracking

Our bug tracking system is available here.', - - docuHtml: function() { - var me = this; - var guideUrl = window.location.origin + me.pveGuidePath; - var text = Ext.String.format('

Documentation

' - + 'The official Proxmox VE Administration Guide' - + ' is included with this installation and can be browsed at ' - + '{0}', guideUrl); - return text; - }, - - updateActive: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.activeHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateCommunity: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.communityHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateInactive: function(data) { - var me = this; - me.update(me.invalidHtml); - }, - - initComponent: function() { - var me = this; - - var reload = function() { - Proxmox.Utils.API2Request({ - url: '/nodes/localhost/subscription', - method: 'GET', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.update('Unable to load subscription status' + ": " + response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data; - - if (data.status === 'Active') { - if (data.level === 'c') { - me.updateCommunity(data); - } else { - me.updateActive(data); - } - } else { - me.updateInactive(data); - } - } - }); - }; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('pve-security-groups', { - extend: 'Ext.data.Model', - - fields: [ 'group', 'comment', 'digest' ], - idProperty: 'group' -}); - -Ext.define('PVE.SecurityGroupEdit', { - extend: 'Proxmox.window.Edit', - - base_url: "/cluster/firewall/groups", - - allow_iface: false, - - initComponent : function() { - var me = this; - - me.isCreate = (me.group_name === undefined); - - var subject; - - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - - var items = [ - { - xtype: 'textfield', - name: 'group', - value: me.group_name || '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: me.group_comment || '', - fieldLabel: gettext('Comment') - } - ]; - - if (me.isCreate) { - subject = gettext('Security Group'); - } else { - subject = gettext('Security Group') + " '" + me.group_name + "'"; - items.push({ - xtype: 'hiddenfield', - name: 'rename', - value: me.group_name - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - // InputPanel does not have a 'create' property, does it need a 'isCreate' - isCreate: me.isCreate, - items: items - }); - - - Ext.apply(me, { - subject: subject, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.SecurityGroupList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveSecurityGroupList', - - stateful: true, - stateId: 'grid-securitygroups', - - rule_panel: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - base_url: "/cluster/firewall/groups", - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (me.rule_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-security-groups', - proxy: { - type: 'proxmox', - url: '/api2/json' + me.base_url - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('group', oldrec.data.group); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.SecurityGroupEdit', { - digest: rec.data.digest, - group_name: rec.data.group, - group_comment: rec.data.comment - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('PVE.SecurityGroupEdit', {}); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - enableFn: function(rec) { - return (rec && me.base_url); - }, - callback: function() { - reload(); - } - }); - - Ext.apply(me, { - store: store, - tbar: [ '' + gettext('Group') + ':', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: gettext('Group'), dataIndex: 'group', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = '/cluster/firewall/groups/' + rec.data.group; - me.rule_panel.setBaseUrl(url); - }, - deselect: function() { - me.rule_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.SecurityGroups', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveSecurityGroups', - - title: 'Security Groups', - - initComponent: function() { - var me = this; - - var rule_panel = Ext.createWidget('pveFirewallRules', { - region: 'center', - allow_groups: false, - list_refs_url: '/cluster/firewall/refs', - tbar_prefix: '' + gettext('Rules') + ':', - border: false - }); - - var sglist = Ext.createWidget('pveSecurityGroupList', { - region: 'west', - rule_panel: rule_panel, - width: '25%', - border: false, - split: true - }); - - - Ext.apply(me, { - layout: 'border', - items: [ sglist, rule_panel ], - listeners: { - show: function() { - sglist.fireEvent('show', sglist); - } - } - }); - - me.callParent(); - } -}); -/* - * Datacenter config panel, located in the center of the ViewPort after the Datacenter view is selected - */ - -Ext.define('PVE.dc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.dc.Config', - - onlineHelp: 'pve_admin_guide', - - initComponent: function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.items = []; - - Ext.apply(me, { - title: gettext("Datacenter"), - hstateid: 'dctab' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - title: gettext('Summary'), - xtype: 'pveDcSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - }, - { - title: gettext('Cluster'), - xtype: 'pveClusterAdministration', - iconCls: 'fa fa-server', - itemId: 'cluster' - }, - { - title: 'Ceph', - itemId: 'ceph', - iconCls: 'fa fa-ceph', - xtype: 'pveNodeCephStatus' - }, - { - xtype: 'pveDcOptionView', - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options' - }); - } - - if (caps.storage['Datastore.Allocate'] || caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveStorageView', - title: gettext('Storage'), - iconCls: 'fa fa-database', - itemId: 'storage' - }); - } - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveDcBackupView', - iconCls: 'fa fa-floppy-o', - title: gettext('Backup'), - itemId: 'backup' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - expandedOnInit: true - }); - } - - me.items.push({ - xtype: 'pveUserView', - groups: ['permissions'], - iconCls: 'fa fa-user', - title: gettext('Users'), - itemId: 'users' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveGroupView', - title: gettext('Groups'), - iconCls: 'fa fa-users', - groups: ['permissions'], - itemId: 'groups' - }, - { - xtype: 'pvePoolView', - title: gettext('Pools'), - iconCls: 'fa fa-tags', - groups: ['permissions'], - itemId: 'pools' - }, - { - xtype: 'pveRoleView', - title: gettext('Roles'), - iconCls: 'fa fa-male', - groups: ['permissions'], - itemId: 'roles' - }, - { - xtype: 'pveAuthView', - title: gettext('Authentication'), - groups: ['permissions'], - iconCls: 'fa fa-key', - itemId: 'domains' - }, - { - xtype: 'pveHAStatus', - title: 'HA', - iconCls: 'fa fa-heartbeat', - itemId: 'ha' - }, - { - title: gettext('Groups'), - groups: ['ha'], - xtype: 'pveHAGroupsView', - iconCls: 'fa fa-object-group', - itemId: 'ha-groups' - }, - { - title: gettext('Fencing'), - groups: ['ha'], - iconCls: 'fa fa-bolt', - xtype: 'pveFencingView', - itemId: 'ha-fencing' - }, - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/cluster/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - iconCls: 'fa fa-shield', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - groups: ['firewall'], - iconCls: 'fa fa-gear', - base_url: '/cluster/firewall/options', - onlineHelp: 'pve_firewall_cluster_wide_setup', - fwtype: 'dc', - itemId: 'firewall-options' - }, - { - xtype: 'pveSecurityGroups', - title: gettext('Security Group'), - groups: ['firewall'], - iconCls: 'fa fa-group', - itemId: 'firewall-sg' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: '/cluster/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: 'IPSet', - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: '/cluster/firewall/ipset', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall-ipset' - }, - { - xtype: 'pveDcSupport', - title: gettext('Support'), - itemId: 'support', - iconCls: 'fa fa-comments-o' - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.dc.NodeView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveDcNodeView', - - title: gettext('Nodes'), - disableSelection: true, - scrollable: true, - - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: 'ID', - width: 40, - sortable: true, - dataIndex: 'nodeid' - }, - { - header: gettext('Online'), - width: 60, - sortable: true, - dataIndex: 'online', - renderer: function(value) { - var cls = (value)?'good':'critical'; - return ''; - } - }, - { - header: gettext('Support'), - width: 100, - sortable: true, - dataIndex: 'level', - renderer: PVE.Utils.render_support_level - }, - { - header: gettext('Server Address'), - width: 115, - sortable: true, - dataIndex: 'ip' - }, - { - header: gettext('CPU usage'), - sortable: true, - width: 110, - dataIndex: 'cpuusage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Memory usage'), - width: 110, - sortable: true, - tdCls: 'x-progressbar-default-cell', - dataIndex: 'memoryusage', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Uptime'), - sortable: true, - dataIndex: 'uptime', - align: 'right', - renderer: Proxmox.Utils.render_uptime - } - ], - - stateful: true, - stateId: 'grid-cluster-nodes', - tools: [ - { - type: 'up', - handler: function(){ - var me = this.up('grid'); - var height = Math.max(me.getHeight()-50, 250); - me.setHeight(height); - } - }, - { - type: 'down', - handler: function(){ - var me = this.up('grid'); - var height = me.getHeight()+50; - me.setHeight(height); - } - } - ] -}, function() { - - Ext.define('pve-dc-nodes', { - extend: 'Ext.data.Model', - fields: [ 'id', 'type', 'name', 'nodeid', 'ip', 'level', 'local', 'online'], - idProperty: 'id' - }); - -}); - -Ext.define('PVE.widget.ProgressBar',{ - extend: 'Ext.Progress', - alias: 'widget.pveProgressBar', - - animate: true, - textTpl: [ - '{percent}%' - ], - - setValue: function(value){ - var me = this; - me.callParent([value]); - - me.removeCls(['warning', 'critical']); - - if (value > 0.89) { - me.addCls('critical'); - } else if (value > 0.59) { - me.addCls('warning'); - } - } -}); -/*jslint confusion: true*/ -Ext.define('pve-cluster-nodes', { - extend: 'Ext.data.Model', - fields: [ - 'node', { type: 'integer', name: 'nodeid' }, 'ring0_addr', 'ring1_addr', - { type: 'integer', name: 'quorum_votes' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/nodes" - }, - idProperty: 'nodeid' -}); - -Ext.define('pve-cluster-info', { - extend: 'Ext.data.Model', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/join" - } -}); - -Ext.define('PVE.ClusterAdministration', { - extend: 'Ext.panel.Panel', - xtype: 'pveClusterAdministration', - - title: gettext('Cluster Administration'), - onlineHelp: 'chapter_pvecm', - - border: false, - defaults: { border: false }, - - viewModel: { - parent: null, - data: { - totem: {}, - nodelist: [], - preferred_node: { - name: '', - fp: '', - addr: '' - }, - isInCluster: false, - nodecount: 0 - } - }, - - items: [ - { - xtype: 'panel', - title: gettext('Cluster Information'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store = Ext.create('Proxmox.data.UpdateStore', { - autoStart: true, - interval: 15 * 1000, - storeid: 'pve-cluster-info', - model: 'pve-cluster-info' - }); - view.store.on('load', this.onLoad, this); - view.on('destroy', view.store.stopUpdate); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records || !records[0].data) { - vm.set('totem', {}); - vm.set('isInCluster', false); - vm.set('nodelist', []); - vm.set('preferred_node', { - name: '', - addr: '', - fp: '' - }); - return; - } - var data = records[0].data; - vm.set('totem', data.totem); - vm.set('isInCluster', !!data.totem.cluster_name); - vm.set('nodelist', data.nodelist); - - var nodeinfo = Ext.Array.findBy(data.nodelist, function (el) { - return el.name === data.preferred_node; - }); - - vm.set('preferred_node', { - name: data.preferred_node, - addr: nodeinfo.pve_addr, - ring_addr: [ nodeinfo.ring0_addr, nodeinfo.ring1_addr ], - fp: nodeinfo.pve_fp - }); - }, - - onCreate: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterCreateWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - }, - - onClusterInfo: function() { - var vm = this.getViewModel(); - var win = Ext.create('PVE.ClusterInfoWindow', { - joinInfo: { - ipAddress: vm.get('preferred_node.addr'), - fingerprint: vm.get('preferred_node.fp'), - ring_addr: vm.get('preferred_node.ring_addr'), - totem: vm.get('totem') - } - }); - win.show(); - }, - - onJoin: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterJoinNodeWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - } - }, - tbar: [ - { - text: gettext('Create Cluster'), - reference: 'createButton', - handler: 'onCreate', - bind: { - disabled: '{isInCluster}' - } - }, - { - text: gettext('Join Information'), - reference: 'addButton', - handler: 'onClusterInfo', - bind: { - disabled: '{!isInCluster}' - } - }, - { - text: gettext('Join Cluster'), - reference: 'joinButton', - handler: 'onJoin', - bind: { - disabled: '{isInCluster}' - } - } - ], - layout: 'hbox', - bodyPadding: 5, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Cluster Name'), - bind: { - value: '{totem.cluster_name}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Config Version'), - bind: { - value: '{totem.config_version}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Number of Nodes'), - labelWidth: 120, - bind: { - value: '{nodecount}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - value: gettext('Standalone node - no cluster defined'), - bind: { - hidden: '{isInCluster}' - }, - flex: 1 - } - ] - }, - { - xtype: 'grid', - title: gettext('Cluster Nodes'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-cluster-nodes', - model: 'pve-cluster-nodes' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'nodeid', - order: 'DESC' - } - })); - Proxmox.Utils.monStoreErrors(view, view.rstore); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records) { - vm.set('nodecount', 0); - return; - } - vm.set('nodecount', records.length); - } - }, - columns: [ - { - header: gettext('Nodename'), - flex: 2, - dataIndex: 'name' - }, - { - header: gettext('ID'), - flex: 1, - dataIndex: 'nodeid' - }, - { - header: gettext('Votes'), - flex: 1, - dataIndex: 'quorum_votes' - }, - { - header: Ext.String.format(gettext('Link {0}'), 0), - flex: 2, - dataIndex: 'ring0_addr' - }, - { - header: Ext.String.format(gettext('Link {0}'), 1), - flex: 2, - dataIndex: 'ring1_addr' - } - ] - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.ClusterCreateWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterCreateWindow', - - title: gettext('Create Cluster'), - width: 600, - - method: 'POST', - url: '/cluster/config', - - isCreate: true, - subject: gettext('Cluster'), - showTaskViewer: true, - - onlineHelp: 'pvecm_create_cluster', - - items: { - xtype: 'inputpanel', - items: [{ - xtype: 'textfield', - fieldLabel: gettext('Cluster Name'), - allowBlank: false, - name: 'clustername' - }, - { - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 0), - emptyText: gettext("Optional, defaults to IP resolved by node's hostname"), - name: 'link0', - autoSelect: false, - valueField: 'address', - displayField: 'address', - skipEmptyText: true - }], - advancedItems: [{ - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 1), - emptyText: gettext("Optional second link for redundancy"), - name: 'link1', - autoSelect: false, - valueField: 'address', - displayField: 'address', - skipEmptyText: true - }] - } -}); - -Ext.define('PVE.ClusterInfoWindow', { - extend: 'Ext.window.Window', - xtype: 'pveClusterInfoWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 800, - modal: true, - resizable: false, - title: gettext('Cluster Join Information'), - - joinInfo: { - ipAddress: undefined, - fingerprint: undefined, - totem: {} - }, - - items: [ - { - xtype: 'component', - border: false, - padding: '10 10 10 10', - html: gettext("Copy the Join Information here and use it on the node you want to add.") - }, - { - xtype: 'container', - layout: 'form', - border: false, - padding: '0 10 10 10', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('IP Address'), - cbind: { value: '{joinInfo.ipAddress}' }, - editable: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - cbind: { value: '{joinInfo.fingerprint}' }, - editable: false - }, - { - xtype: 'textarea', - inputId: 'pveSerializedClusterInfo', - fieldLabel: gettext('Join Information'), - grow: true, - cbind: { joinInfo: '{joinInfo}' }, - editable: false, - listeners: { - afterrender: function(field) { - if (!field.joinInfo) { - return; - } - var jsons = Ext.JSON.encode(field.joinInfo); - var base64s = Ext.util.Base64.encode(jsons); - field.setValue(base64s); - } - } - } - ] - } - ], - dockedItems: [{ - dock: 'bottom', - xtype: 'toolbar', - items: [{ - xtype: 'button', - handler: function(b) { - var el = document.getElementById('pveSerializedClusterInfo'); - el.select(); - document.execCommand("copy"); - }, - text: gettext('Copy Information') - }] - }] -}); - -Ext.define('PVE.ClusterJoinNodeWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterJoinNodeWindow', - - title: gettext('Cluster Join'), - width: 800, - - method: 'POST', - url: '/cluster/config/join', - - defaultFocus: 'textarea[name=serializedinfo]', - isCreate: true, - submitText: gettext('Join'), - showTaskViewer: true, - - onlineHelp: 'chapter_pvecm', - - viewModel: { - parent: null, - data: { - info: { - fp: '', - ip: '', - ring0Needed: false, - ring1Possible: false, - ring1Needed: false - } - }, - formulas: { - ring0EmptyText: function(get) { - if (get('info.ring0Needed')) { - return gettext("Cannot use default address safely"); - } else { - return gettext("Default: IP resolved by node's hostname"); - } - } - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - '#': { - close: function() { - delete PVE.Utils.silenceAuthFailures; - } - }, - 'proxmoxcheckbox[name=assistedEntry]': { - change: 'onInputTypeChange' - }, - 'textarea[name=serializedinfo]': { - change: 'recomputeSerializedInfo', - enable: 'resetField' - }, - 'proxmoxtextfield[name=ring1_addr]': { - enable: 'ring1Needed' - }, - 'textfield': { - disable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - ring1Needed: function(f) { - var vm = this.getViewModel(); - f.allowBlank = !vm.get('info.ring1Needed'); - }, - onInputTypeChange: function(field, assistedInput) { - var vm = this.getViewModel(); - if (!assistedInput) { - vm.set('info.ring1Possible', true); - } - }, - recomputeSerializedInfo: function(field, value) { - var vm = this.getViewModel(); - var jsons = Ext.util.Base64.decode(value); - var joinInfo = Ext.JSON.decode(jsons, true); - - var info = { - fp: '', - ring1Needed: false, - ring1Possible: false, - ip: '' - }; - - var totem = {}; - if (!(joinInfo && joinInfo.totem)) { - field.valid = false; - } else { - var ring0Needed = false; - if (joinInfo.ring_addr !== undefined) { - ring0Needed = joinInfo.ring_addr[0] !== joinInfo.ipAddress; - } - - info = { - ip: joinInfo.ipAddress, - fp: joinInfo.fingerprint, - ring0Needed: ring0Needed, - ring1Possible: !!joinInfo.totem['interface']['1'], - ring1Needed: !!joinInfo.totem['interface']['1'] - }; - totem = joinInfo.totem; - field.valid = true; - } - - vm.set('info', info); - } - }, - - submit: function() { - // joining may produce temporarily auth failures, ignore as long the task runs - PVE.Utils.silenceAuthFailures = true; - this.callParent(); - }, - - taskDone: function(success) { - delete PVE.Utils.silenceAuthFailures; - if (success) { - var txt = gettext('Cluster join task finished, node certificate may have changed, reload GUI!'); - // ensure user cannot do harm - Ext.getBody().mask(txt, ['pve-static-mask']); - // TaskView may hide above mask, so tell him directly - Ext.Msg.show({ - title: gettext('Join Task Finished'), - icon: Ext.Msg.INFO, - msg: txt - }); - // reload always (if user wasn't faster), but wait a bit for pveproxy - Ext.defer(function() { - window.location.reload(true); - }, 5000); - } - }, - - items: [{ - xtype: 'proxmoxcheckbox', - reference: 'assistedEntry', - name: 'assistedEntry', - submitValue: false, - value: true, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Select if join information should be extracted from pasted cluster information, deselect for manual entering') - }, - boxLabel: gettext('Assisted join: Paste encoded cluster join information and enter password.') - }, - { - xtype: 'textarea', - name: 'serializedinfo', - submitValue: false, - allowBlank: false, - fieldLabel: gettext('Information'), - emptyText: gettext('Paste encoded Cluster Information here'), - validator: function(val) { - return val === '' || this.valid || - gettext('Does not seem like a valid encoded Cluster Information!'); - }, - bind: { - disabled: '{!assistedEntry.checked}', - hidden: '{!assistedEntry.checked}' - }, - value: '' - }, - { - xtype: 'inputpanel', - column1: [ - { - xtype: 'textfield', - fieldLabel: gettext('Peer Address'), - allowBlank: false, - bind: { - value: '{info.ip}', - readOnly: '{assistedEntry.checked}' - }, - name: 'hostname' - }, - { - xtype: 'textfield', - inputType: 'password', - emptyText: gettext("Peer's root password"), - fieldLabel: gettext('Password'), - allowBlank: false, - name: 'password' - } - ], - column2: [ - { - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 0), - bind: { - emptyText: '{ring0EmptyText}', - allowBlank: '{!info.ring0Needed}' - }, - skipEmptyText: true, - autoSelect: false, - valueField: 'address', - displayField: 'address', - name: 'link0' - }, - { - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 1), - skipEmptyText: true, - autoSelect: false, - valueField: 'address', - displayField: 'address', - bind: { - disabled: '{!info.ring1Possible}', - allowBlank: '{!info.ring1Needed}', - }, - name: 'link1' - } - ], - columnB: [ - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - allowBlank: false, - bind: { - value: '{info.fp}', - readOnly: '{assistedEntry.checked}' - }, - name: 'fingerprint' - } - ] - }] -}); -/* - * Workspace base class - * - * popup login window when auth fails (call onLogin handler) - * update (re-login) ticket every 15 minutes - * - */ - -Ext.define('PVE.Workspace', { - extend: 'Ext.container.Viewport', - - title: 'Proxmox Virtual Environment', - - loginData: null, // Data from last login call - - onLogin: function(loginData) {}, - - // private - updateLoginData: function(loginData) { - var me = this; - me.loginData = loginData; - Proxmox.Utils.setAuthData(loginData); - - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(loginData.clustername); - - if (loginData.cap) { - Ext.state.Manager.set('GuiCap', loginData.cap); - } - me.response401count = 0; - - me.onLogin(loginData); - }, - - // private - showLogin: function() { - var me = this; - - Proxmox.Utils.authClear(); - Proxmox.UserName = null; - me.loginData = null; - - if (!me.login) { - me.login = Ext.create('PVE.window.LoginWindow', { - handler: function(data) { - me.login = null; - me.updateLoginData(data); - Proxmox.Utils.checked_command(function() {}); // display subscription status - } - }); - } - me.onLogin(null); - me.login.show(); - }, - - initComponent : function() { - var me = this; - - Ext.tip.QuickTipManager.init(); - - // fixme: what about other errors - Ext.Ajax.on('requestexception', function(conn, response, options) { - if (response.status == 401 && !PVE.Utils.silenceAuthFailures) { // auth failure - // don't immediately show as logged out to cope better with some big - // upgrades, which may temporarily produce a false positive 401 err - me.response401count++; - if (me.response401count > 5) { - me.showLogin(); - } - } - }); - - me.callParent(); - - if (!Proxmox.Utils.authOK()) { - me.showLogin(); - } else { - if (me.loginData) { - me.onLogin(me.loginData); - } - } - - Ext.TaskManager.start({ - run: function() { - var ticket = Proxmox.Utils.authOK(); - if (!ticket || !Proxmox.UserName) { - return; - } - - Ext.Ajax.request({ - params: { - username: Proxmox.UserName, - password: ticket - }, - url: '/api2/json/access/ticket', - method: 'POST', - success: function(response, opts) { - var obj = Ext.decode(response.responseText); - me.updateLoginData(obj.data); - } - }); - }, - interval: 15*60*1000 - }); - - } -}); - -Ext.define('PVE.StdWorkspace', { - extend: 'PVE.Workspace', - - alias: ['widget.pveStdWorkspace'], - - // private - setContent: function(comp) { - var me = this; - - var cont = me.child('#content'); - - var lay = cont.getLayout(); - - var cur = lay.getActiveItem(); - - if (comp) { - Proxmox.Utils.setErrorMask(cont, false); - comp.border = false; - cont.add(comp); - if (cur !== null && lay.getNext()) { - lay.next(); - var task = Ext.create('Ext.util.DelayedTask', function(){ - cont.remove(cur); - }); - task.delay(10); - } - } - else { - // helper for cleaning the content when logging out - cont.removeAll(); - } - }, - - selectById: function(nodeid) { - var me = this; - var tree = me.down('pveResourceTree'); - tree.selectById(nodeid); - }, - - onLogin: function(loginData) { - var me = this; - - me.updateUserInfo(); - - if (loginData) { - PVE.data.ResourceStore.startUpdate(); - - Proxmox.Utils.API2Request({ - url: '/version', - method: 'GET', - success: function(response) { - PVE.VersionInfo = response.result.data; - me.updateVersionInfo(); - } - }); - } - }, - - updateUserInfo: function() { - var me = this; - var ui = me.query('#userinfo')[0]; - ui.setText(Proxmox.UserName || ''); - ui.updateLayout(); - }, - - updateVersionInfo: function() { - var me = this; - - var ui = me.query('#versioninfo')[0]; - - if (PVE.VersionInfo) { - var version = PVE.VersionInfo.version; - ui.update('Virtual Environment ' + version); - } else { - ui.update('Virtual Environment'); - } - ui.updateLayout(); - }, - - initComponent : function() { - var me = this; - - Ext.History.init(); - - var sprovider = Ext.create('PVE.StateProvider'); - Ext.state.Manager.setProvider(sprovider); - - var selview = Ext.create('PVE.form.ViewSelector'); - - var rtree = Ext.createWidget('pveResourceTree', { - viewFilter: selview.getViewFilter(), - flex: 1, - selModel: { - selType: 'treemodel', - listeners: { - selectionchange: function(sm, selected) { - if (selected.length > 0) { - var n = selected[0]; - var tlckup = { - root: 'PVE.dc.Config', - node: 'PVE.node.Config', - qemu: 'PVE.qemu.Config', - lxc: 'PVE.lxc.Config', - storage: 'PVE.storage.Browser', - pool: 'pvePoolConfig' - }; - var comp = { - xtype: tlckup[n.data.type || 'root'] || - 'pvePanelConfig', - showSearch: (n.data.id === 'root') || - Ext.isDefined(n.data.groupbyid), - pveSelNode: n, - workspace: me, - viewFilter: selview.getViewFilter() - }; - PVE.curSelectedNode = n; - me.setContent(comp); - } - } - } - } - }); - - selview.on('select', function(combo, records) { - if (records) { - var view = combo.getViewFilter(); - rtree.setViewFilter(view); - } - }); - - var caps = sprovider.get('GuiCap'); - - var createVM = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-desktop', - text: gettext("Create VM"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.qemu.CreateWizard', {}); - wiz.show(); - } - }); - - var createCT = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-cube', - text: gettext("Create CT"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.lxc.CreateWizard', {}); - wiz.show(); - } - }); - - sprovider.on('statechange', function(sp, key, value) { - if (key === 'GuiCap' && value) { - caps = value; - createVM.setDisabled(!caps.vms['VM.Allocate']); - createCT.setDisabled(!caps.vms['VM.Allocate']); - } - }); - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - { - region: 'north', - layout: { - type: 'hbox', - align: 'middle' - }, - baseCls: 'x-plain', - defaults: { - baseCls: 'x-plain' - }, - border: false, - margin: '2 0 2 5', - items: [ - { - html: '' + - '' - }, - { - minWidth: 150, - id: 'versioninfo', - html: 'Virtual Environment' - }, - { - xtype: 'pveGlobalSearchField', - tree: rtree - }, - { - flex: 1 - }, - { - xtype: 'proxmoxHelpButton', - hidden: false, - baseCls: 'x-btn', - iconCls: 'fa fa-book x-btn-icon-el-default-toolbar-small ', - listenToGlobalEvent: false, - onlineHelp: 'pve_documentation_index', - text: gettext('Documentation'), - margin: '0 5 0 0' - }, - createVM, - createCT, - { - pack: 'end', - margin: '0 5 0 0', - id: 'userinfo', - xtype: 'button', - baseCls: 'x-btn', - style: { - // proxmox dark grey p light grey as border - backgroundColor: '#464d4d', - borderColor: '#ABBABA' - }, - iconCls: 'fa fa-user', - menu: [ - { - iconCls: 'fa fa-gear', - text: gettext('My Settings'), - handler: function() { - var win = Ext.create('PVE.window.Settings'); - win.show(); - } - }, - { - text: gettext('Password'), - iconCls: 'fa fa-fw fa-key', - handler: function() { - var win = Ext.create('Proxmox.window.PasswordEdit', { - userid: Proxmox.UserName - }); - win.show(); - } - }, - { - text: 'TFA', - iconCls: 'fa fa-fw fa-lock', - handler: function(btn, event, rec) { - var win = Ext.create('PVE.window.TFAEdit',{ - userid: Proxmox.UserName - }); - win.show(); - } - }, - '-', - { - iconCls: 'fa fa-fw fa-sign-out', - text: gettext("Logout"), - handler: function() { - PVE.data.ResourceStore.loadData([], false); - me.showLogin(); - me.setContent(null); - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(undefined); - rt.clearTree(); - - // empty the stores of the StatusPanel child items - var statusPanels = Ext.ComponentQuery.query('pveStatusPanel grid'); - Ext.Array.forEach(statusPanels, function(comp) { - if (comp.getStore()) { - comp.getStore().loadData([], false); - } - }); - } - } - ] - } - ] - }, - { - region: 'center', - stateful: true, - stateId: 'pvecenter', - minWidth: 100, - minHeight: 100, - id: 'content', - xtype: 'container', - layout: { type: 'card' }, - border: false, - margin: '0 5 0 0', - items: [] - }, - { - region: 'west', - stateful: true, - stateId: 'pvewest', - itemId: 'west', - xtype: 'container', - border: false, - layout: { type: 'vbox', align: 'stretch' }, - margin: '0 0 0 5', - split: true, - width: 200, - items: [ selview, rtree ], - listeners: { - resize: function(panel, width, height) { - var viewWidth = me.getSize().width; - if (width > viewWidth - 100) { - panel.setWidth(viewWidth - 100); - } - } - } - }, - { - xtype: 'pveStatusPanel', - stateful: true, - stateId: 'pvesouth', - itemId: 'south', - region: 'south', - margin:'0 5 5 5', - title: gettext('Logs'), - collapsible: true, - header: false, - height: 200, - split:true, - listeners: { - resize: function(panel, width, height) { - var viewHeight = me.getSize().height; - if (height > (viewHeight - 150)) { - panel.setHeight(viewHeight - 150); - } - } - } - } - ] - }); - - me.callParent(); - - me.updateUserInfo(); - - // on resize, center all modal windows - Ext.on('resize', function(){ - var wins = Ext.ComponentQuery.query('window[modal]'); - if (wins.length > 0) { - wins.forEach(function(win){ - win.alignTo(me, 'c-c'); - }); - } - }); - } -}); - diff --git a/serverside/jsmod/6.1-3.sh b/serverside/jsmod/6.1-3.sh deleted file mode 100644 index b0575a8..0000000 --- a/serverside/jsmod/6.1-3.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -Say () { - printf "\e[1;34m $1 \e[0m \n"; -} - -DotSay () { - printf "[\e[1;34m*\e[0m] \e[1;34m $1 \e[0m \n"; -} - - -Say '[PVE Discord Dark UI Theme JSMOD Installer]' -Say 'Internet connection REQUIRED.' -Say '!!ONLY FOR PVE 6.0-4 - 6.1-x!!' -Say '>Press any key to begin installation' -read -p "" -Say ' ' -DotSay 'Backing up files' -cp /usr/share/pve-manager/js/pvemanagerlib.js /usr/share/pve-manager/js/pvemanagerlib.js.bak -cp /usr/share/javascript/extjs/charts.js /usr/share/javascript/extjs/charts.js.bak -cp /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.bak -DotSay 'Modding files' -#replace white with #23272a -sed -i -e "s/'white'/'#23272a'/g" /usr/share/pve-manager/js/pvemanagerlib.js - -#Proxmox changed nothing here to last version, so we can use it without modifications -rm /usr/share/javascript/extjs/charts.js -wget https://raw.githubusercontent.com/Weilbyte/PVEDiscordDark/master/serverside/jsmod/6.0-4/charts.js -P /usr/share/javascript/extjs/ &> /dev/null - -sed -i -e "s/#c2ddf2/#7289DA/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js -sed -i -e "s/#f5f5f5/#2C2F33/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js - -DotSay 'Applied patch successfully.' -Say '' -Say 'Installation finished!' -Say 'o7' diff --git a/serverside/jsmod/6.1-3/charts.js b/serverside/jsmod/6.1-3/charts.js deleted file mode 100644 index 713bec3..0000000 --- a/serverside/jsmod/6.1-3/charts.js +++ /dev/null @@ -1,22013 +0,0 @@ -Ext.define("Ext.draw.ContainerBase", { - extend: "Ext.panel.Panel", - requires: ["Ext.window.Window"], - previewTitleText: "Chart Preview", - previewAltText: "Chart preview", - layout: "container", - addElementListener: function() { - var b = this, - a = arguments; - if (b.rendered) { - b.el.on.apply(b.el, a) - } else { - b.on("render", function() { - b.el.on.apply(b.el, a) - }) - } - }, - removeElementListener: function() { - var b = this, - a = arguments; - if (b.rendered) { - b.el.un.apply(b.el, a) - } - }, - afterRender: function() { - this.callParent(arguments); - this.initAnimator() - }, - getItems: function() { - var b = this, - a = b.items; - if (!a || !a.isMixedCollection) { - b.initItems() - } - return b.items - }, - onRender: function() { - this.callParent(arguments); - this.element = this.el; - this.innerElement = this.body - }, - setItems: function(a) { - this.items = a; - return a - }, - setSurfaceSize: function(b, a) { - this.resizeHandler({ - width: b, - height: a - }); - this.renderFrame() - }, - onResize: function(c, a, b, e) { - var d = this; - d.callParent([c, a, b, e]); - d.setBodySize({ - width: c, - height: a - }) - }, - preview: function() { - var a = this.getImage(); - new Ext.window.Window({ - title: this.previewTitleText, - closeable: true, - renderTo: Ext.getBody(), - autoShow: true, - maximizeable: true, - maximized: true, - border: true, - layout: { - type: "hbox", - pack: "center", - align: "middle" - }, - items: { - xtype: "container", - items: { - xtype: "image", - mode: "img", - cls: Ext.baseCSSPrefix + "chart-image", - alt: this.previewAltText, - src: a.data, - listeners: { - afterrender: function() { - var e = this, - b = e.imgEl.dom, - d = a.type === "svg" ? 1 : (window.devicePixelRatio || 1), - c; - if (!b.naturalWidth || !b.naturalHeight) { - b.onload = function() { - var g = b.naturalWidth, - f = b.naturalHeight; - e.setWidth(Math.floor(g / d)); - e.setHeight(Math.floor(f / d)) - } - } else { - c = e.getSize(); - e.setWidth(Math.floor(c.width / d)); - e.setHeight(Math.floor(c.height / d)) - } - } - } - } - } - }) - }, - privates: { - getTargetEl: function() { - return this.innerElement - }, - reattachToBody: function() { - var a = this; - if (a.pendingDetachSize) { - a.onBodyResize() - } - a.pendingDetachSize = false; - a.callParent() - } - } -}); -Ext.define("Ext.draw.SurfaceBase", { - extend: "Ext.Widget", - getOwnerBody: function() { - return this.ownerCt.body - }, - destroy: function() { - var a = this; - if (a.hasListeners.destroy) { - a.fireEvent("destroy", a) - } - a.callParent() - } -}); -Ext.define("Ext.draw.Color", { - statics: { - colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/, - rgbToHexRe: /\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/, - rgbaToHexRe: /\s*rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\.\d]+)\)/, - hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/, - NONE: "none", - RGBA_NONE: "rgba(0, 0, 0, 0)" - }, - isColor: true, - lightnessFactor: 0.2, - constructor: function(d, b, a, c) { - this.setRGB(d, b, a, c) - }, - setRGB: function(e, c, a, d) { - var b = this; - b.r = Math.min(255, Math.max(0, e)); - b.g = Math.min(255, Math.max(0, c)); - b.b = Math.min(255, Math.max(0, a)); - if (d === undefined) { - b.a = 1 - } else { - b.a = Math.min(1, Math.max(0, d)) - } - }, - getGrayscale: function() { - return this.r * 0.3 + this.g * 0.59 + this.b * 0.11 - }, - getHSL: function() { - var i = this, - a = i.r / 255, - f = i.g / 255, - j = i.b / 255, - k = Math.max(a, f, j), - d = Math.min(a, f, j), - m = k - d, - e, n = 0, - c = 0.5 * (k + d); - if (d !== k) { - n = (c <= 0.5) ? m / (k + d) : m / (2 - k - d); - if (a === k) { - e = 60 * (f - j) / m - } else { - if (f === k) { - e = 120 + 60 * (j - a) / m - } else { - e = 240 + 60 * (a - f) / m - } - } - if (e < 0) { - e += 360 - } - if (e >= 360) { - e -= 360 - } - } - return [e, n, c] - }, - getHSV: function() { - var i = this, - a = i.r / 255, - f = i.g / 255, - j = i.b / 255, - k = Math.max(a, f, j), - d = Math.min(a, f, j), - c = k - d, - e, m = 0, - l = k; - if (d != k) { - m = l ? c / l : 0; - if (a === k) { - e = 60 * (f - j) / c - } else { - if (f === k) { - e = 60 * (j - a) / c + 120 - } else { - e = 60 * (a - f) / c + 240 - } - } - if (e < 0) { - e += 360 - } - if (e >= 360) { - e -= 360 - } - } - return [e, m, l] - }, - setHSL: function(g, f, e) { - var i = this, - d = Math.abs, - j, b, a; - g = (g % 360 + 360) % 360; - f = f > 1 ? 1 : f < 0 ? 0 : f; - e = e > 1 ? 1 : e < 0 ? 0 : e; - if (f === 0 || g === null) { - e *= 255; - i.setRGB(e, e, e) - } else { - g /= 60; - j = f * (1 - d(2 * e - 1)); - b = j * (1 - d(g % 2 - 1)); - a = e - j / 2; - a *= 255; - j *= 255; - b *= 255; - switch (Math.floor(g)) { - case 0: - i.setRGB(j + a, b + a, a); - break; - case 1: - i.setRGB(b + a, j + a, a); - break; - case 2: - i.setRGB(a, j + a, b + a); - break; - case 3: - i.setRGB(a, b + a, j + a); - break; - case 4: - i.setRGB(b + a, a, j + a); - break; - case 5: - i.setRGB(j + a, a, b + a); - break - } - } - return i - }, - setHSV: function(f, e, d) { - var g = this, - i, b, a; - f = (f % 360 + 360) % 360; - e = e > 1 ? 1 : e < 0 ? 0 : e; - d = d > 1 ? 1 : d < 0 ? 0 : d; - if (e === 0 || f === null) { - d *= 255; - g.setRGB(d, d, d) - } else { - f /= 60; - i = d * e; - b = i * (1 - Math.abs(f % 2 - 1)); - a = d - i; - a *= 255; - i *= 255; - b *= 255; - switch (Math.floor(f)) { - case 0: - g.setRGB(i + a, b + a, a); - break; - case 1: - g.setRGB(b + a, i + a, a); - break; - case 2: - g.setRGB(a, i + a, b + a); - break; - case 3: - g.setRGB(a, b + a, i + a); - break; - case 4: - g.setRGB(b + a, a, i + a); - break; - case 5: - g.setRGB(i + a, a, b + a); - break - } - } - return g - }, - createLighter: function(b) { - if (!b && b !== 0) { - b = this.lightnessFactor - } - var a = this.getHSL(); - a[2] = Ext.Number.constrain(a[2] + b, 0, 1); - return Ext.draw.Color.fromHSL(a[0], a[1], a[2]) - }, - createDarker: function(a) { - if (!a && a !== 0) { - a = this.lightnessFactor - } - return this.createLighter(-a) - }, - toString: function() { - var f = this, - c = Math.round; - if (f.a === 1) { - var e = c(f.r).toString(16), - d = c(f.g).toString(16), - a = c(f.b).toString(16); - e = (e.length === 1) ? "0" + e : e; - d = (d.length === 1) ? "0" + d : d; - a = (a.length === 1) ? "0" + a : a; - return ["#", e, d, a].join("") - } else { - return "rgba(" + [c(f.r), c(f.g), c(f.b), f.a === 0 ? 0 : f.a.toFixed(15)].join(", ") + ")" - } - }, - toHex: function(b) { - if (Ext.isArray(b)) { - b = b[0] - } - if (!Ext.isString(b)) { - return "" - } - if (b.substr(0, 1) === "#") { - return b - } - var e = Ext.draw.Color.colorToHexRe.exec(b); - if (Ext.isArray(e)) { - var f = parseInt(e[2], 10), - d = parseInt(e[3], 10), - a = parseInt(e[4], 10), - c = a | (d << 8) | (f << 16); - return e[1] + "#" + ("000000" + c.toString(16)).slice(-6) - } else { - return "" - } - }, - setFromString: function(j) { - var e, h, f, c, d = 1, - i = parseInt; - if (j === Ext.draw.Color.NONE) { - this.r = this.g = this.b = this.a = 0; - return this - } - if ((j.length === 4 || j.length === 7) && j.substr(0, 1) === "#") { - e = j.match(Ext.draw.Color.hexRe); - if (e) { - h = i(e[1], 16) >> 0; - f = i(e[2], 16) >> 0; - c = i(e[3], 16) >> 0; - if (j.length === 4) { - h += (h * 16); - f += (f * 16); - c += (c * 16) - } - } - } else { - if ((e = j.match(Ext.draw.Color.rgbToHexRe))) { - h = +e[1]; - f = +e[2]; - c = +e[3] - } else { - if ((e = j.match(Ext.draw.Color.rgbaToHexRe))) { - h = +e[1]; - f = +e[2]; - c = +e[3]; - d = +e[4] - } else { - if (Ext.draw.Color.ColorList.hasOwnProperty(j.toLowerCase())) { - return this.setFromString(Ext.draw.Color.ColorList[j.toLowerCase()]) - } - } - } - } - if (typeof h === "undefined") { - return this - } - this.r = h; - this.g = f; - this.b = c; - this.a = d; - return this - } -}, function() { - var a = new this(); - this.addStatics({ - fly: function(f, e, c, d) { - switch (arguments.length) { - case 1: - a.setFromString(f); - break; - case 3: - case 4: - a.setRGB(f, e, c, d); - break; - default: - return null - } - return a - }, - ColorList: { - aliceblue: "#f0f8ff", - antiquewhite: "#faebd7", - aqua: "#00ffff", - aquamarine: "#7fffd4", - azure: "#f0ffff", - beige: "#f5f5dc", - bisque: "#ffe4c4", - black: "#000000", - blanchedalmond: "#ffebcd", - blue: "#0000ff", - blueviolet: "#8a2be2", - brown: "#a52a2a", - burlywood: "#deb887", - cadetblue: "#5f9ea0", - chartreuse: "#7fff00", - chocolate: "#d2691e", - coral: "#ff7f50", - cornflowerblue: "#6495ed", - cornsilk: "#fff8dc", - crimson: "#dc143c", - cyan: "#00ffff", - darkblue: "#00008b", - darkcyan: "#008b8b", - darkgoldenrod: "#b8860b", - darkgray: "#a9a9a9", - darkgreen: "#006400", - darkkhaki: "#bdb76b", - darkmagenta: "#8b008b", - darkolivegreen: "#556b2f", - darkorange: "#ff8c00", - darkorchid: "#9932cc", - darkred: "#8b0000", - darksalmon: "#e9967a", - darkseagreen: "#8fbc8f", - darkslateblue: "#483d8b", - darkslategray: "#2f4f4f", - darkturquoise: "#00ced1", - darkviolet: "#9400d3", - deeppink: "#ff1493", - deepskyblue: "#00bfff", - dimgray: "#696969", - dodgerblue: "#1e90ff", - firebrick: "#b22222", - floralwhite: "#fffaf0", - forestgreen: "#228b22", - fuchsia: "#ff00ff", - gainsboro: "#dcdcdc", - ghostwhite: "#f8f8ff", - gold: "#ffd700", - goldenrod: "#daa520", - gray: "#808080", - green: "#008000", - greenyellow: "#adff2f", - honeydew: "#f0fff0", - hotpink: "#ff69b4", - indianred: "#cd5c5c", - indigo: "#4b0082", - ivory: "#fffff0", - khaki: "#f0e68c", - lavender: "#e6e6fa", - lavenderblush: "#fff0f5", - lawngreen: "#7cfc00", - lemonchiffon: "#fffacd", - lightblue: "#add8e6", - lightcoral: "#f08080", - lightcyan: "#e0ffff", - lightgoldenrodyellow: "#fafad2", - lightgray: "#d3d3d3", - lightgrey: "#d3d3d3", - lightgreen: "#90ee90", - lightpink: "#ffb6c1", - lightsalmon: "#ffa07a", - lightseagreen: "#20b2aa", - lightskyblue: "#87cefa", - lightslategray: "#778899", - lightsteelblue: "#b0c4de", - lightyellow: "#ffffe0", - lime: "#00ff00", - limegreen: "#32cd32", - linen: "#faf0e6", - magenta: "#ff00ff", - maroon: "#800000", - mediumaquamarine: "#66cdaa", - mediumblue: "#0000cd", - mediumorchid: "#ba55d3", - mediumpurple: "#9370d8", - mediumseagreen: "#3cb371", - mediumslateblue: "#7b68ee", - mediumspringgreen: "#00fa9a", - mediumturquoise: "#48d1cc", - mediumvioletred: "#c71585", - midnightblue: "#191970", - mintcream: "#f5fffa", - mistyrose: "#ffe4e1", - moccasin: "#ffe4b5", - navajowhite: "#ffdead", - navy: "#000080", - oldlace: "#fdf5e6", - olive: "#808000", - olivedrab: "#6b8e23", - orange: "#ffa500", - orangered: "#ff4500", - orchid: "#da70d6", - palegoldenrod: "#eee8aa", - palegreen: "#98fb98", - paleturquoise: "#afeeee", - palevioletred: "#d87093", - papayawhip: "#ffefd5", - peachpuff: "#ffdab9", - peru: "#cd853f", - pink: "#ffc0cb", - plum: "#dda0dd", - powderblue: "#b0e0e6", - purple: "#800080", - red: "#ff0000", - rosybrown: "#bc8f8f", - royalblue: "#4169e1", - saddlebrown: "#8b4513", - salmon: "#fa8072", - sandybrown: "#f4a460", - seagreen: "#2e8b57", - seashell: "#fff5ee", - sienna: "#a0522d", - silver: "#c0c0c0", - skyblue: "#87ceeb", - slateblue: "#6a5acd", - slategray: "#708090", - snow: "#fffafa", - springgreen: "#00ff7f", - steelblue: "#4682b4", - tan: "#d2b48c", - teal: "#008080", - thistle: "#d8bfd8", - tomato: "#ff6347", - turquoise: "#40e0d0", - violet: "#ee82ee", - wheat: "#f5deb3", - white: "#ffffff", - whitesmoke: "#f5f5f5", - yellow: "#ffff00", - yellowgreen: "#9acd32" - }, - fromHSL: function(d, c, b) { - return (new this(0, 0, 0, 0)).setHSL(d, c, b) - }, - fromHSV: function(d, c, b) { - return (new this(0, 0, 0, 0)).setHSL(d, c, b) - }, - fromString: function(b) { - return (new this(0, 0, 0, 0)).setFromString(b) - }, - create: function(b) { - if (b instanceof this) { - return b - } else { - if (Ext.isArray(b)) { - return new Ext.draw.Color(b[0], b[1], b[2], b[3]) - } else { - if (Ext.isString(b)) { - return Ext.draw.Color.fromString(b) - } else { - if (arguments.length > 2) { - return new Ext.draw.Color(arguments[0], arguments[1], arguments[2], arguments[3]) - } else { - return new Ext.draw.Color(0, 0, 0, 0) - } - } - } - } - } - }) -}); -Ext.define("Ext.draw.sprite.AnimationParser", function() { - function a(d, c, b) { - return d + (c - d) * b - } - return { - singleton: true, - attributeRe: /^url\(#([a-zA-Z\-]+)\)$/, - requires: ["Ext.draw.Color"], - color: { - parseInitial: function(c, b) { - if (Ext.isString(c)) { - c = Ext.draw.Color.create(c) - } - if (Ext.isString(b)) { - b = Ext.draw.Color.create(b) - } - if ((c instanceof Ext.draw.Color) && (b instanceof Ext.draw.Color)) { - return [ - [c.r, c.g, c.b, c.a], - [b.r, b.g, b.b, b.a] - ] - } else { - return [c || b, b || c] - } - }, - compute: function(d, c, b) { - if (!Ext.isArray(d) || !Ext.isArray(c)) { - return c || d - } else { - return [a(d[0], c[0], b), a(d[1], c[1], b), a(d[2], c[2], b), a(d[3], c[3], b)] - } - }, - serve: function(c) { - var b = Ext.draw.Color.fly(c[0], c[1], c[2], c[3]); - return b.toString() - } - }, - number: { - parse: function(b) { - return b === null ? null : +b - }, - compute: function(d, c, b) { - if (!Ext.isNumber(d) || !Ext.isNumber(c)) { - return c || d - } else { - return a(d, c, b) - } - } - }, - angle: { - parseInitial: function(c, b) { - if (b - c > Math.PI) { - b -= Math.PI * 2 - } else { - if (b - c < -Math.PI) { - b += Math.PI * 2 - } - } - return [c, b] - }, - compute: function(d, c, b) { - if (!Ext.isNumber(d) || !Ext.isNumber(c)) { - return c || d - } else { - return a(d, c, b) - } - } - }, - path: { - parseInitial: function(m, n) { - var c = m.toStripes(), - o = n.toStripes(), - e, d, k = c.length, - p = o.length, - h, f, b, g = o[p - 1], - l = [g[g.length - 2], g[g.length - 1]]; - for (e = k; e < p; e++) { - c.push(c[k - 1].slice(0)) - } - for (e = p; e < k; e++) { - o.push(l.slice(0)) - } - b = c.length; - o.path = n; - o.temp = new Ext.draw.Path(); - for (e = 0; e < b; e++) { - h = c[e]; - f = o[e]; - k = h.length; - p = f.length; - o.temp.commands.push("M"); - for (d = p; d < k; d += 6) { - f.push(l[0], l[1], l[0], l[1], l[0], l[1]) - } - g = o[o.length - 1]; - l = [g[g.length - 2], g[g.length - 1]]; - for (d = k; d < p; d += 6) { - h.push(l[0], l[1], l[0], l[1], l[0], l[1]) - } - for (e = 0; e < f.length; e++) { - f[e] -= h[e] - } - for (e = 2; e < f.length; e += 6) { - o.temp.commands.push("C") - } - } - return [c, o] - }, - compute: function(c, l, m) { - if (m >= 1) { - return l.path - } - var e = 0, - f = c.length, - d = 0, - b, k, h, n = l.temp.params, - g = 0; - for (; e < f; e++) { - k = c[e]; - h = l[e]; - b = k.length; - for (d = 0; d < b; d++) { - n[g++] = h[d] * m + k[d] - } - } - return l.temp - } - }, - data: { - compute: function(h, j, k, g) { - var m = h.length - 1, - b = j.length - 1, - e = Math.max(m, b), - d, l, c; - if (!g || g === h) { - g = [] - } - g.length = e + 1; - for (c = 0; c <= e; c++) { - d = h[Math.min(c, m)]; - l = j[Math.min(c, b)]; - if (Ext.isNumber(d)) { - if (!Ext.isNumber(l)) { - l = 0 - } - g[c] = (l - d) * k + d - } else { - g[c] = l - } - } - return g - } - }, - text: { - compute: function(d, c, b) { - return d.substr(0, Math.round(d.length * (1 - b))) + c.substr(Math.round(c.length * (1 - b))) - } - }, - limited: "number", - limited01: "number" - } -}); -(function() { - if (!Ext.global.Float32Array) { - var a = function(d) { - if (typeof d === "number") { - this.length = d - } else { - if ("length" in d) { - this.length = d.length; - for (var c = 0, b = d.length; c < b; c++) { - this[c] = +d[c] - } - } - } - }; - a.prototype = []; - Ext.global.Float32Array = a - } -})(); -Ext.define("Ext.draw.Draw", { - singleton: true, - radian: Math.PI / 180, - pi2: Math.PI * 2, - reflectFn: function(b) { - return b - }, - rad: function(a) { - return (a % 360) * this.radian - }, - degrees: function(a) { - return (a / this.radian) % 360 - }, - isBBoxIntersect: function(b, a, c) { - c = c || 0; - return (Math.max(b.x, a.x) - c > Math.min(b.x + b.width, a.x + a.width)) || (Math.max(b.y, a.y) - c > Math.min(b.y + b.height, a.y + a.height)) - }, - isPointInBBox: function(a, c, b) { - return !!b && a >= b.x && a <= (b.x + b.width) && c >= b.y && c <= (b.y + b.height) - }, - spline: function(m) { - var e, c, k = m.length, - b, h, l, f, a = 0, - g = new Float32Array(m.length), - n = new Float32Array(m.length * 3 - 2); - g[0] = 0; - g[k - 1] = 0; - for (e = 1; e < k - 1; e++) { - g[e] = (m[e + 1] + m[e - 1] - 2 * m[e]) - g[e - 1]; - a = 1 / (4 - a); - g[e] *= a - } - for (e = k - 2; e > 0; e--) { - a = 3.732050807568877 + 48.248711305964385 / (-13.928203230275537 + Math.pow(0.07179676972449123, e)); - g[e] -= g[e + 1] * a - } - f = m[0]; - b = f - g[0]; - for (e = 0, c = 0; e < k - 1; c += 3) { - l = f; - h = b; - e++; - f = m[e]; - b = f - g[e]; - n[c] = l; - n[c + 1] = (b + 2 * h) / 3; - n[c + 2] = (b * 2 + h) / 3 - } - n[c] = f; - return n - }, - getAnchors: function(e, d, i, h, t, s, o) { - o = o || 4; - var n = Math.PI, - p = n / 2, - k = Math.abs, - a = Math.sin, - b = Math.cos, - f = Math.atan, - r, q, g, j, m, l, v, u, c; - r = (i - e) / o; - q = (t - i) / o; - if ((h >= d && h >= s) || (h <= d && h <= s)) { - g = j = p - } else { - g = f((i - e) / k(h - d)); - if (d < h) { - g = n - g - } - j = f((t - i) / k(h - s)); - if (s < h) { - j = n - j - } - } - c = p - ((g + j) % (n * 2)) / 2; - if (c > p) { - c -= n - } - g += c; - j += c; - m = i - r * a(g); - l = h + r * b(g); - v = i + q * a(j); - u = h + q * b(j); - if ((h > d && l < d) || (h < d && l > d)) { - m += k(d - l) * (m - i) / (l - h); - l = d - } - if ((h > s && u < s) || (h < s && u > s)) { - v -= k(s - u) * (v - i) / (u - h); - u = s - } - return { - x1: m, - y1: l, - x2: v, - y2: u - } - }, - smooth: function(l, j, o) { - var k = l.length, - h, g, c, b, q, p, n, m, f = [], - e = [], - d, a; - for (d = 0; d < k - 1; d++) { - h = l[d]; - g = j[d]; - if (d === 0) { - n = h; - m = g; - f.push(n); - e.push(m); - if (k === 1) { - break - } - } - c = l[d + 1]; - b = j[d + 1]; - q = l[d + 2]; - p = j[d + 2]; - if (!Ext.isNumber(q + p)) { - f.push(n, c, c); - e.push(m, b, b); - break - } - a = this.getAnchors(h, g, c, b, q, p, o); - f.push(n, a.x1, c); - e.push(m, a.y1, b); - n = a.x2; - m = a.y2 - } - return { - smoothX: f, - smoothY: e - } - }, - beginUpdateIOS: Ext.os.is.iOS ? function() { - this.iosUpdateEl = Ext.getBody().createChild({ - style: "position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; background: rgba(0,0,0,0.001); z-index: 100000" - }) - } : Ext.emptyFn, - endUpdateIOS: function() { - this.iosUpdateEl = Ext.destroy(this.iosUpdateEl) - } -}); -Ext.define("Ext.draw.gradient.Gradient", { - requires: ["Ext.draw.Color"], - isGradient: true, - config: { - stops: [] - }, - applyStops: function(f) { - var e = [], - d = f.length, - c, b, a; - for (c = 0; c < d; c++) { - b = f[c]; - a = b.color; - if (!(a && a.isColor)) { - a = Ext.draw.Color.fly(a || Ext.draw.Color.NONE) - } - e.push({ - offset: Math.min(1, Math.max(0, "offset" in b ? b.offset : b.position || 0)), - color: a.toString() - }) - } - e.sort(function(h, g) { - return h.offset - g.offset - }); - return e - }, - onClassExtended: function(a, b) { - if (!b.alias && b.type) { - b.alias = "gradient." + b.type - } - }, - constructor: function(a) { - this.initConfig(a) - }, - generateGradient: Ext.emptyFn -}); -Ext.define("Ext.draw.gradient.GradientDefinition", { - singleton: true, - urlStringRe: /^url\(#([\w\-]+)\)$/, - gradients: {}, - add: function(a) { - var b = this.gradients, - c, e, d; - for (c = 0, e = a.length; c < e; c++) { - d = a[c]; - if (Ext.isString(d.id)) { - b[d.id] = d - } - } - }, - get: function(d) { - var a = this.gradients, - b = d.match(this.urlStringRe), - c; - if (b && b[1] && (c = a[b[1]])) { - return c || d - } - return d - } -}); -Ext.define("Ext.draw.sprite.AttributeParser", { - singleton: true, - attributeRe: /^url\(#([a-zA-Z\-]+)\)$/, - requires: ["Ext.draw.Color", "Ext.draw.gradient.GradientDefinition"], - "default": Ext.identityFn, - string: function(a) { - return String(a) - }, - number: function(a) { - if (Ext.isNumber(+a)) { - return a - } - }, - angle: function(a) { - if (Ext.isNumber(a)) { - a %= Math.PI * 2; - if (a < -Math.PI) { - a += Math.PI * 2 - } else { - if (a >= Math.PI) { - a -= Math.PI * 2 - } - } - return a - } - }, - data: function(a) { - if (Ext.isArray(a)) { - return a.slice() - } else { - if (a instanceof Float32Array) { - return new Float32Array(a) - } - } - }, - bool: function(a) { - return !!a - }, - color: function(a) { - if (a instanceof Ext.draw.Color) { - return a.toString() - } else { - if (a instanceof Ext.draw.gradient.Gradient) { - return a - } else { - if (!a) { - return Ext.draw.Color.NONE - } else { - if (Ext.isString(a)) { - if (a.substr(0, 3) === "url") { - a = Ext.draw.gradient.GradientDefinition.get(a); - if (Ext.isString(a)) { - return a - } - } else { - return Ext.draw.Color.fly(a).toString() - } - } - } - } - } - if (a.type === "linear") { - return Ext.create("Ext.draw.gradient.Linear", a) - } else { - if (a.type === "radial") { - return Ext.create("Ext.draw.gradient.Radial", a) - } else { - if (a.type === "pattern") { - return Ext.create("Ext.draw.gradient.Pattern", a) - } else { - return Ext.draw.Color.NONE - } - } - } - }, - limited: function(a, b) { - return function(c) { - c = +c; - return Ext.isNumber(c) ? Math.min(Math.max(c, a), b) : undefined - } - }, - limited01: function(a) { - a = +a; - return Ext.isNumber(a) ? Math.min(Math.max(a, 0), 1) : undefined - }, - enums: function() { - var d = {}, - a = Array.prototype.slice.call(arguments, 0), - b, c; - for (b = 0, c = a.length; b < c; b++) { - d[a[b]] = true - } - return function(e) { - return e in d ? e : undefined - } - } -}); -Ext.define("Ext.draw.sprite.AttributeDefinition", { - requires: ["Ext.draw.sprite.AttributeParser", "Ext.draw.sprite.AnimationParser"], - config: { - defaults: { - $value: {}, - lazy: true - }, - aliases: {}, - animationProcessors: {}, - processors: { - $value: {}, - lazy: true - }, - dirtyTriggers: {}, - triggers: {}, - updaters: {} - }, - inheritableStatics: { - processorFactoryRe: /^(\w+)\(([\w\-,]*)\)$/ - }, - spriteClass: null, - constructor: function(a) { - var b = this; - b.initConfig(a) - }, - applyDefaults: function(b, a) { - a = Ext.apply(a || {}, this.normalize(b)); - return a - }, - applyAliases: function(b, a) { - return Ext.apply(a || {}, b) - }, - applyProcessors: function(e, i) { - this.getAnimationProcessors(); - var j = i || {}, - h = Ext.draw.sprite.AttributeParser, - a = this.self.processorFactoryRe, - g = {}, - d, b, c, f; - for (b in e) { - f = e[b]; - if (typeof f === "string") { - c = f.match(a); - if (c) { - f = h[c[1]].apply(h, c[2].split(",")) - } else { - if (h[f]) { - g[b] = f; - d = true; - f = h[f] - } - } - } - j[b] = f - } - if (d) { - this.setAnimationProcessors(g) - } - return j - }, - applyAnimationProcessors: function(c, a) { - var e = Ext.draw.sprite.AnimationParser, - b, d; - if (!a) { - a = {} - } - for (b in c) { - d = c[b]; - if (d === "none") { - a[b] = null - } else { - if (Ext.isString(d) && !(b in a)) { - if (d in e) { - while (Ext.isString(e[d])) { - d = e[d] - } - a[b] = e[d] - } - } else { - if (Ext.isObject(d)) { - a[b] = d - } - } - } - } - return a - }, - updateDirtyTriggers: function(a) { - this.setTriggers(a) - }, - applyTriggers: function(b, c) { - if (!c) { - c = {} - } - for (var a in b) { - c[a] = b[a].split(",") - } - return c - }, - applyUpdaters: function(b, a) { - return Ext.apply(a || {}, b) - }, - batchedNormalize: function(f, n) { - if (!f) { - return {} - } - var j = this.getProcessors(), - d = this.getAliases(), - a = f.translation || f.translate, - o = {}, - g, h, b, e, p, c, m, l, k; - if ("rotation" in f) { - p = f.rotation - } else { - p = ("rotate" in f) ? f.rotate : undefined - } - if ("scaling" in f) { - c = f.scaling - } else { - c = ("scale" in f) ? f.scale : undefined - } - if (typeof c !== "undefined") { - if (Ext.isNumber(c)) { - o.scalingX = c; - o.scalingY = c - } else { - if ("x" in c) { - o.scalingX = c.x - } - if ("y" in c) { - o.scalingY = c.y - } - if ("centerX" in c) { - o.scalingCenterX = c.centerX - } - if ("centerY" in c) { - o.scalingCenterY = c.centerY - } - } - } - if (typeof p !== "undefined") { - if (Ext.isNumber(p)) { - p = Ext.draw.Draw.rad(p); - o.rotationRads = p - } else { - if ("rads" in p) { - o.rotationRads = p.rads - } else { - if ("degrees" in p) { - if (Ext.isArray(p.degrees)) { - o.rotationRads = Ext.Array.map(p.degrees, function(i) { - return Ext.draw.Draw.rad(i) - }) - } else { - o.rotationRads = Ext.draw.Draw.rad(p.degrees) - } - } - } - if ("centerX" in p) { - o.rotationCenterX = p.centerX - } - if ("centerY" in p) { - o.rotationCenterY = p.centerY - } - } - } - if (typeof a !== "undefined") { - if ("x" in a) { - o.translationX = a.x - } - if ("y" in a) { - o.translationY = a.y - } - } - if ("matrix" in f) { - m = Ext.draw.Matrix.create(f.matrix); - k = m.split(); - o.matrix = m; - o.rotationRads = k.rotation; - o.rotationCenterX = 0; - o.rotationCenterY = 0; - o.scalingX = k.scaleX; - o.scalingY = k.scaleY; - o.scalingCenterX = 0; - o.scalingCenterY = 0; - o.translationX = k.translateX; - o.translationY = k.translateY - } - for (b in f) { - e = f[b]; - if (typeof e === "undefined") { - continue - } else { - if (Ext.isArray(e)) { - if (b in d) { - b = d[b] - } - if (b in j) { - o[b] = []; - for (g = 0, h = e.length; g < h; g++) { - l = j[b].call(this, e[g]); - if (typeof l !== "undefined") { - o[b][g] = l - } - } - } else { - if (n) { - o[b] = e - } - } - } else { - if (b in d) { - b = d[b] - } - if (b in j) { - e = j[b].call(this, e); - if (typeof e !== "undefined") { - o[b] = e - } - } else { - if (n) { - o[b] = e - } - } - } - } - } - return o - }, - normalize: function(i, j) { - if (!i) { - return {} - } - var f = this.getProcessors(), - d = this.getAliases(), - a = i.translation || i.translate, - k = {}, - b, e, l, c, h, g; - if ("rotation" in i) { - l = i.rotation - } else { - l = ("rotate" in i) ? i.rotate : undefined - } - if ("scaling" in i) { - c = i.scaling - } else { - c = ("scale" in i) ? i.scale : undefined - } - if (a) { - if ("x" in a) { - k.translationX = a.x - } - if ("y" in a) { - k.translationY = a.y - } - } - if (typeof c !== "undefined") { - if (Ext.isNumber(c)) { - k.scalingX = c; - k.scalingY = c - } else { - if ("x" in c) { - k.scalingX = c.x - } - if ("y" in c) { - k.scalingY = c.y - } - if ("centerX" in c) { - k.scalingCenterX = c.centerX - } - if ("centerY" in c) { - k.scalingCenterY = c.centerY - } - } - } - if (typeof l !== "undefined") { - if (Ext.isNumber(l)) { - l = Ext.draw.Draw.rad(l); - k.rotationRads = l - } else { - if ("rads" in l) { - k.rotationRads = l.rads - } else { - if ("degrees" in l) { - k.rotationRads = Ext.draw.Draw.rad(l.degrees) - } - } - if ("centerX" in l) { - k.rotationCenterX = l.centerX - } - if ("centerY" in l) { - k.rotationCenterY = l.centerY - } - } - } - if ("matrix" in i) { - h = Ext.draw.Matrix.create(i.matrix); - g = h.split(); - k.matrix = h; - k.rotationRads = g.rotation; - k.rotationCenterX = 0; - k.rotationCenterY = 0; - k.scalingX = g.scaleX; - k.scalingY = g.scaleY; - k.scalingCenterX = 0; - k.scalingCenterY = 0; - k.translationX = g.translateX; - k.translationY = g.translateY - } - for (b in i) { - e = i[b]; - if (typeof e === "undefined") { - continue - } - if (b in d) { - b = d[b] - } - if (b in f) { - e = f[b].call(this, e); - if (typeof e !== "undefined") { - k[b] = e - } - } else { - if (j) { - k[b] = e - } - } - } - return k - }, - setBypassingNormalization: function(a, c, b) { - return c.pushDown(a, b) - }, - set: function(a, c, b) { - b = this.normalize(b); - return this.setBypassingNormalization(a, c, b) - } -}); -Ext.define("Ext.draw.Matrix", { - isMatrix: true, - statics: { - createAffineMatrixFromTwoPair: function(h, t, g, s, k, o, i, j) { - var v = g - h, - u = s - t, - e = i - k, - q = j - o, - d = 1 / (v * v + u * u), - p = v * e + u * q, - n = e * u - v * q, - m = -p * h - n * t, - l = n * h - p * t; - return new this(p * d, -n * d, n * d, p * d, m * d + k, l * d + o) - }, - createPanZoomFromTwoPair: function(q, e, p, c, h, s, n, g) { - if (arguments.length === 2) { - return this.createPanZoomFromTwoPair.apply(this, q.concat(e)) - } - var k = p - q, - j = c - e, - d = (q + p) * 0.5, - b = (e + c) * 0.5, - o = n - h, - a = g - s, - f = (h + n) * 0.5, - l = (s + g) * 0.5, - m = k * k + j * j, - i = o * o + a * a, - t = Math.sqrt(i / m); - return new this(t, 0, 0, t, f - t * d, l - t * b) - }, - fly: (function() { - var a = null, - b = function(c) { - a.elements = c; - return a - }; - return function(c) { - if (!a) { - a = new Ext.draw.Matrix() - } - a.elements = c; - Ext.draw.Matrix.fly = b; - return a - } - })(), - create: function(a) { - if (a instanceof this) { - return a - } - return new this(a) - } - }, - constructor: function(e, d, a, f, c, b) { - if (e && e.length === 6) { - this.elements = e.slice() - } else { - if (e !== undefined) { - this.elements = [e, d, a, f, c, b] - } else { - this.elements = [1, 0, 0, 1, 0, 0] - } - } - }, - prepend: function(a, l, h, g, m, k) { - var b = this.elements, - d = b[0], - j = b[1], - e = b[2], - c = b[3], - i = b[4], - f = b[5]; - b[0] = a * d + h * j; - b[1] = l * d + g * j; - b[2] = a * e + h * c; - b[3] = l * e + g * c; - b[4] = a * i + h * f + m; - b[5] = l * i + g * f + k; - return this - }, - prependMatrix: function(a) { - return this.prepend.apply(this, a.elements) - }, - append: function(a, l, h, g, m, k) { - var b = this.elements, - d = b[0], - j = b[1], - e = b[2], - c = b[3], - i = b[4], - f = b[5]; - b[0] = a * d + l * e; - b[1] = a * j + l * c; - b[2] = h * d + g * e; - b[3] = h * j + g * c; - b[4] = m * d + k * e + i; - b[5] = m * j + k * c + f; - return this - }, - appendMatrix: function(a) { - return this.append.apply(this, a.elements) - }, - set: function(f, e, a, g, c, b) { - var d = this.elements; - d[0] = f; - d[1] = e; - d[2] = a; - d[3] = g; - d[4] = c; - d[5] = b; - return this - }, - inverse: function(i) { - var g = this.elements, - o = g[0], - m = g[1], - l = g[2], - k = g[3], - j = g[4], - h = g[5], - n = 1 / (o * k - m * l); - o *= n; - m *= n; - l *= n; - k *= n; - if (i) { - i.set(k, -m, -l, o, l * h - k * j, m * j - o * h); - return i - } else { - return new Ext.draw.Matrix(k, -m, -l, o, l * h - k * j, m * j - o * h) - } - }, - translate: function(a, c, b) { - if (b) { - return this.prepend(1, 0, 0, 1, a, c) - } else { - return this.append(1, 0, 0, 1, a, c) - } - }, - scale: function(f, e, c, a, b) { - var d = this; - if (e == null) { - e = f - } - if (c === undefined) { - c = 0 - } - if (a === undefined) { - a = 0 - } - if (b) { - return d.prepend(f, 0, 0, e, c - c * f, a - a * e) - } else { - return d.append(f, 0, 0, e, c - c * f, a - a * e) - } - }, - rotate: function(g, e, c, b) { - var d = this, - f = Math.cos(g), - a = Math.sin(g); - e = e || 0; - c = c || 0; - if (b) { - return d.prepend(f, a, -a, f, e - f * e + c * a, c - f * c - e * a) - } else { - return d.append(f, a, -a, f, e - f * e + c * a, c - f * c - e * a) - } - }, - rotateFromVector: function(a, h, c) { - var e = this, - g = Math.sqrt(a * a + h * h), - f = a / g, - b = h / g; - if (c) { - return e.prepend(f, b, -b, f, 0, 0) - } else { - return e.append(f, b, -b, f, 0, 0) - } - }, - clone: function() { - return new Ext.draw.Matrix(this.elements) - }, - flipX: function() { - return this.append(-1, 0, 0, 1, 0, 0) - }, - flipY: function() { - return this.append(1, 0, 0, -1, 0, 0) - }, - skewX: function(a) { - return this.append(1, 0, Math.tan(a), 1, 0, 0) - }, - skewY: function(a) { - return this.append(1, Math.tan(a), 0, 1, 0, 0) - }, - shearX: function(a) { - return this.append(1, 0, a, 1, 0, 0) - }, - shearY: function(a) { - return this.append(1, a, 0, 1, 0, 0) - }, - reset: function() { - return this.set(1, 0, 0, 1, 0, 0) - }, - precisionCompensate: function(j, g) { - var c = this.elements, - f = c[0], - e = c[1], - i = c[2], - h = c[3], - d = c[4], - b = c[5], - a = e * i - f * h; - g.b = j * e / f; - g.c = j * i / h; - g.d = j; - g.xx = f / j; - g.yy = h / j; - g.dx = (b * f * i - d * f * h) / a / j; - g.dy = (d * e * h - b * f * h) / a / j - }, - precisionCompensateRect: function(j, g) { - var b = this.elements, - f = b[0], - e = b[1], - i = b[2], - h = b[3], - c = b[4], - a = b[5], - d = i / f; - g.b = j * e / f; - g.c = j * d; - g.d = j * h / f; - g.xx = f / j; - g.yy = f / j; - g.dx = (a * i - c * h) / (e * d - h) / j; - g.dy = -(a * f - c * e) / (e * d - h) / j - }, - x: function(a, c) { - var b = this.elements; - return a * b[0] + c * b[2] + b[4] - }, - y: function(a, c) { - var b = this.elements; - return a * b[1] + c * b[3] + b[5] - }, - get: function(b, a) { - return +this.elements[b + a * 2].toFixed(4) - }, - transformPoint: function(b) { - var c = this.elements, - a, d; - if (b.isPoint) { - a = b.x; - d = b.y - } else { - a = b[0]; - d = b[1] - } - return [a * c[0] + d * c[2] + c[4], a * c[1] + d * c[3] + c[5]] - }, - transformBBox: function(q, i, j) { - var b = this.elements, - d = q.x, - r = q.y, - g = q.width * 0.5, - o = q.height * 0.5, - a = b[0], - s = b[1], - n = b[2], - k = b[3], - e = d + g, - c = r + o, - p, f, m; - if (i) { - g -= i; - o -= i; - m = [Math.sqrt(b[0] * b[0] + b[2] * b[2]), Math.sqrt(b[1] * b[1] + b[3] * b[3])]; - p = Math.abs(g * a) + Math.abs(o * n) + Math.abs(m[0] * i); - f = Math.abs(g * s) + Math.abs(o * k) + Math.abs(m[1] * i) - } else { - p = Math.abs(g * a) + Math.abs(o * n); - f = Math.abs(g * s) + Math.abs(o * k) - } - if (!j) { - j = {} - } - j.x = e * a + c * n + b[4] - p; - j.y = e * s + c * k + b[5] - f; - j.width = p + p; - j.height = f + f; - return j - }, - transformList: function(e) { - var b = this.elements, - a = b[0], - h = b[2], - l = b[4], - k = b[1], - g = b[3], - j = b[5], - f = e.length, - c, d; - for (d = 0; d < f; d++) { - c = e[d]; - e[d] = [c[0] * a + c[1] * h + l, c[0] * k + c[1] * g + j] - } - return e - }, - isIdentity: function() { - var a = this.elements; - return a[0] === 1 && a[1] === 0 && a[2] === 0 && a[3] === 1 && a[4] === 0 && a[5] === 0 - }, - isEqual: function(a) { - var c = a && a.isMatrix ? a.elements : a, - b = this.elements; - return b[0] === c[0] && b[1] === c[1] && b[2] === c[2] && b[3] === c[3] && b[4] === c[4] && b[5] === c[5] - }, - equals: function(a) { - return this.isEqual(a) - }, - toArray: function() { - var a = this.elements; - return [a[0], a[2], a[4], a[1], a[3], a[5]] - }, - toVerticalArray: function() { - return this.elements.slice() - }, - toString: function() { - var a = this; - return [a.get(0, 0), a.get(0, 1), a.get(1, 0), a.get(1, 1), a.get(2, 0), a.get(2, 1)].join(",") - }, - toContext: function(a) { - a.transform.apply(a, this.elements); - return this - }, - toSvg: function() { - var a = this.elements; - return "matrix(" + a[0].toFixed(9) + "," + a[1].toFixed(9) + "," + a[2].toFixed(9) + "," + a[3].toFixed(9) + "," + a[4].toFixed(9) + "," + a[5].toFixed(9) + ")" - }, - getScaleX: function() { - var a = this.elements; - return Math.sqrt(a[0] * a[0] + a[2] * a[2]) - }, - getScaleY: function() { - var a = this.elements; - return Math.sqrt(a[1] * a[1] + a[3] * a[3]) - }, - getXX: function() { - return this.elements[0] - }, - getXY: function() { - return this.elements[1] - }, - getYX: function() { - return this.elements[2] - }, - getYY: function() { - return this.elements[3] - }, - getDX: function() { - return this.elements[4] - }, - getDY: function() { - return this.elements[5] - }, - split: function() { - var b = this.elements, - d = b[0], - c = b[1], - e = b[3], - a = { - translateX: b[4], - translateY: b[5] - }; - a.rotate = a.rotation = Math.atan2(c, d); - a.scaleX = d / Math.cos(a.rotate); - a.scaleY = e / d * a.scaleX; - return a - } -}, function() { - function b(e, c, d) { - e[c] = { - get: function() { - return this.elements[d] - }, - set: function(f) { - this.elements[d] = f - } - } - } - if (Object.defineProperties) { - var a = {}; - b(a, "a", 0); - b(a, "b", 1); - b(a, "c", 2); - b(a, "d", 3); - b(a, "e", 4); - b(a, "f", 5); - Object.defineProperties(this.prototype, a) - } - this.prototype.multiply = this.prototype.appendMatrix -}); -Ext.define("Ext.draw.modifier.Modifier", { - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - previous: null, - next: null, - sprite: null - }, - constructor: function(a) { - this.mixins.observable.constructor.call(this, a) - }, - updateNext: function(a) { - if (a) { - a.setPrevious(this) - } - }, - updatePrevious: function(a) { - if (a) { - a.setNext(this) - } - }, - prepareAttributes: function(a) { - if (this._previous) { - this._previous.prepareAttributes(a) - } - }, - popUp: function(a, b) { - if (this._next) { - this._next.popUp(a, b) - } else { - Ext.apply(a, b) - } - }, - pushDown: function(a, c) { - if (this._previous) { - return this._previous.pushDown(a, c) - } else { - for (var b in c) { - if (c[b] === a[b]) { - delete c[b] - } - } - return c - } - } -}); -Ext.define("Ext.draw.modifier.Target", { - requires: ["Ext.draw.Matrix"], - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.target", - statics: { - uniqueId: 0 - }, - prepareAttributes: function(a) { - var b = this.getPrevious(); - if (b) { - b.prepareAttributes(a) - } - a.attributeId = "attribute-" + Ext.draw.modifier.Target.uniqueId++; - if (!a.hasOwnProperty("canvasAttributes")) { - a.bbox = { - plain: { - dirty: true - }, - transform: { - dirty: true - } - }; - a.dirty = true; - a.pendingUpdaters = {}; - a.canvasAttributes = {}; - a.matrix = new Ext.draw.Matrix(); - a.inverseMatrix = new Ext.draw.Matrix() - } - }, - applyChanges: function(f, k) { - Ext.apply(f, k); - var l = this.getSprite(), - o = f.pendingUpdaters, - h = l.self.def.getTriggers(), - p, a, m, b, e, n, d, c, g; - for (b in k) { - e = true; - if ((p = h[b])) { - l.scheduleUpdaters(f, p, [b]) - } - if (f.template && k.removeFromInstance && k.removeFromInstance[b]) { - delete f[b] - } - } - if (!e) { - return - } - if (o.canvas) { - n = o.canvas; - delete o.canvas; - for (d = 0, g = n.length; d < g; d++) { - b = n[d]; - f.canvasAttributes[b] = f[b] - } - } - if (f.hasOwnProperty("children")) { - a = f.children; - for (d = 0, g = a.length; d < g; d++) { - m = a[d]; - Ext.apply(m.pendingUpdaters, o); - if (n) { - for (c = 0; c < n.length; c++) { - b = n[c]; - m.canvasAttributes[b] = m[b] - } - } - l.callUpdaters(m) - } - } - l.setDirty(true); - l.callUpdaters(f) - }, - popUp: function(a, b) { - this.applyChanges(a, b) - }, - pushDown: function(a, b) { - var c = this.getPrevious(); - if (c) { - b = c.pushDown(a, b) - } - this.applyChanges(a, b); - return b - } -}); -Ext.define("Ext.draw.TimingFunctions", function() { - var g = Math.pow, - j = Math.sin, - m = Math.cos, - l = Math.sqrt, - e = Math.PI, - b = ["quad", "cube", "quart", "quint"], - c = { - pow: function(o, i) { - return g(o, i || 6) - }, - expo: function(i) { - return g(2, 8 * (i - 1)) - }, - circ: function(i) { - return 1 - l(1 - i * i) - }, - sine: function(i) { - return 1 - j((1 - i) * e / 2) - }, - back: function(i, o) { - o = o || 1.616; - return i * i * ((o + 1) * i - o) - }, - bounce: function(q) { - for (var o = 0, i = 1; 1; o += i, i /= 2) { - if (q >= (7 - 4 * o) / 11) { - return i * i - g((11 - 6 * o - 11 * q) / 4, 2) - } - } - }, - elastic: function(o, i) { - return g(2, 10 * --o) * m(20 * o * e * (i || 1) / 3) - } - }, - k = {}, - a, f, d; - - function h(i) { - return function(o) { - return g(o, i) - } - } - - function n(i, o) { - k[i + "In"] = function(p) { - return o(p) - }; - k[i + "Out"] = function(p) { - return 1 - o(1 - p) - }; - k[i + "InOut"] = function(p) { - return (p <= 0.5) ? o(2 * p) / 2 : (2 - o(2 * (1 - p))) / 2 - } - } - for (d = 0, f = b.length; d < f; ++d) { - c[b[d]] = h(d + 2) - } - for (a in c) { - n(a, c[a]) - } - k.linear = Ext.identityFn; - k.easeIn = k.quadIn; - k.easeOut = k.quadOut; - k.easeInOut = k.quadInOut; - return { - singleton: true, - easingMap: k - } -}, function(a) { - Ext.apply(a, a.easingMap) -}); -Ext.define("Ext.draw.Animator", { - uses: ["Ext.draw.Draw"], - singleton: true, - frameCallbacks: {}, - frameCallbackId: 0, - scheduled: 0, - frameStartTimeOffset: Ext.now(), - animations: [], - running: false, - animationTime: function() { - return Ext.AnimationQueue.frameStartTime - this.frameStartTimeOffset - }, - add: function(b) { - var a = this; - if (!a.contains(b)) { - a.animations.push(b); - a.ignite(); - if ("fireEvent" in b) { - b.fireEvent("animationstart", b) - } - } - }, - remove: function(d) { - var c = this, - e = c.animations, - b = 0, - a = e.length; - for (; b < a; ++b) { - if (e[b] === d) { - e.splice(b, 1); - if ("fireEvent" in d) { - d.fireEvent("animationend", d) - } - return - } - } - }, - contains: function(a) { - return Ext.Array.indexOf(this.animations, a) > -1 - }, - empty: function() { - return this.animations.length === 0 - }, - step: function(d) { - var c = this, - f = c.animations, - e, a = 0, - b = f.length; - for (; a < b; a++) { - e = f[a]; - e.step(d); - if (!e.animating) { - f.splice(a, 1); - a--; - b--; - if (e.fireEvent) { - e.fireEvent("animationend", e) - } - } - } - }, - schedule: function(c, a) { - a = a || this; - var b = "frameCallback" + (this.frameCallbackId++); - if (Ext.isString(c)) { - c = a[c] - } - Ext.draw.Animator.frameCallbacks[b] = { - fn: c, - scope: a, - once: true - }; - this.scheduled++; - Ext.draw.Animator.ignite(); - return b - }, - scheduleIf: function(e, b) { - b = b || this; - var c = Ext.draw.Animator.frameCallbacks, - a, d; - if (Ext.isString(e)) { - e = b[e] - } - for (d in c) { - a = c[d]; - if (a.once && a.fn === e && a.scope === b) { - return null - } - } - return this.schedule(e, b) - }, - cancel: function(a) { - if (Ext.draw.Animator.frameCallbacks[a] && Ext.draw.Animator.frameCallbacks[a].once) { - this.scheduled--; - delete Ext.draw.Animator.frameCallbacks[a] - } - }, - addFrameCallback: function(c, a) { - a = a || this; - if (Ext.isString(c)) { - c = a[c] - } - var b = "frameCallback" + (this.frameCallbackId++); - Ext.draw.Animator.frameCallbacks[b] = { - fn: c, - scope: a - }; - return b - }, - removeFrameCallback: function(a) { - delete Ext.draw.Animator.frameCallbacks[a] - }, - fireFrameCallbacks: function() { - var c = this.frameCallbacks, - d, b, a; - for (d in c) { - a = c[d]; - b = a.fn; - if (Ext.isString(b)) { - b = a.scope[b] - } - b.call(a.scope); - if (c[d] && a.once) { - this.scheduled--; - delete c[d] - } - } - }, - handleFrame: function() { - this.step(this.animationTime()); - this.fireFrameCallbacks(); - if (!this.scheduled && this.empty()) { - Ext.AnimationQueue.stop(this.handleFrame, this); - this.running = false; - Ext.draw.Draw.endUpdateIOS() - } - }, - ignite: function() { - if (!this.running) { - this.running = true; - Ext.AnimationQueue.start(this.handleFrame, this); - Ext.draw.Draw.beginUpdateIOS() - } - } -}); -Ext.define("Ext.draw.modifier.Animation", { - requires: ["Ext.draw.TimingFunctions", "Ext.draw.Animator"], - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.animation", - config: { - easing: Ext.identityFn, - duration: 0, - customEasings: {}, - customDurations: {}, - customDuration: null - }, - constructor: function(a) { - var b = this; - b.anyAnimation = b.anySpecialAnimations = false; - b.animating = 0; - b.animatingPool = []; - b.callParent([a]) - }, - prepareAttributes: function(a) { - if (!a.hasOwnProperty("timers")) { - a.animating = false; - a.timers = {}; - a.animationOriginal = Ext.Object.chain(a); - a.animationOriginal.prototype = a - } - if (this._previous) { - this._previous.prepareAttributes(a.animationOriginal) - } - }, - updateSprite: function(a) { - this.setConfig(a.config.fx) - }, - updateDuration: function(a) { - this.anyAnimation = a > 0 - }, - applyEasing: function(a) { - if (typeof a === "string") { - a = Ext.draw.TimingFunctions.easingMap[a] - } - return a - }, - applyCustomEasings: function(a, e) { - e = e || {}; - var g, d, b, h, c, f; - for (d in a) { - g = true; - h = a[d]; - b = d.split(","); - if (typeof h === "string") { - h = Ext.draw.TimingFunctions.easingMap[h] - } - for (c = 0, f = b.length; c < f; c++) { - e[b[c]] = h - } - } - if (g) { - this.anySpecialAnimations = g - } - return e - }, - setEasingOn: function(a, e) { - a = Ext.Array.from(a).slice(); - var c = {}, - d = a.length, - b = 0; - for (; b < d; b++) { - c[a[b]] = e - } - this.setCustomEasings(c) - }, - clearEasingOn: function(a) { - a = Ext.Array.from(a, true); - var b = 0, - c = a.length; - for (; b < c; b++) { - delete this._customEasings[a[b]] - } - }, - applyCustomDurations: function(g, h) { - h = h || {}; - var e, c, f, a, b, d; - for (c in g) { - e = true; - f = g[c]; - a = c.split(","); - for (b = 0, d = a.length; b < d; b++) { - h[a[b]] = f - } - } - if (e) { - this.anySpecialAnimations = e - } - return h - }, - applyCustomDuration: function(a, b) { - if (a) { - this.getCustomDurations(); - this.setCustomDurations(a) - } - }, - setDurationOn: function(b, e) { - b = Ext.Array.from(b).slice(); - var a = {}, - c = 0, - d = b.length; - for (; c < d; c++) { - a[b[c]] = e - } - this.setCustomDurations(a) - }, - clearDurationOn: function(a) { - a = Ext.Array.from(a, true); - var b = 0, - c = a.length; - for (; b < c; b++) { - delete this._customDurations[a[b]] - } - }, - setAnimating: function(a, b) { - var e = this, - d = e.animatingPool; - if (a.animating !== b) { - a.animating = b; - if (b) { - d.push(a); - if (e.animating === 0) { - Ext.draw.Animator.add(e) - } - e.animating++ - } else { - for (var c = d.length; c--;) { - if (d[c] === a) { - d.splice(c, 1) - } - } - e.animating = d.length - } - } - }, - setAttrs: function(r, t) { - var s = this, - m = r.timers, - h = s._sprite.self.def._animationProcessors, - f = s._easing, - e = s._duration, - j = s._customDurations, - i = s._customEasings, - g = s.anySpecialAnimations, - n = s.anyAnimation || g, - o = r.animationOriginal, - d = false, - k, u, l, p, c, q, a; - if (!n) { - for (u in t) { - if (r[u] === t[u]) { - delete t[u] - } else { - r[u] = t[u] - } - delete o[u]; - delete m[u] - } - return t - } else { - for (u in t) { - l = t[u]; - p = r[u]; - if (l !== p && p !== undefined && p !== null && (c = h[u])) { - q = f; - a = e; - if (g) { - if (u in i) { - q = i[u] - } - if (u in j) { - a = j[u] - } - } - if (p && p.isGradient || l && l.isGradient) { - a = 0 - } - if (a) { - if (!m[u]) { - m[u] = {} - } - k = m[u]; - k.start = 0; - k.easing = q; - k.duration = a; - k.compute = c.compute; - k.serve = c.serve || Ext.identityFn; - k.remove = t.removeFromInstance && t.removeFromInstance[u]; - if (c.parseInitial) { - var b = c.parseInitial(p, l); - k.source = b[0]; - k.target = b[1] - } else { - if (c.parse) { - k.source = c.parse(p); - k.target = c.parse(l) - } else { - k.source = p; - k.target = l - } - } - o[u] = l; - delete t[u]; - d = true; - continue - } else { - delete o[u] - } - } else { - delete o[u] - } - delete m[u] - } - } - if (d && !r.animating) { - s.setAnimating(r, true) - } - return t - }, - updateAttributes: function(g) { - if (!g.animating) { - return {} - } - var h = {}, - e = false, - d = g.timers, - f = g.animationOriginal, - c = Ext.draw.Animator.animationTime(), - a, b, i; - if (g.lastUpdate === c) { - return null - } - for (a in d) { - b = d[a]; - if (!b.start) { - b.start = c; - i = 0 - } else { - i = (c - b.start) / b.duration - } - if (i >= 1) { - h[a] = f[a]; - delete f[a]; - if (d[a].remove) { - h.removeFromInstance = h.removeFromInstance || {}; - h.removeFromInstance[a] = true - } - delete d[a] - } else { - h[a] = b.serve(b.compute(b.source, b.target, b.easing(i), g[a])); - e = true - } - } - g.lastUpdate = c; - this.setAnimating(g, e); - return h - }, - pushDown: function(a, b) { - b = this.callParent([a.animationOriginal, b]); - return this.setAttrs(a, b) - }, - popUp: function(a, b) { - a = a.prototype; - b = this.setAttrs(a, b); - if (this._next) { - return this._next.popUp(a, b) - } else { - return Ext.apply(a, b) - } - }, - step: function(g) { - var f = this, - c = f.animatingPool.slice(), - e = c.length, - b = 0, - a, d; - for (; b < e; b++) { - a = c[b]; - d = f.updateAttributes(a); - if (d && f._next) { - f._next.popUp(a, d) - } - } - }, - stop: function() { - this.step(); - var d = this, - b = d.animatingPool, - a, c; - for (a = 0, c = b.length; a < c; a++) { - b[a].animating = false - } - d.animatingPool.length = 0; - d.animating = 0; - Ext.draw.Animator.remove(d) - }, - destroy: function() { - this.animatingPool.length = 0; - this.animating = 0; - this.callParent() - } -}); -Ext.define("Ext.draw.modifier.Highlight", { - extend: "Ext.draw.modifier.Modifier", - alias: "modifier.highlight", - config: { - enabled: false, - highlightStyle: null - }, - preFx: true, - applyHighlightStyle: function(b, a) { - a = a || {}; - if (this.getSprite()) { - Ext.apply(a, this.getSprite().self.def.normalize(b)) - } else { - Ext.apply(a, b) - } - return a - }, - prepareAttributes: function(a) { - if (!a.hasOwnProperty("highlightOriginal")) { - a.highlighted = false; - a.highlightOriginal = Ext.Object.chain(a); - a.highlightOriginal.prototype = a; - a.highlightOriginal.removeFromInstance = {} - } - if (this._previous) { - this._previous.prepareAttributes(a.highlightOriginal) - } - }, - updateSprite: function(b, a) { - if (b) { - if (this.getHighlightStyle()) { - this._highlightStyle = b.self.def.normalize(this.getHighlightStyle()) - } - this.setHighlightStyle(b.config.highlight) - } - b.self.def.setConfig({ - defaults: { - highlighted: false - }, - processors: { - highlighted: "bool" - } - }); - this.setSprite(b) - }, - filterChanges: function(a, d) { - var e = this, - f = a.highlightOriginal, - c = e.getHighlightStyle(), - b; - if (a.highlighted) { - for (b in d) { - if (c.hasOwnProperty(b)) { - f[b] = d[b]; - delete d[b] - } - } - } - for (b in d) { - if (b !== "highlighted" && f[b] === d[b]) { - delete d[b] - } - } - return d - }, - pushDown: function(e, g) { - var f = this.getHighlightStyle(), - c = e.highlightOriginal, - i = c.removeFromInstance, - d, a, h, b; - if (g.hasOwnProperty("highlighted")) { - d = g.highlighted; - delete g.highlighted; - if (this._previous) { - g = this._previous.pushDown(c, g) - } - g = this.filterChanges(e, g); - if (d !== e.highlighted) { - if (d) { - for (a in f) { - if (a in g) { - c[a] = g[a] - } else { - h = e.template && e.template.ownAttr; - if (h && !e.prototype.hasOwnProperty(a)) { - i[a] = true; - c[a] = h.animationOriginal[a] - } else { - b = c.timers[a]; - if (b && b.remove) { - i[a] = true - } - c[a] = e[a] - } - } - if (c[a] !== f[a]) { - g[a] = f[a] - } - } - } else { - for (a in f) { - if (!(a in g)) { - g[a] = c[a] - } - delete c[a] - } - g.removeFromInstance = g.removeFromInstance || {}; - Ext.apply(g.removeFromInstance, i); - c.removeFromInstance = {} - } - g.highlighted = d - } - } else { - if (this._previous) { - g = this._previous.pushDown(c, g) - } - g = this.filterChanges(e, g) - } - return g - }, - popUp: function(a, b) { - b = this.filterChanges(a, b); - Ext.draw.modifier.Modifier.prototype.popUp.call(this, a, b) - } -}); -Ext.define("Ext.draw.sprite.Sprite", { - alias: "sprite.sprite", - mixins: { - observable: "Ext.mixin.Observable" - }, - requires: ["Ext.draw.Draw", "Ext.draw.gradient.Gradient", "Ext.draw.sprite.AttributeDefinition", "Ext.draw.modifier.Target", "Ext.draw.modifier.Animation", "Ext.draw.modifier.Highlight"], - isSprite: true, - statics: { - defaultHitTestOptions: { - fill: true, - stroke: true - } - }, - inheritableStatics: { - def: { - processors: { - strokeStyle: "color", - fillStyle: "color", - strokeOpacity: "limited01", - fillOpacity: "limited01", - lineWidth: "number", - lineCap: "enums(butt,round,square)", - lineJoin: "enums(round,bevel,miter)", - lineDash: "data", - lineDashOffset: "number", - miterLimit: "number", - shadowColor: "color", - shadowOffsetX: "number", - shadowOffsetY: "number", - shadowBlur: "number", - globalAlpha: "limited01", - globalCompositeOperation: "enums(source-over,destination-over,source-in,destination-in,source-out,destination-out,source-atop,destination-atop,lighter,xor,copy)", - hidden: "bool", - transformFillStroke: "bool", - zIndex: "number", - translationX: "number", - translationY: "number", - rotationRads: "number", - rotationCenterX: "number", - rotationCenterY: "number", - scalingX: "number", - scalingY: "number", - scalingCenterX: "number", - scalingCenterY: "number", - constrainGradients: "bool" - }, - aliases: { - stroke: "strokeStyle", - fill: "fillStyle", - color: "fillStyle", - "stroke-width": "lineWidth", - "stroke-linecap": "lineCap", - "stroke-linejoin": "lineJoin", - "stroke-miterlimit": "miterLimit", - "text-anchor": "textAlign", - opacity: "globalAlpha", - translateX: "translationX", - translateY: "translationY", - rotateRads: "rotationRads", - rotateCenterX: "rotationCenterX", - rotateCenterY: "rotationCenterY", - scaleX: "scalingX", - scaleY: "scalingY", - scaleCenterX: "scalingCenterX", - scaleCenterY: "scalingCenterY" - }, - defaults: { - hidden: false, - zIndex: 0, - strokeStyle: "none", - fillStyle: "none", - lineWidth: 1, - lineDash: [], - lineDashOffset: 0, - lineCap: "butt", - lineJoin: "miter", - miterLimit: 10, - shadowColor: "none", - shadowOffsetX: 0, - shadowOffsetY: 0, - shadowBlur: 0, - globalAlpha: 1, - strokeOpacity: 1, - fillOpacity: 1, - transformFillStroke: false, - translationX: 0, - translationY: 0, - rotationRads: 0, - rotationCenterX: null, - rotationCenterY: null, - scalingX: 1, - scalingY: 1, - scalingCenterX: null, - scalingCenterY: null, - constrainGradients: false - }, - triggers: { - zIndex: "zIndex", - globalAlpha: "canvas", - globalCompositeOperation: "canvas", - transformFillStroke: "canvas", - strokeStyle: "canvas", - fillStyle: "canvas", - strokeOpacity: "canvas", - fillOpacity: "canvas", - lineWidth: "canvas", - lineCap: "canvas", - lineJoin: "canvas", - lineDash: "canvas", - lineDashOffset: "canvas", - miterLimit: "canvas", - shadowColor: "canvas", - shadowOffsetX: "canvas", - shadowOffsetY: "canvas", - shadowBlur: "canvas", - translationX: "transform", - translationY: "transform", - rotationRads: "transform", - rotationCenterX: "transform", - rotationCenterY: "transform", - scalingX: "transform", - scalingY: "transform", - scalingCenterX: "transform", - scalingCenterY: "transform", - constrainGradients: "canvas" - }, - updaters: { - bbox: "bboxUpdater", - zIndex: function(a) { - a.dirtyZIndex = true - }, - transform: function(a) { - a.dirtyTransform = true; - a.bbox.transform.dirty = true - } - } - } - }, - config: { - parent: null, - surface: null - }, - onClassExtended: function(d, c) { - var b = d.superclass.self.def.initialConfig, - e = c.inheritableStatics && c.inheritableStatics.def, - a; - if (e) { - a = Ext.Object.merge({}, b, e); - d.def = new Ext.draw.sprite.AttributeDefinition(a); - delete c.inheritableStatics.def - } else { - d.def = new Ext.draw.sprite.AttributeDefinition(b) - } - d.def.spriteClass = d - }, - constructor: function(b) { - var d = this, - c = d.self.def, - e = c.getDefaults(), - a; - b = Ext.isObject(b) ? b : {}; - d.id = b.id || Ext.id(null, "ext-sprite-"); - d.attr = {}; - d.mixins.observable.constructor.apply(d, arguments); - a = Ext.Array.from(b.modifiers, true); - d.prepareModifiers(a); - d.initializeAttributes(); - d.setAttributes(e, true); - d.setAttributes(b) - }, - getDirty: function() { - return this.attr.dirty - }, - setDirty: function(b) { - this.attr.dirty = b; - if (b) { - var a = this.getParent(); - if (a) { - a.setDirty(true) - } - } - }, - addModifier: function(a, b) { - var c = this; - if (!(a instanceof Ext.draw.modifier.Modifier)) { - a = Ext.factory(a, null, null, "modifier") - } - a.setSprite(c); - if (a.preFx || a.config && a.config.preFx) { - if (c.fx.getPrevious()) { - c.fx.getPrevious().setNext(a) - } - a.setNext(c.fx) - } else { - c.topModifier.getPrevious().setNext(a); - a.setNext(c.topModifier) - } - if (b) { - c.initializeAttributes() - } - return a - }, - prepareModifiers: function(d) { - var c = this, - a, b; - c.topModifier = new Ext.draw.modifier.Target({ - sprite: c - }); - c.fx = new Ext.draw.modifier.Animation({ - sprite: c - }); - c.fx.setNext(c.topModifier); - for (a = 0, b = d.length; a < b; a++) { - c.addModifier(d[a], false) - } - }, - getAnimation: function() { - return this.fx - }, - setAnimation: function(a) { - this.fx.setConfig(a) - }, - initializeAttributes: function() { - this.topModifier.prepareAttributes(this.attr) - }, - callUpdaters: function(d) { - var e = this, - h = d.pendingUpdaters, - i = e.self.def.getUpdaters(), - c = false, - a = false, - b, g, f; - e.callUpdaters = Ext.emptyFn; - do { - c = false; - for (g in h) { - c = true; - b = h[g]; - delete h[g]; - f = i[g]; - if (typeof f === "string") { - f = e[f] - } - if (f) { - f.call(e, d, b) - } - } - a = a || c - } while (c); - delete e.callUpdaters; - if (a) { - e.setDirty(true) - } - }, - scheduleUpdaters: function(a, e, c) { - var f; - if (c) { - for (var b = 0, d = e.length; b < d; b++) { - f = e[b]; - this.scheduleUpdater(a, f, c) - } - } else { - for (f in e) { - c = e[f]; - this.scheduleUpdater(a, f, c) - } - } - }, - scheduleUpdater: function(a, c, b) { - b = b || []; - var d = a.pendingUpdaters; - if (c in d) { - if (b.length) { - d[c] = Ext.Array.merge(d[c], b) - } - } else { - d[c] = b - } - }, - setAttributes: function(d, g, c) { - var a = this.attr, - b, e, f; - if (g) { - if (c) { - this.topModifier.pushDown(a, d) - } else { - f = {}; - for (b in d) { - e = d[b]; - if (e !== a[b]) { - f[b] = e - } - } - this.topModifier.pushDown(a, f) - } - } else { - this.topModifier.pushDown(a, this.self.def.normalize(d)) - } - }, - setAttributesBypassingNormalization: function(b, a) { - return this.setAttributes(b, true, a) - }, - bboxUpdater: function(b) { - var c = b.rotationRads !== 0, - a = b.scalingX !== 1 || b.scalingY !== 1, - d = b.rotationCenterX === null || b.rotationCenterY === null, - e = b.scalingCenterX === null || b.scalingCenterY === null; - b.bbox.plain.dirty = true; - b.bbox.transform.dirty = true; - if (c && d || a && e) { - this.scheduleUpdater(b, "transform") - } - }, - getBBox: function(d) { - var e = this, - a = e.attr, - f = a.bbox, - c = f.plain, - b = f.transform; - if (c.dirty) { - e.updatePlainBBox(c); - c.dirty = false - } - if (!d) { - e.applyTransformations(); - if (b.dirty) { - e.updateTransformedBBox(b, c); - b.dirty = false - } - return b - } - return c - }, - updatePlainBBox: Ext.emptyFn, - updateTransformedBBox: function(a, b) { - this.attr.matrix.transformBBox(b, 0, a) - }, - getBBoxCenter: function(a) { - var b = this.getBBox(a); - if (b) { - return [b.x + b.width * 0.5, b.y + b.height * 0.5] - } else { - return [0, 0] - } - }, - hide: function() { - this.attr.hidden = true; - this.setDirty(true); - return this - }, - show: function() { - this.attr.hidden = false; - this.setDirty(true); - return this - }, - useAttributes: function(i, f) { - this.applyTransformations(); - var d = this.attr, - h = d.canvasAttributes, - e = h.strokeStyle, - g = h.fillStyle, - b = h.lineDash, - c = h.lineDashOffset, - a; - if (e) { - if (e.isGradient) { - i.strokeStyle = "black"; - i.strokeGradient = e - } else { - i.strokeGradient = false - } - } - if (g) { - if (g.isGradient) { - i.fillStyle = "black"; - i.fillGradient = g - } else { - i.fillGradient = false - } - } - if (b) { - i.setLineDash(b) - } - if (Ext.isNumber(c + i.lineDashOffset)) { - i.lineDashOffset = c - } - for (a in h) { - if (h[a] !== undefined && h[a] !== i[a]) { - i[a] = h[a] - } - } - this.setGradientBBox(i, f) - }, - setGradientBBox: function(b, c) { - var a = this.attr; - if (a.constrainGradients) { - b.setGradientBBox({ - x: c[0], - y: c[1], - width: c[2], - height: c[3] - }) - } else { - b.setGradientBBox(this.getBBox(a.transformFillStroke)) - } - }, - applyTransformations: function(b) { - if (!b && !this.attr.dirtyTransform) { - return - } - var r = this, - k = r.attr, - p = r.getBBoxCenter(true), - g = p[0], - f = p[1], - q = k.translationX, - o = k.translationY, - j = k.scalingX, - i = k.scalingY === null ? k.scalingX : k.scalingY, - m = k.scalingCenterX === null ? g : k.scalingCenterX, - l = k.scalingCenterY === null ? f : k.scalingCenterY, - s = k.rotationRads, - e = k.rotationCenterX === null ? g : k.rotationCenterX, - d = k.rotationCenterY === null ? f : k.rotationCenterY, - c = Math.cos(s), - a = Math.sin(s), - n, h; - if (j === 1 && i === 1) { - m = 0; - l = 0 - } - if (s === 0) { - e = 0; - d = 0 - } - n = m * (1 - j) - e; - h = l * (1 - i) - d; - k.matrix.elements = [c * j, a * j, -a * i, c * i, c * n - a * h + e + q, a * n + c * h + d + o]; - k.matrix.inverse(k.inverseMatrix); - k.dirtyTransform = false; - k.bbox.transform.dirty = true - }, - transform: function(b, c) { - var a = this.attr, - e = a.matrix, - d; - if (b && b.isMatrix) { - d = b.elements - } else { - d = b - } - e.prepend.apply(e, d.slice()); - e.inverse(a.inverseMatrix); - if (c) { - this.updateTransformAttributes() - } - a.dirtyTransform = false; - a.bbox.transform.dirty = true; - this.setDirty(true); - return this - }, - updateTransformAttributes: function() { - var a = this.attr, - b = a.matrix.split(); - a.rotationRads = b.rotate; - a.rotationCenterX = 0; - a.rotationCenterY = 0; - a.scalingX = b.scaleX; - a.scalingY = b.scaleY; - a.scalingCenterX = 0; - a.scalingCenterY = 0; - a.translationX = b.translateX; - a.translationY = b.translateY - }, - resetTransform: function(b) { - var a = this.attr; - a.matrix.reset(); - a.inverseMatrix.reset(); - if (!b) { - this.updateTransformAttributes() - } - a.dirtyTransform = false; - a.bbox.transform.dirty = true; - this.setDirty(true); - return this - }, - setTransform: function(a, b) { - this.resetTransform(true); - this.transform.call(this, a, b); - return this - }, - preRender: Ext.emptyFn, - render: Ext.emptyFn, - hitTest: function(b, c) { - if (this.isVisible()) { - var a = b[0], - f = b[1], - e = this.getBBox(), - d = e && a >= e.x && a <= (e.x + e.width) && f >= e.y && f <= (e.y + e.height); - if (d) { - return { - sprite: this - } - } - } - return null - }, - isVisible: function() { - var e = this.attr, - f = this.getParent(), - g = f && (f.isSurface || f.isVisible()), - d = g && !e.hidden && e.globalAlpha, - b = Ext.draw.Color.NONE, - a = Ext.draw.Color.RGBA_NONE, - c = e.fillOpacity && e.fillStyle !== b && e.fillStyle !== a, - i = e.strokeOpacity && e.strokeStyle !== b && e.strokeStyle !== a, - h = d && (c || i); - return !!h - }, - repaint: function() { - var a = this.getSurface(); - if (a) { - a.renderFrame() - } - }, - remove: function() { - var a = this.getSurface(); - if (a && a.isSurface) { - return a.remove(this) - } - return null - }, - destroy: function() { - var b = this, - a = b.topModifier, - c; - while (a) { - c = a; - a = a.getPrevious(); - c.destroy() - } - delete b.attr; - b.remove(); - if (b.fireEvent("beforedestroy", b) !== false) { - b.fireEvent("destroy", b) - } - b.callParent() - } -}, function() { - this.def = new Ext.draw.sprite.AttributeDefinition(this.def); - this.def.spriteClass = this -}); -Ext.define("Ext.draw.Path", { - requires: ["Ext.draw.Draw"], - statics: { - pathRe: /,?([achlmqrstvxz]),?/gi, - pathRe2: /-/gi, - pathSplitRe: /\s|,/g - }, - svgString: "", - constructor: function(a) { - var b = this; - b.commands = []; - b.params = []; - b.cursor = null; - b.startX = 0; - b.startY = 0; - if (a) { - b.fromSvgString(a) - } - }, - clear: function() { - var a = this; - a.params.length = 0; - a.commands.length = 0; - a.cursor = null; - a.startX = 0; - a.startY = 0; - a.dirt() - }, - dirt: function() { - this.svgString = "" - }, - moveTo: function(a, c) { - var b = this; - if (!b.cursor) { - b.cursor = [a, c] - } - b.params.push(a, c); - b.commands.push("M"); - b.startX = a; - b.startY = c; - b.cursor[0] = a; - b.cursor[1] = c; - b.dirt() - }, - lineTo: function(a, c) { - var b = this; - if (!b.cursor) { - b.cursor = [a, c]; - b.params.push(a, c); - b.commands.push("M") - } else { - b.params.push(a, c); - b.commands.push("L") - } - b.cursor[0] = a; - b.cursor[1] = c; - b.dirt() - }, - bezierCurveTo: function(c, e, b, d, a, g) { - var f = this; - if (!f.cursor) { - f.moveTo(c, e) - } - f.params.push(c, e, b, d, a, g); - f.commands.push("C"); - f.cursor[0] = a; - f.cursor[1] = g; - f.dirt() - }, - quadraticCurveTo: function(b, e, a, d) { - var c = this; - if (!c.cursor) { - c.moveTo(b, e) - } - c.bezierCurveTo((2 * b + c.cursor[0]) / 3, (2 * e + c.cursor[1]) / 3, (2 * b + a) / 3, (2 * e + d) / 3, a, d) - }, - closePath: function() { - var a = this; - if (a.cursor) { - a.cursor = null; - a.commands.push("Z"); - a.dirt() - } - }, - arcTo: function(A, f, z, d, j, i, v) { - var E = this; - if (i === undefined) { - i = j - } - if (v === undefined) { - v = 0 - } - if (!E.cursor) { - E.moveTo(A, f); - return - } - if (j === 0 || i === 0) { - E.lineTo(A, f); - return - } - z -= A; - d -= f; - var B = E.cursor[0] - A, - g = E.cursor[1] - f, - C = z * g - d * B, - b, a, l, r, k, q, x = Math.sqrt(B * B + g * g), - u = Math.sqrt(z * z + d * d), - t, e, c; - if (C === 0) { - E.lineTo(A, f); - return - } - if (i !== j) { - b = Math.cos(v); - a = Math.sin(v); - l = b / j; - r = a / i; - k = -a / j; - q = b / i; - var D = l * B + r * g; - g = k * B + q * g; - B = D; - D = l * z + r * d; - d = k * z + q * d; - z = D - } else { - B /= j; - g /= i; - z /= j; - d /= i - } - e = B * u + z * x; - c = g * u + d * x; - t = 1 / (Math.sin(Math.asin(Math.abs(C) / (x * u)) * 0.5) * Math.sqrt(e * e + c * c)); - e *= t; - c *= t; - var o = (e * B + c * g) / (B * B + g * g), - m = (e * z + c * d) / (z * z + d * d); - var n = B * o - e, - p = g * o - c, - h = z * m - e, - y = d * m - c, - w = Math.atan2(p, n), - s = Math.atan2(y, h); - if (C > 0) { - if (s < w) { - s += Math.PI * 2 - } - } else { - if (w < s) { - w += Math.PI * 2 - } - } - if (i !== j) { - e = b * e * j - a * c * i + A; - c = a * c * i + b * c * i + f; - E.lineTo(b * j * n - a * i * p + e, a * j * n + b * i * p + c); - E.ellipse(e, c, j, i, v, w, s, C < 0) - } else { - e = e * j + A; - c = c * i + f; - E.lineTo(j * n + e, i * p + c); - E.ellipse(e, c, j, i, v, w, s, C < 0) - } - }, - ellipse: function(h, f, c, a, q, n, d, e) { - var o = this, - g = o.params, - b = g.length, - m, l, k; - if (d - n >= Math.PI * 2) { - o.ellipse(h, f, c, a, q, n, n + Math.PI, e); - o.ellipse(h, f, c, a, q, n + Math.PI, d, e); - return - } - if (!e) { - if (d < n) { - d += Math.PI * 2 - } - m = o.approximateArc(g, h, f, c, a, q, n, d) - } else { - if (n < d) { - n += Math.PI * 2 - } - m = o.approximateArc(g, h, f, c, a, q, d, n); - for (l = b, k = g.length - 2; l < k; l += 2, k -= 2) { - var p = g[l]; - g[l] = g[k]; - g[k] = p; - p = g[l + 1]; - g[l + 1] = g[k + 1]; - g[k + 1] = p - } - } - if (!o.cursor) { - o.cursor = [g[g.length - 2], g[g.length - 1]]; - o.commands.push("M") - } else { - o.cursor[0] = g[g.length - 2]; - o.cursor[1] = g[g.length - 1]; - o.commands.push("L") - } - for (l = 2; l < m; l += 6) { - o.commands.push("C") - } - o.dirt() - }, - arc: function(b, f, a, d, c, e) { - this.ellipse(b, f, a, a, 0, d, c, e) - }, - rect: function(b, e, c, a) { - if (c == 0 || a == 0) { - return - } - var d = this; - d.moveTo(b, e); - d.lineTo(b + c, e); - d.lineTo(b + c, e + a); - d.lineTo(b, e + a); - d.closePath() - }, - approximateArc: function(s, i, f, o, n, d, x, v) { - var e = Math.cos(d), - z = Math.sin(d), - k = Math.cos(x), - l = Math.sin(x), - q = e * k * o - z * l * n, - y = -e * l * o - z * k * n, - p = z * k * o + e * l * n, - w = -z * l * o + e * k * n, - m = Math.PI / 2, - r = 2, - j = q, - u = y, - h = p, - t = w, - b = 0.547443256150549, - C, g, A, a, B, c; - v -= x; - if (v < 0) { - v += Math.PI * 2 - } - s.push(q + i, p + f); - while (v >= m) { - s.push(j + u * b + i, h + t * b + f, j * b + u + i, h * b + t + f, u + i, t + f); - r += 6; - v -= m; - C = j; - j = u; - u = -C; - C = h; - h = t; - t = -C - } - if (v) { - g = (0.3294738052815987 + 0.012120855841304373 * v) * v; - A = Math.cos(v); - a = Math.sin(v); - B = A + g * a; - c = a - g * A; - s.push(j + u * g + i, h + t * g + f, j * B + u * c + i, h * B + t * c + f, j * A + u * a + i, h * A + t * a + f); - r += 6 - } - return r - }, - arcSvg: function(j, h, r, m, w, t, c) { - if (j < 0) { - j = -j - } - if (h < 0) { - h = -h - } - var x = this, - u = x.cursor[0], - f = x.cursor[1], - a = (u - t) / 2, - y = (f - c) / 2, - d = Math.cos(r), - s = Math.sin(r), - o = a * d + y * s, - v = -a * s + y * d, - i = o / j, - g = v / h, - p = i * i + g * g, - e = (u + t) * 0.5, - b = (f + c) * 0.5, - l = 0, - k = 0; - if (p >= 1) { - p = Math.sqrt(p); - j *= p; - h *= p - } else { - p = Math.sqrt(1 / p - 1); - if (m === w) { - p = -p - } - l = p * j * g; - k = -p * h * i; - e += d * l - s * k; - b += s * l + d * k - } - var q = Math.atan2((v - k) / h, (o - l) / j), - n = Math.atan2((-v - k) / h, (-o - l) / j) - q; - if (w) { - if (n <= 0) { - n += Math.PI * 2 - } - } else { - if (n >= 0) { - n -= Math.PI * 2 - } - } - x.ellipse(e, b, j, h, r, q, q + n, 1 - w) - }, - fromSvgString: function(e) { - if (!e) { - return - } - var m = this, - h, l = { - a: 7, - c: 6, - h: 1, - l: 2, - m: 2, - q: 4, - s: 4, - t: 2, - v: 1, - z: 0, - A: 7, - C: 6, - H: 1, - L: 2, - M: 2, - Q: 4, - S: 4, - T: 2, - V: 1, - Z: 0 - }, - k = "", - g, f, c = 0, - b = 0, - d = false, - j, n, a; - if (Ext.isString(e)) { - h = e.replace(Ext.draw.Path.pathRe, " $1 ").replace(Ext.draw.Path.pathRe2, " -").split(Ext.draw.Path.pathSplitRe) - } else { - if (Ext.isArray(e)) { - h = e.join(",").split(Ext.draw.Path.pathSplitRe) - } - } - for (j = 0, n = 0; j < h.length; j++) { - if (h[j] !== "") { - h[n++] = h[j] - } - } - h.length = n; - m.clear(); - for (j = 0; j < h.length;) { - k = d; - d = h[j]; - a = (d.toUpperCase() !== d); - j++; - switch (d) { - case "M": - m.moveTo(c = +h[j], b = +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2 - } - break; - case "L": - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b = +h[j + 1]); - j += 2 - } - break; - case "A": - while (j < n && !l.hasOwnProperty(h[j])) { - m.arcSvg(+h[j], +h[j + 1], +h[j + 2] * Math.PI / 180, +h[j + 3], +h[j + 4], c = +h[j + 5], b = +h[j + 6]); - j += 7 - } - break; - case "C": - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(+h[j], +h[j + 1], g = +h[j + 2], f = +h[j + 3], c = +h[j + 4], b = +h[j + 5]); - j += 6 - } - break; - case "Z": - m.closePath(); - break; - case "m": - m.moveTo(c += +h[j], b += +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2 - } - break; - case "l": - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2; - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b += +h[j + 1]); - j += 2 - } - break; - case "a": - while (j < n && !l.hasOwnProperty(h[j])) { - m.arcSvg(+h[j], +h[j + 1], +h[j + 2] * Math.PI / 180, +h[j + 3], +h[j + 4], c += +h[j + 5], b += +h[j + 6]); - j += 7 - } - break; - case "c": - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + (+h[j]), b + (+h[j + 1]), g = c + (+h[j + 2]), f = b + (+h[j + 3]), c += +h[j + 4], b += +h[j + 5]); - j += 6 - } - break; - case "z": - m.closePath(); - break; - case "s": - if (!(k === "c" || k === "C" || k === "s" || k === "S")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + c - g, b + b - f, g = c + (+h[j]), f = b + (+h[j + 1]), c += +h[j + 2], b += +h[j + 3]); - j += 4 - } - break; - case "S": - if (!(k === "c" || k === "C" || k === "s" || k === "S")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.bezierCurveTo(c + c - g, b + b - f, g = +h[j], f = +h[j + 1], c = (+h[j + 2]), b = (+h[j + 3])); - j += 4 - } - break; - case "q": - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + (+h[j]), f = b + (+h[j + 1]), c += +h[j + 2], b += +h[j + 3]); - j += 4 - } - break; - case "Q": - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = +h[j], f = +h[j + 1], c = +h[j + 2], b = +h[j + 3]); - j += 4 - } - break; - case "t": - if (!(k === "q" || k === "Q" || k === "t" || k === "T")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + c - g, f = b + b - f, c += +h[j + 1], b += +h[j + 2]); - j += 2 - } - break; - case "T": - if (!(k === "q" || k === "Q" || k === "t" || k === "T")) { - g = c; - f = b - } - while (j < n && !l.hasOwnProperty(h[j])) { - m.quadraticCurveTo(g = c + c - g, f = b + b - f, c = (+h[j + 1]), b = (+h[j + 2])); - j += 2 - } - break; - case "h": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c += +h[j], b); - j++ - } - break; - case "H": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c = +h[j], b); - j++ - } - break; - case "v": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c, b += +h[j]); - j++ - } - break; - case "V": - while (j < n && !l.hasOwnProperty(h[j])) { - m.lineTo(c, b = +h[j]); - j++ - } - break - } - } - }, - clone: function() { - var a = this, - b = new Ext.draw.Path(); - b.params = a.params.slice(0); - b.commands = a.commands.slice(0); - b.cursor = a.cursor ? a.cursor.slice(0) : null; - b.startX = a.startX; - b.startY = a.startY; - b.svgString = a.svgString; - return b - }, - transform: function(j) { - if (j.isIdentity()) { - return - } - var a = j.getXX(), - f = j.getYX(), - m = j.getDX(), - l = j.getXY(), - e = j.getYY(), - k = j.getDY(), - b = this.params, - c = 0, - d = b.length, - h, g; - for (; c < d; c += 2) { - h = b[c]; - g = b[c + 1]; - b[c] = h * a + g * f + m; - b[c + 1] = h * l + g * e + k - } - this.dirt() - }, - getDimension: function(f) { - if (!f) { - f = {} - } - if (!this.commands || !this.commands.length) { - f.x = 0; - f.y = 0; - f.width = 0; - f.height = 0; - return f - } - f.left = Infinity; - f.top = Infinity; - f.right = -Infinity; - f.bottom = -Infinity; - var d = 0, - c = 0, - b = this.commands, - g = this.params, - e = b.length, - a, h; - for (; d < e; d++) { - switch (b[d]) { - case "M": - case "L": - a = g[c]; - h = g[c + 1]; - f.left = Math.min(a, f.left); - f.top = Math.min(h, f.top); - f.right = Math.max(a, f.right); - f.bottom = Math.max(h, f.bottom); - c += 2; - break; - case "C": - this.expandDimension(f, a, h, g[c], g[c + 1], g[c + 2], g[c + 3], a = g[c + 4], h = g[c + 5]); - c += 6; - break - } - } - f.x = f.left; - f.y = f.top; - f.width = f.right - f.left; - f.height = f.bottom - f.top; - return f - }, - getDimensionWithTransform: function(n, f) { - if (!this.commands || !this.commands.length) { - if (!f) { - f = {} - } - f.x = 0; - f.y = 0; - f.width = 0; - f.height = 0; - return f - } - f.left = Infinity; - f.top = Infinity; - f.right = -Infinity; - f.bottom = -Infinity; - var a = n.getXX(), - k = n.getYX(), - q = n.getDX(), - p = n.getXY(), - h = n.getYY(), - o = n.getDY(), - e = 0, - d = 0, - b = this.commands, - c = this.params, - g = b.length, - m, l; - for (; e < g; e++) { - switch (b[e]) { - case "M": - case "L": - m = c[d] * a + c[d + 1] * k + q; - l = c[d] * p + c[d + 1] * h + o; - f.left = Math.min(m, f.left); - f.top = Math.min(l, f.top); - f.right = Math.max(m, f.right); - f.bottom = Math.max(l, f.bottom); - d += 2; - break; - case "C": - this.expandDimension(f, m, l, c[d] * a + c[d + 1] * k + q, c[d] * p + c[d + 1] * h + o, c[d + 2] * a + c[d + 3] * k + q, c[d + 2] * p + c[d + 3] * h + o, m = c[d + 4] * a + c[d + 5] * k + q, l = c[d + 4] * p + c[d + 5] * h + o); - d += 6; - break - } - } - if (!f) { - f = {} - } - f.x = f.left; - f.y = f.top; - f.width = f.right - f.left; - f.height = f.bottom - f.top; - return f - }, - expandDimension: function(i, d, p, k, g, j, e, c, o) { - var m = this, - f = i.left, - a = i.right, - q = i.top, - n = i.bottom, - h = m.dim || (m.dim = []); - m.curveDimension(d, k, j, c, h); - f = Math.min(f, h[0]); - a = Math.max(a, h[1]); - m.curveDimension(p, g, e, o, h); - q = Math.min(q, h[0]); - n = Math.max(n, h[1]); - i.left = f; - i.right = a; - i.top = q; - i.bottom = n - }, - curveDimension: function(p, n, k, j, h) { - var i = 3 * (-p + 3 * (n - k) + j), - g = 6 * (p - 2 * n + k), - f = -3 * (p - n), - o, m, e = Math.min(p, j), - l = Math.max(p, j), - q; - if (i === 0) { - if (g === 0) { - h[0] = e; - h[1] = l; - return - } else { - o = -f / g; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - } - } else { - q = g * g - 4 * i * f; - if (q >= 0) { - q = Math.sqrt(q); - o = (q - g) / 2 / i; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - if (q > 0) { - o -= q / i; - if (0 < o && o < 1) { - m = this.interpolate(p, n, k, j, o); - e = Math.min(e, m); - l = Math.max(l, m) - } - } - } - } - h[0] = e; - h[1] = l - }, - interpolate: function(f, e, j, i, g) { - if (g === 0) { - return f - } - if (g === 1) { - return i - } - var h = (1 - g) / g; - return g * g * g * (i + h * (3 * j + h * (3 * e + h * f))) - }, - fromStripes: function(g) { - var e = this, - c = 0, - d = g.length, - b, a, f; - e.clear(); - for (; c < d; c++) { - f = g[c]; - e.params.push.apply(e.params, f); - e.commands.push("M"); - for (b = 2, a = f.length; b < a; b += 6) { - e.commands.push("C") - } - } - if (!e.cursor) { - e.cursor = [] - } - e.cursor[0] = e.params[e.params.length - 2]; - e.cursor[1] = e.params[e.params.length - 1]; - e.dirt() - }, - toStripes: function(k) { - var o = k || [], - p, n, m, b, a, h, g, f, e, c = this.commands, - d = this.params, - l = c.length; - for (f = 0, e = 0; f < l; f++) { - switch (c[f]) { - case "M": - p = [h = b = d[e++], g = a = d[e++]]; - o.push(p); - break; - case "L": - n = d[e++]; - m = d[e++]; - p.push((b + b + n) / 3, (a + a + m) / 3, (b + n + n) / 3, (a + m + m) / 3, b = n, a = m); - break; - case "C": - p.push(d[e++], d[e++], d[e++], d[e++], b = d[e++], a = d[e++]); - break; - case "Z": - n = h; - m = g; - p.push((b + b + n) / 3, (a + a + m) / 3, (b + n + n) / 3, (a + m + m) / 3, b = n, a = m); - break - } - } - return o - }, - updateSvgString: function() { - var b = [], - a = this.commands, - f = this.params, - e = a.length, - d = 0, - c = 0; - for (; d < e; d++) { - switch (a[d]) { - case "M": - b.push("M" + f[c] + "," + f[c + 1]); - c += 2; - break; - case "L": - b.push("L" + f[c] + "," + f[c + 1]); - c += 2; - break; - case "C": - b.push("C" + f[c] + "," + f[c + 1] + " " + f[c + 2] + "," + f[c + 3] + " " + f[c + 4] + "," + f[c + 5]); - c += 6; - break; - case "Z": - b.push("Z"); - break - } - } - this.svgString = b.join("") - }, - toString: function() { - if (!this.svgString) { - this.updateSvgString() - } - return this.svgString - } -}); -Ext.define("Ext.draw.overrides.Path", { - override: "Ext.draw.Path", - rayOrigin: { - x: -10000, - y: -10000 - }, - isPointInPath: function(o, n) { - var m = this, - c = m.commands, - q = Ext.draw.PathUtil, - p = m.rayOrigin, - f = m.params, - l = c.length, - e = null, - d = null, - b = 0, - a = 0, - k = 0, - h, g; - for (h = 0, g = 0; h < l; h++) { - switch (c[h]) { - case "M": - if (e !== null) { - if (q.linesIntersection(e, d, b, a, p.x, p.y, o, n)) { - k += 1 - } - } - e = b = f[g]; - d = a = f[g + 1]; - g += 2; - break; - case "L": - if (q.linesIntersection(b, a, f[g], f[g + 1], p.x, p.y, o, n)) { - k += 1 - } - b = f[g]; - a = f[g + 1]; - g += 2; - break; - case "C": - k += q.cubicLineIntersections(b, f[g], f[g + 2], f[g + 4], a, f[g + 1], f[g + 3], f[g + 5], p.x, p.y, o, n).length; - b = f[g + 4]; - a = f[g + 5]; - g += 6; - break; - case "Z": - if (e !== null) { - if (q.linesIntersection(e, d, b, a, p.x, p.y, o, n)) { - k += 1 - } - } - break - } - } - return k % 2 === 1 - }, - isPointOnPath: function(n, m) { - var l = this, - c = l.commands, - o = Ext.draw.PathUtil, - f = l.params, - k = c.length, - e = null, - d = null, - b = 0, - a = 0, - h, g; - for (h = 0, g = 0; h < k; h++) { - switch (c[h]) { - case "M": - if (e !== null) { - if (o.pointOnLine(e, d, b, a, n, m)) { - return true - } - } - e = b = f[g]; - d = a = f[g + 1]; - g += 2; - break; - case "L": - if (o.pointOnLine(b, a, f[g], f[g + 1], n, m)) { - return true - } - b = f[g]; - a = f[g + 1]; - g += 2; - break; - case "C": - if (o.pointOnCubic(b, f[g], f[g + 2], f[g + 4], a, f[g + 1], f[g + 3], f[g + 5], n, m)) { - return true - } - b = f[g + 4]; - a = f[g + 5]; - g += 6; - break; - case "Z": - if (e !== null) { - if (o.pointOnLine(e, d, b, a, n, m)) { - return true - } - } - break - } - } - return false - }, - getSegmentIntersections: function(t, d, s, c, r, b, o, a) { - var w = this, - g = arguments.length, - v = Ext.draw.PathUtil, - f = w.commands, - u = w.params, - k = f.length, - m = null, - l = null, - h = 0, - e = 0, - x = [], - q, n, p; - for (q = 0, n = 0; q < k; q++) { - switch (f[q]) { - case "M": - if (m !== null) { - switch (g) { - case 4: - p = v.linesIntersection(m, l, h, e, t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, m, l, h, e); - x.push.apply(x, p); - break - } - } - m = h = u[n]; - l = e = u[n + 1]; - n += 2; - break; - case "L": - switch (g) { - case 4: - p = v.linesIntersection(h, e, u[n], u[n + 1], t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, h, e, u[n], u[n + 1]); - x.push.apply(x, p); - break - } - h = u[n]; - e = u[n + 1]; - n += 2; - break; - case "C": - switch (g) { - case 4: - p = v.cubicLineIntersections(h, u[n], u[n + 2], u[n + 4], e, u[n + 1], u[n + 3], u[n + 5], t, d, s, c); - x.push.apply(x, p); - break; - case 8: - p = v.cubicsIntersections(h, u[n], u[n + 2], u[n + 4], e, u[n + 1], u[n + 3], u[n + 5], t, s, r, o, d, c, b, a); - x.push.apply(x, p); - break - } - h = u[n + 4]; - e = u[n + 5]; - n += 6; - break; - case "Z": - if (m !== null) { - switch (g) { - case 4: - p = v.linesIntersection(m, l, h, e, t, d, s, c); - if (p) { - x.push(p) - } - break; - case 8: - p = v.cubicLineIntersections(t, s, r, o, d, c, b, a, m, l, h, e); - x.push.apply(x, p); - break - } - } - break - } - } - return x - }, - getIntersections: function(o) { - var m = this, - c = m.commands, - g = m.params, - l = c.length, - f = null, - e = null, - b = 0, - a = 0, - d = [], - k, h, n; - for (k = 0, h = 0; k < l; k++) { - switch (c[k]) { - case "M": - if (f !== null) { - n = o.getSegmentIntersections.call(o, f, e, b, a); - d.push.apply(d, n) - } - f = b = g[h]; - e = a = g[h + 1]; - h += 2; - break; - case "L": - n = o.getSegmentIntersections.call(o, b, a, g[h], g[h + 1]); - d.push.apply(d, n); - b = g[h]; - a = g[h + 1]; - h += 2; - break; - case "C": - n = o.getSegmentIntersections.call(o, b, a, g[h], g[h + 1], g[h + 2], g[h + 3], g[h + 4], g[h + 5]); - d.push.apply(d, n); - b = g[h + 4]; - a = g[h + 5]; - h += 6; - break; - case "Z": - if (f !== null) { - n = o.getSegmentIntersections.call(o, f, e, b, a); - d.push.apply(d, n) - } - break - } - } - return d - } -}); -Ext.define("Ext.draw.sprite.Path", { - extend: "Ext.draw.sprite.Sprite", - requires: ["Ext.draw.Draw", "Ext.draw.Path"], - alias: ["sprite.path", "Ext.draw.Sprite"], - type: "path", - isPath: true, - inheritableStatics: { - def: { - processors: { - path: function(b, a) { - if (!(b instanceof Ext.draw.Path)) { - b = new Ext.draw.Path(b) - } - return b - } - }, - aliases: { - d: "path" - }, - triggers: { - path: "bbox" - }, - updaters: { - path: function(a) { - var b = a.path; - if (!b || b.bindAttr !== a) { - b = new Ext.draw.Path(); - b.bindAttr = a; - a.path = b - } - b.clear(); - this.updatePath(b, a); - this.scheduleUpdater(a, "bbox", ["path"]) - } - } - } - }, - updatePlainBBox: function(a) { - if (this.attr.path) { - this.attr.path.getDimension(a) - } - }, - updateTransformedBBox: function(a) { - if (this.attr.path) { - this.attr.path.getDimensionWithTransform(this.attr.matrix, a) - } - }, - render: function(b, c) { - var d = this.attr.matrix, - a = this.attr; - if (!a.path || a.path.params.length === 0) { - return - } - d.toContext(c); - c.appendPath(a.path); - c.fillStroke(a) - }, - updatePath: function(b, a) {} -}); -Ext.define("Ext.draw.overrides.sprite.Path", { - override: "Ext.draw.sprite.Path", - requires: ["Ext.draw.Color"], - isPointInPath: function(c, g) { - var b = this.attr; - if (b.fillStyle === Ext.draw.Color.RGBA_NONE) { - return this.isPointOnPath(c, g) - } - var e = b.path, - d = b.matrix, - f, a; - if (!d.isIdentity()) { - f = e.params.slice(0); - e.transform(b.matrix) - } - a = e.isPointInPath(c, g); - if (f) { - e.params = f - } - return a - }, - isPointOnPath: function(c, g) { - var b = this.attr, - e = b.path, - d = b.matrix, - f, a; - if (!d.isIdentity()) { - f = e.params.slice(0); - e.transform(b.matrix) - } - a = e.isPointOnPath(c, g); - if (f) { - e.params = f - } - return a - }, - hitTest: function(i, l) { - var e = this, - c = e.attr, - k = c.path, - g = c.matrix, - h = i[0], - f = i[1], - d = e.callParent([i, l]), - j = null, - a, b; - if (!d) { - return j - } - l = l || Ext.draw.sprite.Sprite.defaultHitTestOptions; - if (!g.isIdentity()) { - a = k.params.slice(0); - k.transform(c.matrix) - } - if (l.fill && l.stroke) { - b = c.fillStyle !== Ext.draw.Color.NONE && c.fillStyle !== Ext.draw.Color.RGBA_NONE; - if (b) { - if (k.isPointInPath(h, f)) { - j = { - sprite: e - } - } - } else { - if (k.isPointInPath(h, f) || k.isPointOnPath(h, f)) { - j = { - sprite: e - } - } - } - } else { - if (l.stroke && !l.fill) { - if (k.isPointOnPath(h, f)) { - j = { - sprite: e - } - } - } else { - if (l.fill && !l.stroke) { - if (k.isPointInPath(h, f)) { - j = { - sprite: e - } - } - } - } - } - if (a) { - k.params = a - } - return j - }, - getIntersections: function(j) { - if (!(j.isSprite && j.isPath)) { - return [] - } - var e = this.attr, - d = j.attr, - i = e.path, - h = d.path, - g = e.matrix, - a = d.matrix, - c, f, b; - if (!g.isIdentity()) { - c = i.params.slice(0); - i.transform(e.matrix) - } - if (!a.isIdentity()) { - f = h.params.slice(0); - h.transform(d.matrix) - } - b = i.getIntersections(h); - if (c) { - i.params = c - } - if (f) { - h.params = f - } - return b - } -}); -Ext.define("Ext.draw.sprite.Circle", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.circle", - type: "circle", - inheritableStatics: { - def: { - processors: { - cx: "number", - cy: "number", - r: "number" - }, - aliases: { - radius: "r", - x: "cx", - y: "cy", - centerX: "cx", - centerY: "cy" - }, - defaults: { - cx: 0, - cy: 0, - r: 4 - }, - triggers: { - cx: "path", - cy: "path", - r: "path" - } - } - }, - updatePlainBBox: function(c) { - var b = this.attr, - a = b.cx, - e = b.cy, - d = b.r; - c.x = a - d; - c.y = e - d; - c.width = d + d; - c.height = d + d - }, - updateTransformedBBox: function(d) { - var g = this.attr, - f = g.cx, - e = g.cy, - a = g.r, - h = g.matrix, - j = h.getScaleX(), - i = h.getScaleY(), - c, b; - c = j * a; - b = i * a; - d.x = h.x(f, e) - c; - d.y = h.y(f, e) - b; - d.width = c + c; - d.height = b + b - }, - updatePath: function(b, a) { - b.arc(a.cx, a.cy, a.r, 0, Math.PI * 2, false) - } -}); -Ext.define("Ext.draw.sprite.Arc", { - extend: "Ext.draw.sprite.Circle", - alias: "sprite.arc", - type: "arc", - inheritableStatics: { - def: { - processors: { - startAngle: "number", - endAngle: "number", - anticlockwise: "bool" - }, - aliases: { - from: "startAngle", - to: "endAngle", - start: "startAngle", - end: "endAngle" - }, - defaults: { - startAngle: 0, - endAngle: Math.PI * 2, - anticlockwise: false - }, - triggers: { - startAngle: "path", - endAngle: "path", - anticlockwise: "path" - } - } - }, - updatePath: function(b, a) { - b.arc(a.cx, a.cy, a.r, a.startAngle, a.endAngle, a.anticlockwise) - } -}); -Ext.define("Ext.draw.sprite.Arrow", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.arrow", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 1.5, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c * 0.7, ",", e - c * 0.4, "l", [c * 0.6, 0, 0, -c * 0.4, c, c * 0.8, -c, c * 0.8, 0, -c * 0.4, -c * 0.6, 0], "z")) - } -}); -Ext.define("Ext.draw.sprite.Composite", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.composite", - type: "composite", - isComposite: true, - config: { - sprites: [] - }, - constructor: function() { - this.sprites = []; - this.sprites.map = {}; - this.callParent(arguments) - }, - add: function(c) { - if (!c) { - return null - } - if (!c.isSprite) { - c = Ext.create("sprite." + c.type, c); - c.setParent(this); - c.setSurface(this.getSurface()) - } - var d = this, - a = d.attr, - b = c.applyTransformations; - c.applyTransformations = function() { - if (c.attr.dirtyTransform) { - a.dirtyTransform = true; - a.bbox.plain.dirty = true; - a.bbox.transform.dirty = true - } - b.call(c) - }; - d.sprites.push(c); - d.sprites.map[c.id] = c.getId(); - a.bbox.plain.dirty = true; - a.bbox.transform.dirty = true; - return c - }, - updateSurface: function(a) { - for (var b = 0, c = this.sprites.length; b < c; b++) { - this.sprites[b].setSurface(a) - } - }, - addAll: function(b) { - if (b.isSprite || b.type) { - this.add(b) - } else { - if (Ext.isArray(b)) { - var a = 0; - while (a < b.length) { - this.add(b[a++]) - } - } - } - }, - updatePlainBBox: function(g) { - var e = this, - b = Infinity, - h = -Infinity, - f = Infinity, - a = -Infinity, - j, k, c, d; - for (c = 0, d = e.sprites.length; c < d; c++) { - j = e.sprites[c]; - j.applyTransformations(); - k = j.getBBox(); - if (b > k.x) { - b = k.x - } - if (h < k.x + k.width) { - h = k.x + k.width - } - if (f > k.y) { - f = k.y - } - if (a < k.y + k.height) { - a = k.y + k.height - } - } - g.x = b; - g.y = f; - g.width = h - b; - g.height = a - f - }, - render: function(a, b, f) { - var d = this.attr.matrix, - c, e; - d.toContext(b); - for (c = 0, e = this.sprites.length; c < e; c++) { - a.renderSprite(this.sprites[c], f) - } - }, - destroy: function() { - var c = this, - d = c.sprites, - b = d.length, - a; - c.callParent(); - for (a = 0; a < b; a++) { - d[a].destroy() - } - d.length = 0 - } -}); -Ext.define("Ext.draw.sprite.Cross", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.cross", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size / 1.7, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c, ",", e, "l", [-c, -c, c, -c, c, c, c, -c, c, c, -c, c, c, c, -c, c, -c, -c, -c, c, -c, -c, "z"])) - } -}); -Ext.define("Ext.draw.sprite.Diamond", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.diamond", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 1.25, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString(["M", a, e - c, "l", c, c, -c, c, -c, -c, c, -c, "z"]) - } -}); -Ext.define("Ext.draw.sprite.Ellipse", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.ellipse", - type: "ellipse", - inheritableStatics: { - def: { - processors: { - cx: "number", - cy: "number", - rx: "number", - ry: "number", - axisRotation: "number" - }, - aliases: { - radius: "r", - x: "cx", - y: "cy", - centerX: "cx", - centerY: "cy", - radiusX: "rx", - radiusY: "ry" - }, - defaults: { - cx: 0, - cy: 0, - rx: 1, - ry: 1, - axisRotation: 0 - }, - triggers: { - cx: "path", - cy: "path", - rx: "path", - ry: "path", - axisRotation: "path" - } - } - }, - updatePlainBBox: function(c) { - var b = this.attr, - a = b.cx, - f = b.cy, - e = b.rx, - d = b.ry; - c.x = a - e; - c.y = f - d; - c.width = e + e; - c.height = d + d - }, - updateTransformedBBox: function(d) { - var i = this.attr, - f = i.cx, - e = i.cy, - c = i.rx, - b = i.ry, - l = b / c, - m = i.matrix.clone(), - a, q, k, j, p, o, n, g; - m.append(1, 0, 0, l, 0, e * (1 - l)); - a = m.getXX(); - k = m.getYX(); - p = m.getDX(); - q = m.getXY(); - j = m.getYY(); - o = m.getDY(); - n = Math.sqrt(a * a + k * k) * c; - g = Math.sqrt(q * q + j * j) * c; - d.x = f * a + e * k + p - n; - d.y = f * q + e * j + o - g; - d.width = n + n; - d.height = g + g - }, - updatePath: function(b, a) { - b.ellipse(a.cx, a.cy, a.rx, a.ry, a.axisRotation, 0, Math.PI * 2, false) - } -}); -Ext.define("Ext.draw.sprite.EllipticalArc", { - extend: "Ext.draw.sprite.Ellipse", - alias: "sprite.ellipticalArc", - type: "ellipticalArc", - inheritableStatics: { - def: { - processors: { - startAngle: "number", - endAngle: "number", - anticlockwise: "bool" - }, - aliases: { - from: "startAngle", - to: "endAngle", - start: "startAngle", - end: "endAngle" - }, - defaults: { - startAngle: 0, - endAngle: Math.PI * 2, - anticlockwise: false - }, - triggers: { - startAngle: "path", - endAngle: "path", - anticlockwise: "path" - } - } - }, - updatePath: function(b, a) { - b.ellipse(a.cx, a.cy, a.rx, a.ry, a.axisRotation, a.startAngle, a.endAngle, a.anticlockwise) - } -}); -Ext.define("Ext.draw.sprite.Rect", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.rect", - type: "rect", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number", - radius: "number" - }, - aliases: {}, - triggers: { - x: "path", - y: "path", - width: "path", - height: "path", - radius: "path" - }, - defaults: { - x: 0, - y: 0, - width: 8, - height: 8, - radius: 0 - } - } - }, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.x; - b.y = a.y; - b.width = a.width; - b.height = a.height - }, - updateTransformedBBox: function(a, b) { - this.attr.matrix.transformBBox(b, this.attr.radius, a) - }, - updatePath: function(f, d) { - var c = d.x, - g = d.y, - e = d.width, - b = d.height, - a = Math.min(d.radius, Math.abs(d.height) * 0.5, Math.abs(d.width) * 0.5); - if (a === 0) { - f.rect(c, g, e, b) - } else { - f.moveTo(c + a, g); - f.arcTo(c + e, g, c + e, g + b, a); - f.arcTo(c + e, g + b, c, g + b, a); - f.arcTo(c, g + b, c, g, a); - f.arcTo(c, g, c + a, g, a) - } - } -}); -Ext.define("Ext.draw.sprite.Image", { - extend: "Ext.draw.sprite.Rect", - alias: "sprite.image", - type: "image", - statics: { - imageLoaders: {} - }, - inheritableStatics: { - def: { - processors: { - src: "string" - }, - defaults: { - src: "", - width: null, - height: null - } - } - }, - render: function(c, o) { - var j = this, - h = j.attr, - n = h.matrix, - a = h.src, - l = h.x, - k = h.y, - b = h.width, - m = h.height, - g = Ext.draw.sprite.Image.imageLoaders[a], - f, d, e; - if (g && g.done) { - n.toContext(o); - d = g.image; - o.drawImage(d, l, k, b || (d.naturalWidth || d.width) / c.devicePixelRatio, m || (d.naturalHeight || d.height) / c.devicePixelRatio) - } else { - if (!g) { - f = new Image(); - g = Ext.draw.sprite.Image.imageLoaders[a] = { - image: f, - done: false, - pendingSprites: [j], - pendingSurfaces: [c] - }; - f.width = b; - f.height = m; - f.onload = function() { - if (!g.done) { - g.done = true; - for (e = 0; e < g.pendingSprites.length; e++) { - g.pendingSprites[e].setDirty(true) - } - for (e in g.pendingSurfaces) { - g.pendingSurfaces[e].renderFrame() - } - } - }; - f.src = a - } else { - Ext.Array.include(g.pendingSprites, j); - Ext.Array.include(g.pendingSurfaces, c) - } - } - } -}); -Ext.define("Ext.draw.sprite.Instancing", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.instancing", - type: "instancing", - isInstancing: true, - config: { - template: null - }, - instances: null, - applyTemplate: function(a) { - if (!a.isSprite) { - if (!a.xclass && !a.type) { - a.type = "circle" - } - a = Ext.create(a.xclass || "sprite." + a.type, a) - } - a.setParent(this); - return a - }, - updateTemplate: function(a, b) { - if (b) { - delete b.ownAttr - } - a.setSurface(this.getSurface()); - a.ownAttr = a.attr; - this.clearAll() - }, - updateSurface: function(a) { - var b = this.getTemplate(); - if (b) { - b.setSurface(a) - } - }, - get: function(a) { - return this.instances[a] - }, - getCount: function() { - return this.instances.length - }, - clearAll: function() { - var a = this.getTemplate(); - a.attr.children = this.instances = []; - this.position = 0 - }, - createInstance: function(d, f, c) { - var e = this.getTemplate(), - b = e.attr, - a = Ext.Object.chain(b); - e.topModifier.prepareAttributes(a); - e.attr = a; - e.setAttributes(d, f, c); - a.template = e; - this.instances.push(a); - e.attr = b; - this.position++; - return a - }, - getBBox: function() { - return null - }, - getBBoxFor: function(b, d) { - var c = this.getTemplate(), - a = c.attr, - e; - c.attr = this.instances[b]; - e = c.getBBox(d); - c.attr = a; - return e - }, - isVisible: function() { - var b = this.attr, - c = this.getParent(), - a; - a = c && c.isSurface && !b.hidden && b.globalAlpha; - return !!a - }, - isInstanceVisible: function(c) { - var e = this, - d = e.getTemplate(), - b = d.attr, - f = e.instances, - a = false; - if (!Ext.isNumber(c) || c < 0 || c >= f.length || !e.isVisible()) { - return a - } - d.attr = f[c]; - a = d.isVisible(point, options); - d.attr = b; - return a - }, - render: function(b, l, d, h) { - var g = this, - j = g.getTemplate(), - k = g.attr.matrix, - c = j.attr, - a = g.instances, - e, f = g.position; - k.toContext(l); - j.preRender(b, l, d, h); - j.useAttributes(l, h); - for (e = 0; e < f; e++) { - if (a[e].dirtyZIndex) { - break - } - } - for (e = 0; e < f; e++) { - if (a[e].hidden) { - continue - } - l.save(); - j.attr = a[e]; - j.useAttributes(l, h); - j.render(b, l, d, h); - l.restore() - } - j.attr = c - }, - setAttributesFor: function(c, e, f) { - var d = this.getTemplate(), - b = d.attr, - a = this.instances[c]; - if (!a) { - return - } - d.attr = a; - if (f) { - e = Ext.apply({}, e) - } else { - e = d.self.def.normalize(e) - } - d.topModifier.pushDown(a, e); - d.attr = b - }, - destroy: function() { - var b = this, - a = b.getTemplate(); - b.instances = null; - if (a) { - a.destroy() - } - b.callParent() - } -}); -Ext.define("Ext.draw.overrides.sprite.Instancing", { - override: "Ext.draw.sprite.Instancing", - hitTest: function(f, j) { - var e = this, - g = e.getTemplate(), - b = g.attr, - a = e.instances, - d = a.length, - c = 0, - h = null; - if (!e.isVisible()) { - return h - } - for (; c < d; c++) { - g.attr = a[c]; - h = g.hitTest(f, j); - if (h) { - h.isInstance = true; - h.template = h.sprite; - h.sprite = this; - h.instance = a[c]; - h.index = c; - return h - } - } - g.attr = b; - return h - } -}); -Ext.define("Ext.draw.sprite.Line", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.line", - type: "line", - inheritableStatics: { - def: { - processors: { - fromX: "number", - fromY: "number", - toX: "number", - toY: "number" - }, - defaults: { - fromX: 0, - fromY: 0, - toX: 1, - toY: 1, - strokeStyle: "black" - }, - aliases: { - x1: "fromX", - y1: "fromY", - x2: "toX", - y2: "toY" - } - } - }, - updateLineBBox: function(b, i, s, g, r, f) { - var o = this.attr, - q = o.matrix, - h = o.lineWidth / 2, - m, l, d, c, k, j, n; - if (i) { - n = q.transformPoint([s, g]); - s = n[0]; - g = n[1]; - n = q.transformPoint([r, f]); - r = n[0]; - f = n[1] - } - m = Math.min(s, r); - d = Math.max(s, r); - l = Math.min(g, f); - c = Math.max(g, f); - var t = Math.atan2(d - m, c - l), - a = Math.sin(t), - e = Math.cos(t), - k = h * e, - j = h * a; - m -= k; - l -= j; - d += k; - c += j; - b.x = m; - b.y = l; - b.width = d - m; - b.height = c - l - }, - updatePlainBBox: function(b) { - var a = this.attr; - this.updateLineBBox(b, false, a.fromX, a.fromY, a.toX, a.toY) - }, - updateTransformedBBox: function(b, c) { - var a = this.attr; - this.updateLineBBox(b, true, a.fromX, a.fromY, a.toX, a.toY) - }, - render: function(b, c) { - var a = this.attr, - d = this.attr.matrix; - d.toContext(c); - c.beginPath(); - c.moveTo(a.fromX, a.fromY); - c.lineTo(a.toX, a.toY); - c.stroke() - } -}); -Ext.define("Ext.draw.sprite.Plus", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.plus", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size / 1.3, - a = b.x - b.lineWidth / 2, - e = b.y; - d.fromSvgString("M".concat(a - c / 2, ",", e - c / 2, "l", [0, -c, c, 0, 0, c, c, 0, 0, c, -c, 0, 0, c, -c, 0, 0, -c, -c, 0, 0, -c, "z"])) - } -}); -Ext.define("Ext.draw.sprite.Sector", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.sector", - type: "sector", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - margin: "number" - }, - aliases: { - rho: "endRho" - }, - triggers: { - centerX: "path,bbox", - centerY: "path,bbox", - startAngle: "path,bbox", - endAngle: "path,bbox", - startRho: "path,bbox", - endRho: "path,bbox", - margin: "path,bbox" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: 0, - endAngle: 0, - startRho: 0, - endRho: 150, - margin: 0, - path: "M 0,0" - } - } - }, - getMidAngle: function() { - return this.midAngle || 0 - }, - updatePath: function(j, h) { - var g = Math.min(h.startAngle, h.endAngle), - c = Math.max(h.startAngle, h.endAngle), - b = this.midAngle = (g + c) * 0.5, - d = h.margin, - f = h.centerX, - e = h.centerY, - i = Math.min(h.startRho, h.endRho), - a = Math.max(h.startRho, h.endRho); - if (d) { - f += d * Math.cos(b); - e += d * Math.sin(b) - } - j.moveTo(f + i * Math.cos(g), e + i * Math.sin(g)); - j.lineTo(f + a * Math.cos(g), e + a * Math.sin(g)); - j.arc(f, e, a, g, c, false); - j.lineTo(f + i * Math.cos(c), e + i * Math.sin(c)); - j.arc(f, e, i, c, g, true) - } -}); -Ext.define("Ext.draw.sprite.Square", { - extend: "Ext.draw.sprite.Rect", - alias: "sprite.square", - inheritableStatics: { - def: { - processors: { - size: "number" - }, - defaults: { - size: 4 - }, - triggers: { - size: "size" - }, - updaters: { - size: function(a) { - var c = a.size, - b = a.lineWidth / 2; - this.setAttributes({ - x: a.x - c - b, - y: a.y - c, - height: 2 * c, - width: 2 * c - }) - } - } - } - } -}); -Ext.define("Ext.draw.TextMeasurer", { - singleton: true, - requires: ["Ext.util.TextMetrics"], - measureDiv: null, - measureCache: {}, - precise: Ext.isIE8, - measureDivTpl: { - tag: "div", - style: { - overflow: "hidden", - position: "relative", - "float": "left", - width: 0, - height: 0 - }, - children: { - tag: "div", - style: { - display: "block", - position: "absolute", - x: -100000, - y: -100000, - padding: 0, - margin: 0, - "z-index": -100000, - "white-space": "nowrap" - } - } - }, - actualMeasureText: function(g, b) { - var e = Ext.draw.TextMeasurer, - f = e.measureDiv, - a = 100000, - c; - if (!f) { - var d = Ext.Element.create({ - style: { - overflow: "hidden", - position: "relative", - "float": "left", - width: 0, - height: 0 - } - }); - e.measureDiv = f = Ext.Element.create({ - style: { - position: "absolute", - x: a, - y: a, - "z-index": -a, - "white-space": "nowrap", - display: "block", - padding: 0, - margin: 0 - } - }); - Ext.getBody().appendChild(d); - d.appendChild(f) - } - if (b) { - f.setStyle({ - font: b, - lineHeight: "normal" - }) - } - f.setText("(" + g + ")"); - c = f.getSize(); - f.setText("()"); - c.width -= f.getSize().width; - return c - }, - measureTextSingleLine: function(h, d) { - if (this.precise) { - return this.preciseMeasureTextSingleLine(h, d) - } - h = h.toString(); - var a = this.measureCache, - g = h.split(""), - c = 0, - j = 0, - l, b, e, f, k; - if (!a[d]) { - a[d] = {} - } - a = a[d]; - if (a[h]) { - return a[h] - } - for (e = 0, f = g.length; e < f; e++) { - b = g[e]; - if (!(l = a[b])) { - k = this.actualMeasureText(b, d); - l = a[b] = k - } - c += l.width; - j = Math.max(j, l.height) - } - return a[h] = { - width: c, - height: j - } - }, - preciseMeasureTextSingleLine: function(c, a) { - c = c.toString(); - var b = this.measureDiv || (this.measureDiv = Ext.getBody().createChild(this.measureDivTpl).down("div")); - b.setStyle({ - font: a || "" - }); - return Ext.util.TextMetrics.measure(b, c) - }, - measureText: function(e, b) { - var h = e.split("\n"), - d = h.length, - f = 0, - a = 0, - j, c, g; - if (d === 1) { - return this.measureTextSingleLine(e, b) - } - g = []; - for (c = 0; c < d; c++) { - j = this.measureTextSingleLine(h[c], b); - g.push(j); - f += j.height; - a = Math.max(a, j.width) - } - return { - width: a, - height: f, - sizes: g - } - } -}); -Ext.define("Ext.draw.sprite.Text", function() { - var d = { - "xx-small": true, - "x-small": true, - small: true, - medium: true, - large: true, - "x-large": true, - "xx-large": true - }; - var b = { - normal: true, - bold: true, - bolder: true, - lighter: true, - 100: true, - 200: true, - 300: true, - 400: true, - 500: true, - 600: true, - 700: true, - 800: true, - 900: true - }; - var a = { - start: "start", - left: "start", - center: "center", - middle: "center", - end: "end", - right: "end" - }; - var c = { - top: "top", - hanging: "hanging", - middle: "middle", - center: "middle", - alphabetic: "alphabetic", - ideographic: "ideographic", - bottom: "bottom" - }; - return { - extend: "Ext.draw.sprite.Sprite", - requires: ["Ext.draw.TextMeasurer", "Ext.draw.Color"], - alias: "sprite.text", - type: "text", - lineBreakRe: /\r?\n/g, - inheritableStatics: { - def: { - animationProcessors: { - text: "text" - }, - processors: { - x: "number", - y: "number", - text: "string", - fontSize: function(e) { - if (Ext.isNumber(+e)) { - return e + "px" - } else { - if (e.match(Ext.dom.Element.unitRe)) { - return e - } else { - if (e in d) { - return e - } - } - } - }, - fontStyle: "enums(,italic,oblique)", - fontVariant: "enums(,small-caps)", - fontWeight: function(e) { - if (e in b) { - return String(e) - } else { - return "" - } - }, - fontFamily: "string", - textAlign: function(e) { - return a[e] || "center" - }, - textBaseline: function(e) { - return c[e] || "alphabetic" - }, - font: "string" - }, - aliases: { - "font-size": "fontSize", - "font-family": "fontFamily", - "font-weight": "fontWeight", - "font-variant": "fontVariant", - "text-anchor": "textAlign" - }, - defaults: { - fontStyle: "", - fontVariant: "", - fontWeight: "", - fontSize: "10px", - fontFamily: "sans-serif", - font: "10px sans-serif", - textBaseline: "alphabetic", - textAlign: "start", - strokeStyle: "rgba(0, 0, 0, 0)", - fillStyle: "#000", - x: 0, - y: 0, - text: "" - }, - triggers: { - fontStyle: "fontX,bbox", - fontVariant: "fontX,bbox", - fontWeight: "fontX,bbox", - fontSize: "fontX,bbox", - fontFamily: "fontX,bbox", - font: "font,bbox,canvas", - textBaseline: "bbox", - textAlign: "bbox", - x: "bbox", - y: "bbox", - text: "bbox" - }, - updaters: { - fontX: "makeFontShorthand", - font: "parseFontShorthand" - } - } - }, - constructor: function(e) { - if (e && e.font) { - e = Ext.clone(e); - for (var f in e) { - if (f !== "font" && f.indexOf("font") === 0) { - delete e[f] - } - } - } - Ext.draw.sprite.Sprite.prototype.constructor.call(this, e) - }, - fontValuesMap: { - italic: "fontStyle", - oblique: "fontStyle", - "small-caps": "fontVariant", - bold: "fontWeight", - bolder: "fontWeight", - lighter: "fontWeight", - "100": "fontWeight", - "200": "fontWeight", - "300": "fontWeight", - "400": "fontWeight", - "500": "fontWeight", - "600": "fontWeight", - "700": "fontWeight", - "800": "fontWeight", - "900": "fontWeight", - "xx-small": "fontSize", - "x-small": "fontSize", - small: "fontSize", - medium: "fontSize", - large: "fontSize", - "x-large": "fontSize", - "xx-large": "fontSize" - }, - makeFontShorthand: function(e) { - var f = []; - if (e.fontStyle) { - f.push(e.fontStyle) - } - if (e.fontVariant) { - f.push(e.fontVariant) - } - if (e.fontWeight) { - f.push(e.fontWeight) - } - if (e.fontSize) { - f.push(e.fontSize) - } - if (e.fontFamily) { - f.push(e.fontFamily) - } - this.setAttributes({ - font: f.join(" ") - }, true) - }, - parseFontShorthand: function(j) { - var m = j.font, - k = m.length, - l = {}, - n = this.fontValuesMap, - e = 0, - i, g, f, h; - while (e < k && i !== -1) { - i = m.indexOf(" ", e); - if (i < 0) { - f = m.substr(e) - } else { - if (i > e) { - f = m.substr(e, i - e) - } else { - continue - } - } - g = f.indexOf("/"); - if (g > 0) { - f = f.substr(0, g) - } else { - if (g === 0) { - continue - } - } - if (f !== "normal" && f !== "inherit") { - h = n[f]; - if (h) { - l[h] = f - } else { - if (f.match(Ext.dom.Element.unitRe)) { - l.fontSize = f - } else { - l.fontFamily = m.substr(e); - break - } - } - } - e = i + 1 - } - if (!l.fontStyle) { - l.fontStyle = "" - } - if (!l.fontVariant) { - l.fontVariant = "" - } - if (!l.fontWeight) { - l.fontWeight = "" - } - this.setAttributes(l, true) - }, - fontProperties: { - fontStyle: true, - fontVariant: true, - fontWeight: true, - fontSize: true, - fontFamily: true - }, - setAttributes: function(g, i, e) { - var f, h; - if (g && g.font) { - h = {}; - for (f in g) { - if (!(f in this.fontProperties)) { - h[f] = g[f] - } - } - g = h - } - this.callParent([g, i, e]) - }, - getBBox: function(g) { - var h = this, - f = h.attr.bbox.plain, - e = h.getSurface(); - if (f.dirty) { - h.updatePlainBBox(f); - f.dirty = false - } - if (e.getInherited().rtl && e.getFlipRtlText()) { - h.updatePlainBBox(f, true) - } - return h.callParent([g]) - }, - rtlAlignments: { - start: "end", - center: "center", - end: "start" - }, - updatePlainBBox: function(k, B) { - var C = this, - w = C.attr, - o = w.x, - n = w.y, - q = [], - t = w.font, - r = w.text, - s = w.textBaseline, - l = w.textAlign, - u = (B && C.oldSize) ? C.oldSize : (C.oldSize = Ext.draw.TextMeasurer.measureText(r, t)), - z = C.getSurface(), - p = z.getInherited().rtl, - v = p && z.getFlipRtlText(), - h = z.getRect(), - f = u.sizes, - g = u.height, - j = u.width, - m = f ? f.length : 0, - e, A = 0; - switch (s) { - case "hanging": - case "top": - break; - case "ideographic": - case "bottom": - n -= g; - break; - case "alphabetic": - n -= g * 0.8; - break; - case "middle": - n -= g * 0.5; - break - } - if (v) { - o = h[2] - h[0] - o; - l = C.rtlAlignments[l] - } - switch (l) { - case "start": - if (p) { - for (; A < m; A++) { - e = f[A].width; - q.push(-(j - e)) - } - } - break; - case "end": - o -= j; - if (p) { - break - } - for (; A < m; A++) { - e = f[A].width; - q.push(j - e) - } - break; - case "center": - o -= j * 0.5; - for (; A < m; A++) { - e = f[A].width; - q.push((p ? -1 : 1) * (j - e) * 0.5) - } - break - } - w.textAlignOffsets = q; - k.x = o; - k.y = n; - k.width = j; - k.height = g - }, - setText: function(e) { - this.setAttributes({ - text: e - }, true) - }, - render: function(e, q, k) { - var h = this, - g = h.attr, - p = Ext.draw.Matrix.fly(g.matrix.elements.slice(0)), - o = h.getBBox(true), - s = g.textAlignOffsets, - m = Ext.draw.Color.RGBA_NONE, - l, j, f, r, n; - if (g.text.length === 0) { - return - } - r = g.text.split(h.lineBreakRe); - n = o.height / r.length; - l = g.bbox.plain.x; - j = g.bbox.plain.y + n * 0.78; - p.toContext(q); - if (e.getInherited().rtl) { - l += g.bbox.plain.width - } - for (f = 0; f < r.length; f++) { - if (q.fillStyle !== m) { - q.fillText(r[f], l + (s[f] || 0), j + n * f) - } - if (q.strokeStyle !== m) { - q.strokeText(r[f], l + (s[f] || 0), j + n * f) - } - } - } - } -}); -Ext.define("Ext.draw.sprite.Tick", { - extend: "Ext.draw.sprite.Line", - alias: "sprite.tick", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "tick", - y: "tick", - size: "tick" - }, - updaters: { - tick: function(b) { - var d = b.size * 1.5, - c = b.lineWidth / 2, - a = b.x, - e = b.y; - this.setAttributes({ - fromX: a - c, - fromY: e - d, - toX: a - c, - toY: e + d - }) - } - } - } - } -}); -Ext.define("Ext.draw.sprite.Triangle", { - extend: "Ext.draw.sprite.Path", - alias: "sprite.triangle", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - size: "number" - }, - defaults: { - x: 0, - y: 0, - size: 4 - }, - triggers: { - x: "path", - y: "path", - size: "path" - } - } - }, - updatePath: function(d, b) { - var c = b.size * 2.2, - a = b.x, - e = b.y; - d.fromSvgString("M".concat(a, ",", e, "m0-", c * 0.58, "l", c * 0.5, ",", c * 0.87, "-", c, ",0z")) - } -}); -Ext.define("Ext.draw.gradient.Linear", { - extend: "Ext.draw.gradient.Gradient", - requires: ["Ext.draw.Color"], - type: "linear", - config: { - degrees: 0, - radians: 0 - }, - applyRadians: function(b, a) { - if (Ext.isNumber(b)) { - return b - } - return a - }, - applyDegrees: function(b, a) { - if (Ext.isNumber(b)) { - return b - } - return a - }, - updateRadians: function(a) { - this.setDegrees(Ext.draw.Draw.degrees(a)) - }, - updateDegrees: function(a) { - this.setRadians(Ext.draw.Draw.rad(a)) - }, - generateGradient: function(q, o) { - var c = this.getRadians(), - p = Math.cos(c), - j = Math.sin(c), - m = o.width, - f = o.height, - d = o.x + m * 0.5, - b = o.y + f * 0.5, - n = this.getStops(), - g = n.length, - k, a, e; - if (Ext.isNumber(d + b) && f > 0 && m > 0) { - a = (Math.sqrt(f * f + m * m) * Math.abs(Math.cos(c - Math.atan(f / m)))) / 2; - k = q.createLinearGradient(d + p * a, b + j * a, d - p * a, b - j * a); - for (e = 0; e < g; e++) { - k.addColorStop(n[e].offset, n[e].color) - } - return k - } - return Ext.draw.Color.NONE - } -}); -Ext.define("Ext.draw.gradient.Radial", { - extend: "Ext.draw.gradient.Gradient", - type: "radial", - config: { - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - } - }, - applyStart: function(a, b) { - if (!b) { - return a - } - var c = { - x: b.x, - y: b.y, - r: b.r - }; - if ("x" in a) { - c.x = a.x - } else { - if ("centerX" in a) { - c.x = a.centerX - } - } - if ("y" in a) { - c.y = a.y - } else { - if ("centerY" in a) { - c.y = a.centerY - } - } - if ("r" in a) { - c.r = a.r - } else { - if ("radius" in a) { - c.r = a.radius - } - } - return c - }, - applyEnd: function(b, a) { - if (!a) { - return b - } - var c = { - x: a.x, - y: a.y, - r: a.r - }; - if ("x" in b) { - c.x = b.x - } else { - if ("centerX" in b) { - c.x = b.centerX - } - } - if ("y" in b) { - c.y = b.y - } else { - if ("centerY" in b) { - c.y = b.centerY - } - } - if ("r" in b) { - c.r = b.r - } else { - if ("radius" in b) { - c.r = b.radius - } - } - return c - }, - generateGradient: function(n, m) { - var a = this.getStart(), - b = this.getEnd(), - k = m.width * 0.5, - d = m.height * 0.5, - j = m.x + k, - f = m.y + d, - g = n.createRadialGradient(j + a.x * k, f + a.y * d, a.r * Math.max(k, d), j + b.x * k, f + b.y * d, b.r * Math.max(k, d)), - l = this.getStops(), - e = l.length, - c; - for (c = 0; c < e; c++) { - g.addColorStop(l[c].offset, l[c].color) - } - return g - } -}); -Ext.define("Ext.draw.Surface", { - extend: "Ext.draw.SurfaceBase", - xtype: "surface", - requires: ["Ext.draw.sprite.*", "Ext.draw.gradient.*", "Ext.draw.sprite.AttributeDefinition", "Ext.draw.Matrix", "Ext.draw.Draw"], - uses: ["Ext.draw.engine.Canvas"], - devicePixelRatio: window.devicePixelRatio || window.screen.deviceXDPI / window.screen.logicalXDPI, - deprecated: { - "5.1.0": { - statics: { - methods: { - stableSort: function(a) { - return Ext.Array.sort(a, function(d, c) { - return d.attr.zIndex - c.attr.zIndex - }) - } - } - } - } - }, - config: { - cls: Ext.baseCSSPrefix + "surface", - rect: null, - background: null, - items: [], - dirty: false, - flipRtlText: false - }, - isSurface: true, - isPendingRenderFrame: false, - dirtyPredecessorCount: 0, - constructor: function(a) { - var b = this; - b.predecessors = []; - b.successors = []; - b.map = {}; - b.callParent([a]); - b.matrix = new Ext.draw.Matrix(); - b.inverseMatrix = b.matrix.inverse() - }, - roundPixel: function(a) { - return Math.round(this.devicePixelRatio * a) / this.devicePixelRatio - }, - waitFor: function(a) { - var b = this, - c = b.predecessors; - if (!Ext.Array.contains(c, a)) { - c.push(a); - a.successors.push(b); - if (a.getDirty()) { - b.dirtyPredecessorCount++ - } - } - }, - updateDirty: function(d) { - var c = this.successors, - e = c.length, - b = 0, - a; - for (; b < e; b++) { - a = c[b]; - if (d) { - a.dirtyPredecessorCount++; - a.setDirty(true) - } else { - a.dirtyPredecessorCount--; - if (a.dirtyPredecessorCount === 0 && a.isPendingRenderFrame) { - a.renderFrame() - } - } - } - }, - applyBackground: function(a, b) { - this.setDirty(true); - if (Ext.isString(a)) { - a = { - fillStyle: a - } - } - return Ext.factory(a, Ext.draw.sprite.Rect, b) - }, - applyRect: function(a, b) { - if (b && a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]) { - return - } - if (Ext.isArray(a)) { - return [a[0], a[1], a[2], a[3]] - } else { - if (Ext.isObject(a)) { - return [a.x || a.left, a.y || a.top, a.width || (a.right - a.left), a.height || (a.bottom - a.top)] - } - } - }, - updateRect: function(i) { - var h = this, - c = i[0], - f = i[1], - g = c + i[2], - a = f + i[3], - e = h.getBackground(), - d = h.element; - d.setLocalXY(Math.floor(c), Math.floor(f)); - d.setSize(Math.ceil(g - Math.floor(c)), Math.ceil(a - Math.floor(f))); - if (e) { - e.setAttributes({ - x: 0, - y: 0, - width: Math.ceil(g - Math.floor(c)), - height: Math.ceil(a - Math.floor(f)) - }) - } - h.setDirty(true) - }, - resetTransform: function() { - this.matrix.set(1, 0, 0, 1, 0, 0); - this.inverseMatrix.set(1, 0, 0, 1, 0, 0); - this.setDirty(true) - }, - get: function(a) { - return this.map[a] || this.getItems()[a] - }, - add: function() { - var g = this, - e = Array.prototype.slice.call(arguments), - j = Ext.isArray(e[0]), - a = g.map, - c = [], - f, k, h, b, d; - f = Ext.Array.clean(j ? e[0] : e); - if (!f.length) { - return c - } - for (b = 0, d = f.length; b < d; b++) { - k = f[b]; - h = null; - if (k.isSprite && !a[k.getId()]) { - h = k - } else { - if (!a[k.id]) { - h = this.createItem(k) - } - } - if (h) { - a[h.getId()] = h; - c.push(h); - h.setParent(g); - h.setSurface(g); - g.onAdd(h) - } - } - f = g.getItems(); - if (f) { - f.push.apply(f, c) - } - g.dirtyZIndex = true; - g.setDirty(true); - if (!j && c.length === 1) { - return c[0] - } else { - return c - } - }, - onAdd: Ext.emptyFn, - remove: function(a, c) { - var b = this, - e, d; - if (a) { - if (a.charAt) { - a = b.map[a] - } - if (!a || !a.isSprite) { - return null - } - if (a.isDestroyed || a.isDestroying) { - return a - } - e = a.getId(); - d = b.map[e]; - delete b.map[e]; - if (c) { - a.destroy() - } - if (!d) { - return a - } - a.setParent(null); - a.setSurface(null); - Ext.Array.remove(b.getItems(), a); - b.dirtyZIndex = true; - b.setDirty(true) - } - return a || null - }, - removeAll: function(d) { - var a = this.getItems(), - b = a.length - 1, - c; - if (d) { - for (; b >= 0; b--) { - a[b].destroy() - } - } else { - for (; b >= 0; b--) { - c = a[b]; - c.setParent(null); - c.setSurface(null) - } - } - a.length = 0; - this.map = {}; - this.dirtyZIndex = true - }, - applyItems: function(a) { - if (this.getItems()) { - this.removeAll(true) - } - return Ext.Array.from(this.add(a)) - }, - createItem: function(a) { - return Ext.create(a.xclass || "sprite." + a.type, a) - }, - getBBox: function(f, b) { - var f = Ext.Array.from(f), - c = Infinity, - h = -Infinity, - g = Infinity, - a = -Infinity, - j, k, d, e; - for (d = 0, e = f.length; d < e; d++) { - j = f[d]; - k = j.getBBox(b); - if (c > k.x) { - c = k.x - } - if (h < k.x + k.width) { - h = k.x + k.width - } - if (g > k.y) { - g = k.y - } - if (a < k.y + k.height) { - a = k.y + k.height - } - } - return { - x: c, - y: g, - width: h - c, - height: a - g - } - }, - emptyRect: [0, 0, 0, 0], - getEventXY: function(d) { - var g = this, - f = g.getInherited().rtl, - c = d.getXY(), - a = g.getOwnerBody(), - i = a.getXY(), - h = g.getRect() || g.emptyRect, - j = [], - b; - if (f) { - b = a.getWidth(); - j[0] = i[0] - c[0] - h[0] + b - } else { - j[0] = c[0] - i[0] - h[0] - } - j[1] = c[1] - i[1] - h[1]; - return j - }, - clear: Ext.emptyFn, - orderByZIndex: function() { - var d = this, - a = d.getItems(), - e = false, - b, c; - if (d.getDirty()) { - for (b = 0, c = a.length; b < c; b++) { - if (a[b].attr.dirtyZIndex) { - e = true; - break - } - } - if (e) { - Ext.Array.sort(a, function(g, f) { - return g.attr.zIndex - f.attr.zIndex - }); - this.setDirty(true) - } - for (b = 0, c = a.length; b < c; b++) { - a[b].attr.dirtyZIndex = false - } - } - }, - repaint: function() { - var a = this; - a.repaint = Ext.emptyFn; - Ext.defer(function() { - delete a.repaint; - a.element.repaint() - }, 1) - }, - renderFrame: function() { - var g = this; - if (!g.element) { - return - } - if (g.dirtyPredecessorCount > 0) { - g.isPendingRenderFrame = true; - return - } - var f = g.getRect(), - c = g.getBackground(), - a = g.getItems(), - e, b, d; - if (!f) { - return - } - g.orderByZIndex(); - if (g.getDirty()) { - g.clear(); - g.clearTransform(); - if (c) { - g.renderSprite(c) - } - for (b = 0, d = a.length; b < d; b++) { - e = a[b]; - if (g.renderSprite(e) === false) { - return - } - e.attr.textPositionCount = g.textPosition - } - g.setDirty(false) - } - }, - renderSprite: Ext.emptyFn, - clearTransform: Ext.emptyFn, - destroy: function() { - var a = this; - a.removeAll(true); - a.predecessors = null; - a.successors = null; - a.callParent() - } -}); -Ext.define("Ext.draw.overrides.Surface", { - override: "Ext.draw.Surface", - hitTest: function(b, c) { - var f = this, - g = f.getItems(), - e, d, a; - c = c || Ext.draw.sprite.Sprite.defaultHitTestOptions; - for (e = g.length - 1; e >= 0; e--) { - d = g[e]; - if (d.hitTest) { - a = d.hitTest(b, c); - if (a) { - return a - } - } - } - return null - }, - hitTestEvent: function(b, a) { - var c = this.getEventXY(b); - return this.hitTest(c, a) - } -}); -Ext.define("Ext.draw.engine.SvgContext", { - requires: ["Ext.draw.Color"], - toSave: ["strokeOpacity", "strokeStyle", "fillOpacity", "fillStyle", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "lineDash", "lineDashOffset", "miterLimit", "shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor", "globalCompositeOperation", "position", "fillGradient", "strokeGradient"], - strokeOpacity: 1, - strokeStyle: "none", - fillOpacity: 1, - fillStyle: "none", - lineDash: [], - lineDashOffset: 0, - globalAlpha: 1, - lineWidth: 1, - lineCap: "butt", - lineJoin: "miter", - miterLimit: 10, - shadowOffsetX: 0, - shadowOffsetY: 0, - shadowBlur: 0, - shadowColor: "none", - globalCompositeOperation: "src", - urlStringRe: /^url\(#([\w\-]+)\)$/, - constructor: function(a) { - this.surface = a; - this.state = []; - this.matrix = new Ext.draw.Matrix(); - this.path = null; - this.clear() - }, - clear: function() { - this.group = this.surface.mainGroup; - this.position = 0; - this.path = null - }, - getElement: function(a) { - return this.surface.getSvgElement(this.group, a, this.position++) - }, - removeElement: function(d) { - var d = Ext.fly(d), - h, g, b, f, a, e, c; - if (!d) { - return - } - if (d.dom.tagName === "g") { - a = d.dom.gradients; - for (c in a) { - a[c].destroy() - } - } else { - h = d.getAttribute("fill"); - g = d.getAttribute("stroke"); - b = h && h.match(this.urlStringRe); - f = g && g.match(this.urlStringRe); - if (b && b[1]) { - e = Ext.fly(b[1]); - if (e) { - e.destroy() - } - } - if (f && f[1]) { - e = Ext.fly(f[1]); - if (e) { - e.destroy() - } - } - } - d.destroy() - }, - save: function() { - var c = this.toSave, - e = {}, - d = this.getElement("g"), - b, a; - for (a = 0; a < c.length; a++) { - b = c[a]; - if (b in this) { - e[b] = this[b] - } - } - this.position = 0; - e.matrix = this.matrix.clone(); - this.state.push(e); - this.group = d; - return d - }, - restore: function() { - var d = this.toSave, - e = this.state.pop(), - c = this.group.dom.childNodes, - b, a; - while (c.length > this.position) { - this.removeElement(c[c.length - 1]) - } - for (a = 0; a < d.length; a++) { - b = d[a]; - if (b in e) { - this[b] = e[b] - } else { - delete this[b] - } - } - this.setTransform.apply(this, e.matrix.elements); - this.group = this.group.getParent() - }, - transform: function(f, b, e, g, d, c) { - if (this.path) { - var a = Ext.draw.Matrix.fly([f, b, e, g, d, c]).inverse(); - this.path.transform(a) - } - this.matrix.append(f, b, e, g, d, c) - }, - setTransform: function(e, a, d, f, c, b) { - if (this.path) { - this.path.transform(this.matrix) - } - this.matrix.reset(); - this.transform(e, a, d, f, c, b) - }, - scale: function(a, b) { - this.transform(a, 0, 0, b, 0, 0) - }, - rotate: function(d) { - var c = Math.cos(d), - a = Math.sin(d), - b = -Math.sin(d), - e = Math.cos(d); - this.transform(c, a, b, e, 0, 0) - }, - translate: function(a, b) { - this.transform(1, 0, 0, 1, a, b) - }, - setGradientBBox: function(a) { - this.bbox = a - }, - beginPath: function() { - this.path = new Ext.draw.Path() - }, - moveTo: function(a, b) { - if (!this.path) { - this.beginPath() - } - this.path.moveTo(a, b); - this.path.element = null - }, - lineTo: function(a, b) { - if (!this.path) { - this.beginPath() - } - this.path.lineTo(a, b); - this.path.element = null - }, - rect: function(b, d, c, a) { - this.moveTo(b, d); - this.lineTo(b + c, d); - this.lineTo(b + c, d + a); - this.lineTo(b, d + a); - this.closePath() - }, - strokeRect: function(b, d, c, a) { - this.beginPath(); - this.rect(b, d, c, a); - this.stroke() - }, - fillRect: function(b, d, c, a) { - this.beginPath(); - this.rect(b, d, c, a); - this.fill() - }, - closePath: function() { - if (!this.path) { - this.beginPath() - } - this.path.closePath(); - this.path.element = null - }, - arcSvg: function(d, a, f, g, c, b, e) { - if (!this.path) { - this.beginPath() - } - this.path.arcSvg(d, a, f, g, c, b, e); - this.path.element = null - }, - arc: function(b, f, a, d, c, e) { - if (!this.path) { - this.beginPath() - } - this.path.arc(b, f, a, d, c, e); - this.path.element = null - }, - ellipse: function(a, h, g, f, d, c, b, e) { - if (!this.path) { - this.beginPath() - } - this.path.ellipse(a, h, g, f, d, c, b, e); - this.path.element = null - }, - arcTo: function(b, e, a, d, g, f, c) { - if (!this.path) { - this.beginPath() - } - this.path.arcTo(b, e, a, d, g, f, c); - this.path.element = null - }, - bezierCurveTo: function(d, f, b, e, a, c) { - if (!this.path) { - this.beginPath() - } - this.path.bezierCurveTo(d, f, b, e, a, c); - this.path.element = null - }, - strokeText: function(d, a, e) { - d = String(d); - if (this.strokeStyle) { - var b = this.getElement("text"), - c = this.surface.getSvgElement(b, "tspan", 0); - this.surface.setElementAttributes(b, { - x: a, - y: e, - transform: this.matrix.toSvg(), - stroke: this.strokeStyle, - fill: "none", - opacity: this.globalAlpha, - "stroke-opacity": this.strokeOpacity, - style: "font: " + this.font, - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }); - if (this.lineDash.length) { - this.surface.setElementAttributes(b, { - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }) - } - if (c.dom.firstChild) { - c.dom.removeChild(c.dom.firstChild) - } - this.surface.setElementAttributes(c, { - "alignment-baseline": "alphabetic" - }); - c.dom.appendChild(document.createTextNode(Ext.String.htmlDecode(d))) - } - }, - fillText: function(d, a, e) { - d = String(d); - if (this.fillStyle) { - var b = this.getElement("text"), - c = this.surface.getSvgElement(b, "tspan", 0); - this.surface.setElementAttributes(b, { - x: a, - y: e, - transform: this.matrix.toSvg(), - fill: this.fillStyle, - opacity: this.globalAlpha, - "fill-opacity": this.fillOpacity, - style: "font: " + this.font - }); - if (c.dom.firstChild) { - c.dom.removeChild(c.dom.firstChild) - } - this.surface.setElementAttributes(c, { - "alignment-baseline": "alphabetic" - }); - c.dom.appendChild(document.createTextNode(Ext.String.htmlDecode(d))) - } - }, - drawImage: function(c, k, i, l, e, p, n, a, g) { - var f = this, - d = f.getElement("image"), - j = k, - h = i, - b = typeof l === "undefined" ? c.width : l, - m = typeof e === "undefined" ? c.height : e, - o = null; - if (typeof g !== "undefined") { - o = k + " " + i + " " + l + " " + e; - j = p; - h = n; - b = a; - m = g - } - d.dom.setAttributeNS("http://www.w3.org/1999/xlink", "href", c.src); - f.surface.setElementAttributes(d, { - viewBox: o, - x: j, - y: h, - width: b, - height: m, - opacity: f.globalAlpha, - transform: f.matrix.toSvg() - }) - }, - fill: function() { - if (!this.path) { - return - } - if (this.fillStyle) { - var c, a = this.fillGradient, - d = this.bbox, - b = this.path.element; - if (!b) { - c = this.path.toString(); - b = this.path.element = this.getElement("path"); - this.surface.setElementAttributes(b, { - d: c, - transform: this.matrix.toSvg() - }) - } - this.surface.setElementAttributes(b, { - fill: a && d ? a.generateGradient(this, d) : this.fillStyle, - "fill-opacity": this.fillOpacity * this.globalAlpha - }) - } - }, - stroke: function() { - if (!this.path) { - return - } - if (this.strokeStyle) { - var c, b = this.strokeGradient, - d = this.bbox, - a = this.path.element; - if (!a || !this.path.svgString) { - c = this.path.toString(); - if (!c) { - return - } - a = this.path.element = this.getElement("path"); - this.surface.setElementAttributes(a, { - fill: "none", - d: c, - transform: this.matrix.toSvg() - }) - } - this.surface.setElementAttributes(a, { - stroke: b && d ? b.generateGradient(this, d) : this.strokeStyle, - "stroke-linecap": this.lineCap, - "stroke-linejoin": this.lineJoin, - "stroke-width": this.lineWidth, - "stroke-opacity": this.strokeOpacity * this.globalAlpha, - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }); - if (this.lineDash.length) { - this.surface.setElementAttributes(a, { - "stroke-dasharray": this.lineDash.join(","), - "stroke-dashoffset": this.lineDashOffset - }) - } - } - }, - fillStroke: function(a, e) { - var b = this, - d = b.fillStyle, - g = b.strokeStyle, - c = b.fillOpacity, - f = b.strokeOpacity; - if (e === undefined) { - e = a.transformFillStroke - } - if (!e) { - a.inverseMatrix.toContext(b) - } - if (d && c !== 0) { - b.fill() - } - if (g && f !== 0) { - b.stroke() - } - }, - appendPath: function(a) { - this.path = a.clone() - }, - setLineDash: function(a) { - this.lineDash = a - }, - getLineDash: function() { - return this.lineDash - }, - createLinearGradient: function(d, g, b, e) { - var f = this, - c = f.surface.getNextDef("linearGradient"), - a = f.group.dom.gradients || (f.group.dom.gradients = {}), - h; - f.surface.setElementAttributes(c, { - x1: d, - y1: g, - x2: b, - y2: e, - gradientUnits: "userSpaceOnUse" - }); - h = new Ext.draw.engine.SvgContext.Gradient(f, f.surface, c); - a[c.dom.id] = h; - return h - }, - createRadialGradient: function(b, j, d, a, i, c) { - var g = this, - e = g.surface.getNextDef("radialGradient"), - f = g.group.dom.gradients || (g.group.dom.gradients = {}), - h; - g.surface.setElementAttributes(e, { - fx: b, - fy: j, - cx: a, - cy: i, - r: c, - gradientUnits: "userSpaceOnUse" - }); - h = new Ext.draw.engine.SvgContext.Gradient(g, g.surface, e, d / c); - f[e.dom.id] = h; - return h - } -}); -Ext.define("Ext.draw.engine.SvgContext.Gradient", { - statics: { - map: {} - }, - constructor: function(c, a, d, b) { - var f = this.statics().map, - e; - e = f[d.dom.id]; - if (e) { - e.element = null - } - f[d.dom.id] = this; - this.ctx = c; - this.surface = a; - this.element = d; - this.position = 0; - this.compression = b || 0 - }, - addColorStop: function(d, b) { - var c = this.surface.getSvgElement(this.element, "stop", this.position++), - a = this.compression; - this.surface.setElementAttributes(c, { - offset: (((1 - a) * d + a) * 100).toFixed(2) + "%", - "stop-color": b, - "stop-opacity": Ext.draw.Color.fly(b).a.toFixed(15) - }) - }, - toString: function() { - var a = this.element.dom.childNodes; - while (a.length > this.position) { - Ext.fly(a[a.length - 1]).destroy() - } - return "url(#" + this.element.getId() + ")" - }, - destroy: function() { - var b = this.statics().map, - a = this.element; - if (a && a.dom) { - delete b[a.dom.id]; - a.destroy() - } - this.callParent() - } -}); -Ext.define("Ext.draw.engine.Svg", { - extend: "Ext.draw.Surface", - requires: ["Ext.draw.engine.SvgContext"], - statics: { - BBoxTextCache: {} - }, - config: { - highPrecision: false - }, - getElementConfig: function() { - return { - reference: "element", - style: { - position: "absolute" - }, - children: [{ - reference: "innerElement", - style: { - width: "100%", - height: "100%", - position: "relative" - }, - children: [{ - tag: "svg", - reference: "svgElement", - namespace: "http://www.w3.org/2000/svg", - width: "100%", - height: "100%", - version: 1.1 - }] - }] - } - }, - constructor: function(a) { - var b = this; - b.callParent([a]); - b.mainGroup = b.createSvgNode("g"); - b.defElement = b.createSvgNode("defs"); - b.svgElement.appendChild(b.mainGroup); - b.svgElement.appendChild(b.defElement); - b.ctx = new Ext.draw.engine.SvgContext(b) - }, - createSvgNode: function(a) { - var b = document.createElementNS("http://www.w3.org/2000/svg", a); - return Ext.get(b) - }, - getSvgElement: function(d, b, a) { - var c; - if (d.dom.childNodes.length > a) { - c = d.dom.childNodes[a]; - if (c.tagName === b) { - return Ext.get(c) - } else { - Ext.destroy(c) - } - } - c = Ext.get(this.createSvgNode(b)); - if (a === 0) { - d.insertFirst(c) - } else { - c.insertAfter(Ext.fly(d.dom.childNodes[a - 1])) - } - c.cache = {}; - return c - }, - setElementAttributes: function(d, b) { - var f = d.dom, - a = d.cache, - c, e; - for (c in b) { - e = b[c]; - if (a[c] !== e) { - a[c] = e; - f.setAttribute(c, e) - } - } - }, - getNextDef: function(a) { - return this.getSvgElement(this.defElement, a, this.defPosition++) - }, - clearTransform: function() { - var a = this; - a.mainGroup.set({ - transform: a.matrix.toSvg() - }) - }, - clear: function() { - this.ctx.clear(); - this.defPosition = 0 - }, - renderSprite: function(b) { - var d = this, - c = d.getRect(), - a = d.ctx; - if (b.attr.hidden || b.attr.globalAlpha === 0) { - a.save(); - a.restore(); - return - } - b.element = a.save(); - b.preRender(this); - b.useAttributes(a, c); - if (false === b.render(this, a, [0, 0, c[2], c[3]])) { - return false - } - b.setDirty(false); - a.restore() - }, - flatten: function(e, b) { - var c = '', - f = Ext.getClassName(this), - a, g, d; - c += ''; - for (d = 0; d < b.length; d++) { - a = b[d]; - if (Ext.getClassName(a) !== f) { - continue - } - g = a.getRect(); - c += ''; - c += this.serializeNode(a.svgElement.dom); - c += "" - } - c += ""; - return { - data: "data:image/svg+xml;utf8," + encodeURIComponent(c), - type: "svg" - } - }, - serializeNode: function(d) { - var b = "", - c, f, a, e; - if (d.nodeType === document.TEXT_NODE) { - return d.nodeValue - } - b += "<" + d.nodeName; - if (d.attributes.length) { - for (c = 0, f = d.attributes.length; c < f; c++) { - a = d.attributes[c]; - b += " " + a.name + '="' + a.value + '"' - } - } - b += ">"; - if (d.childNodes && d.childNodes.length) { - for (c = 0, f = d.childNodes.length; c < f; c++) { - e = d.childNodes[c]; - b += this.serializeNode(e) - } - } - b += ""; - return b - }, - destroy: function() { - var a = this; - a.ctx.destroy(); - a.mainGroup.destroy(); - delete a.mainGroup; - delete a.ctx; - a.callParent() - }, - remove: function(a, b) { - if (a && a.element) { - if (this.ctx) { - this.ctx.removeElement(a.element) - } else { - a.element.destroy() - } - a.element = null - } - this.callParent(arguments) - } -}); -Ext.draw || (Ext.draw = {}); -Ext.draw.engine || (Ext.draw.engine = {}); -Ext.draw.engine.excanvas = true; -if (!document.createElement("canvas").getContext) { - (function() { - var ab = Math; - var n = ab.round; - var l = ab.sin; - var A = ab.cos; - var H = ab.abs; - var N = ab.sqrt; - var d = 10; - var f = d / 2; - var z = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1]; - - function y() { - return this.context_ || (this.context_ = new D(this)) - } - var t = Array.prototype.slice; - - function g(j, m, p) { - var i = t.call(arguments, 2); - return function() { - return j.apply(m, i.concat(t.call(arguments))) - } - } - - function af(i) { - return String(i).replace(/&/g, "&").replace(/"/g, """) - } - - function Y(m, j, i) { - Ext.onReady(function() { - if (!m.namespaces[j]) { - m.namespaces.add(j, i, "#default#VML") - } - }) - } - - function R(j) { - Y(j, "g_vml_", "urn:schemas-microsoft-com:vml"); - Y(j, "g_o_", "urn:schemas-microsoft-com:office:office"); - if (!j.styleSheets.ex_canvas_) { - var i = j.createStyleSheet(); - i.owningElement.id = "ex_canvas_"; - i.cssText = "canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}" - } - } - R(document); - var e = { - init: function(i) { - var j = i || document; - j.createElement("canvas"); - j.attachEvent("onreadystatechange", g(this.init_, this, j)) - }, - init_: function(p) { - var m = p.getElementsByTagName("canvas"); - for (var j = 0; j < m.length; j++) { - this.initElement(m[j]) - } - }, - initElement: function(j) { - if (!j.getContext) { - j.getContext = y; - R(j.ownerDocument); - j.innerHTML = ""; - j.attachEvent("onpropertychange", x); - j.attachEvent("onresize", W); - var i = j.attributes; - if (i.width && i.width.specified) { - j.style.width = i.width.nodeValue + "px" - } else { - j.width = j.clientWidth - } - if (i.height && i.height.specified) { - j.style.height = i.height.nodeValue + "px" - } else { - j.height = j.clientHeight - } - } - return j - } - }; - - function x(j) { - var i = j.srcElement; - switch (j.propertyName) { - case "width": - i.getContext().clearRect(); - i.style.width = i.attributes.width.nodeValue + "px"; - i.firstChild.style.width = i.clientWidth + "px"; - break; - case "height": - i.getContext().clearRect(); - i.style.height = i.attributes.height.nodeValue + "px"; - i.firstChild.style.height = i.clientHeight + "px"; - break - } - } - - function W(j) { - var i = j.srcElement; - if (i.firstChild) { - i.firstChild.style.width = i.clientWidth + "px"; - i.firstChild.style.height = i.clientHeight + "px" - } - } - e.init(); - var k = []; - for (var ae = 0; ae < 16; ae++) { - for (var ad = 0; ad < 16; ad++) { - k[ae * 16 + ad] = ae.toString(16) + ad.toString(16) - } - } - - function B() { - return [ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1] - ] - } - - function J(p, m) { - var j = B(); - for (var i = 0; i < 3; i++) { - for (var ah = 0; ah < 3; ah++) { - var Z = 0; - for (var ag = 0; ag < 3; ag++) { - Z += p[i][ag] * m[ag][ah] - } - j[i][ah] = Z - } - } - return j - } - - function v(j, i) { - i.fillStyle = j.fillStyle; - i.lineCap = j.lineCap; - i.lineJoin = j.lineJoin; - i.lineDash = j.lineDash; - i.lineWidth = j.lineWidth; - i.miterLimit = j.miterLimit; - i.shadowBlur = j.shadowBlur; - i.shadowColor = j.shadowColor; - i.shadowOffsetX = j.shadowOffsetX; - i.shadowOffsetY = j.shadowOffsetY; - i.strokeStyle = j.strokeStyle; - i.globalAlpha = j.globalAlpha; - i.font = j.font; - i.textAlign = j.textAlign; - i.textBaseline = j.textBaseline; - i.arcScaleX_ = j.arcScaleX_; - i.arcScaleY_ = j.arcScaleY_; - i.lineScale_ = j.lineScale_ - } - var b = { - aliceblue: "#F0F8FF", - antiquewhite: "#FAEBD7", - aquamarine: "#7FFFD4", - azure: "#F0FFFF", - beige: "#F5F5DC", - bisque: "#FFE4C4", - black: "#000000", - blanchedalmond: "#FFEBCD", - blueviolet: "#8A2BE2", - brown: "#A52A2A", - burlywood: "#DEB887", - cadetblue: "#5F9EA0", - chartreuse: "#7FFF00", - chocolate: "#D2691E", - coral: "#FF7F50", - cornflowerblue: "#6495ED", - cornsilk: "#FFF8DC", - crimson: "#DC143C", - cyan: "#00FFFF", - darkblue: "#00008B", - darkcyan: "#008B8B", - darkgoldenrod: "#B8860B", - darkgray: "#A9A9A9", - darkgreen: "#006400", - darkgrey: "#A9A9A9", - darkkhaki: "#BDB76B", - darkmagenta: "#8B008B", - darkolivegreen: "#556B2F", - darkorange: "#FF8C00", - darkorchid: "#9932CC", - darkred: "#8B0000", - darksalmon: "#E9967A", - darkseagreen: "#8FBC8F", - darkslateblue: "#483D8B", - darkslategray: "#2F4F4F", - darkslategrey: "#2F4F4F", - darkturquoise: "#00CED1", - darkviolet: "#9400D3", - deeppink: "#FF1493", - deepskyblue: "#00BFFF", - dimgray: "#696969", - dimgrey: "#696969", - dodgerblue: "#1E90FF", - firebrick: "#B22222", - floralwhite: "#FFFAF0", - forestgreen: "#228B22", - gainsboro: "#DCDCDC", - ghostwhite: "#F8F8FF", - gold: "#FFD700", - goldenrod: "#DAA520", - grey: "#808080", - greenyellow: "#ADFF2F", - honeydew: "#F0FFF0", - hotpink: "#FF69B4", - indianred: "#CD5C5C", - indigo: "#4B0082", - ivory: "#FFFFF0", - khaki: "#F0E68C", - lavender: "#E6E6FA", - lavenderblush: "#FFF0F5", - lawngreen: "#7CFC00", - lemonchiffon: "#FFFACD", - lightblue: "#ADD8E6", - lightcoral: "#F08080", - lightcyan: "#E0FFFF", - lightgoldenrodyellow: "#FAFAD2", - lightgreen: "#90EE90", - lightgrey: "#D3D3D3", - lightpink: "#FFB6C1", - lightsalmon: "#FFA07A", - lightseagreen: "#20B2AA", - lightskyblue: "#87CEFA", - lightslategray: "#778899", - lightslategrey: "#778899", - lightsteelblue: "#B0C4DE", - lightyellow: "#FFFFE0", - limegreen: "#32CD32", - linen: "#FAF0E6", - magenta: "#FF00FF", - mediumaquamarine: "#66CDAA", - mediumblue: "#0000CD", - mediumorchid: "#BA55D3", - mediumpurple: "#9370DB", - mediumseagreen: "#3CB371", - mediumslateblue: "#7B68EE", - mediumspringgreen: "#00FA9A", - mediumturquoise: "#48D1CC", - mediumvioletred: "#C71585", - midnightblue: "#191970", - mintcream: "#F5FFFA", - mistyrose: "#FFE4E1", - moccasin: "#FFE4B5", - navajowhite: "#FFDEAD", - oldlace: "#FDF5E6", - olivedrab: "#6B8E23", - orange: "#FFA500", - orangered: "#FF4500", - orchid: "#DA70D6", - palegoldenrod: "#EEE8AA", - palegreen: "#98FB98", - paleturquoise: "#AFEEEE", - palevioletred: "#DB7093", - papayawhip: "#FFEFD5", - peachpuff: "#FFDAB9", - peru: "#CD853F", - pink: "#FFC0CB", - plum: "#DDA0DD", - powderblue: "#B0E0E6", - rosybrown: "#BC8F8F", - royalblue: "#4169E1", - saddlebrown: "#8B4513", - salmon: "#FA8072", - sandybrown: "#F4A460", - seagreen: "#2E8B57", - seashell: "#FFF5EE", - sienna: "#A0522D", - skyblue: "#87CEEB", - slateblue: "#6A5ACD", - slategray: "#708090", - slategrey: "#708090", - snow: "#FFFAFA", - springgreen: "#00FF7F", - steelblue: "#4682B4", - tan: "#D2B48C", - thistle: "#D8BFD8", - tomato: "#FF6347", - turquoise: "#40E0D0", - violet: "#EE82EE", - wheat: "#F5DEB3", - whitesmoke: "#F5F5F5", - yellowgreen: "#9ACD32" - }; - - function M(j) { - var p = j.indexOf("(", 3); - var i = j.indexOf(")", p + 1); - var m = j.substring(p + 1, i).split(","); - if (m.length != 4 || j.charAt(3) != "a") { - m[3] = 1 - } - return m - } - - function c(i) { - return parseFloat(i) / 100 - } - - function r(j, m, i) { - return Math.min(i, Math.max(m, j)) - } - - function I(ag) { - var i, ai, aj, ah, ak, Z; - ah = parseFloat(ag[0]) / 360 % 360; - if (ah < 0) { - ah++ - } - ak = r(c(ag[1]), 0, 1); - Z = r(c(ag[2]), 0, 1); - if (ak == 0) { - i = ai = aj = Z - } else { - var j = Z < 0.5 ? Z * (1 + ak) : Z + ak - Z * ak; - var m = 2 * Z - j; - i = a(m, j, ah + 1 / 3); - ai = a(m, j, ah); - aj = a(m, j, ah - 1 / 3) - } - return "#" + k[Math.floor(i * 255)] + k[Math.floor(ai * 255)] + k[Math.floor(aj * 255)] - } - - function a(j, i, m) { - if (m < 0) { - m++ - } - if (m > 1) { - m-- - } - if (6 * m < 1) { - return j + (i - j) * 6 * m - } else { - if (2 * m < 1) { - return i - } else { - if (3 * m < 2) { - return j + (i - j) * (2 / 3 - m) * 6 - } else { - return j - } - } - } - } - var C = {}; - - function F(j) { - if (j in C) { - return C[j] - } - var ag, Z = 1; - j = String(j); - if (j.charAt(0) == "#") { - ag = j - } else { - if (/^rgb/.test(j)) { - var p = M(j); - var ag = "#", - ah; - for (var m = 0; m < 3; m++) { - if (p[m].indexOf("%") != -1) { - ah = Math.floor(c(p[m]) * 255) - } else { - ah = +p[m] - } - ag += k[r(ah, 0, 255)] - } - Z = +p[3] - } else { - if (/^hsl/.test(j)) { - var p = M(j); - ag = I(p); - Z = p[3] - } else { - ag = b[j] || j - } - } - } - return C[j] = { - color: ag, - alpha: Z - } - } - var o = { - style: "normal", - variant: "normal", - weight: "normal", - size: 10, - family: "sans-serif" - }; - var L = {}; - - function E(i) { - if (L[i]) { - return L[i] - } - var p = document.createElement("div"); - var m = p.style; - try { - m.font = i - } catch (j) {} - return L[i] = { - style: m.fontStyle || o.style, - variant: m.fontVariant || o.variant, - weight: m.fontWeight || o.weight, - size: m.fontSize || o.size, - family: m.fontFamily || o.family - } - } - - function u(m, j) { - var i = {}; - for (var ah in m) { - i[ah] = m[ah] - } - var ag = parseFloat(j.currentStyle.fontSize), - Z = parseFloat(m.size); - if (typeof m.size == "number") { - i.size = m.size - } else { - if (m.size.indexOf("px") != -1) { - i.size = Z - } else { - if (m.size.indexOf("em") != -1) { - i.size = ag * Z - } else { - if (m.size.indexOf("%") != -1) { - i.size = (ag / 100) * Z - } else { - if (m.size.indexOf("pt") != -1) { - i.size = Z / 0.75 - } else { - i.size = ag - } - } - } - } - } - i.size *= 0.981; - return i - } - - function ac(i) { - return i.style + " " + i.variant + " " + i.weight + " " + i.size + "px " + i.family - } - var s = { - butt: "flat", - round: "round" - }; - - function S(i) { - return s[i] || "square" - } - - function D(i) { - this.m_ = B(); - this.mStack_ = []; - this.aStack_ = []; - this.currentPath_ = []; - this.strokeStyle = "#000"; - this.fillStyle = "#000"; - this.lineWidth = 1; - this.lineJoin = "miter"; - this.lineDash = []; - this.lineCap = "butt"; - this.miterLimit = d * 1; - this.globalAlpha = 1; - this.font = "10px sans-serif"; - this.textAlign = "left"; - this.textBaseline = "alphabetic"; - this.canvas = i; - var m = "width:" + i.clientWidth + "px;height:" + i.clientHeight + "px;overflow:hidden;position:absolute"; - var j = i.ownerDocument.createElement("div"); - j.style.cssText = m; - i.appendChild(j); - var p = j.cloneNode(false); - p.style.backgroundColor = "red"; - p.style.filter = "alpha(opacity=0)"; - i.appendChild(p); - this.element_ = j; - this.arcScaleX_ = 1; - this.arcScaleY_ = 1; - this.lineScale_ = 1 - } - var q = D.prototype; - q.clearRect = function() { - if (this.textMeasureEl_) { - this.textMeasureEl_.removeNode(true); - this.textMeasureEl_ = null - } - this.element_.innerHTML = "" - }; - q.beginPath = function() { - this.currentPath_ = [] - }; - q.moveTo = function(j, i) { - var m = V(this, j, i); - this.currentPath_.push({ - type: "moveTo", - x: m.x, - y: m.y - }); - this.currentX_ = m.x; - this.currentY_ = m.y - }; - q.lineTo = function(j, i) { - var m = V(this, j, i); - this.currentPath_.push({ - type: "lineTo", - x: m.x, - y: m.y - }); - this.currentX_ = m.x; - this.currentY_ = m.y - }; - q.bezierCurveTo = function(m, j, ak, aj, ai, ag) { - var i = V(this, ai, ag); - var ah = V(this, m, j); - var Z = V(this, ak, aj); - K(this, ah, Z, i) - }; - - function K(i, Z, m, j) { - i.currentPath_.push({ - type: "bezierCurveTo", - cp1x: Z.x, - cp1y: Z.y, - cp2x: m.x, - cp2y: m.y, - x: j.x, - y: j.y - }); - i.currentX_ = j.x; - i.currentY_ = j.y - } - q.quadraticCurveTo = function(ai, m, j, i) { - var ah = V(this, ai, m); - var ag = V(this, j, i); - var aj = { - x: this.currentX_ + 2 / 3 * (ah.x - this.currentX_), - y: this.currentY_ + 2 / 3 * (ah.y - this.currentY_) - }; - var Z = { - x: aj.x + (ag.x - this.currentX_) / 3, - y: aj.y + (ag.y - this.currentY_) / 3 - }; - K(this, aj, Z, ag) - }; - q.arc = function(al, aj, ak, ag, j, m) { - ak *= d; - var ap = m ? "at" : "wa"; - var am = al + A(ag) * ak - f; - var ao = aj + l(ag) * ak - f; - var i = al + A(j) * ak - f; - var an = aj + l(j) * ak - f; - if (am == i && !m) { - am += 0.125 - } - var Z = V(this, al, aj); - var ai = V(this, am, ao); - var ah = V(this, i, an); - this.currentPath_.push({ - type: ap, - x: Z.x, - y: Z.y, - radius: ak, - xStart: ai.x, - yStart: ai.y, - xEnd: ah.x, - yEnd: ah.y - }) - }; - q.rect = function(m, j, i, p) { - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath() - }; - q.strokeRect = function(m, j, i, p) { - var Z = this.currentPath_; - this.beginPath(); - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath(); - this.stroke(); - this.currentPath_ = Z - }; - q.fillRect = function(m, j, i, p) { - var Z = this.currentPath_; - this.beginPath(); - this.moveTo(m, j); - this.lineTo(m + i, j); - this.lineTo(m + i, j + p); - this.lineTo(m, j + p); - this.closePath(); - this.fill(); - this.currentPath_ = Z - }; - q.createLinearGradient = function(j, p, i, m) { - var Z = new U("gradient"); - Z.x0_ = j; - Z.y0_ = p; - Z.x1_ = i; - Z.y1_ = m; - return Z - }; - q.createRadialGradient = function(p, ag, m, j, Z, i) { - var ah = new U("gradientradial"); - ah.x0_ = p; - ah.y0_ = ag; - ah.r0_ = m; - ah.x1_ = j; - ah.y1_ = Z; - ah.r1_ = i; - return ah - }; - q.drawImage = function(an, j) { - var ah, Z, aj, ar, al, ak, ao, av; - var ai = an.runtimeStyle.width; - var am = an.runtimeStyle.height; - an.runtimeStyle.width = "auto"; - an.runtimeStyle.height = "auto"; - var ag = an.width; - var aq = an.height; - an.runtimeStyle.width = ai; - an.runtimeStyle.height = am; - if (arguments.length == 3) { - ah = arguments[1]; - Z = arguments[2]; - al = ak = 0; - ao = aj = ag; - av = ar = aq - } else { - if (arguments.length == 5) { - ah = arguments[1]; - Z = arguments[2]; - aj = arguments[3]; - ar = arguments[4]; - al = ak = 0; - ao = ag; - av = aq - } else { - if (arguments.length == 9) { - al = arguments[1]; - ak = arguments[2]; - ao = arguments[3]; - av = arguments[4]; - ah = arguments[5]; - Z = arguments[6]; - aj = arguments[7]; - ar = arguments[8] - } else { - throw Error("Invalid number of arguments") - } - } - } - var au = V(this, ah, Z); - var at = []; - var i = 10; - var p = 10; - var ap = this.m_; - at.push(" ', '", ""); - this.element_.insertAdjacentHTML("BeforeEnd", at.join("")) - }; - q.setLineDash = function(i) { - if (i.length === 1) { - i = i.slice(); - i[1] = i[0] - } - this.lineDash = i - }; - q.getLineDash = function() { - return this.lineDash - }; - q.stroke = function(ak) { - var ai = []; - var m = 10; - var al = 10; - ai.push(" aj.x) { - aj.x = j.x - } - if (Z.y == null || j.y < Z.y) { - Z.y = j.y - } - if (aj.y == null || j.y > aj.y) { - aj.y = j.y - } - } - } - ai.push(' ">'); - if (!ak) { - w(this, ai) - } else { - G(this, ai, Z, aj) - } - ai.push(""); - this.element_.insertAdjacentHTML("beforeEnd", ai.join("")) - }; - - function w(m, ag) { - var j = F(m.strokeStyle); - var p = j.color; - var Z = j.alpha * m.globalAlpha; - var i = m.lineScale_ * m.lineWidth; - if (i < 1) { - Z *= i - } - ag.push("') - } - - function G(aq, ai, aK, ar) { - var aj = aq.fillStyle; - var aB = aq.arcScaleX_; - var aA = aq.arcScaleY_; - var j = ar.x - aK.x; - var p = ar.y - aK.y; - if (aj instanceof U) { - var an = 0; - var aF = { - x: 0, - y: 0 - }; - var ax = 0; - var am = 1; - if (aj.type_ == "gradient") { - var al = aj.x0_ / aB; - var m = aj.y0_ / aA; - var ak = aj.x1_ / aB; - var aM = aj.y1_ / aA; - var aJ = V(aq, al, m); - var aI = V(aq, ak, aM); - var ag = aI.x - aJ.x; - var Z = aI.y - aJ.y; - an = Math.atan2(ag, Z) * 180 / Math.PI; - if (an < 0) { - an += 360 - } - if (an < 0.000001) { - an = 0 - } - } else { - var aJ = V(aq, aj.x0_, aj.y0_); - aF = { - x: (aJ.x - aK.x) / j, - y: (aJ.y - aK.y) / p - }; - j /= aB * d; - p /= aA * d; - var aD = ab.max(j, p); - ax = 2 * aj.r0_ / aD; - am = 2 * aj.r1_ / aD - ax - } - var av = aj.colors_; - av.sort(function(aN, i) { - return aN.offset - i.offset - }); - var ap = av.length; - var au = av[0].color; - var at = av[ap - 1].color; - var az = av[0].alpha * aq.globalAlpha; - var ay = av[ap - 1].alpha * aq.globalAlpha; - var aE = []; - for (var aH = 0; aH < ap; aH++) { - var ao = av[aH]; - aE.push(ao.offset * am + ax + " " + ao.color) - } - ai.push('') - } else { - if (aj instanceof T) { - if (j && p) { - var ah = -aK.x; - var aC = -aK.y; - ai.push("') - } - } else { - var aL = F(aq.fillStyle); - var aw = aL.color; - var aG = aL.alpha * aq.globalAlpha; - ai.push('') - } - } - } - q.fill = function() { - this.$stroke(true) - }; - q.closePath = function() { - this.currentPath_.push({ - type: "close" - }) - }; - - function V(j, Z, p) { - var i = j.m_; - return { - x: d * (Z * i[0][0] + p * i[1][0] + i[2][0]) - f, - y: d * (Z * i[0][1] + p * i[1][1] + i[2][1]) - f - } - } - q.save = function() { - var i = {}; - v(this, i); - this.aStack_.push(i); - this.mStack_.push(this.m_); - this.m_ = J(B(), this.m_) - }; - q.restore = function() { - if (this.aStack_.length) { - v(this.aStack_.pop(), this); - this.m_ = this.mStack_.pop() - } - }; - - function h(i) { - return isFinite(i[0][0]) && isFinite(i[0][1]) && isFinite(i[1][0]) && isFinite(i[1][1]) && isFinite(i[2][0]) && isFinite(i[2][1]) - } - - function aa(j, i, p) { - if (!h(i)) { - return - } - j.m_ = i; - if (p) { - var Z = i[0][0] * i[1][1] - i[0][1] * i[1][0]; - j.lineScale_ = N(H(Z)) - } - } - q.translate = function(m, j) { - var i = [ - [1, 0, 0], - [0, 1, 0], - [m, j, 1] - ]; - aa(this, J(i, this.m_), false) - }; - q.rotate = function(j) { - var p = A(j); - var m = l(j); - var i = [ - [p, m, 0], - [-m, p, 0], - [0, 0, 1] - ]; - aa(this, J(i, this.m_), false) - }; - q.scale = function(m, j) { - this.arcScaleX_ *= m; - this.arcScaleY_ *= j; - var i = [ - [m, 0, 0], - [0, j, 0], - [0, 0, 1] - ]; - aa(this, J(i, this.m_), true) - }; - q.transform = function(Z, p, ah, ag, j, i) { - var m = [ - [Z, p, 0], - [ah, ag, 0], - [j, i, 1] - ]; - aa(this, J(m, this.m_), true) - }; - q.setTransform = function(ag, Z, ai, ah, p, j) { - var i = [ - [ag, Z, 0], - [ai, ah, 0], - [p, j, 1] - ]; - aa(this, i, true) - }; - q.drawText_ = function(am, ak, aj, ap, ai) { - var ao = this.m_, - at = 1000, - j = 0, - ar = at, - ah = { - x: 0, - y: 0 - }, - ag = []; - var i = u(E(this.font), this.element_); - var p = ac(i); - var au = this.element_.currentStyle; - var Z = this.textAlign.toLowerCase(); - switch (Z) { - case "left": - case "center": - case "right": - break; - case "end": - Z = au.direction == "ltr" ? "right" : "left"; - break; - case "start": - Z = au.direction == "rtl" ? "right" : "left"; - break; - default: - Z = "left" - } - switch (this.textBaseline) { - case "hanging": - case "top": - ah.y = i.size / 1.75; - break; - case "middle": - break; - default: - case null: - case "alphabetic": - case "ideographic": - case "bottom": - ah.y = -i.size / 3; - break - } - switch (Z) { - case "right": - j = at; - ar = 0.05; - break; - case "center": - j = ar = at / 2; - break - } - var aq = V(this, ak + ah.x, aj + ah.y); - ag.push(''); - if (ai) { - w(this, ag) - } else { - G(this, ag, { - x: -j, - y: 0 - }, { - x: ar, - y: i.size - }) - } - var an = ao[0][0].toFixed(3) + "," + ao[1][0].toFixed(3) + "," + ao[0][1].toFixed(3) + "," + ao[1][1].toFixed(3) + ",0,0"; - var al = n(aq.x / d) + "," + n(aq.y / d); - ag.push('', '', ''); - this.element_.insertAdjacentHTML("beforeEnd", ag.join("")) - }; - q.fillText = function(m, i, p, j) { - this.drawText_(m, i, p, j, false) - }; - q.strokeText = function(m, i, p, j) { - this.drawText_(m, i, p, j, true) - }; - q.measureText = function(m) { - if (!this.textMeasureEl_) { - var i = ''; - this.element_.insertAdjacentHTML("beforeEnd", i); - this.textMeasureEl_ = this.element_.lastChild - } - var j = this.element_.ownerDocument; - this.textMeasureEl_.innerHTML = ""; - this.textMeasureEl_.style.font = this.font; - this.textMeasureEl_.appendChild(j.createTextNode(m)); - return { - width: this.textMeasureEl_.offsetWidth - } - }; - q.clip = function() {}; - q.arcTo = function() {}; - q.createPattern = function(j, i) { - return new T(j, i) - }; - - function U(i) { - this.type_ = i; - this.x0_ = 0; - this.y0_ = 0; - this.r0_ = 0; - this.x1_ = 0; - this.y1_ = 0; - this.r1_ = 0; - this.colors_ = [] - } - U.prototype.addColorStop = function(j, i) { - i = F(i); - this.colors_.push({ - offset: j, - color: i.color, - alpha: i.alpha - }) - }; - - function T(j, i) { - Q(j); - switch (i) { - case "repeat": - case null: - case "": - this.repetition_ = "repeat"; - break; - case "repeat-x": - case "repeat-y": - case "no-repeat": - this.repetition_ = i; - break; - default: - O("SYNTAX_ERR") - } - this.src_ = j.src; - this.width_ = j.width; - this.height_ = j.height - } - - function O(i) { - throw new P(i) - } - - function Q(i) { - if (!i || i.nodeType != 1 || i.tagName != "IMG") { - O("TYPE_MISMATCH_ERR") - } - if (i.readyState != "complete") { - O("INVALID_STATE_ERR") - } - } - - function P(i) { - this.code = this[i]; - this.message = i + ": DOM Exception " + this.code - } - var X = P.prototype = new Error(); - X.INDEX_SIZE_ERR = 1; - X.DOMSTRING_SIZE_ERR = 2; - X.HIERARCHY_REQUEST_ERR = 3; - X.WRONG_DOCUMENT_ERR = 4; - X.INVALID_CHARACTER_ERR = 5; - X.NO_DATA_ALLOWED_ERR = 6; - X.NO_MODIFICATION_ALLOWED_ERR = 7; - X.NOT_FOUND_ERR = 8; - X.NOT_SUPPORTED_ERR = 9; - X.INUSE_ATTRIBUTE_ERR = 10; - X.INVALID_STATE_ERR = 11; - X.SYNTAX_ERR = 12; - X.INVALID_MODIFICATION_ERR = 13; - X.NAMESPACE_ERR = 14; - X.INVALID_ACCESS_ERR = 15; - X.VALIDATION_ERR = 16; - X.TYPE_MISMATCH_ERR = 17; - G_vmlCanvasManager = e; - CanvasRenderingContext2D = D; - CanvasGradient = U; - CanvasPattern = T; - DOMException = P - })() -} -Ext.define("Ext.draw.engine.Canvas", { - extend: "Ext.draw.Surface", - requires: ["Ext.draw.engine.excanvas", "Ext.draw.Animator", "Ext.draw.Color"], - config: { - highPrecision: false - }, - statics: { - contextOverrides: { - setGradientBBox: function(a) { - this.bbox = a - }, - fill: function() { - var c = this.fillStyle, - a = this.fillGradient, - b = this.fillOpacity, - d = this.globalAlpha, - e = this.bbox; - if (c !== Ext.draw.Color.RGBA_NONE && b !== 0) { - if (a && e) { - this.fillStyle = a.generateGradient(this, e) - } - if (b !== 1) { - this.globalAlpha = d * b - } - this.$fill(); - if (b !== 1) { - this.globalAlpha = d - } - if (a && e) { - this.fillStyle = c - } - } - }, - stroke: function() { - var e = this.strokeStyle, - c = this.strokeGradient, - a = this.strokeOpacity, - b = this.globalAlpha, - d = this.bbox; - if (e !== Ext.draw.Color.RGBA_NONE && a !== 0) { - if (c && d) { - this.strokeStyle = c.generateGradient(this, d) - } - if (a !== 1) { - this.globalAlpha = b * a - } - this.$stroke(); - if (a !== 1) { - this.globalAlpha = b - } - if (c && d) { - this.strokeStyle = e - } - } - }, - fillStroke: function(d, e) { - var j = this, - i = this.fillStyle, - h = this.fillOpacity, - f = this.strokeStyle, - c = this.strokeOpacity, - b = j.shadowColor, - a = j.shadowBlur, - g = Ext.draw.Color.RGBA_NONE; - if (e === undefined) { - e = d.transformFillStroke - } - if (!e) { - d.inverseMatrix.toContext(j) - } - if (i !== g && h !== 0) { - j.fill(); - j.shadowColor = g; - j.shadowBlur = 0 - } - if (f !== g && c !== 0) { - j.stroke() - } - j.shadowColor = b; - j.shadowBlur = a - }, - setLineDash: function(a) { - if (this.$setLineDash) { - this.$setLineDash(a) - } - }, - getLineDash: function() { - if (this.$getLineDash) { - return this.$getLineDash() - } - }, - ellipse: function(g, e, c, a, j, b, f, d) { - var i = Math.cos(j), - h = Math.sin(j); - this.transform(i * c, h * c, -h * a, i * a, g, e); - this.arc(0, 0, 1, b, f, d); - this.transform(i / c, -h / a, h / c, i / a, -(i * g + h * e) / c, (h * g - i * e) / a) - }, - appendPath: function(f) { - var e = this, - c = 0, - b = 0, - a = f.commands, - g = f.params, - d = a.length; - e.beginPath(); - for (; c < d; c++) { - switch (a[c]) { - case "M": - e.moveTo(g[b], g[b + 1]); - b += 2; - break; - case "L": - e.lineTo(g[b], g[b + 1]); - b += 2; - break; - case "C": - e.bezierCurveTo(g[b], g[b + 1], g[b + 2], g[b + 3], g[b + 4], g[b + 5]); - b += 6; - break; - case "Z": - e.closePath(); - break - } - } - }, - save: function() { - var c = this.toSave, - d = c.length, - e = d && {}, - b = 0, - a; - for (; b < d; b++) { - a = c[b]; - if (a in this) { - e[a] = this[a] - } - } - this.state.push(e); - this.$save() - }, - restore: function() { - var b = this.state.pop(), - a; - if (b) { - for (a in b) { - this[a] = b[a] - } - } - this.$restore() - } - } - }, - splitThreshold: 3000, - toSave: ["fillGradient", "strokeGradient"], - element: { - reference: "element", - style: { - position: "absolute" - }, - children: [{ - reference: "innerElement", - style: { - width: "100%", - height: "100%", - position: "relative" - } - }] - }, - createCanvas: function() { - var c = Ext.Element.create({ - tag: "canvas", - cls: Ext.baseCSSPrefix + "surface-canvas" - }); - window.G_vmlCanvasManager && G_vmlCanvasManager.initElement(c.dom); - var d = Ext.draw.engine.Canvas.contextOverrides, - a = c.dom.getContext("2d"), - b; - if (a.ellipse) { - delete d.ellipse - } - a.state = []; - a.toSave = this.toSave; - for (b in d) { - a["$" + b] = a[b] - } - Ext.apply(a, d); - if (this.getHighPrecision()) { - this.enablePrecisionCompensation(a) - } else { - this.disablePrecisionCompensation(a) - } - this.innerElement.appendChild(c); - this.canvases.push(c); - this.contexts.push(a) - }, - updateHighPrecision: function(d) { - var e = this.contexts, - c = e.length, - b, a; - for (b = 0; b < c; b++) { - a = e[b]; - if (d) { - this.enablePrecisionCompensation(a) - } else { - this.disablePrecisionCompensation(a) - } - } - }, - precisionNames: ["rect", "fillRect", "strokeRect", "clearRect", "moveTo", "lineTo", "arc", "arcTo", "save", "restore", "updatePrecisionCompensate", "setTransform", "transform", "scale", "translate", "rotate", "quadraticCurveTo", "bezierCurveTo", "createLinearGradient", "createRadialGradient", "fillText", "strokeText", "drawImage"], - disablePrecisionCompensation: function(b) { - var a = Ext.draw.engine.Canvas.contextOverrides, - f = this.precisionNames, - e = f.length, - d, c; - for (d = 0; d < e; d++) { - c = f[d]; - if (!(c in a)) { - delete b[c] - } - } - this.setDirty(true) - }, - enablePrecisionCompensation: function(j) { - var c = this, - a = 1, - g = 1, - l = 0, - k = 0, - i = new Ext.draw.Matrix(), - b = [], - e = {}, - d = Ext.draw.engine.Canvas.contextOverrides, - h = j.constructor.prototype; - var f = { - toSave: c.toSave, - rect: function(m, p, n, o) { - return h.rect.call(this, m * a + l, p * g + k, n * a, o * g) - }, - fillRect: function(m, p, n, o) { - this.updatePrecisionCompensateRect(); - h.fillRect.call(this, m * a + l, p * g + k, n * a, o * g); - this.updatePrecisionCompensate() - }, - strokeRect: function(m, p, n, o) { - this.updatePrecisionCompensateRect(); - h.strokeRect.call(this, m * a + l, p * g + k, n * a, o * g); - this.updatePrecisionCompensate() - }, - clearRect: function(m, p, n, o) { - return h.clearRect.call(this, m * a + l, p * g + k, n * a, o * g) - }, - moveTo: function(m, n) { - return h.moveTo.call(this, m * a + l, n * g + k) - }, - lineTo: function(m, n) { - return h.lineTo.call(this, m * a + l, n * g + k) - }, - arc: function(n, r, m, p, o, q) { - this.updatePrecisionCompensateRect(); - h.arc.call(this, n * a + l, r * a + k, m * a, p, o, q); - this.updatePrecisionCompensate() - }, - arcTo: function(o, q, n, p, m) { - this.updatePrecisionCompensateRect(); - h.arcTo.call(this, o * a + l, q * g + k, n * a + l, p * g + k, m * a); - this.updatePrecisionCompensate() - }, - save: function() { - b.push(i); - i = i.clone(); - d.save.call(this); - h.save.call(this) - }, - restore: function() { - i = b.pop(); - d.restore.call(this); - h.restore.call(this); - this.updatePrecisionCompensate() - }, - updatePrecisionCompensate: function() { - i.precisionCompensate(c.devicePixelRatio, e); - a = e.xx; - g = e.yy; - l = e.dx; - k = e.dy; - h.setTransform.call(this, c.devicePixelRatio, e.b, e.c, e.d, 0, 0) - }, - updatePrecisionCompensateRect: function() { - i.precisionCompensateRect(c.devicePixelRatio, e); - a = e.xx; - g = e.yy; - l = e.dx; - k = e.dy; - h.setTransform.call(this, c.devicePixelRatio, e.b, e.c, e.d, 0, 0) - }, - setTransform: function(q, o, n, m, r, p) { - i.set(q, o, n, m, r, p); - this.updatePrecisionCompensate() - }, - transform: function(q, o, n, m, r, p) { - i.append(q, o, n, m, r, p); - this.updatePrecisionCompensate() - }, - scale: function(n, m) { - this.transform(n, 0, 0, m, 0, 0) - }, - translate: function(n, m) { - this.transform(1, 0, 0, 1, n, m) - }, - rotate: function(o) { - var n = Math.cos(o), - m = Math.sin(o); - this.transform(n, m, -m, n, 0, 0) - }, - quadraticCurveTo: function(n, p, m, o) { - h.quadraticCurveTo.call(this, n * a + l, p * g + k, m * a + l, o * g + k) - }, - bezierCurveTo: function(r, p, o, n, m, q) { - h.bezierCurveTo.call(this, r * a + l, p * g + k, o * a + l, n * g + k, m * a + l, q * g + k) - }, - createLinearGradient: function(n, p, m, o) { - this.updatePrecisionCompensateRect(); - var q = h.createLinearGradient.call(this, n * a + l, p * g + k, m * a + l, o * g + k); - this.updatePrecisionCompensate(); - return q - }, - createRadialGradient: function(p, r, o, n, q, m) { - this.updatePrecisionCompensateRect(); - var s = h.createLinearGradient.call(this, p * a + l, r * a + k, o * a, n * a + l, q * a + k, m * a); - this.updatePrecisionCompensate(); - return s - }, - fillText: function(o, m, p, n) { - h.setTransform.apply(this, i.elements); - if (typeof n === "undefined") { - h.fillText.call(this, o, m, p) - } else { - h.fillText.call(this, o, m, p, n) - } - this.updatePrecisionCompensate() - }, - strokeText: function(o, m, p, n) { - h.setTransform.apply(this, i.elements); - if (typeof n === "undefined") { - h.strokeText.call(this, o, m, p) - } else { - h.strokeText.call(this, o, m, p, n) - } - this.updatePrecisionCompensate() - }, - fill: function() { - var m = this.fillGradient, - n = this.bbox; - this.updatePrecisionCompensateRect(); - if (m && n) { - this.fillStyle = m.generateGradient(this, n) - } - h.fill.call(this); - this.updatePrecisionCompensate() - }, - stroke: function() { - var m = this.strokeGradient, - n = this.bbox; - this.updatePrecisionCompensateRect(); - if (m && n) { - this.strokeStyle = m.generateGradient(this, n) - } - h.stroke.call(this); - this.updatePrecisionCompensate() - }, - drawImage: function(u, s, r, q, p, o, n, m, t) { - switch (arguments.length) { - case 3: - return h.drawImage.call(this, u, s * a + l, r * g + k); - case 5: - return h.drawImage.call(this, u, s * a + l, r * g + k, q * a, p * g); - case 9: - return h.drawImage.call(this, u, s, r, q, p, o * a + l, n * g * k, m * a, t * g) - } - } - }; - Ext.apply(j, f); - this.setDirty(true) - }, - updateRect: function(a) { - this.callParent([a]); - var C = this, - p = Math.floor(a[0]), - e = Math.floor(a[1]), - g = Math.ceil(a[0] + a[2]), - B = Math.ceil(a[1] + a[3]), - u = C.devicePixelRatio, - D = C.canvases, - d = g - p, - y = B - e, - n = Math.round(C.splitThreshold / u), - c = C.xSplits = Math.ceil(d / n), - f = C.ySplits = Math.ceil(y / n), - v, s, q, A, z, x, o, m; - for (s = 0, z = 0; s < f; s++, z += n) { - for (v = 0, A = 0; v < c; v++, A += n) { - q = s * c + v; - if (q >= D.length) { - C.createCanvas() - } - x = D[q].dom; - x.style.left = A + "px"; - x.style.top = z + "px"; - m = Math.min(n, y - z); - if (m * u !== x.height) { - x.height = m * u; - x.style.height = m + "px" - } - o = Math.min(n, d - A); - if (o * u !== x.width) { - x.width = o * u; - x.style.width = o + "px" - } - C.applyDefaults(C.contexts[q]) - } - } - for (q += 1; q < D.length; q++) { - D[q].destroy() - } - C.activeCanvases = c * f; - D.length = C.activeCanvases; - C.clear() - }, - clearTransform: function() { - var f = this, - a = f.xSplits, - g = f.ySplits, - d = f.contexts, - h = f.splitThreshold, - l = f.devicePixelRatio, - e, c, b, m; - for (e = 0; e < a; e++) { - for (c = 0; c < g; c++) { - b = c * a + e; - m = d[b]; - m.translate(-h * e, -h * c); - m.scale(l, l); - f.matrix.toContext(m) - } - } - }, - renderSprite: function(q) { - var C = this, - b = C.getRect(), - e = C.matrix, - g = q.getParent(), - v = Ext.draw.Matrix.fly([1, 0, 0, 1, 0, 0]), - p = C.splitThreshold / C.devicePixelRatio, - c = C.xSplits, - m = C.ySplits, - A, z, s, a, r, o, d = 0, - B, n = 0, - f, l = b[2], - y = b[3], - x, u, t; - while (g && (g !== C)) { - v.prependMatrix(g.matrix || g.attr && g.attr.matrix); - g = g.getParent() - } - v.prependMatrix(e); - a = q.getBBox(); - if (a) { - a = v.transformBBox(a) - } - q.preRender(C); - if (q.attr.hidden || q.attr.globalAlpha === 0) { - q.setDirty(false); - return - } - for (u = 0, z = 0; u < m; u++, z += p) { - for (x = 0, A = 0; x < c; x++, A += p) { - t = u * c + x; - s = C.contexts[t]; - r = Math.min(p, l - A); - o = Math.min(p, y - z); - d = A; - B = d + r; - n = z; - f = n + o; - if (a) { - if (a.x > B || a.x + a.width < d || a.y > f || a.y + a.height < n) { - continue - } - } - s.save(); - q.useAttributes(s, b); - if (false === q.render(C, s, [d, n, r, o], b)) { - return false - } - s.restore() - } - } - q.setDirty(false) - }, - flatten: function(n, a) { - var k = document.createElement("canvas"), - f = Ext.getClassName(this), - g = this.devicePixelRatio, - l = k.getContext("2d"), - b, c, h, e, d, m; - k.width = Math.ceil(n.width * g); - k.height = Math.ceil(n.height * g); - for (e = 0; e < a.length; e++) { - b = a[e]; - if (Ext.getClassName(b) !== f) { - continue - } - h = b.getRect(); - for (d = 0; d < b.canvases.length; d++) { - c = b.canvases[d]; - m = c.getOffsetsTo(c.getParent()); - l.drawImage(c.dom, (h[0] + m[0]) * g, (h[1] + m[1]) * g) - } - } - return { - data: k.toDataURL(), - type: "png" - } - }, - applyDefaults: function(a) { - var b = Ext.draw.Color.RGBA_NONE; - a.strokeStyle = b; - a.fillStyle = b; - a.textAlign = "start"; - a.textBaseline = "alphabetic"; - a.miterLimit = 1 - }, - clear: function() { - var d = this, - e = d.activeCanvases, - c, b, a; - for (c = 0; c < e; c++) { - b = d.canvases[c].dom; - a = d.contexts[c]; - a.setTransform(1, 0, 0, 1, 0, 0); - a.clearRect(0, 0, b.width, b.height) - } - d.setDirty(true) - }, - destroy: function() { - var c = this, - a, b = c.canvases.length; - for (a = 0; a < b; a++) { - c.contexts[a] = null; - c.canvases[a].destroy(); - c.canvases[a] = null - } - delete c.contexts; - delete c.canvases; - c.callParent() - }, - privates: { - initElement: function() { - var a = this; - a.callParent(); - a.canvases = []; - a.contexts = []; - a.activeCanvases = (a.xSplits = 0) * (a.ySplits = 0) - } - } -}, function() { - var c = this, - b = c.prototype, - a = 10000000000; - if (Ext.os.is.Android4 && Ext.browser.is.Chrome) { - a = 3000 - } else { - if (Ext.is.iOS) { - a = 2200 - } - } - b.splitThreshold = a -}); -Ext.define("Ext.draw.Container", { - extend: "Ext.draw.ContainerBase", - alternateClassName: "Ext.draw.Component", - xtype: "draw", - defaultType: "surface", - isDrawContainer: true, - requires: ["Ext.draw.Surface", "Ext.draw.engine.Svg", "Ext.draw.engine.Canvas", "Ext.draw.gradient.GradientDefinition"], - engine: "Ext.draw.engine.Canvas", - config: { - cls: Ext.baseCSSPrefix + "draw-container", - resizeHandler: null, - sprites: null, - gradients: [] - }, - defaultDownloadServerUrl: "http://svg.sencha.io", - supportedFormats: ["png", "pdf", "jpeg", "gif"], - supportedOptions: { - version: Ext.isNumber, - data: Ext.isString, - format: function(a) { - return Ext.Array.indexOf(this.supportedFormats, a) >= 0 - }, - filename: Ext.isString, - width: Ext.isNumber, - height: Ext.isNumber, - scale: Ext.isNumber, - pdf: Ext.isObject, - jpeg: Ext.isObject - }, - initAnimator: function() { - this.frameCallbackId = Ext.draw.Animator.addFrameCallback("renderFrame", this) - }, - applyGradients: function(b) { - var a = [], - c, f, d, e; - if (!Ext.isArray(b)) { - return a - } - for (c = 0, f = b.length; c < f; c++) { - d = b[c]; - if (!Ext.isObject(d)) { - continue - } - if (typeof d.type !== "string") { - d.type = "linear" - } - if (d.angle) { - d.degrees = d.angle; - delete d.angle - } - if (Ext.isObject(d.stops)) { - d.stops = (function(i) { - var g = [], - h; - for (e in i) { - h = i[e]; - h.offset = e / 100; - g.push(h) - } - return g - })(d.stops) - } - a.push(d) - } - Ext.draw.gradient.GradientDefinition.add(a); - return a - }, - applySprites: function(f) { - if (!f) { - return - } - f = Ext.Array.from(f); - var e = f.length, - b = [], - d, a, c; - for (d = 0; d < e; d++) { - c = f[d]; - a = c.surface; - if (!(a && a.isSurface)) { - if (Ext.isString(a)) { - a = this.getSurface(a) - } else { - a = this.getSurface("main") - } - } - c = a.add(c); - b.push(c) - } - return b - }, - onBodyResize: function() { - var b = this.element, - a; - if (!b) { - return - } - a = b.getSize(); - if (a.width && a.height) { - this.setBodySize(a) - } - }, - setBodySize: function(c) { - var d = this, - b = d.getResizeHandler() || d.defaultResizeHandler, - a; - d.fireEvent("bodyresize", d, c); - a = b.call(d, c); - if (a !== false) { - d.renderFrame() - } - }, - defaultResizeHandler: function(a) { - this.getItems().each(function(b) { - b.setRect([0, 0, a.width, a.height]) - }) - }, - getSurface: function(d) { - d = this.getId() + "-" + (d || "main"); - var c = this, - b = c.getItems(), - a = b.get(d); - if (!a) { - a = c.add({ - xclass: c.engine, - id: d - }); - c.onBodyResize() - } - return a - }, - renderFrame: function() { - var e = this, - a = e.getItems(), - b, d, c; - for (b = 0, d = a.length; b < d; b++) { - c = a.items[b]; - if (c.isSurface) { - c.renderFrame() - } - } - }, - getImage: function(k) { - var l = this.innerElement.getSize(), - a = Array.prototype.slice.call(this.items.items), - d, g, c = this.surfaceZIndexes, - f, e, b, h; - for (e = 1; e < a.length; e++) { - b = a[e]; - h = c[b.type]; - f = e - 1; - while (f >= 0 && c[a[f].type] > h) { - a[f + 1] = a[f]; - f-- - } - a[f + 1] = b - } - d = a[0].flatten(l, a); - if (k === "image") { - g = new Image(); - g.src = d.data; - d.data = g; - return d - } - if (k === "stream") { - d.data = d.data.replace(/^data:image\/[^;]+/, "data:application/octet-stream"); - return d - } - return d - }, - download: function(d) { - var e = this, - a = [], - b, c, f; - d = Ext.apply({ - version: 2, - data: e.getImage().data - }, d); - for (c in d) { - if (d.hasOwnProperty(c)) { - f = d[c]; - if (c in e.supportedOptions) { - if (e.supportedOptions[c].call(e, f)) { - a.push({ - tag: "input", - type: "hidden", - name: c, - value: Ext.String.htmlEncode(Ext.isObject(f) ? Ext.JSON.encode(f) : f) - }) - } - } - } - } - b = Ext.dom.Helper.markup({ - tag: "html", - children: [{ - tag: "head" - }, { - tag: "body", - children: [{ - tag: "form", - method: "POST", - action: d.url || e.defaultDownloadServerUrl, - children: a - }, { - tag: "script", - type: "text/javascript", - children: 'document.getElementsByTagName("form")[0].submit();' - }] - }] - }); - window.open("", "ImageDownload_" + Date.now()).document.write(b) - }, - destroy: function() { - var a = this.frameCallbackId; - if (a) { - Ext.draw.Animator.removeFrameCallback(a) - } - this.callParent() - } -}, function() { - if (location.search.match("svg")) { - Ext.draw.Container.prototype.engine = "Ext.draw.engine.Svg" - } else { - if ((Ext.os.is.BlackBerry && Ext.os.version.getMajor() === 10) || (Ext.browser.is.AndroidStock4 && (Ext.os.version.getMinor() === 1 || Ext.os.version.getMinor() === 2 || Ext.os.version.getMinor() === 3))) { - Ext.draw.Container.prototype.engine = "Ext.draw.engine.Svg" - } - } -}); -Ext.define("Ext.chart.theme.Base", { - mixins: { - factoryable: "Ext.mixin.Factoryable" - }, - requires: ["Ext.draw.Color"], - factoryConfig: { - type: "chart.theme" - }, - isTheme: true, - config: { - baseColor: null, - colors: undefined, - gradients: null, - chart: { - defaults: { - background: "#23272a" - } - }, - axis: { - defaults: { - label: { - x: 0, - y: 0, - textBaseline: "middle", - textAlign: "center", - fontSize: "default", - fontFamily: "default", - fontWeight: "default", - fillStyle: "black", - color: "white" - }, - title: { - fillStyle: "black", - fontSize: "default*1.23", - fontFamily: "default", - fontWeight: "default", - color: "white" - }, - style: { - strokeStyle: "black" - }, - grid: { - strokeStyle: "rgba(44, 47, 51, 1)" - } - }, - top: { - style: { - textPadding: 5 - } - }, - bottom: { - style: { - textPadding: 5 - } - } - }, - series: { - defaults: { - label: { - fillStyle: "black", - strokeStyle: "none", - fontFamily: "default", - fontWeight: "default", - fontSize: "default*1.077", - textBaseline: "middle", - textAlign: "center" - }, - labelOverflowPadding: 5 - } - }, - sprites: { - text: { - fontSize: "default", - fontWeight: "default", - fontFamily: "default", - fillStyle: "black", - color: "white" - } - }, - seriesThemes: undefined, - markerThemes: { - type: ["circle", "cross", "plus", "square", "triangle", "diamond"] - }, - useGradients: false, - background: null - }, - colorDefaults: ["#94ae0a", "#115fa6", "#a61120", "#ff8809", "#ffd13e", "#a61187", "#24ad9a", "#7c7474", "#a66111"], - constructor: function(a) { - this.initConfig(a); - this.resolveDefaults() - }, - defaultRegEx: /^default([+\-/\*]\d+(?:\.\d+)?)?$/, - defaultOperators: { - "*": function(b, a) { - return b * a - }, - "+": function(b, a) { - return b + a - }, - "-": function(b, a) { - return b - a - } - }, - resolveDefaults: function() { - var a = this; - Ext.onReady(function() { - var f = Ext.clone(a.getSprites()), - e = Ext.clone(a.getAxis()), - d = Ext.clone(a.getSeries()), - g, c, b; - if (!a.superclass.defaults) { - g = Ext.getBody().createChild({ - tag: "div", - cls: "x-component" - }); - a.superclass.defaults = { - fontFamily: g.getStyle("fontFamily"), - fontWeight: g.getStyle("fontWeight"), - fontSize: parseFloat(g.getStyle("fontSize")), - fontVariant: g.getStyle("fontVariant"), - fontStyle: g.getStyle("fontStyle") - }; - g.destroy() - } - a.replaceDefaults(f.text); - a.setSprites(f); - for (c in e) { - b = e[c]; - a.replaceDefaults(b.label); - a.replaceDefaults(b.title) - } - a.setAxis(e); - for (c in d) { - b = d[c]; - a.replaceDefaults(b.label) - } - a.setSeries(d) - }) - }, - replaceDefaults: function(h) { - var e = this, - g = e.superclass.defaults, - a = e.defaultRegEx, - d, f, c, b; - if (Ext.isObject(h)) { - for (d in g) { - c = a.exec(h[d]); - if (c) { - f = g[d]; - c = c[1]; - if (c) { - b = e.defaultOperators[c.charAt(0)]; - f = Math.round(b(f, parseFloat(c.substr(1)))) - } - h[d] = f - } - } - } - }, - applyBaseColor: function(c) { - var a, b; - if (c) { - a = c.isColor ? c : Ext.draw.Color.fromString(c); - b = a.getHSL()[2]; - if (b < 0.15) { - a = a.createLighter(0.3) - } else { - if (b < 0.3) { - a = a.createLighter(0.15) - } else { - if (b > 0.85) { - a = a.createDarker(0.3) - } else { - if (b > 0.7) { - a = a.createDarker(0.15) - } - } - } - } - this.setColors([a.createDarker(0.3).toString(), a.createDarker(0.15).toString(), a.toString(), a.createLighter(0.12).toString(), a.createLighter(0.24).toString(), a.createLighter(0.31).toString()]) - } - return c - }, - applyColors: function(a) { - return a || this.colorDefaults - }, - updateUseGradients: function(a) { - if (a) { - this.updateGradients({ - type: "linear", - degrees: 90 - }) - } - }, - updateBackground: function(a) { - if (a) { - var b = this.getChart(); - b.defaults.background = a; - this.setChart(b) - } - }, - updateGradients: function(a) { - var c = this.getColors(), - e = [], - h, b, d, f, g; - if (Ext.isObject(a)) { - for (f = 0, g = c && c.length || 0; f < g; f++) { - b = Ext.draw.Color.fromString(c[f]); - if (b) { - d = b.createLighter(0.15).toString(); - h = Ext.apply(Ext.Object.chain(a), { - stops: [{ - offset: 1, - color: b.toString() - }, { - offset: 0, - color: d.toString() - }] - }); - e.push(h) - } - } - this.setColors(e) - } - }, - applySeriesThemes: function(a) { - this.getBaseColor(); - this.getUseGradients(); - this.getGradients(); - var b = this.getColors(); - if (!a) { - a = { - fillStyle: Ext.Array.clone(b), - strokeStyle: Ext.Array.map(b, function(d) { - var c = Ext.draw.Color.fromString(d.stops ? d.stops[0].color : d); - return c.createDarker(0.15).toString() - }) - } - } - return a - } -}); -Ext.define("Ext.chart.theme.Default", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.default", "chart.theme.Base"] -}); -Ext.define("Ext.chart.Markers", { - extend: "Ext.draw.sprite.Instancing", - isMarkers: true, - defaultCategory: "default", - constructor: function() { - this.callParent(arguments); - this.categories = {}; - this.revisions = {} - }, - destroy: function() { - this.categories = null; - this.revisions = null; - this.callParent() - }, - getMarkerFor: function(b, a) { - if (b in this.categories) { - var c = this.categories[b]; - if (a in c) { - return this.get(c[a]) - } - } - }, - clear: function(a) { - a = a || this.defaultCategory; - if (!(a in this.revisions)) { - this.revisions[a] = 1 - } else { - this.revisions[a]++ - } - }, - putMarkerFor: function(e, b, c, h, f) { - e = e || this.defaultCategory; - var d = this, - g = d.categories[e] || (d.categories[e] = {}), - a; - if (c in g) { - d.setAttributesFor(g[c], b, h) - } else { - g[c] = d.getCount(); - d.createInstance(b, h) - } - a = d.get(g[c]); - if (a) { - a.category = e; - if (!f) { - a.revision = d.revisions[e] || (d.revisions[e] = 1) - } - } - }, - getMarkerBBoxFor: function(c, a, b) { - if (c in this.categories) { - var d = this.categories[c]; - if (a in d) { - return this.getBBoxFor(d[a], b) - } - } - }, - getBBox: function() { - return null - }, - render: function(a, l, b) { - var f = this, - k = f.revisions, - j = f.attr.matrix, - h = f.getTemplate(), - d = h.attr, - g, c, e; - j.toContext(l); - h.preRender(a, l, b); - h.useAttributes(l, b); - for (c = 0, e = f.instances.length; c < e; c++) { - g = f.get(c); - if (g.hidden || g.revision !== k[g.category]) { - continue - } - l.save(); - h.attr = g; - h.useAttributes(l, b); - h.render(a, l, b); - l.restore() - } - h.attr = d - } -}); -Ext.define("Ext.chart.label.Callout", { - extend: "Ext.draw.modifier.Modifier", - prepareAttributes: function(a) { - if (!a.hasOwnProperty("calloutOriginal")) { - a.calloutOriginal = Ext.Object.chain(a); - a.calloutOriginal.prototype = a - } - if (this._previous) { - this._previous.prepareAttributes(a.calloutOriginal) - } - }, - setAttrs: function(e, h) { - var d = e.callout, - i = e.calloutOriginal, - l = e.bbox.plain, - c = (l.width || 0) + e.labelOverflowPadding, - m = (l.height || 0) + e.labelOverflowPadding, - p, o; - if ("callout" in h) { - d = h.callout - } - if ("callout" in h || "calloutPlaceX" in h || "calloutPlaceY" in h || "x" in h || "y" in h) { - var n = "rotationRads" in h ? i.rotationRads = h.rotationRads : i.rotationRads, - g = "x" in h ? (i.x = h.x) : i.x, - f = "y" in h ? (i.y = h.y) : i.y, - b = "calloutPlaceX" in h ? h.calloutPlaceX : e.calloutPlaceX, - a = "calloutPlaceY" in h ? h.calloutPlaceY : e.calloutPlaceY, - k = "calloutVertical" in h ? h.calloutVertical : e.calloutVertical, - j; - n %= Math.PI * 2; - if (Math.cos(n) < 0) { - n = (n + Math.PI) % (Math.PI * 2) - } - if (n > Math.PI) { - n -= Math.PI * 2 - } - if (k) { - n = n * (1 - d) - Math.PI / 2 * d; - j = c; - c = m; - m = j - } else { - n = n * (1 - d) - } - h.rotationRads = n; - h.x = g * (1 - d) + b * d; - h.y = f * (1 - d) + a * d; - p = b - g; - o = a - f; - if (Math.abs(o * c) > Math.abs(p * m)) { - if (o > 0) { - h.calloutEndX = h.x - (m / 2) * (p / o) * d; - h.calloutEndY = h.y - (m / 2) * d - } else { - h.calloutEndX = h.x + (m / 2) * (p / o) * d; - h.calloutEndY = h.y + (m / 2) * d - } - } else { - if (p > 0) { - h.calloutEndX = h.x - c / 2; - h.calloutEndY = h.y - (c / 2) * (o / p) * d - } else { - h.calloutEndX = h.x + c / 2; - h.calloutEndY = h.y + (c / 2) * (o / p) * d - } - } - if (h.calloutStartX && h.calloutStartY) { - h.calloutHasLine = (p > 0 && h.calloutStartX < h.calloutEndX) || (p <= 0 && h.calloutStartX > h.calloutEndX) || (o > 0 && h.calloutStartY < h.calloutEndY) || (o <= 0 && h.calloutStartY > h.calloutEndY) - } else { - h.calloutHasLine = true - } - } - return h - }, - pushDown: function(a, b) { - b = this.callParent([a.calloutOriginal, b]); - return this.setAttrs(a, b) - }, - popUp: function(a, b) { - a = a.prototype; - b = this.setAttrs(a, b); - if (this._next) { - return this._next.popUp(a, b) - } else { - return Ext.apply(a, b) - } - } -}); -Ext.define("Ext.chart.label.Label", { - extend: "Ext.draw.sprite.Text", - requires: ["Ext.chart.label.Callout"], - inheritableStatics: { - def: { - processors: { - callout: "limited01", - calloutHasLine: "bool", - calloutPlaceX: "number", - calloutPlaceY: "number", - calloutStartX: "number", - calloutStartY: "number", - calloutEndX: "number", - calloutEndY: "number", - calloutColor: "color", - calloutWidth: "number", - calloutVertical: "bool", - labelOverflowPadding: "number", - display: "enums(none,under,over,rotate,insideStart,insideEnd,inside,outside)", - orientation: "enums(horizontal,vertical)", - renderer: "default" - }, - defaults: { - callout: 0, - calloutHasLine: true, - calloutPlaceX: 0, - calloutPlaceY: 0, - calloutStartX: 0, - calloutStartY: 0, - calloutEndX: 0, - calloutEndY: 0, - calloutWidth: 1, - calloutVertical: false, - calloutColor: "black", - labelOverflowPadding: 5, - display: "none", - orientation: "", - renderer: null - }, - triggers: { - callout: "transform", - calloutPlaceX: "transform", - calloutPlaceY: "transform", - labelOverflowPadding: "transform", - calloutRotation: "transform", - display: "hidden" - }, - updaters: { - hidden: function(a) { - a.hidden = a.display === "none" - } - } - } - }, - config: { - fx: { - customDurations: { - callout: 200 - } - }, - field: null, - calloutLine: true - }, - applyCalloutLine: function(a) { - if (a) { - return Ext.apply({}, a) - } - }, - prepareModifiers: function() { - this.callParent(arguments); - this.calloutModifier = new Ext.chart.label.Callout({ - sprite: this - }); - this.fx.setNext(this.calloutModifier); - this.calloutModifier.setNext(this.topModifier) - }, - render: function(b, c) { - var e = this, - a = e.attr, - d = a.calloutColor; - c.save(); - c.globalAlpha *= a.callout; - if (c.globalAlpha > 0 && a.calloutHasLine) { - if (d && d.isGradient) { - d = d.getStops()[0].color - } - c.strokeStyle = d; - c.fillStyle = d; - c.lineWidth = a.calloutWidth; - c.beginPath(); - c.moveTo(e.attr.calloutStartX, e.attr.calloutStartY); - c.lineTo(e.attr.calloutEndX, e.attr.calloutEndY); - c.stroke(); - c.beginPath(); - c.arc(e.attr.calloutStartX, e.attr.calloutStartY, 1 * a.calloutWidth, 0, 2 * Math.PI, true); - c.fill(); - c.beginPath(); - c.arc(e.attr.calloutEndX, e.attr.calloutEndY, 1 * a.calloutWidth, 0, 2 * Math.PI, true); - c.fill() - } - c.restore(); - Ext.draw.sprite.Text.prototype.render.apply(e, arguments) - } -}); -Ext.define("Ext.chart.series.Series", { - requires: ["Ext.chart.Markers", "Ext.chart.label.Label", "Ext.tip.ToolTip"], - mixins: ["Ext.mixin.Observable", "Ext.mixin.Bindable"], - isSeries: true, - defaultBindProperty: "store", - type: null, - seriesType: "sprite", - identifiablePrefix: "ext-line-", - observableType: "series", - darkerStrokeRatio: 0.15, - config: { - chart: null, - title: null, - renderer: null, - showInLegend: true, - triggerAfterDraw: false, - style: {}, - subStyle: {}, - themeStyle: {}, - colors: null, - useDarkerStrokeColor: true, - store: null, - label: {}, - labelOverflowPadding: null, - showMarkers: true, - marker: null, - markerSubStyle: null, - itemInstancing: null, - background: null, - highlightItem: null, - surface: null, - overlaySurface: null, - hidden: false, - highlight: false, - highlightCfg: { - merge: function(a) { - return a - }, - $value: { - fillStyle: "yellow", - strokeStyle: "red" - } - }, - animation: null, - tooltip: null - }, - directions: [], - sprites: null, - themeColorCount: function() { - return 1 - }, - isStoreDependantColorCount: false, - themeMarkerCount: function() { - return 0 - }, - getFields: function(f) { - var e = this, - a = [], - c, b, d; - for (b = 0, d = f.length; b < d; b++) { - c = e["get" + f[b] + "Field"](); - if (Ext.isArray(c)) { - a.push.apply(a, c) - } else { - a.push(c) - } - } - return a - }, - applyAnimation: function(a, b) { - if (!a) { - a = { - duration: 0 - } - } else { - if (a === true) { - a = { - easing: "easeInOut", - duration: 500 - } - } - } - return b ? Ext.apply({}, a, b) : a - }, - getAnimation: function() { - var a = this.getChart(); - if (a && a.animationSuspendCount) { - return { - duration: 0 - } - } else { - return this.callParent() - } - }, - updateTitle: function(a) { - var j = this, - g = j.getChart(); - if (!g || g.isInitializing) { - return - } - a = Ext.Array.from(a); - var c = g.getSeries(), - b = Ext.Array.indexOf(c, j), - e = g.getLegendStore(), - h = j.getYField(), - d, l, k, f; - if (e.getCount() && b !== -1) { - f = h ? Math.min(a.length, h.length) : a.length; - for (d = 0; d < f; d++) { - k = a[d]; - l = e.getAt(b + d); - if (k && l) { - l.set("name", k) - } - } - } - }, - applyHighlight: function(a, b) { - if (Ext.isObject(a)) { - a = Ext.merge({}, this.config.highlightCfg, a) - } else { - if (a === true) { - a = this.config.highlightCfg - } - } - return Ext.apply(b || {}, a) - }, - updateHighlight: function(a) { - this.getStyle(); - if (!Ext.Object.isEmpty(a)) { - this.addItemHighlight() - } - }, - updateHighlightCfg: function(a) { - if (!Ext.Object.equals(a, this.defaultConfig.highlightCfg)) { - this.addItemHighlight() - } - }, - applyItemInstancing: function(a, b) { - return Ext.merge(b || {}, a) - }, - setAttributesForItem: function(c, d) { - var b = c && c.sprite, - a; - if (b) { - if (b.itemsMarker && c.category === "items") { - b.putMarker(c.category, d, c.index, false, true) - } - if (b.isMarkerHolder && c.category === "markers") { - b.putMarker(c.category, d, c.index, false, true) - } else { - if (b.isInstancing) { - b.setAttributesFor(c.index, d) - } else { - if (Ext.isArray(b)) { - for (a = 0; a < b.length; a++) { - b[a].setAttributes(d) - } - } else { - b.setAttributes(d) - } - } - } - } - }, - getBBoxForItem: function(a) { - if (a && a.sprite) { - if (a.sprite.itemsMarker && a.category === "items") { - return a.sprite.getMarkerBBox(a.category, a.index) - } else { - if (a.sprite instanceof Ext.draw.sprite.Instancing) { - return a.sprite.getBBoxFor(a.index) - } else { - return a.sprite.getBBox() - } - } - } - return null - }, - applyHighlightItem: function(d, a) { - if (d === a) { - return - } - if (Ext.isObject(d) && Ext.isObject(a)) { - var c = d.sprite === a.sprite, - b = d.index === a.index; - if (c && b) { - return - } - } - return d - }, - updateHighlightItem: function(b, a) { - this.setAttributesForItem(a, { - highlighted: false - }); - this.setAttributesForItem(b, { - highlighted: true - }) - }, - constructor: function(a) { - var b = this, - c; - a = a || {}; - if (a.tips) { - a = Ext.apply({ - tooltip: a.tips - }, a) - } - if (a.highlightCfg) { - a = Ext.apply({ - highlight: a.highlightCfg - }, a) - } - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.sprites = []; - b.dataRange = []; - b.mixins.observable.constructor.call(b, a); - b.initBindable() - }, - lookupViewModel: function(a) { - var b = this.getChart(); - return b ? b.lookupViewModel(a) : null - }, - applyTooltip: function(c, b) { - var a = Ext.apply({ - xtype: "tooltip", - renderer: Ext.emptyFn, - constrainPosition: true, - shrinkWrapDock: true, - autoHide: true, - offsetX: 10, - offsetY: 10 - }, c); - return Ext.create(a) - }, - updateTooltip: function() { - this.addItemHighlight() - }, - addItemHighlight: function() { - var d = this.getChart(); - if (!d) { - return - } - var e = d.getInteractions(), - c, a, b; - for (c = 0; c < e.length; c++) { - a = e[c]; - if (a.isItemHighlight || a.isItemEdit) { - b = true; - break - } - } - if (!b) { - e.push("itemhighlight"); - d.setInteractions(e) - } - }, - showTooltip: function(l, m) { - var d = this, - n = d.getTooltip(), - j, a, i, f, h, k, g, e, b, c; - if (!n) { - return - } - clearTimeout(d.tooltipTimeout); - b = n.config; - if (n.trackMouse) { - m[0] += b.offsetX; - m[1] += b.offsetY - } else { - j = l.sprite; - a = j.getSurface(); - i = Ext.get(a.getId()); - if (i) { - k = l.series.getBBoxForItem(l); - g = k.x + k.width / 2; - e = k.y + k.height / 2; - h = a.matrix.transformPoint([g, e]); - f = i.getXY(); - c = a.getInherited().rtl; - g = c ? f[0] + i.getWidth() - h[0] : f[0] + h[0]; - e = f[1] + h[1]; - m = [g, e] - } - } - Ext.callback(n.renderer, n.scope, [n, l.record, l], 0, d); - n.show(m) - }, - hideTooltip: function(b) { - var a = this, - c = a.getTooltip(); - if (!c) { - return - } - clearTimeout(a.tooltipTimeout); - a.tooltipTimeout = Ext.defer(function() { - c.hide() - }, 1) - }, - applyStore: function(a) { - return a && Ext.StoreManager.lookup(a) - }, - getStore: function() { - return this._store || this.getChart() && this.getChart().getStore() - }, - updateStore: function(b, a) { - var h = this, - g = h.getChart(), - c = g && g.getStore(), - f, j, e, d; - a = a || c; - if (a && a !== b) { - a.un({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: h - }) - } - if (b) { - b.on({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: h - }); - f = h.getSprites(); - for (d = 0, e = f.length; d < e; d++) { - j = f[d]; - if (j.setStore) { - j.setStore(b) - } - } - h.onDataChanged() - } - h.fireEvent("storechange", h, b, a) - }, - onStoreChange: function(b, a, c) { - if (!this._store) { - this.updateStore(a, c) - } - }, - coordinate: function(o, m, e) { - var l = this, - p = l.getStore(), - h = l.getHidden(), - k = p.getData().items, - b = l["get" + o + "Axis"](), - f = { - min: Infinity, - max: -Infinity - }, - q = l["fieldCategory" + o] || [o], - g = l.getFields(q), - d, n, c, a = {}, - j = l.getSprites(); - if (j.length > 0) { - if (!Ext.isBoolean(h) || !h) { - for (d = 0; d < q.length; d++) { - n = g[d]; - c = l.coordinateData(k, n, b); - l.getRangeOfData(c, f); - a["data" + q[d]] = c - } - } - l.dataRange[m] = f.min; - l.dataRange[m + e] = f.max; - a["dataMin" + o] = f.min; - a["dataMax" + o] = f.max; - if (b) { - b.range = null; - a["range" + o] = b.getRange() - } - for (d = 0; d < j.length; d++) { - j[d].setAttributes(a) - } - } - }, - coordinateData: function(b, h, d) { - var g = [], - f = b.length, - e = d && d.getLayout(), - c, a; - for (c = 0; c < f; c++) { - a = b[c].data[h]; - if (!Ext.isEmpty(a, true)) { - if (e) { - g[c] = e.getCoordFor(a, h, c, b) - } else { - g[c] = +a - } - } else { - g[c] = a - } - } - return g - }, - getRangeOfData: function(g, b) { - var e = g.length, - d = b.min, - a = b.max, - c, f; - for (c = 0; c < e; c++) { - f = g[c]; - if (f < d) { - d = f - } - if (f > a) { - a = f - } - } - b.min = d; - b.max = a - }, - updateLabelData: function() { - var h = this, - l = h.getStore(), - g = l.getData().items, - f = h.getSprites(), - a = h.getLabel().getTemplate(), - n = Ext.Array.from(a.getField()), - c, b, e, d, m, k; - if (!f.length || !n.length) { - return - } - for (c = 0; c < f.length; c++) { - d = []; - m = f[c]; - k = m.getField(); - if (Ext.Array.indexOf(n, k) < 0) { - k = n[c] - } - for (b = 0, e = g.length; b < e; b++) { - d.push(g[b].get(k)) - } - m.setAttributes({ - labels: d - }) - } - }, - processData: function() { - if (!this.getStore()) { - return - } - var d = this, - f = this.directions, - a, c = f.length, - e, b; - for (a = 0; a < c; a++) { - e = f[a]; - b = d["get" + e + "Axis"](); - if (b) { - b.processData(d); - continue - } - if (d["coordinate" + e]) { - d["coordinate" + e]() - } - } - d.updateLabelData() - }, - applyBackground: function(a) { - if (this.getChart()) { - this.getSurface().setBackground(a); - return this.getSurface().getBackground() - } else { - return a - } - }, - updateChart: function(d, a) { - var c = this, - b = c._store; - if (a) { - a.un("axeschange", "onAxesChange", c); - c.clearSprites(); - c.setSurface(null); - c.setOverlaySurface(null); - a.unregister(c); - c.onChartDetached(a); - if (!b) { - c.updateStore(null) - } - } - if (d) { - c.setSurface(d.getSurface("series")); - c.setOverlaySurface(d.getSurface("overlay")); - d.on("axeschange", "onAxesChange", c); - if (d.getAxes()) { - c.onAxesChange(d) - } - c.onChartAttached(d); - d.register(c); - if (!b) { - c.updateStore(d.getStore()) - } - } - }, - onAxesChange: function(h) { - var k = this, - g = h.getAxes(), - c, a = {}, - b = {}, - e = false, - j = this.directions, - l, d, f; - for (d = 0, f = j.length; d < f; d++) { - l = j[d]; - b[l] = k.getFields(k["fieldCategory" + l]) - } - for (d = 0, f = g.length; d < f; d++) { - c = g[d]; - if (!a[c.getDirection()]) { - a[c.getDirection()] = [c] - } else { - a[c.getDirection()].push(c) - } - } - for (d = 0, f = j.length; d < f; d++) { - l = j[d]; - if (k["get" + l + "Axis"]()) { - continue - } - if (a[l]) { - c = k.findMatchingAxis(a[l], b[l]); - if (c) { - k["set" + l + "Axis"](c); - if (c.getNeedHighPrecision()) { - e = true - } - } - } - } - this.getSurface().setHighPrecision(e) - }, - findMatchingAxis: function(f, e) { - var d, c, b, a; - for (b = 0; b < f.length; b++) { - d = f[b]; - c = d.getFields(); - if (!c.length) { - return d - } else { - if (e) { - for (a = 0; a < e.length; a++) { - if (Ext.Array.indexOf(c, e[a]) >= 0) { - return d - } - } - } - } - } - }, - onChartDetached: function(a) { - var b = this; - b.fireEvent("chartdetached", a, b); - a.un("storechange", "onStoreChange", b) - }, - onChartAttached: function(a) { - var b = this; - b.setBackground(b.getBackground()); - b.fireEvent("chartattached", a, b); - a.on("storechange", "onStoreChange", b); - b.processData() - }, - updateOverlaySurface: function(a) { - var b = this; - if (a) { - if (b.getLabel()) { - b.getOverlaySurface().add(b.getLabel()) - } - } - }, - applyLabel: function(a, b) { - if (!b) { - b = new Ext.chart.Markers({ - zIndex: 10 - }); - b.setTemplate(new Ext.chart.label.Label(a)) - } else { - b.getTemplate().setAttributes(a) - } - return b - }, - createItemInstancingSprite: function(c, b) { - var e = this, - f = new Ext.chart.Markers(), - a, d; - f.setAttributes({ - zIndex: Number.MAX_VALUE - }); - a = Ext.apply({}, b); - if (e.getHighlight()) { - a.highlight = e.getHighlight(); - a.modifiers = ["highlight"] - } - f.setTemplate(a); - d = f.getTemplate(); - d.setAttributes(e.getStyle()); - d.fx.on("animationstart", "onSpriteAnimationStart", this); - d.fx.on("animationend", "onSpriteAnimationEnd", this); - c.bindMarker("items", f); - e.getSurface().add(f); - return f - }, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer() - } - }, - updateRenderer: function(c) { - var b = this, - a = b.getChart(), - d; - if (a && a.isInitializing) { - return - } - d = b.getSprites(); - if (d.length) { - d[0].setAttributes({ - renderer: c || null - }); - if (a && !a.isInitializing) { - a.redraw() - } - } - }, - updateShowMarkers: function(a) { - var d = this.getSprites(), - b = d && d[0], - c = b && b.getMarker("markers"); - if (c) { - c.getTemplate().setAttributes({ - hidden: !a - }) - } - }, - createSprite: function() { - var f = this, - a = f.getSurface(), - e = f.getItemInstancing(), - d = a.add(f.getDefaultSpriteConfig()), - b = f.getMarker(), - g, c; - d.setAttributes(f.getStyle()); - d.setSeries(f); - if (e) { - d.itemsMarker = f.createItemInstancingSprite(d, e) - } - if (d.bindMarker) { - if (b) { - g = new Ext.chart.Markers(); - c = Ext.Object.merge({}, b); - if (f.getHighlight()) { - c.highlight = f.getHighlight(); - c.modifiers = ["highlight"] - } - g.setTemplate(c); - g.getTemplate().fx.setCustomDurations({ - translationX: 0, - translationY: 0 - }); - d.dataMarker = g; - d.bindMarker("markers", g); - f.getOverlaySurface().add(g) - } - if (f.getLabel().getTemplate().getField()) { - d.bindMarker("labels", f.getLabel()) - } - } - if (d.setStore) { - d.setStore(f.getStore()) - } - d.fx.on("animationstart", "onSpriteAnimationStart", f); - d.fx.on("animationend", "onSpriteAnimationEnd", f); - f.sprites.push(d); - return d - }, - getSprites: Ext.emptyFn, - onDataChanged: function() { - var d = this, - c = d.getChart(), - b = c && c.getStore(), - a = d.getStore(); - if (a !== b) { - d.processData() - } - }, - isXType: function(a) { - return a === "series" - }, - getItemId: function() { - return this.getId() - }, - applyThemeStyle: function(e, a) { - var b = this, - d, c; - d = e && e.subStyle && e.subStyle.fillStyle; - c = d && e.subStyle.strokeStyle; - if (d && !c) { - e.subStyle.strokeStyle = b.getStrokeColorsFromFillColors(d) - } - d = e && e.markerSubStyle && e.markerSubStyle.fillStyle; - c = d && e.markerSubStyle.strokeStyle; - if (d && !c) { - e.markerSubStyle.strokeStyle = b.getStrokeColorsFromFillColors(d) - } - return Ext.apply(a || {}, e) - }, - applyStyle: function(c, b) { - var a = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + this.seriesType)); - if (a && a.def) { - c = a.def.normalize(c) - } - return Ext.apply({}, c, b) - }, - applySubStyle: function(b, c) { - var a = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + this.seriesType)); - if (a && a.def) { - b = a.def.batchedNormalize(b, true) - } - return Ext.merge({}, c, b) - }, - applyMarker: function(c, a) { - var d = (c && c.type) || (a && a.type) || "circle", - b = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + d)); - if (b && b.def) { - c = b.def.normalize(Ext.isObject(c) ? c : {}, true); - c.type = d - } - return Ext.merge(a || {}, c) - }, - applyMarkerSubStyle: function(c, a) { - var d = (c && c.type) || (a && a.type) || "circle", - b = Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite." + d)); - if (b && b.def) { - c = b.def.batchedNormalize(c, true) - } - return Ext.merge(a || {}, c) - }, - updateHidden: function(b) { - var a = this; - a.getColors(); - a.getSubStyle(); - a.setSubStyle({ - hidden: b - }); - a.processData(); - a.doUpdateStyles(); - if (!Ext.isArray(b)) { - a.updateLegendStore(b) - } - }, - updateLegendStore: function(f, b) { - var e = this, - d = e.getChart(), - c = d.getLegendStore(), - g = e.getId(), - a; - if (c) { - if (arguments.length > 1) { - a = c.findBy(function(h) { - return h.get("series") === g && h.get("index") === b - }); - if (a !== -1) { - a = c.getAt(a) - } - } else { - a = c.findRecord("series", g) - } - if (a && a.get("disabled") !== f) { - a.set("disabled", f) - } - } - }, - setHiddenByIndex: function(a, c) { - var b = this; - if (Ext.isArray(b.getHidden())) { - b.getHidden()[a] = c; - b.updateHidden(b.getHidden()); - b.updateLegendStore(c, a) - } else { - b.setHidden(c) - } - }, - getStrokeColorsFromFillColors: function(a) { - var c = this, - e = c.getUseDarkerStrokeColor(), - b = (Ext.isNumber(e) ? e : c.darkerStrokeRatio), - d; - if (e) { - d = Ext.Array.map(a, function(f) { - f = Ext.isString(f) ? f : f.stops[0].color; - f = Ext.draw.Color.fromString(f); - return f.createDarker(b).toString() - }) - } else { - d = Ext.Array.clone(a) - } - return d - }, - updateThemeColors: function(b) { - var c = this, - d = c.getThemeStyle(), - a = Ext.Array.clone(b), - f = c.getStrokeColorsFromFillColors(b), - e = { - fillStyle: a, - strokeStyle: f - }; - d.subStyle = Ext.apply(d.subStyle || {}, e); - d.markerSubStyle = Ext.apply(d.markerSubStyle || {}, e); - c.doUpdateStyles() - }, - themeOnlyIfConfigured: {}, - updateTheme: function(d) { - var h = this, - a = d.getSeries(), - n = h.getInitialConfig(), - c = h.defaultConfig, - f = h.getConfigurator().configs, - j = a.defaults, - k = a[h.type], - g = h.themeOnlyIfConfigured, - l, i, o, b, m, e; - a = Ext.merge({}, j, k); - for (l in a) { - i = a[l]; - e = f[l]; - if (i !== null && i !== undefined && e) { - m = n[l]; - o = Ext.isObject(i); - b = m === c[l]; - if (o) { - if (b && g[l]) { - continue - } - i = Ext.merge({}, i, m) - } - if (b || o) { - h[e.names.set](i) - } - } - } - }, - updateChartColors: function(a) { - var b = this; - if (!b.getColors()) { - b.updateThemeColors(a) - } - }, - updateColors: function(a) { - this.updateThemeColors(a) - }, - updateStyle: function() { - this.doUpdateStyles() - }, - updateSubStyle: function() { - this.doUpdateStyles() - }, - updateThemeStyle: function() { - this.doUpdateStyles() - }, - doUpdateStyles: function() { - var g = this, - h = g.sprites, - d = g.getItemInstancing(), - c = 0, - f = h && h.length, - a = g.getConfig("showMarkers", true), - b = g.getMarker(), - e; - for (; c < f; c++) { - e = g.getStyleByIndex(c); - if (d) { - h[c].itemsMarker.getTemplate().setAttributes(e) - } - h[c].setAttributes(e); - if (b && h[c].dataMarker) { - h[c].dataMarker.getTemplate().setAttributes(g.getMarkerStyleByIndex(c)) - } - } - }, - getStyleWithTheme: function() { - var b = this, - c = b.getThemeStyle(), - d = (c && c.style) || {}, - a = Ext.applyIf(Ext.apply({}, b.getStyle()), d); - return a - }, - getSubStyleWithTheme: function() { - var c = this, - d = c.getThemeStyle(), - a = (d && d.subStyle) || {}, - b = Ext.applyIf(Ext.apply({}, c.getSubStyle()), a); - return b - }, - getStyleByIndex: function(b) { - var e = this, - h = e.getThemeStyle(), - d, g, c, f, a = {}; - d = e.getStyle(); - g = (h && h.style) || {}; - c = e.styleDataForIndex(e.getSubStyle(), b); - f = e.styleDataForIndex((h && h.subStyle), b); - Ext.apply(a, g); - Ext.apply(a, f); - Ext.apply(a, d); - Ext.apply(a, c); - return a - }, - getMarkerStyleByIndex: function(d) { - var g = this, - c = g.getThemeStyle(), - a, e, k, j, b, l, h, f, m = {}; - a = g.getStyle(); - e = (c && c.style) || {}; - k = g.styleDataForIndex(g.getSubStyle(), d); - if (k.hasOwnProperty("hidden")) { - k.hidden = k.hidden || !this.getConfig("showMarkers", true) - } - j = g.styleDataForIndex((c && c.subStyle), d); - b = g.getMarker(); - l = (c && c.marker) || {}; - h = g.getMarkerSubStyle(); - f = g.styleDataForIndex((c && c.markerSubStyle), d); - Ext.apply(m, e); - Ext.apply(m, j); - Ext.apply(m, l); - Ext.apply(m, f); - Ext.apply(m, a); - Ext.apply(m, k); - Ext.apply(m, b); - Ext.apply(m, h); - return m - }, - styleDataForIndex: function(d, c) { - var e, b, a = {}; - if (d) { - for (b in d) { - e = d[b]; - if (Ext.isArray(e)) { - a[b] = e[c % e.length] - } else { - a[b] = e - } - } - } - return a - }, - getItemForPoint: Ext.emptyFn, - getItemByIndex: function(a, e) { - var d = this, - f = d.getSprites(), - b = f && f[0], - c; - if (!b) { - return - } - if (e === undefined && b.isMarkerHolder) { - e = d.getItemInstancing() ? "items" : "markers" - } else { - if (!e || e === "" || e === "sprites") { - b = f[a] - } - } - if (b) { - c = { - series: d, - category: e, - index: a, - record: d.getStore().getData().items[a], - field: d.getYField(), - sprite: b - }; - return c - } - }, - onSpriteAnimationStart: function(a) { - this.fireEvent("animationstart", this, a) - }, - onSpriteAnimationEnd: function(a) { - this.fireEvent("animationend", this, a) - }, - resolveListenerScope: function(e) { - var d = this, - a = Ext._namedScopes[e], - c = d.getChart(), - b; - if (!a) { - b = c ? c.resolveListenerScope(e, false) : (e || d) - } else { - if (a.isThis) { - b = d - } else { - if (a.isController) { - b = c ? c.resolveListenerScope(e, false) : d - } else { - if (a.isSelf) { - b = c ? c.resolveListenerScope(e, false) : d; - if (b === c && !c.getInheritedConfig("defaultListenerScope")) { - b = d - } - } - } - } - } - return b - }, - provideLegendInfo: function(a) { - a.push({ - name: this.getTitle() || this.getId(), - mark: "black", - disabled: this.getHidden(), - series: this.getId(), - index: 0 - }) - }, - clearSprites: function() { - var d = this.sprites, - b, a, c; - for (a = 0, c = d.length; a < c; a++) { - b = d[a]; - if (b && b.isSprite) { - b.destroy() - } - } - this.sprites = [] - }, - destroy: function() { - var b = this, - a = b._store, - c = b.getConfig("tooltip", true); - if (a && a.getAutoDestroy()) { - Ext.destroy(a) - } - b.setChart(null); - b.clearListeners(); - if (c) { - Ext.destroy(c); - clearTimeout(b.tooltipTimeout) - } - b.callParent() - } -}); -Ext.define("Ext.chart.interactions.Abstract", { - xtype: "interaction", - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - gestures: { - tap: "onGesture" - }, - chart: null, - enabled: true - }, - throttleGap: 0, - stopAnimationBeforeSync: false, - constructor: function(a) { - var b = this, - c; - a = a || {}; - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.mixins.observable.constructor.call(b, a) - }, - initialize: Ext.emptyFn, - updateChart: function(c, a) { - var b = this; - if (a === c) { - return - } - if (a) { - a.unregister(b); - b.removeChartListener(a) - } - if (c) { - c.register(b); - b.addChartListener() - } - }, - updateEnabled: function(a) { - var c = this, - b = c.getChart(); - if (b) { - if (a) { - c.addChartListener() - } else { - c.removeChartListener(b) - } - } - }, - onGesture: Ext.emptyFn, - getItemForEvent: function(d) { - var b = this, - a = b.getChart(), - c = a.getEventXY(d); - return a.getItemForPoint(c[0], c[1]) - }, - getItemsForEvent: function(d) { - var b = this, - a = b.getChart(), - c = a.getEventXY(d); - return a.getItemsForPoint(c[0], c[1]) - }, - addChartListener: function() { - var c = this, - b = c.getChart(), - e = c.getGestures(), - a; - if (!c.getEnabled()) { - return - } - - function d(f, g) { - b.addElementListener(f, c.listeners[f] = function(j) { - var i = c.getLocks(), - h; - if (c.getEnabled() && (!(f in i) || i[f] === c)) { - h = (Ext.isFunction(g) ? g : c[g]).apply(this, arguments); - if (h === false && j && j.stopPropagation) { - j.stopPropagation() - } - return h - } - }, c) - } - c.listeners = c.listeners || {}; - for (a in e) { - d(a, e[a]) - } - }, - removeChartListener: function(c) { - var d = this, - e = d.getGestures(), - b; - - function a(f) { - var g = d.listeners[f]; - if (g) { - c.removeElementListener(f, g); - delete d.listeners[f] - } - } - if (d.listeners) { - for (b in e) { - a(b) - } - } - }, - lockEvents: function() { - var d = this, - c = d.getLocks(), - a = Array.prototype.slice.call(arguments), - b = a.length; - while (b--) { - c[a[b]] = d - } - }, - unlockEvents: function() { - var c = this.getLocks(), - a = Array.prototype.slice.call(arguments), - b = a.length; - while (b--) { - delete c[a[b]] - } - }, - getLocks: function() { - var a = this.getChart(); - return a.lockedEvents || (a.lockedEvents = {}) - }, - isMultiTouch: function() { - if (Ext.browser.is.IE10) { - return true - } - return !Ext.os.is.Desktop - }, - initializeDefaults: Ext.emptyFn, - doSync: function() { - var b = this, - a = b.getChart(); - if (b.syncTimer) { - clearTimeout(b.syncTimer); - b.syncTimer = null - } - if (b.stopAnimationBeforeSync) { - a.animationSuspendCount++ - } - a.redraw(); - if (b.stopAnimationBeforeSync) { - a.animationSuspendCount-- - } - b.syncThrottle = Date.now() + b.throttleGap - }, - sync: function() { - var a = this; - if (a.throttleGap && Ext.frameStartTime < a.syncThrottle) { - if (a.syncTimer) { - return - } - a.syncTimer = Ext.defer(function() { - a.doSync() - }, a.throttleGap) - } else { - a.doSync() - } - }, - getItemId: function() { - return this.getId() - }, - isXType: function(a) { - return a === "interaction" - }, - destroy: function() { - var a = this; - a.setChart(null); - delete a.listeners; - a.callParent() - } -}, function() { - if (Ext.os.is.Android4) { - this.prototype.throttleGap = 40 - } -}); -Ext.define("Ext.chart.MarkerHolder", { - extend: "Ext.Mixin", - mixinConfig: { - id: "markerHolder", - after: { - constructor: "constructor", - preRender: "preRender" - }, - before: { - destroy: "destroy" - } - }, - isMarkerHolder: true, - surfaceMatrix: null, - inverseSurfaceMatrix: null, - deprecated: { - 6: { - methods: { - getBoundMarker: { - message: "Please use the 'getMarker' method instead.", - fn: function(b) { - var a = this.boundMarkers[b]; - return a ? [a] : a - } - } - } - } - }, - constructor: function() { - this.boundMarkers = {}; - this.cleanRedraw = false - }, - bindMarker: function(b, a) { - var c = this, - d = c.boundMarkers; - if (a && a.isMarkers) { - c.releaseMarker(b); - d[b] = a; - a.on("destroy", c.onMarkerDestroy, c) - } - }, - onMarkerDestroy: function(a) { - this.releaseMarker(a) - }, - releaseMarker: function(a) { - var c = this.boundMarkers, - b; - if (a && a.isMarkers) { - for (b in c) { - if (c[b] === a) { - delete c[b]; - break - } - } - } else { - b = a; - a = c[b]; - delete c[b] - } - return a || null - }, - getMarker: function(a) { - return this.boundMarkers[a] || null - }, - preRender: function() { - var f = this, - g = f.getId(), - d = f.boundMarkers, - e = f.getParent(), - c, a, b; - if (f.surfaceMatrix) { - b = f.surfaceMatrix.set(1, 0, 0, 1, 0, 0) - } else { - b = f.surfaceMatrix = new Ext.draw.Matrix() - } - f.cleanRedraw = !f.attr.dirty; - if (!f.cleanRedraw) { - for (c in d) { - a = d[c]; - if (a) { - a.clear(g) - } - } - } - while (e && e.attr && e.attr.matrix) { - b.prependMatrix(e.attr.matrix); - e = e.getParent() - } - b.prependMatrix(e.matrix); - f.surfaceMatrix = b; - f.inverseSurfaceMatrix = b.inverse(f.inverseSurfaceMatrix) - }, - putMarker: function(d, a, c, g, e) { - var b = this.boundMarkers[d], - f = this.getId(); - if (b) { - b.putMarkerFor(f, a, c, g, e) - } - }, - getMarkerBBox: function(c, b, d) { - var a = this.boundMarkers[c], - e = this.getId(); - if (a) { - return a.getMarkerBBoxFor(e, b, d) - } - }, - destroy: function() { - var c = this.boundMarkers, - b, a; - for (b in c) { - a = c[b]; - a.destroy() - } - } -}); -Ext.define("Ext.chart.axis.sprite.Axis", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.axis", - type: "axis", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - requires: ["Ext.draw.sprite.Text"], - inheritableStatics: { - def: { - processors: { - grid: "bool", - axisLine: "bool", - minorTicks: "bool", - minorTickSize: "number", - majorTicks: "bool", - majorTickSize: "number", - length: "number", - startGap: "number", - endGap: "number", - dataMin: "number", - dataMax: "number", - visibleMin: "number", - visibleMax: "number", - position: "enums(left,right,top,bottom,angular,radial,gauge)", - minStepSize: "number", - estStepSize: "number", - titleOffset: "number", - textPadding: "number", - min: "number", - max: "number", - centerX: "number", - centerY: "number", - radius: "number", - totalAngle: "number", - baseRotation: "number", - data: "default", - enlargeEstStepSizeByText: "bool" - }, - defaults: { - grid: false, - axisLine: true, - minorTicks: false, - minorTickSize: 3, - majorTicks: true, - majorTickSize: 5, - length: 0, - startGap: 0, - endGap: 0, - visibleMin: 0, - visibleMax: 1, - dataMin: 0, - dataMax: 1, - position: "", - minStepSize: 0, - estStepSize: 20, - min: 0, - max: 1, - centerX: 0, - centerY: 0, - radius: 1, - baseRotation: 0, - data: null, - titleOffset: 0, - textPadding: 0, - scalingCenterY: 0, - scalingCenterX: 0, - strokeStyle: "black", - enlargeEstStepSizeByText: false - }, - triggers: { - minorTickSize: "bbox", - majorTickSize: "bbox", - position: "bbox,layout", - axisLine: "bbox,layout", - min: "layout", - max: "layout", - length: "layout", - minStepSize: "layout", - estStepSize: "layout", - data: "layout", - dataMin: "layout", - dataMax: "layout", - visibleMin: "layout", - visibleMax: "layout", - enlargeEstStepSizeByText: "layout" - }, - updaters: { - layout: "layoutUpdater" - } - } - }, - config: { - label: null, - layout: null, - segmenter: null, - renderer: null, - layoutContext: null, - axis: null - }, - thickness: 0, - stepSize: 0, - getBBox: function() { - return null - }, - defaultRenderer: function(a) { - return this.segmenter.renderer(a, this) - }, - layoutUpdater: function() { - var h = this, - f = h.getAxis().getChart(); - if (f.isInitializing) { - return - } - var e = h.attr, - d = h.getLayout(), - g = f.getInherited().rtl, - b = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMin, - i = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMax, - c = e.position, - a = { - attr: e, - segmenter: h.getSegmenter(), - renderer: h.defaultRenderer - }; - if (c === "left" || c === "right") { - e.translationX = 0; - e.translationY = i * e.length / (i - b); - e.scalingX = 1; - e.scalingY = -e.length / (i - b); - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } else { - if (c === "top" || c === "bottom") { - if (g) { - e.translationX = e.length + b * e.length / (i - b) + 1 - } else { - e.translationX = -b * e.length / (i - b) - } - e.translationY = 0; - e.scalingX = (g ? -1 : 1) * e.length / (i - b); - e.scalingY = 1; - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } - } - if (d) { - d.calculateLayout(a); - h.setLayoutContext(a) - } - }, - iterate: function(e, j) { - var c, g, a, b, h, d, k = Ext.Array.some, - m = Math.abs, - f; - if (e.getLabel) { - if (e.min < e.from) { - j.call(this, e.min, e.getLabel(e.min), -1, e) - } - for (c = 0; c <= e.steps; c++) { - j.call(this, e.get(c), e.getLabel(c), c, e) - } - if (e.max > e.to) { - j.call(this, e.max, e.getLabel(e.max), e.steps + 1, e) - } - } else { - b = this.getAxis(); - h = b.floatingAxes; - d = []; - f = (e.to - e.from) / (e.steps + 1); - if (b.getFloating()) { - for (a in h) { - d.push(h[a]) - } - } - - function l(i) { - return !d.length || k(d, function(n) { - return m(n - i) > f - }) - } - if (e.min < e.from && l(e.min)) { - j.call(this, e.min, e.min, -1, e) - } - for (c = 0; c <= e.steps; c++) { - g = e.get(c); - if (l(g)) { - j.call(this, g, g, c, e) - } - } - if (e.max > e.to && l(e.max)) { - j.call(this, e.max, e.max, e.steps + 1, e) - } - } - }, - renderTicks: function(l, m, s, p) { - var v = this, - k = v.attr, - u = k.position, - n = k.matrix, - e = 0.5 * k.lineWidth, - f = n.getXX(), - i = n.getDX(), - j = n.getYY(), - h = n.getDY(), - o = s.majorTicks, - d = k.majorTickSize, - a = s.minorTicks, - r = k.minorTickSize; - if (o) { - switch (u) { - case "right": - function q(w) { - return function(x, z, y) { - x = l.roundPixel(x * j + h) + e; - m.moveTo(0, x); - m.lineTo(w, x) - } - } - v.iterate(o, q(d)); - a && v.iterate(a, q(r)); - break; - case "left": - function t(w) { - return function(x, z, y) { - x = l.roundPixel(x * j + h) + e; - m.moveTo(p[2] - w, x); - m.lineTo(p[2], x) - } - } - v.iterate(o, t(d)); - a && v.iterate(a, t(r)); - break; - case "bottom": - function c(w) { - return function(x, z, y) { - x = l.roundPixel(x * f + i) - e; - m.moveTo(x, 0); - m.lineTo(x, w) - } - } - v.iterate(o, c(d)); - a && v.iterate(a, c(r)); - break; - case "top": - function b(w) { - return function(x, z, y) { - x = l.roundPixel(x * f + i) - e; - m.moveTo(x, p[3]); - m.lineTo(x, p[3] - w) - } - } - v.iterate(o, b(d)); - a && v.iterate(a, b(r)); - break; - case "angular": - v.iterate(o, function(w, y, x) { - w = w / (k.max + 1) * Math.PI * 2 + k.baseRotation; - m.moveTo(k.centerX + (k.length) * Math.cos(w), k.centerY + (k.length) * Math.sin(w)); - m.lineTo(k.centerX + (k.length + d) * Math.cos(w), k.centerY + (k.length + d) * Math.sin(w)) - }); - break; - case "gauge": - var g = v.getGaugeAngles(); - v.iterate(o, function(w, y, x) { - w = (w - k.min) / (k.max - k.min + 1) * k.totalAngle - k.totalAngle + g.start; - m.moveTo(k.centerX + (k.length) * Math.cos(w), k.centerY + (k.length) * Math.sin(w)); - m.lineTo(k.centerX + (k.length + d) * Math.cos(w), k.centerY + (k.length + d) * Math.sin(w)) - }); - break - } - } - }, - renderLabels: function(E, q, D, K) { - var o = this, - k = o.attr, - i = 0.5 * k.lineWidth, - u = k.position, - y = k.matrix, - A = k.textPadding, - x = y.getXX(), - d = y.getDX(), - g = y.getYY(), - c = y.getDY(), - n = 0, - I = D.majorTicks, - G = Math.max(k.majorTickSize, k.minorTickSize) + k.lineWidth, - f = Ext.draw.Draw.isBBoxIntersect, - F = o.getLabel(), - J, s, r = null, - w = 0, - b = 0, - m = D.segmenter, - B = o.getRenderer(), - t = o.getAxis(), - z = t.getTitle(), - a = z && z.attr.text !== "" && z.getBBox(), - l, h = null, - p, C, v, e, H; - if (I && F && !F.attr.hidden) { - J = F.attr.font; - if (q.font !== J) { - q.font = J - } - F.setAttributes({ - translationX: 0, - translationY: 0 - }, true); - F.applyTransformations(); - l = F.attr.inverseMatrix.elements.slice(0); - switch (u) { - case "left": - e = a ? a.x + a.width : 0; - switch (F.attr.textAlign) { - case "start": - H = E.roundPixel(e + d) - i; - break; - case "end": - H = E.roundPixel(K[2] - G + d) - i; - break; - default: - H = E.roundPixel(e + (K[2] - e - G) / 2 + d) - i - } - F.setAttributes({ - translationX: H - }, true); - break; - case "right": - e = a ? K[2] - a.x : 0; - switch (F.attr.textAlign) { - case "start": - H = E.roundPixel(G + d) + i; - break; - case "end": - H = E.roundPixel(K[2] - e + d) + i; - break; - default: - H = E.roundPixel(G + (K[2] - G - e) / 2 + d) + i - } - F.setAttributes({ - translationX: H - }, true); - break; - case "top": - e = a ? a.y + a.height : 0; - F.setAttributes({ - translationY: E.roundPixel(e + (K[3] - e - G) / 2) - i - }, true); - break; - case "bottom": - e = a ? K[3] - a.y : 0; - F.setAttributes({ - translationY: E.roundPixel(G + (K[3] - G - e) / 2) + i - }, true); - break; - case "radial": - F.setAttributes({ - translationX: k.centerX - }, true); - break; - case "angular": - F.setAttributes({ - translationY: k.centerY - }, true); - break; - case "gauge": - F.setAttributes({ - translationY: k.centerY - }, true); - break - } - if (u === "left" || u === "right") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - F.setAttributes({ - text: String(v), - translationY: E.roundPixel(L * g + c) - }, true); - F.applyTransformations(); - n = Math.max(n, F.getBBox().width + G); - if (n <= o.thickness) { - C = Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0)); - p = C.prepend.apply(C, l).transformBBox(F.getBBox(true)); - if (h && !f(p, h, A)) { - return - } - E.renderSprite(F); - h = p; - w += p.height; - b++ - } - }) - } else { - if (u === "top" || u === "bottom") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - F.setAttributes({ - text: String(v), - translationX: E.roundPixel(L * x + d) - }, true); - F.applyTransformations(); - n = Math.max(n, F.getBBox().height + G); - if (n <= o.thickness) { - C = Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0)); - p = C.prepend.apply(C, l).transformBBox(F.getBBox(true)); - if (h && !f(p, h, A)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "radial") { - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - if (typeof v !== "undefined") { - F.setAttributes({ - text: String(v), - translationX: k.centerX - E.roundPixel(L) / k.max * k.length * Math.cos(k.baseRotation + Math.PI / 2), - translationY: k.centerY - E.roundPixel(L) / k.max * k.length * Math.sin(k.baseRotation + Math.PI / 2) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "angular") { - s = k.majorTickSize + k.lineWidth * 0.5 + (parseInt(F.attr.fontSize, 10) || 10) / 2; - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - n = Math.max(n, Math.max(k.majorTickSize, k.minorTickSize) + (k.lineCap !== "butt" ? k.lineWidth * 0.5 : 0)); - if (typeof v !== "undefined") { - var O = L / (k.max + 1) * Math.PI * 2 + k.baseRotation; - F.setAttributes({ - text: String(v), - translationX: k.centerX + (k.length + s) * Math.cos(O), - translationY: k.centerY + (k.length + s) * Math.sin(O) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } else { - if (u === "gauge") { - var j = o.getGaugeAngles(); - o.iterate(I, function(L, N, M) { - if (N === undefined) { - return - } - if (B) { - v = Ext.callback(B, null, [t, N, D, r], 0, t) - } else { - v = m.renderer(N, D, r) - } - r = N; - if (typeof v !== "undefined") { - var O = (L - k.min) / (k.max - k.min + 1) * k.totalAngle - k.totalAngle + j.start; - F.setAttributes({ - text: String(v), - translationX: k.centerX + (k.length + 10) * Math.cos(O), - translationY: k.centerY + (k.length + 10) * Math.sin(O) - }, true); - F.applyTransformations(); - p = F.attr.matrix.transformBBox(F.getBBox(true)); - if (h && !f(p, h)) { - return - } - E.renderSprite(F); - h = p; - w += p.width; - b++ - } - }) - } - } - } - } - } - if (k.enlargeEstStepSizeByText && b) { - w /= b; - w += G; - w *= 2; - if (k.estStepSize < w) { - k.estStepSize = w - } - } - if (Math.abs(o.thickness - (n)) > 1) { - o.thickness = n; - k.bbox.plain.dirty = true; - k.bbox.transform.dirty = true; - o.doThicknessChanged(); - return false - } - } - }, - renderAxisLine: function(a, i, e, c) { - var h = this, - g = h.attr, - b = g.lineWidth * 0.5, - j = g.position, - d, f; - if (g.axisLine && g.length) { - switch (j) { - case "left": - d = a.roundPixel(c[2]) - b; - i.moveTo(d, -g.endGap); - i.lineTo(d, g.length + g.startGap + 1); - break; - case "right": - i.moveTo(b, -g.endGap); - i.lineTo(b, g.length + g.startGap + 1); - break; - case "bottom": - i.moveTo(-g.startGap, b); - i.lineTo(g.length + g.endGap, b); - break; - case "top": - d = a.roundPixel(c[3]) - b; - i.moveTo(-g.startGap, d); - i.lineTo(g.length + g.endGap, d); - break; - case "angular": - i.moveTo(g.centerX + g.length, g.centerY); - i.arc(g.centerX, g.centerY, g.length, 0, Math.PI * 2, true); - break; - case "gauge": - f = h.getGaugeAngles(); - i.moveTo(g.centerX + Math.cos(f.start) * g.length, g.centerY + Math.sin(f.start) * g.length); - i.arc(g.centerX, g.centerY, g.length, f.start, f.end, true); - break - } - } - }, - getGaugeAngles: function() { - var a = this, - c = a.attr.totalAngle, - b; - if (c <= Math.PI) { - b = (Math.PI - c) * 0.5 - } else { - b = -(Math.PI * 2 - c) * 0.5 - } - b = Math.PI * 2 - b; - return { - start: b, - end: b - c - } - }, - renderGridLines: function(m, n, s, r) { - var t = this, - b = t.getAxis(), - l = t.attr, - p = l.matrix, - d = l.startGap, - a = l.endGap, - c = p.getXX(), - k = p.getYY(), - h = p.getDX(), - g = p.getDY(), - u = l.position, - f = b.getGridAlignment(), - q = s.majorTicks, - e, o, i; - if (l.grid) { - if (q) { - if (u === "left" || u === "right") { - i = l.min * k + g + a + d; - t.iterate(q, function(j, w, v) { - e = j * k + g + a; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - y: e, - height: i - e - }, o = v, true); - i = e - }); - o++; - e = 0; - t.putMarker(f + "-" + (o % 2 ? "odd" : "even"), { - y: e, - height: i - e - }, o, true) - } else { - if (u === "top" || u === "bottom") { - i = l.min * c + h + d; - if (d) { - t.putMarker(f + "-even", { - x: 0, - width: i - }, -1, true) - } - t.iterate(q, function(j, w, v) { - e = j * c + h + d; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - x: e, - width: i - e - }, o = v, true); - i = e - }); - o++; - e = l.length + l.startGap + l.endGap; - t.putMarker(f + "-" + (o % 2 ? "odd" : "even"), { - x: e, - width: i - e - }, o, true) - } else { - if (u === "radial") { - t.iterate(q, function(j, w, v) { - if (!j) { - return - } - e = j / l.max * l.length; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - scalingX: e, - scalingY: e - }, v, true); - i = e - }) - } else { - if (u === "angular") { - t.iterate(q, function(j, w, v) { - if (!l.length) { - return - } - e = j / (l.max + 1) * Math.PI * 2 + l.baseRotation; - t.putMarker(f + "-" + (v % 2 ? "odd" : "even"), { - rotationRads: e, - rotationCenterX: 0, - rotationCenterY: 0, - scalingX: l.length, - scalingY: l.length - }, v, true); - i = e - }) - } - } - } - } - } - } - }, - renderLimits: function(o) { - var t = this, - a = t.getAxis(), - h = a.getChart(), - p = h.getInnerPadding(), - d = Ext.Array.from(a.getLimits()); - if (!d.length) { - return - } - var r = a.limits.surface.getRect(), - m = t.attr, - n = m.matrix, - u = m.position, - k = Ext.Object.chain, - v = a.limits.titles, - c, j, b, s, l, q, f, g, e; - v.instances = []; - v.position = 0; - if (u === "left" || u === "right") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l * n.getYY() + n.getDY(); - s.line.y = l + p.top; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("horizontal-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - j = s.line.title.position || (u === "left" ? "start" : "end"); - switch (j) { - case "start": - g = 10; - break; - case "end": - g = r[2] - 10; - break; - case "middle": - g = r[2] / 2; - break - } - v.setAttributesFor(v.position - 1, { - x: g, - y: s.line.y - c.height / 2, - textAlign: j, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "top" || u === "bottom") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l * n.getXX() + n.getDX(); - s.line.x = l + p.left; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("vertical-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - j = s.line.title.position || (u === "top" ? "end" : "start"); - switch (j) { - case "start": - e = r[3] - c.width / 2 - 10; - break; - case "end": - e = c.width / 2 + 10; - break; - case "middle": - e = r[3] / 2; - break - } - v.setAttributesFor(v.position - 1, { - x: s.line.x + c.height / 2, - y: e, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle, - rotationRads: Math.PI / 2 - }) - } - } - } else { - if (u === "radial") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - if (l > m.max) { - continue - } - l = l / m.max * m.length; - s.line.cx = m.centerX; - s.line.cy = m.centerY; - s.line.scalingX = l; - s.line.scalingY = l; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("circular-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - v.setAttributesFor(v.position - 1, { - x: m.centerX, - y: m.centerY - l - c.height / 2, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "angular") { - for (q = 0, f = d.length; q < f; q++) { - s = k(d[q]); - !s.line && (s.line = {}); - l = Ext.isString(s.value) ? a.getCoordFor(s.value) : s.value; - l = l / (m.max + 1) * Math.PI * 2 + m.baseRotation; - s.line.translationX = m.centerX; - s.line.translationY = m.centerY; - s.line.rotationRads = l; - s.line.rotationCenterX = 0; - s.line.rotationCenterY = 0; - s.line.scalingX = m.length; - s.line.scalingY = m.length; - s.line.strokeStyle = s.line.strokeStyle || m.strokeStyle; - t.putMarker("radial-limit-lines", s.line, q, true); - if (s.line.title) { - v.createInstance(s.line.title); - c = v.getBBoxFor(v.position - 1); - b = ((l > -0.5 * Math.PI && l < 0.5 * Math.PI) || (l > 1.5 * Math.PI && l < 2 * Math.PI)) ? 1 : -1; - v.setAttributesFor(v.position - 1, { - x: m.centerX + 0.5 * m.length * Math.cos(l) + b * c.height / 2 * Math.sin(l), - y: m.centerY + 0.5 * m.length * Math.sin(l) - b * c.height / 2 * Math.cos(l), - rotationRads: b === 1 ? l : l - Math.PI, - fillStyle: s.line.title.fillStyle || s.line.strokeStyle - }) - } - } - } else { - if (u === "gauge") {} - } - } - } - } - }, - doThicknessChanged: function() { - var a = this.getAxis(); - if (a) { - a.onThicknessChanged() - } - }, - render: function(a, c, d) { - var e = this, - b = e.getLayoutContext(); - if (b) { - if (false === e.renderLabels(a, c, b, d)) { - return false - } - c.beginPath(); - e.renderTicks(a, c, b, d); - e.renderAxisLine(a, c, b, d); - e.renderGridLines(a, c, b, d); - e.renderLimits(d); - c.stroke() - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Segmenter", { - config: { - axis: null - }, - constructor: function(a) { - this.initConfig(a) - }, - renderer: function(b, a) { - return String(b) - }, - from: function(a) { - return a - }, - diff: Ext.emptyFn, - align: Ext.emptyFn, - add: Ext.emptyFn, - preferredStep: Ext.emptyFn -}); -Ext.define("Ext.chart.axis.segmenter.Names", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.names", - renderer: function(b, a) { - return b - }, - diff: function(b, a, c) { - return Math.floor(a - b) - }, - align: function(c, b, a) { - return Math.floor(c) - }, - add: function(c, b, a) { - return c + b - }, - preferredStep: function(c, a, b, d) { - return { - unit: 1, - step: 1 - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Numeric", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.numeric", - isNumeric: true, - renderer: function(b, a) { - return b.toFixed(Math.max(0, a.majorTicks.unit.fixes)) - }, - diff: function(b, a, c) { - return Math.floor((a - b) / c.scale) - }, - align: function(c, b, a) { - return Math.floor(c / (a.scale * b)) * a.scale * b - }, - add: function(c, b, a) { - return c + b * a.scale - }, - preferredStep: function(c, b) { - var a = Math.floor(Math.log(b) * Math.LOG10E), - d = Math.pow(10, a); - b /= d; - if (b < 2) { - b = 2 - } else { - if (b < 5) { - b = 5 - } else { - if (b < 10) { - b = 10; - a++ - } - } - } - return { - unit: { - fixes: -a, - scale: d - }, - step: b - } - }, - exactStep: function(c, b) { - var a = Math.floor(Math.log(b) * Math.LOG10E), - d = Math.pow(10, a); - return { - unit: { - fixes: -a + (b % d === 0 ? 0 : 1), - scale: 1 - }, - step: b - } - }, - adjustByMajorUnit: function(e, g, c) { - var d = c[0], - b = c[1], - a = e * g, - f = d % a; - if (f !== 0) { - c[0] = d - f + (d < 0 ? -a : 0) - } - f = b % a; - if (f !== 0) { - c[1] = b - f + (b > 0 ? a : 0) - } - } -}); -Ext.define("Ext.chart.axis.segmenter.Time", { - extend: "Ext.chart.axis.segmenter.Segmenter", - alias: "segmenter.time", - config: { - step: null - }, - renderer: function(c, b) { - var a = Ext.Date; - switch (b.majorTicks.unit) { - case "y": - return a.format(c, "Y"); - case "mo": - return a.format(c, "Y-m"); - case "d": - return a.format(c, "Y-m-d") - } - return a.format(c, "Y-m-d\nH:i:s") - }, - from: function(a) { - return new Date(a) - }, - diff: function(b, a, c) { - if (isFinite(b)) { - b = new Date(b) - } - if (isFinite(a)) { - a = new Date(a) - } - return Ext.Date.diff(b, a, c) - }, - align: function(a, c, b) { - if (b === "d" && c >= 7) { - a = Ext.Date.align(a, "d", c); - a.setDate(a.getDate() - a.getDay() + 1); - return a - } else { - return Ext.Date.align(a, b, c) - } - }, - add: function(c, b, a) { - return Ext.Date.add(new Date(c), a, b) - }, - stepUnits: [ - [Ext.Date.YEAR, 1, 2, 5, 10, 20, 50, 100, 200, 500], - [Ext.Date.MONTH, 1, 3, 6], - [Ext.Date.DAY, 1, 7, 14], - [Ext.Date.HOUR, 1, 6, 12], - [Ext.Date.MINUTE, 1, 5, 15, 30], - [Ext.Date.SECOND, 1, 5, 15, 30], - [Ext.Date.MILLI, 1, 2, 5, 10, 20, 50, 100, 200, 500] - ], - preferredStep: function(b, e) { - if (this.getStep()) { - return this.getStep() - } - var f = new Date(+b), - g = new Date(+b + Math.ceil(e)), - d = this.stepUnits, - l, k, h, c, a; - for (c = 0; c < d.length; c++) { - k = d[c][0]; - h = this.diff(f, g, k); - if (h > 0) { - for (a = 1; a < d[c].length; a++) { - if (h <= d[c][a]) { - l = { - unit: k, - step: d[c][a] - }; - break - } - } - if (!l) { - c--; - l = { - unit: d[c][0], - step: 1 - } - } - break - } - } - if (!l) { - l = { - unit: Ext.Date.DAY, - step: 1 - } - } - return l - } -}); -Ext.define("Ext.chart.axis.layout.Layout", { - mixins: { - observable: "Ext.mixin.Observable" - }, - config: { - axis: null - }, - constructor: function(a) { - this.mixins.observable.constructor.call(this, a) - }, - processData: function(b) { - var e = this, - c = e.getAxis(), - f = c.getDirection(), - g = c.boundSeries, - a, d; - if (b) { - b["coordinate" + f]() - } else { - for (a = 0, d = g.length; a < d; a++) { - g[a]["coordinate" + f]() - } - } - }, - calculateMajorTicks: function(a) { - var f = this, - e = a.attr, - d = e.max - e.min, - i = d / Math.max(1, e.length) * (e.visibleMax - e.visibleMin), - h = e.min + d * e.visibleMin, - b = e.min + d * e.visibleMax, - g = e.estStepSize * i, - c = f.snapEnds(a, e.min, e.max, g); - if (c) { - f.trimByRange(a, c, h, b); - a.majorTicks = c - } - }, - calculateMinorTicks: function(a) { - if (this.snapMinorEnds) { - a.minorTicks = this.snapMinorEnds(a) - } - }, - calculateLayout: function(b) { - var c = this, - a = b.attr; - if (a.length === 0) { - return null - } - if (a.majorTicks) { - c.calculateMajorTicks(b); - if (a.minorTicks) { - c.calculateMinorTicks(b) - } - } - }, - snapEnds: Ext.emptyFn, - trimByRange: function(b, f, i, a) { - var g = b.segmenter, - j = f.unit, - h = g.diff(f.from, i, j), - d = g.diff(f.from, a, j), - c = Math.max(0, Math.ceil(h / f.step)), - e = Math.min(f.steps, Math.floor(d / f.step)); - if (e < f.steps) { - f.to = g.add(f.from, e * f.step, j) - } - if (f.max > a) { - f.max = f.to - } - if (f.from < i) { - f.from = g.add(f.from, c * f.step, j); - while (f.from < i) { - c++; - f.from = g.add(f.from, f.step, j) - } - } - if (f.min < i) { - f.min = f.from - } - f.steps = e - c - } -}); -Ext.define("Ext.chart.axis.layout.Discrete", { - extend: "Ext.chart.axis.layout.Layout", - alias: "axisLayout.discrete", - isDiscrete: true, - processData: function() { - var f = this, - d = f.getAxis(), - c = d.boundSeries, - g = d.getDirection(), - b, e, a; - f.labels = []; - f.labelMap = {}; - for (b = 0, e = c.length; b < e; b++) { - a = c[b]; - if (a["get" + g + "Axis"]() === d) { - a["coordinate" + g]() - } - } - d.getSprites()[0].setAttributes({ - data: f.labels - }); - f.fireEvent("datachange", f.labels) - }, - calculateLayout: function(a) { - a.data = this.labels; - this.callParent([a]) - }, - calculateMajorTicks: function(a) { - var g = this, - f = a.attr, - d = a.data, - e = f.max - f.min, - j = e / Math.max(1, f.length) * (f.visibleMax - f.visibleMin), - i = f.min + e * f.visibleMin, - b = f.min + e * f.visibleMax, - h = f.estStepSize * j; - var c = g.snapEnds(a, Math.max(0, f.min), Math.min(f.max, d.length - 1), h); - if (c) { - g.trimByRange(a, c, i, b); - a.majorTicks = c - } - }, - snapEnds: function(e, d, a, b) { - b = Math.ceil(b); - var c = Math.floor((a - d) / b), - f = e.data; - return { - min: d, - max: a, - from: d, - to: c * b + d, - step: b, - steps: c, - unit: 1, - getLabel: function(g) { - return f[this.from + this.step * g] - }, - get: function(g) { - return this.from + this.step * g - } - } - }, - trimByRange: function(b, f, h, a) { - var i = f.unit, - g = Math.ceil((h - f.from) / i) * i, - d = Math.floor((a - f.from) / i) * i, - c = Math.max(0, Math.ceil(g / f.step)), - e = Math.min(f.steps, Math.floor(d / f.step)); - if (e < f.steps) { - f.to = e - } - if (f.max > a) { - f.max = f.to - } - if (f.from < h && f.step > 0) { - f.from = f.from + c * f.step * i; - while (f.from < h) { - c++; - f.from += f.step * i - } - } - if (f.min < h) { - f.min = f.from - } - f.steps = e - c - }, - getCoordFor: function(c, d, a, b) { - this.labels.push(c); - return this.labels.length - 1 - } -}); -Ext.define("Ext.chart.axis.layout.CombineDuplicate", { - extend: "Ext.chart.axis.layout.Discrete", - alias: "axisLayout.combineDuplicate", - getCoordFor: function(d, e, b, c) { - if (!(d in this.labelMap)) { - var a = this.labelMap[d] = this.labels.length; - this.labels.push(d); - return a - } - return this.labelMap[d] - } -}); -Ext.define("Ext.chart.axis.layout.Continuous", { - extend: "Ext.chart.axis.layout.Layout", - alias: "axisLayout.continuous", - isContinuous: true, - config: { - adjustMinimumByMajorUnit: false, - adjustMaximumByMajorUnit: false - }, - getCoordFor: function(c, d, a, b) { - return +c - }, - snapEnds: function(a, d, i, h) { - var f = a.segmenter, - c = this.getAxis(), - l = c.getMajorTickSteps(), - e = l && f.exactStep ? f.exactStep(d, (i - d) / l) : f.preferredStep(d, h), - k = e.unit, - b = e.step, - j = f.align(d, b, k), - g = (l || f.diff(d, i, k)) + 1; - return { - min: f.from(d), - max: f.from(i), - from: j, - to: f.add(j, g * b, k), - step: b, - steps: g, - unit: k, - get: function(m) { - return f.add(this.from, this.step * m, k) - } - } - }, - snapMinorEnds: function(a) { - var e = a.majorTicks, - m = this.getAxis().getMinorTickSteps(), - f = a.segmenter, - d = e.min, - i = e.max, - k = e.from, - l = e.unit, - b = e.step / m, - n = b * l.scale, - j = k - d, - c = Math.floor(j / n), - h = c + Math.floor((i - e.to) / n) + 1, - g = e.steps * m + h; - return { - min: d, - max: i, - from: d + j % n, - to: f.add(k, g * b, l), - step: b, - steps: g, - unit: l, - get: function(o) { - return (o % m + c + 1 !== 0) ? f.add(this.from, this.step * o, l) : null - } - } - } -}); -Ext.define("Ext.chart.axis.Axis", { - xtype: "axis", - mixins: { - observable: "Ext.mixin.Observable" - }, - requires: ["Ext.chart.axis.sprite.Axis", "Ext.chart.axis.segmenter.*", "Ext.chart.axis.layout.*"], - isAxis: true, - config: { - position: "bottom", - fields: [], - label: undefined, - grid: false, - limits: null, - renderer: null, - chart: null, - style: null, - margin: 0, - titleMargin: 4, - background: null, - minimum: NaN, - maximum: NaN, - reconcileRange: false, - minZoom: 1, - maxZoom: 10000, - layout: "continuous", - segmenter: "numeric", - hidden: false, - majorTickSteps: 0, - minorTickSteps: 0, - adjustByMajorUnit: true, - title: null, - increment: 0.5, - length: 0, - center: null, - radius: null, - totalAngle: Math.PI, - rotation: null, - labelInSpan: null, - visibleRange: [0, 1], - needHighPrecision: false, - linkedTo: null, - floating: null - }, - titleOffset: 0, - spriteAnimationCount: 0, - prevMin: 0, - prevMax: 1, - boundSeries: [], - sprites: null, - surface: null, - range: null, - xValues: [], - yValues: [], - masterAxis: null, - applyRotation: function(b) { - var a = Math.PI * 2; - return (b % a + Math.PI) % a - Math.PI - }, - updateRotation: function(b) { - var c = this.getSprites(), - a = this.getPosition(); - if (!this.getHidden() && a === "angular" && c[0]) { - c[0].setAttributes({ - baseRotation: b - }) - } - }, - applyTitle: function(c, b) { - var a; - if (Ext.isString(c)) { - c = { - text: c - } - } - if (!b) { - b = Ext.create("sprite.text", c); - if ((a = this.getSurface())) { - a.add(b) - } - } else { - b.setAttributes(c) - } - return b - }, - applyFloating: function(b, a) { - if (b === null) { - b = { - value: null, - alongAxis: null - } - } else { - if (Ext.isNumber(b)) { - b = { - value: b, - alongAxis: null - } - } - } - if (Ext.isObject(b)) { - if (a && a.alongAxis) { - delete this.getChart().getAxis(a.alongAxis).floatingAxes[this.getId()] - } - return b - } - return a - }, - constructor: function(a) { - var b = this, - c; - b.sprites = []; - b.labels = []; - b.floatingAxes = {}; - a = a || {}; - if (a.position === "angular") { - a.style = a.style || {}; - a.style.estStepSize = 1 - } - if ("id" in a) { - c = a.id - } else { - if ("id" in b.config) { - c = b.config.id - } else { - c = b.getId() - } - } - b.setId(c); - b.mixins.observable.constructor.apply(b, arguments) - }, - getAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "vertical"; - case "top": - case "bottom": - return "horizontal"; - case "radial": - return "radial"; - case "angular": - return "angular" - } - }, - getGridAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "horizontal"; - case "top": - case "bottom": - return "vertical"; - case "radial": - return "circular"; - case "angular": - return "radial" - } - }, - getSurface: function() { - var e = this, - d = e.getChart(); - if (d && !e.surface) { - var b = e.surface = d.getSurface(e.getId(), "axis"), - c = e.gridSurface = d.getSurface("main"), - a = e.getSprites()[0], - f = e.getGridAlignment(); - c.waitFor(b); - e.getGrid(); - if (e.getLimits() && f) { - f = f.replace("3d", ""); - e.limits = { - surface: d.getSurface("overlay"), - lines: new Ext.chart.Markers(), - titles: new Ext.draw.sprite.Instancing() - }; - e.limits.lines.setTemplate({ - xclass: "grid." + f - }); - e.limits.lines.getTemplate().setAttributes({ - strokeStyle: "black" - }, true); - e.limits.surface.add(e.limits.lines); - a.bindMarker(f + "-limit-lines", e.limits.lines); - e.limitTitleTpl = new Ext.draw.sprite.Text(); - e.limits.titles.setTemplate(e.limitTitleTpl); - e.limits.surface.add(e.limits.titles); - d.on("redraw", e.renderLimits, e) - } - } - return e.surface - }, - applyGrid: function(a) { - if (a === true) { - return {} - } - return a - }, - updateGrid: function(b) { - var e = this, - d = e.getChart(); - if (!d) { - e.on({ - chartattached: Ext.bind(e.updateGrid, e, [b]), - single: true - }); - return - } - var c = e.gridSurface, - a = e.getSprites()[0], - f = e.getGridAlignment(), - g; - if (b) { - g = e.gridSpriteEven; - if (!g) { - g = e.gridSpriteEven = new Ext.chart.Markers(); - g.setTemplate({ - xclass: "grid." + f - }); - c.add(g); - a.bindMarker(f + "-even", g) - } - if (Ext.isObject(b)) { - g.getTemplate().setAttributes(b); - if (Ext.isObject(b.even)) { - g.getTemplate().setAttributes(b.even) - } - } - g = e.gridSpriteOdd; - if (!g) { - g = e.gridSpriteOdd = new Ext.chart.Markers(); - g.setTemplate({ - xclass: "grid." + f - }); - c.add(g); - a.bindMarker(f + "-odd", g) - } - if (Ext.isObject(b)) { - g.getTemplate().setAttributes(b); - if (Ext.isObject(b.odd)) { - g.getTemplate().setAttributes(b.odd) - } - } - } - }, - renderLimits: function() { - this.getSprites()[0].renderLimits() - }, - getCoordFor: function(c, d, a, b) { - return this.getLayout().getCoordFor(c, d, a, b) - }, - applyPosition: function(a) { - return a.toLowerCase() - }, - applyLength: function(b, a) { - return b > 0 ? b : a - }, - applyLabel: function(b, a) { - if (!a) { - a = new Ext.draw.sprite.Text({}) - } - if (this.limitTitleTpl) { - this.limitTitleTpl.setAttributes(b) - } - a.setAttributes(b); - return a - }, - applyLayout: function(b, a) { - b = Ext.factory(b, null, a, "axisLayout"); - b.setAxis(this); - return b - }, - applySegmenter: function(a, b) { - a = Ext.factory(a, null, b, "segmenter"); - a.setAxis(this); - return a - }, - updateMinimum: function() { - this.range = null - }, - updateMaximum: function() { - this.range = null - }, - hideLabels: function() { - this.getSprites()[0].setDirty(true); - this.setLabel({ - hidden: true - }) - }, - showLabels: function() { - this.getSprites()[0].setDirty(true); - this.setLabel({ - hidden: false - }) - }, - renderFrame: function() { - this.getSurface().renderFrame() - }, - updateChart: function(d, b) { - var c = this, - a; - if (b) { - b.unregister(c); - b.un("serieschange", c.onSeriesChange, c); - b.un("redraw", c.renderLimits, c); - c.linkAxis(); - c.fireEvent("chartdetached", b, c) - } - if (d) { - d.on("serieschange", c.onSeriesChange, c); - c.surface = null; - a = c.getSurface(); - c.getLabel().setSurface(a); - a.add(c.getSprites()); - a.add(c.getTitle()); - d.register(c); - c.fireEvent("chartattached", d, c) - } - }, - applyBackground: function(a) { - var b = Ext.ClassManager.getByAlias("sprite.rect"); - return b.def.normalize(a) - }, - processData: function() { - this.getLayout().processData(); - this.range = null - }, - getDirection: function() { - return this.getChart().getDirectionForAxis(this.getPosition()) - }, - isSide: function() { - var a = this.getPosition(); - return a === "left" || a === "right" - }, - applyFields: function(a) { - return Ext.Array.from(a) - }, - applyVisibleRange: function(a, c) { - this.getChart(); - if (a[0] > a[1]) { - var b = a[0]; - a[0] = a[1]; - a[0] = b - } - if (a[1] === a[0]) { - a[1] += 1 / this.getMaxZoom() - } - if (a[1] > a[0] + 1) { - a[0] = 0; - a[1] = 1 - } else { - if (a[0] < 0) { - a[1] -= a[0]; - a[0] = 0 - } else { - if (a[1] > 1) { - a[0] -= a[1] - 1; - a[1] = 1 - } - } - } - if (c && a[0] === c[0] && a[1] === c[1]) { - return undefined - } - return a - }, - updateVisibleRange: function(a) { - this.fireEvent("visiblerangechange", this, a) - }, - onSeriesChange: function(e) { - var f = this, - b = e.getSeries(), - j = "get" + f.getDirection() + "Axis", - g = [], - c, d = b.length, - a, h; - for (c = 0; c < d; c++) { - if (this === b[c][j]()) { - g.push(b[c]) - } - } - f.boundSeries = g; - a = f.getLinkedTo(); - h = !Ext.isEmpty(a) && e.getAxis(a); - if (h) { - f.linkAxis(h) - } else { - f.getLayout().processData() - } - }, - linkAxis: function(a) { - var c = this; - - function b(f, d, e) { - e.getLayout()[f]("datachange", "onDataChange", d); - e[f]("rangechange", "onMasterAxisRangeChange", d) - } - if (c.masterAxis) { - b("un", c, c.masterAxis); - c.masterAxis = null - } - if (a) { - if (a.type !== this.type) { - Ext.Error.raise("Linked axes must be of the same type.") - } - b("on", c, a); - c.onDataChange(a.getLayout().labels); - c.onMasterAxisRangeChange(a, a.range); - c.setStyle(Ext.apply({}, c.config.style, a.config.style)); - c.setTitle(Ext.apply({}, c.config.title, a.config.title)); - c.setLabel(Ext.apply({}, c.config.label, a.config.label)); - c.masterAxis = a - } - }, - onDataChange: function(a) { - this.getLayout().labels = a - }, - onMasterAxisRangeChange: function(b, a) { - this.range = a - }, - applyRange: function(a) { - if (!a) { - return this.dataRange.slice(0) - } else { - return [a[0] === null ? this.dataRange[0] : a[0], a[1] === null ? this.dataRange[1] : a[1]] - } - }, - getRange: function() { - var m = this; - if (m.range) { - return m.range - } else { - if (m.masterAxis) { - return m.masterAxis.range - } - } - if (Ext.isNumber(m.getMinimum() + m.getMaximum())) { - return m.range = [m.getMinimum(), m.getMaximum()] - } - var d = Infinity, - n = -Infinity, - o = m.boundSeries, - h = m.getLayout(), - l = m.getSegmenter(), - p = m.getVisibleRange(), - b = "get" + m.getDirection() + "Range", - a, j, g, f, e, k; - for (e = 0, k = o.length; e < k; e++) { - f = o[e]; - var c = f[b](); - if (c) { - if (c[0] < d) { - d = c[0] - } - if (c[1] > n) { - n = c[1] - } - } - } - if (!isFinite(n)) { - n = m.prevMax - } - if (!isFinite(d)) { - d = m.prevMin - } - if (m.getLabelInSpan() || d === n) { - n += m.getIncrement(); - d -= m.getIncrement() - } - if (Ext.isNumber(m.getMinimum())) { - d = m.getMinimum() - } else { - m.prevMin = d - } - if (Ext.isNumber(m.getMaximum())) { - n = m.getMaximum() - } else { - m.prevMax = n - } - m.range = [Ext.Number.correctFloat(d), Ext.Number.correctFloat(n)]; - if (m.getReconcileRange()) { - m.reconcileRange() - } - if (m.getAdjustByMajorUnit() && l.adjustByMajorUnit && !m.getMajorTickSteps()) { - j = Ext.Object.chain(m.getSprites()[0].attr); - j.min = m.range[0]; - j.max = m.range[1]; - j.visibleMin = p[0]; - j.visibleMax = p[1]; - a = { - attr: j, - segmenter: l - }; - h.calculateLayout(a); - g = a.majorTicks; - if (g) { - l.adjustByMajorUnit(g.step, g.unit.scale, m.range); - j.min = m.range[0]; - j.max = m.range[1]; - delete a.majorTicks; - h.calculateLayout(a); - g = a.majorTicks; - l.adjustByMajorUnit(g.step, g.unit.scale, m.range) - } else { - if (!m.hasClearRangePending) { - m.hasClearRangePending = true; - m.getChart().on("layout", "clearRange", m) - } - } - } - if (!Ext.Array.equals(m.range, m.oldRange || [])) { - m.fireEvent("rangechange", m, m.range); - m.oldRange = m.range - } - return m.range - }, - clearRange: function() { - delete this.hasClearRangePending; - this.range = null - }, - reconcileRange: function() { - var e = this, - g = e.getChart().getAxes(), - f = e.getDirection(), - b, d, c, a; - if (!g) { - return - } - for (b = 0, d = g.length; b < d; b++) { - c = g[b]; - a = c.getRange(); - if (c === e || c.getDirection() !== f || !a || !c.getReconcileRange()) { - continue - } - if (a[0] < e.range[0]) { - e.range[0] = a[0] - } - if (a[1] > e.range[1]) { - e.range[1] = a[1] - } - } - }, - applyStyle: function(c, b) { - var a = Ext.ClassManager.getByAlias("sprite." + this.seriesType); - if (a && a.def) { - c = a.def.normalize(c) - } - b = Ext.apply(b || {}, c); - return b - }, - themeOnlyIfConfigured: { - grid: true - }, - updateTheme: function(d) { - var i = this, - k = d.getAxis(), - e = i.getPosition(), - o = i.getInitialConfig(), - c = i.defaultConfig, - g = i.getConfigurator().configs, - a = k.defaults, - n = k[e], - h = i.themeOnlyIfConfigured, - l, j, p, b, m, f; - k = Ext.merge({}, a, n); - for (l in k) { - j = k[l]; - f = g[l]; - if (j !== null && j !== undefined && f) { - m = o[l]; - p = Ext.isObject(j); - b = m === c[l]; - if (p) { - if (b && h[l]) { - continue - } - j = Ext.merge({}, j, m) - } - if (b || p) { - i[f.names.set](j) - } - } - } - }, - updateCenter: function(b) { - var e = this.getSprites(), - a = e[0], - d = b[0], - c = b[1]; - if (a) { - a.setAttributes({ - centerX: d, - centerY: c - }) - } - if (this.gridSpriteEven) { - this.gridSpriteEven.getTemplate().setAttributes({ - translationX: d, - translationY: c, - rotationCenterX: d, - rotationCenterY: c - }) - } - if (this.gridSpriteOdd) { - this.gridSpriteOdd.getTemplate().setAttributes({ - translationX: d, - translationY: c, - rotationCenterX: d, - rotationCenterY: c - }) - } - }, - getSprites: function() { - if (!this.getChart()) { - return - } - var i = this, - e = i.getRange(), - f = i.getPosition(), - g = i.getChart(), - c = g.getAnimation(), - d, a, b = i.getLength(), - h = i.superclass; - if (c === false) { - c = { - duration: 0 - } - } - if (e) { - a = Ext.applyIf({ - position: f, - axis: i, - min: e[0], - max: e[1], - length: b, - grid: i.getGrid(), - hidden: i.getHidden(), - titleOffset: i.titleOffset, - layout: i.getLayout(), - segmenter: i.getSegmenter(), - totalAngle: i.getTotalAngle(), - label: i.getLabel() - }, i.getStyle()); - if (!i.sprites.length) { - while (!h.xtype) { - h = h.superclass - } - d = Ext.create("sprite." + h.xtype, a); - d.fx.setCustomDurations({ - baseRotation: 0 - }); - d.fx.on("animationstart", "onAnimationStart", i); - d.fx.on("animationend", "onAnimationEnd", i); - d.setLayout(i.getLayout()); - d.setSegmenter(i.getSegmenter()); - d.setLabel(i.getLabel()); - i.sprites.push(d); - i.updateTitleSprite() - } else { - d = i.sprites[0]; - d.setAnimation(c); - d.setAttributes(a) - } - if (i.getRenderer()) { - d.setRenderer(i.getRenderer()) - } - } - return i.sprites - }, - updateTitleSprite: function() { - var f = this, - b = f.getLength(); - if (!f.sprites[0] || !Ext.isNumber(b)) { - return - } - var h = this.sprites[0].thickness, - a = f.getSurface(), - g = f.getTitle(), - e = f.getPosition(), - c = f.getMargin(), - i = f.getTitleMargin(), - d = a.roundPixel(b / 2); - if (g) { - switch (e) { - case "top": - g.setAttributes({ - x: d, - y: c + i / 2, - textBaseline: "top", - textAlign: "center" - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().height + i; - break; - case "bottom": - g.setAttributes({ - x: d, - y: h + i / 2, - textBaseline: "top", - textAlign: "center" - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().height + i; - break; - case "left": - g.setAttributes({ - x: c + i / 2, - y: d, - textBaseline: "top", - textAlign: "center", - rotationCenterX: c + i / 2, - rotationCenterY: d, - rotationRads: -Math.PI / 2 - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().width + i; - break; - case "right": - g.setAttributes({ - x: h - c + i / 2, - y: d, - textBaseline: "bottom", - textAlign: "center", - rotationCenterX: h + i / 2, - rotationCenterY: d, - rotationRads: Math.PI / 2 - }, true); - g.applyTransformations(); - f.titleOffset = g.getBBox().width + i; - break - } - } - }, - onThicknessChanged: function() { - this.getChart().onThicknessChanged() - }, - getThickness: function() { - if (this.getHidden()) { - return 0 - } - return (this.sprites[0] && this.sprites[0].thickness || 1) + this.titleOffset + this.getMargin() - }, - onAnimationStart: function() { - this.spriteAnimationCount++; - if (this.spriteAnimationCount === 1) { - this.fireEvent("animationstart", this) - } - }, - onAnimationEnd: function() { - this.spriteAnimationCount--; - if (this.spriteAnimationCount === 0) { - this.fireEvent("animationend", this) - } - }, - getItemId: function() { - return this.getId() - }, - getAncestorIds: function() { - return [this.getChart().getId()] - }, - isXType: function(a) { - return a === "axis" - }, - resolveListenerScope: function(e) { - var d = this, - a = Ext._namedScopes[e], - c = d.getChart(), - b; - if (!a) { - b = c ? c.resolveListenerScope(e, false) : (e || d) - } else { - if (a.isThis) { - b = d - } else { - if (a.isController) { - b = c ? c.resolveListenerScope(e, false) : d - } else { - if (a.isSelf) { - b = c ? c.resolveListenerScope(e, false) : d; - if (b === c && !c.getInheritedConfig("defaultListenerScope")) { - b = d - } - } - } - } - } - return b - }, - destroy: function() { - var a = this; - a.setChart(null); - a.surface.destroy(); - a.surface = null; - a.callParent() - } -}); -Ext.define("Ext.chart.LegendBase", { - extend: "Ext.view.View", - config: { - tpl: ['
', '', '
', "', "{name}", "
", "
", "
"], - nodeContainerSelector: "div." + Ext.baseCSSPrefix + "legend-container", - itemSelector: "div." + Ext.baseCSSPrefix + "legend-item", - docked: "bottom" - }, - setDocked: function(d) { - var c = this, - a = c.ownerCt, - b; - c.docked = d; - switch (d) { - case "top": - case "bottom": - c.addCls(Ext.baseCSSPrefix + "horizontal"); - b = "hbox"; - break; - case "left": - case "right": - c.removeCls(Ext.baseCSSPrefix + "horizontal"); - b = "vbox"; - break - } - if (a) { - a.setDocked(d) - } - }, - setStore: function(a) { - this.bindStore(a) - }, - clearViewEl: function() { - this.callParent(arguments); - Ext.removeNode(this.getNodeContainer()) - }, - onItemClick: function(a, c, b, d) { - this.callParent(arguments); - this.toggleItem(b) - } -}); -Ext.define("Ext.chart.Legend", { - xtype: "legend", - extend: "Ext.chart.LegendBase", - config: { - baseCls: Ext.baseCSSPrefix + "legend", - padding: 5, - rect: null, - disableSelection: true, - toggleable: true - }, - toggleItem: function(c) { - if (!this.getToggleable()) { - return - } - var b = this.getStore(), - h = 0, - e, g = true, - d, f, a; - if (b) { - f = b.getCount(); - for (d = 0; d < f; d++) { - a = b.getAt(d); - if (a.get("disabled")) { - h++ - } - } - g = f - h > 1; - a = b.getAt(c); - if (a) { - e = a.get("disabled"); - if (e || g) { - a.set("disabled", !e) - } - } - } - } -}); -Ext.define("Ext.chart.AbstractChart", { - extend: "Ext.draw.Container", - requires: ["Ext.chart.theme.Default", "Ext.chart.series.Series", "Ext.chart.interactions.Abstract", "Ext.chart.axis.Axis", "Ext.data.StoreManager", "Ext.chart.Legend", "Ext.data.Store"], - isChart: true, - defaultBindProperty: "store", - config: { - store: "ext-empty-store", - theme: "default", - style: null, - animation: !Ext.isIE8, - series: [], - axes: [], - legend: null, - colors: null, - insetPadding: { - top: 10, - left: 10, - right: 10, - bottom: 10 - }, - background: null, - interactions: [], - mainRect: null, - resizeHandler: null, - highlightItem: null - }, - animationSuspendCount: 0, - chartLayoutSuspendCount: 0, - axisThicknessSuspendCount: 0, - isThicknessChanged: false, - surfaceZIndexes: { - background: 0, - main: 1, - grid: 2, - series: 3, - axis: 4, - chart: 5, - overlay: 6, - events: 7 - }, - constructor: function(a) { - var b = this; - b.itemListeners = {}; - b.surfaceMap = {}; - b.chartComponents = {}; - b.isInitializing = true; - b.suspendChartLayout(); - b.animationSuspendCount++; - b.callParent(arguments); - delete b.isInitializing; - b.getSurface("main"); - b.getSurface("chart").setFlipRtlText(b.getInherited().rtl); - b.getSurface("overlay").waitFor(b.getSurface("series")); - b.animationSuspendCount--; - b.resumeChartLayout() - }, - applyAnimation: function(a, b) { - if (!a) { - a = { - duration: 0 - } - } else { - if (a === true) { - a = { - easing: "easeInOut", - duration: 500 - } - } - } - return b ? Ext.apply({}, a, b) : a - }, - getAnimation: function() { - if (this.animationSuspendCount) { - return { - duration: 0 - } - } else { - return this.callParent() - } - }, - applyInsetPadding: function(b, a) { - if (!Ext.isObject(b)) { - return Ext.util.Format.parseBox(b) - } else { - if (!a) { - return b - } else { - return Ext.apply(a, b) - } - } - }, - suspendAnimation: function() { - var d = this, - c = d.getSeries(), - e = c.length, - b = -1, - a; - d.animationSuspendCount++; - if (d.animationSuspendCount === 1) { - while (++b < e) { - a = c[b]; - a.setAnimation(a.getAnimation()) - } - } - }, - resumeAnimation: function() { - var d = this, - c = d.getSeries(), - f = c.length, - b = -1, - a, e; - d.animationSuspendCount--; - if (d.animationSuspendCount === 0) { - while (++b < f) { - a = c[b]; - e = a.getAnimation(); - a.setAnimation(e.duration && e || d.getAnimation()) - } - } - }, - suspendChartLayout: function() { - this.chartLayoutSuspendCount++; - if (this.chartLayoutSuspendCount === 1) { - if (this.scheduledLayoutId) { - this.layoutInSuspension = true; - this.cancelChartLayout() - } else { - this.layoutInSuspension = false - } - } - }, - resumeChartLayout: function() { - this.chartLayoutSuspendCount--; - if (this.chartLayoutSuspendCount === 0) { - if (this.layoutInSuspension) { - this.scheduleLayout() - } - } - }, - cancelChartLayout: function() { - if (this.scheduledLayoutId) { - Ext.draw.Animator.cancel(this.scheduledLayoutId); - this.scheduledLayoutId = null - } - }, - scheduleLayout: function() { - var a = this; - if (a.allowSchedule() && !a.scheduledLayoutId) { - a.scheduledLayoutId = Ext.draw.Animator.schedule("doScheduleLayout", a) - } - }, - allowSchedule: function() { - return true - }, - doScheduleLayout: function() { - if (this.chartLayoutSuspendCount) { - this.layoutInSuspension = true - } else { - this.performLayout() - } - }, - suspendThicknessChanged: function() { - this.axisThicknessSuspendCount++ - }, - resumeThicknessChanged: function() { - if (this.axisThicknessSuspendCount > 0) { - this.axisThicknessSuspendCount--; - if (this.axisThicknessSuspendCount === 0 && this.isThicknessChanged) { - this.onThicknessChanged() - } - } - }, - onThicknessChanged: function() { - if (this.axisThicknessSuspendCount === 0) { - this.isThicknessChanged = false; - this.performLayout() - } else { - this.isThicknessChanged = true - } - }, - applySprites: function(b) { - var a = this.getSurface("chart"); - b = Ext.Array.from(b); - a.removeAll(true); - a.add(b); - return b - }, - initItems: function() { - var a = this.items, - b, d, c; - if (a && !a.isMixedCollection) { - this.items = []; - a = Ext.Array.from(a); - for (b = 0, d = a.length; b < d; b++) { - c = a[b]; - if (c.type) { - Ext.raise("To add custom sprites to the chart use the 'sprites' config.") - } else { - this.items.push(c) - } - } - } - this.callParent() - }, - applyBackground: function(c, e) { - var b = this.getSurface("background"), - d, a, f; - if (c) { - if (e) { - d = e.attr.width; - a = e.attr.height; - f = e.type === (c.type || "rect") - } - if (c.isSprite) { - e = c - } else { - if (c.type === "image" && Ext.isString(c.src)) { - if (f) { - e.setAttributes({ - src: c.src - }) - } else { - b.remove(e, true); - e = b.add(c) - } - } else { - if (f) { - e.setAttributes({ - fillStyle: c - }) - } else { - b.remove(e, true); - e = b.add({ - type: "rect", - fillStyle: c, - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0 - } - } - }) - } - } - } - } - if (d && a) { - e.setAttributes({ - width: d, - height: a - }) - } - e.setAnimation(this.getAnimation()); - return e - }, - getLegendStore: function() { - return this.legendStore - }, - refreshLegendStore: function() { - if (this.getLegendStore()) { - var d, e, c = this.getSeries(), - b, a = []; - if (c) { - for (d = 0, e = c.length; d < e; d++) { - b = c[d]; - if (b.getShowInLegend()) { - b.provideLegendInfo(a) - } - } - } - this.getLegendStore().setData(a) - } - }, - resetLegendStore: function() { - var c = this.getLegendStore(), - e, d, a, b; - if (c) { - e = this.getLegendStore().getData().items; - for (d = 0, a = e.length; d < a; d++) { - b = e[d]; - b.beginEdit(); - b.set("disabled", false); - b.commit() - } - } - }, - onUpdateLegendStore: function(b, a) { - var d = this.getSeries(), - c; - if (a && d) { - c = d.map[a.get("series")]; - if (c) { - c.setHiddenByIndex(a.get("index"), a.get("disabled")); - this.redraw() - } - } - }, - defaultResizeHandler: function(a) { - this.scheduleLayout(); - return false - }, - applyMainRect: function(a, b) { - if (!b) { - return a - } - this.getSeries(); - this.getAxes(); - if (a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]) { - return b - } else { - return a - } - }, - register: function(a) { - var b = this.chartComponents, - c = a.getId(); - b[c] = a - }, - unregister: function(a) { - var b = this.chartComponents, - c = a.getId(); - delete b[c] - }, - get: function(a) { - return this.chartComponents[a] - }, - getAxis: function(a) { - if (a instanceof Ext.chart.axis.Axis) { - return a - } else { - if (Ext.isNumber(a)) { - return this.getAxes()[a] - } else { - if (Ext.isString(a)) { - return this.get(a) - } - } - } - }, - getSurface: function(b, c) { - b = b || "main"; - c = c || b; - var d = this, - a = this.callParent([b]), - f = d.surfaceZIndexes, - e = d.surfaceMap; - if (c in f) { - a.element.setStyle("zIndex", f[c]) - } - if (!e[c]) { - e[c] = [] - } - if (Ext.Array.indexOf(e[c], a) < 0) { - a.type = c; - e[c].push(a); - a.on("destroy", d.forgetSurface, d) - } - return a - }, - forgetSurface: function(a) { - var d = this.surfaceMap; - if (!d || this.isDestroying) { - return - } - var c = d[a.type], - b = c ? Ext.Array.indexOf(c, a) : -1; - if (b >= 0) { - c.splice(b, 1) - } - }, - applyAxes: function(b, k) { - var l = this, - g = { - left: "right", - right: "left" - }, - m = [], - c, d, e, a, f, h, j; - l.animationSuspendCount++; - l.getStore(); - if (!k) { - k = []; - k.map = {} - } - j = k.map; - m.map = {}; - b = Ext.Array.from(b, true); - for (f = 0, h = b.length; f < h; f++) { - c = b[f]; - if (!c) { - continue - } - if (c instanceof Ext.chart.axis.Axis) { - d = j[c.getId()]; - c.setChart(l) - } else { - c = Ext.Object.chain(c); - e = c.linkedTo; - a = c.id; - if (Ext.isNumber(e)) { - c = Ext.merge({}, b[e], c) - } else { - if (Ext.isString(e)) { - Ext.Array.each(b, function(i) { - if (i.id === c.linkedTo) { - c = Ext.merge({}, i, c); - return false - } - }) - } - } - c.id = a; - c.chart = l; - if (l.getInherited().rtl) { - c.position = g[c.position] || c.position - } - a = c.getId && c.getId() || c.id; - c = Ext.factory(c, null, d = j[a], "axis") - } - if (c) { - m.push(c); - m.map[c.getId()] = c; - if (!d) { - c.on("animationstart", "onAnimationStart", l); - c.on("animationend", "onAnimationEnd", l) - } - } - } - for (f in j) { - if (!m.map[f]) { - j[f].destroy() - } - } - l.animationSuspendCount--; - return m - }, - updateAxes: function() { - if (!this.isDestroying) { - this.scheduleLayout() - } - }, - circularCopyArray: function(e, f, d) { - var c = [], - b, a = e && e.length; - if (a) { - for (b = 0; b < d; b++) { - c.push(e[(f + b) % a]) - } - } - return c - }, - circularCopyObject: function(f, g, d) { - var c = this, - b, e, a = {}; - if (d) { - for (b in f) { - if (f.hasOwnProperty(b)) { - e = f[b]; - if (Ext.isArray(e)) { - a[b] = c.circularCopyArray(e, g, d) - } else { - a[b] = e - } - } - } - } - return a - }, - getColors: function() { - var b = this, - a = b.config.colors, - c = b.getTheme(); - if (Ext.isArray(a) && a.length > 0) { - a = b.applyColors(a) - } - return a || (c && c.getColors()) - }, - applyColors: function(a) { - a = Ext.Array.map(a, function(b) { - if (Ext.isString(b)) { - return b - } else { - return b.toString() - } - }); - return a - }, - updateColors: function(c) { - var k = this, - e = k.getTheme(), - a = c || (e && e.getColors()), - l = 0, - f = k.getSeries(), - d = f && f.length, - g, j, b, h; - if (a.length) { - for (g = 0; g < d; g++) { - j = f[g]; - h = j.themeColorCount(); - b = k.circularCopyArray(a, l, h); - l += h; - j.updateChartColors(b) - } - } - k.refreshLegendStore() - }, - applyTheme: function(a) { - if (a && a.isTheme) { - return a - } - return Ext.Factory.chartTheme(a) - }, - updateTheme: function(g) { - var e = this, - f = e.getAxes(), - d = e.getSeries(), - a = e.getColors(), - c, b; - e.updateChartTheme(g); - for (b = 0; b < f.length; b++) { - f[b].updateTheme(g) - } - for (b = 0; b < d.length; b++) { - c = d[b]; - c.updateTheme(g) - } - e.updateSpriteTheme(g); - e.updateColors(a); - e.redraw() - }, - themeOnlyIfConfigured: {}, - updateChartTheme: function(c) { - var i = this, - k = c.getChart(), - n = i.getInitialConfig(), - b = i.defaultConfig, - e = i.getConfigurator().configs, - f = k.defaults, - g = k[i.xtype], - h = i.themeOnlyIfConfigured, - l, j, o, a, m, d; - k = Ext.merge({}, f, g); - for (l in k) { - j = k[l]; - d = e[l]; - if (j !== null && j !== undefined && d) { - m = n[l]; - o = Ext.isObject(j); - a = m === b[l]; - if (o) { - if (a && h[l]) { - continue - } - j = Ext.merge({}, j, m) - } - if (a || o) { - i[d.names.set](j) - } - } - } - }, - updateSpriteTheme: function(c) { - this.getSprites(); - var j = this, - e = j.getSurface("chart"), - h = e.getItems(), - m = c.getSprites(), - k, a, l, f, d, b, g; - for (b = 0, g = h.length; b < g; b++) { - k = h[b]; - a = m[k.type]; - if (a) { - f = {}; - d = k.type === "text"; - for (l in a) { - if (!(l in k.config)) { - if (!(d && l.indexOf("font") === 0 && k.config.font)) { - f[l] = a[l] - } - } - } - k.setAttributes(f) - } - } - }, - addSeries: function(b) { - var a = this.getSeries(); - Ext.Array.push(a, b); - this.setSeries(a) - }, - removeSeries: function(d) { - d = Ext.Array.from(d); - var b = this.getSeries(), - f = [], - a = d.length, - g = {}, - c, e; - for (c = 0; c < a; c++) { - e = d[c]; - if (typeof e !== "string") { - e = e.getId() - } - g[e] = true - } - for (c = 0, a = b.length; c < a; c++) { - if (!g[b[c].getId()]) { - f.push(b[c]) - } - } - this.setSeries(f) - }, - applySeries: function(e, d) { - var g = this, - j = [], - h, a, c, f, b; - g.animationSuspendCount++; - g.getAxes(); - if (d) { - h = d.map - } else { - d = []; - h = d.map = {} - } - j.map = {}; - e = Ext.Array.from(e, true); - for (c = 0, f = e.length; c < f; c++) { - b = e[c]; - if (!b) { - continue - } - a = h[b.getId && b.getId() || b.id]; - if (b instanceof Ext.chart.series.Series) { - if (a && a !== b) { - a.destroy() - } - b.setChart(g) - } else { - if (Ext.isObject(b)) { - if (a) { - a.setConfig(b); - b = a - } else { - if (Ext.isString(b)) { - b = { - type: b - } - } - b.chart = g; - b = Ext.create(b.xclass || ("series." + b.type), b); - b.on("animationstart", "onAnimationStart", g); - b.on("animationend", "onAnimationEnd", g) - } - } - } - j.push(b); - j.map[b.getId()] = b - } - for (c in h) { - if (!j.map[h[c].getId()]) { - h[c].destroy() - } - } - g.animationSuspendCount--; - return j - }, - applyLegend: function(b, a) { - return Ext.factory(b, Ext.chart.Legend, a) - }, - updateLegend: function(b, a) { - if (a) { - a.destroy() - } - if (b) { - this.getItems(); - this.legendStore = new Ext.data.Store({ - autoDestroy: true, - fields: ["id", "name", "mark", "disabled", "series", "index"] - }); - b.setStore(this.legendStore); - this.refreshLegendStore(); - this.legendStore.on("update", "onUpdateLegendStore", this) - } - }, - updateSeries: function(b, a) { - var c = this; - if (c.isDestroying) { - return - } - c.animationSuspendCount++; - c.fireEvent("serieschange", c, b, a); - c.refreshLegendStore(); - if (!Ext.isEmpty(b)) { - c.updateTheme(c.getTheme()) - } - c.scheduleLayout(); - c.animationSuspendCount-- - }, - applyInteractions: function(h, d) { - if (!d) { - d = []; - d.map = {} - } - var g = this, - a = [], - c = d.map, - e, f, b; - a.map = {}; - h = Ext.Array.from(h, true); - for (e = 0, f = h.length; e < f; e++) { - b = h[e]; - if (!b) { - continue - } - b = Ext.factory(b, null, c[b.getId && b.getId() || b.id], "interaction"); - if (b) { - b.setChart(g); - a.push(b); - a.map[b.getId()] = b - } - } - for (e in c) { - if (!a.map[e]) { - c[e].destroy() - } - } - return a - }, - getInteraction: function(e) { - var f = this.getInteractions(), - a = f && f.length, - c = null, - b, d; - if (a) { - for (d = 0; d < a; ++d) { - b = f[d]; - if (b.type === e) { - c = b; - break - } - } - } - return c - }, - applyStore: function(a) { - return a && Ext.StoreManager.lookup(a) - }, - updateStore: function(a, c) { - var b = this; - if (c) { - c.un({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: b, - order: "after" - }); - if (c.autoDestroy) { - c.destroy() - } - } - if (a) { - a.on({ - datachanged: "onDataChanged", - update: "onDataChanged", - scope: b, - order: "after" - }) - } - b.fireEvent("storechange", b, a, c); - b.onDataChanged() - }, - redraw: function() { - this.fireEvent("redraw", this) - }, - performLayout: function() { - var d = this, - b = d.getChartSize(true), - c = [0, 0, b.width, b.height], - a = d.getBackground(); - d.hasFirstLayout = true; - d.fireEvent("layout", d); - d.cancelChartLayout(); - d.getSurface("background").setRect(c); - d.getSurface("chart").setRect(c); - a.setAttributes({ - width: b.width, - height: b.height - }) - }, - getChartSize: function(b) { - var a = this; - if (b) { - a.chartSize = null - } - return a.chartSize || (a.chartSize = a.innerElement.getSize()) - }, - getEventXY: function(a) { - return this.getSurface().getEventXY(a) - }, - getItemForPoint: function(h, g) { - var f = this, - a = f.getSeries(), - e = f.getMainRect(), - d = a.length, - b = f.hasFirstLayout ? d - 1 : -1, - c, j; - if (!(e && h >= 0 && h <= e[2] && g >= 0 && g <= e[3])) { - return null - } - for (; b >= 0; b--) { - c = a[b]; - j = c.getItemForPoint(h, g); - if (j) { - return j - } - } - return null - }, - getItemsForPoint: function(h, g) { - var f = this, - a = f.getSeries(), - d = a.length, - b = f.hasFirstLayout ? d - 1 : -1, - e = [], - c, j; - for (; b >= 0; b--) { - c = a[b]; - j = c.getItemForPoint(h, g); - if (j) { - e.push(j) - } - } - return e - }, - onAnimationStart: function() { - this.fireEvent("animationstart", this) - }, - onAnimationEnd: function() { - this.fireEvent("animationend", this) - }, - onDataChanged: function() { - var d = this; - if (d.isInitializing) { - return - } - var c = d.getMainRect(), - a = d.getStore(), - b = d.getSeries(), - e = d.getAxes(); - if (!a || !e || !b) { - return - } - if (!c) { - d.on({ - redraw: d.onDataChanged, - scope: d, - single: true - }); - return - } - d.processData(); - d.redraw() - }, - recordCount: 0, - processData: function() { - var g = this, - e = g.getStore().getCount(), - c = g.getSeries(), - f = c.length, - d = false, - b = 0, - a; - for (; b < f; b++) { - a = c[b]; - a.processData(); - if (!d && a.isStoreDependantColorCount) { - d = true - } - } - if (d && e > g.recordCount) { - g.updateColors(g.getColors()); - g.recordCount = e - } - }, - bindStore: function(a) { - this.setStore(a) - }, - applyHighlightItem: function(f, a) { - if (f === a) { - return - } - if (Ext.isObject(f) && Ext.isObject(a)) { - var e = f, - d = a, - c = e.sprite && (e.sprite[0] || e.sprite), - b = d.sprite && (d.sprite[0] || d.sprite); - if (c === b && e.index === d.index) { - return - } - } - return f - }, - updateHighlightItem: function(b, a) { - if (a) { - a.series.setAttributesForItem(a, { - highlighted: false - }) - } - if (b) { - b.series.setAttributesForItem(b, { - highlighted: true - }); - this.fireEvent("itemhighlight", this, b, a) - } - this.fireEvent("itemhighlightchange", this, b, a) - }, - destroyChart: function() { - var f = this, - d = f.getLegend(), - g = f.getAxes(), - c = f.getSeries(), - h = f.getInteractions(), - b = [], - a, e; - f.surfaceMap = null; - for (a = 0, e = h.length; a < e; a++) { - h[a].destroy() - } - for (a = 0, e = g.length; a < e; a++) { - g[a].destroy() - } - for (a = 0, e = c.length; a < e; a++) { - c[a].destroy() - } - f.setInteractions(b); - f.setAxes(b); - f.setSeries(b); - if (d) { - d.destroy(); - f.setLegend(null) - } - f.legendStore = null; - f.setStore(null); - f.cancelChartLayout() - }, - getRefItems: function(b) { - var g = this, - e = g.getSeries(), - h = g.getAxes(), - a = g.getInteractions(), - c = [], - d, f; - for (d = 0, f = e.length; d < f; d++) { - c.push(e[d]); - if (e[d].getRefItems) { - c.push.apply(c, e[d].getRefItems(b)) - } - } - for (d = 0, f = h.length; d < f; d++) { - c.push(h[d]); - if (h[d].getRefItems) { - c.push.apply(c, h[d].getRefItems(b)) - } - } - for (d = 0, f = a.length; d < f; d++) { - c.push(a[d]); - if (a[d].getRefItems) { - c.push.apply(c, a[d].getRefItems(b)) - } - } - return c - } -}); -Ext.define("Ext.chart.overrides.AbstractChart", { - override: "Ext.chart.AbstractChart", - updateLegend: function(b, a) { - var c; - this.callParent([b, a]); - if (b) { - c = b.docked; - this.addDocked({ - dock: c, - xtype: "panel", - shrinkWrap: true, - scrollable: true, - layout: { - type: c === "top" || c === "bottom" ? "hbox" : "vbox", - pack: "center" - }, - items: b, - cls: Ext.baseCSSPrefix + "legend-panel" - }) - } - }, - performLayout: function() { - if (this.isVisible(true)) { - return this.callParent() - } - this.cancelChartLayout(); - return false - }, - afterComponentLayout: function(c, a, b, d) { - this.callParent([c, a, b, d]); - this.scheduleLayout() - }, - allowSchedule: function() { - return this.rendered - }, - onDestroy: function() { - this.destroyChart(); - this.callParent(arguments) - } -}); -Ext.define("Ext.chart.grid.HorizontalGrid", { - extend: "Ext.draw.sprite.Sprite", - alias: "grid.horizontal", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number" - }, - defaults: { - x: 0, - y: 0, - width: 1, - height: 1, - strokeStyle: "#DDD" - } - } - }, - render: function(b, c, e) { - var a = this.attr, - f = b.roundPixel(a.y), - d = c.lineWidth * 0.5; - c.beginPath(); - c.rect(e[0] - b.matrix.getDX(), f + d, +e[2], a.height); - c.fill(); - c.beginPath(); - c.moveTo(e[0] - b.matrix.getDX(), f + d); - c.lineTo(e[0] + e[2] - b.matrix.getDX(), f + d); - c.stroke() - } -}); -Ext.define("Ext.chart.grid.VerticalGrid", { - extend: "Ext.draw.sprite.Sprite", - alias: "grid.vertical", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number" - }, - defaults: { - x: 0, - y: 0, - width: 1, - height: 1, - strokeStyle: "#DDD" - } - } - }, - render: function(c, d, f) { - var b = this.attr, - a = c.roundPixel(b.x), - e = d.lineWidth * 0.5; - d.beginPath(); - d.rect(a - e, f[1] - c.matrix.getDY(), b.width, f[3]); - d.fill(); - d.beginPath(); - d.moveTo(a - e, f[1] - c.matrix.getDY()); - d.lineTo(a - e, f[1] + f[3] - c.matrix.getDY()); - d.stroke() - } -}); -Ext.define("Ext.chart.CartesianChart", { - extend: "Ext.chart.AbstractChart", - alternateClassName: "Ext.chart.Chart", - requires: ["Ext.chart.grid.HorizontalGrid", "Ext.chart.grid.VerticalGrid"], - xtype: ["cartesian", "chart"], - isCartesian: true, - config: { - flipXY: false, - innerRect: [0, 0, 1, 1], - innerPadding: { - top: 0, - left: 0, - right: 0, - bottom: 0 - } - }, - applyInnerPadding: function(b, a) { - if (!Ext.isObject(b)) { - return Ext.util.Format.parseBox(b) - } else { - if (!a) { - return b - } else { - return Ext.apply(a, b) - } - } - }, - getDirectionForAxis: function(a) { - var b = this.getFlipXY(); - if (a === "left" || a === "right") { - if (b) { - return "X" - } else { - return "Y" - } - } else { - if (b) { - return "Y" - } else { - return "X" - } - } - }, - performLayout: function() { - var A = this; - A.animationSuspendCount++; - if (A.callParent() === false) { - --A.animationSuspendCount; - return - } - A.suspendThicknessChanged(); - var d = A.getSurface("chart").getRect(), - o = d[2], - n = d[3], - z = A.getAxes(), - b, q = A.getSeries(), - h, l, a, f = A.getInsetPadding(), - v = A.getInnerPadding(), - r, c, e = Ext.apply({}, f), - u, p, s, k, m, y, t, x, g, j = A.getInherited().rtl, - w = A.getFlipXY(); - if (o <= 0 || n <= 0) { - return - } - for (x = 0; x < z.length; x++) { - b = z[x]; - l = b.getSurface(); - m = b.getFloating(); - y = m ? m.value : null; - a = b.getThickness(); - switch (b.getPosition()) { - case "top": - l.setRect([0, e.top + 1, o, a]); - break; - case "bottom": - l.setRect([0, n - (e.bottom + a), o, a]); - break; - case "left": - l.setRect([e.left, 0, a, n]); - break; - case "right": - l.setRect([o - (e.right + a), 0, a, n]); - break - } - if (y === null) { - e[b.getPosition()] += a - } - } - o -= e.left + e.right; - n -= e.top + e.bottom; - u = [e.left, e.top, o, n]; - e.left += v.left; - e.top += v.top; - e.right += v.right; - e.bottom += v.bottom; - p = o - v.left - v.right; - s = n - v.top - v.bottom; - A.setInnerRect([e.left, e.top, p, s]); - if (p <= 0 || s <= 0) { - return - } - A.setMainRect(u); - A.getSurface().setRect(u); - for (x = 0, g = A.surfaceMap.grid && A.surfaceMap.grid.length; x < g; x++) { - c = A.surfaceMap.grid[x]; - c.setRect(u); - c.matrix.set(1, 0, 0, 1, v.left, v.top); - c.matrix.inverse(c.inverseMatrix) - } - for (x = 0; x < z.length; x++) { - b = z[x]; - l = b.getSurface(); - t = l.matrix; - k = t.elements; - switch (b.getPosition()) { - case "top": - case "bottom": - k[4] = e.left; - b.setLength(p); - break; - case "left": - case "right": - k[5] = e.top; - b.setLength(s); - break - } - b.updateTitleSprite(); - t.inverse(l.inverseMatrix) - } - for (x = 0, g = q.length; x < g; x++) { - h = q[x]; - r = h.getSurface(); - r.setRect(u); - if (w) { - if (j) { - r.matrix.set(0, -1, -1, 0, v.left + p, v.top + s) - } else { - r.matrix.set(0, -1, 1, 0, v.left, v.top + s) - } - } else { - r.matrix.set(1, 0, 0, -1, v.left, v.top + s) - } - r.matrix.inverse(r.inverseMatrix); - h.getOverlaySurface().setRect(u) - } - A.redraw(); - A.animationSuspendCount--; - A.resumeThicknessChanged() - }, - refloatAxes: function() { - var h = this, - g = h.getAxes(), - o = (g && g.length) || 0, - c, d, n, f, l, b, k, r = h.getChartSize(), - q = h.getInsetPadding(), - p = h.getInnerPadding(), - a = r.width - q.left - q.right, - m = r.height - q.top - q.bottom, - j, e; - for (e = 0; e < o; e++) { - c = g[e]; - f = c.getFloating(); - l = f ? f.value : null; - if (l === null) { - delete c.floatingAtCoord; - continue - } - d = c.getSurface(); - n = d.getRect(); - if (!n) { - continue - } - n = n.slice(); - b = h.getAxis(f.alongAxis); - if (b) { - j = b.getAlignment() === "horizontal"; - if (Ext.isString(l)) { - l = b.getCoordFor(l) - } - b.floatingAxes[c.getId()] = l; - k = b.getSprites()[0].attr.matrix; - if (j) { - l = l * k.getXX() + k.getDX(); - c.floatingAtCoord = l + p.left + p.right - } else { - l = l * k.getYY() + k.getDY(); - c.floatingAtCoord = l + p.top + p.bottom - } - } else { - j = c.getAlignment() === "horizontal"; - if (j) { - c.floatingAtCoord = l + p.top + p.bottom - } else { - c.floatingAtCoord = l + p.left + p.right - } - l = d.roundPixel(0.01 * l * (j ? m : a)) - } - switch (c.getPosition()) { - case "top": - n[1] = q.top + p.top + l - n[3] + 1; - break; - case "bottom": - n[1] = q.top + p.top + (b ? l : m - l); - break; - case "left": - n[0] = q.left + p.left + l - n[2]; - break; - case "right": - n[0] = q.left + p.left + (b ? l : a - l) - 1; - break - } - d.setRect(n) - } - }, - redraw: function() { - var C = this, - r = C.getSeries(), - z = C.getAxes(), - b = C.getMainRect(), - p, t, w = C.getInnerPadding(), - f, l, s, e, q, A, v, g, d, c, a, k, n, y = C.getFlipXY(), - x = 1000, - m, u, h, o, B; - if (!b) { - return - } - p = b[2] - w.left - w.right; - t = b[3] - w.top - w.bottom; - for (A = 0; A < r.length; A++) { - h = r[A]; - if ((c = h.getXAxis())) { - n = c.getVisibleRange(); - l = c.getRange(); - l = [l[0] + (l[1] - l[0]) * n[0], l[0] + (l[1] - l[0]) * n[1]] - } else { - l = h.getXRange() - } - if ((a = h.getYAxis())) { - n = a.getVisibleRange(); - s = a.getRange(); - s = [s[0] + (s[1] - s[0]) * n[0], s[0] + (s[1] - s[0]) * n[1]] - } else { - s = h.getYRange() - } - q = { - visibleMinX: l[0], - visibleMaxX: l[1], - visibleMinY: s[0], - visibleMaxY: s[1], - innerWidth: p, - innerHeight: t, - flipXY: y - }; - f = h.getSprites(); - for (v = 0, g = f.length; v < g; v++) { - o = f[v]; - m = o.attr.zIndex; - if (m < x) { - m += (A + 1) * 100 + x; - o.attr.zIndex = m; - B = o.getMarker("items"); - if (B) { - u = B.attr.zIndex; - if (u === Number.MAX_VALUE) { - B.attr.zIndex = m - } else { - if (u < x) { - B.attr.zIndex = m + u - } - } - } - } - o.setAttributes(q, true) - } - } - for (A = 0; A < z.length; A++) { - d = z[A]; - e = d.isSide(); - f = d.getSprites(); - k = d.getRange(); - n = d.getVisibleRange(); - q = { - dataMin: k[0], - dataMax: k[1], - visibleMin: n[0], - visibleMax: n[1] - }; - if (e) { - q.length = t; - q.startGap = w.bottom; - q.endGap = w.top - } else { - q.length = p; - q.startGap = w.left; - q.endGap = w.right - } - for (v = 0, g = f.length; v < g; v++) { - f[v].setAttributes(q, true) - } - } - C.renderFrame(); - C.callParent(arguments) - }, - renderFrame: function() { - this.refloatAxes(); - this.callParent() - } -}); -Ext.define("Ext.chart.grid.CircularGrid", { - extend: "Ext.draw.sprite.Circle", - alias: "grid.circular", - inheritableStatics: { - def: { - defaults: { - r: 1, - strokeStyle: "#DDD" - } - } - } -}); -Ext.define("Ext.chart.grid.RadialGrid", { - extend: "Ext.draw.sprite.Path", - alias: "grid.radial", - inheritableStatics: { - def: { - processors: { - startRadius: "number", - endRadius: "number" - }, - defaults: { - startRadius: 0, - endRadius: 1, - scalingCenterX: 0, - scalingCenterY: 0, - strokeStyle: "#DDD" - }, - triggers: { - startRadius: "path,bbox", - endRadius: "path,bbox" - } - } - }, - render: function() { - this.callParent(arguments) - }, - updatePath: function(d, a) { - var b = a.startRadius, - c = a.endRadius; - d.moveTo(b, 0); - d.lineTo(c, 0) - } -}); -Ext.define("Ext.chart.PolarChart", { - extend: "Ext.chart.AbstractChart", - requires: ["Ext.chart.grid.CircularGrid", "Ext.chart.grid.RadialGrid"], - xtype: "polar", - isPolar: true, - config: { - center: [0, 0], - radius: 0, - innerPadding: 0 - }, - getDirectionForAxis: function(a) { - return a === "radial" ? "Y" : "X" - }, - applyCenter: function(a, b) { - if (b && a[0] === b[0] && a[1] === b[1]) { - return - } - return [+a[0], +a[1]] - }, - updateCenter: function(a) { - var g = this, - h = g.getAxes(), - d = g.getSeries(), - c, f, e, b; - for (c = 0, f = h.length; c < f; c++) { - e = h[c]; - e.setCenter(a) - } - for (c = 0, f = d.length; c < f; c++) { - b = d[c]; - b.setCenter(a) - } - }, - applyInnerPadding: function(b, a) { - return Ext.isNumber(b) ? b : a - }, - doSetSurfaceRect: function(b, c) { - var a = this.getMainRect(); - b.setRect(c); - b.matrix.set(1, 0, 0, 1, a[0] - c[0], a[1] - c[1]); - b.inverseMatrix.set(1, 0, 0, 1, c[0] - a[0], c[1] - a[1]) - }, - applyAxes: function(f, h) { - var e = this, - g = Ext.Array.from(e.config.series)[0], - b, d, c, a; - if (g.type === "radar" && f && f.length) { - for (b = 0, d = f.length; b < d; b++) { - c = f[b]; - if (c.position === "angular") { - a = true; - break - } - } - if (!a) { - f.push({ - type: "category", - position: "angular", - fields: g.xField || g.angleField, - style: { - estStepSize: 1 - }, - grid: true - }) - } - } - return this.callParent(arguments) - }, - performLayout: function() { - var F = this, - g = true; - try { - F.animationSuspendCount++; - if (this.callParent() === false) { - g = false; - return - } - F.suspendThicknessChanged(); - var h = F.getSurface("chart").getRect(), - v = F.getInsetPadding(), - G = F.getInnerPadding(), - l = Ext.apply({}, v), - d, s = h[2] - v.left - v.right, - r = h[3] - v.top - v.bottom, - x = [v.left, v.top, s, r], - u = F.getSeries(), - p, t = s - G * 2, - w = r - G * 2, - D = [t * 0.5 + G, w * 0.5 + G], - j = Math.min(t, w) * 0.5, - A = F.getAxes(), - f, a, k, m = [], - o = [], - E = j - G, - z, n, b, q, y, c, C; - F.setMainRect(x); - F.doSetSurfaceRect(F.getSurface(), x); - for (z = 0, n = F.surfaceMap.grid && F.surfaceMap.grid.length; z < n; z++) { - F.doSetSurfaceRect(F.surfaceMap.grid[z], h) - } - for (z = 0, n = A.length; z < n; z++) { - f = A[z]; - switch (f.getPosition()) { - case "angular": - m.push(f); - break; - case "radial": - o.push(f); - break - } - } - for (z = 0, n = m.length; z < n; z++) { - f = m[z]; - q = f.getFloating(); - y = q ? q.value : null; - F.doSetSurfaceRect(f.getSurface(), h); - a = f.getThickness(); - for (d in l) { - l[d] += a - } - s = h[2] - l.left - l.right; - r = h[3] - l.top - l.bottom; - b = Math.min(s, r) * 0.5; - if (z === 0) { - E = b - G - } - f.setMinimum(0); - f.setLength(b); - f.getSprites(); - k = f.sprites[0].attr.lineWidth * 0.5; - for (d in l) { - l[d] += k - } - } - for (z = 0, n = o.length; z < n; z++) { - f = o[z]; - F.doSetSurfaceRect(f.getSurface(), h); - f.setMinimum(0); - f.setLength(E); - f.getSprites() - } - for (z = 0, n = u.length; z < n; z++) { - p = u[z]; - if (p.type === "gauge" && !c) { - c = p - } else { - p.setRadius(E) - } - F.doSetSurfaceRect(p.getSurface(), x) - } - F.doSetSurfaceRect(F.getSurface("overlay"), h); - if (c) { - c.setRect(x); - C = c.getRadius() - G; - F.setRadius(C); - F.setCenter(c.getCenter()); - c.setRadius(C); - if (A.length && A[0].getPosition() === "gauge") { - f = A[0]; - F.doSetSurfaceRect(f.getSurface(), h); - f.setTotalAngle(c.getTotalAngle()); - f.setLength(C) - } - } else { - F.setRadius(j); - F.setCenter(D) - } - F.redraw() - } catch (B) { - throw B - } finally { - F.animationSuspendCount--; - if (g) { - F.resumeThicknessChanged() - } - } - }, - refloatAxes: function() { - var j = this, - g = j.getAxes(), - h = j.getMainRect(), - f, k, b, d, a, c, e; - if (!h) { - return - } - e = 0.5 * Math.min(h[2], h[3]); - for (d = 0, a = g.length; d < a; d++) { - c = g[d]; - f = c.getFloating(); - k = f ? f.value : null; - if (k !== null) { - b = j.getAxis(f.alongAxis); - if (c.getPosition() === "angular") { - if (b) { - k = b.getLength() * k / b.getRange()[1] - } else { - k = 0.01 * k * e - } - c.sprites[0].setAttributes({ - length: k - }, true) - } else { - if (b) { - if (Ext.isString(k)) { - k = b.getCoordFor(k) - } - k = k / (b.getRange()[1] + 1) * Math.PI * 2 - Math.PI * 1.5 + c.getRotation() - } else { - k = Ext.draw.Draw.rad(k) - } - c.sprites[0].setAttributes({ - baseRotation: k - }, true) - } - } - } - }, - redraw: function() { - var f = this, - g = f.getAxes(), - d, c = f.getSeries(), - b, a, e; - for (a = 0, e = g.length; a < e; a++) { - d = g[a]; - d.getSprites() - } - for (a = 0, e = c.length; a < e; a++) { - b = c[a]; - b.getSprites() - } - f.renderFrame(); - f.callParent(arguments) - }, - renderFrame: function() { - this.refloatAxes(); - this.callParent() - } -}); -Ext.define("Ext.chart.SpaceFillingChart", { - extend: "Ext.chart.AbstractChart", - xtype: "spacefilling", - config: {}, - performLayout: function() { - var j = this; - try { - j.animationSuspendCount++; - if (j.callParent() === false) { - return - } - var k = j.getSurface("chart").getRect(), - l = j.getInsetPadding(), - a = k[2] - l.left - l.right, - m = k[3] - l.top - l.bottom, - h = [l.left, l.top, a, m], - b = j.getSeries(), - d, c, g; - j.getSurface().setRect(h); - j.setMainRect(h); - for (c = 0, g = b.length; c < g; c++) { - d = b[c]; - d.getSurface().setRect(h); - if (d.setRect) { - d.setRect(h) - } - d.getOverlaySurface().setRect(k) - } - j.redraw() - } catch (f) { - throw f - } finally { - j.animationSuspendCount-- - } - }, - redraw: function() { - var e = this, - c = e.getSeries(), - b, a, d; - for (a = 0, d = c.length; a < d; a++) { - b = c[a]; - b.getSprites() - } - e.renderFrame(); - e.callParent(arguments) - } -}); -Ext.define("Ext.chart.axis.sprite.Axis3D", { - extend: "Ext.chart.axis.sprite.Axis", - alias: "sprite.axis3d", - type: "axis3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - }, - triggers: { - depth: "layout" - } - } - }, - config: { - fx: { - customDurations: { - depth: 0 - } - } - }, - layoutUpdater: function() { - var h = this, - f = h.getAxis().getChart(); - if (f.isInitializing) { - return - } - var e = h.attr, - d = h.getLayout(), - c = d.isDiscrete ? 0 : e.depth, - g = f.getInherited().rtl, - b = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMin, - i = e.dataMin + (e.dataMax - e.dataMin) * e.visibleMax, - a = { - attr: e, - segmenter: h.getSegmenter(), - renderer: h.defaultRenderer - }; - if (e.position === "left" || e.position === "right") { - e.translationX = 0; - e.translationY = i * (e.length - c) / (i - b) + c; - e.scalingX = 1; - e.scalingY = (-e.length + c) / (i - b); - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } else { - if (e.position === "top" || e.position === "bottom") { - if (g) { - e.translationX = e.length + b * e.length / (i - b) + 1 - } else { - e.translationX = -b * e.length / (i - b) - } - e.translationY = 0; - e.scalingX = (g ? -1 : 1) * (e.length - c) / (i - b); - e.scalingY = 1; - e.scalingCenterY = 0; - e.scalingCenterX = 0; - h.applyTransformations(true) - } - } - if (d) { - d.calculateLayout(a); - h.setLayoutContext(a) - } - }, - renderAxisLine: function(a, j, f, c) { - var i = this, - h = i.attr, - b = h.lineWidth * 0.5, - f = i.getLayout(), - d = f.isDiscrete ? 0 : h.depth, - k = h.position, - e, g; - if (h.axisLine && h.length) { - switch (k) { - case "left": - e = a.roundPixel(c[2]) - b; - j.moveTo(e, -h.endGap + d); - j.lineTo(e, h.length + h.startGap); - break; - case "right": - j.moveTo(b, -h.endGap); - j.lineTo(b, h.length + h.startGap); - break; - case "bottom": - j.moveTo(-h.startGap, b); - j.lineTo(h.length - d + h.endGap, b); - break; - case "top": - e = a.roundPixel(c[3]) - b; - j.moveTo(-h.startGap, e); - j.lineTo(h.length + h.endGap, e); - break; - case "angular": - j.moveTo(h.centerX + h.length, h.centerY); - j.arc(h.centerX, h.centerY, h.length, 0, Math.PI * 2, true); - break; - case "gauge": - g = i.getGaugeAngles(); - j.moveTo(h.centerX + Math.cos(g.start) * h.length, h.centerY + Math.sin(g.start) * h.length); - j.arc(h.centerX, h.centerY, h.length, g.start, g.end, true); - break - } - } - } -}); -Ext.define("Ext.chart.axis.Axis3D", { - extend: "Ext.chart.axis.Axis", - xtype: "axis3d", - requires: ["Ext.chart.axis.sprite.Axis3D"], - config: { - depth: 0 - }, - onSeriesChange: function(e) { - var g = this, - b = "depthchange", - f = "onSeriesDepthChange", - d, c; - - function a(h) { - var i = g.boundSeries; - for (d = 0; d < i.length; d++) { - c = i[d]; - c[h](b, f, g) - } - } - a("un"); - g.callParent(arguments); - a("on") - }, - onSeriesDepthChange: function(b, f) { - var d = this, - g = f, - e = d.boundSeries, - a, c; - if (f > d.getDepth()) { - g = f - } else { - for (a = 0; a < e.length; a++) { - c = e[a]; - if (c !== b && c.getDepth) { - f = c.getDepth(); - if (f > g) { - g = f - } - } - } - } - d.setDepth(g) - }, - updateDepth: function(d) { - var b = this, - c = b.getSprites(), - a = { - depth: d - }; - if (c && c.length) { - c[0].setAttributes(a) - } - if (b.gridSpriteEven && b.gridSpriteOdd) { - b.gridSpriteEven.getTemplate().setAttributes(a); - b.gridSpriteOdd.getTemplate().setAttributes(a) - } - }, - getGridAlignment: function() { - switch (this.getPosition()) { - case "left": - case "right": - return "horizontal3d"; - case "top": - case "bottom": - return "vertical3d" - } - } -}); -Ext.define("Ext.chart.axis.Category", { - requires: ["Ext.chart.axis.layout.CombineDuplicate", "Ext.chart.axis.segmenter.Names"], - extend: "Ext.chart.axis.Axis", - alias: "axis.category", - type: "category", - config: { - layout: "combineDuplicate", - segmenter: "names" - } -}); -Ext.define("Ext.chart.axis.Category3D", { - requires: ["Ext.chart.axis.layout.CombineDuplicate", "Ext.chart.axis.segmenter.Names"], - extend: "Ext.chart.axis.Axis3D", - alias: "axis.category3d", - type: "category3d", - config: { - layout: "combineDuplicate", - segmenter: "names" - } -}); -Ext.define("Ext.chart.axis.Numeric", { - extend: "Ext.chart.axis.Axis", - type: "numeric", - alias: ["axis.numeric", "axis.radial"], - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Numeric"], - config: { - layout: "continuous", - segmenter: "numeric", - aggregator: "double" - } -}); -Ext.define("Ext.chart.axis.Numeric3D", { - extend: "Ext.chart.axis.Axis3D", - alias: ["axis.numeric3d"], - type: "numeric3d", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Numeric"], - config: { - layout: "continuous", - segmenter: "numeric", - aggregator: "double" - } -}); -Ext.define("Ext.chart.axis.Time", { - extend: "Ext.chart.axis.Numeric", - alias: "axis.time", - type: "time", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Time"], - config: { - calculateByLabelSize: true, - dateFormat: null, - fromDate: null, - toDate: null, - step: [Ext.Date.DAY, 1], - layout: "continuous", - segmenter: "time", - aggregator: "time" - }, - updateDateFormat: function(a) { - this.setRenderer(function(c, b) { - return Ext.Date.format(new Date(b), a) - }) - }, - updateFromDate: function(a) { - this.setMinimum(+a) - }, - updateToDate: function(a) { - this.setMaximum(+a) - }, - getCoordFor: function(a) { - if (Ext.isString(a)) { - a = new Date(a) - } - return +a - } -}); -Ext.define("Ext.chart.axis.Time3D", { - extend: "Ext.chart.axis.Numeric3D", - alias: "axis.time3d", - type: "time3d", - requires: ["Ext.chart.axis.layout.Continuous", "Ext.chart.axis.segmenter.Time"], - config: { - calculateByLabelSize: true, - dateFormat: null, - fromDate: null, - toDate: null, - step: [Ext.Date.DAY, 1], - layout: "continuous", - segmenter: "time", - aggregator: "time" - }, - updateDateFormat: function(a) { - this.setRenderer(function(c, b) { - return Ext.Date.format(new Date(b), a) - }) - }, - updateFromDate: function(a) { - this.setMinimum(+a) - }, - updateToDate: function(a) { - this.setMaximum(+a) - }, - getCoordFor: function(a) { - if (Ext.isString(a)) { - a = new Date(a) - } - return +a - } -}); -Ext.define("Ext.chart.grid.HorizontalGrid3D", { - extend: "Ext.chart.grid.HorizontalGrid", - alias: "grid.horizontal3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - } - } - }, - render: function(a, k, d) { - var f = this.attr, - i = a.roundPixel(f.x), - h = a.roundPixel(f.y), - l = a.matrix.getDX(), - c = k.lineWidth * 0.5, - j = f.height, - e = f.depth, - b, g; - if (h <= d[1]) { - return - } - b = d[0] + e - l; - g = h + c - e; - k.beginPath(); - k.rect(b, g, d[2], j); - k.fill(); - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + d[2], g); - k.stroke(); - b = d[0] + i - l; - g = h + c; - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + e, g - e); - k.lineTo(b + e, g - e + j); - k.lineTo(b, g + j); - k.closePath(); - k.fill(); - k.beginPath(); - k.moveTo(b, g); - k.lineTo(b + e, g - e); - k.stroke() - } -}); -Ext.define("Ext.chart.grid.VerticalGrid3D", { - extend: "Ext.chart.grid.VerticalGrid", - alias: "grid.vertical3d", - inheritableStatics: { - def: { - processors: { - depth: "number" - }, - defaults: { - depth: 0 - } - } - }, - render_: function(c, d, f) { - var b = this.attr, - a = c.roundPixel(b.x), - e = d.lineWidth * 0.5; - d.beginPath(); - d.rect(a - e, f[1] - c.matrix.getDY(), b.width, f[3]); - d.fill(); - d.beginPath(); - d.moveTo(a - e, f[1] - c.matrix.getDY()); - d.lineTo(a - e, f[1] + f[3] - c.matrix.getDY()); - d.stroke() - }, - render: function(b, j, e) { - var g = this.attr, - i = b.roundPixel(g.x), - k = b.matrix.getDY(), - d = j.lineWidth * 0.5, - a = g.width, - f = g.depth, - c, h; - if (i >= e[2]) { - return - } - c = i - d + f; - h = e[1] - f - k; - j.beginPath(); - j.rect(c, h, a, e[3]); - j.fill(); - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c, h + e[3]); - j.stroke(); - c = i - d; - h = e[3]; - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c + f, h - f); - j.lineTo(c + f + a, h - f); - j.lineTo(c + a, h); - j.closePath(); - j.fill(); - c = i - d; - h = e[3]; - j.beginPath(); - j.moveTo(c, h); - j.lineTo(c + f, h - f); - j.stroke() - } -}); -Ext.define("Ext.chart.interactions.CrossZoom", { - extend: "Ext.chart.interactions.Abstract", - type: "crosszoom", - alias: "interaction.crosszoom", - isCrossZoom: true, - config: { - axes: true, - gestures: { - dragstart: "onGestureStart", - drag: "onGesture", - dragend: "onGestureEnd", - dblclick: "onDoubleTap" - }, - undoButton: {} - }, - stopAnimationBeforeSync: false, - zoomAnimationInProgress: false, - constructor: function() { - this.callParent(arguments); - this.zoomHistory = [] - }, - applyAxes: function(b) { - var a = {}; - if (b === true) { - return { - top: {}, - right: {}, - bottom: {}, - left: {} - } - } else { - if (Ext.isArray(b)) { - a = {}; - Ext.each(b, function(c) { - a[c] = {} - }) - } else { - if (Ext.isObject(b)) { - Ext.iterate(b, function(c, d) { - if (d === true) { - a[c] = {} - } else { - if (d !== false) { - a[c] = d - } - } - }) - } - } - } - return a - }, - applyUndoButton: function(b, a) { - var c = this; - if (a) { - a.destroy() - } - if (b) { - return Ext.create("Ext.Button", Ext.apply({ - cls: [], - text: "Undo Zoom", - disabled: true, - handler: function() { - c.undoZoom() - } - }, b)) - } - }, - getSurface: function() { - return this.getChart() && this.getChart().getSurface("main") - }, - setSeriesOpacity: function(b) { - var a = this.getChart() && this.getChart().getSurface("series"); - if (a) { - a.element.setStyle("opacity", b) - } - }, - onGestureStart: function(h) { - var j = this, - i = j.getChart(), - d = j.getSurface(), - l = i.getInnerRect(), - c = i.getInnerPadding(), - g = c.left, - b = g + l[2], - f = c.top, - a = f + l[3], - n = i.getEventXY(h), - m = n[0], - k = n[1]; - if (j.zoomAnimationInProgress) { - return - } - if (m > g && m < b && k > f && k < a) { - j.gestureEvent = "drag"; - j.lockEvents(j.gestureEvent); - j.startX = m; - j.startY = k; - j.selectionRect = d.add({ - type: "rect", - globalAlpha: 0.5, - fillStyle: "rgba(80,80,140,0.5)", - strokeStyle: "rgba(80,80,140,1)", - lineWidth: 2, - x: m, - y: k, - width: 0, - height: 0, - zIndex: 10000 - }); - j.setSeriesOpacity(0.8); - return false - } - }, - onGesture: function(h) { - var j = this; - if (j.zoomAnimationInProgress) { - return - } - if (j.getLocks()[j.gestureEvent] === j) { - var i = j.getChart(), - d = j.getSurface(), - l = i.getInnerRect(), - c = i.getInnerPadding(), - g = c.left, - b = g + l[2], - f = c.top, - a = f + l[3], - n = i.getEventXY(h), - m = n[0], - k = n[1]; - if (m < g) { - m = g - } else { - if (m > b) { - m = b - } - } - if (k < f) { - k = f - } else { - if (k > a) { - k = a - } - } - j.selectionRect.setAttributes({ - width: m - j.startX, - height: k - j.startY - }); - if (Math.abs(j.startX - m) < 11 || Math.abs(j.startY - k) < 11) { - j.selectionRect.setAttributes({ - globalAlpha: 0.5 - }) - } else { - j.selectionRect.setAttributes({ - globalAlpha: 1 - }) - } - d.renderFrame(); - return false - } - }, - onGestureEnd: function(i) { - var l = this; - if (l.zoomAnimationInProgress) { - return - } - if (l.getLocks()[l.gestureEvent] === l) { - var k = l.getChart(), - d = l.getSurface(), - n = k.getInnerRect(), - c = k.getInnerPadding(), - g = c.left, - b = g + n[2], - f = c.top, - a = f + n[3], - h = n[2], - j = n[3], - p = k.getEventXY(i), - o = p[0], - m = p[1]; - if (o < g) { - o = g - } else { - if (o > b) { - o = b - } - } - if (m < f) { - m = f - } else { - if (m > a) { - m = a - } - } - if (Math.abs(l.startX - o) < 11 || Math.abs(l.startY - m) < 11) { - d.remove(l.selectionRect) - } else { - l.zoomBy([Math.min(l.startX, o) / h, 1 - Math.max(l.startY, m) / j, Math.max(l.startX, o) / h, 1 - Math.min(l.startY, m) / j]); - l.selectionRect.setAttributes({ - x: Math.min(l.startX, o), - y: Math.min(l.startY, m), - width: Math.abs(l.startX - o), - height: Math.abs(l.startY - m) - }); - l.selectionRect.setAnimation(k.getAnimation() || { - duration: 0 - }); - l.selectionRect.setAttributes({ - globalAlpha: 0, - x: 0, - y: 0, - width: h, - height: j - }); - l.zoomAnimationInProgress = true; - k.suspendThicknessChanged(); - l.selectionRect.fx.on("animationend", function() { - k.resumeThicknessChanged(); - d.remove(l.selectionRect); - l.selectionRect = null; - l.zoomAnimationInProgress = false - }) - } - d.renderFrame(); - l.sync(); - l.unlockEvents(l.gestureEvent); - l.setSeriesOpacity(1); - if (!l.zoomAnimationInProgress) { - d.remove(l.selectionRect); - l.selectionRect = null - } - } - }, - zoomBy: function(o) { - var n = this, - a = n.getAxes(), - k = n.getChart(), - j = k.getAxes(), - l = k.getInherited().rtl, - f, d = {}, - c, b; - if (l) { - o = o.slice(); - c = 1 - o[0]; - b = 1 - o[2]; - o[0] = Math.min(c, b); - o[2] = Math.max(c, b) - } - for (var h = 0; h < j.length; h++) { - var g = j[h]; - f = a[g.getPosition()]; - if (f && f.allowZoom !== false) { - var e = g.isSide(), - m = g.getVisibleRange(); - d[g.getId()] = m.slice(0); - if (!e) { - g.setVisibleRange([(m[1] - m[0]) * o[0] + m[0], (m[1] - m[0]) * o[2] + m[0]]) - } else { - g.setVisibleRange([(m[1] - m[0]) * o[1] + m[0], (m[1] - m[0]) * o[3] + m[0]]) - } - } - } - n.zoomHistory.push(d); - n.getUndoButton().setDisabled(false) - }, - undoZoom: function() { - var c = this.zoomHistory.pop(), - d = this.getChart().getAxes(); - if (c) { - for (var a = 0; a < d.length; a++) { - var b = d[a]; - if (c[b.getId()]) { - b.setVisibleRange(c[b.getId()]) - } - } - } - this.getUndoButton().setDisabled(this.zoomHistory.length === 0); - this.sync() - }, - onDoubleTap: function(a) { - this.undoZoom() - }, - destroy: function() { - this.setUndoButton(null); - this.callParent(arguments) - } -}); -Ext.define("Ext.chart.interactions.Crosshair", { - extend: "Ext.chart.interactions.Abstract", - requires: ["Ext.chart.grid.HorizontalGrid", "Ext.chart.grid.VerticalGrid", "Ext.chart.CartesianChart", "Ext.chart.axis.layout.Discrete"], - type: "crosshair", - alias: "interaction.crosshair", - config: { - axes: { - top: { - label: {}, - rect: {} - }, - right: { - label: {}, - rect: {} - }, - bottom: { - label: {}, - rect: {} - }, - left: { - label: {}, - rect: {} - } - }, - lines: { - horizontal: { - strokeStyle: "black", - lineDash: [5, 5] - }, - vertical: { - strokeStyle: "black", - lineDash: [5, 5] - } - }, - gesture: "drag" - }, - applyAxes: function(b, a) { - return Ext.merge(a || {}, b) - }, - applyLines: function(a, b) { - return Ext.merge(b || {}, a) - }, - updateChart: function(a) { - if (a && !a.isCartesian) { - Ext.raise("Crosshair interaction can only be used on cartesian charts.") - } - this.callParent(arguments) - }, - getGestures: function() { - var a = this, - b = {}; - b[a.getGesture()] = "onGesture"; - b[a.getGesture() + "start"] = "onGestureStart"; - b[a.getGesture() + "end"] = "onGestureEnd"; - return b - }, - onGestureStart: function(N) { - var m = this, - O = m.getChart(), - B = O.getTheme().getAxis(), - A, F = O.getSurface("overlay"), - s = O.getInnerRect(), - n = s[2], - M = s[3], - r = O.getEventXY(N), - D = r[0], - C = r[1], - E = O.getAxes(), - u = m.getAxes(), - h = m.getLines(), - q, v, b, d, k, z, G, L, J, o, I, w, l, f, p, j, t, a, g, H, c, K; - if (D > 0 && D < n && C > 0 && C < M) { - m.lockEvents(m.getGesture()); - H = Ext.apply({ - xclass: "Ext.chart.grid.HorizontalGrid", - x: 0, - y: C, - width: n - }, h.horizontal); - c = Ext.apply({ - xclass: "Ext.chart.grid.VerticalGrid", - x: D, - y: 0, - height: M - }, h.vertical); - m.axesLabels = m.axesLabels || {}; - for (K = 0; K < E.length; K++) { - q = E[K]; - v = q.getSurface(); - b = v.getRect(); - w = q.getSprites()[0]; - d = b[2]; - k = b[3]; - z = q.getPosition(); - G = q.getAlignment(); - t = q.getTitle(); - a = t && t.attr.text !== "" && t.getBBox(); - l = w.attr; - f = w.thickness; - p = l.axisLine ? l.lineWidth : 0; - j = p / 2; - I = Math.max(l.majorTickSize, l.minorTickSize) + p; - L = m.axesLabels[z] = v.add({ - type: "composite" - }); - L.labelRect = L.add(Ext.apply({ - type: "rect", - fillStyle: "white", - x: z === "right" ? p : 0, - y: z === "bottom" ? p : 0, - width: d - p - (G === "vertical" && a ? a.width : 0), - height: k - p - (G === "horizontal" && a ? a.height : 0), - translationX: z === "left" && a ? a.width : 0, - translationY: z === "top" && a ? a.height : 0 - }, u.rect || u[z].rect)); - if (G === "vertical" && !c.strokeStyle) { - c.strokeStyle = l.strokeStyle - } - if (G === "horizontal" && !H.strokeStyle) { - H.strokeStyle = l.strokeStyle - } - A = Ext.merge({}, B.defaults, B[z]); - J = Ext.apply({}, q.config.label, A.label); - o = u.label || u[z].label; - L.labelText = L.add(Ext.apply(J, o, { - type: "text", - x: (function() { - switch (z) { - case "left": - g = a ? a.x + a.width : 0; - return g + (d - g - I) / 2 - j; - case "right": - g = a ? d - a.x : 0; - return I + (d - I - g) / 2 + j; - default: - return 0 - } - })(), - y: (function() { - switch (z) { - case "top": - g = a ? a.y + a.height : 0; - return g + (k - g - I) / 2 - j; - case "bottom": - g = a ? k - a.y : 0; - return I + (k - I - g) / 2 + j; - default: - return 0 - } - })() - })) - } - m.horizontalLine = F.add(H); - m.verticalLine = F.add(c); - return false - } - }, - onGesture: function(G) { - var K = this; - if (K.getLocks()[K.getGesture()] !== K) { - return - } - var u = K.getChart(), - z = u.getSurface("overlay"), - a = Ext.Array.slice(u.getInnerRect()), - r = u.getInnerPadding(), - t = r.left, - q = r.top, - E = a[2], - f = a[3], - d = u.getEventXY(G), - k = d[0], - j = d[1], - D = u.getAxes(), - c, h, m, p, J, w, I, H, s, b, C, g, v, n, l, A, F, o, B; - if (k < 0) { - k = 0 - } else { - if (k > E) { - k = E - } - } - if (j < 0) { - j = 0 - } else { - if (j > f) { - j = f - } - } - k += t; - j += q; - for (B = 0; B < D.length; B++) { - c = D[B]; - h = c.getPosition(); - m = c.getAlignment(); - p = c.getSurface(); - J = c.getSprites()[0]; - w = J.attr.matrix; - C = J.attr.textPadding * 2; - s = K.axesLabels[h]; - I = J.getLayoutContext(); - H = c.getSegmenter(); - if (s) { - if (m === "vertical") { - v = w.getYY(); - l = w.getDY(); - F = (j - l - q) / v; - if (c.getLayout() instanceof Ext.chart.axis.layout.Discrete) { - j = Math.round(F) * v + l + q; - F = H.from(Math.round(F)); - F = J.attr.data[F] - } else { - F = H.from(F) - } - o = H.renderer(F, I); - s.setAttributes({ - translationY: j - q - }); - s.labelText.setAttributes({ - text: o - }); - b = s.labelText.getBBox(); - s.labelRect.setAttributes({ - height: b.height + C, - y: -(b.height + C) / 2 - }); - p.renderFrame() - } else { - g = w.getXX(); - n = w.getDX(); - A = (k - n - t) / g; - if (c.getLayout() instanceof Ext.chart.axis.layout.Discrete) { - k = Math.round(A) * g + n + t; - A = H.from(Math.round(A)); - A = J.attr.data[A] - } else { - A = H.from(A) - } - o = H.renderer(A, I); - s.setAttributes({ - translationX: k - t - }); - s.labelText.setAttributes({ - text: o - }); - b = s.labelText.getBBox(); - s.labelRect.setAttributes({ - width: b.width + C, - x: -(b.width + C) / 2 - }); - p.renderFrame() - } - } - } - K.horizontalLine.setAttributes({ - y: j, - strokeStyle: J.attr.strokeStyle - }); - K.verticalLine.setAttributes({ - x: k, - strokeStyle: J.attr.strokeStyle - }); - z.renderFrame(); - return false - }, - onGestureEnd: function(h) { - var l = this, - k = l.getChart(), - a = k.getSurface("overlay"), - j = k.getAxes(), - c, g, d, b, f; - a.remove(l.verticalLine); - a.remove(l.horizontalLine); - for (f = 0; f < j.length; f++) { - c = j[f]; - g = c.getPosition(); - d = c.getSurface(); - b = l.axesLabels[g]; - if (b) { - delete l.axesLabels[g]; - d.remove(b) - } - d.renderFrame() - } - a.renderFrame(); - l.unlockEvents(l.getGesture()) - } -}); -Ext.define("Ext.chart.interactions.ItemHighlight", { - extend: "Ext.chart.interactions.Abstract", - type: "itemhighlight", - alias: "interaction.itemhighlight", - isItemHighlight: true, - config: { - gestures: { - tap: "onTapGesture", - mousemove: "onMouseMoveGesture", - mousedown: "onMouseDownGesture", - mouseup: "onMouseUpGesture", - mouseleave: "onMouseUpGesture" - }, - sticky: false - }, - stickyHighlightItem: null, - onMouseMoveGesture: function(g) { - var d = this, - h = d.tipItem, - a = g.pointerType === "mouse", - c, f, b; - if (d.getSticky()) { - return true - } - if (d.isDragging) { - if (h && a) { - h.series.hideTooltip(h); - d.tipItem = null - } - } else { - if (!d.stickyHighlightItem) { - c = d.getItemForEvent(g); - b = d.getChart(); - if (c !== b.getHighlightItem()) { - d.highlight(c); - d.sync() - } - if (a) { - if (h && (!c || h.field !== c.field || h.record !== c.record)) { - h.series.hideTooltip(h); - d.tipItem = h = null - } - if (c && (f = c.series.getTooltip())) { - if (f.trackMouse || !h) { - c.series.showTooltip(c, g.getXY()) - } - d.tipItem = c - } - } - return false - } - } - }, - highlight: function(a) { - this.getChart().setHighlightItem(a) - }, - showTooltip: function(b, a) { - a.series.showTooltip(a, b.getXY()); - this.tipItem = a - }, - onMouseDownGesture: function() { - this.isDragging = true - }, - onMouseUpGesture: function() { - this.isDragging = false - }, - onTapGesture: function(c) { - var b = this; - if (c.pointerType === "mouse" && !b.getSticky()) { - return - } - var a = b.getItemForEvent(c); - if (b.stickyHighlightItem && a && (b.stickyHighlightItem.index === a.index)) { - a = null - } - b.stickyHighlightItem = a; - b.highlight(a) - } -}); -Ext.define("Ext.chart.interactions.ItemEdit", { - extend: "Ext.chart.interactions.ItemHighlight", - requires: ["Ext.tip.ToolTip"], - type: "itemedit", - alias: "interaction.itemedit", - isItemEdit: true, - config: { - style: null, - renderer: null, - tooltip: true, - gestures: { - dragstart: "onDragStart", - drag: "onDrag", - dragend: "onDragEnd" - }, - cursors: { - ewResize: "ew-resize", - nsResize: "ns-resize", - move: "move" - } - }, - item: null, - applyTooltip: function(b) { - if (b) { - var a = Ext.apply({}, b, { - renderer: this.defaultTooltipRenderer, - constrainPosition: true, - shrinkWrapDock: true, - autoHide: true, - offsetX: 10, - offsetY: 10 - }); - b = new Ext.tip.ToolTip(a) - } - return b - }, - defaultTooltipRenderer: function(b, a, f, d) { - var c = []; - if (f.xField) { - c.push(f.xField + ": " + f.xValue) - } - if (f.yField) { - c.push(f.yField + ": " + f.yValue) - } - b.setHtml(c.join("
")) - }, - onDragStart: function(d) { - var c = this, - a = c.getChart(), - b = a.getHighlightItem(); - if (b) { - a.fireEvent("beginitemedit", a, c, c.item = b); - return false - } - }, - onDrag: function(f) { - var d = this, - b = d.getChart(), - c = b.getHighlightItem(), - a = c && c.sprite.type; - if (c) { - switch (a) { - case "barSeries": - return d.onDragBar(f); - break; - case "scatterSeries": - return d.onDragScatter(f); - break - } - } - }, - highlight: function(f) { - var e = this, - d = e.getChart(), - a = d.getFlipXY(), - g = e.getCursors(), - c = f && f.sprite.type, - b = d.el.dom.style; - e.callParent([f]); - if (f) { - switch (c) { - case "barSeries": - if (a) { - b.cursor = g.ewResize - } else { - b.cursor = g.nsResize - } - break; - case "scatterSeries": - b.cursor = g.move; - break - } - } else { - d.el.dom.style.cursor = "default" - } - }, - onDragBar: function(i) { - var m = this, - k = m.getChart(), - l = k.getInherited().rtl, - f = k.isCartesian && k.getFlipXY(), - q = k.getHighlightItem(), - g = q.sprite.getMarker("items"), - p = g.getMarkerFor(q.sprite.getId(), q.index), - b = q.sprite.getSurface(), - c = b.getRect(), - r = b.getEventXY(i), - o = q.sprite.attr.matrix, - j = m.getRenderer(), - a, n, d, h; - if (f) { - h = l ? c[2] - r[0] : r[0] - } else { - h = c[3] - r[1] - } - a = { - x: p.x, - y: h, - width: p.width, - height: p.height + (p.y - h), - radius: p.radius, - fillStyle: "none", - lineDash: [4, 4], - zIndex: 100 - }; - Ext.apply(a, m.getStyle()); - if (Ext.isArray(q.series.getYField())) { - h = h - p.y - p.height - } - m.target = { - index: q.index, - yField: q.field, - yValue: (h - o.getDY()) / o.getYY() - }; - d = [k, { - target: m.target, - style: a, - item: q - }]; - n = Ext.callback(j, null, d, 0, k); - if (n) { - Ext.apply(a, n) - } - q.sprite.putMarker("items", a, "itemedit"); - m.showTooltip(i, m.target, q); - b.renderFrame() - }, - onDragScatter: function(n) { - var t = this, - g = t.getChart(), - d = g.getInherited().rtl, - l = g.isCartesian && g.getFlipXY(), - o = g.getHighlightItem(), - b = o.sprite.getMarker("items"), - p = b.getMarkerFor(o.sprite.getId(), o.index), - j = o.sprite.getSurface(), - h = j.getRect(), - a = j.getEventXY(n), - k = o.sprite.attr.matrix, - c = o.series.getXAxis(), - f = c && c.getLayout().isContinuous, - i = t.getRenderer(), - m, u, q, s, r; - if (l) { - r = d ? h[2] - a[0] : a[0] - } else { - r = h[3] - a[1] - } - if (f) { - if (l) { - s = h[3] - a[1] - } else { - s = a[0] - } - } else { - s = p.translationX - } - m = { - translationX: s, - translationY: r, - scalingX: p.scalingX, - scalingY: p.scalingY, - r: p.r, - fillStyle: "none", - lineDash: [4, 4], - zIndex: 100 - }; - Ext.apply(m, t.getStyle()); - t.target = { - index: o.index, - yField: o.field, - yValue: (r - k.getDY()) / k.getYY() - }; - if (f) { - Ext.apply(t.target, { - xField: o.series.getXField(), - xValue: (s - k.getDX()) / k.getXX() - }) - } - q = [g, { - target: t.target, - style: m, - item: o - }]; - u = Ext.callback(i, null, q, 0, g); - if (u) { - Ext.apply(m, u) - } - o.sprite.putMarker("items", m, "itemedit"); - t.showTooltip(n, t.target, o); - j.renderFrame() - }, - showTooltip: function(g, f, c) { - var d = this.getTooltip(), - a, b; - if (d && Ext.toolkit !== "modern") { - a = d.config; - b = this.getChart(); - Ext.callback(a.renderer, null, [d, c, f, g], 0, b); - d.show([g.x + a.offsetX, g.y + a.offsetY]) - } - }, - hideTooltip: function() { - var a = this.getTooltip(); - if (a && Ext.toolkit !== "modern") { - a.hide() - } - }, - onDragEnd: function(g) { - var d = this, - f = d.target, - c = d.getChart(), - b = c.getStore(), - a; - if (f) { - a = b.getAt(f.index); - if (f.yField) { - a.set(f.yField, f.yValue, { - convert: false - }) - } - if (f.xField) { - a.set(f.xField, f.xValue, { - convert: false - }) - } - if (f.yField || f.xField) { - d.getChart().onDataChanged() - } - d.target = null - } - d.hideTooltip(); - if (d.item) { - c.fireEvent("enditemedit", c, d, d.item, f) - } - d.highlight(d.item = null) - }, - destroy: function() { - var a = this.getConfig("tooltip", true); - Ext.destroy(a); - this.callParent() - } -}); -Ext.define("Ext.chart.interactions.PanZoom", { - extend: "Ext.chart.interactions.Abstract", - type: "panzoom", - alias: "interaction.panzoom", - requires: ["Ext.draw.Animator"], - config: { - axes: { - top: {}, - right: {}, - bottom: {}, - left: {} - }, - minZoom: null, - maxZoom: null, - showOverflowArrows: true, - panGesture: "drag", - zoomGesture: "pinch", - zoomOnPanGesture: false, - modeToggleButton: { - xtype: "segmentedbutton", - width: 200, - defaults: { - ui: "default-toolbar" - }, - cls: Ext.baseCSSPrefix + "panzoom-toggle", - items: [{ - text: "Pan" - }, { - text: "Zoom" - }] - }, - hideLabelInGesture: false - }, - stopAnimationBeforeSync: true, - applyAxes: function(b, a) { - return Ext.merge(a || {}, b) - }, - applyZoomOnPanGesture: function(a) { - this.getChart(); - if (this.isMultiTouch()) { - return false - } - return a - }, - updateZoomOnPanGesture: function(b) { - var a = this.getModeToggleButton(); - if (!this.isMultiTouch()) { - a.show(); - a.setValue(b ? 1 : 0) - } else { - a.hide() - } - }, - toggleMode: function() { - var a = this; - if (!a.isMultiTouch()) { - a.setZoomOnPanGesture(!a.getZoomOnPanGesture()) - } - }, - applyModeToggleButton: function(c, b) { - var d = this, - a = Ext.factory(c, "Ext.button.Segmented", b); - if (!a && b) { - b.destroy() - } - if (a && !b) { - a.addListener("toggle", function(e) { - d.setZoomOnPanGesture(e.getValue() === 1) - }) - } - return a - }, - getGestures: function() { - var c = this, - e = {}, - d = c.getPanGesture(), - b = c.getZoomGesture(), - a = Ext.supports.Touch; - e[b] = "onZoomGestureMove"; - e[b + "start"] = "onZoomGestureStart"; - e[b + "end"] = "onZoomGestureEnd"; - e[d] = "onPanGestureMove"; - e[d + "start"] = "onPanGestureStart"; - e[d + "end"] = "onPanGestureEnd"; - e.doubletap = "onDoubleTap"; - return e - }, - onDoubleTap: function(h) { - var f = this, - c = f.getChart(), - g = c.getAxes(), - b, a, d; - for (a = 0, d = g.length; a < d; a++) { - b = g[a]; - b.setVisibleRange([0, 1]) - } - c.redraw() - }, - onPanGestureStart: function(d) { - if (!d || !d.touches || d.touches.length < 2) { - var b = this, - a = b.getChart().getInnerRect(), - c = b.getChart().element.getXY(); - b.startX = d.getX() - c[0] - a[0]; - b.startY = d.getY() - c[1] - a[1]; - b.oldVisibleRanges = null; - b.hideLabels(); - b.getChart().suspendThicknessChanged(); - b.lockEvents(b.getPanGesture()); - return false - } - }, - onPanGestureMove: function(d) { - var b = this; - if (b.getLocks()[b.getPanGesture()] === b) { - var a = b.getChart().getInnerRect(), - c = b.getChart().element.getXY(); - if (b.getZoomOnPanGesture()) { - b.transformAxesBy(b.getZoomableAxes(d), 0, 0, (d.getX() - c[0] - a[0]) / b.startX, b.startY / (d.getY() - c[1] - a[1])) - } else { - b.transformAxesBy(b.getPannableAxes(d), d.getX() - c[0] - a[0] - b.startX, d.getY() - c[1] - a[1] - b.startY, 1, 1) - } - b.sync(); - return false - } - }, - onPanGestureEnd: function(b) { - var a = this, - c = a.getPanGesture(); - if (a.getLocks()[c] === a) { - a.getChart().resumeThicknessChanged(); - a.showLabels(); - a.sync(); - a.unlockEvents(c); - return false - } - }, - onZoomGestureStart: function(b) { - if (b.touches && b.touches.length === 2) { - var c = this, - i = c.getChart().element.getXY(), - f = c.getChart().getInnerRect(), - h = i[0] + f[0], - d = i[1] + f[1], - j = [b.touches[0].point.x - h, b.touches[0].point.y - d, b.touches[1].point.x - h, b.touches[1].point.y - d], - g = Math.max(44, Math.abs(j[2] - j[0])), - a = Math.max(44, Math.abs(j[3] - j[1])); - c.getChart().suspendThicknessChanged(); - c.lastZoomDistances = [g, a]; - c.lastPoints = j; - c.oldVisibleRanges = null; - c.hideLabels(); - c.lockEvents(c.getZoomGesture()); - return false - } - }, - onZoomGestureMove: function(d) { - var f = this; - if (f.getLocks()[f.getZoomGesture()] === f) { - var i = f.getChart().getInnerRect(), - n = f.getChart().element.getXY(), - k = n[0] + i[0], - h = n[1] + i[1], - o = Math.abs, - c = f.lastPoints, - m = [d.touches[0].point.x - k, d.touches[0].point.y - h, d.touches[1].point.x - k, d.touches[1].point.y - h], - g = Math.max(44, o(m[2] - m[0])), - b = Math.max(44, o(m[3] - m[1])), - a = this.lastZoomDistances || [g, b], - l = g / a[0], - j = b / a[1]; - f.transformAxesBy(f.getZoomableAxes(d), i[2] * (l - 1) / 2 + m[2] - c[2] * l, i[3] * (j - 1) / 2 + m[3] - c[3] * j, l, j); - f.sync(); - return false - } - }, - onZoomGestureEnd: function(c) { - var b = this, - a = b.getZoomGesture(); - if (b.getLocks()[a] === b) { - b.getChart().resumeThicknessChanged(); - b.showLabels(); - b.sync(); - b.unlockEvents(a); - return false - } - }, - hideLabels: function() { - if (this.getHideLabelInGesture()) { - this.eachInteractiveAxes(function(a) { - a.hideLabels() - }) - } - }, - showLabels: function() { - if (this.getHideLabelInGesture()) { - this.eachInteractiveAxes(function(a) { - a.showLabels() - }) - } - }, - isEventOnAxis: function(c, a) { - var b = a.getSurface().getRect(); - return b[0] <= c.getX() && c.getX() <= b[0] + b[2] && b[1] <= c.getY() && c.getY() <= b[1] + b[3] - }, - getPannableAxes: function(d) { - var h = this, - a = h.getAxes(), - f = h.getChart().getAxes(), - c, g = f.length, - k = [], - j = false, - b; - if (d) { - for (c = 0; c < g; c++) { - if (this.isEventOnAxis(d, f[c])) { - j = true; - break - } - } - } - for (c = 0; c < g; c++) { - b = a[f[c].getPosition()]; - if (b && b.allowPan !== false && (!j || this.isEventOnAxis(d, f[c]))) { - k.push(f[c]) - } - } - return k - }, - getZoomableAxes: function(f) { - var j = this, - a = j.getAxes(), - g = j.getChart().getAxes(), - l = [], - d, h = g.length, - c, k = false, - b; - if (f) { - for (d = 0; d < h; d++) { - if (this.isEventOnAxis(f, g[d])) { - k = true; - break - } - } - } - for (d = 0; d < h; d++) { - c = g[d]; - b = a[c.getPosition()]; - if (b && b.allowZoom !== false && (!k || this.isEventOnAxis(f, c))) { - l.push(c) - } - } - return l - }, - eachInteractiveAxes: function(c) { - var d = this, - b = d.getAxes(), - e = d.getChart().getAxes(); - for (var a = 0; a < e.length; a++) { - if (b[e[a].getPosition()]) { - if (false === c.call(this, e[a])) { - return - } - } - } - }, - transformAxesBy: function(d, j, g, h, e) { - var f = this.getChart().getInnerRect(), - a = this.getAxes(), - k, b = this.oldVisibleRanges, - l = false; - if (!b) { - this.oldVisibleRanges = b = {}; - this.eachInteractiveAxes(function(i) { - b[i.getId()] = i.getVisibleRange() - }) - } - if (!f) { - return - } - for (var c = 0; c < d.length; c++) { - k = a[d[c].getPosition()]; - l = this.transformAxisBy(d[c], b[d[c].getId()], j, g, h, e, this.minZoom || k.minZoom, this.maxZoom || k.maxZoom) || l - } - return l - }, - transformAxisBy: function(c, o, r, q, k, i, h, m) { - var s = this, - b = o[1] - o[0], - l = c.getVisibleRange(), - g = h || s.getMinZoom() || c.config.minZoom, - j = m || s.getMaxZoom() || c.config.maxZoom, - a = s.getChart().getInnerRect(), - f, p; - if (!a) { - return - } - var d = c.isSide(), - e = d ? a[3] : a[2], - n = d ? -q : r; - b /= d ? i : k; - if (b < 0) { - b = -b - } - if (b * g > 1) { - b = 1 - } - if (b * j < 1) { - b = 1 / j - } - f = o[0]; - p = o[1]; - l = l[1] - l[0]; - if (b === l && l === 1) { - return - } - c.setVisibleRange([(o[0] + o[1] - b) * 0.5 - n / e * b, (o[0] + o[1] + b) * 0.5 - n / e * b]); - return (Math.abs(f - c.getVisibleRange()[0]) > 1e-10 || Math.abs(p - c.getVisibleRange()[1]) > 1e-10) - }, - destroy: function() { - this.setModeToggleButton(null); - this.callParent() - } -}); -Ext.define("Ext.chart.interactions.Rotate", { - extend: "Ext.chart.interactions.Abstract", - type: "rotate", - alias: "interaction.rotate", - config: { - gesture: "rotate", - gestures: { - rotate: "onRotate", - rotateend: "onRotate", - dragstart: "onGestureStart", - drag: "onGesture", - dragend: "onGestureEnd" - }, - rotation: 0 - }, - oldRotations: null, - getAngle: function(f) { - var c = this, - b = c.getChart(), - d = b.getEventXY(f), - a = b.getCenter(); - return Math.atan2(d[1] - a[1], d[0] - a[0]) - }, - getRadius: function(a) { - return this.getChart().getRadius() - }, - getEventRadius: function(h) { - var f = this, - d = f.getChart(), - g = d.getEventXY(h), - a = d.getCenter(), - c = g[0] - a[0], - b = g[1] - a[1]; - return Math.sqrt(c * c + b * b) - }, - onGestureStart: function(d) { - var c = this, - b = c.getRadius(d), - a = c.getEventRadius(d); - if (b >= a) { - c.lockEvents("drag"); - c.angle = c.getAngle(d); - c.oldRotations = {}; - return false - } - }, - onGesture: function(b) { - var a = this, - c = a.getAngle(b) - a.angle; - if (a.getLocks().drag === a) { - a.doRotateTo(c, true); - return false - } - }, - doRotateTo: function(d, a, b) { - var n = this, - l = n.getChart(), - k = l.getAxes(), - f = l.getSeries(), - m = n.oldRotations, - c, j, g, e, h; - if (!b) { - l.suspendAnimation() - } - for (e = 0, h = k.length; e < h; e++) { - c = k[e]; - g = m[c.getId()] || (m[c.getId()] = c.getRotation()); - c.setRotation(d + (a ? g : 0)) - } - for (e = 0, h = f.length; e < h; e++) { - j = f[e]; - g = m[j.getId()] || (m[j.getId()] = j.getRotation()); - j.setRotation(d + (a ? g : 0)) - } - n.setRotation(d + (a ? g : 0)); - n.fireEvent("rotate", n, n.getRotation()); - n.sync(); - if (!b) { - l.resumeAnimation() - } - }, - rotateTo: function(c, b, a) { - this.doRotateTo(c, b, a); - this.oldRotations = {} - }, - onGestureEnd: function(b) { - var a = this; - if (a.getLocks().drag === a) { - a.onGesture(b); - a.unlockEvents("drag"); - a.fireEvent("rotationEnd", a, a.getRotation()); - return false - } - }, - onRotate: function(a) {} -}); -Ext.define("Ext.chart.interactions.RotatePie3D", { - extend: "Ext.chart.interactions.Rotate", - type: "rotatePie3d", - alias: "interaction.rotatePie3d", - getAngle: function(g) { - var a = this.getChart(), - f = a.getInherited().rtl, - d = f ? -1 : 1, - h = g.getXY(), - c = a.element.getXY(), - b = a.getMainRect(); - return d * Math.atan2(h[1] - c[1] - b[3] * 0.5, h[0] - c[0] - b[2] * 0.5) - }, - getRadius: function(j) { - var f = this.getChart(), - a = f.getRadius(), - d = f.getSeries(), - h = d.length, - c = 0, - b, g; - for (; c < h; c++) { - b = d[c]; - if (b.isPie3D) { - g = b.getRadius(); - if (g > a) { - a = g - } - } - } - return a - } -}); -Ext.define("Ext.chart.plugin.ItemEvents", { - extend: "Ext.plugin.Abstract", - alias: "plugin.chartitemevents", - moveEvents: false, - mouseMoveEvents: { - mousemove: true, - mouseover: true, - mouseout: true - }, - itemMouseMoveEvents: { - itemmousemove: true, - itemmouseover: true, - itemmouseout: true - }, - init: function(b) { - var a = "handleEvent"; - this.chart = b; - b.addElementListener({ - click: a, - dblclick: a, - mousedown: a, - mousemove: a, - mouseup: a, - mouseover: a, - mouseout: a, - priority: 1001, - scope: this - }) - }, - hasItemMouseMoveListeners: function() { - var b = this.chart.hasListeners, - a; - for (a in this.itemMouseMoveEvents) { - if (a in b) { - return true - } - } - return false - }, - handleEvent: function(g) { - var d = this, - a = d.chart, - h = g.type in d.mouseMoveEvents, - c = d.lastItem, - f, b; - if (h && !d.hasItemMouseMoveListeners() && !d.moveEvents) { - return - } - f = a.getEventXY(g); - b = a.getItemForPoint(f[0], f[1]); - if (h && !Ext.Object.equals(b, c)) { - if (c) { - a.fireEvent("itemmouseout", a, c, g); - c.series.fireEvent("itemmouseout", c.series, c, g) - } - if (b) { - a.fireEvent("itemmouseover", a, b, g); - b.series.fireEvent("itemmouseover", b.series, b, g) - } - } - if (b) { - a.fireEvent("item" + g.type, a, b, g); - b.series.fireEvent("item" + g.type, b.series, b, g) - } - d.lastItem = b - } -}); -Ext.define("Ext.chart.series.Cartesian", { - extend: "Ext.chart.series.Series", - config: { - xField: null, - yField: null, - xAxis: null, - yAxis: null - }, - directions: ["X", "Y"], - fieldCategoryX: ["X"], - fieldCategoryY: ["Y"], - applyXAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - applyYAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - updateXAxis: function(a) { - a.processData(this) - }, - updateYAxis: function(a) { - a.processData(this) - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - getItemForPoint: function(a, g) { - if (this.getSprites()) { - var f = this, - d = f.getSprites()[0], - b = f.getStore(), - e, c; - if (f.getHidden()) { - return null - } - if (d) { - c = d.getIndexNearPoint(a, g); - if (c !== -1) { - e = { - series: f, - category: f.getItemInstancing() ? "items" : "markers", - index: c, - record: b.getData().items[c], - field: f.getYField(), - sprite: d - }; - return e - } - } - } - }, - createSprite: function() { - var c = this, - a = c.callParent(), - b = c.getChart(), - d = c.getXAxis(); - a.setAttributes({ - flipXY: b.getFlipXY(), - xAxis: d - }); - if (a.setAggregator && d && d.getAggregator) { - if (d.getAggregator) { - a.setAggregator({ - strategy: d.getAggregator() - }) - } else { - a.setAggregator({}) - } - } - return a - }, - getSprites: function() { - var d = this, - c = this.getChart(), - e = d.getAnimation() || c && c.getAnimation(), - b = d.getItemInstancing(), - f = d.sprites, - a; - if (!c) { - return [] - } - if (!f.length) { - a = d.createSprite() - } else { - a = f[0] - } - if (e) { - if (b) { - a.itemsMarker.getTemplate().setAnimation(e) - } - a.setAnimation(e) - } - return f - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getSubStyleWithTheme(), - c = a.fillStyle; - if (Ext.isArray(c)) { - c = c[0] - } - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - }, - getXRange: function() { - return [this.dataRange[0], this.dataRange[2]] - }, - getYRange: function() { - return [this.dataRange[1], this.dataRange[3]] - } -}); -Ext.define("Ext.chart.series.StackedCartesian", { - extend: "Ext.chart.series.Cartesian", - config: { - stacked: true, - splitStacks: true, - fullStack: false, - fullStackTotal: 100, - hidden: [] - }, - spriteAnimationCount: 0, - themeColorCount: function() { - var b = this, - a = b.getYField(); - return Ext.isArray(a) ? a.length : 1 - }, - updateStacked: function() { - this.processData() - }, - updateSplitStacks: function() { - this.processData() - }, - coordinateY: function() { - return this.coordinateStacked("Y", 1, 2) - }, - coordinateStacked: function(D, e, m) { - var F = this, - f = F.getStore(), - r = f.getData().items, - B = r.length, - c = F["get" + D + "Axis"](), - x = F.getHidden(), - a = F.getSplitStacks(), - z = F.getFullStack(), - l = F.getFullStackTotal(), - p = { - min: 0, - max: 0 - }, - n = F["fieldCategory" + D], - C = [], - o = [], - E = [], - h, A = F.getStacked(), - g = F.getSprites(), - q = [], - w, v, u, s, H, y, b, d, G, t; - if (!g.length) { - return - } - for (w = 0; w < n.length; w++) { - d = n[w]; - s = F.getFields([d]); - H = s.length; - for (v = 0; v < B; v++) { - C[v] = 0; - o[v] = 0; - E[v] = 0 - } - for (v = 0; v < H; v++) { - if (!x[v]) { - q[v] = F.coordinateData(r, s[v], c) - } - } - if (A && z) { - y = []; - if (a) { - b = [] - } - for (v = 0; v < B; v++) { - y[v] = 0; - if (a) { - b[v] = 0 - } - for (u = 0; u < H; u++) { - G = q[u]; - if (!G) { - continue - } - G = G[v]; - if (G >= 0 || !a) { - y[v] += G - } else { - if (G < 0) { - b[v] += G - } - } - } - } - } - for (v = 0; v < H; v++) { - t = {}; - if (x[v]) { - t["dataStart" + d] = C; - t["data" + d] = C; - g[v].setAttributes(t); - continue - } - G = q[v]; - if (A) { - h = []; - for (u = 0; u < B; u++) { - if (!G[u]) { - G[u] = 0 - } - if (G[u] >= 0 || !a) { - if (z && y[u]) { - G[u] *= l / y[u] - } - C[u] = o[u]; - o[u] += G[u]; - h[u] = o[u] - } else { - if (z && b[u]) { - G[u] *= l / b[u] - } - C[u] = E[u]; - E[u] += G[u]; - h[u] = E[u] - } - } - t["dataStart" + d] = C; - t["data" + d] = h; - F.getRangeOfData(C, p); - F.getRangeOfData(h, p) - } else { - t["dataStart" + d] = C; - t["data" + d] = G; - F.getRangeOfData(G, p) - } - g[v].setAttributes(t) - } - } - F.dataRange[e] = p.min; - F.dataRange[e + m] = p.max; - t = {}; - t["dataMin" + D] = p.min; - t["dataMax" + D] = p.max; - for (w = 0; w < g.length; w++) { - g[w].setAttributes(t) - } - }, - getFields: function(f) { - var e = this, - a = [], - c, b, d; - for (b = 0, d = f.length; b < d; b++) { - c = e["get" + f[b] + "Field"](); - if (Ext.isArray(c)) { - a.push.apply(a, c) - } else { - a.push(c) - } - } - return a - }, - updateLabelOverflowPadding: function(a) { - this.getLabel().setAttributes({ - labelOverflowPadding: a - }) - }, - getSprites: function() { - var k = this, - j = k.getChart(), - c = k.getAnimation() || j && j.getAnimation(), - f = k.getFields(k.fieldCategoryY), - b = k.getItemInstancing(), - h = k.sprites, - l, e = k.getHidden(), - g = false, - d, a = f.length; - if (!j) { - return [] - } - for (d = 0; d < a; d++) { - l = h[d]; - if (!l) { - l = k.createSprite(); - l.setAttributes({ - zIndex: -d - }); - l.setField(f[d]); - g = true; - e.push(false); - if (b) { - l.itemsMarker.getTemplate().setAttributes(k.getStyleByIndex(d)) - } else { - l.setAttributes(k.getStyleByIndex(d)) - } - } - if (c) { - if (b) { - l.itemsMarker.getTemplate().setAnimation(c) - } - l.setAnimation(c) - } - } - if (g) { - k.updateHidden(e) - } - return h - }, - getItemForPoint: function(k, j) { - if (this.getSprites()) { - var h = this, - b, g, m, a = h.getItemInstancing(), - f = h.getSprites(), - l = h.getStore(), - c = h.getHidden(), - n, d, e; - for (b = 0, g = f.length; b < g; b++) { - if (!c[b]) { - m = f[b]; - d = m.getIndexNearPoint(k, j); - if (d !== -1) { - e = h.getYField(); - n = { - series: h, - index: d, - category: a ? "items" : "markers", - record: l.getData().items[d], - field: typeof e === "string" ? e : e[b], - sprite: m - }; - return n - } - } - } - return null - } - }, - provideLegendInfo: function(e) { - var g = this, - f = g.getSprites(), - h = g.getTitle(), - j = g.getYField(), - d = g.getHidden(), - k = f.length === 1, - b, l, c, a; - for (c = 0; c < f.length; c++) { - b = g.getStyleByIndex(c); - l = b.fillStyle; - if (h) { - if (Ext.isArray(h)) { - a = h[c] - } else { - if (k) { - a = h - } - } - } else { - if (Ext.isArray(j)) { - a = j[c] - } else { - a = g.getId() - } - } - e.push({ - name: a, - mark: (Ext.isObject(l) ? l.stops && l.stops[0].color : l) || b.strokeStyle || "black", - disabled: d[c], - series: g.getId(), - index: c - }) - } - }, - onSpriteAnimationStart: function(a) { - this.spriteAnimationCount++; - if (this.spriteAnimationCount === 1) { - this.fireEvent("animationstart") - } - }, - onSpriteAnimationEnd: function(a) { - this.spriteAnimationCount--; - if (this.spriteAnimationCount === 0) { - this.fireEvent("animationend") - } - } -}); -Ext.define("Ext.chart.series.sprite.Series", { - extend: "Ext.draw.sprite.Sprite", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - inheritableStatics: { - def: { - processors: { - dataMinX: "number", - dataMaxX: "number", - dataMinY: "number", - dataMaxY: "number", - rangeX: "data", - rangeY: "data", - dataX: "data", - dataY: "data" - }, - defaults: { - dataMinX: 0, - dataMaxX: 1, - dataMinY: 0, - dataMaxY: 1, - rangeX: null, - rangeY: null, - dataX: null, - dataY: null - }, - triggers: { - dataX: "bbox", - dataY: "bbox", - dataMinX: "bbox", - dataMaxX: "bbox", - dataMinY: "bbox", - dataMaxY: "bbox" - } - } - }, - config: { - store: null, - series: null, - field: null - } -}); -Ext.define("Ext.chart.series.sprite.Cartesian", { - extend: "Ext.chart.series.sprite.Series", - inheritableStatics: { - def: { - processors: { - labels: "default", - labelOverflowPadding: "number", - selectionTolerance: "number", - flipXY: "bool", - renderer: "default", - visibleMinX: "number", - visibleMinY: "number", - visibleMaxX: "number", - visibleMaxY: "number", - innerWidth: "number", - innerHeight: "number" - }, - defaults: { - labels: null, - labelOverflowPadding: 10, - selectionTolerance: 20, - flipXY: false, - renderer: null, - transformFillStroke: false, - visibleMinX: 0, - visibleMinY: 0, - visibleMaxX: 1, - visibleMaxY: 1, - innerWidth: 1, - innerHeight: 1 - }, - triggers: { - dataX: "dataX,bbox", - dataY: "dataY,bbox", - visibleMinX: "panzoom", - visibleMinY: "panzoom", - visibleMaxX: "panzoom", - visibleMaxY: "panzoom", - innerWidth: "panzoom", - innerHeight: "panzoom" - }, - updaters: { - dataX: function(a) { - this.processDataX(); - this.scheduleUpdater(a, "dataY", ["dataY"]) - }, - dataY: function() { - this.processDataY() - }, - panzoom: function(c) { - var e = c.visibleMaxX - c.visibleMinX, - d = c.visibleMaxY - c.visibleMinY, - b = c.flipXY ? c.innerHeight : c.innerWidth, - g = !c.flipXY ? c.innerHeight : c.innerWidth, - a = this.getSurface(), - f = a ? a.getInherited().rtl : false; - if (f && !c.flipXY) { - c.translationX = b + c.visibleMinX * b / e - } else { - c.translationX = -c.visibleMinX * b / e - } - c.translationY = -c.visibleMinY * g / d; - c.scalingX = (f && !c.flipXY ? -1 : 1) * b / e; - c.scalingY = g / d; - c.scalingCenterX = 0; - c.scalingCenterY = 0; - this.applyTransformations(true) - } - } - } - }, - processDataY: Ext.emptyFn, - processDataX: Ext.emptyFn, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.dataMinX; - b.y = a.dataMinY; - b.width = a.dataMaxX - a.dataMinX; - b.height = a.dataMaxY - a.dataMinY - }, - binarySearch: function(d) { - var b = this.attr.dataX, - f = 0, - a = b.length; - if (d <= b[0]) { - return f - } - if (d >= b[a - 1]) { - return a - 1 - } - while (f + 1 < a) { - var c = (f + a) >> 1, - e = b[c]; - if (e === d) { - return c - } else { - if (e < d) { - f = c - } else { - a = c - } - } - } - return f - }, - render: function(b, c, g) { - var f = this, - a = f.attr, - e = a.inverseMatrix.clone(); - e.appendMatrix(b.inverseMatrix); - if (a.dataX === null || a.dataX === undefined) { - return - } - if (a.dataY === null || a.dataY === undefined) { - return - } - if (e.getXX() * e.getYX() || e.getXY() * e.getYY()) { - console.log("Cartesian Series sprite does not support rotation/sheering"); - return - } - var d = e.transformList([ - [g[0] - 1, g[3] + 1], - [g[0] + g[2] + 1, -1] - ]); - d = d[0].concat(d[1]); - f.renderClipped(b, c, d, g) - }, - renderClipped: Ext.emptyFn, - getIndexNearPoint: function(f, e) { - var w = this, - q = w.attr.matrix, - h = w.attr.dataX, - g = w.attr.dataY, - k = w.attr.selectionTolerance, - t, r, c = -1, - j = q.clone().prependMatrix(w.surfaceMatrix).inverse(), - u = j.transformPoint([f, e]), - b = j.transformPoint([f - k, e - k]), - n = j.transformPoint([f + k, e + k]), - a = Math.min(b[0], n[0]), - s = Math.max(b[0], n[0]), - l = Math.min(b[1], n[1]), - d = Math.max(b[1], n[1]), - m, v, o, p; - for (o = 0, p = h.length; o < p; o++) { - m = h[o]; - v = g[o]; - if (m > a && m < s && v > l && v < d) { - if (c === -1 || (Math.abs(m - u[0]) < t) && (Math.abs(v - u[1]) < r)) { - t = Math.abs(m - u[0]); - r = Math.abs(v - u[1]); - c = o - } - } - } - return c - } -}); -Ext.define("Ext.chart.series.sprite.StackedCartesian", { - extend: "Ext.chart.series.sprite.Cartesian", - inheritableStatics: { - def: { - processors: { - groupCount: "number", - groupOffset: "number", - dataStartY: "data" - }, - defaults: { - selectionTolerance: 20, - groupCount: 1, - groupOffset: 0, - dataStartY: null - }, - triggers: { - dataStartY: "dataY,bbox" - } - } - }, - getIndexNearPoint: function(e, d) { - var o = this, - q = o.attr.matrix, - h = o.attr.dataX, - f = o.attr.dataY, - u = o.attr.dataStartY, - l = o.attr.selectionTolerance, - s = 0.5, - r = Infinity, - b = -1, - k = q.clone().prependMatrix(this.surfaceMatrix).inverse(), - t = k.transformPoint([e, d]), - a = k.transformPoint([e - l, d - l]), - n = k.transformPoint([e + l, d + l]), - m = Math.min(a[1], n[1]), - c = Math.max(a[1], n[1]), - j, g; - for (var p = 0; p < h.length; p++) { - if (Math.min(u[p], f[p]) <= c && m <= Math.max(u[p], f[p])) { - j = Math.abs(h[p] - t[0]); - g = Math.max(-Math.min(f[p] - t[1], t[1] - u[p]), 0); - if (j < s && g <= r) { - s = j; - r = g; - b = p - } - } - } - return b - } -}); -Ext.define("Ext.chart.series.sprite.Area", { - alias: "sprite.areaSeries", - extend: "Ext.chart.series.sprite.StackedCartesian", - inheritableStatics: { - def: { - processors: { - step: "bool" - }, - defaults: { - step: false - } - } - }, - renderClipped: function(q, s, A) { - var B = this, - p = B.attr, - l = p.dataX, - j = p.dataY, - C = p.dataStartY, - t = p.matrix, - h, g, v, f, d, z, w, e = t.elements[0], - m = t.elements[4], - o = t.elements[3], - k = t.elements[5], - c = B.surfaceMatrix, - n = {}, - r = Math.min(A[0], A[2]), - u = Math.max(A[0], A[2]), - b = Math.max(0, this.binarySearch(r)), - a = Math.min(l.length - 1, this.binarySearch(u) + 1); - s.beginPath(); - z = l[b] * e + m; - w = j[b] * o + k; - s.moveTo(z, w); - if (p.step) { - d = w; - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, d); - s.lineTo(h, d = g) - } - } else { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, g) - } - } - if (C) { - if (p.step) { - f = l[a] * e + m; - for (v = a; v >= b; v--) { - h = l[v] * e + m; - g = C[v] * o + k; - s.lineTo(f, g); - s.lineTo(f = h, g) - } - } else { - for (v = a; v >= b; v--) { - h = l[v] * e + m; - g = C[v] * o + k; - s.lineTo(h, g) - } - } - } else { - s.lineTo(l[a] * e + m, g); - s.lineTo(l[a] * e + m, k); - s.lineTo(z, k); - s.lineTo(z, j[v] * o + k) - } - if (p.transformFillStroke) { - p.matrix.toContext(s) - } - s.fill(); - if (p.transformFillStroke) { - p.inverseMatrix.toContext(s) - } - s.beginPath(); - s.moveTo(z, w); - if (p.step) { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, d); - s.lineTo(h, d = g); - n.translationX = c.x(h, g); - n.translationY = c.y(h, g); - B.putMarker("markers", n, v, !p.renderer) - } - } else { - for (v = b; v <= a; v++) { - h = l[v] * e + m; - g = j[v] * o + k; - s.lineTo(h, g); - n.translationX = c.x(h, g); - n.translationY = c.y(h, g); - B.putMarker("markers", n, v, !p.renderer) - } - } - if (p.transformFillStroke) { - p.matrix.toContext(s) - } - s.stroke() - } -}); -Ext.define("Ext.chart.series.Area", { - extend: "Ext.chart.series.StackedCartesian", - alias: "series.area", - type: "area", - seriesType: "areaSeries", - requires: ["Ext.chart.series.sprite.Area"], - config: { - splitStacks: false - } -}); -Ext.define("Ext.chart.series.sprite.Bar", { - alias: "sprite.barSeries", - extend: "Ext.chart.series.sprite.StackedCartesian", - inheritableStatics: { - def: { - processors: { - minBarWidth: "number", - maxBarWidth: "number", - minGapWidth: "number", - radius: "number", - inGroupGapWidth: "number" - }, - defaults: { - minBarWidth: 2, - maxBarWidth: 100, - minGapWidth: 5, - inGroupGapWidth: 3, - radius: 0 - } - } - }, - drawLabel: function(k, i, s, h, o) { - var q = this, - n = q.attr, - f = q.getMarker("labels"), - d = f.getTemplate(), - l = q.labelCfg || (q.labelCfg = {}), - c = q.surfaceMatrix, - j = n.labelOverflowPadding, - b = d.attr.display, - m = d.attr.orientation, - g, e, a, r, t, p; - l.x = c.x(i, h); - l.y = c.y(i, h); - if (!n.flipXY) { - l.rotationRads = -Math.PI * 0.5 - } else { - l.rotationRads = 0 - } - l.calloutVertical = !n.flipXY; - switch (m) { - case "horizontal": - l.rotationRads = 0; - l.calloutVertical = false; - break; - case "vertical": - l.rotationRads = -Math.PI * 0.5; - l.calloutVertical = true; - break - } - l.text = k; - if (d.attr.renderer) { - p = [k, f, l, { - store: q.getStore() - }, o]; - r = Ext.callback(d.attr.renderer, null, p, 0, q.getSeries()); - if (typeof r === "string") { - l.text = r - } else { - if (typeof r === "object") { - if ("text" in r) { - l.text = r.text - } - t = true - } - } - } - a = q.getMarkerBBox("labels", o, true); - if (!a) { - q.putMarker("labels", l, o); - a = q.getMarkerBBox("labels", o, true) - } - e = (a.width / 2 + j); - if (s > h) { - e = -e - } - if ((m === "horizontal" && n.flipXY) || (m === "vertical" && !n.flipXY) || !m) { - g = (b === "insideStart") ? s + e : h - e - } else { - g = (b === "insideStart") ? s + j * 2 : h - j * 2 - } - l.x = c.x(i, g); - l.y = c.y(i, g); - g = (b === "insideStart") ? s - e : h + e; - l.calloutPlaceX = c.x(i, g); - l.calloutPlaceY = c.y(i, g); - g = (b === "insideStart") ? s : h; - l.calloutStartX = c.x(i, g); - l.calloutStartY = c.y(i, g); - if (s > h) { - e = -e - } - if (Math.abs(h - s) <= e * 2 || b === "outside") { - l.callout = 1 - } else { - l.callout = 0 - } - if (t) { - Ext.apply(l, r) - } - q.putMarker("labels", l, o) - }, - drawBar: function(l, b, d, c, h, k, a, e) { - var g = this, - j = {}, - f = g.attr.renderer, - i; - j.x = c; - j.y = h; - j.width = k - c; - j.height = a - h; - j.radius = g.attr.radius; - if (f) { - i = Ext.callback(f, null, [g, j, { - store: g.getStore() - }, e], 0, g.getSeries()); - Ext.apply(j, i) - } - g.putMarker("items", j, e, !f) - }, - renderClipped: function(G, u, F, C) { - if (this.cleanRedraw) { - return - } - var q = this, - o = q.attr, - w = o.dataX, - v = o.dataY, - H = o.labels, - n = o.dataStartY, - m = o.groupCount, - E = o.groupOffset - (m - 1) * 0.5, - z = o.inGroupGapWidth, - t = u.lineWidth, - D = o.matrix, - B = D.elements[0], - j = D.elements[3], - e = D.elements[4], - d = G.roundPixel(D.elements[5]) - 1, - J = (B < 0 ? -1 : 1) * B - o.minGapWidth, - k = (Math.min(J, o.maxBarWidth) - z * (m - 1)) / m, - A = G.roundPixel(Math.max(o.minBarWidth, k)), - c = q.surfaceMatrix, - g, I, b, h, K, a, l = 0.5 * o.lineWidth, - L = Math.min(F[0], F[2]), - x = Math.max(F[0], F[2]), - y = Math.max(0, Math.floor(L)), - p = Math.min(w.length - 1, Math.ceil(x)), - f = H && q.getMarker("labels"), - s, r; - for (K = y; K <= p; K++) { - s = n ? n[K] : 0; - r = v[K]; - a = w[K] * B + e + E * (A + z); - g = G.roundPixel(a - A / 2) + l; - h = G.roundPixel(r * j + d + t); - I = G.roundPixel(a + A / 2) - l; - b = G.roundPixel(s * j + d + t); - q.drawBar(u, G, F, g, h - l, I, b - l, K); - if (f && H[K] != null) { - q.drawLabel(H[K], a, b, h, K) - } - q.putMarker("markers", { - translationX: c.x(a, h), - translationY: c.y(a, h) - }, K, true) - } - }, - getIndexNearPoint: function(l, k) { - var m = this, - g = m.attr, - h = g.dataX, - a = m.getSurface(), - b = a.getRect() || [0, 0, 0, 0], - j = b[3], - e, d, c, n, f = -1; - if (g.flipXY) { - e = j - k; - if (a.getInherited().rtl) { - d = b[2] - l - } else { - d = l - } - } else { - e = l; - d = j - k - } - for (c = 0; c < h.length; c++) { - n = m.getMarkerBBox("items", c); - if (Ext.draw.Draw.isPointInBBox(e, d, n)) { - f = c; - break - } - } - return f - } -}); -Ext.define("Ext.chart.series.Bar", { - extend: "Ext.chart.series.StackedCartesian", - alias: "series.bar", - type: "bar", - seriesType: "barSeries", - requires: ["Ext.chart.series.sprite.Bar", "Ext.draw.sprite.Rect"], - config: { - itemInstancing: { - type: "rect", - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0, - radius: 0 - } - } - } - }, - getItemForPoint: function(a, f) { - if (this.getSprites()) { - var d = this, - c = d.getChart(), - e = c.getInnerPadding(), - b = c.getInherited().rtl; - arguments[0] = a + (b ? e.right : -e.left); - arguments[1] = f + e.bottom; - return d.callParent(arguments) - } - }, - updateXAxis: function(a) { - a.setLabelInSpan(true); - this.callParent(arguments) - }, - updateHidden: function(a) { - this.callParent(arguments); - this.updateStacked() - }, - updateStacked: function(c) { - var e = this, - g = e.getSprites(), - d = g.length, - f = [], - a = {}, - b; - for (b = 0; b < d; b++) { - if (!g[b].attr.hidden) { - f.push(g[b]) - } - } - d = f.length; - if (e.getStacked()) { - a.groupCount = 1; - a.groupOffset = 0; - for (b = 0; b < d; b++) { - f[b].setAttributes(a) - } - } else { - a.groupCount = f.length; - for (b = 0; b < d; b++) { - a.groupOffset = b; - f[b].setAttributes(a) - } - } - e.callParent(arguments) - } -}); -Ext.define("Ext.chart.series.sprite.Bar3D", { - extend: "Ext.chart.series.sprite.Bar", - alias: "sprite.bar3dSeries", - requires: ["Ext.draw.gradient.Linear"], - inheritableStatics: { - def: { - processors: { - depthWidthRatio: "number", - saturationFactor: "number", - brightnessFactor: "number", - colorSpread: "number" - }, - defaults: { - depthWidthRatio: 1 / 3, - saturationFactor: 1, - brightnessFactor: 1, - colorSpread: 1, - transformFillStroke: true - }, - triggers: { - groupCount: "panzoom" - }, - updaters: { - panzoom: function(c) { - var g = this, - e = c.visibleMaxX - c.visibleMinX, - d = c.visibleMaxY - c.visibleMinY, - b = c.flipXY ? c.innerHeight : c.innerWidth, - h = !c.flipXY ? c.innerHeight : c.innerWidth, - a = g.getSurface(), - f = a ? a.getInherited().rtl : false; - if (f && !c.flipXY) { - c.translationX = b + c.visibleMinX * b / e - } else { - c.translationX = -c.visibleMinX * b / e - } - c.translationY = -c.visibleMinY * (h - g.depth) / d; - c.scalingX = (f && !c.flipXY ? -1 : 1) * b / e; - c.scalingY = (h - g.depth) / d; - c.scalingCenterX = 0; - c.scalingCenterY = 0; - g.applyTransformations(true) - } - } - } - }, - config: { - showStroke: false - }, - depth: 0, - drawBar: function(p, b, d, c, l, o, a, h) { - var k = this, - i = k.attr, - n = {}, - j = i.renderer, - m, g, f, e; - n.x = (c + o) * 0.5; - n.y = l; - n.width = (o - c) * 0.75; - n.height = a - l; - n.depth = g = n.width * i.depthWidthRatio; - n.orientation = i.flipXY ? "horizontal" : "vertical"; - n.saturationFactor = i.saturationFactor; - n.brightnessFactor = i.brightnessFactor; - n.colorSpread = i.colorSpread; - if (g !== k.depth) { - k.depth = g; - f = k.getSeries(); - f.fireEvent("depthchange", f, g) - } - if (j) { - e = [k, n, { - store: k.getStore() - }, h]; - m = Ext.callback(j, null, e, 0, k.getSeries()); - Ext.apply(n, m) - } - k.putMarker("items", n, h, !j) - } -}); -Ext.define("Ext.chart.series.sprite.Box", { - extend: "Ext.draw.sprite.Sprite", - alias: "sprite.box", - type: "box", - inheritableStatics: { - def: { - processors: { - x: "number", - y: "number", - width: "number", - height: "number", - depth: "number", - orientation: "enums(vertical,horizontal)", - showStroke: "bool", - saturationFactor: "number", - brightnessFactor: "number", - colorSpread: "number" - }, - triggers: { - x: "bbox", - y: "bbox", - width: "bbox", - height: "bbox", - depth: "bbox", - orientation: "bbox" - }, - defaults: { - x: 0, - y: 0, - width: 8, - height: 8, - depth: 8, - orientation: "vertical", - showStroke: false, - saturationFactor: 1, - brightnessFactor: 1, - colorSpread: 1, - lineJoin: "bevel" - } - } - }, - constructor: function(a) { - this.callParent([a]); - this.topGradient = new Ext.draw.gradient.Linear({}); - this.rightGradient = new Ext.draw.gradient.Linear({}); - this.frontGradient = new Ext.draw.gradient.Linear({}) - }, - updatePlainBBox: function(d) { - var c = this.attr, - b = c.x, - g = c.y, - e = c.width, - a = c.height, - f = c.depth; - d.x = b - e * 0.5; - d.width = e + f; - if (a > 0) { - d.y = g; - d.height = a + f - } else { - d.y = g + f; - d.height = a - f - } - }, - render: function(l, m) { - var u = this, - k = u.attr, - r = k.x, - j = k.y, - f = j + k.height, - i = j < f, - e = k.width * 0.5, - v = k.depth, - d = k.orientation === "horizontal", - g = k.globalAlpha < 1, - c = k.fillStyle, - n = Ext.draw.Color.create(c.isGradient ? c.getStops()[0].color : c), - h = k.saturationFactor, - o = k.brightnessFactor, - t = k.colorSpread, - b = n.getHSV(), - a = {}, - s, q, p; - if (!k.showStroke) { - m.strokeStyle = Ext.draw.Color.RGBA_NONE - } - if (i) { - p = j; - j = f; - f = p - } - u.topGradient.setDegrees(d ? 0 : 80); - u.topGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 + t * 0.1) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.11) * o, 0, 1)) - }]); - u.rightGradient.setDegrees(d ? 45 : 90); - u.rightGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.14) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 + t * 0.4) * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.32) * o, 0, 1)) - }]); - if (d) { - u.frontGradient.setDegrees(0) - } else { - u.frontGradient.setRadians(Math.atan2(j - f, e * 2)) - } - u.frontGradient.setStops([{ - offset: 0, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 - t * 0.1) * h, 0, 1), Ext.Number.constrain((0.5 + t * 0.1) * o, 0, 1)) - }, { - offset: 1, - color: Ext.draw.Color.fromHSV(b[0], Ext.Number.constrain(b[1] * (1 + t * 0.1) * h, 0, 1), Ext.Number.constrain((0.5 - t * 0.23) * o, 0, 1)) - }]); - if (g || i) { - m.beginPath(); - m.moveTo(r - e, f); - m.lineTo(r - e + v, f + v); - m.lineTo(r + e + v, f + v); - m.lineTo(r + e, f); - m.closePath(); - a.x = r - e; - a.y = j; - a.width = e + v; - a.height = v; - m.fillStyle = (d ? u.rightGradient : u.topGradient).generateGradient(m, a); - m.fillStroke(k) - } - if (g) { - m.beginPath(); - m.moveTo(r - e, j); - m.lineTo(r - e + v, j + v); - m.lineTo(r - e + v, f + v); - m.lineTo(r - e, f); - m.closePath(); - a.x = r + e; - a.y = f; - a.width = v; - a.height = j + v - f; - m.fillStyle = (d ? u.topGradient : u.rightGradient).generateGradient(m, a); - m.fillStroke(k) - } - q = l.roundPixel(j); - m.beginPath(); - m.moveTo(r - e, q); - m.lineTo(r - e + v, j + v); - m.lineTo(r + e + v, j + v); - m.lineTo(r + e, q); - m.closePath(); - a.x = r - e; - a.y = j; - a.width = e + v; - a.height = v; - m.fillStyle = (d ? u.rightGradient : u.topGradient).generateGradient(m, a); - m.fillStroke(k); - s = l.roundPixel(r + e); - m.beginPath(); - m.moveTo(s, l.roundPixel(j)); - m.lineTo(r + e + v, j + v); - m.lineTo(r + e + v, f + v); - m.lineTo(s, f); - m.closePath(); - a.x = r + e; - a.y = f; - a.width = v; - a.height = j + v - f; - m.fillStyle = (d ? u.topGradient : u.rightGradient).generateGradient(m, a); - m.fillStroke(k); - s = l.roundPixel(r + e); - q = l.roundPixel(j); - m.beginPath(); - m.moveTo(r - e, f); - m.lineTo(r - e, q); - m.lineTo(s, q); - m.lineTo(s, f); - m.closePath(); - a.x = r - e; - a.y = f; - a.width = e * 2; - a.height = j - f; - m.fillStyle = u.frontGradient.generateGradient(m, a); - m.fillStroke(k) - } -}); -Ext.define("Ext.chart.series.Bar3D", { - extend: "Ext.chart.series.Bar", - requires: ["Ext.chart.series.sprite.Bar3D", "Ext.chart.series.sprite.Box"], - alias: "series.bar3d", - type: "bar3d", - seriesType: "bar3dSeries", - config: { - itemInstancing: { - type: "box", - fx: { - customDurations: { - x: 0, - y: 0, - width: 0, - height: 0, - depth: 0 - } - } - }, - highlightCfg: { - opacity: 0.8 - } - }, - getSprites: function() { - var c = this.callParent(arguments), - b, d, a; - for (a = 0; a < c.length; a++) { - b = c[a]; - d = b.attr.zIndex; - if (d < 0) { - b.setAttributes({ - zIndex: -d - }) - } - if (b.setSeries) { - b.setSeries(this) - } - } - return c - }, - getDepth: function() { - var a = this.getSprites()[0]; - return a ? (a.depth || 0) : 0 - }, - getItemForPoint: function(m, k) { - if (this.getSprites()) { - var j = this, - b, o, a = j.getItemInstancing(), - h = j.getSprites(), - n = j.getStore(), - c = j.getHidden(), - g = j.getChart(), - l = g.getInnerPadding(), - f = g.getInherited().rtl, - p, d, e; - m = m + (f ? l.right : -l.left); - k = k + l.bottom; - for (b = h.length - 1; b >= 0; b--) { - if (!c[b]) { - o = h[b]; - d = o.getIndexNearPoint(m, k); - if (d !== -1) { - e = j.getYField(); - p = { - series: j, - index: d, - category: a ? "items" : "markers", - record: n.getData().items[d], - field: typeof e === "string" ? e : e[b], - sprite: o - }; - return p - } - } - } - return null - } - } -}); -Ext.define("Ext.draw.LimitedCache", { - config: { - limit: 40, - feeder: function() { - return 0 - }, - scope: null - }, - cache: null, - constructor: function(a) { - this.cache = {}; - this.cache.list = []; - this.cache.tail = 0; - this.initConfig(a) - }, - get: function(e) { - var c = this.cache, - b = this.getLimit(), - a = this.getFeeder(), - d = this.getScope() || this; - if (c[e]) { - return c[e].value - } - if (c.list[c.tail]) { - delete c[c.list[c.tail].cacheId] - } - c[e] = c.list[c.tail] = { - value: a.apply(d, Array.prototype.slice.call(arguments, 1)), - cacheId: e - }; - c.tail++; - if (c.tail === b) { - c.tail = 0 - } - return c[e].value - }, - clear: function() { - this.cache = {}; - this.cache.list = []; - this.cache.tail = 0 - } -}); -Ext.define("Ext.draw.SegmentTree", { - config: { - strategy: "double" - }, - time: function(m, l, n, c, E, d, e) { - var f = 0, - o, A, s = new Date(n[m.startIdx[0]]), - x = new Date(n[m.endIdx[l - 1]]), - D = Ext.Date, - u = [ - [D.MILLI, 1, "ms1", null], - [D.MILLI, 2, "ms2", "ms1"], - [D.MILLI, 5, "ms5", "ms1"], - [D.MILLI, 10, "ms10", "ms5"], - [D.MILLI, 50, "ms50", "ms10"], - [D.MILLI, 100, "ms100", "ms50"], - [D.MILLI, 500, "ms500", "ms100"], - [D.SECOND, 1, "s1", "ms500"], - [D.SECOND, 10, "s10", "s1"], - [D.SECOND, 30, "s30", "s10"], - [D.MINUTE, 1, "mi1", "s10"], - [D.MINUTE, 5, "mi5", "mi1"], - [D.MINUTE, 10, "mi10", "mi5"], - [D.MINUTE, 30, "mi30", "mi10"], - [D.HOUR, 1, "h1", "mi30"], - [D.HOUR, 6, "h6", "h1"], - [D.HOUR, 12, "h12", "h6"], - [D.DAY, 1, "d1", "h12"], - [D.DAY, 7, "d7", "d1"], - [D.MONTH, 1, "mo1", "d1"], - [D.MONTH, 3, "mo3", "mo1"], - [D.MONTH, 6, "mo6", "mo3"], - [D.YEAR, 1, "y1", "mo3"], - [D.YEAR, 5, "y5", "y1"], - [D.YEAR, 10, "y10", "y5"], - [D.YEAR, 100, "y100", "y10"] - ], - z, b, k = f, - F = l, - j = false, - r = m.startIdx, - h = m.endIdx, - w = m.minIdx, - C = m.maxIdx, - a = m.open, - y = m.close, - g = m.minX, - q = m.minY, - p = m.maxX, - B = m.maxY, - v, t; - for (z = 0; l > f + 1 && z < u.length; z++) { - s = new Date(n[r[0]]); - b = u[z]; - s = D.align(s, b[0], b[1]); - if (D.diff(s, x, b[0]) > n.length * 2 * b[1]) { - continue - } - if (b[3] && m.map["time_" + b[3]]) { - o = m.map["time_" + b[3]][0]; - A = m.map["time_" + b[3]][1] - } else { - o = k; - A = F - } - f = l; - t = s; - j = true; - r[l] = r[o]; - h[l] = h[o]; - w[l] = w[o]; - C[l] = C[o]; - a[l] = a[o]; - y[l] = y[o]; - g[l] = g[o]; - q[l] = q[o]; - p[l] = p[o]; - B[l] = B[o]; - t = Ext.Date.add(t, b[0], b[1]); - for (v = o + 1; v < A; v++) { - if (n[h[v]] < +t) { - h[l] = h[v]; - y[l] = y[v]; - if (B[v] > B[l]) { - B[l] = B[v]; - p[l] = p[v]; - C[l] = C[v] - } - if (q[v] < q[l]) { - q[l] = q[v]; - g[l] = g[v]; - w[l] = w[v] - } - } else { - l++; - r[l] = r[v]; - h[l] = h[v]; - w[l] = w[v]; - C[l] = C[v]; - a[l] = a[v]; - y[l] = y[v]; - g[l] = g[v]; - q[l] = q[v]; - p[l] = p[v]; - B[l] = B[v]; - t = Ext.Date.add(t, b[0], b[1]) - } - } - if (l > f) { - m.map["time_" + b[2]] = [f, l] - } - } - }, - "double": function(h, u, j, a, t, b, c) { - var e = 0, - k, f = 1, - n, d, v, g, s, l, m, r, q, p, o; - while (u > e + 1) { - k = e; - e = u; - f += f; - for (n = k; n < e; n += 2) { - if (n === e - 1) { - d = h.startIdx[n]; - v = h.endIdx[n]; - g = h.minIdx[n]; - s = h.maxIdx[n]; - l = h.open[n]; - m = h.close[n]; - r = h.minX[n]; - q = h.minY[n]; - p = h.maxX[n]; - o = h.maxY[n] - } else { - d = h.startIdx[n]; - v = h.endIdx[n + 1]; - l = h.open[n]; - m = h.close[n]; - if (h.minY[n] <= h.minY[n + 1]) { - g = h.minIdx[n]; - r = h.minX[n]; - q = h.minY[n] - } else { - g = h.minIdx[n + 1]; - r = h.minX[n + 1]; - q = h.minY[n + 1] - } - if (h.maxY[n] >= h.maxY[n + 1]) { - s = h.maxIdx[n]; - p = h.maxX[n]; - o = h.maxY[n] - } else { - s = h.maxIdx[n + 1]; - p = h.maxX[n + 1]; - o = h.maxY[n + 1] - } - } - h.startIdx[u] = d; - h.endIdx[u] = v; - h.minIdx[u] = g; - h.maxIdx[u] = s; - h.open[u] = l; - h.close[u] = m; - h.minX[u] = r; - h.minY[u] = q; - h.maxX[u] = p; - h.maxY[u] = o; - u++ - } - h.map["double_" + f] = [e, u] - } - }, - none: Ext.emptyFn, - aggregateData: function(h, a, r, c, d) { - var b = h.length, - e = [], - s = [], - f = [], - q = [], - j = [], - p = [], - n = [], - o = [], - m = [], - k = [], - g = { - startIdx: e, - endIdx: s, - minIdx: f, - maxIdx: q, - open: j, - minX: p, - minY: n, - maxX: o, - maxY: m, - close: k - }, - l; - for (l = 0; l < b; l++) { - e[l] = l; - s[l] = l; - f[l] = l; - q[l] = l; - j[l] = a[l]; - p[l] = h[l]; - n[l] = c[l]; - o[l] = h[l]; - m[l] = r[l]; - k[l] = d[l] - } - g.map = { - original: [0, b] - }; - if (b) { - this[this.getStrategy()](g, b, h, a, r, c, d) - } - return g - }, - binarySearchMin: function(c, g, a, e) { - var b = this.dataX; - if (e <= b[c.startIdx[0]]) { - return g - } - if (e >= b[c.startIdx[a - 1]]) { - return a - 1 - } - while (g + 1 < a) { - var d = (g + a) >> 1, - f = b[c.startIdx[d]]; - if (f === e) { - return d - } else { - if (f < e) { - g = d - } else { - a = d - } - } - } - return g - }, - binarySearchMax: function(c, g, a, e) { - var b = this.dataX; - if (e <= b[c.endIdx[0]]) { - return g - } - if (e >= b[c.endIdx[a - 1]]) { - return a - 1 - } - while (g + 1 < a) { - var d = (g + a) >> 1, - f = b[c.endIdx[d]]; - if (f === e) { - return d - } else { - if (f < e) { - g = d - } else { - a = d - } - } - } - return a - }, - constructor: function(a) { - this.initConfig(a) - }, - setData: function(d, a, b, c, e) { - if (!b) { - e = c = b = a - } - this.dataX = d; - this.dataOpen = a; - this.dataHigh = b; - this.dataLow = c; - this.dataClose = e; - if (d.length === b.length && d.length === c.length) { - this.cache = this.aggregateData(d, a, b, c, e) - } - }, - getAggregation: function(d, k, i) { - if (!this.cache) { - return null - } - var c = Infinity, - g = this.dataX[this.dataX.length - 1] - this.dataX[0], - l = this.cache.map, - m = l.original, - a, e, j, b, f, h; - for (a in l) { - e = l[a]; - j = e[1] - e[0] - 1; - b = g / j; - if (i <= b && b < c) { - m = e; - c = b - } - } - f = Math.max(this.binarySearchMin(this.cache, m[0], m[1], d), m[0]); - h = Math.min(this.binarySearchMax(this.cache, m[0], m[1], k) + 1, m[1]); - return { - data: this.cache, - start: f, - end: h - } - } -}); -Ext.define("Ext.chart.series.sprite.Aggregative", { - extend: "Ext.chart.series.sprite.Cartesian", - requires: ["Ext.draw.LimitedCache", "Ext.draw.SegmentTree"], - inheritableStatics: { - def: { - processors: { - dataHigh: "data", - dataLow: "data", - dataClose: "data" - }, - aliases: { - dataOpen: "dataY" - }, - defaults: { - dataHigh: null, - dataLow: null, - dataClose: null - } - } - }, - config: { - aggregator: {} - }, - applyAggregator: function(b, a) { - return Ext.factory(b, Ext.draw.SegmentTree, a) - }, - constructor: function() { - this.callParent(arguments) - }, - processDataY: function() { - var d = this, - b = d.attr, - e = b.dataHigh, - a = b.dataLow, - f = b.dataClose, - c = b.dataY; - d.callParent(arguments); - if (b.dataX && c && c.length > 0) { - if (e) { - d.getAggregator().setData(b.dataX, b.dataY, e, a, f) - } else { - d.getAggregator().setData(b.dataX, b.dataY) - } - } - }, - getGapWidth: function() { - return 1 - }, - renderClipped: function(b, c, g, f) { - var e = this, - d = Math.min(g[0], g[2]), - a = Math.max(g[0], g[2]), - h = e.getAggregator() && e.getAggregator().getAggregation(d, a, (a - d) / f[2] * e.getGapWidth()); - if (h) { - e.dataStart = h.data.startIdx[h.start]; - e.dataEnd = h.data.endIdx[h.end - 1]; - e.renderAggregates(h.data, h.start, h.end, b, c, g, f) - } - } -}); -Ext.define("Ext.chart.series.sprite.CandleStick", { - alias: "sprite.candlestickSeries", - extend: "Ext.chart.series.sprite.Aggregative", - inheritableStatics: { - def: { - processors: { - raiseStyle: function(b, a) { - return Ext.merge({}, a || {}, b) - }, - dropStyle: function(b, a) { - return Ext.merge({}, a || {}, b) - }, - barWidth: "number", - padding: "number", - ohlcType: "enums(candlestick,ohlc)" - }, - defaults: { - raiseStyle: { - strokeStyle: "green", - fillStyle: "green" - }, - dropStyle: { - strokeStyle: "red", - fillStyle: "red" - }, - planar: false, - barWidth: 15, - padding: 3, - lineJoin: "miter", - miterLimit: 5, - ohlcType: "candlestick" - }, - triggers: { - raiseStyle: "raiseStyle", - dropStyle: "dropStyle" - }, - updaters: { - raiseStyle: function() { - this.raiseTemplate && this.raiseTemplate.setAttributes(this.attr.raiseStyle) - }, - dropStyle: function() { - this.dropTemplate && this.dropTemplate.setAttributes(this.attr.dropStyle) - } - } - } - }, - candlestick: function(i, c, a, e, h, f, b) { - var d = Math.min(c, h), - g = Math.max(c, h); - i.moveTo(f, e); - i.lineTo(f, g); - i.moveTo(f + b, g); - i.lineTo(f + b, d); - i.lineTo(f - b, d); - i.lineTo(f - b, g); - i.closePath(); - i.moveTo(f, a); - i.lineTo(f, d) - }, - ohlc: function(b, d, e, a, f, c, g) { - b.moveTo(c, e); - b.lineTo(c, a); - b.moveTo(c, d); - b.lineTo(c - g, d); - b.moveTo(c, f); - b.lineTo(c + g, f) - }, - constructor: function() { - this.callParent(arguments); - this.raiseTemplate = new Ext.draw.sprite.Rect({ - parent: this - }); - this.dropTemplate = new Ext.draw.sprite.Rect({ - parent: this - }) - }, - getGapWidth: function() { - var a = this.attr, - b = a.barWidth, - c = a.padding; - return b + c - }, - renderAggregates: function(d, c, b, t, u, z) { - var D = this, - s = this.attr, - j = s.dataX, - v = s.matrix, - e = v.getXX(), - r = v.getYY(), - l = v.getDX(), - h = v.getDY(), - o = s.barWidth / e, - C, k = s.ohlcType, - f = Math.round(o * 0.5 * e), - a = d.open, - y = d.close, - B = d.maxY, - p = d.minY, - q = d.startIdx, - m, g, E, n, A, x, w = s.lineWidth * t.devicePixelRatio / 2; - w -= Math.floor(w); - u.save(); - C = this.raiseTemplate; - C.useAttributes(u, z); - u.beginPath(); - for (x = c; x < b; x++) { - if (a[x] <= y[x]) { - m = Math.round(a[x] * r + h) + w; - g = Math.round(B[x] * r + h) + w; - E = Math.round(p[x] * r + h) + w; - n = Math.round(y[x] * r + h) + w; - A = Math.round(j[q[x]] * e + l) + w; - D[k](u, m, g, E, n, A, f) - } - } - u.fillStroke(C.attr); - u.restore(); - u.save(); - C = this.dropTemplate; - C.useAttributes(u, z); - u.beginPath(); - for (x = c; x < b; x++) { - if (a[x] > y[x]) { - m = Math.round(a[x] * r + h) + w; - g = Math.round(B[x] * r + h) + w; - E = Math.round(p[x] * r + h) + w; - n = Math.round(y[x] * r + h) + w; - A = Math.round(j[q[x]] * e + l) + w; - D[k](u, m, g, E, n, A, f) - } - } - u.fillStroke(C.attr); - u.restore() - } -}); -Ext.define("Ext.chart.series.CandleStick", { - extend: "Ext.chart.series.Cartesian", - requires: ["Ext.chart.series.sprite.CandleStick"], - alias: "series.candlestick", - type: "candlestick", - seriesType: "candlestickSeries", - config: { - openField: null, - highField: null, - lowField: null, - closeField: null - }, - fieldCategoryY: ["Open", "High", "Low", "Close"], - themeColorCount: function() { - return 2 - } -}); -Ext.define("Ext.chart.series.Polar", { - extend: "Ext.chart.series.Series", - config: { - rotation: 0, - radius: null, - center: [0, 0], - offsetX: 0, - offsetY: 0, - showInLegend: true, - xField: null, - yField: null, - angleField: null, - radiusField: null, - xAxis: null, - yAxis: null - }, - directions: ["X", "Y"], - fieldCategoryX: ["X"], - fieldCategoryY: ["Y"], - deprecatedConfigs: { - field: "angleField", - lengthField: "radiusField" - }, - constructor: function(b) { - var c = this, - a = c.getConfigurator(), - e = a.configs, - d; - if (b) { - for (d in c.deprecatedConfigs) { - if (d in b && !(b in e)) { - Ext.raise("'" + d + "' config has been deprecated. Please use the '" + c.deprecatedConfigs[d] + "' config instead.") - } - } - } - c.callParent([b]) - }, - getXField: function() { - return this.getAngleField() - }, - updateXField: function(a) { - this.setAngleField(a) - }, - getYField: function() { - return this.getRadiusField() - }, - updateYField: function(a) { - this.setRadiusField(a) - }, - applyXAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - applyYAxis: function(a, b) { - return this.getChart().getAxis(a) || b - }, - getXRange: function() { - return [this.dataRange[0], this.dataRange[2]] - }, - getYRange: function() { - return [this.dataRange[1], this.dataRange[3]] - }, - themeColorCount: function() { - var c = this, - a = c.getStore(), - b = a && a.getCount() || 0; - return b - }, - isStoreDependantColorCount: true, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer(), - centerX: 0, - centerY: 0, - rotationCenterX: 0, - rotationCenterY: 0 - } - }, - applyRotation: function(a) { - return Ext.draw.sprite.AttributeParser.angle(a) - }, - updateRotation: function(a) { - var b = this.getSprites(); - if (b && b[0]) { - b[0].setAttributes({ - baseRotation: a - }) - } - } -}); -Ext.define("Ext.chart.series.Gauge", { - alias: "series.gauge", - extend: "Ext.chart.series.Polar", - type: "gauge", - seriesType: "pieslice", - requires: ["Ext.draw.sprite.Sector"], - config: { - needle: false, - needleLength: 90, - needleWidth: 4, - donut: 30, - showInLegend: false, - value: null, - colors: null, - sectors: null, - minimum: 0, - maximum: 100, - rotation: 0, - totalAngle: Math.PI / 2, - rect: [0, 0, 1, 1], - center: [0.5, 0.75], - radius: 0.5, - wholeDisk: false - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - updateNeedle: function(b) { - var a = this, - d = a.getSprites(), - c = a.valueToAngle(a.getValue()); - if (d && d.length) { - d[0].setAttributes({ - startAngle: (b ? c : 0), - endAngle: c, - strokeOpacity: (b ? 1 : 0), - lineWidth: (b ? a.getNeedleWidth() : 0) - }); - a.doUpdateStyles() - } - }, - themeColorCount: function() { - var c = this, - a = c.getStore(), - b = a && a.getCount() || 0; - return b + (c.getNeedle() ? 0 : 1) - }, - updateColors: function(a, b) { - var f = this, - h = f.getSectors(), - j = h && h.length, - e = f.getSprites(), - c = Ext.Array.clone(a), - g = a && a.length, - d; - if (!g || !a[0]) { - return - } - for (d = 0; d < j; d++) { - c[d + 1] = h[d].color || c[d + 1] || a[d % g] - } - if (e.length) { - e[0].setAttributes({ - strokeStyle: c[0] - }) - } - this.setSubStyle({ - fillStyle: c, - strokeStyle: c - }); - this.doUpdateStyles() - }, - updateRect: function(f) { - var d = this.getWholeDisk(), - c = d ? Math.PI : this.getTotalAngle() / 2, - g = this.getDonut() / 100, - e, b, a; - if (c <= Math.PI / 2) { - e = 2 * Math.sin(c); - b = 1 - g * Math.cos(c) - } else { - e = 2; - b = 1 - Math.cos(c) - } - a = Math.min(f[2] / e, f[3] / b); - this.setRadius(a); - this.setCenter([f[2] / 2, a + (f[3] - b * a) / 2]) - }, - updateCenter: function(a) { - this.setStyle({ - centerX: a[0], - centerY: a[1], - rotationCenterX: a[0], - rotationCenterY: a[1] - }); - this.doUpdateStyles() - }, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a - (this.getTotalAngle() + Math.PI) / 2 - }); - this.doUpdateStyles() - }, - doUpdateShape: function(b, f) { - var a, d = this.getSectors(), - c = (d && d.length) || 0, - e = this.getNeedleLength() / 100; - a = [b * e, b]; - while (c--) { - a.push(b) - } - this.setSubStyle({ - endRho: a, - startRho: b / 100 * f - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - var b = this.getDonut(); - this.doUpdateShape(a, b) - }, - updateDonut: function(b) { - var a = this.getRadius(); - this.doUpdateShape(a, b) - }, - valueToAngle: function(a) { - a = this.applyValue(a); - return this.getTotalAngle() * (a - this.getMinimum()) / (this.getMaximum() - this.getMinimum()) - }, - applyValue: function(a) { - return Math.min(this.getMaximum(), Math.max(a, this.getMinimum())) - }, - updateValue: function(b) { - var a = this, - c = a.getNeedle(), - e = a.valueToAngle(b), - d = a.getSprites(); - d[0].rendererData.value = b; - d[0].setAttributes({ - startAngle: (c ? e : 0), - endAngle: e - }); - a.doUpdateStyles() - }, - processData: function() { - var f = this, - j = f.getStore(), - a, d, h, b, g, e = j && j.first(), - c, i; - if (e) { - c = f.getXField(); - if (c) { - i = e.get(c) - } - } - if (a = f.getXAxis()) { - d = a.getMinimum(); - h = a.getMaximum(); - b = a.getSprites()[0].fx; - g = b.getDuration(); - b.setDuration(0); - if (Ext.isNumber(d)) { - f.setMinimum(d) - } else { - a.setMinimum(f.getMinimum()) - } - if (Ext.isNumber(h)) { - f.setMaximum(h) - } else { - a.setMaximum(f.getMaximum()) - } - b.setDuration(g) - } - if (!Ext.isNumber(i)) { - i = f.getMinimum() - } - f.setValue(i) - }, - getDefaultSpriteConfig: function() { - return { - type: this.seriesType, - renderer: this.getRenderer(), - fx: { - customDurations: { - translationX: 0, - translationY: 0, - rotationCenterX: 0, - rotationCenterY: 0, - centerX: 0, - centerY: 0, - startRho: 0, - endRho: 0, - baseRotation: 0 - } - } - } - }, - normalizeSectors: function(f) { - var d = this, - c = (f && f.length) || 0, - b, e, g, a; - if (c) { - for (b = 0; b < c; b++) { - e = f[b]; - if (typeof e === "number") { - f[b] = { - start: (b > 0 ? f[b - 1].end : d.getMinimum()), - end: Math.min(e, d.getMaximum()) - }; - if (b == (c - 1) && f[b].end < d.getMaximum()) { - f[b + 1] = { - start: f[b].end, - end: d.getMaximum() - } - } - } else { - if (typeof e.start === "number") { - g = Math.max(e.start, d.getMinimum()) - } else { - g = (b > 0 ? f[b - 1].end : d.getMinimum()) - } - if (typeof e.end === "number") { - a = Math.min(e.end, d.getMaximum()) - } else { - a = d.getMaximum() - } - f[b].start = g; - f[b].end = a - } - } - } else { - f = [{ - start: d.getMinimum(), - end: d.getMaximum() - }] - } - return f - }, - getSprites: function() { - var j = this, - m = j.getStore(), - l = j.getValue(), - c, g; - if (!m && !Ext.isNumber(l)) { - return [] - } - var h = j.getChart(), - b = j.getAnimation() || h && h.getAnimation(), - f = j.sprites, - k = 0, - o, n, e, d, a = []; - if (f && f.length) { - f[0].setAnimation(b); - return f - } - d = { - store: m, - field: j.getXField(), - angleField: j.getXField(), - value: l, - series: j - }; - o = j.createSprite(); - o.setAttributes({ - zIndex: 10 - }, true); - o.rendererData = d; - o.rendererIndex = k++; - a.push(j.getNeedleWidth()); - j.getLabel().getTemplate().setField(true); - n = j.normalizeSectors(j.getSectors()); - for (c = 0, g = n.length; c < g; c++) { - e = { - startAngle: j.valueToAngle(n[c].start), - endAngle: j.valueToAngle(n[c].end), - label: n[c].label, - fillStyle: n[c].color, - strokeOpacity: 0, - doCallout: false, - labelOverflowPadding: -1 - }; - Ext.apply(e, n[c].style); - o = j.createSprite(); - o.rendererData = d; - o.rendererIndex = k++; - o.setAttributes(e, true); - a.push(e.lineWidth) - } - j.setSubStyle({ - lineWidth: a - }); - j.doUpdateStyles(); - return f - } -}); -Ext.define("Ext.chart.series.sprite.Line", { - alias: "sprite.lineSeries", - extend: "Ext.chart.series.sprite.Aggregative", - inheritableStatics: { - def: { - processors: { - smooth: "bool", - fillArea: "bool", - step: "bool", - preciseStroke: "bool", - xAxis: "default", - yCap: "default" - }, - defaults: { - smooth: false, - fillArea: false, - step: false, - preciseStroke: true, - xAxis: null, - yCap: Math.pow(2, 20), - yJump: 50 - }, - triggers: { - dataX: "dataX,bbox,smooth", - dataY: "dataY,bbox,smooth", - smooth: "smooth" - }, - updaters: { - smooth: function(a) { - var c = a.dataX, - b = a.dataY; - if (a.smooth && c && b && c.length > 2 && b.length > 2) { - this.smoothX = Ext.draw.Draw.spline(c); - this.smoothY = Ext.draw.Draw.spline(b) - } else { - delete this.smoothX; - delete this.smoothY - } - } - } - } - }, - list: null, - updatePlainBBox: function(d) { - var b = this.attr, - c = Math.min(0, b.dataMinY), - a = Math.max(0, b.dataMaxY); - d.x = b.dataMinX; - d.y = c; - d.width = b.dataMaxX - b.dataMinX; - d.height = a - c - }, - drawStrip: function(a, c) { - a.moveTo(c[0], c[1]); - for (var b = 2, d = c.length; b < d; b += 2) { - a.lineTo(c[b], c[b + 1]) - } - }, - drawStraightStroke: function(p, q, e, d, u, h) { - var w = this, - o = w.attr, - n = o.renderer, - g = o.step, - a = true, - l = { - type: "line", - smooth: false, - step: g - }, - m = [], - l, z, v, f, k, j, t, c, s, b, r; - for (r = 3; r < u.length; r += 3) { - t = u[r - 3]; - c = u[r - 2]; - k = u[r]; - j = u[r + 1]; - s = u[r + 3]; - b = u[r + 4]; - if (n) { - l.x = k; - l.y = j; - l.x0 = t; - l.y0 = c; - v = [w, l, w.rendererData, e + r / 3]; - z = Ext.callback(n, null, v, 0, w.getSeries()) - } - if (Ext.isNumber(k + j + t + c)) { - if (a) { - q.beginPath(); - q.moveTo(t, c); - m.push(t, c); - f = t; - a = false - } - } else { - continue - } - if (g) { - q.lineTo(k, c); - m.push(k, c) - } - q.lineTo(k, j); - m.push(k, j); - if (z || !(Ext.isNumber(s + b))) { - q.save(); - Ext.apply(q, z); - if (o.fillArea) { - q.lineTo(k, h); - q.lineTo(f, h); - q.closePath(); - q.fill() - } - q.beginPath(); - w.drawStrip(q, m); - m = []; - q.stroke(); - q.restore(); - q.beginPath(); - a = true - } - } - }, - calculateScale: function(c, a) { - var b = 0, - d = c; - while (d < a && c > 0) { - b++; - d += c >> b - } - return Math.pow(2, b > 0 ? b - 1 : b) - }, - drawSmoothStroke: function(u, v, c, b, C, f) { - var G = this, - t = G.attr, - d = t.step, - z = t.matrix, - s = t.renderer, - e = z.getXX(), - p = z.getYY(), - m = z.getDX(), - k = z.getDY(), - r = G.smoothX, - q = G.smoothY, - I = G.calculateScale(t.dataX.length, b), - o, F, n, E, h, g, B, a, A, w, H, D, l = { - type: "line", - smooth: true, - step: d - }; - v.beginPath(); - v.moveTo(r[c * 3] * e + m, q[c * 3] * p + k); - for (A = 0, w = c * 3 + 1; A < C.length - 3; A += 3, w += 3 * I) { - o = r[w] * e + m; - F = q[w] * p + k; - n = r[w + 1] * e + m; - E = q[w + 1] * p + k; - h = u.roundPixel(C[A + 3]); - g = C[A + 4]; - B = u.roundPixel(C[A]); - a = C[A + 1]; - if (s) { - l.x0 = B; - l.y0 = a; - l.cx1 = o; - l.cy1 = F; - l.cx2 = n; - l.cy2 = E; - l.x = h; - l.y = g; - D = [G, l, G.rendererData, c + A / 3 + 1]; - H = Ext.callback(s, null, D, 0, G.getSeries()); - v.save(); - Ext.apply(v, H) - } - if (t.fillArea) { - v.moveTo(B, a); - v.bezierCurveTo(o, F, n, E, h, g); - v.lineTo(h, f); - v.lineTo(B, f); - v.lineTo(B, a); - v.closePath(); - v.fill(); - v.beginPath() - } - v.moveTo(B, a); - v.bezierCurveTo(o, F, n, E, h, g); - v.stroke(); - v.moveTo(B, a); - v.closePath(); - if (s) { - v.restore() - } - v.beginPath(); - v.moveTo(h, g) - } - v.beginPath() - }, - drawLabel: function(k, i, h, o, a) { - var q = this, - n = q.attr, - e = q.getMarker("labels"), - d = e.getTemplate(), - m = q.labelCfg || (q.labelCfg = {}), - c = q.surfaceMatrix, - g, f, j = n.labelOverflowPadding, - l, b, r, p, s; - m.x = c.x(i, h); - m.y = c.y(i, h); - if (n.flipXY) { - m.rotationRads = Math.PI * 0.5 - } else { - m.rotationRads = 0 - } - m.text = k; - if (d.attr.renderer) { - p = [k, e, m, q.rendererData, o]; - r = Ext.callback(d.attr.renderer, null, p, 0, q.getSeries()); - if (typeof r === "string") { - m.text = r - } else { - if (typeof r === "object") { - if ("text" in r) { - m.text = r.text - } - s = true - } - } - } - b = q.getMarkerBBox("labels", o, true); - if (!b) { - q.putMarker("labels", m, o); - b = q.getMarkerBBox("labels", o, true) - } - l = b.height / 2; - g = i; - switch (d.attr.display) { - case "under": - f = h - l - j; - break; - case "rotate": - g += j; - f = h - j; - m.rotationRads = -Math.PI / 4; - break; - default: - f = h + l + j - } - m.x = c.x(g, f); - m.y = c.y(g, f); - if (s) { - Ext.apply(m, r) - } - q.putMarker("labels", m, o) - }, - drawMarker: function(j, h, d) { - var g = this, - e = g.attr, - f = e.renderer, - c = g.surfaceMatrix, - b = {}, - i, a; - if (f && g.getMarker("markers")) { - b.type = "marker"; - b.x = j; - b.y = h; - a = [g, b, g.rendererData, d]; - i = Ext.callback(f, null, a, 0, g.getSeries()); - if (i) { - Ext.apply(b, i) - } - } - b.translationX = c.x(j, h); - b.translationY = c.y(j, h); - delete b.x; - delete b.y; - g.putMarker("markers", b, d, !f) - }, - drawStroke: function(a, c, h, b, f, e) { - var d = this, - g = d.attr.smooth && d.smoothX && d.smoothY; - if (g) { - d.drawSmoothStroke(a, c, h, b, f, e) - } else { - d.drawStraightStroke(a, c, h, b, f, e) - } - }, - renderAggregates: function(B, w, l, N, o, I, D) { - var m = this, - k = m.attr, - s = k.dataX, - r = k.dataY, - h = k.labels, - v = k.xAxis, - a = k.yCap, - g = k.smooth && m.smoothX && m.smoothY, - d = h && m.getMarker("labels"), - t = m.getMarker("markers"), - E = k.matrix, - u = N.devicePixelRatio, - C = E.getXX(), - f = E.getYY(), - c = E.getDX(), - b = E.getDY(), - q = m.list || (m.list = []), - F = B.minX, - e = B.maxX, - j = B.minY, - P = B.maxY, - U = B.startIdx, - S = true, - Q, T, L, K, R, G; - m.rendererData = { - store: m.getStore() - }; - q.length = 0; - for (R = w; R < l; R++) { - var O = F[R], - p = e[R], - M = j[R], - n = P[R]; - if (O < p) { - q.push(O * C + c, M * f + b, U[R]); - q.push(p * C + c, n * f + b, U[R]) - } else { - if (O > p) { - q.push(p * C + c, n * f + b, U[R]); - q.push(O * C + c, M * f + b, U[R]) - } else { - q.push(p * C + c, n * f + b, U[R]) - } - } - } - if (q.length) { - for (R = 0; R < q.length; R += 3) { - L = q[R]; - K = q[R + 1]; - if (Ext.isNumber(L + K)) { - if (K > a) { - K = a - } else { - if (K < -a) { - K = -a - } - } - q[R + 1] = K - } else { - S = false; - continue - } - G = q[R + 2]; - if (t) { - m.drawMarker(L, K, G) - } - if (d && h[G]) { - m.drawLabel(h[G], L, K, G, D) - } - } - m.isContinuousLine = S; - if (g && !S) { - Ext.raise("Line smoothing in only supported for gapless data, where all data points are finite numbers.") - } - if (v) { - T = v.getAlignment() === "vertical"; - if (Ext.isNumber(v.floatingAtCoord)) { - Q = (T ? D[2] : D[3]) - v.floatingAtCoord - } else { - Q = T ? D[0] : D[1] - } - } else { - Q = k.flipXY ? D[0] : D[1] - } - if (k.preciseStroke) { - if (k.fillArea) { - o.fill() - } - if (k.transformFillStroke) { - k.inverseMatrix.toContext(o) - } - m.drawStroke(N, o, w, l, q, Q); - if (k.transformFillStroke) { - k.matrix.toContext(o) - } - o.stroke() - } else { - m.drawStroke(N, o, w, l, q, Q); - if (S && g && k.fillArea && !k.renderer) { - var A = s[s.length - 1] * C + c + u, - z = r[r.length - 1] * f + b, - J = s[0] * C + c - u, - H = r[0] * f + b; - o.lineTo(A, z); - o.lineTo(A, Q - k.lineWidth); - o.lineTo(J, Q - k.lineWidth); - o.lineTo(J, H) - } - if (k.transformFillStroke) { - k.matrix.toContext(o) - } - if (k.fillArea) { - o.fillStroke(k, true) - } else { - o.stroke(true) - } - } - } - } -}); -Ext.define("Ext.chart.series.Line", { - extend: "Ext.chart.series.Cartesian", - alias: "series.line", - type: "line", - seriesType: "lineSeries", - requires: ["Ext.chart.series.sprite.Line"], - config: { - selectionTolerance: 20, - smooth: false, - step: false, - fill: undefined, - aggregator: { - strategy: "double" - } - }, - defaultSmoothness: 3, - overflowBuffer: 1, - themeMarkerCount: function() { - return 1 - }, - getDefaultSpriteConfig: function() { - var d = this, - e = d.callParent(arguments), - c = Ext.apply({}, d.getStyle()), - b, a = false; - if (typeof d.config.fill != "undefined") { - if (d.config.fill) { - a = true; - if (typeof c.fillStyle == "undefined") { - if (typeof c.strokeStyle == "undefined") { - b = d.getStyleWithTheme(); - c.fillStyle = b.fillStyle; - c.strokeStyle = b.strokeStyle - } else { - c.fillStyle = c.strokeStyle - } - } - } - } else { - if (c.fillStyle) { - a = true - } - } - if (!a) { - delete c.fillStyle - } - c = Ext.apply(e || {}, c); - return Ext.apply(c, { - fillArea: a, - step: d.config.step, - smooth: d.config.smooth, - selectionTolerance: d.config.selectionTolerance - }) - }, - updateStep: function(b) { - var a = this.getSprites()[0]; - if (a && a.attr.step !== b) { - a.setAttributes({ - step: b - }) - } - }, - updateFill: function(b) { - var a = this.getSprites()[0]; - if (a && a.attr.fillArea !== b) { - a.setAttributes({ - fillArea: b - }) - } - }, - updateSmooth: function(a) { - var b = this.getSprites()[0]; - if (b && b.attr.smooth !== a) { - b.setAttributes({ - smooth: a - }) - } - } -}); -Ext.define("Ext.chart.series.sprite.PieSlice", { - extend: "Ext.draw.sprite.Sector", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - alias: "sprite.pieslice", - inheritableStatics: { - def: { - processors: { - doCallout: "bool", - label: "string", - rotateLabels: "bool", - labelOverflowPadding: "number", - renderer: "default" - }, - defaults: { - doCallout: true, - rotateLabels: true, - label: "", - labelOverflowPadding: 10, - renderer: null - } - } - }, - config: { - rendererData: null, - rendererIndex: 0, - series: null - }, - setGradientBBox: function(q, k) { - var j = this, - i = j.attr, - g = (i.fillStyle && i.fillStyle.isGradient) || (i.strokeStyle && i.strokeStyle.isGradient); - if (g && !i.constrainGradients) { - var b = j.getMidAngle(), - d = i.margin, - e = i.centerX, - c = i.centerY, - a = i.endRho, - l = i.matrix, - o = l.getScaleX(), - n = l.getScaleY(), - m = o * a, - f = n * a, - p = { - width: m + m, - height: f + f - }; - if (d) { - e += d * Math.cos(b); - c += d * Math.sin(b) - } - p.x = l.x(e, c) - m; - p.y = l.y(e, c) - f; - q.setGradientBBox(p) - } else { - j.callParent([q, k]) - } - }, - render: function(b, c, g, f) { - var e = this, - a = e.attr, - h = {}, - d; - if (a.renderer) { - h = { - type: "sector", - text: a.text, - centerX: a.centerX, - centerY: a.centerY, - margin: a.margin, - startAngle: Math.min(a.startAngle, a.endAngle), - endAngle: Math.max(a.startAngle, a.endAngle), - startRho: Math.min(a.startRho, a.endRho), - endRho: Math.max(a.startRho, a.endRho) - }; - d = Ext.callback(a.renderer, null, [e, h, e.rendererData, e.rendererIndex], 0, e.getSeries()); - e.setAttributes(d); - e.useAttributes(c, g) - } - e.callParent([b, c, g, f]); - if (a.label && e.getMarker("labels")) { - e.placeLabel() - } - }, - placeLabel: function() { - var z = this, - s = z.attr, - r = s.attributeId, - t = Math.min(s.startAngle, s.endAngle), - p = Math.max(s.startAngle, s.endAngle), - k = (t + p) * 0.5, - n = s.margin, - h = s.centerX, - g = s.centerY, - f = Math.sin(k), - c = Math.cos(k), - v = Math.min(s.startRho, s.endRho) + n, - m = Math.max(s.startRho, s.endRho) + n, - l = (v + m) * 0.5, - b = z.surfaceMatrix, - o = z.labelCfg || (z.labelCfg = {}), - e = z.getMarker("labels"), - d = e.getTemplate(), - a = d.getCalloutLine(), - q = a && a.length || 40, - u, j, i, A, w; - b.appendMatrix(s.matrix); - o.text = s.label; - j = h + c * l; - i = g + f * l; - o.x = b.x(j, i); - o.y = b.y(j, i); - j = h + c * m; - i = g + f * m; - o.calloutStartX = b.x(j, i); - o.calloutStartY = b.y(j, i); - j = h + c * (m + q); - i = g + f * (m + q); - o.calloutPlaceX = b.x(j, i); - o.calloutPlaceY = b.y(j, i); - if (!s.rotateLabels) { - o.rotationRads = 0 - } else { - switch (d.attr.orientation) { - case "horizontal": - o.rotationRads = k + Math.atan2(b.y(1, 0) - b.y(0, 0), b.x(1, 0) - b.x(0, 0)) + Math.PI / 2; - break; - case "vertical": - o.rotationRads = k + Math.atan2(b.y(1, 0) - b.y(0, 0), b.x(1, 0) - b.x(0, 0)); - break - } - } - o.calloutColor = (a && a.color) || z.attr.fillStyle; - if (a) { - if (a.width) { - o.calloutWidth = a.width - } - } else { - o.calloutHasLine = false - } - o.globalAlpha = s.globalAlpha * s.fillOpacity; - o.hidden = (s.startAngle == s.endAngle); - if (d.attr.renderer) { - w = [z.attr.label, e, o, z.rendererData, z.rendererIndex]; - A = Ext.callback(d.attr.renderer, null, w, 0, z.getSeries()); - if (typeof A === "string") { - o.text = A - } else { - Ext.apply(o, A) - } - } - z.putMarker("labels", o, r); - u = z.getMarkerBBox("labels", r, true); - if (u) { - if (s.doCallout) { - if (d.attr.display === "outside") { - z.putMarker("labels", { - callout: 1 - }, r) - } else { - if (d.attr.display === "inside") { - z.putMarker("labels", { - callout: 0 - }, r) - } else { - z.putMarker("labels", { - callout: 1 - z.sliceContainsLabel(s, u) - }, r) - } - } - } else { - z.putMarker("labels", { - globalAlpha: z.sliceContainsLabel(s, u) - }, r) - } - } - }, - sliceContainsLabel: function(d, f) { - var e = d.labelOverflowPadding, - h = (d.endRho + d.startRho) / 2, - g = h + (f.width + e) / 2, - i = h - (f.width + e) / 2, - j, c, b, a; - if (e < 0) { - return 1 - } - if (f.width + e * 2 > (d.endRho - d.startRho)) { - return 0 - } - c = Math.sqrt(d.endRho * d.endRho - g * g); - b = Math.sqrt(d.endRho * d.endRho - i * i); - j = Math.abs(d.endAngle - d.startAngle); - a = (j > Math.PI / 2 ? i : Math.abs(Math.tan(j / 2)) * i); - if (f.height + e * 2 > Math.min(c, b, a) * 2) { - return 0 - } - return 1 - } -}); -Ext.define("Ext.chart.series.Pie", { - extend: "Ext.chart.series.Polar", - requires: ["Ext.chart.series.sprite.PieSlice"], - type: "pie", - alias: "series.pie", - seriesType: "pieslice", - config: { - donut: 0, - rotation: 0, - clockwise: true, - totalAngle: 2 * Math.PI, - hidden: [], - radiusFactor: 100, - highlightCfg: { - margin: 20 - }, - style: {} - }, - directions: ["X"], - applyLabel: function(a, b) { - if (Ext.isObject(a) && !Ext.isString(a.orientation)) { - Ext.apply(a = Ext.Object.chain(a), { - orientation: "vertical" - }) - } - return this.callParent([a, b]) - }, - updateLabelData: function() { - var h = this, - j = h.getStore(), - g = j.getData().items, - e = h.getSprites(), - a = h.getLabel().getTemplate().getField(), - d = h.getHidden(), - b, f, c, k; - if (e.length && a) { - c = []; - for (b = 0, f = g.length; b < f; b++) { - c.push(g[b].get(a)) - } - for (b = 0, f = e.length; b < f; b++) { - k = e[b]; - k.setAttributes({ - label: c[b] - }); - k.putMarker("labels", { - hidden: d[b] - }, k.attr.attributeId) - } - } - }, - coordinateX: function() { - var t = this, - f = t.getStore(), - q = f.getData().items, - c = q.length, - b = t.getXField(), - e = t.getYField(), - l, a = 0, - m, k, s = 0, - o = t.getHidden(), - d = [], - p, g = 0, - h = t.getTotalAngle(), - r = t.getClockwise() ? 1 : -1, - j = t.getSprites(), - n; - if (!j) { - return - } - for (p = 0; p < c; p++) { - l = Math.abs(Number(q[p].get(b))) || 0; - k = e && Math.abs(Number(q[p].get(e))) || 0; - if (!o[p]) { - a += l; - if (k > s) { - s = k - } - } - d[p] = a; - if (p >= o.length) { - o[p] = false - } - } - o.length = c; - t.maxY = s; - if (a !== 0) { - m = h / a - } - for (p = 0; p < c; p++) { - j[p].setAttributes({ - startAngle: g, - endAngle: g = (m ? r * d[p] * m : 0), - globalAlpha: 1 - }) - } - if (c < t.sprites.length) { - for (p = c; p < t.sprites.length; p++) { - n = t.sprites[p]; - n.getMarker("labels").clear(n.getId()); - n.releaseMarker("labels"); - n.destroy() - } - t.sprites.length = c - } - for (p = c; p < t.sprites.length; p++) { - j[p].setAttributes({ - startAngle: h, - endAngle: h, - globalAlpha: 0 - }) - } - t.getChart().refreshLegendStore() - }, - updateCenter: function(a) { - this.setStyle({ - translationX: a[0] + this.getOffsetX(), - translationY: a[1] + this.getOffsetY() - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - this.setStyle({ - startRho: a * this.getDonut() * 0.01, - endRho: a * this.getRadiusFactor() * 0.01 - }); - this.doUpdateStyles() - }, - getStyleByIndex: function(c) { - var g = this, - j = g.getStore(), - k = j.getAt(c), - f = g.getYField(), - d = g.getRadius(), - a = {}, - e, b, h; - if (k) { - h = f && Math.abs(Number(k.get(f))) || 0; - e = d * g.getDonut() * 0.01; - b = d * g.getRadiusFactor() * 0.01; - a = g.callParent([c]); - a.startRho = e; - a.endRho = g.maxY ? (e + (b - e) * h / g.maxY) : b - } - return a - }, - updateDonut: function(b) { - var a = this.getRadius(); - this.setStyle({ - startRho: a * b * 0.01, - endRho: a * this.getRadiusFactor() * 0.01 - }); - this.doUpdateStyles() - }, - rotationOffset: -Math.PI / 2, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a + this.rotationOffset - }); - this.doUpdateStyles() - }, - updateTotalAngle: function(a) { - this.processData() - }, - getSprites: function() { - var k = this, - h = k.getChart(), - n = k.getStore(); - if (!h || !n) { - return [] - } - k.getColors(); - k.getSubStyle(); - var j = n.getData().items, - b = j.length, - d = k.getAnimation() || h && h.getAnimation(), - g = k.sprites, - o, l = 0, - f, e, c = false, - m = k.getLabel(), - a = m.getTemplate(); - f = { - store: n, - field: k.getXField(), - angleField: k.getXField(), - radiusField: k.getYField(), - series: k - }; - for (e = 0; e < b; e++) { - o = g[e]; - if (!o) { - o = k.createSprite(); - if (k.getHighlight()) { - o.config.highlight = k.getHighlight(); - o.addModifier("highlight", true) - } - if (a.getField()) { - a.setAttributes({ - labelOverflowPadding: k.getLabelOverflowPadding() - }); - a.fx.setCustomDurations({ - callout: 200 - }) - } - o.setAttributes(k.getStyleByIndex(e)); - o.rendererData = f; - o.rendererIndex = l++; - c = true - } - o.setAnimation(d) - } - if (c) { - k.doUpdateStyles() - } - return k.sprites - }, - betweenAngle: function(d, f, c) { - var e = Math.PI * 2, - g = this.rotationOffset; - if (!this.getClockwise()) { - d *= -1; - f *= -1; - c *= -1; - f -= g; - c -= g - } else { - f += g; - c += g - } - d -= f; - c -= f; - d %= e; - c %= e; - d += e; - c += e; - d %= e; - c %= e; - return d < c || c === 0 - }, - getItemForAngle: function(a) { - var h = this, - f = h.getSprites(), - d; - a %= Math.PI * 2; - while (a < 0) { - a += Math.PI * 2 - } - if (f) { - var j = h.getStore(), - g = j.getData().items, - c = h.getHidden(), - b = 0, - e = j.getCount(); - for (; b < e; b++) { - if (!c[b]) { - d = f[b].attr; - if (d.startAngle <= a && d.endAngle >= a) { - return { - series: h, - sprite: f[b], - index: b, - record: g[b], - field: h.getXField() - } - } - } - } - } - return null - }, - getItemForPoint: function(f, e) { - var t = this, - c = t.getSprites(); - if (c) { - var s = t.getCenter(), - q = t.getOffsetX(), - p = t.getOffsetY(), - j = f - s[0] + q, - h = e - s[1] + p, - b = t.getStore(), - g = t.getDonut(), - o = b.getData().items, - r = Math.atan2(h, j) - t.getRotation(), - a = Math.sqrt(j * j + h * h), - l = t.getRadius() * g * 0.01, - m = t.getHidden(), - n, d, k; - for (n = 0, d = o.length; n < d; n++) { - if (!m[n]) { - k = c[n].attr; - if (a >= l + k.margin && a <= k.endRho + k.margin) { - if (t.betweenAngle(r, k.startAngle, k.endAngle)) { - return { - series: t, - sprite: c[n], - index: n, - record: o[n], - field: t.getXField() - } - } - } - } - } - return null - } - }, - provideLegendInfo: function(f) { - var h = this, - j = h.getStore(); - if (j) { - var g = j.getData().items, - b = h.getLabel().getTemplate().getField(), - c = h.getXField(), - e = h.getHidden(), - d, a, k; - for (d = 0; d < g.length; d++) { - a = h.getStyleByIndex(d); - k = a.fillStyle; - if (Ext.isObject(k)) { - k = k.stops && k.stops[0].color - } - f.push({ - name: b ? String(g[d].get(b)) : c + " " + d, - mark: k || a.strokeStyle || "black", - disabled: e[d], - series: h.getId(), - index: d - }) - } - } - } -}); -Ext.define("Ext.chart.series.sprite.Pie3DPart", { - extend: "Ext.draw.sprite.Path", - mixins: { - markerHolder: "Ext.chart.MarkerHolder" - }, - alias: "sprite.pie3dPart", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - margin: "number", - thickness: "number", - bevelWidth: "number", - distortion: "number", - baseColor: "color", - colorSpread: "number", - baseRotation: "number", - part: "enums(top,bottom,start,end,innerFront,innerBack,outerFront,outerBack)", - label: "string" - }, - aliases: { - rho: "endRho" - }, - triggers: { - centerX: "path,bbox", - centerY: "path,bbox", - startAngle: "path,partZIndex", - endAngle: "path,partZIndex", - startRho: "path", - endRho: "path,bbox", - margin: "path,bbox", - thickness: "path", - distortion: "path", - baseRotation: "path,partZIndex", - baseColor: "partZIndex,partColor", - colorSpread: "partColor", - part: "path,partZIndex", - globalAlpha: "canvas,alpha" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: Math.PI * 2, - endAngle: Math.PI * 2, - startRho: 0, - endRho: 150, - margin: 0, - thickness: 35, - distortion: 0.5, - baseRotation: 0, - baseColor: "white", - colorSpread: 1, - miterLimit: 1, - bevelWidth: 5, - strokeOpacity: 0, - part: "top", - label: "" - }, - updaters: { - alpha: "alphaUpdater", - partColor: "partColorUpdater", - partZIndex: "partZIndexUpdater" - } - } - }, - bevelParams: [], - constructor: function(a) { - this.callParent([a]); - this.bevelGradient = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: "rgba(255,255,255,0)" - }, { - offset: 0.7, - color: "rgba(255,255,255,0.6)" - }, { - offset: 1, - color: "rgba(255,255,255,0)" - }] - }) - }, - alphaUpdater: function(a) { - var d = this, - c = a.globalAlpha, - b = d.oldOpacity; - if (c !== b && (c === 1 || b === 1)) { - d.scheduleUpdater(a, "path", ["globalAlpha"]); - d.oldOpacity = c - } - }, - partColorUpdater: function(a) { - var d = Ext.draw.Color.fly(a.baseColor), - b = d.toString(), - e = a.colorSpread, - c; - switch (a.part) { - case "top": - c = new Ext.draw.gradient.Radial({ - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - }, - stops: [{ - offset: 0, - color: d.createLighter(0.1 * e) - }, { - offset: 1, - color: d.createDarker(0.1 * e) - }] - }); - break; - case "bottom": - c = new Ext.draw.gradient.Radial({ - start: { - x: 0, - y: 0, - r: 0 - }, - end: { - x: 0, - y: 0, - r: 1 - }, - stops: [{ - offset: 0, - color: d.createDarker(0.2 * e) - }, { - offset: 1, - color: d.toString() - }] - }); - break; - case "outerFront": - case "outerBack": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.15 * e).toString() - }, { - offset: 0.3, - color: b - }, { - offset: 0.8, - color: d.createLighter(0.2 * e).toString() - }, { - offset: 1, - color: d.createDarker(0.25 * e).toString() - }] - }); - break; - case "start": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 1, - color: d.createLighter(0.2 * e).toString() - }] - }); - break; - case "end": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 1, - color: d.createLighter(0.2 * e).toString() - }] - }); - break; - case "innerFront": - case "innerBack": - c = new Ext.draw.gradient.Linear({ - stops: [{ - offset: 0, - color: d.createDarker(0.1 * e).toString() - }, { - offset: 0.2, - color: d.createLighter(0.2 * e).toString() - }, { - offset: 0.7, - color: b - }, { - offset: 1, - color: d.createDarker(0.1 * e).toString() - }] - }); - break - } - a.fillStyle = c; - a.canvasAttributes.fillStyle = c - }, - partZIndexUpdater: function(a) { - var c = Ext.draw.sprite.AttributeParser.angle, - e = a.baseRotation, - d = a.startAngle, - b = a.endAngle, - f; - switch (a.part) { - case "top": - a.zIndex = 5; - break; - case "outerFront": - d = c(d + e); - b = c(b + e); - if (d >= 0 && b < 0) { - f = Math.sin(d) - } else { - if (d <= 0 && b > 0) { - f = Math.sin(b) - } else { - if (d >= 0 && b > 0) { - if (d > b) { - f = 0 - } else { - f = Math.max(Math.sin(d), Math.sin(b)) - } - } else { - f = 1 - } - } - } - a.zIndex = 4 + f; - break; - case "outerBack": - a.zIndex = 1; - break; - case "start": - a.zIndex = 4 + Math.sin(c(d + e)); - break; - case "end": - a.zIndex = 4 + Math.sin(c(b + e)); - break; - case "innerFront": - a.zIndex = 2; - break; - case "innerBack": - a.zIndex = 4 + Math.sin(c((d + b) / 2 + e)); - break; - case "bottom": - a.zIndex = 0; - break - } - a.dirtyZIndex = true - }, - updatePlainBBox: function(k) { - var f = this.attr, - a = f.part, - b = f.baseRotation, - e = f.centerX, - d = f.centerY, - j, c, i, h, g, l; - if (a === "start") { - c = f.startAngle + b - } else { - if (a === "end") { - c = f.endAngle + b - } - } - if (Ext.isNumber(c)) { - g = Math.sin(c); - l = Math.cos(c); - i = Math.min(e + l * f.startRho, e + l * f.endRho); - h = d + g * f.startRho * f.distortion; - k.x = i; - k.y = h; - k.width = l * (f.endRho - f.startRho); - k.height = f.thickness + g * (f.endRho - f.startRho) * 2; - return - } - if (a === "innerFront" || a === "innerBack") { - j = f.startRho - } else { - j = f.endRho - } - k.width = j * 2; - k.height = j * f.distortion * 2 + f.thickness; - k.x = f.centerX - j; - k.y = f.centerY - j * f.distortion - }, - updateTransformedBBox: function(a) { - if (this.attr.part === "start" || this.attr.part === "end") { - return this.callParent(arguments) - } - return this.updatePlainBBox(a) - }, - updatePath: function(a) { - if (!this.attr.globalAlpha) { - return - } - if (this.attr.endAngle < this.attr.startAngle) { - return - } - this[this.attr.part + "Renderer"](a) - }, - render: function(b, c) { - var d = this, - a = d.attr; - if (!a.globalAlpha) { - return - } - d.callParent([b, c]); - d.bevelRenderer(b, c); - if (a.label && d.getMarker("labels")) { - d.placeLabel() - } - }, - placeLabel: function() { - var z = this, - u = z.attr, - t = u.attributeId, - p = u.margin, - c = u.distortion, - i = u.centerX, - h = u.centerY, - j = u.baseRotation, - v = u.startAngle + j, - r = u.endAngle + j, - m = (v + r) / 2, - w = u.startRho + p, - o = u.endRho + p, - n = (w + o) / 2, - a = Math.sin(m), - b = Math.cos(m), - e = z.surfaceMatrix, - g = z.getMarker("labels"), - f = g.getTemplate(), - d = f.getCalloutLine(), - s = d && d.length || 40, - q = {}, - l, k; - e.appendMatrix(u.matrix); - q.text = u.label; - l = i + b * n; - k = h + a * n * c; - q.x = e.x(l, k); - q.y = e.y(l, k); - l = i + b * o; - k = h + a * o * c; - q.calloutStartX = e.x(l, k); - q.calloutStartY = e.y(l, k); - l = i + b * (o + s); - k = h + a * (o + s) * c; - q.calloutPlaceX = e.x(l, k); - q.calloutPlaceY = e.y(l, k); - q.calloutWidth = 2; - z.putMarker("labels", q, t); - z.putMarker("labels", { - callout: 1 - }, t) - }, - bevelRenderer: function(b, c) { - var f = this, - a = f.attr, - e = a.bevelWidth, - g = f.bevelParams, - d; - for (d = 0; d < g.length; d++) { - c.beginPath(); - c.ellipse.apply(c, g[d]); - c.save(); - c.lineWidth = e; - c.strokeOpacity = e ? 1 : 0; - c.strokeGradient = f.bevelGradient; - c.stroke(a); - c.restore() - } - }, - lidRenderer: function(o, m) { - var k = this.attr, - g = k.margin, - c = k.distortion, - i = k.centerX, - h = k.centerY, - f = k.baseRotation, - j = k.startAngle + f, - e = k.endAngle + f, - d = (j + e) / 2, - l = k.startRho, - b = k.endRho, - n = Math.sin(e), - a = Math.cos(e); - i += Math.cos(d) * g; - h += Math.sin(d) * g * c; - o.ellipse(i, h + m, l, l * c, 0, j, e, false); - o.lineTo(i + a * b, h + m + n * b * c); - o.ellipse(i, h + m, b, b * c, 0, e, j, true); - o.closePath() - }, - topRenderer: function(a) { - this.lidRenderer(a, 0) - }, - bottomRenderer: function(b) { - var a = this.attr; - if (a.globalAlpha < 1 || a.shadowColor !== Ext.draw.Color.RGBA_NONE) { - this.lidRenderer(b, a.thickness) - } - }, - sideRenderer: function(l, s) { - var o = this.attr, - k = o.margin, - g = o.centerX, - f = o.centerY, - e = o.distortion, - h = o.baseRotation, - p = o.startAngle + h, - m = o.endAngle + h, - a = o.thickness, - q = o.startRho, - j = o.endRho, - r = (s === "start" && p) || (s === "end" && m), - b = Math.sin(r), - d = Math.cos(r), - c = o.globalAlpha < 1, - n = s === "start" && d < 0 || s === "end" && d > 0 || c, - i; - if (n) { - i = (p + m) / 2; - g += Math.cos(i) * k; - f += Math.sin(i) * k * e; - l.moveTo(g + d * q, f + b * q * e); - l.lineTo(g + d * j, f + b * j * e); - l.lineTo(g + d * j, f + b * j * e + a); - l.lineTo(g + d * q, f + b * q * e + a); - l.closePath() - } - }, - startRenderer: function(a) { - this.sideRenderer(a, "start") - }, - endRenderer: function(a) { - this.sideRenderer(a, "end") - }, - rimRenderer: function(q, e, o, j) { - var w = this, - s = w.attr, - p = s.margin, - h = s.centerX, - g = s.centerY, - d = s.distortion, - i = s.baseRotation, - t = Ext.draw.sprite.AttributeParser.angle, - u = s.startAngle + i, - r = s.endAngle + i, - k = t((u + r) / 2), - a = s.thickness, - b = s.globalAlpha < 1, - c, n, v; - w.bevelParams = []; - u = t(u); - r = t(r); - h += Math.cos(k) * p; - g += Math.sin(k) * p * d; - c = u >= 0 && r >= 0; - n = u <= 0 && r <= 0; - - function l() { - q.ellipse(h, g + a, e, e * d, 0, Math.PI, u, true); - q.lineTo(h + Math.cos(u) * e, g + Math.sin(u) * e * d); - v = [h, g, e, e * d, 0, u, Math.PI, false]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function f() { - q.ellipse(h, g + a, e, e * d, 0, 0, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, 0, true]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function x() { - q.ellipse(h, g + a, e, e * d, 0, Math.PI, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, Math.PI, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - - function m() { - q.ellipse(h, g + a, e, e * d, 0, u, 0, false); - q.lineTo(h + e, g); - v = [h, g, e, e * d, 0, 0, u, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - if (j) { - if (!o || b) { - if (u >= 0 && r < 0) { - l() - } else { - if (u <= 0 && r > 0) { - f() - } else { - if (u <= 0 && r < 0) { - if (u > r) { - q.ellipse(h, g + a, e, e * d, 0, 0, Math.PI, false); - q.lineTo(h - e, g); - v = [h, g, e, e * d, 0, Math.PI, 0, true]; - if (!o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } else { - if (u > r) { - l(); - f() - } else { - v = [h, g, e, e * d, 0, u, r, false]; - if (c && !o || n && o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d + a); - q.ellipse(h, g + a, e, e * d, 0, r, u, true); - q.closePath() - } - } - } - } - } - } else { - if (o || b) { - if (u >= 0 && r < 0) { - x() - } else { - if (u <= 0 && r > 0) { - m() - } else { - if (u <= 0 && r < 0) { - if (u > r) { - x(); - m() - } else { - q.ellipse(h, g + a, e, e * d, 0, u, r, false); - q.lineTo(h + Math.cos(r) * e, g + Math.sin(r) * e * d); - v = [h, g, e, e * d, 0, r, u, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } else { - if (u > r) { - q.ellipse(h, g + a, e, e * d, 0, -Math.PI, 0, false); - q.lineTo(h + e, g); - v = [h, g, e, e * d, 0, 0, -Math.PI, true]; - if (o) { - w.bevelParams.push(v) - } - q.ellipse.apply(q, v); - q.closePath() - } - } - } - } - } - } - }, - innerFrontRenderer: function(a) { - this.rimRenderer(a, this.attr.startRho, true, true) - }, - innerBackRenderer: function(a) { - this.rimRenderer(a, this.attr.startRho, true, false) - }, - outerFrontRenderer: function(a) { - this.rimRenderer(a, this.attr.endRho, false, true) - }, - outerBackRenderer: function(a) { - this.rimRenderer(a, this.attr.endRho, false, false) - } -}); -Ext.define("Ext.draw.PathUtil", function() { - var a = Math.abs, - c = Math.pow, - e = Math.cos, - b = Math.acos, - d = Math.sqrt, - f = Math.PI; - return { - singleton: true, - requires: ["Ext.draw.overrides.Path", "Ext.draw.overrides.sprite.Path", "Ext.draw.overrides.sprite.Instancing", "Ext.draw.overrides.Surface"], - cubicRoots: function(m) { - var z = m[0], - x = m[1], - w = m[2], - v = m[3]; - if (z === 0) { - return this.quadraticRoots(x, w, v) - } - var s = x / z, - r = w / z, - q = v / z, - k = (3 * r - c(s, 2)) / 9, - j = (9 * s * r - 27 * q - 2 * c(s, 3)) / 54, - p = c(k, 3) + c(j, 2), - n = [], - h, g, o, l, u, y = Ext.Number.sign; - if (p >= 0) { - h = y(j + d(p)) * c(a(j + d(p)), 1 / 3); - g = y(j - d(p)) * c(a(j - d(p)), 1 / 3); - n[0] = -s / 3 + (h + g); - n[1] = -s / 3 - (h + g) / 2; - n[2] = n[1]; - o = a(d(3) * (h - g) / 2); - if (o !== 0) { - n[1] = -1; - n[2] = -1 - } - } else { - l = b(j / d(-c(k, 3))); - n[0] = 2 * d(-k) * e(l / 3) - s / 3; - n[1] = 2 * d(-k) * e((l + 2 * f) / 3) - s / 3; - n[2] = 2 * d(-k) * e((l + 4 * f) / 3) - s / 3 - } - for (u = 0; u < 3; u++) { - if (n[u] < 0 || n[u] > 1) { - n[u] = -1 - } - } - return n - }, - quadraticRoots: function(h, g, n) { - var m, l, k, j; - if (h === 0) { - return this.linearRoot(g, n) - } - m = g * g - 4 * h * n; - if (m === 0) { - k = [-g / (2 * h)] - } else { - if (m > 0) { - l = d(m); - k = [(-g - l) / (2 * h), (-g + l) / (2 * h)] - } else { - return [] - } - } - for (j = 0; j < k.length; j++) { - if (k[j] < 0 || k[j] > 1) { - k[j] = -1 - } - } - return k - }, - linearRoot: function(h, g) { - var i = -g / h; - if (h === 0 || i < 0 || i > 1) { - return [] - } - return [i] - }, - bezierCoeffs: function(h, g, k, j) { - var i = []; - i[0] = -h + 3 * g - 3 * k + j; - i[1] = 3 * h - 6 * g + 3 * k; - i[2] = -3 * h + 3 * g; - i[3] = h; - return i - }, - cubicLineIntersections: function(I, G, F, E, l, k, j, h, M, p, K, n) { - var u = [], - N = [], - D = p - n, - z = K - M, - y = M * (n - p) - p * (K - M), - L = this.bezierCoeffs(I, G, F, E), - J = this.bezierCoeffs(l, k, j, h), - H, x, w, v, g, q, o, m; - u[0] = D * L[0] + z * J[0]; - u[1] = D * L[1] + z * J[1]; - u[2] = D * L[2] + z * J[2]; - u[3] = D * L[3] + z * J[3] + y; - x = this.cubicRoots(u); - for (H = 0; H < x.length; H++) { - v = x[H]; - if (v < 0 || v > 1) { - continue - } - g = v * v; - q = g * v; - o = L[0] * q + L[1] * g + L[2] * v + L[3]; - m = J[0] * q + J[1] * g + J[2] * v + J[3]; - if ((K - M) !== 0) { - w = (o - M) / (K - M) - } else { - w = (m - p) / (n - p) - } - if (!(w < 0 || w > 1)) { - N.push([o, m]) - } - } - return N - }, - splitCubic: function(g, q, p, o, m) { - var j = m * m, - n = m * j, - i = m - 1, - h = i * i, - k = i * h, - l = n * o - 3 * j * i * p + 3 * m * h * q - k * g; - return [ - [g, m * q - i * g, j * p - 2 * m * i * q + h * g, l], - [l, j * o - 2 * m * i * p + h * q, m * o - i * p, o] - ] - }, - cubicDimension: function(p, o, l, k) { - var j = 3 * (-p + 3 * (o - l) + k), - i = 6 * (p - 2 * o + l), - h = -3 * (p - o), - q, n, g = Math.min(p, k), - m = Math.max(p, k), - r; - if (j === 0) { - if (i === 0) { - return [g, m] - } else { - q = -h / i; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - } - } else { - r = i * i - 4 * j * h; - if (r >= 0) { - r = d(r); - q = (r - i) / 2 / j; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - if (r > 0) { - q -= r / j; - if (0 < q && q < 1) { - n = this.interpolateCubic(p, o, l, k, q); - g = Math.min(g, n); - m = Math.max(m, n) - } - } - } - } - return [g, m] - }, - interpolateCubic: function(h, g, l, k, i) { - if (i === 0) { - return h - } - if (i === 1) { - return k - } - var j = (1 - i) / i; - return i * i * i * (k + j * (3 * l + j * (3 * g + j * h))) - }, - cubicsIntersections: function(r, q, p, o, A, z, y, v, g, F, E, D, m, l, k, i) { - var C = this, - x = C.cubicDimension(r, q, p, o), - B = C.cubicDimension(A, z, y, v), - n = C.cubicDimension(g, F, E, D), - s = C.cubicDimension(m, l, k, i), - j, h, u, t, w = []; - if (x[0] > n[1] || x[1] < n[0] || B[0] > s[1] || B[1] < s[0]) { - return [] - } - if (a(A - z) < 1 && a(y - v) < 1 && a(r - o) < 1 && a(q - p) < 1 && a(m - l) < 1 && a(k - i) < 1 && a(g - D) < 1 && a(F - E) < 1) { - return [ - [(r + o) * 0.5, (A + z) * 0.5] - ] - } - j = C.splitCubic(r, q, p, o, 0.5); - h = C.splitCubic(A, z, y, v, 0.5); - u = C.splitCubic(g, F, E, D, 0.5); - t = C.splitCubic(m, l, k, i, 0.5); - w.push.apply(w, C.cubicsIntersections.apply(C, j[0].concat(h[0], u[0], t[0]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[0].concat(h[0], u[1], t[1]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[1].concat(h[1], u[0], t[0]))); - w.push.apply(w, C.cubicsIntersections.apply(C, j[1].concat(h[1], u[1], t[1]))); - return w - }, - linesIntersection: function(k, p, j, o, h, n, q, m) { - var l = (j - k) * (m - n) - (o - p) * (q - h), - i, g; - if (l === 0) { - return null - } - i = ((q - h) * (p - n) - (k - h) * (m - n)) / l; - g = ((j - k) * (p - n) - (o - p) * (k - h)) / l; - if (i >= 0 && i <= 1 && g >= 0 && g <= 1) { - return [k + i * (j - k), p + i * (o - p)] - } - return null - }, - pointOnLine: function(j, m, h, l, g, n) { - var k, i; - if (a(h - j) < a(l - m)) { - i = j; - j = m; - m = i; - i = h; - h = l; - l = i; - i = g; - g = n; - n = i - } - k = (g - j) / (h - j); - if (k < 0 || k > 1) { - return false - } - return a(m + k * (l - m) - n) < 4 - }, - pointOnCubic: function(w, u, s, r, l, k, h, g, p, o) { - var C = this, - B = C.bezierCoeffs(w, u, s, r), - A = C.bezierCoeffs(l, k, h, g), - z, v, n, m, q; - B[3] -= p; - A[3] -= o; - n = C.cubicRoots(B); - m = C.cubicRoots(A); - for (z = 0; z < n.length; z++) { - q = n[z]; - for (v = 0; v < m.length; v++) { - if (q >= 0 && q <= 1 && a(q - m[v]) < 0.05) { - return true - } - } - } - return false - } - } -}); -Ext.define("Ext.chart.series.Pie3D", { - extend: "Ext.chart.series.Polar", - requires: ["Ext.chart.series.sprite.Pie3DPart", "Ext.draw.PathUtil"], - type: "pie3d", - seriesType: "pie3d", - alias: "series.pie3d", - isPie3D: true, - config: { - rect: [0, 0, 0, 0], - thickness: 35, - distortion: 0.5, - donut: false, - hidden: [], - highlightCfg: { - margin: 20 - }, - shadow: false - }, - rotationOffset: -Math.PI / 2, - setField: function(a) { - return this.setXField(a) - }, - getField: function() { - return this.getXField() - }, - updateRotation: function(a) { - this.setStyle({ - baseRotation: a + this.rotationOffset - }); - this.doUpdateStyles() - }, - updateDistortion: function() { - this.setRadius() - }, - updateThickness: function() { - this.setRadius() - }, - updateColors: function(a) { - this.setSubStyle({ - baseColor: a - }) - }, - applyShadow: function(a) { - if (a === true) { - a = { - shadowColor: "rgba(0,0,0,0.8)", - shadowBlur: 30 - } - } else { - if (!Ext.isObject(a)) { - a = { - shadowColor: Ext.draw.Color.RGBA_NONE - } - } - } - return a - }, - updateShadow: function(g) { - var e = this, - f = e.getSprites(), - d = e.spritesPerSlice, - c = f && f.length, - b, a; - for (b = 1; b < c; b += d) { - a = f[b]; - if (a.attr.part = "bottom") { - a.setAttributes(g) - } - } - }, - getStyleByIndex: function(b) { - var d = this.callParent([b]), - c = this.getStyle(), - a = d.fillStyle || d.fill || d.color, - e = c.strokeStyle || c.stroke; - if (a) { - d.baseColor = a; - delete d.fillStyle; - delete d.fill; - delete d.color - } - if (e) { - d.strokeStyle = e - } - return d - }, - doUpdateStyles: function() { - var g = this, - h = g.getSprites(), - f = g.spritesPerSlice, - e = h && h.length, - c = 0, - b = 0, - a, d; - for (; c < e; c += f, b++) { - d = g.getStyleByIndex(b); - for (a = 0; a < f; a++) { - h[c + a].setAttributes(d) - } - } - }, - coordinateX: function() { - var w = this, - m = w.getChart(), - u = m && m.getAnimation(), - f = w.getStore(), - t = f.getData().items, - d = t.length, - b = w.getXField(), - p = w.getRotation(), - s = w.getHidden(), - n, c = 0, - h, e = [], - k = w.getSprites(), - a = k.length, - l = w.spritesPerSlice, - g = 0, - o = Math.PI * 2, - v = 1e-10, - r, q; - for (r = 0; r < d; r++) { - n = Math.abs(Number(t[r].get(b))) || 0; - if (!s[r]) { - c += n - } - e[r] = c; - if (r >= s.length) { - s[r] = false - } - } - s.length = d; - if (c === 0) { - return - } - h = 2 * Math.PI / c; - for (r = 0; r < d; r++) { - e[r] *= h - } - for (r = 0; r < a; r++) { - k[r].setAnimation(u) - } - for (r = 0; r < d; r++) { - for (q = 0; q < l; q++) { - k[r * l + q].setAttributes({ - startAngle: g, - endAngle: e[r] - v, - globalAlpha: 1, - baseRotation: p - }) - } - g = e[r] - } - for (r *= l; r < a; r++) { - k[r].setAnimation(u); - k[r].setAttributes({ - startAngle: o, - endAngle: o, - globalAlpha: 0, - baseRotation: p - }) - } - }, - updateLabelData: function() { - var l = this, - m = l.getStore(), - k = m.getData().items, - h = l.getSprites(), - b = l.getLabel().getTemplate().getField(), - f = l.getHidden(), - a = l.spritesPerSlice, - d, c, g, e, n; - if (h.length && b) { - e = []; - for (d = 0, g = k.length; d < g; d++) { - e.push(k[d].get(b)) - } - for (d = 0, c = 0, g = h.length; d < g; d += a, c++) { - n = h[d]; - n.setAttributes({ - label: e[c] - }); - n.putMarker("labels", { - hidden: f[c] - }, n.attr.attributeId) - } - } - }, - applyRadius: function() { - var f = this, - d = f.getChart(), - h = d.getInnerPadding(), - e = d.getMainRect() || [0, 0, 1, 1], - c = e[2] - h * 2, - a = e[3] - h * 2 - f.getThickness(), - g = c / 2, - b = g * f.getDistortion(); - if (b > a / 2) { - return a / (f.getDistortion() * 2) - } else { - return g - } - }, - getSprites: function() { - var y = this, - e = y.getStore(); - if (!e) { - return [] - } - var n = y.getChart(), - p = y.getSurface(), - t = e.getData().items, - l = y.spritesPerSlice, - a = t.length, - v = y.getAnimation() || n && n.getAnimation(), - x = y.getCenter(), - w = y.getOffsetX(), - u = y.getOffsetY(), - b = y.getRadius(), - q = y.getRotation(), - d = y.getHighlight(), - c = { - centerX: x[0] + w, - centerY: x[1] + u - y.getThickness() / 2, - endRho: b, - startRho: b * y.getDonut() / 100, - thickness: y.getThickness(), - distortion: y.getDistortion() - }, - k = y.sprites, - h = y.getLabel(), - f = h.getTemplate(), - m, g, o, s, r; - for (s = 0; s < a; s++) { - g = Ext.apply({}, this.getStyleByIndex(s), c); - if (!k[s * l]) { - for (r = 0; r < y.partNames.length; r++) { - o = p.add({ - type: "pie3dPart", - part: y.partNames[r] - }); - if (r === 0 && f.getField()) { - o.bindMarker("labels", h) - } - o.fx.setDurationOn("baseRotation", q); - if (d) { - o.config.highlight = d; - o.addModifier("highlight", true) - } - o.setAttributes(g); - k.push(o) - } - } else { - m = k.slice(s * l, (s + 1) * l); - for (r = 0; r < m.length; r++) { - o = m[r]; - if (v) { - o.setAnimation(v) - } - o.setAttributes(g) - } - } - } - return k - }, - betweenAngle: function(d, f, c) { - var e = Math.PI * 2, - g = this.rotationOffset; - f += g; - c += g; - d -= f; - c -= f; - d %= e; - c %= e; - d += e; - c += e; - d %= e; - c %= e; - return d < c || c === 0 - }, - getItemForPoint: function(k, j) { - var h = this, - g = h.getSprites(); - if (g) { - var l = h.getStore(), - b = l.getData().items, - a = h.spritesPerSlice, - e = h.getHidden(), - c, f, m, d; - for (c = 0, f = b.length; c < f; c++) { - if (!e[c]) { - d = c * a; - m = g[d]; - if (m.hitTest([k, j])) { - return { - series: h, - sprite: g.slice(d, d + a), - index: c, - record: b[c], - category: "sprites", - field: h.getXField() - } - } - } - } - return null - } - }, - provideLegendInfo: function(f) { - var h = this, - k = h.getStore(); - if (k) { - var g = k.getData().items, - b = h.getLabel().getTemplate().getField(), - j = h.getField(), - e = h.getHidden(), - d, a, c; - for (d = 0; d < g.length; d++) { - a = h.getStyleByIndex(d); - c = a.baseColor; - f.push({ - name: b ? String(g[d].get(b)) : j + " " + d, - mark: c || "black", - disabled: e[d], - series: h.getId(), - index: d - }) - } - } - } -}, function() { - var b = this.prototype, - a = Ext.chart.series.sprite.Pie3DPart.def.getInitialConfig().processors.part; - b.partNames = a.replace(/^enums\(|\)/g, "").split(","); - b.spritesPerSlice = b.partNames.length -}); -Ext.define("Ext.chart.series.sprite.Polar", { - extend: "Ext.chart.series.sprite.Series", - inheritableStatics: { - def: { - processors: { - centerX: "number", - centerY: "number", - startAngle: "number", - endAngle: "number", - startRho: "number", - endRho: "number", - baseRotation: "number", - labels: "default", - labelOverflowPadding: "number" - }, - defaults: { - centerX: 0, - centerY: 0, - startAngle: 0, - endAngle: Math.PI, - startRho: 0, - endRho: 150, - baseRotation: 0, - labels: null, - labelOverflowPadding: 10 - }, - triggers: { - centerX: "bbox", - centerY: "bbox", - startAngle: "bbox", - endAngle: "bbox", - startRho: "bbox", - endRho: "bbox", - baseRotation: "bbox" - } - } - }, - updatePlainBBox: function(b) { - var a = this.attr; - b.x = a.centerX - a.endRho; - b.y = a.centerY + a.endRho; - b.width = a.endRho * 2; - b.height = a.endRho * 2 - } -}); -Ext.define("Ext.chart.series.sprite.Radar", { - alias: "sprite.radar", - extend: "Ext.chart.series.sprite.Polar", - getDataPointXY: function(d) { - var u = this, - n = u.attr, - f = n.centerX, - e = n.centerY, - o = n.matrix, - t = n.dataMinX, - s = n.dataMaxX, - k = n.dataX, - j = n.dataY, - l = n.endRho, - p = n.startRho, - g = n.baseRotation, - i, h, m, c, b, a, q; - if (n.rangeY) { - q = n.rangeY[1] - } else { - q = n.dataMaxY - } - c = (k[d] - t) / (s - t + 1) * 2 * Math.PI + g; - m = j[d] / q * (l - p) + p; - b = f + Math.cos(c) * m; - a = e + Math.sin(c) * m; - i = o.x(b, a); - h = o.y(b, a); - return [i, h] - }, - render: function(a, l) { - var h = this, - f = h.attr, - g = f.dataX, - b = g.length, - e = h.surfaceMatrix, - d = {}, - c, k, j, m; - l.beginPath(); - for (c = 0; c < b; c++) { - m = h.getDataPointXY(c); - k = m[0]; - j = m[1]; - if (c === 0) { - l.moveTo(k, j) - } - l.lineTo(k, j); - d.translationX = e.x(k, j); - d.translationY = e.y(k, j); - h.putMarker("markers", d, c, true) - } - l.closePath(); - l.fillStroke(f) - } -}); -Ext.define("Ext.chart.series.Radar", { - extend: "Ext.chart.series.Polar", - type: "radar", - seriesType: "radar", - alias: "series.radar", - requires: ["Ext.chart.series.sprite.Radar"], - themeColorCount: function() { - return 1 - }, - isStoreDependantColorCount: false, - themeMarkerCount: function() { - return 1 - }, - updateAngularAxis: function(a) { - a.processData(this) - }, - updateRadialAxis: function(a) { - a.processData(this) - }, - coordinateX: function() { - return this.coordinate("X", 0, 2) - }, - coordinateY: function() { - return this.coordinate("Y", 1, 2) - }, - updateCenter: function(a) { - this.setStyle({ - translationX: a[0] + this.getOffsetX(), - translationY: a[1] + this.getOffsetY() - }); - this.doUpdateStyles() - }, - updateRadius: function(a) { - this.setStyle({ - endRho: a - }); - this.doUpdateStyles() - }, - updateRotation: function(a) { - this.setStyle({ - rotationRads: a - }); - this.doUpdateStyles() - }, - updateTotalAngle: function(a) { - this.processData() - }, - getItemForPoint: function(k, j) { - var h = this, - m = h.sprites && h.sprites[0], - f = m.attr, - g = f.dataX, - a = g.length, - l = h.getStore(), - e = h.getMarker(), - b, o, p, d, n, c; - if (h.getHidden()) { - return null - } - if (m && e) { - c = m.getMarker("markers"); - for (d = 0; d < a; d++) { - n = c.getBBoxFor(d); - b = (n.width + n.height) * 0.25; - p = m.getDataPointXY(d); - if (Math.abs(p[0] - k) < b && Math.abs(p[1] - j) < b) { - o = { - series: h, - sprite: m, - index: d, - category: "markers", - record: l.getData().items[d], - field: h.getYField() - }; - return o - } - } - } - return h.callParent(arguments) - }, - getDefaultSpriteConfig: function() { - var a = this.callParent(), - b = { - customDurations: { - translationX: 0, - translationY: 0, - rotationRads: 0, - dataMinX: 0, - dataMaxX: 0 - } - }; - if (a.fx) { - Ext.apply(a.fx, b) - } else { - a.fx = b - } - return a - }, - getSprites: function() { - var d = this, - c = d.getChart(), - e = d.getAnimation() || c && c.getAnimation(), - b = d.sprites[0], - a; - if (!c) { - return [] - } - if (!b) { - b = d.createSprite() - } - if (e) { - a = b.getMarker("markers"); - if (a) { - a.getTemplate().setAnimation(e) - } - b.setAnimation(e) - } - return d.sprites - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getSubStyleWithTheme(), - c = a.fillStyle; - if (Ext.isArray(c)) { - c = c[0] - } - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - } -}); -Ext.define("Ext.chart.series.sprite.Scatter", { - alias: "sprite.scatterSeries", - extend: "Ext.chart.series.sprite.Cartesian", - renderClipped: function(r, s, w, u) { - if (this.cleanRedraw) { - return - } - var C = this, - q = C.attr, - l = q.dataX, - h = q.dataY, - z = q.labels, - j = C.getSeries(), - b = z && C.getMarker("labels"), - t = C.attr.matrix, - c = t.getXX(), - p = t.getYY(), - m = t.getDX(), - k = t.getDY(), - n = {}, - D, B, d = r.getInherited().rtl && !q.flipXY ? -1 : 1, - a, A, o, e, g, f, v; - if (q.flipXY) { - a = u[1] - c * d; - A = u[1] + u[3] + c * d; - o = u[0] - p; - e = u[0] + u[2] + p - } else { - a = u[0] - c * d; - A = u[0] + u[2] + c * d; - o = u[1] - p; - e = u[1] + u[3] + p - } - for (v = 0; v < l.length; v++) { - g = l[v]; - f = h[v]; - g = g * c + m; - f = f * p + k; - if (a <= g && g <= A && o <= f && f <= e) { - if (q.renderer) { - n = { - type: "items", - translationX: g, - translationY: f - }; - B = [C, n, { - store: C.getStore() - }, v]; - D = Ext.callback(q.renderer, null, B, 0, j); - n = Ext.apply(n, D) - } else { - n.translationX = g; - n.translationY = f - } - C.putMarker("items", n, v, !q.renderer); - if (b && z[v]) { - C.drawLabel(z[v], g, f, v, u) - } - } - } - }, - drawLabel: function(j, h, g, p, a) { - var r = this, - m = r.attr, - d = r.getMarker("labels"), - c = d.getTemplate(), - l = r.labelCfg || (r.labelCfg = {}), - b = r.surfaceMatrix, - f, e, i = m.labelOverflowPadding, - o = m.flipXY, - k, n, s, q; - l.text = j; - n = r.getMarkerBBox("labels", p, true); - if (!n) { - r.putMarker("labels", l, p); - n = r.getMarkerBBox("labels", p, true) - } - if (o) { - l.rotationRads = Math.PI * 0.5 - } else { - l.rotationRads = 0 - } - k = n.height / 2; - f = h; - switch (c.attr.display) { - case "under": - e = g - k - i; - break; - case "rotate": - f += i; - e = g - i; - l.rotationRads = -Math.PI / 4; - break; - default: - e = g + k + i - } - l.x = b.x(f, e); - l.y = b.y(f, e); - if (c.attr.renderer) { - q = [j, d, l, { - store: r.getStore() - }, p]; - s = Ext.callback(c.attr.renderer, null, q, 0, r.getSeries()); - if (typeof s === "string") { - l.text = s - } else { - Ext.apply(l, s) - } - } - r.putMarker("labels", l, p) - } -}); -Ext.define("Ext.chart.series.Scatter", { - extend: "Ext.chart.series.Cartesian", - alias: "series.scatter", - type: "scatter", - seriesType: "scatterSeries", - requires: ["Ext.chart.series.sprite.Scatter"], - config: { - itemInstancing: { - fx: { - customDurations: { - translationX: 0, - translationY: 0 - } - } - } - }, - themeMarkerCount: function() { - return 1 - }, - applyMarker: function(b, a) { - this.getItemInstancing(); - this.setItemInstancing(b); - return this.callParent(arguments) - }, - provideLegendInfo: function(d) { - var b = this, - a = b.getMarkerStyleByIndex(0), - c = a.fillStyle; - d.push({ - name: b.getTitle() || b.getYField() || b.getId(), - mark: (Ext.isObject(c) ? c.stops && c.stops[0].color : c) || a.strokeStyle || "black", - disabled: b.getHidden(), - series: b.getId(), - index: 0 - }) - } -}); -Ext.define("Ext.chart.theme.Blue", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.blue", "chart.theme.Blue"], - config: { - baseColor: "#4d7fe6" - } -}); -Ext.define("Ext.chart.theme.BlueGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.blue-gradients", "chart.theme.Blue:gradients"], - config: { - baseColor: "#4d7fe6", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category1", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category1", "chart.theme.Category1"], - config: { - colors: ["#f0a50a", "#c20024", "#2044ba", "#810065", "#7eae29"] - } -}); -Ext.define("Ext.chart.theme.Category1Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category1-gradients", "chart.theme.Category1:gradients"], - config: { - colors: ["#f0a50a", "#c20024", "#2044ba", "#810065", "#7eae29"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category2", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category2", "chart.theme.Category2"], - config: { - colors: ["#6d9824", "#87146e", "#2a9196", "#d39006", "#1e40ac"] - } -}); -Ext.define("Ext.chart.theme.Category2Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category2-gradients", "chart.theme.Category2:gradients"], - config: { - colors: ["#6d9824", "#87146e", "#2a9196", "#d39006", "#1e40ac"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category3", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category3", "chart.theme.Category3"], - config: { - colors: ["#fbbc29", "#ce2e4e", "#7e0062", "#158b90", "#57880e"] - } -}); -Ext.define("Ext.chart.theme.Category3Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category3-gradients", "chart.theme.Category3:gradients"], - config: { - colors: ["#fbbc29", "#ce2e4e", "#7e0062", "#158b90", "#57880e"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category4", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category4", "chart.theme.Category4"], - config: { - colors: ["#ef5773", "#fcbd2a", "#4f770d", "#1d3eaa", "#9b001f"] - } -}); -Ext.define("Ext.chart.theme.Category4Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category4-gradients", "chart.theme.Category4:gradients"], - config: { - colors: ["#ef5773", "#fcbd2a", "#4f770d", "#1d3eaa", "#9b001f"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category5", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category5", "chart.theme.Category5"], - config: { - colors: ["#7eae29", "#fdbe2a", "#910019", "#27b4bc", "#d74dbc"] - } -}); -Ext.define("Ext.chart.theme.Category5Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category5-gradients", "chart.theme.Category5:gradients"], - config: { - colors: ["#7eae29", "#fdbe2a", "#910019", "#27b4bc", "#d74dbc"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Category6", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category6", "chart.theme.Category6"], - config: { - colors: ["#44dce1", "#0b2592", "#996e05", "#7fb325", "#b821a1"] - } -}); -Ext.define("Ext.chart.theme.Category6Gradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.category6-gradients", "chart.theme.Category6:gradients"], - config: { - colors: ["#44dce1", "#0b2592", "#996e05", "#7fb325", "#b821a1"], - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.DefaultGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.default-gradients", "chart.theme.Base:gradients"], - config: { - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Green", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.green", "chart.theme.Green"], - config: { - baseColor: "#b1da5a" - } -}); -Ext.define("Ext.chart.theme.GreenGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.green-gradients", "chart.theme.Green:gradients"], - config: { - baseColor: "#b1da5a", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Midnight", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.midnight", "chart.theme.Midnight"], - config: { - colors: ["#A837FF", "#4AC0F2", "#FF4D35", "#FF8809", "#61C102", "#FF37EA"], - chart: { - defaults: { - background: "rgb(52, 52, 53)" - } - }, - axis: { - defaults: { - style: { - strokeStyle: "rgb(224, 224, 227)" - }, - label: { - fillStyle: "rgb(224, 224, 227)" - }, - title: { - fillStyle: "rgb(224, 224, 227)" - }, - grid: { - strokeStyle: "rgb(112, 112, 115)" - } - } - }, - series: { - defaults: { - label: { - fillStyle: "rgb(224, 224, 227)" - } - } - }, - sprites: { - text: { - fillStyle: "rgb(224, 224, 227)" - } - } - } -}); -Ext.define("Ext.chart.theme.Muted", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.muted", "chart.theme.Muted"], - config: { - colors: ["#8ca640", "#974144", "#4091ba", "#8e658e", "#3b8d8b", "#b86465", "#d2af69", "#6e8852", "#3dcc7e", "#a6bed1", "#cbaa4b", "#998baa"] - } -}); -Ext.define("Ext.chart.theme.Purple", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.purple", "chart.theme.Purple"], - config: { - baseColor: "#da5abd" - } -}); -Ext.define("Ext.chart.theme.PurpleGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.purple-gradients", "chart.theme.Purple:gradients"], - config: { - baseColor: "#da5abd", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Red", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.red", "chart.theme.Red"], - config: { - baseColor: "#e84b67" - } -}); -Ext.define("Ext.chart.theme.RedGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.red-gradients", "chart.theme.Red:gradients"], - config: { - baseColor: "#e84b67", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Sky", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.sky", "chart.theme.Sky"], - config: { - baseColor: "#4ce0e7" - } -}); -Ext.define("Ext.chart.theme.SkyGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.sky-gradients", "chart.theme.Sky:gradients"], - config: { - baseColor: "#4ce0e7", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.chart.theme.Yellow", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.yellow", "chart.theme.Yellow"], - config: { - baseColor: "#fec935" - } -}); -Ext.define("Ext.chart.theme.YellowGradients", { - extend: "Ext.chart.theme.Base", - singleton: true, - alias: ["chart.theme.yellow-gradients", "chart.theme.Yellow:gradients"], - config: { - baseColor: "#fec935", - gradients: { - type: "linear", - degrees: 90 - } - } -}); -Ext.define("Ext.draw.Point", { - requires: ["Ext.draw.Draw", "Ext.draw.Matrix"], - isPoint: true, - x: 0, - y: 0, - length: 0, - angle: 0, - angleUnits: "degrees", - statics: { - fly: (function() { - var a = null; - return function(b, c) { - if (!a) { - a = new Ext.draw.Point() - } - a.constructor(b, c); - return a - } - })() - }, - constructor: function(a, c) { - var b = this; - if (typeof a === "number") { - b.x = a; - if (typeof c === "number") { - b.y = c - } else { - b.y = a - } - } else { - if (Ext.isArray(a)) { - b.x = a[0]; - b.y = a[1] - } else { - if (a) { - b.x = a.x; - b.y = a.y - } - } - } - b.calculatePolar() - }, - calculateCartesian: function() { - var b = this, - a = b.length, - c = b.angle; - if (b.angleUnits === "degrees") { - c = Ext.draw.Draw.rad(c) - } - b.x = Math.cos(c) * a; - b.y = Math.sin(c) * a - }, - calculatePolar: function() { - var b = this, - a = b.x, - c = b.y; - b.length = Math.sqrt(a * a + c * c); - b.angle = Math.atan2(c, a); - if (b.angleUnits === "degrees") { - b.angle = Ext.draw.Draw.degrees(b.angle) - } - }, - setX: function(a) { - this.x = a; - this.calculatePolar() - }, - setY: function(a) { - this.y = a; - this.calculatePolar() - }, - set: function(a, b) { - this.constructor(a, b) - }, - setAngle: function(a) { - this.angle = a; - this.calculateCartesian() - }, - setLength: function(a) { - this.length = a; - this.calculateCartesian() - }, - setPolar: function(b, a) { - this.angle = b; - this.length = a; - this.calculateCartesian() - }, - clone: function() { - return new Ext.draw.Point(this.x, this.y) - }, - add: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return new Ext.draw.Point(this.x + b.x, this.y + b.y) - }, - sub: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return new Ext.draw.Point(this.x - b.x, this.y - b.y) - }, - mul: function(a) { - return new Ext.draw.Point(this.x * a, this.y * a) - }, - div: function(a) { - return new Ext.draw.Point(this.x / a, this.y / a) - }, - dot: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return this.x * b.x + this.y * b.y - }, - equals: function(a, c) { - var b = Ext.draw.Point.fly(a, c); - return this.x === b.x && this.y === b.y - }, - rotate: function(f, c) { - var d, e, b, g, a; - if (this.angleUnits === "degrees") { - f = Ext.draw.Draw.rad(f); - d = Math.sin(f); - e = Math.cos(f) - } - if (c) { - b = c.x; - g = c.y - } else { - b = 0; - g = 0 - } - a = Ext.draw.Matrix.fly([e, d, -d, e, b - e * b + g * d, g - e * g + b * -d]).transformPoint(this); - return new Ext.draw.Point(a) - }, - transform: function(a) { - if (a && a.isMatrix) { - return new Ext.draw.Point(a.transformPoint(this)) - } else { - if (arguments.length === 6) { - return new Ext.draw.Point(Ext.draw.Matrix.fly(arguments).transformPoint(this)) - } else { - Ext.raise("Invalid parameters.") - } - } - }, - round: function() { - return new Ext.draw.Point(Math.round(this.x), Math.round(this.y)) - }, - ceil: function() { - return new Ext.draw.Point(Math.ceil(this.x), Math.ceil(this.y)) - }, - floor: function() { - return new Ext.draw.Point(Math.floor(this.x), Math.floor(this.y)) - }, - abs: function(a, b) { - return new Ext.draw.Point(Math.abs(this.x), Math.abs(this.y)) - }, - normalize: function(c) { - var b = this.x, - f = this.y, - a, e, d; - c = c || 1; - if (b === 0) { - a = 0; - e = c * Ext.Number.sign(f) - } else { - d = f / b; - a = c / Math.sqrt(1 + d * d); - e = a * d - } - return new Ext.draw.Point(a, e) - }, - getDistanceToLine: function(c, b) { - if (arguments.length === 4) { - c = new Ext.draw.Point(arguments[0], arguments[1]); - b = new Ext.draw.Point(arguments[2], arguments[3]) - } - var d = b.sub(c).normalize(), - a = c.sub(this); - return a.sub(d.mul(a.dot(d))) - }, - isZero: function() { - return this.x === 0 && this.y === 0 - }, - isNumber: function() { - return Ext.isNumber(this.x + this.y) - } -}); -Ext.define("Ext.draw.plugin.SpriteEvents", { - extend: "Ext.plugin.Abstract", - alias: "plugin.spriteevents", - requires: ["Ext.draw.PathUtil"], - mouseMoveEvents: { - mousemove: true, - mouseover: true, - mouseout: true - }, - spriteMouseMoveEvents: { - spritemousemove: true, - spritemouseover: true, - spritemouseout: true - }, - init: function(a) { - var b = "handleEvent"; - this.drawContainer = a; - a.addElementListener({ - click: b, - dblclick: b, - mousedown: b, - mousemove: b, - mouseup: b, - mouseover: b, - mouseout: b, - priority: 1001, - scope: this - }) - }, - hasSpriteMouseMoveListeners: function() { - var b = this.drawContainer.hasListeners, - a; - for (a in this.spriteMouseMoveEvents) { - if (a in b) { - return true - } - } - return false - }, - hitTestEvent: function(f) { - var b = this.drawContainer.getItems(), - a, d, c; - for (c = b.length - 1; c >= 0; c--) { - a = b.get(c); - d = a.hitTestEvent(f); - if (d) { - return d - } - } - return null - }, - handleEvent: function(f) { - var d = this, - b = d.drawContainer, - g = f.type in d.mouseMoveEvents, - a = d.lastSprite, - c; - if (g && !d.hasSpriteMouseMoveListeners()) { - return - } - c = d.hitTestEvent(f); - if (g && !Ext.Object.equals(c, a)) { - if (a) { - b.fireEvent("spritemouseout", a, f) - } - if (c) { - b.fireEvent("spritemouseover", c, f) - } - } - if (c) { - b.fireEvent("sprite" + f.type, c, f) - } - d.lastSprite = c - } -}); -Ext.define("Ext.chart.TipSurface", { - extend: "Ext.draw.Container", - spriteArray: false, - renderFirst: true, - constructor: function(a) { - this.callParent([a]); - if (a.sprites) { - this.spriteArray = [].concat(a.sprites); - delete a.sprites - } - }, - onRender: function() { - var c = this, - b = 0, - a = 0, - d, e; - this.callParent(arguments); - e = c.spriteArray; - if (c.renderFirst && e) { - c.renderFirst = false; - for (a = e.length; b < a; b++) { - d = c.surface.add(e[b]); - d.setAttributes({ - hidden: false - }, true) - } - } - } -}); -Ext.define("Ext.chart.interactions.ItemInfo", { - extend: "Ext.chart.interactions.Abstract", - type: "iteminfo", - alias: "interaction.iteminfo", - config: { - extjsGestures: { - start: { - event: "click", - handler: "onInfoGesture" - }, - move: { - event: "mousemove", - handler: "onInfoGesture" - }, - end: { - event: "mouseleave", - handler: "onInfoGesture" - } - } - }, - item: null, - onInfoGesture: function(f, a) { - var c = this, - b = c.getItemForEvent(f), - d = b && b.series.tooltip; - if (d) { - d.onMouseMove.call(d, f) - } - if (b !== c.item) { - if (b) { - b.series.showTip(b) - } else { - c.item.series.hideTip(c.item) - } - c.item = b - } - return false - } -}); \ No newline at end of file diff --git a/serverside/jsmod/6.1-3/charts.js.original b/serverside/jsmod/6.1-3/charts.js.original deleted file mode 100644 index 2b8dd71..0000000 --- a/serverside/jsmod/6.1-3/charts.js.original +++ /dev/null @@ -1 +0,0 @@ -Ext.define("Ext.draw.ContainerBase",{extend:"Ext.panel.Panel",requires:["Ext.window.Window"],previewTitleText:"Chart Preview",previewAltText:"Chart preview",layout:"container",addElementListener:function(){var b=this,a=arguments;if(b.rendered){b.el.on.apply(b.el,a)}else{b.on("render",function(){b.el.on.apply(b.el,a)})}},removeElementListener:function(){var b=this,a=arguments;if(b.rendered){b.el.un.apply(b.el,a)}},afterRender:function(){this.callParent(arguments);this.initAnimator()},getItems:function(){var b=this,a=b.items;if(!a||!a.isMixedCollection){b.initItems()}return b.items},onRender:function(){this.callParent(arguments);this.element=this.el;this.innerElement=this.body},setItems:function(a){this.items=a;return a},setSurfaceSize:function(b,a){this.resizeHandler({width:b,height:a});this.renderFrame()},onResize:function(c,a,b,e){var d=this;d.callParent([c,a,b,e]);d.setBodySize({width:c,height:a})},preview:function(){var a=this.getImage();new Ext.window.Window({title:this.previewTitleText,closeable:true,renderTo:Ext.getBody(),autoShow:true,maximizeable:true,maximized:true,border:true,layout:{type:"hbox",pack:"center",align:"middle"},items:{xtype:"container",items:{xtype:"image",mode:"img",cls:Ext.baseCSSPrefix+"chart-image",alt:this.previewAltText,src:a.data,listeners:{afterrender:function(){var e=this,b=e.imgEl.dom,d=a.type==="svg"?1:(window.devicePixelRatio||1),c;if(!b.naturalWidth||!b.naturalHeight){b.onload=function(){var g=b.naturalWidth,f=b.naturalHeight;e.setWidth(Math.floor(g/d));e.setHeight(Math.floor(f/d))}}else{c=e.getSize();e.setWidth(Math.floor(c.width/d));e.setHeight(Math.floor(c.height/d))}}}}}})},privates:{getTargetEl:function(){return this.innerElement},reattachToBody:function(){var a=this;if(a.pendingDetachSize){a.onBodyResize()}a.pendingDetachSize=false;a.callParent()}}});Ext.define("Ext.draw.SurfaceBase",{extend:"Ext.Widget",getOwnerBody:function(){return this.ownerCt.body},destroy:function(){var a=this;if(a.hasListeners.destroy){a.fireEvent("destroy",a)}a.callParent()}});Ext.define("Ext.draw.Color",{statics:{colorToHexRe:/(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,rgbToHexRe:/\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/,rgbaToHexRe:/\s*rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\.\d]+)\)/,hexRe:/\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/,NONE:"none",RGBA_NONE:"rgba(0, 0, 0, 0)"},isColor:true,lightnessFactor:0.2,constructor:function(d,b,a,c){this.setRGB(d,b,a,c)},setRGB:function(e,c,a,d){var b=this;b.r=Math.min(255,Math.max(0,e));b.g=Math.min(255,Math.max(0,c));b.b=Math.min(255,Math.max(0,a));if(d===undefined){b.a=1}else{b.a=Math.min(1,Math.max(0,d))}},getGrayscale:function(){return this.r*0.3+this.g*0.59+this.b*0.11},getHSL:function(){var i=this,a=i.r/255,f=i.g/255,j=i.b/255,k=Math.max(a,f,j),d=Math.min(a,f,j),m=k-d,e,n=0,c=0.5*(k+d);if(d!==k){n=(c<=0.5)?m/(k+d):m/(2-k-d);if(a===k){e=60*(f-j)/m}else{if(f===k){e=120+60*(j-a)/m}else{e=240+60*(a-f)/m}}if(e<0){e+=360}if(e>=360){e-=360}}return[e,n,c]},getHSV:function(){var i=this,a=i.r/255,f=i.g/255,j=i.b/255,k=Math.max(a,f,j),d=Math.min(a,f,j),c=k-d,e,m=0,l=k;if(d!=k){m=l?c/l:0;if(a===k){e=60*(f-j)/c}else{if(f===k){e=60*(j-a)/c+120}else{e=60*(a-f)/c+240}}if(e<0){e+=360}if(e>=360){e-=360}}return[e,m,l]},setHSL:function(g,f,e){var i=this,d=Math.abs,j,b,a;g=(g%360+360)%360;f=f>1?1:f<0?0:f;e=e>1?1:e<0?0:e;if(f===0||g===null){e*=255;i.setRGB(e,e,e)}else{g/=60;j=f*(1-d(2*e-1));b=j*(1-d(g%2-1));a=e-j/2;a*=255;j*=255;b*=255;switch(Math.floor(g)){case 0:i.setRGB(j+a,b+a,a);break;case 1:i.setRGB(b+a,j+a,a);break;case 2:i.setRGB(a,j+a,b+a);break;case 3:i.setRGB(a,b+a,j+a);break;case 4:i.setRGB(b+a,a,j+a);break;case 5:i.setRGB(j+a,a,b+a);break}}return i},setHSV:function(f,e,d){var g=this,i,b,a;f=(f%360+360)%360;e=e>1?1:e<0?0:e;d=d>1?1:d<0?0:d;if(e===0||f===null){d*=255;g.setRGB(d,d,d)}else{f/=60;i=d*e;b=i*(1-Math.abs(f%2-1));a=d-i;a*=255;i*=255;b*=255;switch(Math.floor(f)){case 0:g.setRGB(i+a,b+a,a);break;case 1:g.setRGB(b+a,i+a,a);break;case 2:g.setRGB(a,i+a,b+a);break;case 3:g.setRGB(a,b+a,i+a);break;case 4:g.setRGB(b+a,a,i+a);break;case 5:g.setRGB(i+a,a,b+a);break}}return g},createLighter:function(b){if(!b&&b!==0){b=this.lightnessFactor}var a=this.getHSL();a[2]=Ext.Number.constrain(a[2]+b,0,1);return Ext.draw.Color.fromHSL(a[0],a[1],a[2])},createDarker:function(a){if(!a&&a!==0){a=this.lightnessFactor}return this.createLighter(-a)},toString:function(){var f=this,c=Math.round;if(f.a===1){var e=c(f.r).toString(16),d=c(f.g).toString(16),a=c(f.b).toString(16);e=(e.length===1)?"0"+e:e;d=(d.length===1)?"0"+d:d;a=(a.length===1)?"0"+a:a;return["#",e,d,a].join("")}else{return"rgba("+[c(f.r),c(f.g),c(f.b),f.a===0?0:f.a.toFixed(15)].join(", ")+")"}},toHex:function(b){if(Ext.isArray(b)){b=b[0]}if(!Ext.isString(b)){return""}if(b.substr(0,1)==="#"){return b}var e=Ext.draw.Color.colorToHexRe.exec(b);if(Ext.isArray(e)){var f=parseInt(e[2],10),d=parseInt(e[3],10),a=parseInt(e[4],10),c=a|(d<<8)|(f<<16);return e[1]+"#"+("000000"+c.toString(16)).slice(-6)}else{return""}},setFromString:function(j){var e,h,f,c,d=1,i=parseInt;if(j===Ext.draw.Color.NONE){this.r=this.g=this.b=this.a=0;return this}if((j.length===4||j.length===7)&&j.substr(0,1)==="#"){e=j.match(Ext.draw.Color.hexRe);if(e){h=i(e[1],16)>>0;f=i(e[2],16)>>0;c=i(e[3],16)>>0;if(j.length===4){h+=(h*16);f+=(f*16);c+=(c*16)}}}else{if((e=j.match(Ext.draw.Color.rgbToHexRe))){h=+e[1];f=+e[2];c=+e[3]}else{if((e=j.match(Ext.draw.Color.rgbaToHexRe))){h=+e[1];f=+e[2];c=+e[3];d=+e[4]}else{if(Ext.draw.Color.ColorList.hasOwnProperty(j.toLowerCase())){return this.setFromString(Ext.draw.Color.ColorList[j.toLowerCase()])}}}}if(typeof h==="undefined"){return this}this.r=h;this.g=f;this.b=c;this.a=d;return this}},function(){var a=new this();this.addStatics({fly:function(f,e,c,d){switch(arguments.length){case 1:a.setFromString(f);break;case 3:case 4:a.setRGB(f,e,c,d);break;default:return null}return a},ColorList:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},fromHSL:function(d,c,b){return(new this(0,0,0,0)).setHSL(d,c,b)},fromHSV:function(d,c,b){return(new this(0,0,0,0)).setHSL(d,c,b)},fromString:function(b){return(new this(0,0,0,0)).setFromString(b)},create:function(b){if(b instanceof this){return b}else{if(Ext.isArray(b)){return new Ext.draw.Color(b[0],b[1],b[2],b[3])}else{if(Ext.isString(b)){return Ext.draw.Color.fromString(b)}else{if(arguments.length>2){return new Ext.draw.Color(arguments[0],arguments[1],arguments[2],arguments[3])}else{return new Ext.draw.Color(0,0,0,0)}}}}}})});Ext.define("Ext.draw.sprite.AnimationParser",function(){function a(d,c,b){return d+(c-d)*b}return{singleton:true,attributeRe:/^url\(#([a-zA-Z\-]+)\)$/,requires:["Ext.draw.Color"],color:{parseInitial:function(c,b){if(Ext.isString(c)){c=Ext.draw.Color.create(c)}if(Ext.isString(b)){b=Ext.draw.Color.create(b)}if((c instanceof Ext.draw.Color)&&(b instanceof Ext.draw.Color)){return[[c.r,c.g,c.b,c.a],[b.r,b.g,b.b,b.a]]}else{return[c||b,b||c]}},compute:function(d,c,b){if(!Ext.isArray(d)||!Ext.isArray(c)){return c||d}else{return[a(d[0],c[0],b),a(d[1],c[1],b),a(d[2],c[2],b),a(d[3],c[3],b)]}},serve:function(c){var b=Ext.draw.Color.fly(c[0],c[1],c[2],c[3]);return b.toString()}},number:{parse:function(b){return b===null?null:+b},compute:function(d,c,b){if(!Ext.isNumber(d)||!Ext.isNumber(c)){return c||d}else{return a(d,c,b)}}},angle:{parseInitial:function(c,b){if(b-c>Math.PI){b-=Math.PI*2}else{if(b-c<-Math.PI){b+=Math.PI*2}}return[c,b]},compute:function(d,c,b){if(!Ext.isNumber(d)||!Ext.isNumber(c)){return c||d}else{return a(d,c,b)}}},path:{parseInitial:function(m,n){var c=m.toStripes(),o=n.toStripes(),e,d,k=c.length,p=o.length,h,f,b,g=o[p-1],l=[g[g.length-2],g[g.length-1]];for(e=k;e=1){return l.path}var e=0,f=c.length,d=0,b,k,h,n=l.temp.params,g=0;for(;eMath.min(b.x+b.width,a.x+a.width))||(Math.max(b.y,a.y)-c>Math.min(b.y+b.height,a.y+a.height))},isPointInBBox:function(a,c,b){return !!b&&a>=b.x&&a<=(b.x+b.width)&&c>=b.y&&c<=(b.y+b.height)},spline:function(m){var e,c,k=m.length,b,h,l,f,a=0,g=new Float32Array(m.length),n=new Float32Array(m.length*3-2);g[0]=0;g[k-1]=0;for(e=1;e0;e--){a=3.732050807568877+48.248711305964385/(-13.928203230275537+Math.pow(0.07179676972449123,e));g[e]-=g[e+1]*a}f=m[0];b=f-g[0];for(e=0,c=0;e=d&&h>=s)||(h<=d&&h<=s)){g=j=p}else{g=f((i-e)/k(h-d));if(dp){c-=n}g+=c;j+=c;m=i-r*a(g);l=h+r*b(g);v=i+q*a(j);u=h+q*b(j);if((h>d&&ld)){m+=k(d-l)*(m-i)/(l-h);l=d}if((h>s&&us)){v-=k(s-u)*(v-i)/(u-h);u=s}return{x1:m,y1:l,x2:v,y2:u}},smooth:function(l,j,o){var k=l.length,h,g,c,b,q,p,n,m,f=[],e=[],d,a;for(d=0;d=Math.PI){a-=Math.PI*2}}return a}},data:function(a){if(Ext.isArray(a)){return a.slice()}else{if(a instanceof Float32Array){return new Float32Array(a)}}},bool:function(a){return !!a},color:function(a){if(a instanceof Ext.draw.Color){return a.toString()}else{if(a instanceof Ext.draw.gradient.Gradient){return a}else{if(!a){return Ext.draw.Color.NONE}else{if(Ext.isString(a)){if(a.substr(0,3)==="url"){a=Ext.draw.gradient.GradientDefinition.get(a);if(Ext.isString(a)){return a}}else{return Ext.draw.Color.fly(a).toString()}}}}}if(a.type==="linear"){return Ext.create("Ext.draw.gradient.Linear",a)}else{if(a.type==="radial"){return Ext.create("Ext.draw.gradient.Radial",a)}else{if(a.type==="pattern"){return Ext.create("Ext.draw.gradient.Pattern",a)}else{return Ext.draw.Color.NONE}}}},limited:function(a,b){return function(c){c=+c;return Ext.isNumber(c)?Math.min(Math.max(c,a),b):undefined}},limited01:function(a){a=+a;return Ext.isNumber(a)?Math.min(Math.max(a,0),1):undefined},enums:function(){var d={},a=Array.prototype.slice.call(arguments,0),b,c;for(b=0,c=a.length;b=(7-4*o)/11){return i*i-g((11-6*o-11*q)/4,2)}}},elastic:function(o,i){return g(2,10*--o)*m(20*o*e*(i||1)/3)}},k={},a,f,d;function h(i){return function(o){return g(o,i)}}function n(i,o){k[i+"In"]=function(p){return o(p)};k[i+"Out"]=function(p){return 1-o(1-p)};k[i+"InOut"]=function(p){return(p<=0.5)?o(2*p)/2:(2-o(2*(1-p)))/2}}for(d=0,f=b.length;d-1},empty:function(){return this.animations.length===0},step:function(d){var c=this,f=c.animations,e,a=0,b=f.length;for(;a0},applyEasing:function(a){if(typeof a==="string"){a=Ext.draw.TimingFunctions.easingMap[a]}return a},applyCustomEasings:function(a,e){e=e||{};var g,d,b,h,c,f;for(d in a){g=true;h=a[d];b=d.split(",");if(typeof h==="string"){h=Ext.draw.TimingFunctions.easingMap[h]}for(c=0,f=b.length;c=1){h[a]=f[a];delete f[a];if(d[a].remove){h.removeFromInstance=h.removeFromInstance||{};h.removeFromInstance[a]=true}delete d[a]}else{h[a]=b.serve(b.compute(b.source,b.target,b.easing(i),g[a]));e=true}}g.lastUpdate=c;this.setAnimating(g,e);return h},pushDown:function(a,b){b=this.callParent([a.animationOriginal,b]);return this.setAttrs(a,b)},popUp:function(a,b){a=a.prototype;b=this.setAttrs(a,b);if(this._next){return this._next.popUp(a,b)}else{return Ext.apply(a,b)}},step:function(g){var f=this,c=f.animatingPool.slice(),e=c.length,b=0,a,d;for(;b=e.x&&a<=(e.x+e.width)&&f>=e.y&&f<=(e.y+e.height);if(d){return{sprite:this}}}return null},isVisible:function(){var e=this.attr,f=this.getParent(),g=f&&(f.isSurface||f.isVisible()),d=g&&!e.hidden&&e.globalAlpha,b=Ext.draw.Color.NONE,a=Ext.draw.Color.RGBA_NONE,c=e.fillOpacity&&e.fillStyle!==b&&e.fillStyle!==a,i=e.strokeOpacity&&e.strokeStyle!==b&&e.strokeStyle!==a,h=d&&(c||i);return !!h},repaint:function(){var a=this.getSurface();if(a){a.renderFrame()}},remove:function(){var a=this.getSurface();if(a&&a.isSurface){return a.remove(this)}return null},destroy:function(){var b=this,a=b.topModifier,c;while(a){c=a;a=a.getPrevious();c.destroy()}delete b.attr;b.remove();if(b.fireEvent("beforedestroy",b)!==false){b.fireEvent("destroy",b)}b.callParent()}},function(){this.def=new Ext.draw.sprite.AttributeDefinition(this.def);this.def.spriteClass=this});Ext.define("Ext.draw.Path",{requires:["Ext.draw.Draw"],statics:{pathRe:/,?([achlmqrstvxz]),?/gi,pathRe2:/-/gi,pathSplitRe:/\s|,/g},svgString:"",constructor:function(a){var b=this;b.commands=[];b.params=[];b.cursor=null;b.startX=0;b.startY=0;if(a){b.fromSvgString(a)}},clear:function(){var a=this;a.params.length=0;a.commands.length=0;a.cursor=null;a.startX=0;a.startY=0;a.dirt()},dirt:function(){this.svgString=""},moveTo:function(a,c){var b=this;if(!b.cursor){b.cursor=[a,c]}b.params.push(a,c);b.commands.push("M");b.startX=a;b.startY=c;b.cursor[0]=a;b.cursor[1]=c;b.dirt()},lineTo:function(a,c){var b=this;if(!b.cursor){b.cursor=[a,c];b.params.push(a,c);b.commands.push("M")}else{b.params.push(a,c);b.commands.push("L")}b.cursor[0]=a;b.cursor[1]=c;b.dirt()},bezierCurveTo:function(c,e,b,d,a,g){var f=this;if(!f.cursor){f.moveTo(c,e)}f.params.push(c,e,b,d,a,g);f.commands.push("C");f.cursor[0]=a;f.cursor[1]=g;f.dirt()},quadraticCurveTo:function(b,e,a,d){var c=this;if(!c.cursor){c.moveTo(b,e)}c.bezierCurveTo((2*b+c.cursor[0])/3,(2*e+c.cursor[1])/3,(2*b+a)/3,(2*e+d)/3,a,d)},closePath:function(){var a=this;if(a.cursor){a.cursor=null;a.commands.push("Z");a.dirt()}},arcTo:function(A,f,z,d,j,i,v){var E=this;if(i===undefined){i=j}if(v===undefined){v=0}if(!E.cursor){E.moveTo(A,f);return}if(j===0||i===0){E.lineTo(A,f);return}z-=A;d-=f;var B=E.cursor[0]-A,g=E.cursor[1]-f,C=z*g-d*B,b,a,l,r,k,q,x=Math.sqrt(B*B+g*g),u=Math.sqrt(z*z+d*d),t,e,c;if(C===0){E.lineTo(A,f);return}if(i!==j){b=Math.cos(v);a=Math.sin(v);l=b/j;r=a/i;k=-a/j;q=b/i;var D=l*B+r*g;g=k*B+q*g;B=D;D=l*z+r*d;d=k*z+q*d;z=D}else{B/=j;g/=i;z/=j;d/=i}e=B*u+z*x;c=g*u+d*x;t=1/(Math.sin(Math.asin(Math.abs(C)/(x*u))*0.5)*Math.sqrt(e*e+c*c));e*=t;c*=t;var o=(e*B+c*g)/(B*B+g*g),m=(e*z+c*d)/(z*z+d*d);var n=B*o-e,p=g*o-c,h=z*m-e,y=d*m-c,w=Math.atan2(p,n),s=Math.atan2(y,h);if(C>0){if(s=Math.PI*2){o.ellipse(h,f,c,a,q,n,n+Math.PI,e);o.ellipse(h,f,c,a,q,n+Math.PI,d,e);return}if(!e){if(d=m){s.push(j+u*b+i,h+t*b+f,j*b+u+i,h*b+t+f,u+i,t+f);r+=6;v-=m;C=j;j=u;u=-C;C=h;h=t;t=-C}if(v){g=(0.3294738052815987+0.012120855841304373*v)*v;A=Math.cos(v);a=Math.sin(v);B=A+g*a;c=a-g*A;s.push(j+u*g+i,h+t*g+f,j*B+u*c+i,h*B+t*c+f,j*A+u*a+i,h*A+t*a+f);r+=6}return r},arcSvg:function(j,h,r,m,w,t,c){if(j<0){j=-j}if(h<0){h=-h}var x=this,u=x.cursor[0],f=x.cursor[1],a=(u-t)/2,y=(f-c)/2,d=Math.cos(r),s=Math.sin(r),o=a*d+y*s,v=-a*s+y*d,i=o/j,g=v/h,p=i*i+g*g,e=(u+t)*0.5,b=(f+c)*0.5,l=0,k=0;if(p>=1){p=Math.sqrt(p);j*=p;h*=p}else{p=Math.sqrt(1/p-1);if(m===w){p=-p}l=p*j*g;k=-p*h*i;e+=d*l-s*k;b+=s*l+d*k}var q=Math.atan2((v-k)/h,(o-l)/j),n=Math.atan2((-v-k)/h,(-o-l)/j)-q;if(w){if(n<=0){n+=Math.PI*2}}else{if(n>=0){n-=Math.PI*2}}x.ellipse(e,b,j,h,r,q,q+n,1-w)},fromSvgString:function(e){if(!e){return}var m=this,h,l={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0,A:7,C:6,H:1,L:2,M:2,Q:4,S:4,T:2,V:1,Z:0},k="",g,f,c=0,b=0,d=false,j,n,a;if(Ext.isString(e)){h=e.replace(Ext.draw.Path.pathRe," $1 ").replace(Ext.draw.Path.pathRe2," -").split(Ext.draw.Path.pathSplitRe)}else{if(Ext.isArray(e)){h=e.join(",").split(Ext.draw.Path.pathSplitRe)}}for(j=0,n=0;j=0){q=Math.sqrt(q);o=(q-g)/2/i;if(00){o-=q/i;if(0k.x){b=k.x}if(hk.y){f=k.y}if(a=f.length||!e.isVisible()){return a}d.attr=f[c];a=d.isVisible(point,options);d.attr=b;return a},render:function(b,l,d,h){var g=this,j=g.getTemplate(),k=g.attr.matrix,c=j.attr,a=g.instances,e,f=g.position;k.toContext(l);j.preRender(b,l,d,h);j.useAttributes(l,h);for(e=0;ee){f=m.substr(e,i-e)}else{continue}}g=f.indexOf("/");if(g>0){f=f.substr(0,g)}else{if(g===0){continue}}if(f!=="normal"&&f!=="inherit"){h=n[f];if(h){l[h]=f}else{if(f.match(Ext.dom.Element.unitRe)){l.fontSize=f}else{l.fontFamily=m.substr(e);break}}}e=i+1}if(!l.fontStyle){l.fontStyle=""}if(!l.fontVariant){l.fontVariant=""}if(!l.fontWeight){l.fontWeight=""}this.setAttributes(l,true)},fontProperties:{fontStyle:true,fontVariant:true,fontWeight:true,fontSize:true,fontFamily:true},setAttributes:function(g,i,e){var f,h;if(g&&g.font){h={};for(f in g){if(!(f in this.fontProperties)){h[f]=g[f]}}g=h}this.callParent([g,i,e])},getBBox:function(g){var h=this,f=h.attr.bbox.plain,e=h.getSurface();if(f.dirty){h.updatePlainBBox(f);f.dirty=false}if(e.getInherited().rtl&&e.getFlipRtlText()){h.updatePlainBBox(f,true)}return h.callParent([g])},rtlAlignments:{start:"end",center:"center",end:"start"},updatePlainBBox:function(k,B){var C=this,w=C.attr,o=w.x,n=w.y,q=[],t=w.font,r=w.text,s=w.textBaseline,l=w.textAlign,u=(B&&C.oldSize)?C.oldSize:(C.oldSize=Ext.draw.TextMeasurer.measureText(r,t)),z=C.getSurface(),p=z.getInherited().rtl,v=p&&z.getFlipRtlText(),h=z.getRect(),f=u.sizes,g=u.height,j=u.width,m=f?f.length:0,e,A=0;switch(s){case"hanging":case"top":break;case"ideographic":case"bottom":n-=g;break;case"alphabetic":n-=g*0.8;break;case"middle":n-=g*0.5;break}if(v){o=h[2]-h[0]-o;l=C.rtlAlignments[l]}switch(l){case"start":if(p){for(;A0&&m>0){a=(Math.sqrt(f*f+m*m)*Math.abs(Math.cos(c-Math.atan(f/m))))/2;k=q.createLinearGradient(d+p*a,b+j*a,d-p*a,b-j*a);for(e=0;e=0;b--){a[b].destroy()}}else{for(;b>=0;b--){c=a[b];c.setParent(null);c.setSurface(null)}}a.length=0;this.map={};this.dirtyZIndex=true},applyItems:function(a){if(this.getItems()){this.removeAll(true)}return Ext.Array.from(this.add(a))},createItem:function(a){return Ext.create(a.xclass||"sprite."+a.type,a)},getBBox:function(f,b){var f=Ext.Array.from(f),c=Infinity,h=-Infinity,g=Infinity,a=-Infinity,j,k,d,e;for(d=0,e=f.length;dk.x){c=k.x}if(hk.y){g=k.y}if(a0){g.isPendingRenderFrame=true;return}var f=g.getRect(),c=g.getBackground(),a=g.getItems(),e,b,d;if(!f){return}g.orderByZIndex();if(g.getDirty()){g.clear();g.clearTransform();if(c){g.renderSprite(c)}for(b=0,d=a.length;b=0;e--){d=g[e];if(d.hitTest){a=d.hitTest(b,c);if(a){return a}}}return null},hitTestEvent:function(b,a){var c=this.getEventXY(b);return this.hitTest(c,a)}});Ext.define("Ext.draw.engine.SvgContext",{requires:["Ext.draw.Color"],toSave:["strokeOpacity","strokeStyle","fillOpacity","fillStyle","globalAlpha","lineWidth","lineCap","lineJoin","lineDash","lineDashOffset","miterLimit","shadowOffsetX","shadowOffsetY","shadowBlur","shadowColor","globalCompositeOperation","position","fillGradient","strokeGradient"],strokeOpacity:1,strokeStyle:"none",fillOpacity:1,fillStyle:"none",lineDash:[],lineDashOffset:0,globalAlpha:1,lineWidth:1,lineCap:"butt",lineJoin:"miter",miterLimit:10,shadowOffsetX:0,shadowOffsetY:0,shadowBlur:0,shadowColor:"none",globalCompositeOperation:"src",urlStringRe:/^url\(#([\w\-]+)\)$/,constructor:function(a){this.surface=a;this.state=[];this.matrix=new Ext.draw.Matrix();this.path=null;this.clear()},clear:function(){this.group=this.surface.mainGroup;this.position=0;this.path=null},getElement:function(a){return this.surface.getSvgElement(this.group,a,this.position++)},removeElement:function(d){var d=Ext.fly(d),h,g,b,f,a,e,c;if(!d){return}if(d.dom.tagName==="g"){a=d.dom.gradients;for(c in a){a[c].destroy()}}else{h=d.getAttribute("fill");g=d.getAttribute("stroke");b=h&&h.match(this.urlStringRe);f=g&&g.match(this.urlStringRe);if(b&&b[1]){e=Ext.fly(b[1]);if(e){e.destroy()}}if(f&&f[1]){e=Ext.fly(f[1]);if(e){e.destroy()}}}d.destroy()},save:function(){var c=this.toSave,e={},d=this.getElement("g"),b,a;for(a=0;athis.position){this.removeElement(c[c.length-1])}for(a=0;athis.position){Ext.fly(a[a.length-1]).destroy()}return"url(#"+this.element.getId()+")"},destroy:function(){var b=this.statics().map,a=this.element;if(a&&a.dom){delete b[a.dom.id];a.destroy()}this.callParent()}});Ext.define("Ext.draw.engine.Svg",{extend:"Ext.draw.Surface",requires:["Ext.draw.engine.SvgContext"],statics:{BBoxTextCache:{}},config:{highPrecision:false},getElementConfig:function(){return{reference:"element",style:{position:"absolute"},children:[{reference:"innerElement",style:{width:"100%",height:"100%",position:"relative"},children:[{tag:"svg",reference:"svgElement",namespace:"http://www.w3.org/2000/svg",width:"100%",height:"100%",version:1.1}]}]}},constructor:function(a){var b=this;b.callParent([a]);b.mainGroup=b.createSvgNode("g");b.defElement=b.createSvgNode("defs");b.svgElement.appendChild(b.mainGroup);b.svgElement.appendChild(b.defElement);b.ctx=new Ext.draw.engine.SvgContext(b)},createSvgNode:function(a){var b=document.createElementNS("http://www.w3.org/2000/svg",a);return Ext.get(b)},getSvgElement:function(d,b,a){var c;if(d.dom.childNodes.length>a){c=d.dom.childNodes[a];if(c.tagName===b){return Ext.get(c)}else{Ext.destroy(c)}}c=Ext.get(this.createSvgNode(b));if(a===0){d.insertFirst(c)}else{c.insertAfter(Ext.fly(d.dom.childNodes[a-1]))}c.cache={};return c},setElementAttributes:function(d,b){var f=d.dom,a=d.cache,c,e;for(c in b){e=b[c];if(a[c]!==e){a[c]=e;f.setAttribute(c,e)}}},getNextDef:function(a){return this.getSvgElement(this.defElement,a,this.defPosition++)},clearTransform:function(){var a=this;a.mainGroup.set({transform:a.matrix.toSvg()})},clear:function(){this.ctx.clear();this.defPosition=0},renderSprite:function(b){var d=this,c=d.getRect(),a=d.ctx;if(b.attr.hidden||b.attr.globalAlpha===0){a.save();a.restore();return}b.element=a.save();b.preRender(this);b.useAttributes(a,c);if(false===b.render(this,a,[0,0,c[2],c[3]])){return false}b.setDirty(false);a.restore()},flatten:function(e,b){var c='',f=Ext.getClassName(this),a,g,d;c+='';for(d=0;d';c+=this.serializeNode(a.svgElement.dom);c+=""}c+="";return{data:"data:image/svg+xml;utf8,"+encodeURIComponent(c),type:"svg"}},serializeNode:function(d){var b="",c,f,a,e;if(d.nodeType===document.TEXT_NODE){return d.nodeValue}b+="<"+d.nodeName;if(d.attributes.length){for(c=0,f=d.attributes.length;c";return b},destroy:function(){var a=this;a.ctx.destroy();a.mainGroup.destroy();delete a.mainGroup;delete a.ctx;a.callParent()},remove:function(a,b){if(a&&a.element){if(this.ctx){this.ctx.removeElement(a.element)}else{a.element.destroy()}a.element=null}this.callParent(arguments)}});Ext.draw||(Ext.draw={});Ext.draw.engine||(Ext.draw.engine={});Ext.draw.engine.excanvas=true;if(!document.createElement("canvas").getContext){(function(){var ab=Math;var n=ab.round;var l=ab.sin;var A=ab.cos;var H=ab.abs;var N=ab.sqrt;var d=10;var f=d/2;var z=+navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];function y(){return this.context_||(this.context_=new D(this))}var t=Array.prototype.slice;function g(j,m,p){var i=t.call(arguments,2);return function(){return j.apply(m,i.concat(t.call(arguments)))}}function af(i){return String(i).replace(/&/g,"&").replace(/"/g,""")}function Y(m,j,i){Ext.onReady(function(){if(!m.namespaces[j]){m.namespaces.add(j,i,"#default#VML")}})}function R(j){Y(j,"g_vml_","urn:schemas-microsoft-com:vml");Y(j,"g_o_","urn:schemas-microsoft-com:office:office");if(!j.styleSheets.ex_canvas_){var i=j.createStyleSheet();i.owningElement.id="ex_canvas_";i.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}R(document);var e={init:function(i){var j=i||document;j.createElement("canvas");j.attachEvent("onreadystatechange",g(this.init_,this,j))},init_:function(p){var m=p.getElementsByTagName("canvas");for(var j=0;j1){m--}if(6*m<1){return j+(i-j)*6*m}else{if(2*m<1){return i}else{if(3*m<2){return j+(i-j)*(2/3-m)*6}else{return j}}}}var C={};function F(j){if(j in C){return C[j]}var ag,Z=1;j=String(j);if(j.charAt(0)=="#"){ag=j}else{if(/^rgb/.test(j)){var p=M(j);var ag="#",ah;for(var m=0;m<3;m++){if(p[m].indexOf("%")!=-1){ah=Math.floor(c(p[m])*255)}else{ah=+p[m]}ag+=k[r(ah,0,255)]}Z=+p[3]}else{if(/^hsl/.test(j)){var p=M(j);ag=I(p);Z=p[3]}else{ag=b[j]||j}}}return C[j]={color:ag,alpha:Z}}var o={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var L={};function E(i){if(L[i]){return L[i]}var p=document.createElement("div");var m=p.style;try{m.font=i}catch(j){}return L[i]={style:m.fontStyle||o.style,variant:m.fontVariant||o.variant,weight:m.fontWeight||o.weight,size:m.fontSize||o.size,family:m.fontFamily||o.family}}function u(m,j){var i={};for(var ah in m){i[ah]=m[ah]}var ag=parseFloat(j.currentStyle.fontSize),Z=parseFloat(m.size);if(typeof m.size=="number"){i.size=m.size}else{if(m.size.indexOf("px")!=-1){i.size=Z}else{if(m.size.indexOf("em")!=-1){i.size=ag*Z}else{if(m.size.indexOf("%")!=-1){i.size=(ag/100)*Z}else{if(m.size.indexOf("pt")!=-1){i.size=Z/0.75}else{i.size=ag}}}}}i.size*=0.981;return i}function ac(i){return i.style+" "+i.variant+" "+i.weight+" "+i.size+"px "+i.family}var s={butt:"flat",round:"round"};function S(i){return s[i]||"square"}function D(i){this.m_=B();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineDash=[];this.lineCap="butt";this.miterLimit=d*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var m="width:"+i.clientWidth+"px;height:"+i.clientHeight+"px;overflow:hidden;position:absolute";var j=i.ownerDocument.createElement("div");j.style.cssText=m;i.appendChild(j);var p=j.cloneNode(false);p.style.backgroundColor="red";p.style.filter="alpha(opacity=0)";i.appendChild(p);this.element_=j;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var q=D.prototype;q.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};q.beginPath=function(){this.currentPath_=[]};q.moveTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"moveTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.lineTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"lineTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.bezierCurveTo=function(m,j,ak,aj,ai,ag){var i=V(this,ai,ag);var ah=V(this,m,j);var Z=V(this,ak,aj);K(this,ah,Z,i)};function K(i,Z,m,j){i.currentPath_.push({type:"bezierCurveTo",cp1x:Z.x,cp1y:Z.y,cp2x:m.x,cp2y:m.y,x:j.x,y:j.y});i.currentX_=j.x;i.currentY_=j.y}q.quadraticCurveTo=function(ai,m,j,i){var ah=V(this,ai,m);var ag=V(this,j,i);var aj={x:this.currentX_+2/3*(ah.x-this.currentX_),y:this.currentY_+2/3*(ah.y-this.currentY_)};var Z={x:aj.x+(ag.x-this.currentX_)/3,y:aj.y+(ag.y-this.currentY_)/3};K(this,aj,Z,ag)};q.arc=function(al,aj,ak,ag,j,m){ak*=d;var ap=m?"at":"wa";var am=al+A(ag)*ak-f;var ao=aj+l(ag)*ak-f;var i=al+A(j)*ak-f;var an=aj+l(j)*ak-f;if(am==i&&!m){am+=0.125}var Z=V(this,al,aj);var ai=V(this,am,ao);var ah=V(this,i,an);this.currentPath_.push({type:ap,x:Z.x,y:Z.y,radius:ak,xStart:ai.x,yStart:ai.y,xEnd:ah.x,yEnd:ah.y})};q.rect=function(m,j,i,p){this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath()};q.strokeRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.stroke();this.currentPath_=Z};q.fillRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.fill();this.currentPath_=Z};q.createLinearGradient=function(j,p,i,m){var Z=new U("gradient");Z.x0_=j;Z.y0_=p;Z.x1_=i;Z.y1_=m;return Z};q.createRadialGradient=function(p,ag,m,j,Z,i){var ah=new U("gradientradial");ah.x0_=p;ah.y0_=ag;ah.r0_=m;ah.x1_=j;ah.y1_=Z;ah.r1_=i;return ah};q.drawImage=function(an,j){var ah,Z,aj,ar,al,ak,ao,av;var ai=an.runtimeStyle.width;var am=an.runtimeStyle.height;an.runtimeStyle.width="auto";an.runtimeStyle.height="auto";var ag=an.width;var aq=an.height;an.runtimeStyle.width=ai;an.runtimeStyle.height=am;if(arguments.length==3){ah=arguments[1];Z=arguments[2];al=ak=0;ao=aj=ag;av=ar=aq}else{if(arguments.length==5){ah=arguments[1];Z=arguments[2];aj=arguments[3];ar=arguments[4];al=ak=0;ao=ag;av=aq}else{if(arguments.length==9){al=arguments[1];ak=arguments[2];ao=arguments[3];av=arguments[4];ah=arguments[5];Z=arguments[6];aj=arguments[7];ar=arguments[8]}else{throw Error("Invalid number of arguments")}}}var au=V(this,ah,Z);var at=[];var i=10;var p=10;var ap=this.m_;at.push(" ','","");this.element_.insertAdjacentHTML("BeforeEnd",at.join(""))};q.setLineDash=function(i){if(i.length===1){i=i.slice();i[1]=i[0]}this.lineDash=i};q.getLineDash=function(){return this.lineDash};q.stroke=function(ak){var ai=[];var m=10;var al=10;ai.push("aj.x){aj.x=j.x}if(Z.y==null||j.yaj.y){aj.y=j.y}}}ai.push(' ">');if(!ak){w(this,ai)}else{G(this,ai,Z,aj)}ai.push("");this.element_.insertAdjacentHTML("beforeEnd",ai.join(""))};function w(m,ag){var j=F(m.strokeStyle);var p=j.color;var Z=j.alpha*m.globalAlpha;var i=m.lineScale_*m.lineWidth;if(i<1){Z*=i}ag.push("')}function G(aq,ai,aK,ar){var aj=aq.fillStyle;var aB=aq.arcScaleX_;var aA=aq.arcScaleY_;var j=ar.x-aK.x;var p=ar.y-aK.y;if(aj instanceof U){var an=0;var aF={x:0,y:0};var ax=0;var am=1;if(aj.type_=="gradient"){var al=aj.x0_/aB;var m=aj.y0_/aA;var ak=aj.x1_/aB;var aM=aj.y1_/aA;var aJ=V(aq,al,m);var aI=V(aq,ak,aM);var ag=aI.x-aJ.x;var Z=aI.y-aJ.y;an=Math.atan2(ag,Z)*180/Math.PI;if(an<0){an+=360}if(an<0.000001){an=0}}else{var aJ=V(aq,aj.x0_,aj.y0_);aF={x:(aJ.x-aK.x)/j,y:(aJ.y-aK.y)/p};j/=aB*d;p/=aA*d;var aD=ab.max(j,p);ax=2*aj.r0_/aD;am=2*aj.r1_/aD-ax}var av=aj.colors_;av.sort(function(aN,i){return aN.offset-i.offset});var ap=av.length;var au=av[0].color;var at=av[ap-1].color;var az=av[0].alpha*aq.globalAlpha;var ay=av[ap-1].alpha*aq.globalAlpha;var aE=[];for(var aH=0;aH')}else{if(aj instanceof T){if(j&&p){var ah=-aK.x;var aC=-aK.y;ai.push("')}}else{var aL=F(aq.fillStyle);var aw=aL.color;var aG=aL.alpha*aq.globalAlpha;ai.push('')}}}q.fill=function(){this.$stroke(true)};q.closePath=function(){this.currentPath_.push({type:"close"})};function V(j,Z,p){var i=j.m_;return{x:d*(Z*i[0][0]+p*i[1][0]+i[2][0])-f,y:d*(Z*i[0][1]+p*i[1][1]+i[2][1])-f}}q.save=function(){var i={};v(this,i);this.aStack_.push(i);this.mStack_.push(this.m_);this.m_=J(B(),this.m_)};q.restore=function(){if(this.aStack_.length){v(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function h(i){return isFinite(i[0][0])&&isFinite(i[0][1])&&isFinite(i[1][0])&&isFinite(i[1][1])&&isFinite(i[2][0])&&isFinite(i[2][1])}function aa(j,i,p){if(!h(i)){return}j.m_=i;if(p){var Z=i[0][0]*i[1][1]-i[0][1]*i[1][0];j.lineScale_=N(H(Z))}}q.translate=function(m,j){var i=[[1,0,0],[0,1,0],[m,j,1]];aa(this,J(i,this.m_),false)};q.rotate=function(j){var p=A(j);var m=l(j);var i=[[p,m,0],[-m,p,0],[0,0,1]];aa(this,J(i,this.m_),false)};q.scale=function(m,j){this.arcScaleX_*=m;this.arcScaleY_*=j;var i=[[m,0,0],[0,j,0],[0,0,1]];aa(this,J(i,this.m_),true)};q.transform=function(Z,p,ah,ag,j,i){var m=[[Z,p,0],[ah,ag,0],[j,i,1]];aa(this,J(m,this.m_),true)};q.setTransform=function(ag,Z,ai,ah,p,j){var i=[[ag,Z,0],[ai,ah,0],[p,j,1]];aa(this,i,true)};q.drawText_=function(am,ak,aj,ap,ai){var ao=this.m_,at=1000,j=0,ar=at,ah={x:0,y:0},ag=[];var i=u(E(this.font),this.element_);var p=ac(i);var au=this.element_.currentStyle;var Z=this.textAlign.toLowerCase();switch(Z){case"left":case"center":case"right":break;case"end":Z=au.direction=="ltr"?"right":"left";break;case"start":Z=au.direction=="rtl"?"right":"left";break;default:Z="left"}switch(this.textBaseline){case"hanging":case"top":ah.y=i.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":ah.y=-i.size/3;break}switch(Z){case"right":j=at;ar=0.05;break;case"center":j=ar=at/2;break}var aq=V(this,ak+ah.x,aj+ah.y);ag.push('');if(ai){w(this,ag)}else{G(this,ag,{x:-j,y:0},{x:ar,y:i.size})}var an=ao[0][0].toFixed(3)+","+ao[1][0].toFixed(3)+","+ao[0][1].toFixed(3)+","+ao[1][1].toFixed(3)+",0,0";var al=n(aq.x/d)+","+n(aq.y/d);ag.push('','','');this.element_.insertAdjacentHTML("beforeEnd",ag.join(""))};q.fillText=function(m,i,p,j){this.drawText_(m,i,p,j,false)};q.strokeText=function(m,i,p,j){this.drawText_(m,i,p,j,true)};q.measureText=function(m){if(!this.textMeasureEl_){var i='';this.element_.insertAdjacentHTML("beforeEnd",i);this.textMeasureEl_=this.element_.lastChild}var j=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(j.createTextNode(m));return{width:this.textMeasureEl_.offsetWidth}};q.clip=function(){};q.arcTo=function(){};q.createPattern=function(j,i){return new T(j,i)};function U(i){this.type_=i;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}U.prototype.addColorStop=function(j,i){i=F(i);this.colors_.push({offset:j,color:i.color,alpha:i.alpha})};function T(j,i){Q(j);switch(i){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=i;break;default:O("SYNTAX_ERR")}this.src_=j.src;this.width_=j.width;this.height_=j.height}function O(i){throw new P(i)}function Q(i){if(!i||i.nodeType!=1||i.tagName!="IMG"){O("TYPE_MISMATCH_ERR")}if(i.readyState!="complete"){O("INVALID_STATE_ERR")}}function P(i){this.code=this[i];this.message=i+": DOM Exception "+this.code}var X=P.prototype=new Error();X.INDEX_SIZE_ERR=1;X.DOMSTRING_SIZE_ERR=2;X.HIERARCHY_REQUEST_ERR=3;X.WRONG_DOCUMENT_ERR=4;X.INVALID_CHARACTER_ERR=5;X.NO_DATA_ALLOWED_ERR=6;X.NO_MODIFICATION_ALLOWED_ERR=7;X.NOT_FOUND_ERR=8;X.NOT_SUPPORTED_ERR=9;X.INUSE_ATTRIBUTE_ERR=10;X.INVALID_STATE_ERR=11;X.SYNTAX_ERR=12;X.INVALID_MODIFICATION_ERR=13;X.NAMESPACE_ERR=14;X.INVALID_ACCESS_ERR=15;X.VALIDATION_ERR=16;X.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=e;CanvasRenderingContext2D=D;CanvasGradient=U;CanvasPattern=T;DOMException=P})()}Ext.define("Ext.draw.engine.Canvas",{extend:"Ext.draw.Surface",requires:["Ext.draw.engine.excanvas","Ext.draw.Animator","Ext.draw.Color"],config:{highPrecision:false},statics:{contextOverrides:{setGradientBBox:function(a){this.bbox=a},fill:function(){var c=this.fillStyle,a=this.fillGradient,b=this.fillOpacity,d=this.globalAlpha,e=this.bbox;if(c!==Ext.draw.Color.RGBA_NONE&&b!==0){if(a&&e){this.fillStyle=a.generateGradient(this,e)}if(b!==1){this.globalAlpha=d*b}this.$fill();if(b!==1){this.globalAlpha=d}if(a&&e){this.fillStyle=c}}},stroke:function(){var e=this.strokeStyle,c=this.strokeGradient,a=this.strokeOpacity,b=this.globalAlpha,d=this.bbox;if(e!==Ext.draw.Color.RGBA_NONE&&a!==0){if(c&&d){this.strokeStyle=c.generateGradient(this,d)}if(a!==1){this.globalAlpha=b*a}this.$stroke();if(a!==1){this.globalAlpha=b}if(c&&d){this.strokeStyle=e}}},fillStroke:function(d,e){var j=this,i=this.fillStyle,h=this.fillOpacity,f=this.strokeStyle,c=this.strokeOpacity,b=j.shadowColor,a=j.shadowBlur,g=Ext.draw.Color.RGBA_NONE;if(e===undefined){e=d.transformFillStroke}if(!e){d.inverseMatrix.toContext(j)}if(i!==g&&h!==0){j.fill();j.shadowColor=g;j.shadowBlur=0}if(f!==g&&c!==0){j.stroke()}j.shadowColor=b;j.shadowBlur=a},setLineDash:function(a){if(this.$setLineDash){this.$setLineDash(a)}},getLineDash:function(){if(this.$getLineDash){return this.$getLineDash()}},ellipse:function(g,e,c,a,j,b,f,d){var i=Math.cos(j),h=Math.sin(j);this.transform(i*c,h*c,-h*a,i*a,g,e);this.arc(0,0,1,b,f,d);this.transform(i/c,-h/a,h/c,i/a,-(i*g+h*e)/c,(h*g-i*e)/a)},appendPath:function(f){var e=this,c=0,b=0,a=f.commands,g=f.params,d=a.length;e.beginPath();for(;c=D.length){C.createCanvas()}x=D[q].dom;x.style.left=A+"px";x.style.top=z+"px";m=Math.min(n,y-z);if(m*u!==x.height){x.height=m*u;x.style.height=m+"px"}o=Math.min(n,d-A);if(o*u!==x.width){x.width=o*u;x.style.width=o+"px"}C.applyDefaults(C.contexts[q])}}for(q+=1;qB||a.x+a.widthf||a.y+a.height=0},filename:Ext.isString,width:Ext.isNumber,height:Ext.isNumber,scale:Ext.isNumber,pdf:Ext.isObject,jpeg:Ext.isObject},initAnimator:function(){this.frameCallbackId=Ext.draw.Animator.addFrameCallback("renderFrame",this)},applyGradients:function(b){var a=[],c,f,d,e;if(!Ext.isArray(b)){return a}for(c=0,f=b.length;c=0&&c[a[f].type]>h){a[f+1]=a[f];f--}a[f+1]=b}d=a[0].flatten(l,a);if(k==="image"){g=new Image();g.src=d.data;d.data=g;return d}if(k==="stream"){d.data=d.data.replace(/^data:image\/[^;]+/,"data:application/octet-stream");return d}return d},download:function(d){var e=this,a=[],b,c,f;d=Ext.apply({version:2,data:e.getImage().data},d);for(c in d){if(d.hasOwnProperty(c)){f=d[c];if(c in e.supportedOptions){if(e.supportedOptions[c].call(e,f)){a.push({tag:"input",type:"hidden",name:c,value:Ext.String.htmlEncode(Ext.isObject(f)?Ext.JSON.encode(f):f)})}}}}b=Ext.dom.Helper.markup({tag:"html",children:[{tag:"head"},{tag:"body",children:[{tag:"form",method:"POST",action:d.url||e.defaultDownloadServerUrl,children:a},{tag:"script",type:"text/javascript",children:'document.getElementsByTagName("form")[0].submit();'}]}]});window.open("","ImageDownload_"+Date.now()).document.write(b)},destroy:function(){var a=this.frameCallbackId;if(a){Ext.draw.Animator.removeFrameCallback(a)}this.callParent()}},function(){if(location.search.match("svg")){Ext.draw.Container.prototype.engine="Ext.draw.engine.Svg"}else{if((Ext.os.is.BlackBerry&&Ext.os.version.getMajor()===10)||(Ext.browser.is.AndroidStock4&&(Ext.os.version.getMinor()===1||Ext.os.version.getMinor()===2||Ext.os.version.getMinor()===3))){Ext.draw.Container.prototype.engine="Ext.draw.engine.Svg"}}});Ext.define("Ext.chart.theme.Base",{mixins:{factoryable:"Ext.mixin.Factoryable"},requires:["Ext.draw.Color"],factoryConfig:{type:"chart.theme"},isTheme:true,config:{baseColor:null,colors:undefined,gradients:null,chart:{defaults:{background:"white"}},axis:{defaults:{label:{x:0,y:0,textBaseline:"middle",textAlign:"center",fontSize:"default",fontFamily:"default",fontWeight:"default",fillStyle:"black"},title:{fillStyle:"black",fontSize:"default*1.23",fontFamily:"default",fontWeight:"default"},style:{strokeStyle:"black"},grid:{strokeStyle:"rgb(221, 221, 221)"}},top:{style:{textPadding:5}},bottom:{style:{textPadding:5}}},series:{defaults:{label:{fillStyle:"black",strokeStyle:"none",fontFamily:"default",fontWeight:"default",fontSize:"default*1.077",textBaseline:"middle",textAlign:"center"},labelOverflowPadding:5}},sprites:{text:{fontSize:"default",fontWeight:"default",fontFamily:"default",fillStyle:"black"}},seriesThemes:undefined,markerThemes:{type:["circle","cross","plus","square","triangle","diamond"]},useGradients:false,background:null},colorDefaults:["#94ae0a","#115fa6","#a61120","#ff8809","#ffd13e","#a61187","#24ad9a","#7c7474","#a66111"],constructor:function(a){this.initConfig(a);this.resolveDefaults()},defaultRegEx:/^default([+\-/\*]\d+(?:\.\d+)?)?$/,defaultOperators:{"*":function(b,a){return b*a},"+":function(b,a){return b+a},"-":function(b,a){return b-a}},resolveDefaults:function(){var a=this;Ext.onReady(function(){var f=Ext.clone(a.getSprites()),e=Ext.clone(a.getAxis()),d=Ext.clone(a.getSeries()),g,c,b;if(!a.superclass.defaults){g=Ext.getBody().createChild({tag:"div",cls:"x-component"});a.superclass.defaults={fontFamily:g.getStyle("fontFamily"),fontWeight:g.getStyle("fontWeight"),fontSize:parseFloat(g.getStyle("fontSize")),fontVariant:g.getStyle("fontVariant"),fontStyle:g.getStyle("fontStyle")};g.destroy()}a.replaceDefaults(f.text);a.setSprites(f);for(c in e){b=e[c];a.replaceDefaults(b.label);a.replaceDefaults(b.title)}a.setAxis(e);for(c in d){b=d[c];a.replaceDefaults(b.label)}a.setSeries(d)})},replaceDefaults:function(h){var e=this,g=e.superclass.defaults,a=e.defaultRegEx,d,f,c,b;if(Ext.isObject(h)){for(d in g){c=a.exec(h[d]);if(c){f=g[d];c=c[1];if(c){b=e.defaultOperators[c.charAt(0)];f=Math.round(b(f,parseFloat(c.substr(1))))}h[d]=f}}}},applyBaseColor:function(c){var a,b;if(c){a=c.isColor?c:Ext.draw.Color.fromString(c);b=a.getHSL()[2];if(b<0.15){a=a.createLighter(0.3)}else{if(b<0.3){a=a.createLighter(0.15)}else{if(b>0.85){a=a.createDarker(0.3)}else{if(b>0.7){a=a.createDarker(0.15)}}}}this.setColors([a.createDarker(0.3).toString(),a.createDarker(0.15).toString(),a.toString(),a.createLighter(0.12).toString(),a.createLighter(0.24).toString(),a.createLighter(0.31).toString()])}return c},applyColors:function(a){return a||this.colorDefaults},updateUseGradients:function(a){if(a){this.updateGradients({type:"linear",degrees:90})}},updateBackground:function(a){if(a){var b=this.getChart();b.defaults.background=a;this.setChart(b)}},updateGradients:function(a){var c=this.getColors(),e=[],h,b,d,f,g;if(Ext.isObject(a)){for(f=0,g=c&&c.length||0;fMath.PI){n-=Math.PI*2}if(k){n=n*(1-d)-Math.PI/2*d;j=c;c=m;m=j}else{n=n*(1-d)}h.rotationRads=n;h.x=g*(1-d)+b*d;h.y=f*(1-d)+a*d;p=b-g;o=a-f;if(Math.abs(o*c)>Math.abs(p*m)){if(o>0){h.calloutEndX=h.x-(m/2)*(p/o)*d;h.calloutEndY=h.y-(m/2)*d}else{h.calloutEndX=h.x+(m/2)*(p/o)*d;h.calloutEndY=h.y+(m/2)*d}}else{if(p>0){h.calloutEndX=h.x-c/2;h.calloutEndY=h.y-(c/2)*(o/p)*d}else{h.calloutEndX=h.x+c/2;h.calloutEndY=h.y+(c/2)*(o/p)*d}}if(h.calloutStartX&&h.calloutStartY){h.calloutHasLine=(p>0&&h.calloutStartXh.calloutEndX)||(o>0&&h.calloutStartYh.calloutEndY)}else{h.calloutHasLine=true}}return h},pushDown:function(a,b){b=this.callParent([a.calloutOriginal,b]);return this.setAttrs(a,b)},popUp:function(a,b){a=a.prototype;b=this.setAttrs(a,b);if(this._next){return this._next.popUp(a,b)}else{return Ext.apply(a,b)}}});Ext.define("Ext.chart.label.Label",{extend:"Ext.draw.sprite.Text",requires:["Ext.chart.label.Callout"],inheritableStatics:{def:{processors:{callout:"limited01",calloutHasLine:"bool",calloutPlaceX:"number",calloutPlaceY:"number",calloutStartX:"number",calloutStartY:"number",calloutEndX:"number",calloutEndY:"number",calloutColor:"color",calloutWidth:"number",calloutVertical:"bool",labelOverflowPadding:"number",display:"enums(none,under,over,rotate,insideStart,insideEnd,inside,outside)",orientation:"enums(horizontal,vertical)",renderer:"default"},defaults:{callout:0,calloutHasLine:true,calloutPlaceX:0,calloutPlaceY:0,calloutStartX:0,calloutStartY:0,calloutEndX:0,calloutEndY:0,calloutWidth:1,calloutVertical:false,calloutColor:"black",labelOverflowPadding:5,display:"none",orientation:"",renderer:null},triggers:{callout:"transform",calloutPlaceX:"transform",calloutPlaceY:"transform",labelOverflowPadding:"transform",calloutRotation:"transform",display:"hidden"},updaters:{hidden:function(a){a.hidden=a.display==="none"}}}},config:{fx:{customDurations:{callout:200}},field:null,calloutLine:true},applyCalloutLine:function(a){if(a){return Ext.apply({},a)}},prepareModifiers:function(){this.callParent(arguments);this.calloutModifier=new Ext.chart.label.Callout({sprite:this});this.fx.setNext(this.calloutModifier);this.calloutModifier.setNext(this.topModifier)},render:function(b,c){var e=this,a=e.attr,d=a.calloutColor;c.save();c.globalAlpha*=a.callout;if(c.globalAlpha>0&&a.calloutHasLine){if(d&&d.isGradient){d=d.getStops()[0].color}c.strokeStyle=d;c.fillStyle=d;c.lineWidth=a.calloutWidth;c.beginPath();c.moveTo(e.attr.calloutStartX,e.attr.calloutStartY);c.lineTo(e.attr.calloutEndX,e.attr.calloutEndY);c.stroke();c.beginPath();c.arc(e.attr.calloutStartX,e.attr.calloutStartY,1*a.calloutWidth,0,2*Math.PI,true);c.fill();c.beginPath();c.arc(e.attr.calloutEndX,e.attr.calloutEndY,1*a.calloutWidth,0,2*Math.PI,true);c.fill()}c.restore();Ext.draw.sprite.Text.prototype.render.apply(e,arguments)}});Ext.define("Ext.chart.series.Series",{requires:["Ext.chart.Markers","Ext.chart.label.Label","Ext.tip.ToolTip"],mixins:["Ext.mixin.Observable","Ext.mixin.Bindable"],isSeries:true,defaultBindProperty:"store",type:null,seriesType:"sprite",identifiablePrefix:"ext-line-",observableType:"series",darkerStrokeRatio:0.15,config:{chart:null,title:null,renderer:null,showInLegend:true,triggerAfterDraw:false,style:{},subStyle:{},themeStyle:{},colors:null,useDarkerStrokeColor:true,store:null,label:{},labelOverflowPadding:null,showMarkers:true,marker:null,markerSubStyle:null,itemInstancing:null,background:null,highlightItem:null,surface:null,overlaySurface:null,hidden:false,highlight:false,highlightCfg:{merge:function(a){return a},$value:{fillStyle:"yellow",strokeStyle:"red"}},animation:null,tooltip:null},directions:[],sprites:null,themeColorCount:function(){return 1},isStoreDependantColorCount:false,themeMarkerCount:function(){return 0},getFields:function(f){var e=this,a=[],c,b,d;for(b=0,d=f.length;b0){if(!Ext.isBoolean(h)||!h){for(d=0;da){a=f}}b.min=d;b.max=a},updateLabelData:function(){var h=this,l=h.getStore(),g=l.getData().items,f=h.getSprites(),a=h.getLabel().getTemplate(),n=Ext.Array.from(a.getField()),c,b,e,d,m,k;if(!f.length||!n.length){return}for(c=0;c=0){return d}}}}}},onChartDetached:function(a){var b=this;b.fireEvent("chartdetached",a,b);a.un("storechange","onStoreChange",b)},onChartAttached:function(a){var b=this;b.setBackground(b.getBackground());b.fireEvent("chartattached",a,b);a.on("storechange","onStoreChange",b);b.processData()},updateOverlaySurface:function(a){var b=this;if(a){if(b.getLabel()){b.getOverlaySurface().add(b.getLabel())}}},applyLabel:function(a,b){if(!b){b=new Ext.chart.Markers({zIndex:10});b.setTemplate(new Ext.chart.label.Label(a))}else{b.getTemplate().setAttributes(a)}return b},createItemInstancingSprite:function(c,b){var e=this,f=new Ext.chart.Markers(),a,d;f.setAttributes({zIndex:Number.MAX_VALUE});a=Ext.apply({},b);if(e.getHighlight()){a.highlight=e.getHighlight();a.modifiers=["highlight"]}f.setTemplate(a);d=f.getTemplate();d.setAttributes(e.getStyle());d.fx.on("animationstart","onSpriteAnimationStart",this);d.fx.on("animationend","onSpriteAnimationEnd",this);c.bindMarker("items",f);e.getSurface().add(f);return f},getDefaultSpriteConfig:function(){return{type:this.seriesType,renderer:this.getRenderer()}},updateRenderer:function(c){var b=this,a=b.getChart(),d;if(a&&a.isInitializing){return}d=b.getSprites();if(d.length){d[0].setAttributes({renderer:c||null});if(a&&!a.isInitializing){a.redraw()}}},updateShowMarkers:function(a){var d=this.getSprites(),b=d&&d[0],c=b&&b.getMarker("markers");if(c){c.getTemplate().setAttributes({hidden:!a})}},createSprite:function(){var f=this,a=f.getSurface(),e=f.getItemInstancing(),d=a.add(f.getDefaultSpriteConfig()),b=f.getMarker(),g,c;d.setAttributes(f.getStyle());d.setSeries(f);if(e){d.itemsMarker=f.createItemInstancingSprite(d,e)}if(d.bindMarker){if(b){g=new Ext.chart.Markers();c=Ext.Object.merge({},b);if(f.getHighlight()){c.highlight=f.getHighlight();c.modifiers=["highlight"]}g.setTemplate(c);g.getTemplate().fx.setCustomDurations({translationX:0,translationY:0});d.dataMarker=g;d.bindMarker("markers",g);f.getOverlaySurface().add(g)}if(f.getLabel().getTemplate().getField()){d.bindMarker("labels",f.getLabel())}}if(d.setStore){d.setStore(f.getStore())}d.fx.on("animationstart","onSpriteAnimationStart",f);d.fx.on("animationend","onSpriteAnimationEnd",f);f.sprites.push(d);return d},getSprites:Ext.emptyFn,onDataChanged:function(){var d=this,c=d.getChart(),b=c&&c.getStore(),a=d.getStore();if(a!==b){d.processData()}},isXType:function(a){return a==="series"},getItemId:function(){return this.getId()},applyThemeStyle:function(e,a){var b=this,d,c;d=e&&e.subStyle&&e.subStyle.fillStyle;c=d&&e.subStyle.strokeStyle;if(d&&!c){e.subStyle.strokeStyle=b.getStrokeColorsFromFillColors(d)}d=e&&e.markerSubStyle&&e.markerSubStyle.fillStyle;c=d&&e.markerSubStyle.strokeStyle;if(d&&!c){e.markerSubStyle.strokeStyle=b.getStrokeColorsFromFillColors(d)}return Ext.apply(a||{},e)},applyStyle:function(c,b){var a=Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite."+this.seriesType));if(a&&a.def){c=a.def.normalize(c)}return Ext.apply({},c,b)},applySubStyle:function(b,c){var a=Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite."+this.seriesType));if(a&&a.def){b=a.def.batchedNormalize(b,true)}return Ext.merge({},c,b)},applyMarker:function(c,a){var d=(c&&c.type)||(a&&a.type)||"circle",b=Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite."+d));if(b&&b.def){c=b.def.normalize(Ext.isObject(c)?c:{},true);c.type=d}return Ext.merge(a||{},c)},applyMarkerSubStyle:function(c,a){var d=(c&&c.type)||(a&&a.type)||"circle",b=Ext.ClassManager.get(Ext.ClassManager.getNameByAlias("sprite."+d));if(b&&b.def){c=b.def.batchedNormalize(c,true)}return Ext.merge(a||{},c)},updateHidden:function(b){var a=this;a.getColors();a.getSubStyle();a.setSubStyle({hidden:b});a.processData();a.doUpdateStyles();if(!Ext.isArray(b)){a.updateLegendStore(b)}},updateLegendStore:function(f,b){var e=this,d=e.getChart(),c=d.getLegendStore(),g=e.getId(),a;if(c){if(arguments.length>1){a=c.findBy(function(h){return h.get("series")===g&&h.get("index")===b});if(a!==-1){a=c.getAt(a)}}else{a=c.findRecord("series",g)}if(a&&a.get("disabled")!==f){a.set("disabled",f)}}},setHiddenByIndex:function(a,c){var b=this;if(Ext.isArray(b.getHidden())){b.getHidden()[a]=c;b.updateHidden(b.getHidden());b.updateLegendStore(c,a)}else{b.setHidden(c)}},getStrokeColorsFromFillColors:function(a){var c=this,e=c.getUseDarkerStrokeColor(),b=(Ext.isNumber(e)?e:c.darkerStrokeRatio),d;if(e){d=Ext.Array.map(a,function(f){f=Ext.isString(f)?f:f.stops[0].color;f=Ext.draw.Color.fromString(f);return f.createDarker(b).toString()})}else{d=Ext.Array.clone(a)}return d},updateThemeColors:function(b){var c=this,d=c.getThemeStyle(),a=Ext.Array.clone(b),f=c.getStrokeColorsFromFillColors(b),e={fillStyle:a,strokeStyle:f};d.subStyle=Ext.apply(d.subStyle||{},e);d.markerSubStyle=Ext.apply(d.markerSubStyle||{},e);c.doUpdateStyles()},themeOnlyIfConfigured:{},updateTheme:function(d){var h=this,a=d.getSeries(),n=h.getInitialConfig(),c=h.defaultConfig,f=h.getConfigurator().configs,j=a.defaults,k=a[h.type],g=h.themeOnlyIfConfigured,l,i,o,b,m,e;a=Ext.merge({},j,k);for(l in a){i=a[l];e=f[l];if(i!==null&&i!==undefined&&e){m=n[l];o=Ext.isObject(i);b=m===c[l];if(o){if(b&&g[l]){continue}i=Ext.merge({},i,m)}if(b||o){h[e.names.set](i)}}}},updateChartColors:function(a){var b=this;if(!b.getColors()){b.updateThemeColors(a)}},updateColors:function(a){this.updateThemeColors(a)},updateStyle:function(){this.doUpdateStyles()},updateSubStyle:function(){this.doUpdateStyles()},updateThemeStyle:function(){this.doUpdateStyles()},doUpdateStyles:function(){var g=this,h=g.sprites,d=g.getItemInstancing(),c=0,f=h&&h.length,a=g.getConfig("showMarkers",true),b=g.getMarker(),e;for(;ce.to){j.call(this,e.max,e.getLabel(e.max),e.steps+1,e)}}else{b=this.getAxis();h=b.floatingAxes;d=[];f=(e.to-e.from)/(e.steps+1);if(b.getFloating()){for(a in h){d.push(h[a])}}function l(i){return !d.length||k(d,function(n){return m(n-i)>f})}if(e.mine.to&&l(e.max)){j.call(this,e.max,e.max,e.steps+1,e)}}},renderTicks:function(l,m,s,p){var v=this,k=v.attr,u=k.position,n=k.matrix,e=0.5*k.lineWidth,f=n.getXX(),i=n.getDX(),j=n.getYY(),h=n.getDY(),o=s.majorTicks,d=k.majorTickSize,a=s.minorTicks,r=k.minorTickSize;if(o){switch(u){case"right":function q(w){return function(x,z,y){x=l.roundPixel(x*j+h)+e;m.moveTo(0,x);m.lineTo(w,x)}}v.iterate(o,q(d));a&&v.iterate(a,q(r));break;case"left":function t(w){return function(x,z,y){x=l.roundPixel(x*j+h)+e;m.moveTo(p[2]-w,x);m.lineTo(p[2],x)}}v.iterate(o,t(d));a&&v.iterate(a,t(r));break;case"bottom":function c(w){return function(x,z,y){x=l.roundPixel(x*f+i)-e;m.moveTo(x,0);m.lineTo(x,w)}}v.iterate(o,c(d));a&&v.iterate(a,c(r));break;case"top":function b(w){return function(x,z,y){x=l.roundPixel(x*f+i)-e;m.moveTo(x,p[3]);m.lineTo(x,p[3]-w)}}v.iterate(o,b(d));a&&v.iterate(a,b(r));break;case"angular":v.iterate(o,function(w,y,x){w=w/(k.max+1)*Math.PI*2+k.baseRotation;m.moveTo(k.centerX+(k.length)*Math.cos(w),k.centerY+(k.length)*Math.sin(w));m.lineTo(k.centerX+(k.length+d)*Math.cos(w),k.centerY+(k.length+d)*Math.sin(w))});break;case"gauge":var g=v.getGaugeAngles();v.iterate(o,function(w,y,x){w=(w-k.min)/(k.max-k.min+1)*k.totalAngle-k.totalAngle+g.start;m.moveTo(k.centerX+(k.length)*Math.cos(w),k.centerY+(k.length)*Math.sin(w));m.lineTo(k.centerX+(k.length+d)*Math.cos(w),k.centerY+(k.length+d)*Math.sin(w))});break}}},renderLabels:function(E,q,D,K){var o=this,k=o.attr,i=0.5*k.lineWidth,u=k.position,y=k.matrix,A=k.textPadding,x=y.getXX(),d=y.getDX(),g=y.getYY(),c=y.getDY(),n=0,I=D.majorTicks,G=Math.max(k.majorTickSize,k.minorTickSize)+k.lineWidth,f=Ext.draw.Draw.isBBoxIntersect,F=o.getLabel(),J,s,r=null,w=0,b=0,m=D.segmenter,B=o.getRenderer(),t=o.getAxis(),z=t.getTitle(),a=z&&z.attr.text!==""&&z.getBBox(),l,h=null,p,C,v,e,H;if(I&&F&&!F.attr.hidden){J=F.attr.font;if(q.font!==J){q.font=J}F.setAttributes({translationX:0,translationY:0},true);F.applyTransformations();l=F.attr.inverseMatrix.elements.slice(0);switch(u){case"left":e=a?a.x+a.width:0;switch(F.attr.textAlign){case"start":H=E.roundPixel(e+d)-i;break;case"end":H=E.roundPixel(K[2]-G+d)-i;break;default:H=E.roundPixel(e+(K[2]-e-G)/2+d)-i}F.setAttributes({translationX:H},true);break;case"right":e=a?K[2]-a.x:0;switch(F.attr.textAlign){case"start":H=E.roundPixel(G+d)+i;break;case"end":H=E.roundPixel(K[2]-e+d)+i;break;default:H=E.roundPixel(G+(K[2]-G-e)/2+d)+i}F.setAttributes({translationX:H},true);break;case"top":e=a?a.y+a.height:0;F.setAttributes({translationY:E.roundPixel(e+(K[3]-e-G)/2)-i},true);break;case"bottom":e=a?K[3]-a.y:0;F.setAttributes({translationY:E.roundPixel(G+(K[3]-G-e)/2)+i},true);break;case"radial":F.setAttributes({translationX:k.centerX},true);break;case"angular":F.setAttributes({translationY:k.centerY},true);break;case"gauge":F.setAttributes({translationY:k.centerY},true);break}if(u==="left"||u==="right"){o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;F.setAttributes({text:String(v),translationY:E.roundPixel(L*g+c)},true);F.applyTransformations();n=Math.max(n,F.getBBox().width+G);if(n<=o.thickness){C=Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0));p=C.prepend.apply(C,l).transformBBox(F.getBBox(true));if(h&&!f(p,h,A)){return}E.renderSprite(F);h=p;w+=p.height;b++}})}else{if(u==="top"||u==="bottom"){o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;F.setAttributes({text:String(v),translationX:E.roundPixel(L*x+d)},true);F.applyTransformations();n=Math.max(n,F.getBBox().height+G);if(n<=o.thickness){C=Ext.draw.Matrix.fly(F.attr.matrix.elements.slice(0));p=C.prepend.apply(C,l).transformBBox(F.getBBox(true));if(h&&!f(p,h,A)){return}E.renderSprite(F);h=p;w+=p.width;b++}})}else{if(u==="radial"){o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;if(typeof v!=="undefined"){F.setAttributes({text:String(v),translationX:k.centerX-E.roundPixel(L)/k.max*k.length*Math.cos(k.baseRotation+Math.PI/2),translationY:k.centerY-E.roundPixel(L)/k.max*k.length*Math.sin(k.baseRotation+Math.PI/2)},true);F.applyTransformations();p=F.attr.matrix.transformBBox(F.getBBox(true));if(h&&!f(p,h)){return}E.renderSprite(F);h=p;w+=p.width;b++}})}else{if(u==="angular"){s=k.majorTickSize+k.lineWidth*0.5+(parseInt(F.attr.fontSize,10)||10)/2;o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;n=Math.max(n,Math.max(k.majorTickSize,k.minorTickSize)+(k.lineCap!=="butt"?k.lineWidth*0.5:0));if(typeof v!=="undefined"){var O=L/(k.max+1)*Math.PI*2+k.baseRotation;F.setAttributes({text:String(v),translationX:k.centerX+(k.length+s)*Math.cos(O),translationY:k.centerY+(k.length+s)*Math.sin(O)},true);F.applyTransformations();p=F.attr.matrix.transformBBox(F.getBBox(true));if(h&&!f(p,h)){return}E.renderSprite(F);h=p;w+=p.width;b++}})}else{if(u==="gauge"){var j=o.getGaugeAngles();o.iterate(I,function(L,N,M){if(N===undefined){return}if(B){v=Ext.callback(B,null,[t,N,D,r],0,t)}else{v=m.renderer(N,D,r)}r=N;if(typeof v!=="undefined"){var O=(L-k.min)/(k.max-k.min+1)*k.totalAngle-k.totalAngle+j.start;F.setAttributes({text:String(v),translationX:k.centerX+(k.length+10)*Math.cos(O),translationY:k.centerY+(k.length+10)*Math.sin(O)},true);F.applyTransformations();p=F.attr.matrix.transformBBox(F.getBBox(true));if(h&&!f(p,h)){return}E.renderSprite(F);h=p;w+=p.width;b++}})}}}}}if(k.enlargeEstStepSizeByText&&b){w/=b;w+=G;w*=2;if(k.estStepSize1){o.thickness=n;k.bbox.plain.dirty=true;k.bbox.transform.dirty=true;o.doThicknessChanged();return false}}},renderAxisLine:function(a,i,e,c){var h=this,g=h.attr,b=g.lineWidth*0.5,j=g.position,d,f;if(g.axisLine&&g.length){switch(j){case"left":d=a.roundPixel(c[2])-b;i.moveTo(d,-g.endGap);i.lineTo(d,g.length+g.startGap+1);break;case"right":i.moveTo(b,-g.endGap);i.lineTo(b,g.length+g.startGap+1);break;case"bottom":i.moveTo(-g.startGap,b);i.lineTo(g.length+g.endGap,b);break;case"top":d=a.roundPixel(c[3])-b;i.moveTo(-g.startGap,d);i.lineTo(g.length+g.endGap,d);break;case"angular":i.moveTo(g.centerX+g.length,g.centerY);i.arc(g.centerX,g.centerY,g.length,0,Math.PI*2,true);break;case"gauge":f=h.getGaugeAngles();i.moveTo(g.centerX+Math.cos(f.start)*g.length,g.centerY+Math.sin(f.start)*g.length);i.arc(g.centerX,g.centerY,g.length,f.start,f.end,true);break}}},getGaugeAngles:function(){var a=this,c=a.attr.totalAngle,b;if(c<=Math.PI){b=(Math.PI-c)*0.5}else{b=-(Math.PI*2-c)*0.5}b=Math.PI*2-b;return{start:b,end:b-c}},renderGridLines:function(m,n,s,r){var t=this,b=t.getAxis(),l=t.attr,p=l.matrix,d=l.startGap,a=l.endGap,c=p.getXX(),k=p.getYY(),h=p.getDX(),g=p.getDY(),u=l.position,f=b.getGridAlignment(),q=s.majorTicks,e,o,i;if(l.grid){if(q){if(u==="left"||u==="right"){i=l.min*k+g+a+d;t.iterate(q,function(j,w,v){e=j*k+g+a;t.putMarker(f+"-"+(v%2?"odd":"even"),{y:e,height:i-e},o=v,true);i=e});o++;e=0;t.putMarker(f+"-"+(o%2?"odd":"even"),{y:e,height:i-e},o,true)}else{if(u==="top"||u==="bottom"){i=l.min*c+h+d;if(d){t.putMarker(f+"-even",{x:0,width:i},-1,true)}t.iterate(q,function(j,w,v){e=j*c+h+d;t.putMarker(f+"-"+(v%2?"odd":"even"),{x:e,width:i-e},o=v,true);i=e});o++;e=l.length+l.startGap+l.endGap;t.putMarker(f+"-"+(o%2?"odd":"even"),{x:e,width:i-e},o,true)}else{if(u==="radial"){t.iterate(q,function(j,w,v){if(!j){return}e=j/l.max*l.length;t.putMarker(f+"-"+(v%2?"odd":"even"),{scalingX:e,scalingY:e},v,true);i=e})}else{if(u==="angular"){t.iterate(q,function(j,w,v){if(!l.length){return}e=j/(l.max+1)*Math.PI*2+l.baseRotation;t.putMarker(f+"-"+(v%2?"odd":"even"),{rotationRads:e,rotationCenterX:0,rotationCenterY:0,scalingX:l.length,scalingY:l.length},v,true);i=e})}}}}}}},renderLimits:function(o){var t=this,a=t.getAxis(),h=a.getChart(),p=h.getInnerPadding(),d=Ext.Array.from(a.getLimits());if(!d.length){return}var r=a.limits.surface.getRect(),m=t.attr,n=m.matrix,u=m.position,k=Ext.Object.chain,v=a.limits.titles,c,j,b,s,l,q,f,g,e;v.instances=[];v.position=0;if(u==="left"||u==="right"){for(q=0,f=d.length;qm.max){continue}l=l/m.max*m.length;s.line.cx=m.centerX;s.line.cy=m.centerY;s.line.scalingX=l;s.line.scalingY=l;s.line.strokeStyle=s.line.strokeStyle||m.strokeStyle;t.putMarker("circular-limit-lines",s.line,q,true);if(s.line.title){v.createInstance(s.line.title);c=v.getBBoxFor(v.position-1);v.setAttributesFor(v.position-1,{x:m.centerX,y:m.centerY-l-c.height/2,fillStyle:s.line.title.fillStyle||s.line.strokeStyle})}}}else{if(u==="angular"){for(q=0,f=d.length;q-0.5*Math.PI&&l<0.5*Math.PI)||(l>1.5*Math.PI&&l<2*Math.PI))?1:-1;v.setAttributesFor(v.position-1,{x:m.centerX+0.5*m.length*Math.cos(l)+b*c.height/2*Math.sin(l),y:m.centerY+0.5*m.length*Math.sin(l)-b*c.height/2*Math.cos(l),rotationRads:b===1?l:l-Math.PI,fillStyle:s.line.title.fillStyle||s.line.strokeStyle})}}}else{if(u==="gauge"){}}}}}},doThicknessChanged:function(){var a=this.getAxis();if(a){a.onThicknessChanged()}},render:function(a,c,d){var e=this,b=e.getLayoutContext();if(b){if(false===e.renderLabels(a,c,b,d)){return false}c.beginPath();e.renderTicks(a,c,b,d);e.renderAxisLine(a,c,b,d);e.renderGridLines(a,c,b,d);e.renderLimits(d);c.stroke()}}});Ext.define("Ext.chart.axis.segmenter.Segmenter",{config:{axis:null},constructor:function(a){this.initConfig(a)},renderer:function(b,a){return String(b)},from:function(a){return a},diff:Ext.emptyFn,align:Ext.emptyFn,add:Ext.emptyFn,preferredStep:Ext.emptyFn});Ext.define("Ext.chart.axis.segmenter.Names",{extend:"Ext.chart.axis.segmenter.Segmenter",alias:"segmenter.names",renderer:function(b,a){return b},diff:function(b,a,c){return Math.floor(a-b)},align:function(c,b,a){return Math.floor(c)},add:function(c,b,a){return c+b},preferredStep:function(c,a,b,d){return{unit:1,step:1}}});Ext.define("Ext.chart.axis.segmenter.Numeric",{extend:"Ext.chart.axis.segmenter.Segmenter",alias:"segmenter.numeric",isNumeric:true,renderer:function(b,a){return b.toFixed(Math.max(0,a.majorTicks.unit.fixes))},diff:function(b,a,c){return Math.floor((a-b)/c.scale)},align:function(c,b,a){return Math.floor(c/(a.scale*b))*a.scale*b},add:function(c,b,a){return c+b*a.scale},preferredStep:function(c,b){var a=Math.floor(Math.log(b)*Math.LOG10E),d=Math.pow(10,a);b/=d;if(b<2){b=2}else{if(b<5){b=5}else{if(b<10){b=10;a++}}}return{unit:{fixes:-a,scale:d},step:b}},exactStep:function(c,b){var a=Math.floor(Math.log(b)*Math.LOG10E),d=Math.pow(10,a);return{unit:{fixes:-a+(b%d===0?0:1),scale:1},step:b}},adjustByMajorUnit:function(e,g,c){var d=c[0],b=c[1],a=e*g,f=d%a;if(f!==0){c[0]=d-f+(d<0?-a:0)}f=b%a;if(f!==0){c[1]=b-f+(b>0?a:0)}}});Ext.define("Ext.chart.axis.segmenter.Time",{extend:"Ext.chart.axis.segmenter.Segmenter",alias:"segmenter.time",config:{step:null},renderer:function(c,b){var a=Ext.Date;switch(b.majorTicks.unit){case"y":return a.format(c,"Y");case"mo":return a.format(c,"Y-m");case"d":return a.format(c,"Y-m-d")}return a.format(c,"Y-m-d\nH:i:s")},from:function(a){return new Date(a)},diff:function(b,a,c){if(isFinite(b)){b=new Date(b)}if(isFinite(a)){a=new Date(a)}return Ext.Date.diff(b,a,c)},align:function(a,c,b){if(b==="d"&&c>=7){a=Ext.Date.align(a,"d",c);a.setDate(a.getDate()-a.getDay()+1);return a}else{return Ext.Date.align(a,b,c)}},add:function(c,b,a){return Ext.Date.add(new Date(c),a,b)},stepUnits:[[Ext.Date.YEAR,1,2,5,10,20,50,100,200,500],[Ext.Date.MONTH,1,3,6],[Ext.Date.DAY,1,7,14],[Ext.Date.HOUR,1,6,12],[Ext.Date.MINUTE,1,5,15,30],[Ext.Date.SECOND,1,5,15,30],[Ext.Date.MILLI,1,2,5,10,20,50,100,200,500]],preferredStep:function(b,e){if(this.getStep()){return this.getStep()}var f=new Date(+b),g=new Date(+b+Math.ceil(e)),d=this.stepUnits,l,k,h,c,a;for(c=0;c0){for(a=1;aa){f.max=f.to}if(f.froma){f.max=f.to}if(f.from0){f.from=f.from+c*f.step*i;while(f.from0?b:a},applyLabel:function(b,a){if(!a){a=new Ext.draw.sprite.Text({})}if(this.limitTitleTpl){this.limitTitleTpl.setAttributes(b)}a.setAttributes(b);return a},applyLayout:function(b,a){b=Ext.factory(b,null,a,"axisLayout");b.setAxis(this);return b},applySegmenter:function(a,b){a=Ext.factory(a,null,b,"segmenter");a.setAxis(this);return a},updateMinimum:function(){this.range=null},updateMaximum:function(){this.range=null},hideLabels:function(){this.getSprites()[0].setDirty(true);this.setLabel({hidden:true})},showLabels:function(){this.getSprites()[0].setDirty(true);this.setLabel({hidden:false})},renderFrame:function(){this.getSurface().renderFrame()},updateChart:function(d,b){var c=this,a;if(b){b.unregister(c);b.un("serieschange",c.onSeriesChange,c);b.un("redraw",c.renderLimits,c);c.linkAxis();c.fireEvent("chartdetached",b,c)}if(d){d.on("serieschange",c.onSeriesChange,c);c.surface=null;a=c.getSurface();c.getLabel().setSurface(a);a.add(c.getSprites());a.add(c.getTitle());d.register(c);c.fireEvent("chartattached",d,c)}},applyBackground:function(a){var b=Ext.ClassManager.getByAlias("sprite.rect");return b.def.normalize(a)},processData:function(){this.getLayout().processData();this.range=null},getDirection:function(){return this.getChart().getDirectionForAxis(this.getPosition())},isSide:function(){var a=this.getPosition();return a==="left"||a==="right"},applyFields:function(a){return Ext.Array.from(a)},applyVisibleRange:function(a,c){this.getChart();if(a[0]>a[1]){var b=a[0];a[0]=a[1];a[0]=b}if(a[1]===a[0]){a[1]+=1/this.getMaxZoom()}if(a[1]>a[0]+1){a[0]=0;a[1]=1}else{if(a[0]<0){a[1]-=a[0];a[0]=0}else{if(a[1]>1){a[0]-=a[1]-1;a[1]=1}}}if(c&&a[0]===c[0]&&a[1]===c[1]){return undefined}return a},updateVisibleRange:function(a){this.fireEvent("visiblerangechange",this,a)},onSeriesChange:function(e){var f=this,b=e.getSeries(),j="get"+f.getDirection()+"Axis",g=[],c,d=b.length,a,h;for(c=0;cn){n=c[1]}}}if(!isFinite(n)){n=m.prevMax}if(!isFinite(d)){d=m.prevMin}if(m.getLabelInSpan()||d===n){n+=m.getIncrement();d-=m.getIncrement()}if(Ext.isNumber(m.getMinimum())){d=m.getMinimum()}else{m.prevMin=d}if(Ext.isNumber(m.getMaximum())){n=m.getMaximum()}else{m.prevMax=n}m.range=[Ext.Number.correctFloat(d),Ext.Number.correctFloat(n)];if(m.getReconcileRange()){m.reconcileRange()}if(m.getAdjustByMajorUnit()&&l.adjustByMajorUnit&&!m.getMajorTickSteps()){j=Ext.Object.chain(m.getSprites()[0].attr);j.min=m.range[0];j.max=m.range[1];j.visibleMin=p[0];j.visibleMax=p[1];a={attr:j,segmenter:l};h.calculateLayout(a);g=a.majorTicks;if(g){l.adjustByMajorUnit(g.step,g.unit.scale,m.range);j.min=m.range[0];j.max=m.range[1];delete a.majorTicks;h.calculateLayout(a);g=a.majorTicks;l.adjustByMajorUnit(g.step,g.unit.scale,m.range)}else{if(!m.hasClearRangePending){m.hasClearRangePending=true;m.getChart().on("layout","clearRange",m)}}}if(!Ext.Array.equals(m.range,m.oldRange||[])){m.fireEvent("rangechange",m,m.range);m.oldRange=m.range}return m.range},clearRange:function(){delete this.hasClearRangePending;this.range=null},reconcileRange:function(){var e=this,g=e.getChart().getAxes(),f=e.getDirection(),b,d,c,a;if(!g){return}for(b=0,d=g.length;be.range[1]){e.range[1]=a[1]}}},applyStyle:function(c,b){var a=Ext.ClassManager.getByAlias("sprite."+this.seriesType);if(a&&a.def){c=a.def.normalize(c)}b=Ext.apply(b||{},c);return b},themeOnlyIfConfigured:{grid:true},updateTheme:function(d){var i=this,k=d.getAxis(),e=i.getPosition(),o=i.getInitialConfig(),c=i.defaultConfig,g=i.getConfigurator().configs,a=k.defaults,n=k[e],h=i.themeOnlyIfConfigured,l,j,p,b,m,f;k=Ext.merge({},a,n);for(l in k){j=k[l];f=g[l];if(j!==null&&j!==undefined&&f){m=o[l];p=Ext.isObject(j);b=m===c[l];if(p){if(b&&h[l]){continue}j=Ext.merge({},j,m)}if(b||p){i[f.names.set](j)}}}},updateCenter:function(b){var e=this.getSprites(),a=e[0],d=b[0],c=b[1];if(a){a.setAttributes({centerX:d,centerY:c})}if(this.gridSpriteEven){this.gridSpriteEven.getTemplate().setAttributes({translationX:d,translationY:c,rotationCenterX:d,rotationCenterY:c})}if(this.gridSpriteOdd){this.gridSpriteOdd.getTemplate().setAttributes({translationX:d,translationY:c,rotationCenterX:d,rotationCenterY:c})}},getSprites:function(){if(!this.getChart()){return}var i=this,e=i.getRange(),f=i.getPosition(),g=i.getChart(),c=g.getAnimation(),d,a,b=i.getLength(),h=i.superclass;if(c===false){c={duration:0}}if(e){a=Ext.applyIf({position:f,axis:i,min:e[0],max:e[1],length:b,grid:i.getGrid(),hidden:i.getHidden(),titleOffset:i.titleOffset,layout:i.getLayout(),segmenter:i.getSegmenter(),totalAngle:i.getTotalAngle(),label:i.getLabel()},i.getStyle());if(!i.sprites.length){while(!h.xtype){h=h.superclass}d=Ext.create("sprite."+h.xtype,a);d.fx.setCustomDurations({baseRotation:0});d.fx.on("animationstart","onAnimationStart",i);d.fx.on("animationend","onAnimationEnd",i);d.setLayout(i.getLayout());d.setSegmenter(i.getSegmenter());d.setLabel(i.getLabel());i.sprites.push(d);i.updateTitleSprite()}else{d=i.sprites[0];d.setAnimation(c);d.setAttributes(a)}if(i.getRenderer()){d.setRenderer(i.getRenderer())}}return i.sprites},updateTitleSprite:function(){var f=this,b=f.getLength();if(!f.sprites[0]||!Ext.isNumber(b)){return}var h=this.sprites[0].thickness,a=f.getSurface(),g=f.getTitle(),e=f.getPosition(),c=f.getMargin(),i=f.getTitleMargin(),d=a.roundPixel(b/2);if(g){switch(e){case"top":g.setAttributes({x:d,y:c+i/2,textBaseline:"top",textAlign:"center"},true);g.applyTransformations();f.titleOffset=g.getBBox().height+i;break;case"bottom":g.setAttributes({x:d,y:h+i/2,textBaseline:"top",textAlign:"center"},true);g.applyTransformations();f.titleOffset=g.getBBox().height+i;break;case"left":g.setAttributes({x:c+i/2,y:d,textBaseline:"top",textAlign:"center",rotationCenterX:c+i/2,rotationCenterY:d,rotationRads:-Math.PI/2},true);g.applyTransformations();f.titleOffset=g.getBBox().width+i;break;case"right":g.setAttributes({x:h-c+i/2,y:d,textBaseline:"bottom",textAlign:"center",rotationCenterX:h+i/2,rotationCenterY:d,rotationRads:Math.PI/2},true);g.applyTransformations();f.titleOffset=g.getBBox().width+i;break}}},onThicknessChanged:function(){this.getChart().onThicknessChanged()},getThickness:function(){if(this.getHidden()){return 0}return(this.sprites[0]&&this.sprites[0].thickness||1)+this.titleOffset+this.getMargin()},onAnimationStart:function(){this.spriteAnimationCount++;if(this.spriteAnimationCount===1){this.fireEvent("animationstart",this)}},onAnimationEnd:function(){this.spriteAnimationCount--;if(this.spriteAnimationCount===0){this.fireEvent("animationend",this)}},getItemId:function(){return this.getId()},getAncestorIds:function(){return[this.getChart().getId()]},isXType:function(a){return a==="axis"},resolveListenerScope:function(e){var d=this,a=Ext._namedScopes[e],c=d.getChart(),b;if(!a){b=c?c.resolveListenerScope(e,false):(e||d)}else{if(a.isThis){b=d}else{if(a.isController){b=c?c.resolveListenerScope(e,false):d}else{if(a.isSelf){b=c?c.resolveListenerScope(e,false):d;if(b===c&&!c.getInheritedConfig("defaultListenerScope")){b=d}}}}}return b},destroy:function(){var a=this;a.setChart(null);a.surface.destroy();a.surface=null;a.callParent()}});Ext.define("Ext.chart.LegendBase",{extend:"Ext.view.View",config:{tpl:['
','','
',"',"{name}","
","
","
"],nodeContainerSelector:"div."+Ext.baseCSSPrefix+"legend-container",itemSelector:"div."+Ext.baseCSSPrefix+"legend-item",docked:"bottom"},setDocked:function(d){var c=this,a=c.ownerCt,b;c.docked=d;switch(d){case"top":case"bottom":c.addCls(Ext.baseCSSPrefix+"horizontal");b="hbox";break;case"left":case"right":c.removeCls(Ext.baseCSSPrefix+"horizontal");b="vbox";break}if(a){a.setDocked(d)}},setStore:function(a){this.bindStore(a)},clearViewEl:function(){this.callParent(arguments);Ext.removeNode(this.getNodeContainer())},onItemClick:function(a,c,b,d){this.callParent(arguments);this.toggleItem(b)}});Ext.define("Ext.chart.Legend",{xtype:"legend",extend:"Ext.chart.LegendBase",config:{baseCls:Ext.baseCSSPrefix+"legend",padding:5,rect:null,disableSelection:true,toggleable:true},toggleItem:function(c){if(!this.getToggleable()){return}var b=this.getStore(),h=0,e,g=true,d,f,a;if(b){f=b.getCount();for(d=0;d1;a=b.getAt(c);if(a){e=a.get("disabled");if(e||g){a.set("disabled",!e)}}}}});Ext.define("Ext.chart.AbstractChart",{extend:"Ext.draw.Container",requires:["Ext.chart.theme.Default","Ext.chart.series.Series","Ext.chart.interactions.Abstract","Ext.chart.axis.Axis","Ext.data.StoreManager","Ext.chart.Legend","Ext.data.Store"],isChart:true,defaultBindProperty:"store",config:{store:"ext-empty-store",theme:"default",style:null,animation:!Ext.isIE8,series:[],axes:[],legend:null,colors:null,insetPadding:{top:10,left:10,right:10,bottom:10},background:null,interactions:[],mainRect:null,resizeHandler:null,highlightItem:null},animationSuspendCount:0,chartLayoutSuspendCount:0,axisThicknessSuspendCount:0,isThicknessChanged:false,surfaceZIndexes:{background:0,main:1,grid:2,series:3,axis:4,chart:5,overlay:6,events:7},constructor:function(a){var b=this;b.itemListeners={};b.surfaceMap={};b.chartComponents={};b.isInitializing=true;b.suspendChartLayout();b.animationSuspendCount++;b.callParent(arguments);delete b.isInitializing;b.getSurface("main");b.getSurface("chart").setFlipRtlText(b.getInherited().rtl);b.getSurface("overlay").waitFor(b.getSurface("series"));b.animationSuspendCount--;b.resumeChartLayout()},applyAnimation:function(a,b){if(!a){a={duration:0}}else{if(a===true){a={easing:"easeInOut",duration:500}}}return b?Ext.apply({},a,b):a},getAnimation:function(){if(this.animationSuspendCount){return{duration:0}}else{return this.callParent()}},applyInsetPadding:function(b,a){if(!Ext.isObject(b)){return Ext.util.Format.parseBox(b)}else{if(!a){return b}else{return Ext.apply(a,b)}}},suspendAnimation:function(){var d=this,c=d.getSeries(),e=c.length,b=-1,a;d.animationSuspendCount++;if(d.animationSuspendCount===1){while(++b0){this.axisThicknessSuspendCount--;if(this.axisThicknessSuspendCount===0&&this.isThicknessChanged){this.onThicknessChanged()}}},onThicknessChanged:function(){if(this.axisThicknessSuspendCount===0){this.isThicknessChanged=false;this.performLayout()}else{this.isThicknessChanged=true}},applySprites:function(b){var a=this.getSurface("chart");b=Ext.Array.from(b);a.removeAll(true);a.add(b);return b},initItems:function(){var a=this.items,b,d,c;if(a&&!a.isMixedCollection){this.items=[];a=Ext.Array.from(a);for(b=0,d=a.length;b=0){c.splice(b,1)}},applyAxes:function(b,k){var l=this,g={left:"right",right:"left"},m=[],c,d,e,a,f,h,j;l.animationSuspendCount++;l.getStore();if(!k){k=[];k.map={}}j=k.map;m.map={};b=Ext.Array.from(b,true);for(f=0,h=b.length;f0){a=b.applyColors(a)}return a||(c&&c.getColors())},applyColors:function(a){a=Ext.Array.map(a,function(b){if(Ext.isString(b)){return b}else{return b.toString()}});return a},updateColors:function(c){var k=this,e=k.getTheme(),a=c||(e&&e.getColors()),l=0,f=k.getSeries(),d=f&&f.length,g,j,b,h;if(a.length){for(g=0;g=0&&h<=e[2]&&g>=0&&g<=e[3])){return null}for(;b>=0;b--){c=a[b];j=c.getItemForPoint(h,g);if(j){return j}}return null},getItemsForPoint:function(h,g){var f=this,a=f.getSeries(),d=a.length,b=f.hasFirstLayout?d-1:-1,e=[],c,j;for(;b>=0;b--){c=a[b];j=c.getItemForPoint(h,g);if(j){e.push(j)}}return e},onAnimationStart:function(){this.fireEvent("animationstart",this)},onAnimationEnd:function(){this.fireEvent("animationend",this)},onDataChanged:function(){var d=this;if(d.isInitializing){return}var c=d.getMainRect(),a=d.getStore(),b=d.getSeries(),e=d.getAxes();if(!a||!e||!b){return}if(!c){d.on({redraw:d.onDataChanged,scope:d,single:true});return}d.processData();d.redraw()},recordCount:0,processData:function(){var g=this,e=g.getStore().getCount(),c=g.getSeries(),f=c.length,d=false,b=0,a;for(;bg.recordCount){g.updateColors(g.getColors());g.recordCount=e}},bindStore:function(a){this.setStore(a)},applyHighlightItem:function(f,a){if(f===a){return}if(Ext.isObject(f)&&Ext.isObject(a)){var e=f,d=a,c=e.sprite&&(e.sprite[0]||e.sprite),b=d.sprite&&(d.sprite[0]||d.sprite);if(c===b&&e.index===d.index){return}}return f},updateHighlightItem:function(b,a){if(a){a.series.setAttributesForItem(a,{highlighted:false})}if(b){b.series.setAttributesForItem(b,{highlighted:true});this.fireEvent("itemhighlight",this,b,a)}this.fireEvent("itemhighlightchange",this,b,a)},destroyChart:function(){var f=this,d=f.getLegend(),g=f.getAxes(),c=f.getSeries(),h=f.getInteractions(),b=[],a,e;f.surfaceMap=null;for(a=0,e=h.length;ad.getDepth()){g=f}else{for(a=0;ag){g=f}}}}d.setDepth(g)},updateDepth:function(d){var b=this,c=b.getSprites(),a={depth:d};if(c&&c.length){c[0].setAttributes(a)}if(b.gridSpriteEven&&b.gridSpriteOdd){b.gridSpriteEven.getTemplate().setAttributes(a);b.gridSpriteOdd.getTemplate().setAttributes(a)}},getGridAlignment:function(){switch(this.getPosition()){case"left":case"right":return"horizontal3d";case"top":case"bottom":return"vertical3d"}}});Ext.define("Ext.chart.axis.Category",{requires:["Ext.chart.axis.layout.CombineDuplicate","Ext.chart.axis.segmenter.Names"],extend:"Ext.chart.axis.Axis",alias:"axis.category",type:"category",config:{layout:"combineDuplicate",segmenter:"names"}});Ext.define("Ext.chart.axis.Category3D",{requires:["Ext.chart.axis.layout.CombineDuplicate","Ext.chart.axis.segmenter.Names"],extend:"Ext.chart.axis.Axis3D",alias:"axis.category3d",type:"category3d",config:{layout:"combineDuplicate",segmenter:"names"}});Ext.define("Ext.chart.axis.Numeric",{extend:"Ext.chart.axis.Axis",type:"numeric",alias:["axis.numeric","axis.radial"],requires:["Ext.chart.axis.layout.Continuous","Ext.chart.axis.segmenter.Numeric"],config:{layout:"continuous",segmenter:"numeric",aggregator:"double"}});Ext.define("Ext.chart.axis.Numeric3D",{extend:"Ext.chart.axis.Axis3D",alias:["axis.numeric3d"],type:"numeric3d",requires:["Ext.chart.axis.layout.Continuous","Ext.chart.axis.segmenter.Numeric"],config:{layout:"continuous",segmenter:"numeric",aggregator:"double"}});Ext.define("Ext.chart.axis.Time",{extend:"Ext.chart.axis.Numeric",alias:"axis.time",type:"time",requires:["Ext.chart.axis.layout.Continuous","Ext.chart.axis.segmenter.Time"],config:{calculateByLabelSize:true,dateFormat:null,fromDate:null,toDate:null,step:[Ext.Date.DAY,1],layout:"continuous",segmenter:"time",aggregator:"time"},updateDateFormat:function(a){this.setRenderer(function(c,b){return Ext.Date.format(new Date(b),a)})},updateFromDate:function(a){this.setMinimum(+a)},updateToDate:function(a){this.setMaximum(+a)},getCoordFor:function(a){if(Ext.isString(a)){a=new Date(a)}return +a}});Ext.define("Ext.chart.axis.Time3D",{extend:"Ext.chart.axis.Numeric3D",alias:"axis.time3d",type:"time3d",requires:["Ext.chart.axis.layout.Continuous","Ext.chart.axis.segmenter.Time"],config:{calculateByLabelSize:true,dateFormat:null,fromDate:null,toDate:null,step:[Ext.Date.DAY,1],layout:"continuous",segmenter:"time",aggregator:"time"},updateDateFormat:function(a){this.setRenderer(function(c,b){return Ext.Date.format(new Date(b),a)})},updateFromDate:function(a){this.setMinimum(+a)},updateToDate:function(a){this.setMaximum(+a)},getCoordFor:function(a){if(Ext.isString(a)){a=new Date(a)}return +a}});Ext.define("Ext.chart.grid.HorizontalGrid3D",{extend:"Ext.chart.grid.HorizontalGrid",alias:"grid.horizontal3d",inheritableStatics:{def:{processors:{depth:"number"},defaults:{depth:0}}},render:function(a,k,d){var f=this.attr,i=a.roundPixel(f.x),h=a.roundPixel(f.y),l=a.matrix.getDX(),c=k.lineWidth*0.5,j=f.height,e=f.depth,b,g;if(h<=d[1]){return}b=d[0]+e-l;g=h+c-e;k.beginPath();k.rect(b,g,d[2],j);k.fill();k.beginPath();k.moveTo(b,g);k.lineTo(b+d[2],g);k.stroke();b=d[0]+i-l;g=h+c;k.beginPath();k.moveTo(b,g);k.lineTo(b+e,g-e);k.lineTo(b+e,g-e+j);k.lineTo(b,g+j);k.closePath();k.fill();k.beginPath();k.moveTo(b,g);k.lineTo(b+e,g-e);k.stroke()}});Ext.define("Ext.chart.grid.VerticalGrid3D",{extend:"Ext.chart.grid.VerticalGrid",alias:"grid.vertical3d",inheritableStatics:{def:{processors:{depth:"number"},defaults:{depth:0}}},render_:function(c,d,f){var b=this.attr,a=c.roundPixel(b.x),e=d.lineWidth*0.5;d.beginPath();d.rect(a-e,f[1]-c.matrix.getDY(),b.width,f[3]);d.fill();d.beginPath();d.moveTo(a-e,f[1]-c.matrix.getDY());d.lineTo(a-e,f[1]+f[3]-c.matrix.getDY());d.stroke()},render:function(b,j,e){var g=this.attr,i=b.roundPixel(g.x),k=b.matrix.getDY(),d=j.lineWidth*0.5,a=g.width,f=g.depth,c,h;if(i>=e[2]){return}c=i-d+f;h=e[1]-f-k;j.beginPath();j.rect(c,h,a,e[3]);j.fill();j.beginPath();j.moveTo(c,h);j.lineTo(c,h+e[3]);j.stroke();c=i-d;h=e[3];j.beginPath();j.moveTo(c,h);j.lineTo(c+f,h-f);j.lineTo(c+f+a,h-f);j.lineTo(c+a,h);j.closePath();j.fill();c=i-d;h=e[3];j.beginPath();j.moveTo(c,h);j.lineTo(c+f,h-f);j.stroke()}});Ext.define("Ext.chart.interactions.CrossZoom",{extend:"Ext.chart.interactions.Abstract",type:"crosszoom",alias:"interaction.crosszoom",isCrossZoom:true,config:{axes:true,gestures:{dragstart:"onGestureStart",drag:"onGesture",dragend:"onGestureEnd",dblclick:"onDoubleTap"},undoButton:{}},stopAnimationBeforeSync:false,zoomAnimationInProgress:false,constructor:function(){this.callParent(arguments);this.zoomHistory=[]},applyAxes:function(b){var a={};if(b===true){return{top:{},right:{},bottom:{},left:{}}}else{if(Ext.isArray(b)){a={};Ext.each(b,function(c){a[c]={}})}else{if(Ext.isObject(b)){Ext.iterate(b,function(c,d){if(d===true){a[c]={}}else{if(d!==false){a[c]=d}}})}}}return a},applyUndoButton:function(b,a){var c=this;if(a){a.destroy()}if(b){return Ext.create("Ext.Button",Ext.apply({cls:[],text:"Undo Zoom",disabled:true,handler:function(){c.undoZoom()}},b))}},getSurface:function(){return this.getChart()&&this.getChart().getSurface("main")},setSeriesOpacity:function(b){var a=this.getChart()&&this.getChart().getSurface("series");if(a){a.element.setStyle("opacity",b)}},onGestureStart:function(h){var j=this,i=j.getChart(),d=j.getSurface(),l=i.getInnerRect(),c=i.getInnerPadding(),g=c.left,b=g+l[2],f=c.top,a=f+l[3],n=i.getEventXY(h),m=n[0],k=n[1];if(j.zoomAnimationInProgress){return}if(m>g&&mf&&kb){m=b}}if(ka){k=a}}j.selectionRect.setAttributes({width:m-j.startX,height:k-j.startY});if(Math.abs(j.startX-m)<11||Math.abs(j.startY-k)<11){j.selectionRect.setAttributes({globalAlpha:0.5})}else{j.selectionRect.setAttributes({globalAlpha:1})}d.renderFrame();return false}},onGestureEnd:function(i){var l=this;if(l.zoomAnimationInProgress){return}if(l.getLocks()[l.gestureEvent]===l){var k=l.getChart(),d=l.getSurface(),n=k.getInnerRect(),c=k.getInnerPadding(),g=c.left,b=g+n[2],f=c.top,a=f+n[3],h=n[2],j=n[3],p=k.getEventXY(i),o=p[0],m=p[1];if(ob){o=b}}if(ma){m=a}}if(Math.abs(l.startX-o)<11||Math.abs(l.startY-m)<11){d.remove(l.selectionRect)}else{l.zoomBy([Math.min(l.startX,o)/h,1-Math.max(l.startY,m)/j,Math.max(l.startX,o)/h,1-Math.min(l.startY,m)/j]);l.selectionRect.setAttributes({x:Math.min(l.startX,o),y:Math.min(l.startY,m),width:Math.abs(l.startX-o),height:Math.abs(l.startY-m)});l.selectionRect.setAnimation(k.getAnimation()||{duration:0});l.selectionRect.setAttributes({globalAlpha:0,x:0,y:0,width:h,height:j});l.zoomAnimationInProgress=true;k.suspendThicknessChanged();l.selectionRect.fx.on("animationend",function(){k.resumeThicknessChanged();d.remove(l.selectionRect);l.selectionRect=null;l.zoomAnimationInProgress=false})}d.renderFrame();l.sync();l.unlockEvents(l.gestureEvent);l.setSeriesOpacity(1);if(!l.zoomAnimationInProgress){d.remove(l.selectionRect);l.selectionRect=null}}},zoomBy:function(o){var n=this,a=n.getAxes(),k=n.getChart(),j=k.getAxes(),l=k.getInherited().rtl,f,d={},c,b;if(l){o=o.slice();c=1-o[0];b=1-o[2];o[0]=Math.min(c,b);o[2]=Math.max(c,b)}for(var h=0;h0&&D0&&CE){k=E}}if(j<0){j=0}else{if(j>f){j=f}}k+=t;j+=q;for(B=0;B"))},onDragStart:function(d){var c=this,a=c.getChart(),b=a.getHighlightItem();if(b){a.fireEvent("beginitemedit",a,c,c.item=b);return false}},onDrag:function(f){var d=this,b=d.getChart(),c=b.getHighlightItem(),a=c&&c.sprite.type;if(c){switch(a){case"barSeries":return d.onDragBar(f);break;case"scatterSeries":return d.onDragScatter(f);break}}},highlight:function(f){var e=this,d=e.getChart(),a=d.getFlipXY(),g=e.getCursors(),c=f&&f.sprite.type,b=d.el.dom.style;e.callParent([f]);if(f){switch(c){case"barSeries":if(a){b.cursor=g.ewResize}else{b.cursor=g.nsResize}break;case"scatterSeries":b.cursor=g.move;break}}else{d.el.dom.style.cursor="default"}},onDragBar:function(i){var m=this,k=m.getChart(),l=k.getInherited().rtl,f=k.isCartesian&&k.getFlipXY(),q=k.getHighlightItem(),g=q.sprite.getMarker("items"),p=g.getMarkerFor(q.sprite.getId(),q.index),b=q.sprite.getSurface(),c=b.getRect(),r=b.getEventXY(i),o=q.sprite.attr.matrix,j=m.getRenderer(),a,n,d,h;if(f){h=l?c[2]-r[0]:r[0]}else{h=c[3]-r[1]}a={x:p.x,y:h,width:p.width,height:p.height+(p.y-h),radius:p.radius,fillStyle:"none",lineDash:[4,4],zIndex:100};Ext.apply(a,m.getStyle());if(Ext.isArray(q.series.getYField())){h=h-p.y-p.height}m.target={index:q.index,yField:q.field,yValue:(h-o.getDY())/o.getYY()};d=[k,{target:m.target,style:a,item:q}];n=Ext.callback(j,null,d,0,k);if(n){Ext.apply(a,n)}q.sprite.putMarker("items",a,"itemedit");m.showTooltip(i,m.target,q);b.renderFrame()},onDragScatter:function(n){var t=this,g=t.getChart(),d=g.getInherited().rtl,l=g.isCartesian&&g.getFlipXY(),o=g.getHighlightItem(),b=o.sprite.getMarker("items"),p=b.getMarkerFor(o.sprite.getId(),o.index),j=o.sprite.getSurface(),h=j.getRect(),a=j.getEventXY(n),k=o.sprite.attr.matrix,c=o.series.getXAxis(),f=c&&c.getLayout().isContinuous,i=t.getRenderer(),m,u,q,s,r;if(l){r=d?h[2]-a[0]:a[0]}else{r=h[3]-a[1]}if(f){if(l){s=h[3]-a[1]}else{s=a[0]}}else{s=p.translationX}m={translationX:s,translationY:r,scalingX:p.scalingX,scalingY:p.scalingY,r:p.r,fillStyle:"none",lineDash:[4,4],zIndex:100};Ext.apply(m,t.getStyle());t.target={index:o.index,yField:o.field,yValue:(r-k.getDY())/k.getYY()};if(f){Ext.apply(t.target,{xField:o.series.getXField(),xValue:(s-k.getDX())/k.getXX()})}q=[g,{target:t.target,style:m,item:o}];u=Ext.callback(i,null,q,0,g);if(u){Ext.apply(m,u)}o.sprite.putMarker("items",m,"itemedit");t.showTooltip(n,t.target,o);j.renderFrame()},showTooltip:function(g,f,c){var d=this.getTooltip(),a,b;if(d&&Ext.toolkit!=="modern"){a=d.config;b=this.getChart();Ext.callback(a.renderer,null,[d,c,f,g],0,b);d.show([g.x+a.offsetX,g.y+a.offsetY])}},hideTooltip:function(){var a=this.getTooltip();if(a&&Ext.toolkit!=="modern"){a.hide()}},onDragEnd:function(g){var d=this,f=d.target,c=d.getChart(),b=c.getStore(),a;if(f){a=b.getAt(f.index);if(f.yField){a.set(f.yField,f.yValue,{convert:false})}if(f.xField){a.set(f.xField,f.xValue,{convert:false})}if(f.yField||f.xField){d.getChart().onDataChanged()}d.target=null}d.hideTooltip();if(d.item){c.fireEvent("enditemedit",c,d,d.item,f)}d.highlight(d.item=null)},destroy:function(){var a=this.getConfig("tooltip",true);Ext.destroy(a);this.callParent()}});Ext.define("Ext.chart.interactions.PanZoom",{extend:"Ext.chart.interactions.Abstract",type:"panzoom",alias:"interaction.panzoom",requires:["Ext.draw.Animator"],config:{axes:{top:{},right:{},bottom:{},left:{}},minZoom:null,maxZoom:null,showOverflowArrows:true,panGesture:"drag",zoomGesture:"pinch",zoomOnPanGesture:false,modeToggleButton:{xtype:"segmentedbutton",width:200,defaults:{ui:"default-toolbar"},cls:Ext.baseCSSPrefix+"panzoom-toggle",items:[{text:"Pan"},{text:"Zoom"}]},hideLabelInGesture:false},stopAnimationBeforeSync:true,applyAxes:function(b,a){return Ext.merge(a||{},b)},applyZoomOnPanGesture:function(a){this.getChart();if(this.isMultiTouch()){return false}return a},updateZoomOnPanGesture:function(b){var a=this.getModeToggleButton();if(!this.isMultiTouch()){a.show();a.setValue(b?1:0)}else{a.hide()}},toggleMode:function(){var a=this;if(!a.isMultiTouch()){a.setZoomOnPanGesture(!a.getZoomOnPanGesture())}},applyModeToggleButton:function(c,b){var d=this,a=Ext.factory(c,"Ext.button.Segmented",b);if(!a&&b){b.destroy()}if(a&&!b){a.addListener("toggle",function(e){d.setZoomOnPanGesture(e.getValue()===1)})}return a},getGestures:function(){var c=this,e={},d=c.getPanGesture(),b=c.getZoomGesture(),a=Ext.supports.Touch;e[b]="onZoomGestureMove";e[b+"start"]="onZoomGestureStart";e[b+"end"]="onZoomGestureEnd";e[d]="onPanGestureMove";e[d+"start"]="onPanGestureStart";e[d+"end"]="onPanGestureEnd";e.doubletap="onDoubleTap";return e},onDoubleTap:function(h){var f=this,c=f.getChart(),g=c.getAxes(),b,a,d;for(a=0,d=g.length;a1){b=1}if(b*j<1){b=1/j}f=o[0];p=o[1];l=l[1]-l[0];if(b===l&&l===1){return}c.setVisibleRange([(o[0]+o[1]-b)*0.5-n/e*b,(o[0]+o[1]+b)*0.5-n/e*b]);return(Math.abs(f-c.getVisibleRange()[0])>1e-10||Math.abs(p-c.getVisibleRange()[1])>1e-10)},destroy:function(){this.setModeToggleButton(null);this.callParent()}});Ext.define("Ext.chart.interactions.Rotate",{extend:"Ext.chart.interactions.Abstract",type:"rotate",alias:"interaction.rotate",config:{gesture:"rotate",gestures:{rotate:"onRotate",rotateend:"onRotate",dragstart:"onGestureStart",drag:"onGesture",dragend:"onGestureEnd"},rotation:0},oldRotations:null,getAngle:function(f){var c=this,b=c.getChart(),d=b.getEventXY(f),a=b.getCenter();return Math.atan2(d[1]-a[1],d[0]-a[0])},getRadius:function(a){return this.getChart().getRadius()},getEventRadius:function(h){var f=this,d=f.getChart(),g=d.getEventXY(h),a=d.getCenter(),c=g[0]-a[0],b=g[1]-a[1];return Math.sqrt(c*c+b*b)},onGestureStart:function(d){var c=this,b=c.getRadius(d),a=c.getEventRadius(d);if(b>=a){c.lockEvents("drag");c.angle=c.getAngle(d);c.oldRotations={};return false}},onGesture:function(b){var a=this,c=a.getAngle(b)-a.angle;if(a.getLocks().drag===a){a.doRotateTo(c,true);return false}},doRotateTo:function(d,a,b){var n=this,l=n.getChart(),k=l.getAxes(),f=l.getSeries(),m=n.oldRotations,c,j,g,e,h;if(!b){l.suspendAnimation()}for(e=0,h=k.length;ea){a=g}}}return a}});Ext.define("Ext.chart.plugin.ItemEvents",{extend:"Ext.plugin.Abstract",alias:"plugin.chartitemevents",moveEvents:false,mouseMoveEvents:{mousemove:true,mouseover:true,mouseout:true},itemMouseMoveEvents:{itemmousemove:true,itemmouseover:true,itemmouseout:true},init:function(b){var a="handleEvent";this.chart=b;b.addElementListener({click:a,dblclick:a,mousedown:a,mousemove:a,mouseup:a,mouseover:a,mouseout:a,priority:1001,scope:this})},hasItemMouseMoveListeners:function(){var b=this.chart.hasListeners,a;for(a in this.itemMouseMoveEvents){if(a in b){return true}}return false},handleEvent:function(g){var d=this,a=d.chart,h=g.type in d.mouseMoveEvents,c=d.lastItem,f,b;if(h&&!d.hasItemMouseMoveListeners()&&!d.moveEvents){return}f=a.getEventXY(g);b=a.getItemForPoint(f[0],f[1]);if(h&&!Ext.Object.equals(b,c)){if(c){a.fireEvent("itemmouseout",a,c,g);c.series.fireEvent("itemmouseout",c.series,c,g)}if(b){a.fireEvent("itemmouseover",a,b,g);b.series.fireEvent("itemmouseover",b.series,b,g)}}if(b){a.fireEvent("item"+g.type,a,b,g);b.series.fireEvent("item"+g.type,b.series,b,g)}d.lastItem=b}});Ext.define("Ext.chart.series.Cartesian",{extend:"Ext.chart.series.Series",config:{xField:null,yField:null,xAxis:null,yAxis:null},directions:["X","Y"],fieldCategoryX:["X"],fieldCategoryY:["Y"],applyXAxis:function(a,b){return this.getChart().getAxis(a)||b},applyYAxis:function(a,b){return this.getChart().getAxis(a)||b},updateXAxis:function(a){a.processData(this)},updateYAxis:function(a){a.processData(this)},coordinateX:function(){return this.coordinate("X",0,2)},coordinateY:function(){return this.coordinate("Y",1,2)},getItemForPoint:function(a,g){if(this.getSprites()){var f=this,d=f.getSprites()[0],b=f.getStore(),e,c;if(f.getHidden()){return null}if(d){c=d.getIndexNearPoint(a,g);if(c!==-1){e={series:f,category:f.getItemInstancing()?"items":"markers",index:c,record:b.getData().items[c],field:f.getYField(),sprite:d};return e}}}},createSprite:function(){var c=this,a=c.callParent(),b=c.getChart(),d=c.getXAxis();a.setAttributes({flipXY:b.getFlipXY(),xAxis:d});if(a.setAggregator&&d&&d.getAggregator){if(d.getAggregator){a.setAggregator({strategy:d.getAggregator()})}else{a.setAggregator({})}}return a},getSprites:function(){var d=this,c=this.getChart(),e=d.getAnimation()||c&&c.getAnimation(),b=d.getItemInstancing(),f=d.sprites,a;if(!c){return[]}if(!f.length){a=d.createSprite()}else{a=f[0]}if(e){if(b){a.itemsMarker.getTemplate().setAnimation(e)}a.setAnimation(e)}return f},provideLegendInfo:function(d){var b=this,a=b.getSubStyleWithTheme(),c=a.fillStyle;if(Ext.isArray(c)){c=c[0]}d.push({name:b.getTitle()||b.getYField()||b.getId(),mark:(Ext.isObject(c)?c.stops&&c.stops[0].color:c)||a.strokeStyle||"black",disabled:b.getHidden(),series:b.getId(),index:0})},getXRange:function(){return[this.dataRange[0],this.dataRange[2]]},getYRange:function(){return[this.dataRange[1],this.dataRange[3]]}});Ext.define("Ext.chart.series.StackedCartesian",{extend:"Ext.chart.series.Cartesian",config:{stacked:true,splitStacks:true,fullStack:false,fullStackTotal:100,hidden:[]},spriteAnimationCount:0,themeColorCount:function(){var b=this,a=b.getYField();return Ext.isArray(a)?a.length:1},updateStacked:function(){this.processData()},updateSplitStacks:function(){this.processData()},coordinateY:function(){return this.coordinateStacked("Y",1,2)},coordinateStacked:function(D,e,m){var F=this,f=F.getStore(),r=f.getData().items,B=r.length,c=F["get"+D+"Axis"](),x=F.getHidden(),a=F.getSplitStacks(),z=F.getFullStack(),l=F.getFullStackTotal(),p={min:0,max:0},n=F["fieldCategory"+D],C=[],o=[],E=[],h,A=F.getStacked(),g=F.getSprites(),q=[],w,v,u,s,H,y,b,d,G,t;if(!g.length){return}for(w=0;w=0||!a){y[v]+=G}else{if(G<0){b[v]+=G}}}}}for(v=0;v=0||!a){if(z&&y[u]){G[u]*=l/y[u]}C[u]=o[u];o[u]+=G[u];h[u]=o[u]}else{if(z&&b[u]){G[u]*=l/b[u]}C[u]=E[u];E[u]+=G[u];h[u]=E[u]}}t["dataStart"+d]=C;t["data"+d]=h;F.getRangeOfData(C,p);F.getRangeOfData(h,p)}else{t["dataStart"+d]=C;t["data"+d]=G;F.getRangeOfData(G,p)}g[v].setAttributes(t)}}F.dataRange[e]=p.min;F.dataRange[e+m]=p.max;t={};t["dataMin"+D]=p.min;t["dataMax"+D]=p.max;for(w=0;w=b[a-1]){return a-1}while(f+1>1,e=b[c];if(e===d){return c}else{if(ea&&ml&&v=b;v--){h=l[v]*e+m;g=C[v]*o+k;s.lineTo(f,g);s.lineTo(f=h,g)}}else{for(v=a;v>=b;v--){h=l[v]*e+m;g=C[v]*o+k;s.lineTo(h,g)}}}else{s.lineTo(l[a]*e+m,g);s.lineTo(l[a]*e+m,k);s.lineTo(z,k);s.lineTo(z,j[v]*o+k)}if(p.transformFillStroke){p.matrix.toContext(s)}s.fill();if(p.transformFillStroke){p.inverseMatrix.toContext(s)}s.beginPath();s.moveTo(z,w);if(p.step){for(v=b;v<=a;v++){h=l[v]*e+m;g=j[v]*o+k;s.lineTo(h,d);s.lineTo(h,d=g);n.translationX=c.x(h,g);n.translationY=c.y(h,g);B.putMarker("markers",n,v,!p.renderer)}}else{for(v=b;v<=a;v++){h=l[v]*e+m;g=j[v]*o+k;s.lineTo(h,g);n.translationX=c.x(h,g);n.translationY=c.y(h,g);B.putMarker("markers",n,v,!p.renderer)}}if(p.transformFillStroke){p.matrix.toContext(s)}s.stroke()}});Ext.define("Ext.chart.series.Area",{extend:"Ext.chart.series.StackedCartesian",alias:"series.area",type:"area",seriesType:"areaSeries",requires:["Ext.chart.series.sprite.Area"],config:{splitStacks:false}});Ext.define("Ext.chart.series.sprite.Bar",{alias:"sprite.barSeries",extend:"Ext.chart.series.sprite.StackedCartesian",inheritableStatics:{def:{processors:{minBarWidth:"number",maxBarWidth:"number",minGapWidth:"number",radius:"number",inGroupGapWidth:"number"},defaults:{minBarWidth:2,maxBarWidth:100,minGapWidth:5,inGroupGapWidth:3,radius:0}}},drawLabel:function(k,i,s,h,o){var q=this,n=q.attr,f=q.getMarker("labels"),d=f.getTemplate(),l=q.labelCfg||(q.labelCfg={}),c=q.surfaceMatrix,j=n.labelOverflowPadding,b=d.attr.display,m=d.attr.orientation,g,e,a,r,t,p;l.x=c.x(i,h);l.y=c.y(i,h);if(!n.flipXY){l.rotationRads=-Math.PI*0.5}else{l.rotationRads=0}l.calloutVertical=!n.flipXY;switch(m){case"horizontal":l.rotationRads=0;l.calloutVertical=false;break;case"vertical":l.rotationRads=-Math.PI*0.5;l.calloutVertical=true;break}l.text=k;if(d.attr.renderer){p=[k,f,l,{store:q.getStore()},o];r=Ext.callback(d.attr.renderer,null,p,0,q.getSeries());if(typeof r==="string"){l.text=r}else{if(typeof r==="object"){if("text" in r){l.text=r.text}t=true}}}a=q.getMarkerBBox("labels",o,true);if(!a){q.putMarker("labels",l,o);a=q.getMarkerBBox("labels",o,true)}e=(a.width/2+j);if(s>h){e=-e}if((m==="horizontal"&&n.flipXY)||(m==="vertical"&&!n.flipXY)||!m){g=(b==="insideStart")?s+e:h-e}else{g=(b==="insideStart")?s+j*2:h-j*2}l.x=c.x(i,g);l.y=c.y(i,g);g=(b==="insideStart")?s-e:h+e;l.calloutPlaceX=c.x(i,g);l.calloutPlaceY=c.y(i,g);g=(b==="insideStart")?s:h;l.calloutStartX=c.x(i,g);l.calloutStartY=c.y(i,g);if(s>h){e=-e}if(Math.abs(h-s)<=e*2||b==="outside"){l.callout=1}else{l.callout=0}if(t){Ext.apply(l,r)}q.putMarker("labels",l,o)},drawBar:function(l,b,d,c,h,k,a,e){var g=this,j={},f=g.attr.renderer,i;j.x=c;j.y=h;j.width=k-c;j.height=a-h;j.radius=g.attr.radius;if(f){i=Ext.callback(f,null,[g,j,{store:g.getStore()},e],0,g.getSeries());Ext.apply(j,i)}g.putMarker("items",j,e,!f)},renderClipped:function(G,u,F,C){if(this.cleanRedraw){return}var q=this,o=q.attr,w=o.dataX,v=o.dataY,H=o.labels,n=o.dataStartY,m=o.groupCount,E=o.groupOffset-(m-1)*0.5,z=o.inGroupGapWidth,t=u.lineWidth,D=o.matrix,B=D.elements[0],j=D.elements[3],e=D.elements[4],d=G.roundPixel(D.elements[5])-1,J=(B<0?-1:1)*B-o.minGapWidth,k=(Math.min(J,o.maxBarWidth)-z*(m-1))/m,A=G.roundPixel(Math.max(o.minBarWidth,k)),c=q.surfaceMatrix,g,I,b,h,K,a,l=0.5*o.lineWidth,L=Math.min(F[0],F[2]),x=Math.max(F[0],F[2]),y=Math.max(0,Math.floor(L)),p=Math.min(w.length-1,Math.ceil(x)),f=H&&q.getMarker("labels"),s,r;for(K=y;K<=p;K++){s=n?n[K]:0;r=v[K];a=w[K]*B+e+E*(A+z);g=G.roundPixel(a-A/2)+l;h=G.roundPixel(r*j+d+t);I=G.roundPixel(a+A/2)-l;b=G.roundPixel(s*j+d+t);q.drawBar(u,G,F,g,h-l,I,b-l,K);if(f&&H[K]!=null){q.drawLabel(H[K],a,b,h,K)}q.putMarker("markers",{translationX:c.x(a,h),translationY:c.y(a,h)},K,true)}},getIndexNearPoint:function(l,k){var m=this,g=m.attr,h=g.dataX,a=m.getSurface(),b=a.getRect()||[0,0,0,0],j=b[3],e,d,c,n,f=-1;if(g.flipXY){e=j-k;if(a.getInherited().rtl){d=b[2]-l}else{d=l}}else{e=l;d=j-k}for(c=0;c0){d.y=g;d.height=a+f}else{d.y=g+f;d.height=a-f}},render:function(l,m){var u=this,k=u.attr,r=k.x,j=k.y,f=j+k.height,i=j=0;b--){if(!c[b]){o=h[b];d=o.getIndexNearPoint(m,k);if(d!==-1){e=j.getYField();p={series:j,index:d,category:a?"items":"markers",record:n.getData().items[d],field:typeof e==="string"?e:e[b],sprite:o};return p}}}return null}}});Ext.define("Ext.draw.LimitedCache",{config:{limit:40,feeder:function(){return 0},scope:null},cache:null,constructor:function(a){this.cache={};this.cache.list=[];this.cache.tail=0;this.initConfig(a)},get:function(e){var c=this.cache,b=this.getLimit(),a=this.getFeeder(),d=this.getScope()||this;if(c[e]){return c[e].value}if(c.list[c.tail]){delete c[c.list[c.tail].cacheId]}c[e]=c.list[c.tail]={value:a.apply(d,Array.prototype.slice.call(arguments,1)),cacheId:e};c.tail++;if(c.tail===b){c.tail=0}return c[e].value},clear:function(){this.cache={};this.cache.list=[];this.cache.tail=0}});Ext.define("Ext.draw.SegmentTree",{config:{strategy:"double"},time:function(m,l,n,c,E,d,e){var f=0,o,A,s=new Date(n[m.startIdx[0]]),x=new Date(n[m.endIdx[l-1]]),D=Ext.Date,u=[[D.MILLI,1,"ms1",null],[D.MILLI,2,"ms2","ms1"],[D.MILLI,5,"ms5","ms1"],[D.MILLI,10,"ms10","ms5"],[D.MILLI,50,"ms50","ms10"],[D.MILLI,100,"ms100","ms50"],[D.MILLI,500,"ms500","ms100"],[D.SECOND,1,"s1","ms500"],[D.SECOND,10,"s10","s1"],[D.SECOND,30,"s30","s10"],[D.MINUTE,1,"mi1","s10"],[D.MINUTE,5,"mi5","mi1"],[D.MINUTE,10,"mi10","mi5"],[D.MINUTE,30,"mi30","mi10"],[D.HOUR,1,"h1","mi30"],[D.HOUR,6,"h6","h1"],[D.HOUR,12,"h12","h6"],[D.DAY,1,"d1","h12"],[D.DAY,7,"d7","d1"],[D.MONTH,1,"mo1","d1"],[D.MONTH,3,"mo3","mo1"],[D.MONTH,6,"mo6","mo3"],[D.YEAR,1,"y1","mo3"],[D.YEAR,5,"y5","y1"],[D.YEAR,10,"y10","y5"],[D.YEAR,100,"y100","y10"]],z,b,k=f,F=l,j=false,r=m.startIdx,h=m.endIdx,w=m.minIdx,C=m.maxIdx,a=m.open,y=m.close,g=m.minX,q=m.minY,p=m.maxX,B=m.maxY,v,t;for(z=0;l>f+1&&zn.length*2*b[1]){continue}if(b[3]&&m.map["time_"+b[3]]){o=m.map["time_"+b[3]][0];A=m.map["time_"+b[3]][1]}else{o=k;A=F}f=l;t=s;j=true;r[l]=r[o];h[l]=h[o];w[l]=w[o];C[l]=C[o];a[l]=a[o];y[l]=y[o];g[l]=g[o];q[l]=q[o];p[l]=p[o];B[l]=B[o];t=Ext.Date.add(t,b[0],b[1]);for(v=o+1;vB[l]){B[l]=B[v];p[l]=p[v];C[l]=C[v]}if(q[v]f){m.map["time_"+b[2]]=[f,l]}}},"double":function(h,u,j,a,t,b,c){var e=0,k,f=1,n,d,v,g,s,l,m,r,q,p,o;while(u>e+1){k=e;e=u;f+=f;for(n=k;n=h.maxY[n+1]){s=h.maxIdx[n];p=h.maxX[n];o=h.maxY[n]}else{s=h.maxIdx[n+1];p=h.maxX[n+1];o=h.maxY[n+1]}}h.startIdx[u]=d;h.endIdx[u]=v;h.minIdx[u]=g;h.maxIdx[u]=s;h.open[u]=l;h.close[u]=m;h.minX[u]=r;h.minY[u]=q;h.maxX[u]=p;h.maxY[u]=o;u++}h.map["double_"+f]=[e,u]}},none:Ext.emptyFn,aggregateData:function(h,a,r,c,d){var b=h.length,e=[],s=[],f=[],q=[],j=[],p=[],n=[],o=[],m=[],k=[],g={startIdx:e,endIdx:s,minIdx:f,maxIdx:q,open:j,minX:p,minY:n,maxX:o,maxY:m,close:k},l;for(l=0;l=b[c.startIdx[a-1]]){return a-1}while(g+1>1,f=b[c.startIdx[d]];if(f===e){return d}else{if(f=b[c.endIdx[a-1]]){return a-1}while(g+1>1,f=b[c.endIdx[d]];if(f===e){return d}else{if(f0){if(e){d.getAggregator().setData(b.dataX,b.dataY,e,a,f)}else{d.getAggregator().setData(b.dataX,b.dataY)}}},getGapWidth:function(){return 1},renderClipped:function(b,c,g,f){var e=this,d=Math.min(g[0],g[2]),a=Math.max(g[0],g[2]),h=e.getAggregator()&&e.getAggregator().getAggregation(d,a,(a-d)/f[2]*e.getGapWidth());if(h){e.dataStart=h.data.startIdx[h.start];e.dataEnd=h.data.endIdx[h.end-1];e.renderAggregates(h.data,h.start,h.end,b,c,g,f)}}});Ext.define("Ext.chart.series.sprite.CandleStick",{alias:"sprite.candlestickSeries",extend:"Ext.chart.series.sprite.Aggregative",inheritableStatics:{def:{processors:{raiseStyle:function(b,a){return Ext.merge({},a||{},b)},dropStyle:function(b,a){return Ext.merge({},a||{},b)},barWidth:"number",padding:"number",ohlcType:"enums(candlestick,ohlc)"},defaults:{raiseStyle:{strokeStyle:"green",fillStyle:"green"},dropStyle:{strokeStyle:"red",fillStyle:"red"},planar:false,barWidth:15,padding:3,lineJoin:"miter",miterLimit:5,ohlcType:"candlestick"},triggers:{raiseStyle:"raiseStyle",dropStyle:"dropStyle"},updaters:{raiseStyle:function(){this.raiseTemplate&&this.raiseTemplate.setAttributes(this.attr.raiseStyle)},dropStyle:function(){this.dropTemplate&&this.dropTemplate.setAttributes(this.attr.dropStyle)}}}},candlestick:function(i,c,a,e,h,f,b){var d=Math.min(c,h),g=Math.max(c,h);i.moveTo(f,e);i.lineTo(f,g);i.moveTo(f+b,g);i.lineTo(f+b,d);i.lineTo(f-b,d);i.lineTo(f-b,g);i.closePath();i.moveTo(f,a);i.lineTo(f,d)},ohlc:function(b,d,e,a,f,c,g){b.moveTo(c,e);b.lineTo(c,a);b.moveTo(c,d);b.lineTo(c-g,d);b.moveTo(c,f);b.lineTo(c+g,f)},constructor:function(){this.callParent(arguments);this.raiseTemplate=new Ext.draw.sprite.Rect({parent:this});this.dropTemplate=new Ext.draw.sprite.Rect({parent:this})},getGapWidth:function(){var a=this.attr,b=a.barWidth,c=a.padding;return b+c},renderAggregates:function(d,c,b,t,u,z){var D=this,s=this.attr,j=s.dataX,v=s.matrix,e=v.getXX(),r=v.getYY(),l=v.getDX(),h=v.getDY(),o=s.barWidth/e,C,k=s.ohlcType,f=Math.round(o*0.5*e),a=d.open,y=d.close,B=d.maxY,p=d.minY,q=d.startIdx,m,g,E,n,A,x,w=s.lineWidth*t.devicePixelRatio/2;w-=Math.floor(w);u.save();C=this.raiseTemplate;C.useAttributes(u,z);u.beginPath();for(x=c;xy[x]){m=Math.round(a[x]*r+h)+w;g=Math.round(B[x]*r+h)+w;E=Math.round(p[x]*r+h)+w;n=Math.round(y[x]*r+h)+w;A=Math.round(j[q[x]]*e+l)+w;D[k](u,m,g,E,n,A,f)}}u.fillStroke(C.attr);u.restore()}});Ext.define("Ext.chart.series.CandleStick",{extend:"Ext.chart.series.Cartesian",requires:["Ext.chart.series.sprite.CandleStick"],alias:"series.candlestick",type:"candlestick",seriesType:"candlestickSeries",config:{openField:null,highField:null,lowField:null,closeField:null},fieldCategoryY:["Open","High","Low","Close"],themeColorCount:function(){return 2}});Ext.define("Ext.chart.series.Polar",{extend:"Ext.chart.series.Series",config:{rotation:0,radius:null,center:[0,0],offsetX:0,offsetY:0,showInLegend:true,xField:null,yField:null,angleField:null,radiusField:null,xAxis:null,yAxis:null},directions:["X","Y"],fieldCategoryX:["X"],fieldCategoryY:["Y"],deprecatedConfigs:{field:"angleField",lengthField:"radiusField"},constructor:function(b){var c=this,a=c.getConfigurator(),e=a.configs,d;if(b){for(d in c.deprecatedConfigs){if(d in b&&!(b in e)){Ext.raise("'"+d+"' config has been deprecated. Please use the '"+c.deprecatedConfigs[d]+"' config instead.")}}}c.callParent([b])},getXField:function(){return this.getAngleField()},updateXField:function(a){this.setAngleField(a)},getYField:function(){return this.getRadiusField()},updateYField:function(a){this.setRadiusField(a)},applyXAxis:function(a,b){return this.getChart().getAxis(a)||b},applyYAxis:function(a,b){return this.getChart().getAxis(a)||b},getXRange:function(){return[this.dataRange[0],this.dataRange[2]]},getYRange:function(){return[this.dataRange[1],this.dataRange[3]]},themeColorCount:function(){var c=this,a=c.getStore(),b=a&&a.getCount()||0;return b},isStoreDependantColorCount:true,getDefaultSpriteConfig:function(){return{type:this.seriesType,renderer:this.getRenderer(),centerX:0,centerY:0,rotationCenterX:0,rotationCenterY:0}},applyRotation:function(a){return Ext.draw.sprite.AttributeParser.angle(a)},updateRotation:function(a){var b=this.getSprites();if(b&&b[0]){b[0].setAttributes({baseRotation:a})}}});Ext.define("Ext.chart.series.Gauge",{alias:"series.gauge",extend:"Ext.chart.series.Polar",type:"gauge",seriesType:"pieslice",requires:["Ext.draw.sprite.Sector"],config:{needle:false,needleLength:90,needleWidth:4,donut:30,showInLegend:false,value:null,colors:null,sectors:null,minimum:0,maximum:100,rotation:0,totalAngle:Math.PI/2,rect:[0,0,1,1],center:[0.5,0.75],radius:0.5,wholeDisk:false},coordinateX:function(){return this.coordinate("X",0,2)},coordinateY:function(){return this.coordinate("Y",1,2)},updateNeedle:function(b){var a=this,d=a.getSprites(),c=a.valueToAngle(a.getValue());if(d&&d.length){d[0].setAttributes({startAngle:(b?c:0),endAngle:c,strokeOpacity:(b?1:0),lineWidth:(b?a.getNeedleWidth():0)});a.doUpdateStyles()}},themeColorCount:function(){var c=this,a=c.getStore(),b=a&&a.getCount()||0;return b+(c.getNeedle()?0:1)},updateColors:function(a,b){var f=this,h=f.getSectors(),j=h&&h.length,e=f.getSprites(),c=Ext.Array.clone(a),g=a&&a.length,d;if(!g||!a[0]){return}for(d=0;d0?f[b-1].end:d.getMinimum()),end:Math.min(e,d.getMaximum())};if(b==(c-1)&&f[b].end0?f[b-1].end:d.getMinimum())}if(typeof e.end==="number"){a=Math.min(e.end,d.getMaximum())}else{a=d.getMaximum()}f[b].start=g;f[b].end=a}}}else{f=[{start:d.getMinimum(),end:d.getMaximum()}]}return f},getSprites:function(){var j=this,m=j.getStore(),l=j.getValue(),c,g;if(!m&&!Ext.isNumber(l)){return[]}var h=j.getChart(),b=j.getAnimation()||h&&h.getAnimation(),f=j.sprites,k=0,o,n,e,d,a=[];if(f&&f.length){f[0].setAnimation(b);return f}d={store:m,field:j.getXField(),angleField:j.getXField(),value:l,series:j};o=j.createSprite();o.setAttributes({zIndex:10},true);o.rendererData=d;o.rendererIndex=k++;a.push(j.getNeedleWidth());j.getLabel().getTemplate().setField(true);n=j.normalizeSectors(j.getSectors());for(c=0,g=n.length;c2&&b.length>2){this.smoothX=Ext.draw.Draw.spline(c);this.smoothY=Ext.draw.Draw.spline(b)}else{delete this.smoothX;delete this.smoothY}}}}},list:null,updatePlainBBox:function(d){var b=this.attr,c=Math.min(0,b.dataMinY),a=Math.max(0,b.dataMaxY);d.x=b.dataMinX;d.y=c;d.width=b.dataMaxX-b.dataMinX;d.height=a-c},drawStrip:function(a,c){a.moveTo(c[0],c[1]);for(var b=2,d=c.length;b0){b++;d+=c>>b}return Math.pow(2,b>0?b-1:b)},drawSmoothStroke:function(u,v,c,b,C,f){var G=this,t=G.attr,d=t.step,z=t.matrix,s=t.renderer,e=z.getXX(),p=z.getYY(),m=z.getDX(),k=z.getDY(),r=G.smoothX,q=G.smoothY,I=G.calculateScale(t.dataX.length,b),o,F,n,E,h,g,B,a,A,w,H,D,l={type:"line",smooth:true,step:d};v.beginPath();v.moveTo(r[c*3]*e+m,q[c*3]*p+k);for(A=0,w=c*3+1;Ap){q.push(p*C+c,n*f+b,U[R]);q.push(O*C+c,M*f+b,U[R])}else{q.push(p*C+c,n*f+b,U[R])}}}if(q.length){for(R=0;Ra){K=a}else{if(K<-a){K=-a}}q[R+1]=K}else{S=false;continue}G=q[R+2];if(t){m.drawMarker(L,K,G)}if(d&&h[G]){m.drawLabel(h[G],L,K,G,D)}}m.isContinuousLine=S;if(g&&!S){Ext.raise("Line smoothing in only supported for gapless data, where all data points are finite numbers.")}if(v){T=v.getAlignment()==="vertical";if(Ext.isNumber(v.floatingAtCoord)){Q=(T?D[2]:D[3])-v.floatingAtCoord}else{Q=T?D[0]:D[1]}}else{Q=k.flipXY?D[0]:D[1]}if(k.preciseStroke){if(k.fillArea){o.fill()}if(k.transformFillStroke){k.inverseMatrix.toContext(o)}m.drawStroke(N,o,w,l,q,Q);if(k.transformFillStroke){k.matrix.toContext(o)}o.stroke()}else{m.drawStroke(N,o,w,l,q,Q);if(S&&g&&k.fillArea&&!k.renderer){var A=s[s.length-1]*C+c+u,z=r[r.length-1]*f+b,J=s[0]*C+c-u,H=r[0]*f+b;o.lineTo(A,z);o.lineTo(A,Q-k.lineWidth);o.lineTo(J,Q-k.lineWidth);o.lineTo(J,H)}if(k.transformFillStroke){k.matrix.toContext(o)}if(k.fillArea){o.fillStroke(k,true)}else{o.stroke(true)}}}}});Ext.define("Ext.chart.series.Line",{extend:"Ext.chart.series.Cartesian",alias:"series.line",type:"line",seriesType:"lineSeries",requires:["Ext.chart.series.sprite.Line"],config:{selectionTolerance:20,smooth:false,step:false,fill:undefined,aggregator:{strategy:"double"}},defaultSmoothness:3,overflowBuffer:1,themeMarkerCount:function(){return 1},getDefaultSpriteConfig:function(){var d=this,e=d.callParent(arguments),c=Ext.apply({},d.getStyle()),b,a=false;if(typeof d.config.fill!="undefined"){if(d.config.fill){a=true;if(typeof c.fillStyle=="undefined"){if(typeof c.strokeStyle=="undefined"){b=d.getStyleWithTheme();c.fillStyle=b.fillStyle;c.strokeStyle=b.strokeStyle}else{c.fillStyle=c.strokeStyle}}}}else{if(c.fillStyle){a=true}}if(!a){delete c.fillStyle}c=Ext.apply(e||{},c);return Ext.apply(c,{fillArea:a,step:d.config.step,smooth:d.config.smooth,selectionTolerance:d.config.selectionTolerance})},updateStep:function(b){var a=this.getSprites()[0];if(a&&a.attr.step!==b){a.setAttributes({step:b})}},updateFill:function(b){var a=this.getSprites()[0];if(a&&a.attr.fillArea!==b){a.setAttributes({fillArea:b})}},updateSmooth:function(a){var b=this.getSprites()[0];if(b&&b.attr.smooth!==a){b.setAttributes({smooth:a})}}});Ext.define("Ext.chart.series.sprite.PieSlice",{extend:"Ext.draw.sprite.Sector",mixins:{markerHolder:"Ext.chart.MarkerHolder"},alias:"sprite.pieslice",inheritableStatics:{def:{processors:{doCallout:"bool",label:"string",rotateLabels:"bool",labelOverflowPadding:"number",renderer:"default"},defaults:{doCallout:true,rotateLabels:true,label:"",labelOverflowPadding:10,renderer:null}}},config:{rendererData:null,rendererIndex:0,series:null},setGradientBBox:function(q,k){var j=this,i=j.attr,g=(i.fillStyle&&i.fillStyle.isGradient)||(i.strokeStyle&&i.strokeStyle.isGradient);if(g&&!i.constrainGradients){var b=j.getMidAngle(),d=i.margin,e=i.centerX,c=i.centerY,a=i.endRho,l=i.matrix,o=l.getScaleX(),n=l.getScaleY(),m=o*a,f=n*a,p={width:m+m,height:f+f};if(d){e+=d*Math.cos(b);c+=d*Math.sin(b)}p.x=l.x(e,c)-m;p.y=l.y(e,c)-f;q.setGradientBBox(p)}else{j.callParent([q,k])}},render:function(b,c,g,f){var e=this,a=e.attr,h={},d;if(a.renderer){h={type:"sector",text:a.text,centerX:a.centerX,centerY:a.centerY,margin:a.margin,startAngle:Math.min(a.startAngle,a.endAngle),endAngle:Math.max(a.startAngle,a.endAngle),startRho:Math.min(a.startRho,a.endRho),endRho:Math.max(a.startRho,a.endRho)};d=Ext.callback(a.renderer,null,[e,h,e.rendererData,e.rendererIndex],0,e.getSeries());e.setAttributes(d);e.useAttributes(c,g)}e.callParent([b,c,g,f]);if(a.label&&e.getMarker("labels")){e.placeLabel()}},placeLabel:function(){var z=this,s=z.attr,r=s.attributeId,t=Math.min(s.startAngle,s.endAngle),p=Math.max(s.startAngle,s.endAngle),k=(t+p)*0.5,n=s.margin,h=s.centerX,g=s.centerY,f=Math.sin(k),c=Math.cos(k),v=Math.min(s.startRho,s.endRho)+n,m=Math.max(s.startRho,s.endRho)+n,l=(v+m)*0.5,b=z.surfaceMatrix,o=z.labelCfg||(z.labelCfg={}),e=z.getMarker("labels"),d=e.getTemplate(),a=d.getCalloutLine(),q=a&&a.length||40,u,j,i,A,w;b.appendMatrix(s.matrix);o.text=s.label;j=h+c*l;i=g+f*l;o.x=b.x(j,i);o.y=b.y(j,i);j=h+c*m;i=g+f*m;o.calloutStartX=b.x(j,i);o.calloutStartY=b.y(j,i);j=h+c*(m+q);i=g+f*(m+q);o.calloutPlaceX=b.x(j,i);o.calloutPlaceY=b.y(j,i);if(!s.rotateLabels){o.rotationRads=0}else{switch(d.attr.orientation){case"horizontal":o.rotationRads=k+Math.atan2(b.y(1,0)-b.y(0,0),b.x(1,0)-b.x(0,0))+Math.PI/2;break;case"vertical":o.rotationRads=k+Math.atan2(b.y(1,0)-b.y(0,0),b.x(1,0)-b.x(0,0));break}}o.calloutColor=(a&&a.color)||z.attr.fillStyle;if(a){if(a.width){o.calloutWidth=a.width}}else{o.calloutHasLine=false}o.globalAlpha=s.globalAlpha*s.fillOpacity;o.hidden=(s.startAngle==s.endAngle);if(d.attr.renderer){w=[z.attr.label,e,o,z.rendererData,z.rendererIndex];A=Ext.callback(d.attr.renderer,null,w,0,z.getSeries());if(typeof A==="string"){o.text=A}else{Ext.apply(o,A)}}z.putMarker("labels",o,r);u=z.getMarkerBBox("labels",r,true);if(u){if(s.doCallout){if(d.attr.display==="outside"){z.putMarker("labels",{callout:1},r)}else{if(d.attr.display==="inside"){z.putMarker("labels",{callout:0},r)}else{z.putMarker("labels",{callout:1-z.sliceContainsLabel(s,u)},r)}}}else{z.putMarker("labels",{globalAlpha:z.sliceContainsLabel(s,u)},r)}}},sliceContainsLabel:function(d,f){var e=d.labelOverflowPadding,h=(d.endRho+d.startRho)/2,g=h+(f.width+e)/2,i=h-(f.width+e)/2,j,c,b,a;if(e<0){return 1}if(f.width+e*2>(d.endRho-d.startRho)){return 0}c=Math.sqrt(d.endRho*d.endRho-g*g);b=Math.sqrt(d.endRho*d.endRho-i*i);j=Math.abs(d.endAngle-d.startAngle);a=(j>Math.PI/2?i:Math.abs(Math.tan(j/2))*i);if(f.height+e*2>Math.min(c,b,a)*2){return 0}return 1}});Ext.define("Ext.chart.series.Pie",{extend:"Ext.chart.series.Polar",requires:["Ext.chart.series.sprite.PieSlice"],type:"pie",alias:"series.pie",seriesType:"pieslice",config:{donut:0,rotation:0,clockwise:true,totalAngle:2*Math.PI,hidden:[],radiusFactor:100,highlightCfg:{margin:20},style:{}},directions:["X"],applyLabel:function(a,b){if(Ext.isObject(a)&&!Ext.isString(a.orientation)){Ext.apply(a=Ext.Object.chain(a),{orientation:"vertical"})}return this.callParent([a,b])},updateLabelData:function(){var h=this,j=h.getStore(),g=j.getData().items,e=h.getSprites(),a=h.getLabel().getTemplate().getField(),d=h.getHidden(),b,f,c,k;if(e.length&&a){c=[];for(b=0,f=g.length;bs){s=k}}d[p]=a;if(p>=o.length){o[p]=false}}o.length=c;t.maxY=s;if(a!==0){m=h/a}for(p=0;p=a){return{series:h,sprite:f[b],index:b,record:g[b],field:h.getXField()}}}}}return null},getItemForPoint:function(f,e){var t=this,c=t.getSprites();if(c){var s=t.getCenter(),q=t.getOffsetX(),p=t.getOffsetY(),j=f-s[0]+q,h=e-s[1]+p,b=t.getStore(),g=t.getDonut(),o=b.getData().items,r=Math.atan2(h,j)-t.getRotation(),a=Math.sqrt(j*j+h*h),l=t.getRadius()*g*0.01,m=t.getHidden(),n,d,k;for(n=0,d=o.length;n=l+k.margin&&a<=k.endRho+k.margin){if(t.betweenAngle(r,k.startAngle,k.endAngle)){return{series:t,sprite:c[n],index:n,record:o[n],field:t.getXField()}}}}}return null}},provideLegendInfo:function(f){var h=this,j=h.getStore();if(j){var g=j.getData().items,b=h.getLabel().getTemplate().getField(),c=h.getXField(),e=h.getHidden(),d,a,k;for(d=0;d=0&&b<0){f=Math.sin(d)}else{if(d<=0&&b>0){f=Math.sin(b)}else{if(d>=0&&b>0){if(d>b){f=0}else{f=Math.max(Math.sin(d),Math.sin(b))}}else{f=1}}}a.zIndex=4+f;break;case"outerBack":a.zIndex=1;break;case"start":a.zIndex=4+Math.sin(c(d+e));break;case"end":a.zIndex=4+Math.sin(c(b+e));break;case"innerFront":a.zIndex=2;break;case"innerBack":a.zIndex=4+Math.sin(c((d+b)/2+e));break;case"bottom":a.zIndex=0;break}a.dirtyZIndex=true},updatePlainBBox:function(k){var f=this.attr,a=f.part,b=f.baseRotation,e=f.centerX,d=f.centerY,j,c,i,h,g,l;if(a==="start"){c=f.startAngle+b}else{if(a==="end"){c=f.endAngle+b}}if(Ext.isNumber(c)){g=Math.sin(c);l=Math.cos(c);i=Math.min(e+l*f.startRho,e+l*f.endRho);h=d+g*f.startRho*f.distortion;k.x=i;k.y=h;k.width=l*(f.endRho-f.startRho);k.height=f.thickness+g*(f.endRho-f.startRho)*2;return}if(a==="innerFront"||a==="innerBack"){j=f.startRho}else{j=f.endRho}k.width=j*2;k.height=j*f.distortion*2+f.thickness;k.x=f.centerX-j;k.y=f.centerY-j*f.distortion},updateTransformedBBox:function(a){if(this.attr.part==="start"||this.attr.part==="end"){return this.callParent(arguments)}return this.updatePlainBBox(a)},updatePath:function(a){if(!this.attr.globalAlpha){return}if(this.attr.endAngle0||c,i;if(n){i=(p+m)/2;g+=Math.cos(i)*k;f+=Math.sin(i)*k*e;l.moveTo(g+d*q,f+b*q*e);l.lineTo(g+d*j,f+b*j*e);l.lineTo(g+d*j,f+b*j*e+a);l.lineTo(g+d*q,f+b*q*e+a);l.closePath()}},startRenderer:function(a){this.sideRenderer(a,"start")},endRenderer:function(a){this.sideRenderer(a,"end")},rimRenderer:function(q,e,o,j){var w=this,s=w.attr,p=s.margin,h=s.centerX,g=s.centerY,d=s.distortion,i=s.baseRotation,t=Ext.draw.sprite.AttributeParser.angle,u=s.startAngle+i,r=s.endAngle+i,k=t((u+r)/2),a=s.thickness,b=s.globalAlpha<1,c,n,v;w.bevelParams=[];u=t(u);r=t(r);h+=Math.cos(k)*p;g+=Math.sin(k)*p*d;c=u>=0&&r>=0;n=u<=0&&r<=0;function l(){q.ellipse(h,g+a,e,e*d,0,Math.PI,u,true);q.lineTo(h+Math.cos(u)*e,g+Math.sin(u)*e*d);v=[h,g,e,e*d,0,u,Math.PI,false];if(!o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}function f(){q.ellipse(h,g+a,e,e*d,0,0,r,false);q.lineTo(h+Math.cos(r)*e,g+Math.sin(r)*e*d);v=[h,g,e,e*d,0,r,0,true];if(!o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}function x(){q.ellipse(h,g+a,e,e*d,0,Math.PI,r,false);q.lineTo(h+Math.cos(r)*e,g+Math.sin(r)*e*d);v=[h,g,e,e*d,0,r,Math.PI,true];if(o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}function m(){q.ellipse(h,g+a,e,e*d,0,u,0,false);q.lineTo(h+e,g);v=[h,g,e,e*d,0,0,u,true];if(o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}if(j){if(!o||b){if(u>=0&&r<0){l()}else{if(u<=0&&r>0){f()}else{if(u<=0&&r<0){if(u>r){q.ellipse(h,g+a,e,e*d,0,0,Math.PI,false);q.lineTo(h-e,g);v=[h,g,e,e*d,0,Math.PI,0,true];if(!o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}}else{if(u>r){l();f()}else{v=[h,g,e,e*d,0,u,r,false];if(c&&!o||n&&o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.lineTo(h+Math.cos(r)*e,g+Math.sin(r)*e*d+a);q.ellipse(h,g+a,e,e*d,0,r,u,true);q.closePath()}}}}}}else{if(o||b){if(u>=0&&r<0){x()}else{if(u<=0&&r>0){m()}else{if(u<=0&&r<0){if(u>r){x();m()}else{q.ellipse(h,g+a,e,e*d,0,u,r,false);q.lineTo(h+Math.cos(r)*e,g+Math.sin(r)*e*d);v=[h,g,e,e*d,0,r,u,true];if(o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}}else{if(u>r){q.ellipse(h,g+a,e,e*d,0,-Math.PI,0,false);q.lineTo(h+e,g);v=[h,g,e,e*d,0,0,-Math.PI,true];if(o){w.bevelParams.push(v)}q.ellipse.apply(q,v);q.closePath()}}}}}}},innerFrontRenderer:function(a){this.rimRenderer(a,this.attr.startRho,true,true)},innerBackRenderer:function(a){this.rimRenderer(a,this.attr.startRho,true,false)},outerFrontRenderer:function(a){this.rimRenderer(a,this.attr.endRho,false,true)},outerBackRenderer:function(a){this.rimRenderer(a,this.attr.endRho,false,false)}});Ext.define("Ext.draw.PathUtil",function(){var a=Math.abs,c=Math.pow,e=Math.cos,b=Math.acos,d=Math.sqrt,f=Math.PI;return{singleton:true,requires:["Ext.draw.overrides.Path","Ext.draw.overrides.sprite.Path","Ext.draw.overrides.sprite.Instancing","Ext.draw.overrides.Surface"],cubicRoots:function(m){var z=m[0],x=m[1],w=m[2],v=m[3];if(z===0){return this.quadraticRoots(x,w,v)}var s=x/z,r=w/z,q=v/z,k=(3*r-c(s,2))/9,j=(9*s*r-27*q-2*c(s,3))/54,p=c(k,3)+c(j,2),n=[],h,g,o,l,u,y=Ext.Number.sign;if(p>=0){h=y(j+d(p))*c(a(j+d(p)),1/3);g=y(j-d(p))*c(a(j-d(p)),1/3);n[0]=-s/3+(h+g);n[1]=-s/3-(h+g)/2;n[2]=n[1];o=a(d(3)*(h-g)/2);if(o!==0){n[1]=-1;n[2]=-1}}else{l=b(j/d(-c(k,3)));n[0]=2*d(-k)*e(l/3)-s/3;n[1]=2*d(-k)*e((l+2*f)/3)-s/3;n[2]=2*d(-k)*e((l+4*f)/3)-s/3}for(u=0;u<3;u++){if(n[u]<0||n[u]>1){n[u]=-1}}return n},quadraticRoots:function(h,g,n){var m,l,k,j;if(h===0){return this.linearRoot(g,n)}m=g*g-4*h*n;if(m===0){k=[-g/(2*h)]}else{if(m>0){l=d(m);k=[(-g-l)/(2*h),(-g+l)/(2*h)]}else{return[]}}for(j=0;j1){k[j]=-1}}return k},linearRoot:function(h,g){var i=-g/h;if(h===0||i<0||i>1){return[]}return[i]},bezierCoeffs:function(h,g,k,j){var i=[];i[0]=-h+3*g-3*k+j;i[1]=3*h-6*g+3*k;i[2]=-3*h+3*g;i[3]=h;return i},cubicLineIntersections:function(I,G,F,E,l,k,j,h,M,p,K,n){var u=[],N=[],D=p-n,z=K-M,y=M*(n-p)-p*(K-M),L=this.bezierCoeffs(I,G,F,E),J=this.bezierCoeffs(l,k,j,h),H,x,w,v,g,q,o,m;u[0]=D*L[0]+z*J[0];u[1]=D*L[1]+z*J[1];u[2]=D*L[2]+z*J[2];u[3]=D*L[3]+z*J[3]+y;x=this.cubicRoots(u);for(H=0;H1){continue}g=v*v;q=g*v;o=L[0]*q+L[1]*g+L[2]*v+L[3];m=J[0]*q+J[1]*g+J[2]*v+J[3];if((K-M)!==0){w=(o-M)/(K-M)}else{w=(m-p)/(n-p)}if(!(w<0||w>1)){N.push([o,m])}}return N},splitCubic:function(g,q,p,o,m){var j=m*m,n=m*j,i=m-1,h=i*i,k=i*h,l=n*o-3*j*i*p+3*m*h*q-k*g;return[[g,m*q-i*g,j*p-2*m*i*q+h*g,l],[l,j*o-2*m*i*p+h*q,m*o-i*p,o]]},cubicDimension:function(p,o,l,k){var j=3*(-p+3*(o-l)+k),i=6*(p-2*o+l),h=-3*(p-o),q,n,g=Math.min(p,k),m=Math.max(p,k),r;if(j===0){if(i===0){return[g,m]}else{q=-h/i;if(0=0){r=d(r);q=(r-i)/2/j;if(00){q-=r/j;if(0n[1]||x[1]s[1]||B[1]=0&&i<=1&&g>=0&&g<=1){return[k+i*(j-k),p+i*(o-p)]}return null},pointOnLine:function(j,m,h,l,g,n){var k,i;if(a(h-j)1){return false}return a(m+k*(l-m)-n)<4},pointOnCubic:function(w,u,s,r,l,k,h,g,p,o){var C=this,B=C.bezierCoeffs(w,u,s,r),A=C.bezierCoeffs(l,k,h,g),z,v,n,m,q;B[3]-=p;A[3]-=o;n=C.cubicRoots(B);m=C.cubicRoots(A);for(z=0;z=0&&q<=1&&a(q-m[v])<0.05){return true}}}return false}}});Ext.define("Ext.chart.series.Pie3D",{extend:"Ext.chart.series.Polar",requires:["Ext.chart.series.sprite.Pie3DPart","Ext.draw.PathUtil"],type:"pie3d",seriesType:"pie3d",alias:"series.pie3d",isPie3D:true,config:{rect:[0,0,0,0],thickness:35,distortion:0.5,donut:false,hidden:[],highlightCfg:{margin:20},shadow:false},rotationOffset:-Math.PI/2,setField:function(a){return this.setXField(a)},getField:function(){return this.getXField()},updateRotation:function(a){this.setStyle({baseRotation:a+this.rotationOffset});this.doUpdateStyles()},updateDistortion:function(){this.setRadius()},updateThickness:function(){this.setRadius()},updateColors:function(a){this.setSubStyle({baseColor:a})},applyShadow:function(a){if(a===true){a={shadowColor:"rgba(0,0,0,0.8)",shadowBlur:30}}else{if(!Ext.isObject(a)){a={shadowColor:Ext.draw.Color.RGBA_NONE}}}return a},updateShadow:function(g){var e=this,f=e.getSprites(),d=e.spritesPerSlice,c=f&&f.length,b,a;for(b=1;b=s.length){s[r]=false}}s.length=d;if(c===0){return}h=2*Math.PI/c;for(r=0;ra/2){return a/(f.getDistortion()*2)}else{return g}},getSprites:function(){var y=this,e=y.getStore();if(!e){return[]}var n=y.getChart(),p=y.getSurface(),t=e.getData().items,l=y.spritesPerSlice,a=t.length,v=y.getAnimation()||n&&n.getAnimation(),x=y.getCenter(),w=y.getOffsetX(),u=y.getOffsetY(),b=y.getRadius(),q=y.getRotation(),d=y.getHighlight(),c={centerX:x[0]+w,centerY:x[1]+u-y.getThickness()/2,endRho:b,startRho:b*y.getDonut()/100,thickness:y.getThickness(),distortion:y.getDistortion()},k=y.sprites,h=y.getLabel(),f=h.getTemplate(),m,g,o,s,r;for(s=0;s=0;c--){a=b.get(c);d=a.hitTestEvent(f);if(d){return d}}return null},handleEvent:function(f){var d=this,b=d.drawContainer,g=f.type in d.mouseMoveEvents,a=d.lastSprite,c;if(g&&!d.hasSpriteMouseMoveListeners()){return}c=d.hitTestEvent(f);if(g&&!Ext.Object.equals(c,a)){if(a){b.fireEvent("spritemouseout",a,f)}if(c){b.fireEvent("spritemouseover",c,f)}}if(c){b.fireEvent("sprite"+f.type,c,f)}d.lastSprite=c}});Ext.define("Ext.chart.TipSurface",{extend:"Ext.draw.Container",spriteArray:false,renderFirst:true,constructor:function(a){this.callParent([a]);if(a.sprites){this.spriteArray=[].concat(a.sprites);delete a.sprites}},onRender:function(){var c=this,b=0,a=0,d,e;this.callParent(arguments);e=c.spriteArray;if(c.renderFirst&&e){c.renderFirst=false;for(a=e.length;b 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"); - }, - - format_duration_long: function(ut) { - - var days = Math.floor(ut / 86400); - ut -= days*86400; - var hours = Math.floor(ut / 3600); - ut -= hours*3600; - var mins = Math.floor(ut / 60); - ut -= mins*60; - - var hours_str = '00' + hours.toString(); - hours_str = hours_str.substr(hours_str.length - 2); - var mins_str = "00" + mins.toString(); - mins_str = mins_str.substr(mins_str.length - 2); - var ut_str = "00" + ut.toString(); - ut_str = ut_str.substr(ut_str.length - 2); - - if (days) { - var 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; } - - var tm = new Ext.util.TextMetrics(); - var 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; - } - return (Proxmox.UserName !== '') && Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name); - }, - - authClear: function() { - if (Proxmox.LoggedOut) { - return undefined; - } - 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) { - var el = comp.el; - if (!el) { - return; - } - if (!msg) { - el.unmask(); - } else { - if (msg === true) { - el.mask(gettext("Loading...")); - } else { - el.mask(msg); - } - } - }, - - monStoreErrors: function(me, store, clearMaskBeforeLoad) { - if (clearMaskBeforeLoad) { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - Proxmox.Utils.setErrorMask(me, false); - }); - } else { - me.mon(store, 'beforeload', function(s, operation, eOpts) { - if (!me.loadCount) { - me.loadCount = 0; // make sure it is numeric - Proxmox.Utils.setErrorMask(me, true); - } - }); - } - - // only works with 'proxmox' proxy - me.mon(store.proxy, 'afterload', function(proxy, request, success) { - me.loadCount++; - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - - var msg; - /*jslint nomen: true */ - var operation = request._operation; - var error = operation.getError(); - if (error.statusText) { - msg = error.statusText + ' (' + error.status + ')'; - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - extractRequestError: function(result, verbose) { - var 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) { - - var newopts = Ext.apply({ - waitMsg: gettext('Please wait...') - }, reqOpts); - - if (!newopts.url.match(/^\/api2/)) { - newopts.url = '/api2/extjs' + newopts.url; - } - delete newopts.callback; - - var 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); - } - } - var 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) {} - var 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); - - var 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', - //waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var 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)) { - Ext.Object.each(data, function(name, val) { - if (values.hasOwnProperty(name)) { - var 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; - } - }); - } - }, - - 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') ], - '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: ['VM/CT', gettext('Backup') ], - 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') ] - }, - - format_task_description: function(type, id) { - var farray = Proxmox.Utils.task_desc_table[type]; - var text; - if (!farray) { - text = type; - if (id) { - type += ' ' + id; - } - return text; - } - var prefix = farray[0]; - text = farray[1]; - if (prefix) { - return prefix + ' ' + id + ' - ' + text; - } - return text; - }, - - format_size: function(size) { - /*jslint confusion: true */ - - var units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; - var num = 0; - - while (size >= 1024 && ((num++)+1) < units.length) { - size = size / 1024; - } - - return size.toFixed((num > 0)?2:0) + " " + units[num] + "B"; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - render_uptime: function(value) { - - var uptime = value; - - if (uptime === undefined) { - return ''; - } - - if (uptime <= 0) { - return '-'; - } - - return Proxmox.Utils.format_duration_long(uptime); - }, - - parse_task_upid: function(upid) { - var task = {}; - - var 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_timestamp: function(value, metaData, record, rowIndex, colIndex, store) { - var servertime = new Date(value * 1000); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }, - - get_help_info: function(section) { - var helpMap; - if (typeof proxmoxOnlineHelpInfo !== 'undefined') { - helpMap = proxmoxOnlineHelpInfo; - } else if (typeof pveOnlineHelpInfo !== 'undefined') { - // be backward compatible with older pve-doc-generators - helpMap = pveOnlineHelpInfo; - } else { - throw "no global OnlineHelpInfo map declared"; - } - - return helpMap[section]; - }, - - get_help_link: function(section) { - var info = Proxmox.Utils.get_help_info(section); - if (!info) { - return; - } - - return window.location.origin + info.link; - }, - - openXtermJsViewer: function(vmtype, vmid, nodename, vmname, cmd) { - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - xtermjs: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - cmd: cmd, - - }); - var 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() { - var me = this; - Ext.apply(me, me.utilities); - - var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])"; - var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")"; - var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})"; - var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")"; - var IPV4_CIDR_MASK = "([0-9]{1,2})"; - var 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 + "$"); - - var 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 + ")?::" + ")" + ")" + - ")"; - - 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 + ")$"); - - var 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 = new RegExp('^vlan(\\d+)'); - me.VlanInterface_match = new RegExp('(\\w+)\\.(\\d+)'); - } -}); -// ExtJS related things - - // do not send '_dc' parameter -Ext.Ajax.disableCaching = false; - -// FIXME: HACK! Makes scrolling in number spinner work again. fixed in ExtJS >= 6.1 -if (Ext.isFirefox) { - Ext.$eventNameMap.DOMMouseScroll = 'DOMMouseScroll'; -} - -// 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] <= 128); - }, - IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "
" + gettext('Valid CIDR Range') + ': 8-128', - 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\.:]/, - - IP64CIDRAddress: function(v) { - var result = Proxmox.Utils.IP64_cidr_match.exec(v); - if (result === null) { - return false; - } - if (result[1] !== undefined) { - return result[1] >= 8 && result[1] <= 128; - } else if (result[2] !== undefined) { - return result[2] >= 8 && result[2] <= 32; - } else { - return false; - } - }, - IP64CIDRAddressText: gettext('Example') + ': 192.168.1.1/24 2001:DB8::42/64', - IP64CIDRAddressMask: /[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', - - MacPrefix: function(v) { - return (/^[a-f0-9][02468ace](?::[a-f0-9]{2}){0,2}:?$/i).test(v); - }, - MacPrefixMask: /[a-fA-F0-9:]/, - MacPrefixText: gettext('Example') + ': 02:8f - ' + gettext('only unicast addresses are allowed'), - - BridgeName: function(v) { - return (/^vmbr\d{1,4}$/).test(v); - }, - VlanName: function(v) { - if (Proxmox.Utils.VlanInterface_match.test(v)) { - return true; - } else if (Proxmox.Utils.Vlan_match.test(v)) { - return true; - } - return true; - }, - 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'", - - 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", - - DnsOrIp: function(v) { - if (!Proxmox.Utils.DnsName_match.test(v) && - !Proxmox.Utils.IP64_match.test(v)) { - return false; - } - - return true; - }, - DnsOrIpText: gettext('Not a valid DNS name or IP address.'), - - 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'), - - password: function(val, field) { - if (field.initialPassField) { - var pwd = field.up('form').down( - '[name=' + field.initialPassField + ']'); - return (val == pwd.getValue()); - } - return true; - }, - - passwordText: gettext('Passwords do not match') -}); - -// Firefox 52+ Touchscreen bug -// see https://www.sencha.com/forum/showthread.php?336762-Examples-don-t-work-in-Firefox-52-touchscreen/page2 -// and https://bugzilla.proxmox.com/show_bug.cgi?id=1223 -Ext.define('EXTJS_23846.Element', { - override: 'Ext.dom.Element' -}, function(Element) { - var supports = Ext.supports, - proto = Element.prototype, - eventMap = proto.eventMap, - additiveEvents = proto.additiveEvents; - - if (Ext.os.is.Desktop && supports.TouchEvents && !supports.PointerEvents) { - eventMap.touchstart = 'mousedown'; - eventMap.touchmove = 'mousemove'; - eventMap.touchend = 'mouseup'; - eventMap.touchcancel = 'mouseup'; - - additiveEvents.mousedown = 'mousedown'; - additiveEvents.mousemove = 'mousemove'; - additiveEvents.mouseup = 'mouseup'; - additiveEvents.touchstart = 'touchstart'; - additiveEvents.touchmove = 'touchmove'; - additiveEvents.touchend = 'touchend'; - additiveEvents.touchcancel = 'touchcancel'; - - additiveEvents.pointerdown = 'mousedown'; - additiveEvents.pointermove = 'mousemove'; - additiveEvents.pointerup = 'mouseup'; - additiveEvents.pointercancel = 'mouseup'; - } -}); - -Ext.define('EXTJS_23846.Gesture', { - override: 'Ext.event.publisher.Gesture' -}, function(Gesture) { - var me = Gesture.instance; - - if (Ext.supports.TouchEvents && !Ext.isWebKit && Ext.os.is.Desktop) { - me.handledDomEvents.push('mousedown', 'mousemove', 'mouseup'); - me.registerEvents(); - } -}); - -Ext.define('EXTJS_18900.Pie', { - override: 'Ext.chart.series.Pie', - - // from 6.0.2 - betweenAngle: function (x, a, b) { - var pp = Math.PI * 2, - offset = this.rotationOffset; - - if (a === b) { - return false; - } - - if (!this.getClockwise()) { - x *= -1; - a *= -1; - b *= -1; - a -= offset; - b -= offset; - } else { - a += offset; - b += offset; - } - - x -= a; - b -= a; - - // Normalize, so that both x and b are in the [0,360) interval. - x %= pp; - b %= pp; - x += pp; - b += pp; - x %= pp; - b %= pp; - - // Because 360 * n angles will be normalized to 0, - // we need to treat b === 0 as a special case. - return x < b || b === 0; - }, -}); - -// 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 -// 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; - } -}); - -// '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('Proxmox.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 a grid/tree view, restoring the focus moves the view back to -// the previously focused item. Save scroll position before refocusing. -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('Proxmox.Datepicker', { - override: 'Ext.picker.Date', - hideMode: 'visibility' -}); - -// ExtJS 6.0.1 has no setSubmitValue() (although you find it in the docs). -// Note: this.submitValue is a boolean flag, whereas getSubmitValue() returns -// data to be submitted. -Ext.define('Proxmox.form.field.Text', { - override: 'Ext.form.field.Text', - - setSubmitValue: function(v) { - this.submitValue = v; - }, -}); - -// this should be fixed with ExtJS 6.0.2 -// make mousescrolling work in firefox in the containers overflowhandler -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. differs from the default onMouseWheel - // handler by using deltaY instead of wheelDeltaY and no normalizing, - // because it is already - onMouseWheelFirefox: function(e) { - e.stopEvent(); - var delta = e.browserEvent.deltaY || 0; - this.scrollBy(delta * this.wheelIncrement, false); - } - -}); - -// add '@' to the valid id -Ext.define('Proxmox.validIdReOverride', { - override: 'Ext.Component', - validIdRe: /^[a-z_][a-z0-9\-_\@]*$/i, -}); - -// 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); - } - } -}); -Ext.define('Proxmox.Mixin.CBind', { - extend: 'Ext.Mixin', - - mixinConfig: { - before: { - initComponent: 'cloneTemplates' - } - }, - - cloneTemplates: function() { - var me = this; - - if (typeof(me.cbindData) == "function") { - me.cbindData = me.cbindData(me.initialConfig); - } - me.cbindData = me.cbindData || {}; - - var getConfigValue = function(cname) { - - if (cname in me.initialConfig) { - return me.initialConfig[cname]; - } - if (cname in me.cbindData) { - return me.cbindData[cname]; - } - if (cname in me) { - return me[cname]; - } - throw "unable to get cbind data for '" + cname + "'"; - }; - - var applyCBind = function(obj) { - var cbind = obj.cbind, prop, cdata, cvalue, match, found; - if (!cbind) return; - - for (prop in cbind) { - cdata = cbind[prop]; - - found = false; - if (typeof cdata === 'function') { - obj[prop] = cdata(getConfigValue, prop); - found = true; - } else if (match = /^\{(!)?([a-z_][a-z0-9_]*)\}$/i.exec(cdata)) { - var cvalue = getConfigValue(match[2]); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else if (match = /^\{(!)?([a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)+)\}$/i.exec(cdata)) { - var keys = match[2].split('.'); - var cvalue = getConfigValue(keys.shift()); - keys.forEach(function(k) { - if (k in cvalue) { - cvalue = cvalue[k]; - } else { - throw "unable to get cbind data for '" + match[2] + "'"; - } - }); - if (match[1]) cvalue = !cvalue; - obj[prop] = cvalue; - found = true; - } else { - obj[prop] = cdata.replace(/{([a-z_][a-z0-9_]*)\}/ig, function(match, cname) { - var cvalue = getConfigValue(cname); - found = true; - return cvalue; - }); - } - if (!found) { - throw "unable to parse cbind template '" + cdata + "'"; - } - - } - }; - - if (me.cbind) { - applyCBind(me); - } - - var cloneTemplateArray = function(org) { - var copy, i, found, el, elcopy, arrayLength; - - arrayLength = org.length; - found = false; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - found = true; - break; - } - } - - if (!found) return org; // no need to copy - - copy = []; - for (i = 0; i < arrayLength; i++) { - el = org[i]; - if (el.constructor == Object && el.xtype) { - elcopy = cloneTemplateObject(el); - if (elcopy.cbind) { - applyCBind(elcopy); - } - copy.push(elcopy); - } else if (el.constructor == Array) { - elcopy = cloneTemplateArray(el); - copy.push(elcopy); - } else { - copy.push(el); - } - } - return copy; - }; - - var cloneTemplateObject = function(org) { - var res = {}, prop, el, copy; - for (prop in org) { - el = org[prop]; - if (el === undefined || el === null) { - res[prop] = el; - continue; - } - if (el.constructor == Object && el.xtype) { - copy = cloneTemplateObject(el); - if (copy.cbind) { - applyCBind(copy); - } - res[prop] = copy; - } else if (el.constructor == Array) { - copy = cloneTemplateArray(el); - res[prop] = copy; - } else { - res[prop] = el; - } - } - return res; - }; - - var condCloneProperties = function() { - var prop, el, i, tmp; - - for (prop in me) { - el = me[prop]; - if (el === undefined || el === null) continue; - if (typeof(el) === 'object' && el.constructor == Object) { - if (el.xtype && prop != 'config') { - me[prop] = cloneTemplateObject(el); - } - } else if (el.constructor == Array) { - tmp = cloneTemplateArray(el); - me[prop] = tmp; - } - } - }; - - condCloneProperties(); - } -}); -/* A reader to store a single JSON Object (hash) into a storage. - * Also accepts an array containing a single hash. - * - * So it can read: - * - * example1: {data1: "xyz", data2: "abc"} - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * example2: [ {data1: "xyz", data2: "abc"} ] - * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}] - * - * If you set 'readArray', the reader expexts the object as array: - * - * example3: [ { key: "data1", value: "xyz", p2: "cde" }, { key: "data2", value: "abc", p2: "efg" }] - * returns [{key: "data1", value: "xyz", p2: "cde}, {key: "data2", value: "abc", p2: "efg"}] - * - * Note: The records can contain additional properties (like 'p2' above) when you use 'readArray' - * - * Additional feature: specify allowed properties with default values with 'rows' object - * - * var rows = { - * memory: { - * required: true, - * defaultValue: 512 - * } - * } - * - */ - -Ext.define('Proxmox.data.reader.JsonObject', { - extend: 'Ext.data.reader.Json', - alias : 'reader.jsonobject', - - readArray: false, - - rows: undefined, - - constructor: function(config) { - var me = this; - - Ext.apply(me, config || {}); - - me.callParent([config]); - }, - - getResponseData: function(response) { - var me = this; - - var data = []; - try { - var result = Ext.decode(response.responseText); - // get our data items inside the server response - var root = result[me.getRootProperty()]; - - if (me.readArray) { - - var rec_hash = {}; - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - rec_hash[rec.key] = rec; - } - }); - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - var rec = rec_hash[key]; - if (Ext.isDefined(rec)) { - if (!Ext.isDefined(rec.value)) { - rec.value = rowdef.defaultValue; - } - data.push(rec); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue} ); - } else if (rowdef.required) { - data.push({key: key, value: undefined }); - } - }); - } else { - Ext.Array.each(root, function(rec) { - if (Ext.isDefined(rec.key)) { - data.push(rec); - } - }); - } - - } else { - - var org_root = root; - - if (Ext.isArray(org_root)) { - if (root.length == 1) { - root = org_root[0]; - } else { - root = {}; - } - } - - if (me.rows) { - Ext.Object.each(me.rows, function(key, rowdef) { - if (Ext.isDefined(root[key])) { - data.push({key: key, value: root[key]}); - } else if (Ext.isDefined(rowdef.defaultValue)) { - data.push({key: key, value: rowdef.defaultValue}); - } else if (rowdef.required) { - data.push({key: key, value: undefined}); - } - }); - } else { - Ext.Object.each(root, function(key, value) { - data.push({key: key, value: value }); - }); - } - } - } - catch (ex) { - Ext.Error.raise({ - response: response, - json: response.responseText, - parseError: ex, - msg: 'Unable to parse the JSON returned by the server: ' + ex.toString() - }); - } - - return data; - } -}); - -Ext.define('Proxmox.RestProxy', { - extend: 'Ext.data.RestProxy', - alias : 'proxy.proxmox', - - pageParam : null, - startParam: null, - limitParam: null, - groupParam: null, - sortParam: null, - filterParam: null, - noCache : false, - - afterRequest: function(request, success) { - this.fireEvent('afterload', this, request, success); - return; - }, - - constructor: function(config) { - - Ext.applyIf(config, { - reader: { - type: 'json', - rootProperty: config.root || 'data' - } - }); - - this.callParent([config]); - } -}, function() { - - Ext.define('KeyValue', { - extend: "Ext.data.Model", - fields: [ 'key', 'value' ], - idProperty: 'key' - }); - - Ext.define('KeyValuePendingDelete', { - extend: "Ext.data.Model", - fields: [ 'key', 'value', 'pending', 'delete' ], - idProperty: 'key' - }); - - Ext.define('proxmox-tasks', { - extend: 'Ext.data.Model', - fields: [ - { name: 'starttime', type : 'date', dateFormat: 'timestamp' }, - { name: 'endtime', type : 'date', dateFormat: 'timestamp' }, - { name: 'pid', type: 'int' }, - 'node', 'upid', 'user', 'status', 'type', 'id' - ], - idProperty: 'upid' - }); - - Ext.define('proxmox-cluster-log', { - extend: 'Ext.data.Model', - fields: [ - { name: 'uid' , type: 'int' }, - { name: 'time', type : 'date', dateFormat: 'timestamp' }, - { name: 'pri', type: 'int' }, - { name: 'pid', type: 'int' }, - 'node', 'user', 'tag', 'msg', - { - name: 'id', - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - // compute unique ID - return info.uid + ':' + info.node; - } - } - ], - idProperty: 'id' - }); - -}); -/* Extends the Ext.data.Store type - * with startUpdate() and stopUpdate() methods - * to refresh the store data in the background - * Components using this store directly will flicker - * due to the redisplay of the element ater 'config.interval' ms - * - * Note that you have to call yourself startUpdate() for the background load - * to begin - */ -Ext.define('Proxmox.data.UpdateStore', { - extend: 'Ext.data.Store', - alias: 'store.update', - - isStopped: true, - - autoStart: false, - - destroy: function() { - var me = this; - me.stopUpdate(); - me.callParent(); - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.interval) { - config.interval = 3000; - } - - if (!config.storeid) { - throw "no storeid specified"; - } - - var load_task = new Ext.util.DelayedTask(); - - var run_load_task = function() { - if (me.isStopped) { - return; - } - - if (Proxmox.Utils.authOK()) { - var start = new Date(); - me.load(function() { - var runtime = (new Date()) - start; - var interval = config.interval + runtime*2; - load_task.delay(interval, run_load_task); - }); - } else { - load_task.delay(200, run_load_task); - } - }; - - Ext.apply(config, { - startUpdate: function() { - me.isStopped = false; - // run_load_task(); this makes problems with chrome - load_task.delay(1, run_load_task); - }, - stopUpdate: function() { - me.isStopped = true; - load_task.cancel(); - } - }); - - me.callParent([config]); - - me.load_task = load_task; - - if (me.autoStart) { - me.startUpdate(); - } - } -}); -/* - * The DiffStore is a in-memory store acting as proxy between a real store - * instance and a component. - * Its purpose is to redisplay the component *only* if the data has been changed - * inside the real store, to avoid the annoying visual flickering of using - * the real store directly. - * - * Implementation: - * The DiffStore monitors via mon() the 'load' events sent by the real store. - * On each 'load' event, the DiffStore compares its own content with the target - * store (call to cond_add_item()) and then fires a 'refresh' event. - * The 'refresh' event will automatically trigger a view refresh on the component - * who binds to this store. - */ - -/* Config properties: - * rstore: the realstore which will autorefresh its content from the API - * Only works if rstore has a model and use 'idProperty' - * sortAfterUpdate: sort the diffstore before rendering the view - */ -Ext.define('Proxmox.data.DiffStore', { - extend: 'Ext.data.Store', - alias: 'store.diff', - - sortAfterUpdate: false, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.rstore) { - throw "no rstore specified"; - } - - if (!config.rstore.model) { - throw "no rstore model specified"; - } - - var rstore = config.rstore; - - Ext.apply(config, { - model: rstore.model, - proxy: { type: 'memory' } - }); - - me.callParent([config]); - - var first_load = true; - - var cond_add_item = function(data, id) { - var olditem = me.getById(id); - if (olditem) { - olditem.beginEdit(); - Ext.Array.each(me.model.prototype.fields, function(field) { - if (olditem.data[field.name] !== data[field.name]) { - olditem.set(field.name, data[field.name]); - } - }); - olditem.endEdit(true); - olditem.commit(); - } else { - var newrec = Ext.create(me.model, data); - var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length; - me.insert(pos, newrec); - } - }; - - var loadFn = function(s, records, success) { - - if (!success) { - return; - } - - me.suspendEvents(); - - // getSource returns null if data is not filtered - // if it is filtered it returns all records - var allItems = me.getData().getSource() || me.getData(); - - // remove vanished items - allItems.each(function(olditem) { - var item = rstore.getById(olditem.getId()); - if (!item) { - me.remove(olditem); - } - }); - - rstore.each(function(item) { - cond_add_item(item.data, item.getId()); - }); - - me.filter(); - - if (me.sortAfterUpdate) { - me.sort(); - } - - first_load = false; - - me.resumeEvents(); - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - }; - - if (rstore.isLoaded()) { - // if store is already loaded, - // insert items instantly - loadFn(rstore, [], true); - } - - me.mon(rstore, 'load', loadFn); - } -}); -/* This store encapsulates data items which are organized as an Array of key-values Objects - * ie data[0] contains something like {key: "keyboard", value: "da"} -* -* Designed to work with the KeyValue model and the JsonObject data reader -*/ -Ext.define('Proxmox.data.ObjectStore', { - extend: 'Proxmox.data.UpdateStore', - - getRecord: function() { - var me = this; - var record = Ext.create('Ext.data.Model'); - me.getData().each(function(item) { - record.set(item.data.key, item.data.value); - }); - record.commit(true); - return record; - }, - - constructor: function(config) { - var me = this; - - config = config || {}; - - if (!config.storeid) { - config.storeid = 'proxmox-store-' + (++Ext.idSeed); - } - - Ext.applyIf(config, { - model: 'KeyValue', - proxy: { - type: 'proxmox', - url: config.url, - extraParams: config.extraParams, - reader: { - type: 'jsonobject', - rows: config.rows, - readArray: config.readArray, - rootProperty: config.root || 'data' - } - } - }); - - me.callParent([config]); - } -}); -/* Extends the Proxmox.data.UpdateStore type - * - * - */ -Ext.define('Proxmox.data.RRDStore', { - extend: 'Proxmox.data.UpdateStore', - alias: 'store.proxmoxRRDStore', - - setRRDUrl: function(timeframe, cf) { - var me = this; - if (!timeframe) { - timeframe = me.timeframe; - } - - if (!cf) { - cf = me.cf; - } - - me.proxy.url = me.rrdurl + "?timeframe=" + timeframe + "&cf=" + cf; - }, - - proxy: { - type: 'proxmox' - }, - - timeframe: 'hour', - - cf: 'AVERAGE', - - constructor: function(config) { - var me = this; - - config = config || {}; - - // set default interval to 30seconds - if (!config.interval) { - config.interval = 30000; - } - - // set a new storeid - if (!config.storeid) { - config.storeid = 'rrdstore-' + (++Ext.idSeed); - } - - // rrdurl is required - if (!config.rrdurl) { - throw "no rrdurl specified"; - } - - var stateid = 'proxmoxRRDTypeSelection'; - var sp = Ext.state.Manager.getProvider(); - var stateinit = sp.get(stateid); - - if (stateinit) { - if(stateinit.timeframe !== me.timeframe || stateinit.cf !== me.rrdcffn){ - me.timeframe = stateinit.timeframe; - me.rrdcffn = stateinit.cf; - } - } - - me.callParent([config]); - - me.setRRDUrl(); - me.mon(sp, 'statechange', function(prov, key, state){ - if (key === stateid) { - if (state && state.id) { - if (state.timeframe !== me.timeframe || state.cf !== me.cf) { - me.timeframe = state.timeframe; - me.cf = state.cf; - me.setRRDUrl(); - me.reload(); - } - } - } - }); - } -}); -Ext.define('Timezone', { - extend: 'Ext.data.Model', - fields: ['zone'] -}); - -Ext.define('Proxmox.data.TimezoneStore', { - extend: 'Ext.data.Store', - model: 'Timezone', - data: [ - ['Africa/Abidjan'], - ['Africa/Accra'], - ['Africa/Addis_Ababa'], - ['Africa/Algiers'], - ['Africa/Asmara'], - ['Africa/Bamako'], - ['Africa/Bangui'], - ['Africa/Banjul'], - ['Africa/Bissau'], - ['Africa/Blantyre'], - ['Africa/Brazzaville'], - ['Africa/Bujumbura'], - ['Africa/Cairo'], - ['Africa/Casablanca'], - ['Africa/Ceuta'], - ['Africa/Conakry'], - ['Africa/Dakar'], - ['Africa/Dar_es_Salaam'], - ['Africa/Djibouti'], - ['Africa/Douala'], - ['Africa/El_Aaiun'], - ['Africa/Freetown'], - ['Africa/Gaborone'], - ['Africa/Harare'], - ['Africa/Johannesburg'], - ['Africa/Kampala'], - ['Africa/Khartoum'], - ['Africa/Kigali'], - ['Africa/Kinshasa'], - ['Africa/Lagos'], - ['Africa/Libreville'], - ['Africa/Lome'], - ['Africa/Luanda'], - ['Africa/Lubumbashi'], - ['Africa/Lusaka'], - ['Africa/Malabo'], - ['Africa/Maputo'], - ['Africa/Maseru'], - ['Africa/Mbabane'], - ['Africa/Mogadishu'], - ['Africa/Monrovia'], - ['Africa/Nairobi'], - ['Africa/Ndjamena'], - ['Africa/Niamey'], - ['Africa/Nouakchott'], - ['Africa/Ouagadougou'], - ['Africa/Porto-Novo'], - ['Africa/Sao_Tome'], - ['Africa/Tripoli'], - ['Africa/Tunis'], - ['Africa/Windhoek'], - ['America/Adak'], - ['America/Anchorage'], - ['America/Anguilla'], - ['America/Antigua'], - ['America/Araguaina'], - ['America/Argentina/Buenos_Aires'], - ['America/Argentina/Catamarca'], - ['America/Argentina/Cordoba'], - ['America/Argentina/Jujuy'], - ['America/Argentina/La_Rioja'], - ['America/Argentina/Mendoza'], - ['America/Argentina/Rio_Gallegos'], - ['America/Argentina/Salta'], - ['America/Argentina/San_Juan'], - ['America/Argentina/San_Luis'], - ['America/Argentina/Tucuman'], - ['America/Argentina/Ushuaia'], - ['America/Aruba'], - ['America/Asuncion'], - ['America/Atikokan'], - ['America/Bahia'], - ['America/Bahia_Banderas'], - ['America/Barbados'], - ['America/Belem'], - ['America/Belize'], - ['America/Blanc-Sablon'], - ['America/Boa_Vista'], - ['America/Bogota'], - ['America/Boise'], - ['America/Cambridge_Bay'], - ['America/Campo_Grande'], - ['America/Cancun'], - ['America/Caracas'], - ['America/Cayenne'], - ['America/Cayman'], - ['America/Chicago'], - ['America/Chihuahua'], - ['America/Costa_Rica'], - ['America/Cuiaba'], - ['America/Curacao'], - ['America/Danmarkshavn'], - ['America/Dawson'], - ['America/Dawson_Creek'], - ['America/Denver'], - ['America/Detroit'], - ['America/Dominica'], - ['America/Edmonton'], - ['America/Eirunepe'], - ['America/El_Salvador'], - ['America/Fortaleza'], - ['America/Glace_Bay'], - ['America/Godthab'], - ['America/Goose_Bay'], - ['America/Grand_Turk'], - ['America/Grenada'], - ['America/Guadeloupe'], - ['America/Guatemala'], - ['America/Guayaquil'], - ['America/Guyana'], - ['America/Halifax'], - ['America/Havana'], - ['America/Hermosillo'], - ['America/Indiana/Indianapolis'], - ['America/Indiana/Knox'], - ['America/Indiana/Marengo'], - ['America/Indiana/Petersburg'], - ['America/Indiana/Tell_City'], - ['America/Indiana/Vevay'], - ['America/Indiana/Vincennes'], - ['America/Indiana/Winamac'], - ['America/Inuvik'], - ['America/Iqaluit'], - ['America/Jamaica'], - ['America/Juneau'], - ['America/Kentucky/Louisville'], - ['America/Kentucky/Monticello'], - ['America/La_Paz'], - ['America/Lima'], - ['America/Los_Angeles'], - ['America/Maceio'], - ['America/Managua'], - ['America/Manaus'], - ['America/Marigot'], - ['America/Martinique'], - ['America/Matamoros'], - ['America/Mazatlan'], - ['America/Menominee'], - ['America/Merida'], - ['America/Mexico_City'], - ['America/Miquelon'], - ['America/Moncton'], - ['America/Monterrey'], - ['America/Montevideo'], - ['America/Montreal'], - ['America/Montserrat'], - ['America/Nassau'], - ['America/New_York'], - ['America/Nipigon'], - ['America/Nome'], - ['America/Noronha'], - ['America/North_Dakota/Center'], - ['America/North_Dakota/New_Salem'], - ['America/Ojinaga'], - ['America/Panama'], - ['America/Pangnirtung'], - ['America/Paramaribo'], - ['America/Phoenix'], - ['America/Port-au-Prince'], - ['America/Port_of_Spain'], - ['America/Porto_Velho'], - ['America/Puerto_Rico'], - ['America/Rainy_River'], - ['America/Rankin_Inlet'], - ['America/Recife'], - ['America/Regina'], - ['America/Resolute'], - ['America/Rio_Branco'], - ['America/Santa_Isabel'], - ['America/Santarem'], - ['America/Santiago'], - ['America/Santo_Domingo'], - ['America/Sao_Paulo'], - ['America/Scoresbysund'], - ['America/Shiprock'], - ['America/St_Barthelemy'], - ['America/St_Johns'], - ['America/St_Kitts'], - ['America/St_Lucia'], - ['America/St_Thomas'], - ['America/St_Vincent'], - ['America/Swift_Current'], - ['America/Tegucigalpa'], - ['America/Thule'], - ['America/Thunder_Bay'], - ['America/Tijuana'], - ['America/Toronto'], - ['America/Tortola'], - ['America/Vancouver'], - ['America/Whitehorse'], - ['America/Winnipeg'], - ['America/Yakutat'], - ['America/Yellowknife'], - ['Antarctica/Casey'], - ['Antarctica/Davis'], - ['Antarctica/DumontDUrville'], - ['Antarctica/Macquarie'], - ['Antarctica/Mawson'], - ['Antarctica/McMurdo'], - ['Antarctica/Palmer'], - ['Antarctica/Rothera'], - ['Antarctica/South_Pole'], - ['Antarctica/Syowa'], - ['Antarctica/Vostok'], - ['Arctic/Longyearbyen'], - ['Asia/Aden'], - ['Asia/Almaty'], - ['Asia/Amman'], - ['Asia/Anadyr'], - ['Asia/Aqtau'], - ['Asia/Aqtobe'], - ['Asia/Ashgabat'], - ['Asia/Baghdad'], - ['Asia/Bahrain'], - ['Asia/Baku'], - ['Asia/Bangkok'], - ['Asia/Beirut'], - ['Asia/Bishkek'], - ['Asia/Brunei'], - ['Asia/Choibalsan'], - ['Asia/Chongqing'], - ['Asia/Colombo'], - ['Asia/Damascus'], - ['Asia/Dhaka'], - ['Asia/Dili'], - ['Asia/Dubai'], - ['Asia/Dushanbe'], - ['Asia/Gaza'], - ['Asia/Harbin'], - ['Asia/Ho_Chi_Minh'], - ['Asia/Hong_Kong'], - ['Asia/Hovd'], - ['Asia/Irkutsk'], - ['Asia/Jakarta'], - ['Asia/Jayapura'], - ['Asia/Jerusalem'], - ['Asia/Kabul'], - ['Asia/Kamchatka'], - ['Asia/Karachi'], - ['Asia/Kashgar'], - ['Asia/Kathmandu'], - ['Asia/Kolkata'], - ['Asia/Krasnoyarsk'], - ['Asia/Kuala_Lumpur'], - ['Asia/Kuching'], - ['Asia/Kuwait'], - ['Asia/Macau'], - ['Asia/Magadan'], - ['Asia/Makassar'], - ['Asia/Manila'], - ['Asia/Muscat'], - ['Asia/Nicosia'], - ['Asia/Novokuznetsk'], - ['Asia/Novosibirsk'], - ['Asia/Omsk'], - ['Asia/Oral'], - ['Asia/Phnom_Penh'], - ['Asia/Pontianak'], - ['Asia/Pyongyang'], - ['Asia/Qatar'], - ['Asia/Qyzylorda'], - ['Asia/Rangoon'], - ['Asia/Riyadh'], - ['Asia/Sakhalin'], - ['Asia/Samarkand'], - ['Asia/Seoul'], - ['Asia/Shanghai'], - ['Asia/Singapore'], - ['Asia/Taipei'], - ['Asia/Tashkent'], - ['Asia/Tbilisi'], - ['Asia/Tehran'], - ['Asia/Thimphu'], - ['Asia/Tokyo'], - ['Asia/Ulaanbaatar'], - ['Asia/Urumqi'], - ['Asia/Vientiane'], - ['Asia/Vladivostok'], - ['Asia/Yakutsk'], - ['Asia/Yekaterinburg'], - ['Asia/Yerevan'], - ['Atlantic/Azores'], - ['Atlantic/Bermuda'], - ['Atlantic/Canary'], - ['Atlantic/Cape_Verde'], - ['Atlantic/Faroe'], - ['Atlantic/Madeira'], - ['Atlantic/Reykjavik'], - ['Atlantic/South_Georgia'], - ['Atlantic/St_Helena'], - ['Atlantic/Stanley'], - ['Australia/Adelaide'], - ['Australia/Brisbane'], - ['Australia/Broken_Hill'], - ['Australia/Currie'], - ['Australia/Darwin'], - ['Australia/Eucla'], - ['Australia/Hobart'], - ['Australia/Lindeman'], - ['Australia/Lord_Howe'], - ['Australia/Melbourne'], - ['Australia/Perth'], - ['Australia/Sydney'], - ['Europe/Amsterdam'], - ['Europe/Andorra'], - ['Europe/Athens'], - ['Europe/Belgrade'], - ['Europe/Berlin'], - ['Europe/Bratislava'], - ['Europe/Brussels'], - ['Europe/Bucharest'], - ['Europe/Budapest'], - ['Europe/Chisinau'], - ['Europe/Copenhagen'], - ['Europe/Dublin'], - ['Europe/Gibraltar'], - ['Europe/Guernsey'], - ['Europe/Helsinki'], - ['Europe/Isle_of_Man'], - ['Europe/Istanbul'], - ['Europe/Jersey'], - ['Europe/Kaliningrad'], - ['Europe/Kiev'], - ['Europe/Lisbon'], - ['Europe/Ljubljana'], - ['Europe/London'], - ['Europe/Luxembourg'], - ['Europe/Madrid'], - ['Europe/Malta'], - ['Europe/Mariehamn'], - ['Europe/Minsk'], - ['Europe/Monaco'], - ['Europe/Moscow'], - ['Europe/Oslo'], - ['Europe/Paris'], - ['Europe/Podgorica'], - ['Europe/Prague'], - ['Europe/Riga'], - ['Europe/Rome'], - ['Europe/Samara'], - ['Europe/San_Marino'], - ['Europe/Sarajevo'], - ['Europe/Simferopol'], - ['Europe/Skopje'], - ['Europe/Sofia'], - ['Europe/Stockholm'], - ['Europe/Tallinn'], - ['Europe/Tirane'], - ['Europe/Uzhgorod'], - ['Europe/Vaduz'], - ['Europe/Vatican'], - ['Europe/Vienna'], - ['Europe/Vilnius'], - ['Europe/Volgograd'], - ['Europe/Warsaw'], - ['Europe/Zagreb'], - ['Europe/Zaporozhye'], - ['Europe/Zurich'], - ['Indian/Antananarivo'], - ['Indian/Chagos'], - ['Indian/Christmas'], - ['Indian/Cocos'], - ['Indian/Comoro'], - ['Indian/Kerguelen'], - ['Indian/Mahe'], - ['Indian/Maldives'], - ['Indian/Mauritius'], - ['Indian/Mayotte'], - ['Indian/Reunion'], - ['Pacific/Apia'], - ['Pacific/Auckland'], - ['Pacific/Chatham'], - ['Pacific/Chuuk'], - ['Pacific/Easter'], - ['Pacific/Efate'], - ['Pacific/Enderbury'], - ['Pacific/Fakaofo'], - ['Pacific/Fiji'], - ['Pacific/Funafuti'], - ['Pacific/Galapagos'], - ['Pacific/Gambier'], - ['Pacific/Guadalcanal'], - ['Pacific/Guam'], - ['Pacific/Honolulu'], - ['Pacific/Johnston'], - ['Pacific/Kiritimati'], - ['Pacific/Kosrae'], - ['Pacific/Kwajalein'], - ['Pacific/Majuro'], - ['Pacific/Marquesas'], - ['Pacific/Midway'], - ['Pacific/Nauru'], - ['Pacific/Niue'], - ['Pacific/Norfolk'], - ['Pacific/Noumea'], - ['Pacific/Pago_Pago'], - ['Pacific/Palau'], - ['Pacific/Pitcairn'], - ['Pacific/Pohnpei'], - ['Pacific/Port_Moresby'], - ['Pacific/Rarotonga'], - ['Pacific/Saipan'], - ['Pacific/Tahiti'], - ['Pacific/Tarawa'], - ['Pacific/Tongatapu'], - ['Pacific/Wake'], - ['Pacific/Wallis'], - ['UTC'] - ] -}); -Ext.define('Proxmox.form.field.Integer',{ - extend: 'Ext.form.field.Number', - alias: 'widget.proxmoxintegerfield', - - config: { - deleteEmpty: false - }, - - allowDecimals: false, - allowExponential: false, - step: 1, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== undefined && val !== null && val !== '') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - } - -}); -Ext.define('Proxmox.form.field.Textfield', { - extend: 'Ext.form.field.Text', - alias: ['widget.proxmoxtextfield'], - - config: { - skipEmptyText: true, - - deleteEmpty: false, - }, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue && !me.isFileUpload()) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - getSubmitValue: function() { - var me = this; - - var value = this.processRawValue(this.getRawValue()); - if (value !== '') { - return value; - } - - return me.getSkipEmptyText() ? null: value; - }, - - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - this.validate(); - } -}); -Ext.define('Proxmox.DateTimeField', { - extend: 'Ext.form.FieldContainer', - xtype: 'promxoxDateTimeField', - - layout: 'hbox', - - referenceHolder: true, - - submitFormat: 'U', - - getValue: function() { - var me = this; - var d = me.lookupReference('dateentry').getValue(); - - if (d === undefined || d === null) { return null; } - - var t = me.lookupReference('timeentry').getValue(); - - if (t === undefined || t === null) { return null; } - - var offset = (t.getHours()*3600+t.getMinutes()*60)*1000; - - return new Date(d.getTime() + offset); - }, - - getSubmitValue: function() { - var me = this; - var format = me.submitFormat; - var value = me.getValue(); - - return value ? Ext.Date.format(value, format) : null; - }, - - items: [ - { - xtype: 'datefield', - editable: false, - reference: 'dateentry', - flex: 1, - format: 'Y-m-d' - }, - { - xtype: 'timefield', - reference: 'timeentry', - format: 'H:i', - width: 80, - value: '00:00', - increment: 60 - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - - var value = me.value || new Date(); - - me.lookupReference('dateentry').setValue(value); - me.lookupReference('timeentry').setValue(value); - - me.relayEvents(me.lookupReference('dateentry'), ['change']); - me.relayEvents(me.lookupReference('timeentry'), ['change']); - } -}); -Ext.define('Proxmox.form.Checkbox', { - extend: 'Ext.form.field.Checkbox', - alias: ['widget.proxmoxcheckbox'], - - config: { - defaultValue: undefined, - deleteDefaultValue: false, - deleteEmpty: false - }, - - inputValue: '1', - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null) { - data = {}; - if ((val == me.getDefaultValue()) && me.getDeleteDefaultValue()) { - data['delete'] = me.getName(); - } else { - data[me.getName()] = val; - } - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - // also accept integer 1 as true - setRawValue: function(value) { - var me = this; - - if (value === 1) { - me.callParent([true]); - } else { - me.callParent([value]); - } - } - -}); -/* Key-Value ComboBox - * - * config properties: - * comboItems: an array of Key - Value pairs - * deleteEmpty: if set to true (default), an empty value received from the - * comboBox will reset the property to its default value - */ -Ext.define('Proxmox.form.KVComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.proxmoxKVComboBox', - - config: { - deleteEmpty: true - }, - - comboItems: undefined, - displayField: 'value', - valueField: 'key', - queryMode: 'local', - - // overide framework function to implement deleteEmpty behaviour - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val !== null && val !== '' && val !== '__default__') { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - validator: function(val) { - var me = this; - - if (me.editable || val === null || val === '') { - return true; - } - - if (me.store.getCount() > 0) { - var values = me.multiSelect ? val.split(me.delimiter) : [val]; - var items = me.store.getData().collect('value', 'data'); - if (Ext.Array.every(values, function(value) { - return Ext.Array.contains(items, value); - })) { - return true; - } - } - - // returns a boolean or string - /*jslint confusion: true */ - return "value '" + val + "' not allowed!"; - }, - - initComponent: function() { - var me = this; - - me.store = Ext.create('Ext.data.ArrayStore', { - model: 'KeyValue', - data : me.comboItems - }); - - if (me.initialConfig.editable === undefined) { - me.editable = false; - } - - me.callParent(); - }, - - setComboItems: function(items) { - var me = this; - - me.getStore().setData(items); - } - -}); -Ext.define('Proxmox.form.LanguageSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'proxmoxLanguageSelector', - - comboItems: Proxmox.Utils.language_array() -}); -/* - * ComboGrid component: a ComboBox where the dropdown menu (the - * "Picker") is a Grid with Rows and Columns expects a listConfig - * object with a columns property roughly based on the GridPicker from - * https://www.sencha.com/forum/showthread.php?299909 - * -*/ - -Ext.define('Proxmox.form.ComboGrid', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxComboGrid'], - - // this value is used as default value after load() - preferredValue: undefined, - - // hack: allow to select empty value - // seems extjs does not allow that when 'editable == false' - onKeyUp: function(e, t) { - var me = this; - var key = e.getKey(); - - if (!me.editable && me.allowBlank && !me.multiSelect && - (key == e.BACKSPACE || key == e.DELETE)) { - me.setValue(''); - } - - me.callParent(arguments); - }, - - config: { - skipEmptyText: false, - notFoundIsValid: false, - deleteEmpty: false, - }, - - // needed to trigger onKeyUp etc. - enableKeyEvents: true, - - editable: false, - - triggers: { - clear: { - cls: 'pmx-clear-trigger', - weight: -1, - hidden: true, - handler: function() { - var me = this; - me.setValue(''); - } - } - }, - - setValue: function(value) { - var me = this; - let empty = Ext.isArray(value) ? !value.length : !value; - me.triggers.clear.setVisible(!empty && me.allowBlank); - return me.callParent([value]); - }, - - // override ExtJS method - // if the field has multiSelect enabled, the store is not loaded, and - // the displayfield == valuefield, it saves the rawvalue as an array - // but the getRawValue method is only defined in the textfield class - // (which has not to deal with arrays) an returns the string in the - // field (not an array) - // - // so if we have multiselect enabled, return the rawValue (which - // should be an array) and else we do callParent so - // it should not impact any other use of the class - getRawValue: function() { - var me = this; - if (me.multiSelect) { - return me.rawValue; - } else { - return me.callParent(); - } - }, - - getSubmitData: function() { - var me = this; - - let data = null; - if (!me.disabled && me.submitValue) { - let val = me.getSubmitValue(); - if (val !== null) { - data = {}; - data[me.getName()] = val; - } else if (me.getDeleteEmpty()) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - getSubmitValue: function() { - var me = this; - - var value = me.callParent(); - if (value !== '') { - return value; - } - - return me.getSkipEmptyText() ? null: value; - }, - - setAllowBlank: function(allowBlank) { - this.allowBlank = allowBlank; - this.validate(); - }, - -// override ExtJS protected method - onBindStore: function(store, initial) { - var me = this, - picker = me.picker, - extraKeySpec, - valueCollectionConfig; - - // We're being bound, not unbound... - if (store) { - // If store was created from a 2 dimensional array with generated field names 'field1' and 'field2' - if (store.autoCreated) { - me.queryMode = 'local'; - me.valueField = me.displayField = 'field1'; - if (!store.expanded) { - me.displayField = 'field2'; - } - - // displayTpl config will need regenerating with the autogenerated displayField name 'field1' - me.setDisplayTpl(null); - } - if (!Ext.isDefined(me.valueField)) { - me.valueField = me.displayField; - } - - // Add a byValue index to the store so that we can efficiently look up records by the value field - // when setValue passes string value(s). - // The two indices (Ext.util.CollectionKeys) are configured unique: false, so that if duplicate keys - // are found, they are all returned by the get call. - // This is so that findByText and findByValue are able to return the *FIRST* matching value. By default, - // if unique is true, CollectionKey keeps the *last* matching value. - extraKeySpec = { - byValue: { - rootProperty: 'data', - unique: false - } - }; - extraKeySpec.byValue.property = me.valueField; - store.setExtraKeys(extraKeySpec); - - if (me.displayField === me.valueField) { - store.byText = store.byValue; - } else { - extraKeySpec.byText = { - rootProperty: 'data', - unique: false - }; - extraKeySpec.byText.property = me.displayField; - store.setExtraKeys(extraKeySpec); - } - - // We hold a collection of the values which have been selected, keyed by this field's valueField. - // This collection also functions as the selected items collection for the BoundList's selection model - valueCollectionConfig = { - rootProperty: 'data', - extraKeys: { - byInternalId: { - property: 'internalId' - }, - byValue: { - property: me.valueField, - rootProperty: 'data' - } - }, - // Whenever this collection is changed by anyone, whether by this field adding to it, - // or the BoundList operating, we must refresh our value. - listeners: { - beginupdate: me.onValueCollectionBeginUpdate, - endupdate: me.onValueCollectionEndUpdate, - scope: me - } - }; - - // This becomes our collection of selected records for the Field. - me.valueCollection = new Ext.util.Collection(valueCollectionConfig); - - // We use the selected Collection as our value collection and the basis - // for rendering the tag list. - - //proxmox override: since the picker is represented by a grid panel, - // we changed here the selection to RowModel - me.pickerSelectionModel = new Ext.selection.RowModel({ - mode: me.multiSelect ? 'SIMPLE' : 'SINGLE', - // There are situations when a row is selected on mousedown but then the mouse is dragged to another row - // and released. In these situations, the event target for the click event won't be the row where the mouse - // was released but the boundview. The view will then determine that it should fire a container click, and - // the DataViewModel will then deselect all prior selections. Setting `deselectOnContainerClick` here will - // prevent the model from deselecting. - deselectOnContainerClick: false, - enableInitialSelection: false, - pruneRemoved: false, - selected: me.valueCollection, - store: store, - listeners: { - scope: me, - lastselectedchanged: me.updateBindSelection - } - }); - - if (!initial) { - me.resetToDefault(); - } - - if (picker) { - picker.setSelectionModel(me.pickerSelectionModel); - if (picker.getStore() !== store) { - picker.bindStore(store); - } - } - } - }, - - // copied from ComboBox - createPicker: function() { - var me = this; - var picker; - - var pickerCfg = Ext.apply({ - // proxmox overrides: display a grid for selection - xtype: 'gridpanel', - id: me.pickerId, - pickerField: me, - floating: true, - hidden: true, - store: me.store, - displayField: me.displayField, - preserveScrollOnRefresh: true, - pageSize: me.pageSize, - tpl: me.tpl, - selModel: me.pickerSelectionModel, - focusOnToFront: false - }, me.listConfig, me.defaultListConfig); - - picker = me.picker || Ext.widget(pickerCfg); - - if (picker.getStore() !== me.store) { - picker.bindStore(me.store); - } - - if (me.pageSize) { - picker.pagingToolbar.on('beforechange', me.onPageChange, me); - } - - // proxmox overrides: pass missing method in gridPanel to its view - picker.refresh = function() { - picker.getSelectionModel().select(me.valueCollection.getRange()); - picker.getView().refresh(); - }; - picker.getNodeByRecord = function() { - picker.getView().getNodeByRecord(arguments); - }; - - // We limit the height of the picker to fit in the space above - // or below this field unless the picker has its own ideas about that. - if (!picker.initialConfig.maxHeight) { - picker.on({ - beforeshow: me.onBeforePickerShow, - scope: me - }); - } - picker.getSelectionModel().on({ - beforeselect: me.onBeforeSelect, - beforedeselect: me.onBeforeDeselect, - focuschange: me.onFocusChange, - selectionChange: function (sm, selectedRecords) { - var me = this; - if (selectedRecords.length) { - me.setValue(selectedRecords); - me.fireEvent('select', me, selectedRecords); - } - }, - scope: me - }); - - // hack for extjs6 - // when the clicked item is the same as the previously selected, - // it does not select the item - // instead we hide the picker - if (!me.multiSelect) { - picker.on('itemclick', function (sm,record) { - if (picker.getSelection()[0] === record) { - picker.hide(); - } - }); - } - - // when our store is not yet loaded, we increase - // the height of the gridpanel, so that we can see - // the loading mask - // - // we save the minheight to reset it after the load - picker.on('show', function() { - if (me.enableLoadMask) { - me.savedMinHeight = picker.getMinHeight(); - picker.setMinHeight(100); - } - }); - - picker.getNavigationModel().navigateOnSpace = false; - - return picker; - }, - - clearLocalFilter: function() { - var me = this, - filter = me.queryFilter; - - if (filter) { - me.queryFilter = null; - me.changingFilters = true; - me.store.removeFilter(filter, true); - me.changingFilters = false; - } - }, - - isValueInStore: function(value) { - var me = this; - var store = me.store; - var found = false; - - if (!store) { - return found; - } - - // Make sure the current filter is removed before checking the store - // to prevent false negative results when iterating over a filtered store. - // All store.find*() method's operate on the filtered store. - if (me.queryFilter && me.queryMode === 'local' && me.clearFilterOnBlur) { - me.clearLocalFilter(); - } - - if (Ext.isArray(value)) { - Ext.Array.each(value, function(v) { - if (store.findRecord(me.valueField, v)) { - found = true; - return false; // break - } - }); - } else { - found = !!store.findRecord(me.valueField, value); - } - - return found; - }, - - validator: function (value) { - var me = this; - - if (!value) { - return true; // handled later by allowEmpty in the getErrors call chain - } - - // we normally get here the displayField as value, but if a valueField - // is configured we need to get the "actual" value, to ensure it is in - // the store. Below check is copied from ExtJS 6.0.2 ComboBox source - // - // we also have to get the 'real' value if the we have a mulitSelect - // Field but got a non array value - if ((me.valueField && me.valueField !== me.displayField) || - (me.multiSelect && !Ext.isArray(value))) { - value = me.getValue(); - } - - if (!(me.notFoundIsValid || me.isValueInStore(value))) { - return gettext('Invalid Value'); - } - - return true; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - queryMode: 'local', - matchFieldWidth: false - }); - - Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug - - Ext.applyIf(me.listConfig, { width: 400 }); - - me.callParent(); - - // Create the picker at an early stage, so it is available to store the previous selection - if (!me.picker) { - me.createPicker(); - } - - if (me.editable) { - // The trigger.picker causes first a focus event on the field then - // toggles the selection picker. Thus skip expanding in this case, - // else our focus listner expands and the picker.trigger then - // collapses it directly afterwards. - Ext.override(me.triggers.picker, { - onMouseDown : function (e) { - // copied "should we focus" check from Ext.form.trigger.Trigger - if (e.pointerType !== 'touch' && !this.field.owns(Ext.Element.getActiveElement())) { - me.skip_expand_on_focus = true; - } - this.callParent(arguments); - } - }); - - me.on("focus", function(me) { - if (!me.isExpanded && !me.skip_expand_on_focus) { - me.expand(); - } - me.skip_expand_on_focus = false; - }); - } - - me.mon(me.store, 'beforeload', function() { - if (!me.isDisabled()) { - me.enableLoadMask = true; - } - }); - - // hack: autoSelect does not work - me.mon(me.store, 'load', function(store, r, success, o) { - if (success) { - me.clearInvalid(); - - if (me.enableLoadMask) { - delete me.enableLoadMask; - - // if the picker exists, - // we reset its minheight to the saved var/0 - // we have to update the layout, otherwise the height - // gets not recalculated - if (me.picker) { - me.picker.setMinHeight(me.savedMinHeight || 0); - delete me.savedMinHeight; - me.picker.updateLayout(); - } - } - - var def = me.getValue() || me.preferredValue; - if (def) { - me.setValue(def, true); // sync with grid - } - var found = false; - if (def) { - found = me.isValueInStore(def); - } - - if (!found) { - var rec = me.store.first(); - if (me.autoSelect && rec && rec.data) { - def = rec.data[me.valueField]; - me.setValue(def, true); - } else if (!me.allowBlank && !(Ext.isArray(def) ? def.length : def)) { - me.setValue(def); - if (!me.notFoundIsValid) { - me.markInvalid(me.blankText); - } - } - } - } - }); - } -}); -Ext.define('Proxmox.form.RRDTypeSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.proxmoxRRDTypeSelector'], - - displayField: 'text', - valueField: 'id', - editable: false, - queryMode: 'local', - value: 'hour', - stateEvents: [ 'select' ], - stateful: true, - stateId: 'proxmoxRRDTypeSelection', - store: { - type: 'array', - fields: [ 'id', 'timeframe', 'cf', 'text' ], - data : [ - [ 'hour', 'hour', 'AVERAGE', - gettext('Hour') + ' (' + gettext('average') +')' ], - [ 'hourmax', 'hour', 'MAX', - gettext('Hour') + ' (' + gettext('maximum') + ')' ], - [ 'day', 'day', 'AVERAGE', - gettext('Day') + ' (' + gettext('average') + ')' ], - [ 'daymax', 'day', 'MAX', - gettext('Day') + ' (' + gettext('maximum') + ')' ], - [ 'week', 'week', 'AVERAGE', - gettext('Week') + ' (' + gettext('average') + ')' ], - [ 'weekmax', 'week', 'MAX', - gettext('Week') + ' (' + gettext('maximum') + ')' ], - [ 'month', 'month', 'AVERAGE', - gettext('Month') + ' (' + gettext('average') + ')' ], - [ 'monthmax', 'month', 'MAX', - gettext('Month') + ' (' + gettext('maximum') + ')' ], - [ 'year', 'year', 'AVERAGE', - gettext('Year') + ' (' + gettext('average') + ')' ], - [ 'yearmax', 'year', 'MAX', - gettext('Year') + ' (' + gettext('maximum') + ')' ] - ] - }, - // save current selection in the state Provider so RRDView can read it - getState: function() { - var ind = this.getStore().findExact('id', this.getValue()); - var rec = this.getStore().getAt(ind); - if (!rec) { - return; - } - return { - id: rec.data.id, - timeframe: rec.data.timeframe, - cf: rec.data.cf - }; - }, - // set selection based on last saved state - applyState : function(state) { - if (state && state.id) { - this.setValue(state.id); - } - } -}); -Ext.define('Proxmox.form.BondModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondModeSelector'], - - openvswitch: false, - - initComponent: function() { - var me = this; - - if (me.openvswitch) { - me.comboItems = Proxmox.Utils.bond_mode_array([ - 'active-backup', - 'balance-slb', - 'lacp-balance-slb', - 'lacp-balance-tcp', - ]); - } else { - me.comboItems = Proxmox.Utils.bond_mode_array([ - 'balance-rr', - 'active-backup', - 'balance-xor', - 'broadcast', - '802.3ad', - 'balance-tlb', - 'balance-alb', - ]); - } - - me.callParent(); - } -}); - -Ext.define('Proxmox.form.BondPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.bondPolicySelector'], - comboItems: [ - ['layer2', 'layer2'], - ['layer2+3', 'layer2+3'], - ['layer3+4', 'layer3+4'] - ] -}); - -Ext.define('Proxmox.form.NetworkSelectorController', { - extend: 'Ext.app.ViewController', - alias: 'controller.proxmoxNetworkSelectorController', - - init: function(view) { - var me = this; - - if (!view.nodename) { - throw "missing custom view config: nodename"; - } - view.getStore().getProxy().setUrl('/api2/json/nodes/'+ view.nodename + '/network'); - } -}); - -Ext.define('Proxmox.data.NetworkSelector', { - extend: 'Ext.data.Model', - fields: [ - {name: 'active'}, - {name: 'cidr'}, - {name: 'cidr6'}, - {name: 'address'}, - {name: 'address6'}, - {name: 'comments'}, - {name: 'iface'}, - {name: 'slaves'}, - {name: 'type'} - ] -}); - -Ext.define('Proxmox.form.NetworkSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.proxmoxNetworkSelector', - - controller: 'proxmoxNetworkSelectorController', - - nodename: 'localhost', - setNodename: function(nodename) { - this.nodename = nodename; - var networkSelectorStore = this.getStore(); - networkSelectorStore.removeAll(); - // because of manual local copy of data for ip4/6 - this.getPicker().refresh(); - if (networkSelectorStore && typeof networkSelectorStore.getProxy === 'function') { - networkSelectorStore.getProxy().setUrl('/api2/json/nodes/'+ nodename + '/network'); - networkSelectorStore.load(); - } - }, - // set default value to empty array, else it inits it with - // null and after the store load it is an empty array, - // triggering dirtychange - value: [], - valueField: 'cidr', - displayField: 'cidr', - store: { - autoLoad: true, - model: 'Proxmox.data.NetworkSelector', - proxy: { - type: 'proxmox' - }, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ], - filters: [ - function(item) { - return item.data.cidr; - } - ], - listeners: { - load: function(store, records, successfull) { - - if (successfull) { - records.forEach(function(record) { - if (record.data.cidr6) { - let dest = (record.data.cidr) ? record.copy(null) : record; - dest.data.cidr = record.data.cidr6; - dest.data.address = record.data.address6; - delete record.data.cidr6; - dest.data.comments = record.data.comments6; - delete record.data.comments6; - store.add(dest); - } - }); - } - } - } - }, - listConfig: { - width: 600, - columns: [ - { - - header: gettext('CIDR'), - dataIndex: 'cidr', - hideable: false, - flex: 1 - }, - { - - header: gettext('IP'), - dataIndex: 'address', - hidden: true, - }, - { - header: gettext('Interface'), - width: 90, - dataIndex: 'iface' - }, - { - header: gettext('Active'), - renderer: Proxmox.Utils.format_boolean, - width: 60, - dataIndex: 'active' - }, - { - header: gettext('Type'), - width: 80, - hidden: true, - dataIndex: 'type' - }, - { - header: gettext('Comment'), - flex: 2, - dataIndex: 'comments' - } - ] - } -}); -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - */ -Ext.define('Proxmox.button.Button', { - extend: 'Ext.button.Button', - alias: 'widget.proxmoxButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - - // Note: me.realHandler may be a string (see named scopes) - var realHandler = me.handler; - - me.handler = function(button, event) { - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - message: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn !== 'yes') { - return; - } - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }); - } else { - Ext.callback(realHandler, me.scope, [button, event, rec], 0, me); - } - }; - } - - me.callParent(); - - var grid; - if (!me.selModel && me.selModel !== null) { - grid = me.up('grid'); - if (grid && grid.selModel) { - me.selModel = grid.selModel; - } - } - - if (me.waitMsgTarget === true) { - grid = me.up('grid'); - if (grid) { - me.waitMsgTarget = grid; - } else { - throw "unable to find waitMsgTarget"; - } - } - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); - - -Ext.define('Proxmox.button.StdRemoveButton', { - extend: 'Proxmox.button.Button', - alias: 'widget.proxmoxStdRemoveButton', - - text: gettext('Remove'), - - disabled: true, - - // time to wait for removal task to finish - delay: undefined, - - config: { - baseurl: undefined - }, - - getUrl: function(rec) { - var me = this; - - return me.baseurl + '/' + rec.getId(); - }, - - // also works with names scopes - callback: function(options, success, response) {}, - - getRecordName: function(rec) { return rec.getId() }, - - confirmMsg: function (rec) { - var me = this; - - var name = me.getRecordName(rec); - return Ext.String.format( - gettext('Are you sure you want to remove entry {0}'), - "'" + name + "'"); - }, - - handler: function(btn, event, rec) { - var me = this; - - var url = me.getUrl(rec); - - if (typeof me.delay !== 'undefined' && me .delay >= 0) { - url += "?delay=" + me.delay; - } - - Proxmox.Utils.API2Request({ - url: url, - method: 'DELETE', - waitMsgTarget: me.waitMsgTarget, - callback: function(options, success, response) { - Ext.callback(me.callback, me.scope, [options, success, response], 0, me); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -/* help button pointing to an online documentation - for components contained in a modal window -*/ -/*global - proxmoxOnlineHelpInfo -*/ -Ext.define('Proxmox.button.Help', { - extend: 'Ext.button.Button', - xtype: 'proxmoxHelpButton', - - text: gettext('Help'), - - // make help button less flashy by styling it like toolbar buttons - iconCls: ' x-btn-icon-el-default-toolbar-small fa fa-question-circle', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - - hidden: true, - - listenToGlobalEvent: true, - - controller: { - xclass: 'Ext.app.ViewController', - listen: { - global: { - proxmoxShowHelp: 'onProxmoxShowHelp', - proxmoxHideHelp: 'onProxmoxHideHelp' - } - }, - onProxmoxShowHelp: function(helpLink) { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.setOnlineHelp(helpLink); - me.show(); - } - }, - onProxmoxHideHelp: function() { - var me = this.getView(); - if (me.listenToGlobalEvent === true) { - me.hide(); - } - } - }, - - // this sets the link and the tooltip text - setOnlineHelp:function(blockid) { - var me = this; - - var info = Proxmox.Utils.get_help_info(blockid); - if (info) { - me.onlineHelp = blockid; - var title = info.title; - if (info.subtitle) { - title += ' - ' + info.subtitle; - } - me.setTooltip(title); - } - }, - - // helper to set the onlineHelp via a config object - setHelpConfig: function(config) { - var me = this; - me.setOnlineHelp(config.onlineHelp); - }, - - handler: function() { - var me = this; - var docsURI; - - if (me.onlineHelp) { - docsURI = Proxmox.Utils.get_help_link(me.onlineHelp); - } - - if (docsURI) { - window.open(docsURI); - } else { - Ext.Msg.alert(gettext('Help'), gettext('No Help available')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.callParent(); - - if (me.onlineHelp) { - me.setOnlineHelp(me.onlineHelp); // set tooltip - } - } -}); -/* Renders a list of key values objets - -mandatory config parameters: -rows: an object container where each propery is a key-value object we want to render - var rows = { - keyboard: { - header: gettext('Keyboard Layout'), - editor: 'Your.KeyboardEdit', - required: true - }, - -optional: -disabled: setting this parameter to true will disable selection and focus on the -proxmoxObjectGrid as well as greying out input elements. -Useful for a readonly tabular display - -*/ - -Ext.define('Proxmox.grid.ObjectGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.proxmoxObjectGrid'], - disabled: false, - hideHeaders: true, - - monStoreErrors: false, - - add_combobox_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxKVComboBox', - name: name, - comboItems: opts.comboItems, - value: opts.defaultValue, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_text_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxtextfield', - name: name, - deleteEmpty: opts.deleteEmpty ? true : false, - emptyText: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - vtype: opts.vtype, - fieldLabel: text - } - } - }; - }, - - add_boolean_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue || 0, - header: text, - renderer: opts.renderer || Proxmox.Utils.format_boolean, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxcheckbox', - name: name, - uncheckedValue: 0, - defaultValue: opts.defaultValue || 0, - checked: opts.defaultValue ? true : false, - deleteDefaultValue: opts.deleteDefaultValue ? true : false, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - add_integer_row: function(name, text, opts) { - var me = this; - - opts = opts || {} - me.rows = me.rows || {}; - - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: { - xtype: 'proxmoxWindowEdit', - subject: text, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - items: { - xtype: 'proxmoxintegerfield', - name: name, - minValue: opts.minValue, - maxValue: opts.maxValue, - emptyText: gettext('Default'), - deleteEmpty: opts.deleteEmpty ? true : false, - value: opts.defaultValue, - labelWidth: Proxmox.Utils.compute_min_label_width( - text, opts.labelWidth), - fieldLabel: text - } - } - }; - }, - - editorConfig: {}, // default config passed to editor - - run_editor: function() { - var me = this; - - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rows = me.rows; - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - var config; - if (Ext.isString(rowdef.editor)) { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - win = Ext.create(rowdef.editor, config); - } else { - config = Ext.apply({ - confid: rec.data.key, - }, me.editorConfig); - Ext.apply(config, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', me.reload, me); - }, - - reload: function() { - var me = this; - me.rstore.load(); - }, - - getObjectValue: function(key, defaultValue) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }, - - renderKey: function(key, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - return rowdef.header || key; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - - var renderer = rowdef.renderer; - if (renderer) { - return renderer(value, metaData, record, rowIndex, colIndex, store); - } - - return value; - }, - - listeners: { - itemkeydown: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER) { - this.pressedIndex = index; - } - }, - itemkeyup: function(view, record, item, index, e) { - if (e.getKey() === e.ENTER && index == this.pressedIndex) { - this.run_editor(); - } - - this.pressedIndex = undefined; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - var rstore = me.rstore; - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore, - sorters: [], - filters: [] - }); - - if (rows) { - Ext.Object.each(rows, function(key, rowdef) { - if (Ext.isDefined(rowdef.defaultValue)) { - store.add({ key: key, value: rowdef.defaultValue }); - } else if (rowdef.required) { - store.add({ key: key, value: undefined }); - } - }); - } - - if (me.sorterFn) { - store.sorters.add(Ext.create('Ext.util.Sorter', { - sorterFn: me.sorterFn - })); - } - - store.filters.add(Ext.create('Ext.util.Filter', { - filterFn: function(item) { - if (rows) { - var rowdef = rows[item.data.key]; - if (!rowdef || (rowdef.visible === false)) { - return false; - } - } - return true; - } - })); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.applyIf(me, { - store: store, - stateful: false, - columns: [ - { - header: gettext('Name'), - width: me.cwidth1 || 200, - dataIndex: 'key', - renderer: me.renderKey - }, - { - flex: 1, - header: gettext('Value'), - dataIndex: 'value', - renderer: me.renderValue - } - ] - }); - - me.callParent(); - - if (me.monStoreErrors) { - Proxmox.Utils.monStoreErrors(me, me.store); - } - } -}); -Ext.define('Proxmox.grid.PendingObjectGrid', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxPendingObjectGrid'], - - getObjectValue: function(key, defaultValue, pending) { - var me = this; - var rec = me.store.getById(key); - if (rec) { - var value = rec.data.value; - if (pending) { - if (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') { - value = rec.data.pending; - } else if (rec.data['delete'] === 1) { - value = defaultValue; - } - } - - if (Ext.isDefined(value) && (value !== '')) { - return value; - } else { - return defaultValue; - } - } - return defaultValue; - }, - - hasPendingChanges: function(key) { - var me = this; - var rows = me.rows; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var keys = rowdef.multiKey || [ key ]; - var pending = false; - - Ext.Array.each(keys, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && ( - (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') || - rec.data['delete'] === 1 - )) { - pending = true; - return false; // break - } - }); - - return pending; - }, - - renderValue: function(value, metaData, record, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var key = record.data.key; - var rowdef = (rows && rows[key]) ? rows[key] : {}; - var renderer = rowdef.renderer; - var current = ''; - var pendingdelete = ''; - var pending = ''; - - if (renderer) { - current = renderer(value, metaData, record, rowIndex, colIndex, store, false); - if (me.hasPendingChanges(key)) { - pending = renderer(record.data.pending, metaData, record, rowIndex, colIndex, store, true); - } - if (pending == current) { - pending = undefined; - } - } else { - current = value || ''; - pending = record.data.pending; - } - - if (record.data['delete']) { - var delete_all = true; - if (rowdef.multiKey) { - Ext.Array.each(rowdef.multiKey, function(k) { - var rec = me.store.getById(k); - if (rec && rec.data && rec.data['delete'] !== 1) { - delete_all = false; - return false; // break - } - }); - } - if (delete_all) { - pending = '
'+ current +'
'; - } - } - - if (pending) { - return current + '
' + pending + '
'; - } else { - return current; - } - }, - - initComponent : function() { - var me = this; - - var rows = me.rows; - - if (!me.rstore) { - if (!me.url) { - throw "no url specified"; - } - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - model: 'KeyValuePendingDelete', - readArray: true, - url: me.url, - interval: me.interval, - extraParams: me.extraParams, - rows: me.rows - }); - } - - me.callParent(); - } -}); -Ext.define('Proxmox.panel.InputPanel', { - extend: 'Ext.panel.Panel', - alias: ['widget.inputpanel'], - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - border: false, - - // override this with an URL to a relevant chapter of the pve manual - // setting this will display a help button in our parent panel - onlineHelp: undefined, - - // will be set if the inputpanel has advanced items - hasAdvanced: false, - - // if the panel has advanced items, - // this will determine if they are shown by default - showAdvanced: false, - - // overwrite this to modify submit data - onGetValues: function(values) { - return values; - }, - - getValues: function(dirtyOnly) { - var me = this; - - if (Ext.isFunction(me.onGetValues)) { - dirtyOnly = false; - } - - var values = {}; - - Ext.Array.each(me.query('[isFormField]'), function(field) { - if (!dirtyOnly || field.isDirty()) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - return me.onGetValues(values); - }, - - setAdvancedVisible: function(visible) { - var me = this; - var advItems = me.getComponent('advancedContainer'); - if (advItems) { - advItems.setVisible(visible); - } - }, - - setValues: function(values) { - var me = this; - - var form = me.up('form'); - - Ext.iterate(values, function(fieldId, val) { - var field = me.query('[isFormField][name=' + fieldId + ']')[0]; - if (field) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - }, - - initComponent: function() { - var me = this; - - var items; - - if (me.items) { - me.columns = 1; - items = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.items - } - ]; - me.items = undefined; - } else if (me.column4) { - me.columns = 4; - items = [ - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column2 - }, - { - columnWidth: 0.25, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column3 - }, - { - columnWidth: 0.25, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column4 - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else if (me.column1) { - me.columns = 2; - items = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.column1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.column2 || [] // allow empty column - } - ]; - if (me.columnB) { - items.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.columnB - }); - } - } else { - throw "unsupported config"; - } - - var advItems; - if (me.advancedItems) { - advItems = [ - { - columnWidth: 1, - layout: 'anchor', - items: me.advancedItems - } - ]; - me.advancedItems = undefined; - } else if (me.advancedColumn1) { - advItems = [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: me.advancedColumn1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: me.advancedColumn2 || [] // allow empty column - } - ]; - - me.advancedColumn1 = undefined; - me.advancedColumn2 = undefined; - - if (me.advancedColumnB) { - advItems.push({ - columnWidth: 1, - padding: '10 0 0 0', - layout: 'anchor', - items: me.advancedColumnB - }); - me.advancedColumnB = undefined; - } - } - - if (advItems) { - me.hasAdvanced = true; - advItems.unshift({ - columnWidth: 1, - xtype: 'box', - hidden: false, - border: true, - autoEl: { - tag: 'hr' - } - }); - items.push({ - columnWidth: 1, - xtype: 'container', - itemId: 'advancedContainer', - hidden: !me.showAdvanced, - layout: 'column', - defaults: { - border: false - }, - items: advItems - }); - } - - if (me.useFieldContainer) { - Ext.apply(me, { - layout: 'fit', - items: Ext.apply(me.useFieldContainer, { - layout: 'column', - defaultType: 'container', - items: items - }) - }); - } else { - Ext.apply(me, { - layout: 'column', - defaultType: 'container', - items: items - }); - } - - me.callParent(); - } -}); -/* - * Display log entries in a panel with scrollbar - * The log entries are automatically refreshed via a background task, - * with newest entries comming at the bottom - */ -Ext.define('Proxmox.panel.LogView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxLogView', - - pageSize: 500, - viewBuffer: 50, - lineHeight: 16, - - scrollToEnd: true, - - // callback for load failure, used for ceph - failCallback: undefined, - - controller: { - xclass: 'Ext.app.ViewController', - - updateParams: function() { - var me = this; - var viewModel = me.getViewModel(); - var since = viewModel.get('since'); - var until = viewModel.get('until'); - if (viewModel.get('hide_timespan')) { - return; - } - - if (since > until) { - Ext.Msg.alert('Error', 'Since date must be less equal than Until date.'); - return; - } - - viewModel.set('params.since', Ext.Date.format(since, 'Y-m-d')); - viewModel.set('params.until', Ext.Date.format(until, 'Y-m-d') + ' 23:59:59'); - me.getView().loadTask.delay(200); - }, - - scrollPosBottom: function() { - var view = this.getView(); - var pos = view.getScrollY(); - var maxPos = view.getScrollable().getMaxPosition().y; - return maxPos - pos; - }, - - updateView: function(text, first, total) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - var content = me.lookup('content'); - var data = viewModel.get('data'); - - if (first === data.first && total === data.total && text.length === data.textlen) { - return; // same content, skip setting and scrolling - } - viewModel.set('data', { - first: first, - total: total, - textlen: text.length - }); - - var scrollPos = me.scrollPosBottom(); - - content.update(text); - - if (view.scrollToEnd && scrollPos <= 0) { - // we use setTimeout to work around scroll handling on touchscreens - setTimeout(function() { view.scrollTo(0, Infinity); }, 10); - } - }, - - doLoad: function() { - var me = this; - if (me.running) { - me.requested = true; - return; - } - me.running = true; - var view = me.getView(); - var viewModel = me.getViewModel(); - Proxmox.Utils.API2Request({ - url: me.getView().url, - params: viewModel.get('params'), - method: 'GET', - success: function(response) { - Proxmox.Utils.setErrorMask(me, false); - var total = response.result.total; - var lines = new Array(); - var first = Infinity; - - Ext.Array.each(response.result.data, function(line) { - if (first > line.n) { - first = line.n; - } - lines[line.n - 1] = Ext.htmlEncode(line.t); - }); - - lines.length = total; - me.updateView(lines.join('
'), first - 1, total); - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - }, - failure: function(response) { - if (view.failCallback) { - view.failCallback(response); - } else { - var msg = response.htmlStatus; - Proxmox.Utils.setErrorMask(me, msg); - } - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - } - }); - }, - - onScroll: function(x, y) { - var me = this; - var view = me.getView(); - var viewModel = me.getViewModel(); - - var lineHeight = view.lineHeight; - var line = view.getScrollY()/lineHeight; - var start = viewModel.get('params.start'); - var limit = viewModel.get('params.limit'); - var viewLines = view.getHeight()/lineHeight; - - var viewStart = Math.max(parseInt(line - 1 - view.viewBuffer, 10), 0); - var viewEnd = parseInt(line + viewLines + 1 + view.viewBuffer, 10); - - if (viewStart < start || viewEnd > (start+limit)) { - viewModel.set('params.start', - Math.max(parseInt(line - limit/2 + 10, 10), 0)); - view.loadTask.delay(200); - } - }, - - init: function(view) { - var me = this; - - if (!view.url) { - throw "no url specified"; - } - - var viewModel = this.getViewModel(); - var since = new Date(); - since.setDate(since.getDate() - 3); - viewModel.set('until', new Date()); - viewModel.set('since', since); - viewModel.set('params.limit', view.pageSize); - viewModel.set('hide_timespan', !view.log_select_timespan); - me.lookup('content').setStyle('line-height', view.lineHeight + 'px'); - - view.loadTask = new Ext.util.DelayedTask(me.doLoad, me); - - me.updateParams(); - view.task = Ext.TaskManager.start({ - run: function() { - if (!view.isVisible() || !view.scrollToEnd) { - return; - } - - if (me.scrollPosBottom() <= 1) { - view.loadTask.delay(200); - } - }, - interval: 1000 - }); - } - }, - - onDestroy: function() { - var me = this; - me.loadTask.cancel(); - Ext.TaskManager.stop(me.task); - }, - - // for user to initiate a load from outside - requestUpdate: function() { - var me = this; - me.loadTask.delay(200); - }, - - viewModel: { - data: { - until: null, - since: null, - hide_timespan: false, - data: { - start: 0, - total: 0, - textlen: 0 - }, - params: { - start: 0, - limit: 500, - } - } - }, - - layout: 'auto', - bodyPadding: 5, - scrollable: { - x: 'auto', - y: 'auto', - listeners: { - // we have to have this here, since we cannot listen to events - // of the scroller in the viewcontroller (extjs bug?), nor does - // the panel have a 'scroll' event' - scroll: { - fn: function(scroller, x, y) { - var controller = this.component.getController(); - if (controller) { // on destroy, controller can be gone - controller.onScroll(x,y); - } - }, - buffer: 200 - }, - } - }, - - tbar: { - bind: { - hidden: '{hide_timespan}' - }, - items: [ - '->', - 'Since: ', - { - xtype: 'datefield', - name: 'since_date', - reference: 'since', - format: 'Y-m-d', - bind: { - value: '{since}', - maxValue: '{until}' - } - }, - 'Until: ', - { - xtype: 'datefield', - name: 'until_date', - reference: 'until', - format: 'Y-m-d', - bind: { - value: '{until}', - minValue: '{since}' - } - }, - { - xtype: 'button', - text: 'Update', - handler: 'updateParams' - } - ], - }, - - items: [ - { - xtype: 'box', - reference: 'content', - style: { - font: 'normal 11px tahoma, arial, verdana, sans-serif', - 'white-space': 'pre' - }, - } - ] -}); -/* - * Display log entries in a panel with scrollbar - * The log entries are automatically refreshed via a background task, - * with newest entries comming at the bottom - */ -Ext.define('Proxmox.panel.JournalView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxJournalView', - - numEntries: 500, - lineHeight: 16, - - scrollToEnd: true, - - controller: { - xclass: 'Ext.app.ViewController', - - updateParams: function() { - var me = this; - var viewModel = me.getViewModel(); - var since = viewModel.get('since'); - var until = viewModel.get('until'); - - since.setHours(0, 0, 0, 0); - until.setHours(0, 0, 0, 0); - until.setDate(until.getDate()+1); - - me.getView().loadTask.delay(200, undefined, undefined, [ - false, - false, - Ext.Date.format(since, "U"), - Ext.Date.format(until, "U") - ]); - }, - - scrollPosBottom: function() { - var view = this.getView(); - var pos = view.getScrollY(); - var maxPos = view.getScrollable().getMaxPosition().y; - return maxPos - pos; - }, - - scrollPosTop: function() { - var view = this.getView(); - return view.getScrollY(); - }, - - updateScroll: function(livemode, num, scrollPos, scrollPosTop) { - var me = this; - var view = me.getView(); - - if (!livemode) { - setTimeout(function() { view.scrollTo(0, 0); }, 10); - } else if (view.scrollToEnd && scrollPos <= 0) { - setTimeout(function() { view.scrollTo(0, Infinity); }, 10); - } else if (!view.scrollToEnd && scrollPosTop < 20*view.lineHeight) { - setTimeout(function() { view.scrollTo(0, num*view.lineHeight + scrollPosTop); }, 10); - } - }, - - updateView: function(lines, livemode, top) { - var me = this; - var view = me.getView(); - var viewmodel = me.getViewModel(); - if (viewmodel.get('livemode') !== livemode) { - return; // we switched mode, do not update the content - } - var contentEl = me.lookup('content'); - - // save old scrollpositions - var scrollPos = me.scrollPosBottom(); - var scrollPosTop = me.scrollPosTop(); - - var newend = lines.shift(); - var newstart = lines.pop(); - - var num = lines.length; - var text = lines.map(Ext.htmlEncode).join('
'); - - if (!livemode) { - if (num) { - view.content = text; - } else { - view.content = 'nothing logged or no timespan selected'; - } - } else { - // update content - if (top && num) { - view.content = view.content ? text + '
' + view.content : text; - } else if (!top && num) { - view.content = view.content ? view.content + '
' + text : text; - } - - // update cursors - if (!top || !view.startcursor) { - view.startcursor = newstart; - } - - if (top || !view.endcursor) { - view.endcursor = newend; - } - } - - contentEl.update(view.content); - - me.updateScroll(livemode, num, scrollPos, scrollPosTop); - }, - - doLoad: function(livemode, top, since, until) { - var me = this; - if (me.running) { - me.requested = true; - return; - } - me.running = true; - var view = me.getView(); - var params = { - lastentries: view.numEntries || 500, - }; - if (livemode) { - if (!top && view.startcursor) { - params = { - startcursor: view.startcursor - }; - } else if (view.endcursor) { - params.endcursor = view.endcursor; - } - } else { - params = { - since: since, - until: until - }; - } - Proxmox.Utils.API2Request({ - url: view.url, - params: params, - waitMsgTarget: (!livemode) ? view : undefined, - method: 'GET', - success: function(response) { - Proxmox.Utils.setErrorMask(me, false); - var lines = response.result.data; - me.updateView(lines, livemode, top); - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - }, - failure: function(response) { - var msg = response.htmlStatus; - Proxmox.Utils.setErrorMask(me, msg); - me.running = false; - if (me.requested) { - me.requested = false; - view.loadTask.delay(200); - } - } - }); - }, - - onScroll: function(x, y) { - var me = this; - var view = me.getView(); - var viewmodel = me.getViewModel(); - var livemode = viewmodel.get('livemode'); - if (!livemode) { - return; - } - - if (me.scrollPosTop() < 20*view.lineHeight) { - view.scrollToEnd = false; - view.loadTask.delay(200, undefined, undefined, [true, true]); - } else if (me.scrollPosBottom() <= 1) { - view.scrollToEnd = true; - } - }, - - init: function(view) { - var me = this; - - if (!view.url) { - throw "no url specified"; - } - - var viewmodel = me.getViewModel(); - var viewModel = this.getViewModel(); - var since = new Date(); - since.setDate(since.getDate() - 3); - viewModel.set('until', new Date()); - viewModel.set('since', since); - me.lookup('content').setStyle('line-height', view.lineHeight + 'px'); - - view.loadTask = new Ext.util.DelayedTask(me.doLoad, me, [true, false]); - - me.updateParams(); - view.task = Ext.TaskManager.start({ - run: function() { - if (!view.isVisible() || !view.scrollToEnd || !viewmodel.get('livemode')) { - return; - } - - if (me.scrollPosBottom() <= 1) { - view.loadTask.delay(200, undefined, undefined, [true, false]); - } - }, - interval: 1000 - }); - }, - - onLiveMode: function() { - var me = this; - var view = me.getView(); - delete view.startcursor; - delete view.endcursor; - delete view.content; - me.getViewModel().set('livemode', true); - view.scrollToEnd = true; - me.updateView([], true, false); - }, - - onTimespan: function() { - var me = this; - me.getViewModel().set('livemode', false); - me.updateView([], false); - } - }, - - onDestroy: function() { - var me = this; - me.loadTask.cancel(); - Ext.TaskManager.stop(me.task); - delete me.content; - }, - - // for user to initiate a load from outside - requestUpdate: function() { - var me = this; - me.loadTask.delay(200); - }, - - viewModel: { - data: { - livemode: true, - until: null, - since: null - } - }, - - layout: 'auto', - bodyPadding: 5, - scrollable: { - x: 'auto', - y: 'auto', - listeners: { - // we have to have this here, since we cannot listen to events - // of the scroller in the viewcontroller (extjs bug?), nor does - // the panel have a 'scroll' event' - scroll: { - fn: function(scroller, x, y) { - var controller = this.component.getController(); - if (controller) { // on destroy, controller can be gone - controller.onScroll(x,y); - } - }, - buffer: 200 - }, - } - }, - - tbar: { - - items: [ - '->', - { - xtype: 'segmentedbutton', - items: [ - { - text: gettext('Live Mode'), - bind: { - pressed: '{livemode}' - }, - handler: 'onLiveMode', - }, - { - text: gettext('Select Timespan'), - bind: { - pressed: '{!livemode}' - }, - handler: 'onTimespan', - } - ] - }, - { - xtype: 'box', - bind: { disabled: '{livemode}' }, - autoEl: { cn: gettext('Since') + ':' } - }, - { - xtype: 'datefield', - name: 'since_date', - reference: 'since', - format: 'Y-m-d', - bind: { - disabled: '{livemode}', - value: '{since}', - maxValue: '{until}' - } - }, - { - xtype: 'box', - bind: { disabled: '{livemode}' }, - autoEl: { cn: gettext('Until') + ':' } - }, - { - xtype: 'datefield', - name: 'until_date', - reference: 'until', - format: 'Y-m-d', - bind: { - disabled: '{livemode}', - value: '{until}', - minValue: '{since}' - } - }, - { - xtype: 'button', - text: 'Update', - reference: 'updateBtn', - handler: 'updateParams', - bind: { - disabled: '{livemode}' - } - } - ] - }, - - items: [ - { - xtype: 'box', - reference: 'content', - style: { - font: 'normal 11px tahoma, arial, verdana, sans-serif', - 'white-space': 'pre' - }, - } - ] -}); -Ext.define('Proxmox.widget.RRDChart', { - extend: 'Ext.chart.CartesianChart', - alias: 'widget.proxmoxRRDChart', - - unit: undefined, // bytes, bytespersecond, percent - - controller: { - xclass: 'Ext.app.ViewController', - - convertToUnits: function(value) { - var units = ['', 'k','M','G','T', 'P']; - var si = 0; - while(value >= 1000 && si < (units.length -1)){ - value = value / 1000; - si++; - } - - // javascript floating point weirdness - value = Ext.Number.correctFloat(value); - - // limit to 2 decimal points - value = Ext.util.Format.number(value, "0.##"); - - return value.toString() + " " + units[si]; - }, - - leftAxisRenderer: function(axis, label, layoutContext) { - var me = this; - - return me.convertToUnits(label); - }, - - onSeriesTooltipRender: function(tooltip, record, item) { - var me = this.getView(); - - var suffix = ''; - - if (me.unit === 'percent') { - suffix = '%'; - } else if (me.unit === 'bytes') { - suffix = 'B'; - } else if (me.unit === 'bytespersecond') { - suffix = 'B/s'; - } - - var prefix = item.field; - if (me.fieldTitles && me.fieldTitles[me.fields.indexOf(item.field)]) { - prefix = me.fieldTitles[me.fields.indexOf(item.field)]; - } - tooltip.setHtml(prefix + ': ' + this.convertToUnits(record.get(item.field)) + suffix + - '
' + new Date(record.get('time'))); - }, - - onAfterAnimation: function(chart, eopts) { - // if the undobuton is disabled, - // disable our tool - - var ourUndoZoomButton = chart.tools[0]; - var undoButton = chart.interactions[0].getUndoButton(); - ourUndoZoomButton.setDisabled(undoButton.isDisabled()); - } - }, - - width: 770, - height: 300, - animation: false, - interactions: [{ - type: 'crosszoom' - }], - axes: [{ - type: 'numeric', - position: 'left', - grid: true, - renderer: 'leftAxisRenderer', - //renderer: function(axis, label) { return label; }, - minimum: 0 - }, { - type: 'time', - position: 'bottom', - grid: true, - fields: ['time'] - }], - legend: { - docked: 'bottom' - }, - listeners: { - animationend: 'onAfterAnimation' - }, - - - initComponent: function() { - var me = this; - var series = {}; - - if (!me.store) { - throw "cannot work without store"; - } - - if (!me.fields) { - throw "cannot work without fields"; - } - - me.callParent(); - - // add correct label for left axis - var axisTitle = ""; - if (me.unit === 'percent') { - axisTitle = "%"; - } else if (me.unit === 'bytes') { - axisTitle = "Bytes"; - } else if (me.unit === 'bytespersecond') { - axisTitle = "Bytes/s"; - } else if (me.fieldTitles && me.fieldTitles.length === 1) { - axisTitle = me.fieldTitles[0]; - } else if (me.fields.length === 1) { - axisTitle = me.fields[0]; - } - - me.axes[0].setTitle(axisTitle); - - if (!me.noTool) { - me.addTool([{ - type: 'minus', - disabled: true, - tooltip: gettext('Undo Zoom'), - handler: function(){ - var undoButton = me.interactions[0].getUndoButton(); - if (undoButton.handler) { - undoButton.handler(); - } - } - },{ - type: 'restore', - tooltip: gettext('Toggle Legend'), - handler: function(){ - if (me.legend) { - me.legend.setVisible(!me.legend.isVisible()); - } - } - }]); - } - - // add a series for each field we get - me.fields.forEach(function(item, index){ - var title = item; - if (me.fieldTitles && me.fieldTitles[index]) { - title = me.fieldTitles[index]; - } - me.addSeries(Ext.apply( - { - type: 'line', - xField: 'time', - yField: item, - title: title, - fill: true, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - }, - tooltip: { - trackMouse: true, - renderer: 'onSeriesTooltipRender' - } - }, - me.seriesConfig - )); - }); - - // enable animation after the store is loaded - me.store.onAfter('load', function() { - me.setAnimation(true); - }, this, {single: true}); - } -}); -Ext.define('Proxmox.panel.GaugeWidget', { - extend: 'Ext.panel.Panel', - alias: 'widget.proxmoxGauge', - - defaults: { - style: { - 'text-align':'center' - } - }, - items: [ - { - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}

' - }, - { - xtype: 'polar', - height: 120, - border: false, - itemId: 'chart', - series: [{ - type: 'gauge', - value: 0, - colors: ['#f5f5f5'], - sectors: [0], - donut: 90, - needleLength: 100, - totalAngle: Math.PI - }], - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '', - textAlign: 'center', - textBaseline: 'bottom', - x: 125, - y: 110, - fontSize: 30 - }] - }, - { - xtype: 'box', - itemId: 'text' - } - ], - - header: false, - border: false, - - warningThreshold: 0.6, - criticalThreshold: 0.9, - warningColor: '#fc0', - criticalColor: '#FF6C59', - defaultColor: '#c2ddf2', - backgroundColor: '#f5f5f5', - - initialValue: 0, - - - updateValue: function(value, text) { - var me = this; - var color = me.defaultColor; - var attr = {}; - - if (value >= me.criticalThreshold) { - color = me.criticalColor; - } else if (value >= me.warningThreshold) { - color = me.warningColor; - } - - me.chart.series[0].setColors([color, me.backgroundColor]); - me.chart.series[0].setValue(value*100); - - me.valueSprite.setText(' '+(value*100).toFixed(0) + '%'); - attr.x = me.chart.getWidth()/2; - attr.y = me.chart.getHeight()-20; - if (me.spriteFontSize) { - attr.fontSize = me.spriteFontSize; - } - me.valueSprite.setAttributes(attr, true); - - if (text !== undefined) { - me.text.setHtml(text); - } - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.text = me.getComponent('text'); - me.chart = me.getComponent('chart'); - me.valueSprite = me.chart.getSurface('chart').get('valueSprite'); - } -}); -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ -Ext.define('Proxmox.window.Edit', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxWindowEdit', - - // autoLoad trigger a load() after component creation - autoLoad: false, - - resizable: false, - - // use this tio atimatically generate a title like - // Create: - subject: undefined, - - // set isCreate to true if you want a Create button (instead - // OK and RESET) - isCreate: false, - - // set to true if you want an Add button (instead of Create) - isAdd: false, - - // set to true if you want an Remove button (instead of Create) - isRemove: false, - - // custom submitText - submitText: undefined, - - backgroundDelay: 0, - - // needed for finding the reference to submitbutton - // because we do not have a controller - referenceHolder: true, - defaultButton: 'submitbutton', - - // finds the first form field - defaultFocus: 'field[disabled=false][hidden=false]', - - showProgress: false, - - showTaskViewer: false, - - // gets called if we have a progress bar or taskview and it detected that - // the task finished. function(success) - taskDone: Ext.emptyFn, - - // gets called when the api call is finished, right at the beginning - // function(success, response, options) - apiCallDone: Ext.emptyFn, - - // assign a reference from docs, to add a help button docked to the - // bottom of the window. If undefined we magically fall back to the - // onlineHelp of our first item, if set. - onlineHelp: undefined, - - isValid: function() { - var me = this; - - var form = me.formPanel.getForm(); - return form.isValid(); - }, - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.formPanel.getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - setValues: function(values) { - var me = this; - - var form = me.formPanel.getForm(); - - Ext.iterate(values, function(fieldId, val) { - var field = form.findField(fieldId); - if (field && !field.up('inputpanel')) { - field.setValue(val); - if (form.trackResetOnLoad) { - field.resetOriginalValue(); - } - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setValues(values); - }); - }, - - setSubmitText: function(text) { - this.lookup('submitbutton').setText(text); - }, - - submit: function() { - var me = this; - - var form = me.formPanel.getForm(); - - var values = me.getValues(); - Ext.Object.each(values, function(name, val) { - if (values.hasOwnProperty(name)) { - if (Ext.isArray(val) && !val.length) { - values[name] = ''; - } - } - }); - - if (me.digest) { - values.digest = me.digest; - } - - if (me.backgroundDelay) { - values.background_delay = me.backgroundDelay; - } - - var url = me.url; - if (me.method === 'DELETE') { - url = url + "?" + Ext.Object.toQueryString(values); - values = undefined; - } - - Proxmox.Utils.API2Request({ - url: url, - waitMsgTarget: me, - method: me.method || (me.backgroundDelay ? 'POST' : 'PUT'), - params: values, - failure: function(response, options) { - me.apiCallDone(false, response, options); - - if (response.result && response.result.errors) { - form.markInvalid(response.result.errors); - } - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = (me.backgroundDelay || me.showProgress || me.showTaskViewer) && - response.result.data ? true : false; - - me.apiCallDone(true, response, options); - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - me.hide(); - - var upid = response.result.data; - var viewerClass = me.showTaskViewer ? 'Viewer' : 'Progress'; - var win = Ext.create('Proxmox.window.Task' + viewerClass, { - upid: upid, - taskDone: me.taskDone, - listeners: { - destroy: function () { - me.close(); - } - } - }); - win.show(); - } else { - me.close(); - } - } - }); - }, - - load: function(options) { - var me = this; - - var form = me.formPanel.getForm(); - - options = options || {}; - - var newopts = Ext.apply({ - waitMsgTarget: me - }, options); - - var createWrapper = function(successFn) { - Ext.apply(newopts, { - url: me.url, - method: 'GET', - success: function(response, opts) { - form.clearInvalid(); - me.digest = response.result.data.digest; - if (successFn) { - successFn(response, opts); - } else { - me.setValues(response.result.data); - } - // hack: fix ExtJS bug - Ext.Array.each(me.query('radiofield'), function(f) { - f.resetOriginalValue(); - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() { - me.close(); - }); - } - }); - }; - - createWrapper(options.success); - - Proxmox.Utils.API2Request(newopts); - }, - - initComponent : function() { - var me = this; - - if (!me.url) { - throw "no url specified"; - } - - if (me.create) {throw "deprecated parameter, use isCreate";} - - var items = Ext.isArray(me.items) ? me.items : [ me.items ]; - - me.items = undefined; - - me.formPanel = Ext.create('Ext.form.Panel', { - url: me.url, - method: me.method || 'PUT', - trackResetOnLoad: true, - bodyPadding: 10, - border: false, - defaults: Ext.apply({}, me.defaults, { - border: false - }), - fieldDefaults: Ext.apply({}, me.fieldDefaults, { - labelWidth: 100, - anchor: '100%' - }), - items: items - }); - - var inputPanel = me.formPanel.down('inputpanel'); - - var form = me.formPanel.getForm(); - - var submitText; - if (me.isCreate) { - if (me.submitText) { - submitText = me.submitText; - } else if (me.isAdd) { - submitText = gettext('Add'); - } else if (me.isRemove) { - submitText = gettext('Remove'); - } else { - submitText = gettext('Create'); - } - } else { - submitText = me.submitText || gettext('OK'); - } - - var submitBtn = Ext.create('Ext.Button', { - reference: 'submitbutton', - text: submitText, - disabled: !me.isCreate, - handler: function() { - me.submit(); - } - }); - - var resetBtn = Ext.create('Ext.Button', { - text: 'Reset', - disabled: true, - handler: function(){ - form.reset(); - } - }); - - var set_button_status = function() { - var valid = form.isValid(); - var dirty = form.isDirty(); - submitBtn.setDisabled(!valid || !(dirty || me.isCreate)); - resetBtn.setDisabled(!dirty); - - if (inputPanel && inputPanel.hasAdvanced) { - // we want to show the advanced options - // as soon as some of it is not valid - var advancedItems = me.down('#advancedContainer').query('field'); - var valid = true; - advancedItems.forEach(function(field) { - if (!field.isValid()) { - valid = false; - } - }); - - if (!valid) { - inputPanel.setAdvancedVisible(true); - me.down('#advancedcb').setValue(true); - } - } - }; - - form.on('dirtychange', set_button_status); - form.on('validitychange', set_button_status); - - var colwidth = 300; - if (me.fieldDefaults && me.fieldDefaults.labelWidth) { - colwidth += me.fieldDefaults.labelWidth - 100; - } - - var twoColumn = inputPanel && - (inputPanel.column1 || inputPanel.column2); - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, me.isCreate, me.isAdd); - } - - if (me.isCreate) { - me.buttons = [ submitBtn ] ; - } else { - me.buttons = [ submitBtn, resetBtn ]; - } - - if (inputPanel && inputPanel.hasAdvanced) { - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - inputPanel.setAdvancedVisible(advchecked); - me.buttons.unshift( - { - xtype: 'proxmoxcheckbox', - itemId: 'advancedcb', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - stateId: 'proxmox-advanced-cb', - value: advchecked, - listeners: { - change: function(cb, val) { - inputPanel.setAdvancedVisible(val); - sp.set('proxmox-advanced-cb', val); - } - } - } - ); - } - - var onlineHelp = me.onlineHelp; - if (!onlineHelp && inputPanel && inputPanel.onlineHelp) { - onlineHelp = inputPanel.onlineHelp; - } - - if (onlineHelp) { - var helpButton = Ext.create('Proxmox.button.Help'); - me.buttons.unshift(helpButton, '->'); - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', onlineHelp); - } - - Ext.applyIf(me, { - modal: true, - width: twoColumn ? colwidth*2 : colwidth, - border: false, - items: [ me.formPanel ] - }); - - me.callParent(); - - // always mark invalid fields - me.on('afterlayout', function() { - // on touch devices, the isValid function - // triggers a layout, which triggers an isValid - // and so on - // to prevent this we disable the layouting here - // and enable it afterwards - me.suspendLayout = true; - me.isValid(); - me.suspendLayout = false; - }); - - if (me.autoLoad) { - me.load(); - } - } -}); -Ext.define('Proxmox.window.PasswordEdit', { - extend: 'Proxmox.window.Edit', - alias: 'proxmoxWindowPasswordEdit', - - subject: gettext('Password'), - - url: '/api2/extjs/access/password', - - fieldDefaults: { - labelWidth: 120 - }, - - items: [ - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - allowBlank: false, - name: 'password', - listeners: { - change: function(field){ - field.next().validate(); - }, - blur: function(field){ - field.next().validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - allowBlank: false, - vtype: 'password', - initialPassField: 'password', - submitValue: false - }, - { - xtype: 'hiddenfield', - name: 'userid' - } - ], - - initComponent : function() { - var me = this; - - if (!me.userid) { - throw "no userid specified"; - } - - me.callParent(); - me.down('[name=userid]').setValue(me.userid); - } -}); -Ext.define('Proxmox.window.TaskProgress', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskProgress', - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: { - status: { defaultValue: 'unknown' }, - exitstatus: { defaultValue: 'unknown' } - } - }); - - me.on('destroy', statstore.stopUpdate); - - var getObjectValue = function(key, defaultValue) { - var rec = statstore.getById(key); - if (rec) { - return rec.data.value; - } - return defaultValue; - }; - - var pbar = Ext.create('Ext.ProgressBar', { text: 'running...' }); - - me.mon(statstore, 'load', function() { - var status = getObjectValue('status'); - if (status === 'stopped') { - var exitstatus = getObjectValue('exitstatus'); - if (exitstatus == 'OK') { - pbar.reset(); - pbar.updateText("Done!"); - Ext.Function.defer(me.close, 1000, me); - } else { - me.close(); - Ext.Msg.alert('Task failed', exitstatus); - } - me.taskDone(exitstatus == 'OK'); - } - }); - - var descr = Proxmox.Utils.format_task_description(task.type, task.id); - - Ext.apply(me, { - title: gettext('Task') + ': ' + descr, - width: 300, - layout: 'auto', - modal: true, - bodyPadding: 5, - items: pbar, - buttons: [ - { - text: gettext('Details'), - handler: function() { - var win = Ext.create('Proxmox.window.TaskViewer', { - taskDone: me.taskDone, - upid: me.upid - }); - win.show(); - me.close(); - } - } - ] - }); - - me.callParent(); - - statstore.startUpdate(); - - pbar.wait(); - } -}); - -// fixme: how can we avoid those lint errors? -/*jslint confusion: true */ - -Ext.define('Proxmox.window.TaskViewer', { - extend: 'Ext.window.Window', - alias: 'widget.proxmoxTaskViewer', - - extraTitle: '', // string to prepend after the generic task title - - taskDone: Ext.emptyFn, - - initComponent: function() { - var me = this; - - if (!me.upid) { - throw "no task specified"; - } - - var task = Proxmox.Utils.parse_task_upid(me.upid); - - var statgrid; - - var rows = { - status: { - header: gettext('Status'), - defaultValue: 'unknown', - renderer: function(value) { - if (value != 'stopped') { - return value; - } - var es = statgrid.getObjectValue('exitstatus'); - if (es) { - return value + ': ' + es; - } - } - }, - exitstatus: { - visible: false - }, - type: { - header: gettext('Task type'), - required: true - }, - user: { - header: gettext('User name'), - required: true - }, - node: { - header: gettext('Node'), - required: true - }, - pid: { - header: gettext('Process ID'), - required: true - }, - task_id: { - header: gettext('Task ID'), - }, - starttime: { - header: gettext('Start Time'), - required: true, - renderer: Proxmox.Utils.render_timestamp - }, - upid: { - header: gettext('Unique task ID') - } - }; - - var statstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status", - interval: 1000, - rows: rows - }); - - me.on('destroy', statstore.stopUpdate); - - var stop_task = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + task.node + "/tasks/" + me.upid, - waitMsgTarget: me, - method: 'DELETE', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var stop_btn1 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - var stop_btn2 = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: stop_task - }); - - statgrid = Ext.create('Proxmox.grid.ObjectGrid', { - title: gettext('Status'), - layout: 'fit', - tbar: [ stop_btn1 ], - rstore: statstore, - rows: rows, - border: false - }); - - var logView = Ext.create('Proxmox.panel.LogView', { - title: gettext('Output'), - tbar: [ stop_btn2 ], - border: false, - url: "/api2/extjs/nodes/" + task.node + "/tasks/" + me.upid + "/log" - }); - - me.mon(statstore, 'load', function() { - var status = statgrid.getObjectValue('status'); - - if (status === 'stopped') { - logView.scrollToEnd = false; - logView.requestUpdate(); - statstore.stopUpdate(); - me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK'); - } - - stop_btn1.setDisabled(status !== 'running'); - stop_btn2.setDisabled(status !== 'running'); - }); - - statstore.startUpdate(); - - Ext.apply(me, { - title: "Task viewer: " + task.desc + me.extraTitle, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [{ - xtype: 'tabpanel', - region: 'center', - items: [ logView, statgrid ] - }] - }); - - me.callParent(); - - logView.fireEvent('show', logView); - } -}); - -Ext.define('apt-pkglist', { - extend: 'Ext.data.Model', - fields: [ 'Package', 'Title', 'Description', 'Section', 'Arch', - 'Priority', 'Version', 'OldVersion', 'ChangeLogUrl', 'Origin' ], - idProperty: 'Package' -}); - -Ext.define('Proxmox.node.APT', { - extend: 'Ext.grid.GridPanel', - - xtype: 'proxmoxNodeAPT', - - upgradeBtn: undefined, - - columns: [ - { - header: gettext('Package'), - width: 200, - sortable: true, - dataIndex: 'Package' - }, - { - text: gettext('Version'), - columns: [ - { - header: gettext('current'), - width: 100, - sortable: false, - dataIndex: 'OldVersion' - }, - { - header: gettext('new'), - width: 100, - sortable: false, - dataIndex: 'Version' - } - ] - }, - { - header: gettext('Description'), - sortable: false, - dataIndex: 'Title', - flex: 1 - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'apt-pkglist', - groupField: 'Origin', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/apt/update" - }, - sorters: [ - { - property : 'Package', - direction: 'ASC' - } - ] - }); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping', { - groupHeaderTpl: '{[ "Origin: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})', - enableGroupingMenu: false - }); - - var rowBodyFeature = Ext.create('Ext.grid.feature.RowBody', { - getAdditionalData: function (data, rowIndex, record, orig) { - var headerCt = this.view.headerCt; - var colspan = headerCt.getColumnCount(); - return { - rowBody: '
' + - Ext.String.htmlEncode(data.Description) + - '
', - rowBodyCls: me.full_description ? '' : Ext.baseCSSPrefix + 'grid-row-body-hidden', - rowBodyColspan: colspan - }; - } - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store, true); - - var apt_command = function(cmd){ - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/apt/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.mon(win, 'close', reload); - } - }); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var update_btn = new Ext.Button({ - text: gettext('Refresh'), - handler: function() { - Proxmox.Utils.checked_command(function() { apt_command('update'); }); - } - }); - - var show_changelog = function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return; - } - - var view = Ext.createWidget('component', { - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Changelog') + ": " + rec.data.Package, - width: 800, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + me.nodename + "/apt/changelog", - params: { - name: rec.data.Package, - version: rec.data.Version - }, - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - view.update(Ext.htmlEncode(response.result.data)); - } - }); - - }; - - var changelog_btn = new Proxmox.button.Button({ - text: gettext('Changelog'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) { - return false; - } - return true; - }, - handler: function(b, e, rec) { - show_changelog(rec); - } - }); - - var verbose_desc_checkbox = new Ext.form.field.Checkbox({ - boxLabel: gettext('Show details'), - value: false, - listeners: { - change: (f, val) => { - me.full_description = val; - me.getView().refresh(); - } - } - }); - - if (me.upgradeBtn) { - me.tbar = [ update_btn, me.upgradeBtn, changelog_btn, '->', verbose_desc_checkbox ]; - } else { - me.tbar = [ update_btn, changelog_btn, '->', verbose_desc_checkbox ]; - } - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-update', - selModel: sm, - viewConfig: { - stripeRows: false, - emptyText: '
' + gettext('No updates available.') + '
' - }, - features: [ groupingFeature, rowBodyFeature ], - listeners: { - activate: reload, - itemdblclick: function(v, rec) { - show_changelog(rec); - } - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.NetworkEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeNetworkEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.iftype) { - throw "no network device type specified"; - } - - me.isCreate = !me.iface; - - var iface_vtype; - - if (me.iftype === 'bridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'bond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'eth' && !me.isCreate) { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'vlan') { - iface_vtype = 'VlanName'; - } else if (me.iftype === 'OVSBridge') { - iface_vtype = 'BridgeName'; - } else if (me.iftype === 'OVSBond') { - iface_vtype = 'BondName'; - } else if (me.iftype === 'OVSIntPort') { - iface_vtype = 'InterfaceName'; - } else if (me.iftype === 'OVSPort') { - iface_vtype = 'InterfaceName'; - } else { - console.log(me.iftype); - throw "unknown network device type specified"; - } - - me.subject = Proxmox.Utils.render_network_iface_type(me.iftype); - - let column1 = [], - column2 = [], - columnB = [], - advancedColumn1 = [], - advancedColumn2 = []; - - if (!(me.iftype === 'OVSIntPort' || me.iftype === 'OVSPort' || me.iftype === 'OVSBond')) { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Autostart'), - name: 'autostart', - uncheckedValue: 0, - checked: me.isCreate ? true : undefined - }); - } - - if (me.iftype === 'bridge') { - column2.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('VLAN aware'), - name: 'bridge_vlan_aware', - deleteEmpty: !me.isCreate - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'bridge_ports' - }); - } else if (me.iftype === 'OVSBridge') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Bridge ports'), - name: 'ovs_ports' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'OVSPort' || me.iftype === 'OVSIntPort') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } else if (me.iftype === 'vlan') { - - if (!me.isCreate) { - - me.disablevlanid = false; - me.disablevlanrawdevice = false; - me.vlanrawdevicevalue = ''; - me.vlanidvalue = ''; - - if (Proxmox.Utils.VlanInterface_match.test(me.iface)) { - me.disablevlanid = true; - me.disablevlanrawdevice = true; - var arr = Proxmox.Utils.VlanInterface_match.exec(me.iface); - me.vlanrawdevicevalue = arr[1]; - me.vlanidvalue = arr[2]; - - } else if (Proxmox.Utils.Vlan_match.test(me.iface)) { - me.disablevlanid = true; - var arr = Proxmox.Utils.Vlan_match.exec(me.iface); - me.vlanidvalue = arr[1]; - } - } else { - - me.disablevlanid = true; - me.disablevlanrawdevice = true; - } - - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Vlan raw device'), - name: 'vlan-raw-device', - value: me.vlanrawdevicevalue, - disabled: me.disablevlanrawdevice - }); - - column2.push({ - xtype: 'pveVlanField', - name: 'vlan-id', - value: me.vlanidvalue, - disabled: me.disablevlanid - }); - - columnB.push({ - xtype: 'label', - userCls: 'pmx-hint', - text: 'Either add the VLAN number to an existing interface name, or choose your own name and set the VLAN raw device (for the latter ifupdown1 supports vlanXY naming only)', - }); - - } else if (me.iftype === 'bond') { - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'slaves' - }); - - var policySelector = Ext.createWidget('bondPolicySelector', { - fieldLabel: gettext('Hash policy'), - name: 'bond_xmit_hash_policy', - deleteEmpty: !me.isCreate, - disabled: true - }); - - var primaryfield = Ext.createWidget('textfield', { - fieldLabel: gettext('bond-primary'), - name: 'bond-primary', - value: '', - disabled: true - }); - - column2.push({ - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - value: me.isCreate ? 'balance-rr' : undefined, - listeners: { - change: function(f, value) { - if (value === 'balance-xor' || - value === '802.3ad') { - policySelector.setDisabled(false); - primaryfield.setDisabled(true); - primaryfield.setValue(''); - } else if (value === 'active-backup') { - primaryfield.setDisabled(false); - policySelector.setDisabled(true); - policySelector.setValue(''); - } else { - policySelector.setDisabled(true); - policySelector.setValue(''); - primaryfield.setDisabled(true); - primaryfield.setValue(''); - } - } - }, - allowBlank: false - }); - - column2.push(policySelector); - column2.push(primaryfield); - - } else if (me.iftype === 'OVSBond') { - column2.push({ - xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield', - fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'), - allowBlank: false, - nodename: me.nodename, - bridgeType: 'OVSBridge', - name: 'ovs_bridge' - }); - column2.push({ - xtype: 'pveVlanField', - deleteEmpty: !me.isCreate, - name: 'ovs_tag', - value: '' - }); - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('OVS options'), - name: 'ovs_options' - }); - } - - column2.push({ - xtype: 'textfield', - fieldLabel: gettext('Comment'), - allowBlank: true, - nodename: me.nodename, - name: 'comments' - }); - - var url; - var method; - - if (me.isCreate) { - url = "/api2/extjs/nodes/" + me.nodename + "/network"; - method = 'POST'; - } else { - url = "/api2/extjs/nodes/" + me.nodename + "/network/" + me.iface; - method = 'PUT'; - } - - column1.push({ - xtype: 'hiddenfield', - name: 'type', - value: me.iftype - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'iface', - value: me.iface, - vtype: iface_vtype, - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate && iface_vtype === 'VlanName') { - var vlanidField = me.down('field[name=vlan-id]'); - var vlanrawdeviceField = me.down('field[name=vlan-raw-device]'); - if (Proxmox.Utils.VlanInterface_match.test(value)) { - vlanidField.setDisabled(true); - vlanrawdeviceField.setDisabled(true); - } else if (Proxmox.Utils.Vlan_match.test(value)) { - vlanidField.setDisabled(true); - vlanrawdeviceField.setDisabled(false); - } else { - vlanidField.setDisabled(false); - vlanrawdeviceField.setDisabled(false); - } - } - } - } - }); - - if (me.iftype === 'OVSBond') { - column1.push( - { - xtype: 'bondModeSelector', - fieldLabel: gettext('Mode'), - name: 'bond_mode', - openvswitch: true, - value: me.isCreate ? 'active-backup' : undefined, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Slaves'), - name: 'ovs_bonds' - } - ); - } else { - - column1.push( - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: 'IPv4/CIDR', - vtype: 'IPCIDRAddress', - name: 'cidr' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway') + ' (IPv4)', - vtype: 'IPAddress', - name: 'gateway' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: 'IPv6/CIDR', - vtype: 'IP6CIDRAddress', - name: 'cidr6' - }, - { - xtype: 'proxmoxtextfield', - deleteEmpty: !me.isCreate, - fieldLabel: gettext('Gateway') + ' (IPv6)', - vtype: 'IP6Address', - name: 'gateway6' - }, - ); - advancedColumn1. push( - { - xtype: 'proxmoxintegerfield', - minValue: 1280, - maxValue: 65520, - deleteEmpty: !me.isCreate, - emptyText: 1500, - fieldLabel: 'MTU', - name: 'mtu' - }, - ); - } - - Ext.applyIf(me, { - url: url, - method: method, - items: { - xtype: 'inputpanel', - column1: column1, - column2: column2, - columnB: columnB, - advancedColumn1: advancedColumn1, - advancedColumn2: advancedColumn2, - } - }); - - me.callParent(); - - if (me.isCreate) { - me.down('field[name=iface]').setValue(me.iface_default); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.type !== me.iftype) { - var msg = "Got unexpected device type"; - Ext.Msg.alert(gettext('Error'), msg, function() { - me.close(); - }); - return; - } - me.setValues(data); - me.isValid(); // trigger validation - } - }); - } - } -}); -Ext.define('proxmox-networks', { - extend: 'Ext.data.Model', - fields: [ - 'iface', 'type', 'active', 'autostart', - 'bridge_ports', 'slaves', - 'address', 'netmask', 'gateway', - 'address6', 'netmask6', 'gateway6', - 'cidr', 'cidr6', - 'comments' - ], - idProperty: 'iface' -}); - -Ext.define('Proxmox.node.NetworkView', { - extend: 'Ext.panel.Panel', - - alias: ['widget.proxmoxNodeNetworkView'], - - // defines what types of network devices we want to create - // order is always the same - types: ['bridge', 'bond', 'vlan', 'ovs'], - - showApplyBtn: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseUrl = '/nodes/' + me.nodename + '/network'; - - var store = Ext.create('Ext.data.Store', { - model: 'proxmox-networks', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseUrl - }, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }); - - var reload = function() { - var changeitem = me.down('#changes'); - var apply_btn = me.down('#apply'); - var revert_btn = me.down('#revert'); - Proxmox.Utils.API2Request({ - url: baseUrl, - failure: function(response, opts) { - store.loadData({}); - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - changeitem.update(''); - changeitem.setHidden(true); - }, - success: function(response, opts) { - var result = Ext.decode(response.responseText); - store.loadData(result.data); - var changes = result.changes; - if (changes === undefined || changes === '') { - changes = gettext("No changes"); - changeitem.setHidden(true); - apply_btn.setDisabled(true); - revert_btn.setDisabled(true); - } else { - changeitem.update("
" + Ext.htmlEncode(changes) + "
"); - changeitem.setHidden(false); - apply_btn.setDisabled(false); - revert_btn.setDisabled(false); - } - } - }); - }; - - var run_editor = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iface: rec.data.iface, - iftype: rec.data.type - }); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: run_editor - }); - - var del_btn = new Ext.Button({ - text: gettext('Remove'), - disabled: true, - handler: function(){ - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var iface = rec.data.iface; - - Proxmox.Utils.API2Request({ - url: baseUrl + '/' + iface, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var apply_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Apply Configuration'), - itemId: 'apply', - disabled: true, - confirmMsg: 'Do you want to apply pending network changes?', - hidden: !me.showApplyBtn, - handler: function() { - Proxmox.Utils.API2Request({ - url: baseUrl, - method: 'PUT', - waitMsgTarget: me, - success: function(response, opts) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskProgress', { - taskDone: reload, - upid: upid - }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var set_button_status = function() { - var grid = me.down('gridpanel'); - var sm = grid.getSelectionModel(); - var rec = sm.getSelection()[0]; - - edit_btn.setDisabled(!rec); - del_btn.setDisabled(!rec); - }; - - var render_ports = function(value, metaData, record) { - if (value === 'bridge') { - return record.data.bridge_ports; - } else if (value === 'bond') { - return record.data.slaves; - } else if (value === 'OVSBridge') { - return record.data.ovs_ports; - } else if (value === 'OVSBond') { - return record.data.ovs_bonds; - } - }; - - var find_next_iface_id = function(prefix) { - var next; - for (next = 0; next <= 9999; next++) { - if (!store.getById(prefix + next.toString())) { - break; - } - } - return prefix + next.toString(); - }; - - var menu_items = []; - - if (me.types.indexOf('bridge') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bridge', - iface_default: find_next_iface_id('vmbr'), - onlineHelp: 'sysadmin_network_configuration', - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('bond') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('bond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'bond', - iface_default: find_next_iface_id('bond'), - onlineHelp: 'sysadmin_network_configuration', - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('vlan') !== -1) { - menu_items.push({ - text: Proxmox.Utils.render_network_iface_type('vlan'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'vlan', - iface_default: 'interfaceX.1', - onlineHelp: 'sysadmin_network_configuration', - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - if (me.types.indexOf('ovs') !== -1) { - if (menu_items.length > 0) { - menu_items.push({ xtype: 'menuseparator' }); - } - - menu_items.push( - { - text: Proxmox.Utils.render_network_iface_type('OVSBridge'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBridge', - iface_default: find_next_iface_id('vmbr') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSBond'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSBond', - iface_default: find_next_iface_id('bond') - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: Proxmox.Utils.render_network_iface_type('OVSIntPort'), - handler: function() { - var win = Ext.create('Proxmox.node.NetworkEdit', { - nodename: me.nodename, - iftype: 'OVSIntPort' - }); - win.on('destroy', reload); - win.show(); - } - } - ); - } - - var renderer_generator = function(fieldname) { - return function(val, metaData, rec) { - var tmp = []; - if (rec.data[fieldname]) { - tmp.push(rec.data[fieldname]); - } - if (rec.data[fieldname + '6']) { - tmp.push(rec.data[fieldname + '6']); - } - return tmp.join('
') || ''; - }; - }; - - Ext.apply(me, { - layout: 'border', - tbar: [ - { - text: gettext('Create'), - menu: { - plain: true, - items: menu_items - } - }, '-', - { - text: gettext('Revert'), - itemId: 'revert', - handler: function() { - Proxmox.Utils.API2Request({ - url: baseUrl, - method: 'DELETE', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - edit_btn, - del_btn, - '-', - apply_btn - ], - items: [ - { - xtype: 'gridpanel', - stateful: true, - stateId: 'grid-node-network', - store: store, - region: 'center', - border: false, - columns: [ - { - header: gettext('Name'), - sortable: true, - dataIndex: 'iface' - }, - { - header: gettext('Type'), - sortable: true, - width: 120, - renderer: Proxmox.Utils.render_network_iface_type, - dataIndex: 'type' - }, - { - xtype: 'booleancolumn', - header: gettext('Active'), - width: 80, - sortable: true, - dataIndex: 'active', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText, - }, - { - xtype: 'booleancolumn', - header: gettext('Autostart'), - width: 80, - sortable: true, - dataIndex: 'autostart', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - xtype: 'booleancolumn', - header: gettext('VLAN aware'), - width: 80, - sortable: true, - dataIndex: 'bridge_vlan_aware', - trueText: Proxmox.Utils.yesText, - falseText: Proxmox.Utils.noText, - undefinedText: Proxmox.Utils.noText - }, - { - header: gettext('Ports/Slaves'), - dataIndex: 'type', - renderer: render_ports - }, - { - header: gettext('Bond Mode'), - dataIndex: 'bond_mode', - renderer: Proxmox.Utils.render_bond_mode, - }, - { - header: gettext('Hash Policy'), - hidden: true, - dataIndex: 'bond_xmit_hash_policy', - }, - { - header: gettext('IP address'), - sortable: true, - width: 120, - hidden: true, - dataIndex: 'address', - renderer: renderer_generator('address'), - }, - { - header: gettext('Subnet mask'), - width: 120, - sortable: true, - hidden: true, - dataIndex: 'netmask', - renderer: renderer_generator('netmask'), - }, - { - header: gettext('CIDR'), - width: 120, - sortable: true, - dataIndex: 'cidr', - renderer: renderer_generator('cidr'), - }, - { - header: gettext('Gateway'), - width: 120, - sortable: true, - dataIndex: 'gateway', - renderer: renderer_generator('gateway'), - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - flex: 1, - renderer: Ext.String.htmlEncode - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: run_editor - } - }, - { - border: false, - region: 'south', - autoScroll: true, - hidden: true, - itemId: 'changes', - tbar: [ - gettext('Pending changes') + ' (' + - gettext("Either reboot or use 'Apply Configuration' (needs ifupdown2) to activate") + ')' - ], - split: true, - bodyPadding: 5, - flex: 0.6, - html: gettext("No changes") - } - ], - }); - - me.callParent(); - reload(); - } -}); -Ext.define('Proxmox.node.DNSEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeDNSEdit'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.items = [ - { - xtype: 'textfield', - fieldLabel: gettext('Search domain'), - name: 'search', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 1", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns1' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 2", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns2' - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS server') + " 3", - vtype: 'IP64Address', - skipEmptyText: true, - name: 'dns3' - } - ]; - - Ext.applyIf(me, { - subject: gettext('DNS'), - url: "/api2/extjs/nodes/" + me.nodename + "/dns", - fieldDefaults: { - labelWidth: 120 - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('Proxmox.node.HostsView', { - extend: 'Ext.panel.Panel', - xtype: 'proxmoxNodeHostsView', - - reload: function() { - var me = this; - me.store.load(); - }, - - tbar: [ - { - text: gettext('Save'), - disabled: true, - itemId: 'savebtn', - handler: function() { - var me = this.up('panel'); - Proxmox.Utils.API2Request({ - params: { - digest: me.digest, - data: me.down('#hostsfield').getValue() - }, - method: 'POST', - url: '/nodes/' + me.nodename + '/hosts', - waitMsgTarget: me, - success: function(response, opts) { - me.reload(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - text: gettext('Revert'), - disabled: true, - itemId: 'resetbtn', - handler: function() { - var me = this.up('panel'); - me.down('#hostsfield').reset(); - } - } - ], - - layout: 'fit', - - items: [ - { - xtype: 'textarea', - itemId: 'hostsfield', - fieldStyle: { - 'font-family': 'monospace', - 'white-space': 'pre' - }, - listeners: { - dirtychange: function(ta, dirty) { - var me = this.up('panel'); - me.down('#savebtn').setDisabled(!dirty); - me.down('#resetbtn').setDisabled(!dirty); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.store = Ext.create('Ext.data.Store', { - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/hosts", - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.store); - - me.mon(me.store, 'load', function(store, records, success) { - if (!success || records.length < 1) { - return; - } - me.digest = records[0].data.digest; - var data = records[0].data.data; - me.down('#hostsfield').setValue(data); - me.down('#hostsfield').resetOriginalValue(); - }); - - me.reload(); - } -}); -Ext.define('Proxmox.node.DNSView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeDNSView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var run_editor = function() { - var win = Ext.create('Proxmox.node.DNSEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/dns", - cwidth1: 130, - interval: 1000, - run_editor: run_editor, - rows: { - search: { - header: 'Search domain', - required: true, - renderer: Ext.htmlEncode - }, - dns1: { - header: gettext('DNS server') + " 1", - required: true, - renderer: Ext.htmlEncode - }, - dns2: { - header: gettext('DNS server') + " 2", - renderer: Ext.htmlEncode - }, - dns3: { - header: gettext('DNS server') + " 3", - renderer: Ext.htmlEncode - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); -Ext.define('Proxmox.node.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeTasks'], - stateful: true, - stateId: 'grid-node-tasks', - loadMask: true, - sortableColumns: false, - vmidFilter: 0, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.BufferedStore', { - pageSize: 500, - autoLoad: true, - remoteFilter: true, - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - startParam: 'start', - limitParam: 'limit', - url: "/api2/json/nodes/" + me.nodename + "/tasks" - } - }); - - var userfilter = ''; - var filter_errors = 0; - - var updateProxyParams = function() { - var params = { - errors: filter_errors - }; - if (userfilter) { - params.userfilter = userfilter; - } - if (me.vmidFilter) { - params.vmid = me.vmidFilter; - } - store.proxy.extraParams = params; - }; - - updateProxyParams(); - - var reload_task = Ext.create('Ext.util.DelayedTask',function() { - updateProxyParams(); - store.reload(); - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - var view_btn = new Ext.Button({ - text: gettext('View'), - disabled: true, - handler: run_task_viewer - }); - - Proxmox.Utils.monStoreErrors(me, store, true); - - Ext.apply(me, { - store: store, - viewConfig: { - trackOver: false, - stripeRows: false, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - tbar: [ - view_btn, '->', gettext('User name') +':', ' ', - { - xtype: 'textfield', - width: 200, - value: userfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - userfilter = field.getValue(); - reload_task.delay(500); - } - } - }, ' ', gettext('Only Errors') + ':', ' ', - { - xtype: 'checkbox', - hideLabel: true, - checked: filter_errors, - listeners: { - change: function(field, checked) { - filter_errors = checked ? 1 : 0; - reload_task.delay(10); - } - } - }, ' ' - ], - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 100, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 100, - renderer: function(value, metaData, record) { - return Ext.Date.format(value,"M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return "ERROR: " + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - selectionchange: function(v, selections) { - view_btn.setDisabled(!(selections && selections[0])); - }, - show: function() { reload_task.delay(10); }, - destroy: function() { reload_task.cancel(); } - } - }); - - me.callParent(); - - } -}); -Ext.define('proxmox-services', { - extend: 'Ext.data.Model', - fields: [ 'service', 'name', 'desc', 'state' ], - idProperty: 'service' -}); - -Ext.define('Proxmox.node.ServiceView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.proxmoxNodeServiceView'], - - startOnlyServices: {}, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 1000, - storeid: 'proxmox-services' + me.nodename, - model: 'proxmox-services', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + "/services" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - sortAfterUpdate: true, - sorters: [ - { - property : 'name', - direction: 'ASC' - } - ] - }); - - var view_service_log = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - var win = Ext.create('Ext.window.Window', { - title: gettext('Syslog') + ': ' + rec.data.service, - modal: true, - width: 800, - height: 400, - layout: 'fit', - items: { - xtype: 'proxmoxLogView', - url: "/api2/extjs/nodes/" + me.nodename + "/syslog?service=" + - rec.data.service, - log_select_timespan: 1 - } - }); - win.show(); - }; - - var service_cmd = function(cmd) { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/services/" + rec.data.service + "/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.loading = true; - }, - success: function(response, opts) { - rstore.startUpdate(); - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - }; - - var start_btn = new Ext.Button({ - text: gettext('Start'), - disabled: true, - handler: function(){ - service_cmd("start"); - } - }); - - var stop_btn = new Ext.Button({ - text: gettext('Stop'), - disabled: true, - handler: function(){ - service_cmd("stop"); - } - }); - - var restart_btn = new Ext.Button({ - text: gettext('Restart'), - disabled: true, - handler: function(){ - service_cmd("restart"); - } - }); - - var syslog_btn = new Ext.Button({ - text: gettext('Syslog'), - disabled: true, - handler: view_service_log - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - start_btn.disable(); - stop_btn.disable(); - restart_btn.disable(); - syslog_btn.disable(); - return; - } - var service = rec.data.service; - var state = rec.data.state; - - syslog_btn.enable(); - - if (me.startOnlyServices[service]) { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - } - stop_btn.disable(); - } else { - if (state == 'running') { - start_btn.disable(); - restart_btn.enable(); - stop_btn.enable(); - } else { - start_btn.enable(); - restart_btn.disable(); - stop_btn.disable(); - } - } - }; - - me.mon(store, 'refresh', set_button_status); - - Proxmox.Utils.monStoreErrors(me, rstore); - - Ext.apply(me, { - store: store, - stateful: false, - tbar: [ start_btn, stop_btn, restart_btn, syslog_btn ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: gettext('Status'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - dataIndex: 'desc', - flex: 2 - } - ], - listeners: { - selectionchange: set_button_status, - itemdblclick: view_service_log, - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.proxmoxNodeTimeEdit'], - - subject: gettext('Time zone'), - - width: 400, - - autoLoad: true, - - fieldDefaults: { - labelWidth: 70 - }, - - items: { - xtype: 'combo', - fieldLabel: gettext('Time zone'), - name: 'timezone', - queryMode: 'local', - store: Ext.create('Proxmox.data.TimezoneStore'), - displayField: 'zone', - editable: true, - anyMatch: true, - forceSelection: true, - allowBlank: false - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.url = "/api2/extjs/nodes/" + me.nodename + "/time"; - - me.callParent(); - } -}); -Ext.define('Proxmox.node.TimeView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.proxmoxNodeTimeView'], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var tzoffset = (new Date()).getTimezoneOffset()*60000; - var renderlocaltime = function(value) { - var servertime = new Date((value * 1000) + tzoffset); - return Ext.Date.format(servertime, 'Y-m-d H:i:s'); - }; - - var run_editor = function() { - var win = Ext.create('Proxmox.node.TimeEdit', { - nodename: me.nodename - }); - win.show(); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + me.nodename + "/time", - cwidth1: 150, - interval: 1000, - run_editor: run_editor, - rows: { - timezone: { - header: gettext('Time zone'), - required: true - }, - localtime: { - header: gettext('Server time'), - required: true, - renderer: renderlocaltime - } - }, - tbar: [ - { - text: gettext("Edit"), - handler: run_editor - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('deactivate', me.rstore.stopUpdate); - me.on('destroy', me.rstore.stopUpdate); - } -}); diff --git a/serverside/jsmod/6.1-3/pvemanagerlib.js.original b/serverside/jsmod/6.1-3/pvemanagerlib.js.original deleted file mode 100644 index df8b7ac..0000000 --- a/serverside/jsmod/6.1-3/pvemanagerlib.js.original +++ /dev/null @@ -1,40953 +0,0 @@ -var pveOnlineHelpInfo = { - "ceph_rados_block_devices" : { - "link" : "/pve-docs/chapter-pvesm.html#ceph_rados_block_devices", - "title" : "Ceph RADOS Block Devices (RBD)" - }, - "chapter_ha_manager" : { - "link" : "/pve-docs/chapter-ha-manager.html#chapter_ha_manager", - "title" : "High Availability" - }, - "chapter_lvm" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_lvm", - "title" : "Logical Volume Manager (LVM)" - }, - "chapter_pct" : { - "link" : "/pve-docs/chapter-pct.html#chapter_pct", - "title" : "Proxmox Container Toolkit" - }, - "chapter_pve_firewall" : { - "link" : "/pve-docs/chapter-pve-firewall.html#chapter_pve_firewall", - "title" : "Proxmox VE Firewall" - }, - "chapter_pveceph" : { - "link" : "/pve-docs/chapter-pveceph.html#chapter_pveceph", - "title" : "Deploy Hyper-Converged Ceph Cluster" - }, - "chapter_pvecm" : { - "link" : "/pve-docs/chapter-pvecm.html#chapter_pvecm", - "title" : "Cluster Manager" - }, - "chapter_pvesr" : { - "link" : "/pve-docs/chapter-pvesr.html#chapter_pvesr", - "title" : "Storage Replication" - }, - "chapter_storage" : { - "link" : "/pve-docs/chapter-pvesm.html#chapter_storage", - "title" : "Proxmox VE Storage" - }, - "chapter_system_administration" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_system_administration", - "title" : "Host System Administration" - }, - "chapter_user_management" : { - "link" : "/pve-docs/chapter-pveum.html#chapter_user_management", - "title" : "User Management" - }, - "chapter_virtual_machines" : { - "link" : "/pve-docs/chapter-qm.html#chapter_virtual_machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "chapter_vzdump" : { - "link" : "/pve-docs/chapter-vzdump.html#chapter_vzdump", - "title" : "Backup and Restore" - }, - "chapter_zfs" : { - "link" : "/pve-docs/chapter-sysadmin.html#chapter_zfs", - "title" : "ZFS on Linux" - }, - "datacenter_configuration_file" : { - "link" : "/pve-docs/pve-admin-guide.html#datacenter_configuration_file", - "title" : "Datacenter Configuration" - }, - "getting_help" : { - "link" : "/pve-docs/pve-admin-guide.html#getting_help", - "title" : "Getting Help" - }, - "gui_my_settings" : { - "link" : "/pve-docs/chapter-pve-gui.html#gui_my_settings", - "subtitle" : "My Settings", - "title" : "Graphical User Interface" - }, - "ha_manager_fencing" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_fencing", - "subtitle" : "Fencing", - "title" : "High Availability" - }, - "ha_manager_groups" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_groups", - "subtitle" : "Groups", - "title" : "High Availability" - }, - "ha_manager_resource_config" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resource_config", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "ha_manager_resources" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_resources", - "subtitle" : "Resources", - "title" : "High Availability" - }, - "ha_manager_shutdown_policy" : { - "link" : "/pve-docs/chapter-ha-manager.html#ha_manager_shutdown_policy", - "subtitle" : "Shutdown Policy", - "title" : "High Availability" - }, - "pct_configuration" : { - "link" : "/pve-docs/chapter-pct.html#pct_configuration", - "subtitle" : "Configuration", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_images" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_images", - "subtitle" : "Container Images", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_network" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_network", - "subtitle" : "Network", - "title" : "Proxmox Container Toolkit" - }, - "pct_container_storage" : { - "link" : "/pve-docs/chapter-pct.html#pct_container_storage", - "subtitle" : "Container Storage", - "title" : "Proxmox Container Toolkit" - }, - "pct_cpu" : { - "link" : "/pve-docs/chapter-pct.html#pct_cpu", - "subtitle" : "CPU", - "title" : "Proxmox Container Toolkit" - }, - "pct_general" : { - "link" : "/pve-docs/chapter-pct.html#pct_general", - "subtitle" : "General Settings", - "title" : "Proxmox Container Toolkit" - }, - "pct_memory" : { - "link" : "/pve-docs/chapter-pct.html#pct_memory", - "subtitle" : "Memory", - "title" : "Proxmox Container Toolkit" - }, - "pct_migration" : { - "link" : "/pve-docs/chapter-pct.html#pct_migration", - "subtitle" : "Migration", - "title" : "Proxmox Container Toolkit" - }, - "pct_options" : { - "link" : "/pve-docs/chapter-pct.html#pct_options", - "subtitle" : "Options", - "title" : "Proxmox Container Toolkit" - }, - "pct_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-pct.html#pct_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Containers", - "title" : "Proxmox Container Toolkit" - }, - "pve_admin_guide" : { - "link" : "/pve-docs/pve-admin-guide.html", - "title" : "Proxmox VE Administration Guide" - }, - "pve_ceph_install" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_install", - "subtitle" : "Installation of Ceph Packages", - "title" : "Deploy Hyper-Converged Ceph Cluster" - }, - "pve_ceph_osds" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_osds", - "subtitle" : "Ceph OSDs", - "title" : "Deploy Hyper-Converged Ceph Cluster" - }, - "pve_ceph_pools" : { - "link" : "/pve-docs/chapter-pveceph.html#pve_ceph_pools", - "subtitle" : "Ceph Pools", - "title" : "Deploy Hyper-Converged Ceph Cluster" - }, - "pve_documentation_index" : { - "link" : "/pve-docs/index.html", - "title" : "Proxmox VE Documentation Index" - }, - "pve_firewall_cluster_wide_setup" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_cluster_wide_setup", - "subtitle" : "Cluster Wide Setup", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_host_specific_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_host_specific_configuration", - "subtitle" : "Host Specific Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_aliases" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_aliases", - "subtitle" : "IP Aliases", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_ip_sets" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_ip_sets", - "subtitle" : "IP Sets", - "title" : "Proxmox VE Firewall" - }, - "pve_firewall_vm_container_configuration" : { - "link" : "/pve-docs/chapter-pve-firewall.html#pve_firewall_vm_container_configuration", - "subtitle" : "VM/Container Configuration", - "title" : "Proxmox VE Firewall" - }, - "pve_service_daemons" : { - "link" : "/pve-docs/index.html#_service_daemons", - "title" : "Service Daemons" - }, - "pveceph_fs" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs", - "subtitle" : "CephFS", - "title" : "Deploy Hyper-Converged Ceph Cluster" - }, - "pveceph_fs_create" : { - "link" : "/pve-docs/chapter-pveceph.html#pveceph_fs_create", - "subtitle" : "Create CephFS", - "title" : "Deploy Hyper-Converged Ceph Cluster" - }, - "pvecm_create_cluster" : { - "link" : "/pve-docs/chapter-pvecm.html#pvecm_create_cluster", - "subtitle" : "Create a Cluster", - "title" : "Cluster Manager" - }, - "pvecm_join_node_to_cluster" : { - "link" : "/pve-docs/chapter-pvecm.html#pvecm_join_node_to_cluster", - "subtitle" : "Adding Nodes to the Cluster", - "title" : "Cluster Manager" - }, - "pvesr_schedule_time_format" : { - "link" : "/pve-docs/chapter-pvesr.html#pvesr_schedule_time_format", - "subtitle" : "Schedule Format", - "title" : "Storage Replication" - }, - "pveum_authentication_realms" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_authentication_realms", - "subtitle" : "Authentication Realms", - "title" : "User Management" - }, - "pveum_configure_u2f" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_configure_u2f", - "subtitle" : "Server side U2F configuration", - "title" : "User Management" - }, - "pveum_groups" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_groups", - "subtitle" : "Groups", - "title" : "User Management" - }, - "pveum_permission_management" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_permission_management", - "subtitle" : "Permission Management", - "title" : "User Management" - }, - "pveum_pools" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_pools", - "subtitle" : "Pools", - "title" : "User Management" - }, - "pveum_roles" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_roles", - "subtitle" : "Roles", - "title" : "User Management" - }, - "pveum_tfa_auth" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_tfa_auth", - "subtitle" : "Two-factor authentication", - "title" : "User Management" - }, - "pveum_users" : { - "link" : "/pve-docs/chapter-pveum.html#pveum_users", - "subtitle" : "Users", - "title" : "User Management" - }, - "qm_bios_and_uefi" : { - "link" : "/pve-docs/chapter-qm.html#qm_bios_and_uefi", - "subtitle" : "BIOS and UEFI", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cloud_init" : { - "link" : "/pve-docs/chapter-qm.html#qm_cloud_init", - "title" : "Cloud-Init Support" - }, - "qm_copy_and_clone" : { - "link" : "/pve-docs/chapter-qm.html#qm_copy_and_clone", - "subtitle" : "Copies and Clones", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_cpu" : { - "link" : "/pve-docs/chapter-qm.html#qm_cpu", - "subtitle" : "CPU", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_display" : { - "link" : "/pve-docs/chapter-qm.html#qm_display", - "subtitle" : "Display", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_general_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_general_settings", - "subtitle" : "General Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_hard_disk" : { - "link" : "/pve-docs/chapter-qm.html#qm_hard_disk", - "subtitle" : "Hard Disk", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_memory" : { - "link" : "/pve-docs/chapter-qm.html#qm_memory", - "subtitle" : "Memory", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_migration" : { - "link" : "/pve-docs/chapter-qm.html#qm_migration", - "subtitle" : "Migration", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_network_device" : { - "link" : "/pve-docs/chapter-qm.html#qm_network_device", - "subtitle" : "Network Device", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_options" : { - "link" : "/pve-docs/chapter-qm.html#qm_options", - "subtitle" : "Options", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_os_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_os_settings", - "subtitle" : "OS Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_pci_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_pci_passthrough", - "title" : "PCI(e) Passthrough" - }, - "qm_spice_enhancements" : { - "link" : "/pve-docs/chapter-qm.html#qm_spice_enhancements", - "subtitle" : "SPICE Enhancements", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_startup_and_shutdown" : { - "link" : "/pve-docs/chapter-qm.html#qm_startup_and_shutdown", - "subtitle" : "Automatic Start and Shutdown of Virtual Machines", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_system_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_system_settings", - "subtitle" : "System Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_usb_passthrough" : { - "link" : "/pve-docs/chapter-qm.html#qm_usb_passthrough", - "subtitle" : "USB Passthrough", - "title" : "Qemu/KVM Virtual Machines" - }, - "qm_virtual_machines_settings" : { - "link" : "/pve-docs/chapter-qm.html#qm_virtual_machines_settings", - "subtitle" : "Virtual Machines Settings", - "title" : "Qemu/KVM Virtual Machines" - }, - "storage_cephfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cephfs", - "title" : "Ceph Filesystem (CephFS)" - }, - "storage_cifs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_cifs", - "title" : "CIFS Backend" - }, - "storage_directory" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_directory", - "title" : "Directory Backend" - }, - "storage_glusterfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_glusterfs", - "title" : "GlusterFS Backend" - }, - "storage_lvm" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvm", - "title" : "LVM Backend" - }, - "storage_lvmthin" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_lvmthin", - "title" : "LVM thin Backend" - }, - "storage_nfs" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_nfs", - "title" : "NFS Backend" - }, - "storage_open_iscsi" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_open_iscsi", - "title" : "Open-iSCSI initiator" - }, - "storage_zfspool" : { - "link" : "/pve-docs/chapter-pvesm.html#storage_zfspool", - "title" : "Local ZFS Pool Backend" - }, - "sysadmin_certificate_management" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_certificate_management", - "title" : "Certificate Management" - }, - "sysadmin_network_configuration" : { - "link" : "/pve-docs/chapter-sysadmin.html#sysadmin_network_configuration", - "title" : "Network Configuration" - } -}; -Ext.ns('PVE'); - -// 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)) { - var console = { - log: function() {} - }; -} -console.log("Starting PVE Manager"); - -Ext.Ajax.defaultHeaders = { - 'Accept': 'application/json' -}; - -/*jslint confusion: true */ -Ext.define('PVE.Utils', { utilities: { - - // this singleton contains miscellaneous utilities - - toolkit: undefined, // (extjs|touch), set inside Toolkit.js - - bus_match: /^(ide|sata|virtio|scsi)\d+$/, - - log_severity_hash: { - 0: "panic", - 1: "alert", - 2: "critical", - 3: "error", - 4: "warning", - 5: "notice", - 6: "info", - 7: "debug" - }, - - support_level_hash: { - 'c': gettext('Community'), - 'b': gettext('Basic'), - 's': gettext('Standard'), - 'p': gettext('Premium') - }, - - noSubKeyHtml: 'You do not have a valid subscription for this server. Please visit www.proxmox.com to get a list of available options.', - - kvm_ostypes: { - 'Linux': [ - { desc: '5.x - 2.6 Kernel', val: 'l26' }, - { desc: '2.4 Kernel', val: 'l24' } - ], - 'Microsoft Windows': [ - { desc: '10/2016/2019', val: 'win10' }, - { desc: '8.x/2012/2012r2', val: 'win8' }, - { desc: '7/2008r2', val: 'win7' }, - { desc: 'Vista/2008', val: 'w2k8' }, - { desc: 'XP/2003', val: 'wxp' }, - { desc: '2000', val: 'w2k' } - ], - 'Solaris Kernel': [ - { desc: '-', val: 'solaris'} - ], - 'Other': [ - { desc: '-', val: 'other'} - ] - }, - - get_health_icon: function(state, circle) { - if (circle === undefined) { - circle = false; - } - - if (state === undefined) { - state = 'uknown'; - } - - var icon = 'faded fa-question'; - switch(state) { - case 'good': - icon = 'good fa-check'; - break; - case 'upgrade': - icon = 'warning fa-upload'; - break; - case 'old': - icon = 'warning fa-refresh'; - break; - case 'warning': - icon = 'warning fa-exclamation'; - break; - case 'critical': - icon = 'critical fa-times'; - break; - default: break; - } - - if (circle) { - icon += '-circle'; - } - - return icon; - }, - - parse_ceph_version: function(service) { - if (service.ceph_version_short) { - return service.ceph_version_short; - } - - if (service.ceph_version) { - var match = service.ceph_version.match(/version (\d+(\.\d+)*)/); - if (match) { - return match[1]; - } - } - - return undefined; - }, - - compare_ceph_versions: function(a, b) { - if (a === b) { - return 0; - } - let avers = a.toString().split('.'); - let bvers = b.toString().split('.'); - - while (true) { - let av = avers.shift(); - let bv = bvers.shift(); - - if (av === undefined && bv === undefined) { - return 0; - } else if (av === undefined) { - return -1; - } else if (bv === undefined) { - return 1; - } else { - let diff = parseInt(av, 10) - parseInt(bv, 10); - if (diff != 0) return diff; - // else we need to look at the next parts - } - } - - }, - - get_ceph_icon_html: function(health, fw) { - var state = PVE.Utils.map_ceph_health[health]; - var cls = PVE.Utils.get_health_icon(state); - if (fw) { - cls += ' fa-fw'; - } - return " "; - }, - - map_ceph_health: { - 'HEALTH_OK':'good', - 'HEALTH_UPGRADE':'upgrade', - 'HEALTH_OLD':'old', - 'HEALTH_WARN':'warning', - 'HEALTH_ERR':'critical' - }, - - render_ceph_health: function(healthObj) { - var state = { - iconCls: PVE.Utils.get_health_icon(), - text: '' - }; - - if (!healthObj || !healthObj.status) { - return state; - } - - var health = PVE.Utils.map_ceph_health[healthObj.status]; - - state.iconCls = PVE.Utils.get_health_icon(health, true); - state.text = healthObj.status; - - return state; - }, - - render_zfs_health: function(value) { - if (typeof value == 'undefined'){ - return ""; - } - var iconCls = 'question-circle'; - switch (value) { - case 'AVAIL': - case 'ONLINE': - iconCls = 'check-circle good'; - break; - case 'REMOVED': - case 'DEGRADED': - iconCls = 'exclamation-circle warning'; - break; - case 'UNAVAIL': - case 'FAULTED': - case 'OFFLINE': - iconCls = 'times-circle critical'; - break; - default: //unknown - } - - return ' ' + value; - - }, - - get_kvm_osinfo: function(value) { - var info = { base: 'Other' }; // default - if (value) { - Ext.each(Object.keys(PVE.Utils.kvm_ostypes), function(k) { - Ext.each(PVE.Utils.kvm_ostypes[k], function(e) { - if (e.val === value) { - info = { desc: e.desc, base: k }; - } - }); - }); - } - return info; - }, - - render_kvm_ostype: function (value) { - var osinfo = PVE.Utils.get_kvm_osinfo(value); - if (osinfo.desc && osinfo.desc !== '-') { - return osinfo.base + ' ' + osinfo.desc; - } else { - return osinfo.base; - } - }, - - render_hotplug_features: function (value) { - var fa = []; - - if (!value || (value === '0')) { - return gettext('Disabled'); - } - - if (value === '1') { - value = 'disk,network,usb'; - } - - Ext.each(value.split(','), function(el) { - if (el === 'disk') { - fa.push(gettext('Disk')); - } else if (el === 'network') { - fa.push(gettext('Network')); - } else if (el === 'usb') { - fa.push('USB'); - } else if (el === 'memory') { - fa.push(gettext('Memory')); - } else if (el === 'cpu') { - fa.push(gettext('CPU')); - } else { - fa.push(el); - } - }); - - return fa.join(', '); - }, - - render_qga_features: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (' + Proxmox.Utils.disabledText + ')'; - } - var props = PVE.Parser.parsePropertyString(value, 'enabled'); - if (!PVE.Parser.parseBoolean(props.enabled)) { - return Proxmox.Utils.disabledText; - } - - delete props.enabled; - var agentstring = Proxmox.Utils.enabledText; - - Ext.Object.each(props, function(key, value) { - var keystring = '' ; - agentstring += ', ' + key + ': '; - - if (key === 'type') { - let map = { - isa: "ISA", - virtio: "VirtIO", - }; - agentstring += map[value] || Proxmox.Utils.unknownText; - } else { - if (PVE.Parser.parseBoolean(value)) { - agentstring += Proxmox.Utils.enabledText; - } else { - agentstring += Proxmox.Utils.disabledText; - } - } - }); - - return agentstring; - }, - - render_qemu_machine: function(value) { - return value || (Proxmox.Utils.defaultText + ' (i440fx)'); - }, - - render_qemu_bios: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (SeaBIOS)'; - } else if (value === 'seabios') { - return "SeaBIOS"; - } else if (value === 'ovmf') { - return "OVMF (UEFI)"; - } else { - return value; - } - }, - - render_dc_ha_opts: function(value) { - if (!value) { - return Proxmox.Utils.defaultText; - } else { - return PVE.Parser.printPropertyString(value); - } - }, - render_as_property_string: function(value) { - return (!value) ? Proxmox.Utils.defaultText - : PVE.Parser.printPropertyString(value); - }, - - render_scsihw: function(value) { - if (!value) { - return Proxmox.Utils.defaultText + ' (LSI 53C895A)'; - } else if (value === 'lsi') { - return 'LSI 53C895A'; - } else if (value === 'lsi53c810') { - return 'LSI 53C810'; - } else if (value === 'megasas') { - return 'MegaRAID SAS 8708EM2'; - } else if (value === 'virtio-scsi-pci') { - return 'VirtIO SCSI'; - } else if (value === 'virtio-scsi-single') { - return 'VirtIO SCSI single'; - } else if (value === 'pvscsi') { - return 'VMware PVSCSI'; - } else { - return value; - } - }, - - render_spice_enhancements: function(values) { - let props = PVE.Parser.parsePropertyString(values); - if (Ext.Object.isEmpty(props)) { - return Proxmox.Utils.noneText; - } - - let output = []; - if (PVE.Parser.parseBoolean(props.foldersharing)) { - output.push('Folder Sharing: ' + gettext('Enabled')); - } - if (props.videostreaming === 'all' || props.videostreaming === 'filter') { - output.push('Video Streaming: ' + props.videostreaming); - } - return output.join(', '); - }, - - // fixme: auto-generate this - // for now, please keep in sync with PVE::Tools::kvmkeymaps - kvm_keymaps: { - //ar: 'Arabic', - da: 'Danish', - de: 'German', - 'de-ch': 'German (Swiss)', - 'en-gb': 'English (UK)', - 'en-us': 'English (USA)', - es: 'Spanish', - //et: 'Estonia', - fi: 'Finnish', - //fo: 'Faroe Islands', - fr: 'French', - 'fr-be': 'French (Belgium)', - 'fr-ca': 'French (Canada)', - 'fr-ch': 'French (Swiss)', - //hr: 'Croatia', - hu: 'Hungarian', - is: 'Icelandic', - it: 'Italian', - ja: 'Japanese', - lt: 'Lithuanian', - //lv: 'Latvian', - mk: 'Macedonian', - nl: 'Dutch', - //'nl-be': 'Dutch (Belgium)', - no: 'Norwegian', - pl: 'Polish', - pt: 'Portuguese', - 'pt-br': 'Portuguese (Brazil)', - //ru: 'Russian', - sl: 'Slovenian', - sv: 'Swedish', - //th: 'Thai', - tr: 'Turkish' - }, - - kvm_vga_drivers: { - std: gettext('Standard VGA'), - vmware: gettext('VMware compatible'), - qxl: 'SPICE', - qxl2: 'SPICE dual monitor', - qxl3: 'SPICE three monitors', - qxl4: 'SPICE four monitors', - serial0: gettext('Serial terminal') + ' 0', - serial1: gettext('Serial terminal') + ' 1', - serial2: gettext('Serial terminal') + ' 2', - serial3: gettext('Serial terminal') + ' 3', - virtio: 'VirtIO-GPU', - none: Proxmox.Utils.noneText - }, - - render_kvm_language: function (value) { - if (!value || value === '__default__') { - return Proxmox.Utils.defaultText; - } - var text = PVE.Utils.kvm_keymaps[value]; - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_keymap_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_language('')]]; - Ext.Object.each(PVE.Utils.kvm_keymaps, function(key, value) { - data.push([key, PVE.Utils.render_kvm_language(value)]); - }); - - return data; - }, - - console_map: { - '__default__': Proxmox.Utils.defaultText + ' (xterm.js)', - 'vv': 'SPICE (remote-viewer)', - 'html5': 'HTML5 (noVNC)', - 'xtermjs': 'xterm.js' - }, - - render_console_viewer: function(value) { - value = value || '__default__'; - if (PVE.Utils.console_map[value]) { - return PVE.Utils.console_map[value]; - } - return value; - }, - - console_viewer_array: function() { - return Ext.Array.map(Object.keys(PVE.Utils.console_map), function(v) { - return [v, PVE.Utils.render_console_viewer(v)]; - }); - }, - - render_kvm_vga_driver: function (value) { - if (!value) { - return Proxmox.Utils.defaultText; - } - var vga = PVE.Parser.parsePropertyString(value, 'type'); - var text = PVE.Utils.kvm_vga_drivers[vga.type]; - if (!vga.type) { - text = Proxmox.Utils.defaultText; - } - if (text) { - return text + ' (' + value + ')'; - } - return value; - }, - - kvm_vga_driver_array: function() { - var data = [['__default__', PVE.Utils.render_kvm_vga_driver('')]]; - Ext.Object.each(PVE.Utils.kvm_vga_drivers, function(key, value) { - data.push([key, PVE.Utils.render_kvm_vga_driver(value)]); - }); - - return data; - }, - - render_kvm_startup: function(value) { - var startup = PVE.Parser.parseStartup(value); - - var res = 'order='; - if (startup.order === undefined) { - res += 'any'; - } else { - res += startup.order; - } - if (startup.up !== undefined) { - res += ',up=' + startup.up; - } - if (startup.down !== undefined) { - res += ',down=' + startup.down; - } - - return res; - }, - - extractFormActionError: function(action) { - var msg; - switch (action.failureType) { - case Ext.form.action.Action.CLIENT_INVALID: - msg = gettext('Form fields may not be submitted with invalid values'); - break; - case Ext.form.action.Action.CONNECT_FAILURE: - msg = gettext('Connection error'); - var resp = action.response; - if (resp.status && resp.statusText) { - msg += " " + resp.status + ": " + resp.statusText; - } - break; - case Ext.form.action.Action.LOAD_FAILURE: - case Ext.form.action.Action.SERVER_INVALID: - msg = Proxmox.Utils.extractRequestError(action.result, true); - break; - } - return msg; - }, - - format_duration_short: function(ut) { - - if (ut < 60) { - return ut.toFixed(1) + 's'; - } - - if (ut < 3600) { - var mins = ut / 60; - return mins.toFixed(1) + 'm'; - } - - if (ut < 86400) { - var hours = ut / 3600; - return hours.toFixed(1) + 'h'; - } - - var days = ut / 86400; - return days.toFixed(1) + 'd'; - }, - - contentTypes: { - 'images': gettext('Disk image'), - 'backup': gettext('VZDump backup file'), - 'vztmpl': gettext('Container template'), - 'iso': gettext('ISO image'), - 'rootdir': gettext('Container'), - 'snippets': gettext('Snippets') - }, - - volume_is_qemu_backup: function(volid, format) { - return format === 'pbs-vm' || volid.match(':backup/vzdump-qemu-'); - }, - - volume_is_lxc_backup: function(volid, format) { - return format === 'pbs-ct' || volid.match(':backup/vzdump-(lxc|openvz)-'); - }, - - storageSchema: { - dir: { - name: Proxmox.Utils.directoryText, - ipanel: 'DirInputPanel', - faIcon: 'folder' - }, - lvm: { - name: 'LVM', - ipanel: 'LVMInputPanel', - faIcon: 'folder' - }, - lvmthin: { - name: 'LVM-Thin', - ipanel: 'LvmThinInputPanel', - faIcon: 'folder' - }, - nfs: { - name: 'NFS', - ipanel: 'NFSInputPanel', - faIcon: 'building' - }, - cifs: { - name: 'CIFS', - ipanel: 'CIFSInputPanel', - faIcon: 'building' - }, - glusterfs: { - name: 'GlusterFS', - ipanel: 'GlusterFsInputPanel', - faIcon: 'building' - }, - iscsi: { - name: 'iSCSI', - ipanel: 'IScsiInputPanel', - faIcon: 'building' - }, - cephfs: { - name: 'CephFS', - ipanel: 'CephFSInputPanel', - faIcon: 'building' - }, - pvecephfs: { - name: 'CephFS (PVE)', - ipanel: 'CephFSInputPanel', - hideAdd: true, - faIcon: 'building' - }, - rbd: { - name: 'RBD', - ipanel: 'RBDInputPanel', - faIcon: 'building' - }, - pveceph: { - name: 'RBD (PVE)', - ipanel: 'RBDInputPanel', - hideAdd: true, - faIcon: 'building' - }, - zfs: { - name: 'ZFS over iSCSI', - ipanel: 'ZFSInputPanel', - faIcon: 'building' - }, - zfspool: { - name: 'ZFS', - ipanel: 'ZFSPoolInputPanel', - faIcon: 'folder' - }, - drbd: { - name: 'DRBD', - hideAdd: true - } - }, - - format_storage_type: function(value, md, record) { - if (value === 'rbd') { - value = (!record || record.get('monhost') ? 'rbd' : 'pveceph'); - } else if (value === 'cephfs') { - value = (!record || record.get('monhost') ? 'cephfs' : 'pvecephfs'); - } - - var schema = PVE.Utils.storageSchema[value]; - if (schema) { - return schema.name; - } - return Proxmox.Utils.unknownText; - }, - - format_ha: function(value) { - var text = Proxmox.Utils.noneText; - - if (value.managed) { - text = value.state || Proxmox.Utils.noneText; - - text += ', ' + Proxmox.Utils.groupText + ': '; - text += value.group || Proxmox.Utils.noneText; - } - - return text; - }, - - format_content_types: function(value) { - return value.split(',').sort().map(function(ct) { - return PVE.Utils.contentTypes[ct] || ct; - }).join(', '); - }, - - render_storage_content: function(value, metaData, record) { - var data = record.data; - if (Ext.isNumber(data.channel) && - Ext.isNumber(data.id) && - Ext.isNumber(data.lun)) { - return "CH " + - Ext.String.leftPad(data.channel,2, '0') + - " ID " + data.id + " LUN " + data.lun; - } - return data.volid.replace(/^.*?:(.*?\/)?/,''); - }, - - render_serverity: function (value) { - return PVE.Utils.log_severity_hash[value] || value; - }, - - render_cpu: function(value, metaData, record, rowIndex, colIndex, store) { - - if (!(record.data.uptime && Ext.isNumeric(value))) { - return ''; - } - - var maxcpu = record.data.maxcpu || 1; - - if (!Ext.isNumeric(maxcpu) && (maxcpu >= 1)) { - return ''; - } - - var per = value * 100; - - return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU'); - }, - - render_size: function(value, metaData, record, rowIndex, colIndex, store) { - /*jslint confusion: true */ - - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value); - }, - - render_bandwidth: function(value) { - if (!Ext.isNumeric(value)) { - return ''; - } - - return Proxmox.Utils.format_size(value) + '/s'; - }, - - render_timestamp_human_readable: function(value) { - return Ext.Date.format(new Date(value * 1000), 'l d F Y H:i:s'); - }, - - render_duration: function(value) { - if (value === undefined) { - return '-'; - } - return PVE.Utils.format_duration_short(value); - }, - - calculate_mem_usage: function(data) { - if (!Ext.isNumeric(data.mem) || - data.maxmem === 0 || - data.uptime < 1) { - return -1; - } - - return (data.mem / data.maxmem); - }, - - render_mem_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - if (value > 1 ) { - // we got no percentage but bytes - var mem = value; - var maxmem = record.data.maxmem; - if (!record.data.uptime || - maxmem === 0 || - !Ext.isNumeric(mem)) { - return ''; - } - - return ((mem*100)/maxmem).toFixed(1) + " %"; - } - return (value*100).toFixed(1) + " %"; - }, - - render_mem_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var mem = value; - var maxmem = record.data.maxmem; - - if (!record.data.uptime) { - return ''; - } - - if (!(Ext.isNumeric(mem) && maxmem)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - calculate_disk_usage: function(data) { - - if (!Ext.isNumeric(data.disk) || - data.type === 'qemu' || - (data.type === 'lxc' && data.uptime === 0) || - data.maxdisk === 0) { - return -1; - } - - return (data.disk / data.maxdisk); - }, - - render_disk_usage_percent: function(value, metaData, record, rowIndex, colIndex, store) { - if (!Ext.isNumeric(value) || value === -1) { - return ''; - } - - return (value * 100).toFixed(1) + " %"; - }, - - render_disk_usage: function(value, metaData, record, rowIndex, colIndex, store) { - - var disk = value; - var maxdisk = record.data.maxdisk; - var type = record.data.type; - - if (!Ext.isNumeric(disk) || - type === 'qemu' || - maxdisk === 0 || - (type === 'lxc' && record.data.uptime === 0)) { - return ''; - } - - return PVE.Utils.render_size(value); - }, - - get_object_icon_class: function(type, record) { - var status = ''; - var objType = type; - - if (type === 'type') { - // for folder view - objType = record.groupbyid; - } else if (record.template) { - // templates - objType = 'template'; - status = type; - } else { - // everything else - status = record.status + ' ha-' + record.hastate; - } - - if (record.lock) { - status += ' locked lock-' + record.lock; - } - - var defaults = PVE.tree.ResourceTree.typeDefaults[objType]; - if (defaults && defaults.iconCls) { - var retVal = defaults.iconCls + ' ' + status; - return retVal; - } - - return ''; - }, - - render_resource_type: function(value, metaData, record, rowIndex, colIndex, store) { - - var cls = PVE.Utils.get_object_icon_class(value,record.data); - - var fa = ' '; - return fa + value; - }, - - render_support_level: function(value, metaData, record) { - return PVE.Utils.support_level_hash[value] || '-'; - }, - - render_upid: function(value, metaData, record) { - var type = record.data.type; - var id = record.data.id; - - return Proxmox.Utils.format_task_description(type, id); - }, - - /* render functions for new status panel */ - - render_usage: function(val) { - return (val*100).toFixed(2) + '%'; - }, - - render_cpu_usage: function(val, max) { - return Ext.String.format(gettext('{0}% of {1}') + - ' ' + gettext('CPU(s)'), (val*100).toFixed(2), max); - }, - - render_size_usage: function(val, max) { - if (max === 0) { - return gettext('N/A'); - } - return (val*100/max).toFixed(2) + '% '+ '(' + - Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(val), PVE.Utils.render_size(max)) + ')'; - }, - - /* this is different for nodes */ - render_node_cpu_usage: function(value, record) { - return PVE.Utils.render_cpu_usage(value, record.cpus); - }, - - /* this is different for nodes */ - render_node_size_usage: function(record) { - return PVE.Utils.render_size_usage(record.used, record.total); - }, - - render_optional_url: function(value) { - var match; - if (value && (match = value.match(/^https?:\/\//)) !== null) { - return '' + value + ''; - } - return value; - }, - - render_san: function(value) { - var names = []; - if (Ext.isArray(value)) { - value.forEach(function(val) { - if (!Ext.isNumber(val)) { - names.push(val); - } - }); - return names.join('
'); - } - return value; - }, - - render_full_name: function(firstname, metaData, record) { - var first = firstname || ''; - var last = record.data.lastname || ''; - return Ext.htmlEncode(first + " " + last); - }, - - render_u2f_error: function(error) { - var ErrorNames = { - '1': gettext('Other Error'), - '2': gettext('Bad Request'), - '3': gettext('Configuration Unsupported'), - '4': gettext('Device Ineligible'), - '5': gettext('Timeout') - }; - return "U2F Error: " + ErrorNames[error] || Proxmox.Utils.unknownText; - }, - - windowHostname: function() { - return window.location.hostname.replace(Proxmox.Utils.IP6_bracket_match, - function(m, addr, offset, original) { return addr; }); - }, - - openDefaultConsoleWindow: function(consoles, vmtype, vmid, nodename, vmname, cmd) { - var dv = PVE.Utils.defaultViewer(consoles); - PVE.Utils.openConsoleWindow(dv, vmtype, vmid, nodename, vmname, cmd); - }, - - openConsoleWindow: function(viewer, vmtype, vmid, nodename, vmname, cmd) { - // kvm, lxc, shell, upgrade - - if (vmid == undefined && (vmtype === 'kvm' || vmtype === 'lxc')) { - throw "missing vmid"; - } - - if (!nodename) { - throw "no nodename specified"; - } - - if (viewer === 'html5') { - PVE.Utils.openVNCViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'xtermjs') { - Proxmox.Utils.openXtermJsViewer(vmtype, vmid, nodename, vmname, cmd); - } else if (viewer === 'vv') { - var url; - var params = { proxy: PVE.Utils.windowHostname() }; - if (vmtype === 'kvm') { - url = '/nodes/' + nodename + '/qemu/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'lxc') { - url = '/nodes/' + nodename + '/lxc/' + vmid.toString() + '/spiceproxy'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'shell') { - url = '/nodes/' + nodename + '/spiceshell'; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'upgrade') { - url = '/nodes/' + nodename + '/spiceshell'; - params.upgrade = 1; - PVE.Utils.openSpiceViewer(url, params); - } else if (vmtype === 'cmd') { - url = '/nodes/' + nodename + '/spiceshell'; - params.cmd = cmd; - PVE.Utils.openSpiceViewer(url, params); - } - } else { - throw "unknown viewer type"; - } - }, - - defaultViewer: function(consoles) { - - var allowSpice, allowXtermjs; - - if (consoles === true) { - allowSpice = true; - allowXtermjs = true; - } else if (typeof consoles === 'object') { - allowSpice = consoles.spice; - allowXtermjs = !!consoles.xtermjs; - } - var dv = PVE.VersionInfo.console || 'xtermjs'; - if (dv === 'vv' && !allowSpice) { - dv = (allowXtermjs) ? 'xtermjs' : 'html5'; - } else if (dv === 'xtermjs' && !allowXtermjs) { - dv = (allowSpice) ? 'vv' : 'html5'; - } - - return dv; - }, - - openVNCViewer: function(vmtype, vmid, nodename, vmname, cmd) { - let scaling = 'off'; - if (Proxmox.Utils.toolkit !== 'touch') { - var sp = Ext.state.Manager.getProvider(); - scaling = sp.get('novnc-scaling', 'off'); - } - var url = Ext.Object.toQueryString({ - console: vmtype, // kvm, lxc, upgrade or shell - novnc: 1, - vmid: vmid, - vmname: vmname, - node: nodename, - resize: scaling, - cmd: cmd - }); - var nw = window.open("?" + url, '_blank', "innerWidth=745,innerheight=427"); - if (nw) { - nw.focus(); - } - }, - - openSpiceViewer: function(url, params){ - - var downloadWithName = function(uri, name) { - var link = Ext.DomHelper.append(document.body, { - tag: 'a', - href: uri, - css : 'display:none;visibility:hidden;height:0px;' - }); - - // Note: we need to tell android the correct file name extension - // but we do not set 'download' tag for other environments, because - // It can have strange side effects (additional user prompt on firefox) - var andriod = navigator.userAgent.match(/Android/i) ? true : false; - if (andriod) { - link.download = name; - } - - if (link.fireEvent) { - link.fireEvent('onclick'); - } else { - var evt = document.createEvent("MouseEvents"); - evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null); - link.dispatchEvent(evt); - } - }; - - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - failure: function(response, opts){ - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts){ - var raw = "[virt-viewer]\n"; - Ext.Object.each(response.result.data, function(k, v) { - raw += k + "=" + v + "\n"; - }); - var url = 'data:application/x-virt-viewer;charset=UTF-8,' + - encodeURIComponent(raw); - - downloadWithName(url, "pve-spice.vv"); - } - }); - }, - - openTreeConsole: function(tree, record, item, index, e) { - e.stopEvent(); - var nodename = record.data.node; - var vmid = record.data.vmid; - var vmname = record.data.name; - if (record.data.type === 'qemu' && !record.data.template) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - let conf = response.result.data; - var consoles = { - spice: !!conf.spice, - xtermjs: !!conf.serial, - }; - PVE.Utils.openDefaultConsoleWindow(consoles, 'kvm', vmid, nodename, vmname); - } - }); - } else if (record.data.type === 'lxc' && !record.data.template) { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - }, - - // test automation helper - call_menu_handler: function(menu, text) { - - var list = menu.query('menuitem'); - - Ext.Array.each(list, function(item) { - if (item.text === text) { - if (item.handler) { - item.handler(); - return 1; - } else { - return undefined; - } - } - }); - }, - - createCmdMenu: function(v, record, item, index, event) { - event.stopEvent(); - if (!(v instanceof Ext.tree.View)) { - v.select(record); - } - var menu; - var template = !!record.data.template; - var type = record.data.type; - - if (template) { - if (type === 'qemu' || type == 'lxc') { - menu = Ext.create('PVE.menu.TemplateMenu', { - pveSelNode: record - }); - } - } else if (type === 'qemu' || - type === 'lxc' || - type === 'node') { - menu = Ext.create('PVE.' + type + '.CmdMenu', { - pveSelNode: record, - nodename: record.data.node - }); - } else { - return; - } - - menu.showAt(event.getXY()); - return menu; - }, - - // helper for deleting field which are set to there default values - delete_if_default: function(values, fieldname, default_val, create) { - if (values[fieldname] === '' || values[fieldname] === default_val) { - if (!create) { - if (values['delete']) { - values['delete'] += ',' + fieldname; - } else { - values['delete'] = fieldname; - } - } - - delete values[fieldname]; - } - }, - - loadSSHKeyFromFile: function(file, callback) { - // ssh-keygen produces 740 bytes for an average 4096 bit rsa key, with - // a user@host comment, 1420 for 8192 bits; current max is 16kbit - // assume: 740*8 for max. 32kbit (5920 byte file) - // round upwards to nearest nice number => 8192 bytes, leaves lots of comment space - if (file.size > 8192) { - Ext.Msg.alert(gettext('Error'), gettext("Invalid file size: ") + file.size); - return; - } - /*global - FileReader - */ - var reader = new FileReader(); - reader.onload = function(evt) { - callback(evt.target.result); - }; - reader.readAsText(file); - }, - - diskControllerMaxIDs: { - ide: 4, - sata: 6, - scsi: 31, - virtio: 16, - }, - - // types is either undefined (all busses), an array of busses, or a single bus - forEachBus: function(types, func) { - var busses = Object.keys(PVE.Utils.diskControllerMaxIDs); - var i, j, count, cont; - - if (Ext.isArray(types)) { - busses = types; - } else if (Ext.isDefined(types)) { - busses = [ types ]; - } - - // check if we only have valid busses - for (i = 0; i < busses.length; i++) { - if (!PVE.Utils.diskControllerMaxIDs[busses[i]]) { - throw "invalid bus: '" + busses[i] + "'"; - } - } - - for (i = 0; i < busses.length; i++) { - count = PVE.Utils.diskControllerMaxIDs[busses[i]]; - for (j = 0; j < count; j++) { - cont = func(busses[i], j); - if (!cont && cont !== undefined) { - return; - } - } - } - }, - - mp_counts: { mps: 256, unused: 256 }, - - forEachMP: function(func, includeUnused) { - var i, cont; - for (i = 0; i < PVE.Utils.mp_counts.mps; i++) { - cont = func('mp', i); - if (!cont && cont !== undefined) { - return; - } - } - - if (!includeUnused) { - return; - } - - for (i = 0; i < PVE.Utils.mp_counts.unused; i++) { - cont = func('unused', i); - if (!cont && cont !== undefined) { - return; - } - } - }, - - hardware_counts: { net: 32, usb: 5, hostpci: 16, audio: 1, efidisk: 1, serial: 4, rng: 1 }, - - cleanEmptyObjectKeys: function (obj) { - var propName; - for (propName in obj) { - if (obj.hasOwnProperty(propName)) { - if (obj[propName] === null || obj[propName] === undefined) { - delete obj[propName]; - } - } - } - }, - - handleStoreErrorOrMask: function(me, store, regex, callback) { - - me.mon(store, 'load', function (proxy, response, success, operation) { - - if (success) { - Proxmox.Utils.setErrorMask(me, false); - return; - } - var msg; - - if (operation.error.statusText) { - if (operation.error.statusText.match(regex)) { - callback(me, operation.error); - return; - } else { - msg = operation.error.statusText + ' (' + operation.error.status + ')'; - } - } else { - msg = gettext('Connection error'); - } - Proxmox.Utils.setErrorMask(me, msg); - }); - }, - - showCephInstallOrMask: function(container, msg, nodename, callback){ - var regex = new RegExp("not (installed|initialized)", "i"); - if (msg.match(regex)) { - if (Proxmox.UserName === 'root@pam') { - container.el.mask(); - if (!container.down('pveCephInstallWindow')){ - var isInstalled = msg.match(/not initialized/i) ? true : false; - var win = Ext.create('PVE.ceph.Install', { - nodename: nodename - }); - win.getViewModel().set('isInstalled', isInstalled); - container.add(win); - win.show(); - callback(win); - } - } else { - container.mask(Ext.String.format(gettext('{0} not installed.') + - ' ' + gettext('Log in as root to install.'), 'Ceph'), ['pve-static-mask']); - } - return true; - } else { - return false; - } - }, - - propertyStringSet: function(target, source, name, value) { - if (source) { - if (value === undefined) { - target[name] = source; - } else { - target[name] = value; - } - } else { - delete target[name]; - } - }, - - updateColumns: 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 < 1400 ? 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(); - }, - - forEachCorosyncLink: function(nodeinfo, cb) { - let re = /(?:ring|link)(\d+)_addr/; - Ext.iterate(nodeinfo, (prop, val) => { - let match = re.exec(prop); - if (match) { - cb(Number(match[1]), val); - } - }); - }, -}, - - singleton: true, - constructor: function() { - var me = this; - Ext.apply(me, me.utilities); - } - -}); -// ExtJS related things - -Proxmox.Utils.toolkit = 'extjs'; - -// custom PVE specific VTypes -Ext.apply(Ext.form.field.VTypes, { - - 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"', - IP64AddressList: function(v) { - var list = v.split(/[\ \,\;]+/); - var i; - for (i = 0; i < list.length; i++) { - if (list[i] == '') { - continue; - } - - if (!Proxmox.Utils.IP64_match.test(list[i])) { - return false; - } - } - - return true; - }, - IP64AddressListText: gettext('Example') + ': 192.168.1.1,192.168.1.2', - IP64AddressListMask: /[A-Fa-f0-9\,\:\.\;\ ]/ -}); - -Ext.define('PVE.form.field.Display', { - override: 'Ext.form.field.Display', - - setSubmitValue: function(value) { - // do nothing, this is only to allow generalized bindings for the: - // `me.isCreate ? 'textfield' : 'displayfield'` cases we have. - } -}); -// Some configuration values are complex strings - -// so we need parsers/generators for them. - -Ext.define('PVE.Parser', { statics: { - - // this class only contains static functions - - parseACME: function(value) { - if (!value) { - return; - } - - var res = {}; - var errors = false; - - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; //continue - } - - var match_res; - if ((match_res = p.match(/^(?:domains=)?((?:[a-zA-Z0-9\-\.]+[;, ]?)+)$/)) !== null) { - res.domains = match_res[1].split(/[;, ]/); - } else { - errors = true; - return false; - } - }); - - if (errors || !res) { - return; - } - - return res; - }, - - parseBoolean: function(value, default_value) { - if (!Ext.isDefined(value)) { - return default_value; - } - value = value.toLowerCase(); - return value === '1' || - value === 'on' || - value === 'yes' || - value === 'true'; - }, - - parsePropertyString: function(value, defaultKey) { - var res = {}, - error; - - if (typeof value !== 'string' || value === '') { - return res; - } - - Ext.Array.each(value.split(','), function(p) { - var kv = p.split('=', 2); - if (Ext.isDefined(kv[1])) { - res[kv[0]] = kv[1]; - } else if (Ext.isDefined(defaultKey)) { - if (Ext.isDefined(res[defaultKey])) { - error = 'defaultKey may be only defined once in propertyString'; - return false; // break - } - res[defaultKey] = kv[0]; - } else { - error = 'invalid propertyString, not a key=value pair and no defaultKey defined'; - return false; // break - } - }); - - if (error !== undefined) { - console.error(error); - return; - } - - return res; - }, - - printPropertyString: function(data, defaultKey) { - var stringparts = [], - gotDefaultKeyVal = false, - defaultKeyVal; - - Ext.Object.each(data, function(key, value) { - if (defaultKey !== undefined && key === defaultKey) { - gotDefaultKeyVal = true; - defaultKeyVal = value; - } else if (value !== '') { - stringparts.push(key + '=' + value); - } - }); - - stringparts = stringparts.sort(); - if (gotDefaultKeyVal) { - stringparts.unshift(defaultKeyVal); - } - - return stringparts.join(','); - }, - - parseQemuNetwork: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(ne2k_pci|e1000|e1000-82540em|e1000-82544gc|e1000-82545em|vmxnet3|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i)) !== null) { - res.model = match_res[1].toLowerCase(); - if (match_res[3]) { - res.macaddr = match_res[3]; - } - } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) { - res.bridge = match_res[1]; - } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) { - res.rate = match_res[1]; - } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) { - res.tag = match_res[1]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - res.firewall = match_res[1]; - } else if ((match_res = p.match(/^link_down=(\d+)$/)) !== null) { - res.disconnect = match_res[1]; - } else if ((match_res = p.match(/^queues=(\d+)$/)) !== null) { - res.queues = match_res[1]; - } else if ((match_res = p.match(/^trunks=(\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*)$/)) !== null) { - res.trunks = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors || !res.model) { - return; - } - - return res; - }, - - printQemuNetwork: function(net) { - - var netstr = net.model; - if (net.macaddr) { - netstr += "=" + net.macaddr; - } - if (net.bridge) { - netstr += ",bridge=" + net.bridge; - if (net.tag) { - netstr += ",tag=" + net.tag; - } - if (net.firewall) { - netstr += ",firewall=" + net.firewall; - } - } - if (net.rate) { - netstr += ",rate=" + net.rate; - } - if (net.queues) { - netstr += ",queues=" + net.queues; - } - if (net.disconnect) { - netstr += ",link_down=" + net.disconnect; - } - if (net.trunks) { - netstr += ",trunks=" + net.trunks; - } - return netstr; - }, - - parseQemuDrive: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var match_res = key.match(/^([a-z]+)(\d+)$/); - if (!match_res) { - return; - } - res['interface'] = match_res[1]; - res.index = match_res[2]; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - if (k === 'cache' && v === 'off') { - v = 'none'; - } - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - return res; - }, - - printQemuDrive: function(drive) { - - var drivestr = drive.file; - - Ext.Object.each(drive, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'index' || key === 'interface') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseIPConfig: function(key, value) { - if (!(key && value)) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - if ((match_res = p.match(/^ip=(\S+)$/)) !== null) { - res.ip = match_res[1]; - } else if ((match_res = p.match(/^gw=(\S+)$/)) !== null) { - res.gw = match_res[1]; - } else if ((match_res = p.match(/^ip6=(\S+)$/)) !== null) { - res.ip6 = match_res[1]; - } else if ((match_res = p.match(/^gw6=(\S+)$/)) !== null) { - res.gw6 = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printIPConfig: function(cfg) { - var c = ""; - var str = ""; - if (cfg.ip) { - str += "ip=" + cfg.ip; - c = ","; - } - if (cfg.gw) { - str += c + "gw=" + cfg.gw; - c = ","; - } - if (cfg.ip6) { - str += c + "ip6=" + cfg.ip6; - c = ","; - } - if (cfg.gw6) { - str += c + "gw6=" + cfg.gw6; - c = ","; - } - return str; - }, - - parseOpenVZNetIf: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(';'), function(item) { - if (!item || item.match(/^\s*$/)) { - return; // continue - } - - var data = {}; - Ext.Array.each(item.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - if (match_res[1] === 'bridge'){ - var bridgevlanf = match_res[2]; - var bridge_res = bridgevlanf.match(/^(vmbr(\d+))(v(\d+))?(f)?$/); - if (!bridge_res) { - errors = true; - return false; // break - } - data.bridge = bridge_res[1]; - data.tag = bridge_res[4]; - /*jslint confusion: true*/ - data.firewall = bridge_res[5] ? 1 : 0; - /*jslint confusion: false*/ - } else { - data[match_res[1]] = match_res[2]; - } - }); - - if (errors || !data.ifname) { - errors = true; - return false; // break - } - - data.raw = item; - - res[data.ifname] = data; - }); - - return errors ? undefined: res; - }, - - printOpenVZNetIf: function(netif) { - var netarray = []; - - Ext.Object.each(netif, function(iface, data) { - var tmparray = []; - Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname' , 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) { - var value = data[key]; - if (key === 'bridge'){ - if(data.tag){ - value = value + 'v' + data.tag; - } - if (data.firewall){ - value = value + 'f'; - } - } - if (value) { - tmparray.push(key + '=' + value); - } - - }); - netarray.push(tmparray.join(',')); - }); - - return netarray.join(';'); - }, - - parseLxcNetwork: function(value) { - if (!value) { - return; - } - - var data = {}; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|tag|rate)=(\S+)$/); - if (match_res) { - data[match_res[1]] = match_res[2]; - } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) { - data.firewall = PVE.Parser.parseBoolean(match_res[1]); - } else { - // todo: simply ignore errors ? - return; // continue - } - }); - - return data; - }, - - printLxcNetwork: function(data) { - var tmparray = []; - Ext.Array.each(['bridge', 'hwaddr', 'mtu', 'name', 'ip', - 'gw', 'ip6', 'gw6', 'firewall', 'tag'], function(key) { - var value = data[key]; - if (value) { - tmparray.push(key + '=' + value); - } - }); - - /*jslint confusion: true*/ - if (data.rate > 0) { - tmparray.push('rate=' + data.rate); - } - /*jslint confusion: false*/ - return tmparray.join(','); - }, - - parseLxcMountPoint: function(value) { - if (!value) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - var match_res = p.match(/^([a-z_]+)=(.+)$/); - if (!match_res) { - if (!p.match(/\=/)) { - res.file = p; - return; // continue - } - errors = true; - return false; // break - } - var k = match_res[1]; - if (k === 'volume') { - k = 'file'; - } - - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - var v = match_res[2]; - - res[k] = v; - }); - - if (errors || !res.file) { - return; - } - - var m = res.file.match(/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):/i); - if (m) { - res.storage = m[1]; - res.type = 'volume'; - } else if (res.file.match(/^\/dev\//)) { - res.type = 'device'; - } else { - res.type = 'bind'; - } - - return res; - }, - - printLxcMountPoint: function(mp) { - var drivestr = mp.file; - - Ext.Object.each(mp, function(key, value) { - if (!Ext.isDefined(value) || key === 'file' || - key === 'type' || key === 'storage') { - return; // continue - } - drivestr += ',' + key + '=' + value; - }); - - return drivestr; - }, - - parseStartup: function(value) { - if (value === undefined) { - return; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - var match_res; - - if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) { - res.order = match_res[2]; - } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) { - res.up = match_res[1]; - } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) { - res.down = match_res[1]; - } else { - errors = true; - return false; // break - } - }); - - if (errors) { - return; - } - - return res; - }, - - printStartup: function(startup) { - var arr = []; - if (startup.order !== undefined && startup.order !== '') { - arr.push('order=' + startup.order); - } - if (startup.up !== undefined && startup.up !== '') { - arr.push('up=' + startup.up); - } - if (startup.down !== undefined && startup.down !== '') { - arr.push('down=' + startup.down); - } - - return arr.join(','); - }, - - parseQemuSmbios1: function(value) { - var res = value.split(',').reduce(function (accumulator, currentValue) { - var splitted = currentValue.split(new RegExp("=(.+)")); - accumulator[splitted[0]] = splitted[1]; - return accumulator; - }, {}); - - if (PVE.Parser.parseBoolean(res.base64, false)) { - Ext.Object.each(res, function(key, value) { - if (key === 'uuid') { return; } - res[key] = Ext.util.Base64.decode(value); - }); - } - - return res; - }, - - printQemuSmbios1: function(data) { - - var datastr = ''; - var base64 = false; - Ext.Object.each(data, function(key, value) { - if (value === '') { return; } - if (key === 'uuid') { - datastr += (datastr !== '' ? ',' : '') + key + '=' + value; - } else { - // values should be base64 encoded from now on, mark config strings correspondingly - if (!base64) { - base64 = true; - datastr += (datastr !== '' ? ',' : '') + 'base64=1'; - } - datastr += (datastr !== '' ? ',' : '') + key + '=' + Ext.util.Base64.encode(value); - } - }); - - return datastr; - }, - - parseTfaConfig: function(value) { - var res = {}; - - Ext.Array.each(value.split(','), function(p) { - var kva = p.split('=', 2); - res[kva[0]] = kva[1]; - }); - - return res; - }, - - parseTfaType: function(value) { - /*jslint confusion: true*/ - var match; - if (!value || !value.length) { - return undefined; - } else if (value === 'x!oath') { - return 'totp'; - } else if (!!(match = value.match(/^x!(.+)$/))) { - return match[1]; - } else { - return 1; - } - }, - - parseQemuCpu: function(value) { - if (!value) { - return {}; - } - - var res = {}; - - var errors = false; - Ext.Array.each(value.split(','), function(p) { - if (!p || p.match(/^\s*$/)) { - return; // continue - } - - if (!p.match(/\=/)) { - if (Ext.isDefined(res.cpu)) { - errors = true; - return false; // break - } - res.cputype = p; - return; // continue - } - - var match_res = p.match(/^([a-z_]+)=(\S+)$/); - if (!match_res) { - errors = true; - return false; // break - } - - var k = match_res[1]; - if (Ext.isDefined(res[k])) { - errors = true; - return false; // break - } - - res[k] = match_res[2]; - }); - - if (errors || !res.cputype) { - return; - } - - return res; - }, - - printQemuCpu: function(cpu) { - var cpustr = cpu.cputype; - var optstr = ''; - - Ext.Object.each(cpu, function(key, value) { - if (!Ext.isDefined(value) || key === 'cputype') { - return; // continue - } - optstr += ',' + key + '=' + value; - }); - - if (!cpustr) { - if (optstr) { - return 'kvm64' + optstr; - } - return; - } - - return cpustr + optstr; - }, - - parseSSHKey: function(key) { - // |--- options can have quotes--| type key comment - var keyre = /^(?:((?:[^\s"]|\"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/; - var typere = /^(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)$/; - - var m = key.match(keyre); - if (!m) { - return null; - } - if (m.length < 3 || !m[2]) { // [2] is always either type or key - return null; - } - if (m[1] && m[1].match(typere)) { - return { - type: m[1], - key: m[2], - comment: m[3] - }; - } - if (m[2].match(typere)) { - return { - options: m[1], - type: m[2], - key: m[3], - comment: m[4] - }; - } - return null; - } -}}); -/* This state provider keeps part of the state inside - * the browser history. - * - * We compress (shorten) url using dictionary based compression - * i.e. use column separated list instead of url encoded hash: - * #v\d* version/format - * := indicates string values - * :\d+ lookup value in dictionary hash - * #v1:=value1:5:=value2:=value3:... -*/ - -Ext.define('PVE.StateProvider', { - extend: 'Ext.state.LocalStorageProvider', - - // private - setHV: function(name, newvalue, fireEvents) { - var me = this; - - var changes = false; - var oldtext = Ext.encode(me.UIState[name]); - var newtext = Ext.encode(newvalue); - if (newtext != oldtext) { - changes = true; - me.UIState[name] = newvalue; - //console.log("changed old " + name + " " + oldtext); - //console.log("changed new " + name + " " + newtext); - if (fireEvents) { - me.fireEvent("statechange", me, name, { value: newvalue }); - } - } - return changes; - }, - - // private - hslist: [ - // order is important for notifications - // [ name, default ] - ['view', 'server'], - ['rid', 'root'], - ['ltab', 'tasks'], - ['nodetab', ''], - ['storagetab', ''], - ['pooltab', ''], - ['kvmtab', ''], - ['lxctab', ''], - ['dctab', ''] - ], - - hprefix: 'v1', - - compDict: { - cloudinit: 52, - replication: 51, - system: 50, - monitor: 49, - 'ha-fencing': 48, - 'ha-groups': 47, - 'ha-resources': 46, - 'ceph-log': 45, - 'ceph-crushmap':44, - 'ceph-pools': 43, - 'ceph-osdtree': 42, - 'ceph-disklist': 41, - 'ceph-monlist': 40, - 'ceph-config': 39, - ceph: 38, - 'firewall-fwlog': 37, - 'firewall-options': 36, - 'firewall-ipset': 35, - 'firewall-aliases': 34, - 'firewall-sg': 33, - firewall: 32, - apt: 31, - members: 30, - snapshot: 29, - ha: 28, - support: 27, - pools: 26, - syslog: 25, - ubc: 24, - initlog: 23, - openvz: 22, - backup: 21, - resources: 20, - content: 19, - root: 18, - domains: 17, - roles: 16, - groups: 15, - users: 14, - time: 13, - dns: 12, - network: 11, - services: 10, - options: 9, - console: 8, - hardware: 7, - permissions: 6, - summary: 5, - tasks: 4, - clog: 3, - storage: 2, - folder: 1, - server: 0 - }, - - decodeHToken: function(token) { - var me = this; - - var state = {}; - if (!token) { - Ext.Array.each(me.hslist, function(rec) { - state[rec[0]] = rec[1]; - }); - return state; - } - - // return Ext.urlDecode(token); - - var items = token.split(':'); - var prefix = items.shift(); - - if (prefix != me.hprefix) { - return me.decodeHToken(); - } - - Ext.Array.each(me.hslist, function(rec) { - var value = items.shift(); - if (value) { - if (value[0] === '=') { - value = decodeURIComponent(value.slice(1)); - } else { - Ext.Object.each(me.compDict, function(key, cv) { - if (value == cv) { - value = key; - return false; - } - }); - } - } - state[rec[0]] = value; - }); - - return state; - }, - - encodeHToken: function(state) { - var me = this; - - // return Ext.urlEncode(state); - - var ctoken = me.hprefix; - Ext.Array.each(me.hslist, function(rec) { - var value = state[rec[0]]; - if (!Ext.isDefined(value)) { - value = rec[1]; - } - value = encodeURIComponent(value); - if (!value) { - ctoken += ':'; - } else { - var comp = me.compDict[value]; - if (Ext.isDefined(comp)) { - ctoken += ":" + comp; - } else { - ctoken += ":=" + value; - } - } - }); - - return ctoken; - }, - - constructor: function(config){ - var me = this; - - me.callParent([config]); - - me.UIState = me.decodeHToken(); // set default - - var history_change_cb = function(token) { - //console.log("HC " + token); - if (!token) { - var res = window.confirm(gettext('Are you sure you want to navigate away from this page?')); - if (res){ - // process text value and close... - Ext.History.back(); - } else { - Ext.History.forward(); - } - return; - } - - var newstate = me.decodeHToken(token); - Ext.Array.each(me.hslist, function(rec) { - if (typeof newstate[rec[0]] == "undefined") { - return; - } - me.setHV(rec[0], newstate[rec[0]], true); - }); - }; - - var start_token = Ext.History.getToken(); - if (start_token) { - history_change_cb(start_token); - } else { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - - Ext.History.on('change', history_change_cb); - }, - - get: function(name, defaultValue){ - /*jslint confusion: true */ - var me = this; - var data; - - if (typeof me.UIState[name] != "undefined") { - data = { value: me.UIState[name] }; - } else { - data = me.callParent(arguments); - if (!data && name === 'GuiCap') { - data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {} }; - } - } - - //console.log("GET " + name + " " + Ext.encode(data)); - return data; - }, - - clear: function(name){ - var me = this; - - if (typeof me.UIState[name] != "undefined") { - me.UIState[name] = null; - } - - me.callParent(arguments); - }, - - set: function(name, value, fireevent){ - var me = this; - - //console.log("SET " + name + " " + Ext.encode(value)); - if (typeof me.UIState[name] != "undefined") { - var newvalue = value ? value.value : null; - if (me.setHV(name, newvalue, fireevent)) { - var htext = me.encodeHToken(me.UIState); - Ext.History.add(htext); - } - } else { - me.callParent(arguments); - } - } -}); -Ext.define('PVE.menu.Item', { - extend: 'Ext.menu.Item', - alias: 'widget.pveMenuItem', - - // set to wrap the handler callback in a confirm dialog showing this text - confirmMsg: false, - - // set to focus 'No' instead of 'Yes' button and show a warning symbol - dangerous: false, - - initComponent: function() { - var me = this; - - if (me.handler) { - me.setHandler(me.handler, me.scope); - } - - me.callParent(); - }, - - setHandler: function(fn, scope) { - var me = this; - me.scope = scope; - me.handler = function(button, e) { - var rec, msg; - if (me.confirmMsg) { - msg = me.confirmMsg; - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - defaultFocus: me.dangerous ? 'no' : 'yes', - callback: function(btn) { - if (btn === 'yes') { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - } - }); - } else { - Ext.callback(fn, me.scope, [me, e], 0, me); - } - }; - } -}); -Ext.define('PVE.menu.TemplateMenu', { - extend: 'Ext.menu.Menu', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var guestType = me.pveSelNode.data.type; - if (guestType !== 'qemu' && guestType != 'lxc') { - throw "invalid guest type"; - } - - var vmname = me.pveSelNode.data.name; - - var template = me.pveSelNode.data.template; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - me.title = (guestType === 'qemu' ? 'VM ' : 'CT ') + vmid; - - me.items = [ - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: guestType, - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - handler: function() { - var win = Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: template - }); - win.show(); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.button.ConsoleButton', { - extend: 'Ext.button.Split', - alias: 'widget.pveConsoleButton', - - consoleType: 'shell', // one of 'shell', 'kvm', 'lxc', 'upgrade', 'cmd' - - cmd: undefined, - - consoleName: undefined, - - iconCls: 'fa fa-terminal', - - enableSpice: true, - enableXtermjs: true, - - nodename: undefined, - - vmid: 0, - - text: gettext('Console'), - - setEnableSpice: function(enable){ - var me = this; - - me.enableSpice = enable; - me.down('#spicemenu').setDisabled(!enable); - }, - - setEnableXtermJS: function(enable){ - var me = this; - - me.enableXtermjs = enable; - me.down('#xtermjs').setDisabled(!enable); - }, - - handler: function() { - var me = this; - var consoles = { - spice: me.enableSpice, - xtermjs: me.enableXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, me.consoleType, me.vmid, - me.nodename, me.consoleName, me.cmd); - }, - - menu: [ - { - xtype:'menuitem', - text: 'noVNC', - iconCls: 'pve-itype-icon-novnc', - type: 'html5', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - xterm: 'menuitem', - itemId: 'spicemenu', - text: 'SPICE', - type: 'vv', - iconCls: 'pve-itype-icon-virt-viewer', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - }, - { - text: 'xterm.js', - itemId: 'xtermjs', - iconCls: 'pve-itype-icon-xtermjs', - type: 'xtermjs', - handler: function(button) { - var me = this.up('button'); - PVE.Utils.openConsoleWindow(button.type, me.consoleType, me.vmid, me.nodename, me.consoleName, me.cmd); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.callParent(); - } -}); -Ext.define('PVE.button.PendingRevert', { - extend: 'Proxmox.button.Button', - alias: 'widget.pvePendingRevertButton', - - text: gettext('Revert'), - disabled: true, - config: { - pendingGrid: null, - apiurl: undefined, - }, - - handler: function() { - if (!this.pendingGrid) { - this.pendingGrid = this.up('proxmoxPendingObjectGrid'); - if (!this.pendingGrid) throw "revert button requires a pendingGrid"; - } - let view = this.pendingGrid; - - let rec = view.getSelectionModel().getSelection()[0]; - if (!rec) return; - - let rowdef = view.rows[rec.data.key] || {}; - let keys = rowdef.multiKey || [ rec.data.key ]; - - Proxmox.Utils.API2Request({ - url: this.apiurl || view.editorConfig.url, - waitMsgTarget: view, - selModel: view.getSelectionModel(), - method: 'PUT', - params: { - 'revert': keys.join(',') - }, - callback: () => view.reload(), - failure: (response) => Ext.Msg.alert('Error', response.htmlStatus), - }); - }, -}); -/* Button features: - * - observe selection changes to enable/disable the button using enableFn() - * - pop up confirmation dialog using confirmMsg() - * - * does this for the button and every menu item - */ -Ext.define('PVE.button.Split', { - extend: 'Ext.button.Split', - alias: 'widget.pveSplitButton', - - // the selection model to observe - selModel: undefined, - - // if 'false' handler will not be called (button disabled) - enableFn: function(record) { }, - - // function(record) or text - confirmMsg: false, - - // take special care in confirm box (select no as default). - dangerous: false, - - handlerWrapper: function(button, event) { - var me = this; - var rec, msg; - if (me.selModel) { - rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - return; - } - } - - if (me.confirmMsg) { - msg = me.confirmMsg; - // confirMsg can be boolean or function - /*jslint confusion: true*/ - if (Ext.isFunction(me.confirmMsg)) { - msg = me.confirmMsg(rec); - } - /*jslint confusion: false*/ - Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: msg, - buttons: Ext.Msg.YESNO, - callback: function(btn) { - if (btn !== 'yes') { - return; - } - me.realHandler(button, event, rec); - } - }); - } else { - me.realHandler(button, event, rec); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - - var me = this; - - if (me.handler) { - me.realHandler = me.handler; - me.handler = me.handlerWrapper; - } - - if (me.menu && me.menu.items) { - me.menu.items.forEach(function(item) { - if (item.handler) { - item.realHandler = item.handler; - item.handler = me.handlerWrapper; - } - - if (item.selModel) { - me.mon(item.selModel, "selectionchange", function() { - var rec = item.selModel.getSelection()[0]; - if (!rec || (item.enableFn(rec) === false )) { - item.setDisabled(true); - } else { - item.setDisabled(false); - } - }); - } - }); - } - - me.callParent(); - - if (me.selModel) { - - me.mon(me.selModel, "selectionchange", function() { - var rec = me.selModel.getSelection()[0]; - if (!rec || (me.enableFn(rec) === false)) { - me.setDisabled(true); - } else { - me.setDisabled(false); - } - }); - } - } -}); -Ext.define('PVE.controller.StorageEdit', { - extend: 'Ext.app.ViewController', - alias: 'controller.storageEdit', - control: { - 'field[name=content]': { - change: function(field, value) { - var hasBackups = Ext.Array.contains(value, 'backup'); - var maxfiles = this.lookupReference('maxfiles'); - if (!maxfiles) { - return; - } - - if (!hasBackups) { - // clear values which will never be submitted - maxfiles.reset(); - } - maxfiles.setDisabled(!hasBackups); - } - } - } -}); -Ext.define('PVE.qemu.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'suspended': - stopped = false; - suspended = true; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = "VM " + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - hidden: running || suspended, - disabled: running || suspended, - handler: function() { - vm_command('start'); - } - }, - { - text: gettext('Pause'), - iconCls: 'fa fa-fw fa-pause', - hidden: stopped || suspended, - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmpause', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend'); - }); - } - }, - { - text: gettext('Hibernate'), - iconCls: 'fa fa-fw fa-download', - hidden: stopped || suspended, - disabled: stopped || suspended, - tooltip: gettext('Suspend to disk'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmsuspend', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - vm_command('suspend', { todisk: 1 }); - }); - } - }, - { - text: gettext('Resume'), - iconCls: 'fa fa-fw fa-play', - hidden: !suspended, - handler: function() { - vm_command('resume'); - } - }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - text: gettext('Reboot'), - iconCls: 'fa fa-fw fa-refresh', - disabled: stopped, - tooltip: Ext.String.format(gettext('Reboot {0}'), 'VM'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmreboot', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("reboot"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: (standalone || !caps.vms['VM.Migrate']) && !caps.vms['VM.Allocate'] && !caps.vms['VM.Clone'] - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - hidden: !caps.vms['VM.Allocate'], - handler: function() { - var msg = Proxmox.Utils.format_task_description('qmtemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu/' + vmid + '/status/current', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var allowSpice = response.result.data.spice; - var allowXtermjs = response.result.data.serial; - var consoles = { - spice: allowSpice, - xtermjs: allowXtermjs - }; - PVE.Utils.openDefaultConsoleWindow(consoles, 'kvm', vmid, nodename, vmname); - } - }); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.lxc.CmdMenu', { - extend: 'Ext.menu.Menu', - - showSeparator: false, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no CT ID specified"; - } - var vmname = me.pveSelNode.data.name; - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + nodename + '/lxc/' + vmid + "/status/" + cmd, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var caps = Ext.state.Manager.get('GuiCap'); - - var running = false; - var stopped = true; - var suspended = false; - var standalone = PVE.data.ResourceStore.getNodes().length < 2; - - switch (me.pveSelNode.data.status) { - case 'running': - running = true; - stopped = false; - break; - case 'paused': - stopped = false; - suspended = true; - break; - default: break; - } - - me.title = 'CT ' + vmid; - - me.items = [ - { - text: gettext('Start'), - iconCls: 'fa fa-fw fa-play', - disabled: running, - handler: function() { - vm_command('start'); - } - }, -// { -// text: gettext('Suspend'), -// iconCls: 'fa fa-fw fa-pause', -// hidde: suspended, -// disabled: stopped || suspended, -// handler: function() { -// var msg = Proxmox.Utils.format_task_description('vzsuspend', vmid); -// Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { -// if (btn !== 'yes') { -// return; -// } -// -// vm_command('suspend'); -// }); -// } -// }, -// { -// text: gettext('Resume'), -// iconCls: 'fa fa-fw fa-play', -// hidden: !suspended, -// handler: function() { -// vm_command('resume'); -// } -// }, - { - text: gettext('Shutdown'), - iconCls: 'fa fa-fw fa-power-off', - disabled: stopped || suspended, - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzshutdown', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command('shutdown'); - }); - } - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-fw fa-stop', - disabled: stopped, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzstop', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("stop"); - }); - } - }, - { - text: gettext('Reboot'), - iconCls: 'fa fa-fw fa-refresh', - disabled: stopped, - tooltip: Ext.String.format(gettext('Reboot {0}'), 'CT'), - handler: function() { - var msg = Proxmox.Utils.format_task_description('vzreboot', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - vm_command("reboot"); - }); - } - }, - { - xtype: 'menuseparator', - hidden: (standalone || !caps.vms['VM.Migrate']) && !caps.vms['VM.Allocate'] && !caps.vms['VM.Clone'] - }, - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: !caps.vms['VM.Clone'], - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, me.isTemplate, 'lxc'); - } - }, - { - text: gettext('Migrate'), - iconCls: 'fa fa-fw fa-send-o', - hidden: standalone || !caps.vms['VM.Migrate'], - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - } - }, - { - text: gettext('Convert to template'), - iconCls: 'fa fa-fw fa-file-o', - handler: function() { - var msg = Proxmox.Utils.format_task_description('vztemplate', vmid); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc/' + vmid + '/template', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Console'), - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - PVE.Utils.openDefaultConsoleWindow(true, 'lxc', vmid, nodename, vmname); - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.node.CmdMenu', { - extend: 'Ext.menu.Menu', - xtype: 'nodeCmdMenu', - - showSeparator: false, - - items: [ - { - text: gettext('Create VM'), - itemId: 'createvm', - iconCls: 'fa fa-desktop', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.qemu.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { - text: gettext('Create CT'), - itemId: 'createct', - iconCls: 'fa fa-cube', - handler: function() { - var me = this.up('menu'); - var wiz = Ext.create('PVE.lxc.CreateWizard', { - nodename: me.nodename - }); - wiz.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Bulk Start'), - itemId: 'bulkstart', - iconCls: 'fa fa-fw fa-play', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - itemId: 'bulkstop', - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - itemId: 'bulkmigrate', - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var me = this.up('menu'); - var win = Ext.create('PVE.window.BulkAction', { - nodename: me.nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Shell'), - itemId: 'shell', - iconCls: 'fa fa-fw fa-terminal', - handler: function() { - var me = this.up('menu'); - PVE.Utils.openDefaultConsoleWindow(true, 'shell', undefined, me.nodename, undefined); - } - }, - { xtype: 'menuseparator' }, - { - text: gettext('Wake-on-LAN'), - itemId: 'wakeonlan', - iconCls: 'fa fa-fw fa-power-off', - handler: function() { - var me = this.up('menu'); - Proxmox.Utils.API2Request({ - param: {}, - url: '/nodes/' + me.nodename + '/wakeonlan', - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - Ext.Msg.show({ - title: 'Success', - icon: Ext.Msg.INFO, - msg: Ext.String.format(gettext("Wake on LAN packet send for '{0}': '{1}'"), me.nodename, response.result.data) - }); - } - }); - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no nodename specified'; - } - - me.title = gettext('Node') + " '" + me.nodename + "'"; - me.callParent(); - - var caps = Ext.state.Manager.get('GuiCap'); - // disable not allowed options - if (!caps.vms['VM.Allocate']) { - me.getComponent('createct').setDisabled(true); - me.getComponent('createvm').setDisabled(true); - } - - if (!caps.nodes['Sys.PowerMgmt']) { - me.getComponent('bulkstart').setDisabled(true); - me.getComponent('bulkstop').setDisabled(true); - me.getComponent('bulkmigrate').setDisabled(true); - me.getComponent('wakeonlan').setDisabled(true); - } - - if (!caps.nodes['Sys.Console']) { - me.getComponent('shell').setDisabled(true); - } - - if (me.pveSelNode.data.running) { - me.getComponent('wakeonlan').setDisabled(true); - } - } -}); -Ext.define('PVE.noVncConsole', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNoVncConsole', - - nodename: undefined, - vmid: undefined, - cmd: undefined, - - consoleType: undefined, // lxc, kvm, shell, cmd - xtermjs: false, - - layout: 'fit', - border: false, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.consoleType) { - throw "no console type specified"; - } - - if (!me.vmid && me.consoleType !== 'shell' && me.consoleType !== 'cmd') { - throw "no VM ID specified"; - } - - // always use same iframe, to avoid running several noVnc clients - // at same time (to avoid performance problems) - var box = Ext.create('Ext.ux.IFrame', { itemid : "vncconsole" }); - - var type = me.xtermjs ? 'xtermjs' : 'novnc'; - Ext.apply(me, { - items: box, - listeners: { - activate: function() { - var sp = Ext.state.Manager.getProvider(); - var queryDict = { - console: me.consoleType, // kvm, lxc, upgrade or shell - vmid: me.vmid, - node: me.nodename, - cmd: me.cmd, - resize: sp.get('novnc-scaling', 'scale'), - }; - queryDict[type] = 1; - PVE.Utils.cleanEmptyObjectKeys(queryDict); - var url = '/?' + Ext.Object.toQueryString(queryDict); - box.load(url); - } - } - }); - - me.callParent(); - - me.on('afterrender', function() { - me.focus(); - }); - }, - - reload: function() { - // reload IFrame content to forcibly reconnect VNC/xterm.js to VM - var box = this.down('[itemid=vncconsole]'); - box.getWin().location.reload(); - } -}); - -Ext.define('PVE.data.PermPathStore', { - extend: 'Ext.data.Store', - alias: 'store.pvePermPath', - fields: [ 'value' ], - autoLoad: false, - data: [ - {'value': '/'}, - {'value': '/access'}, - {'value': '/nodes'}, - {'value': '/pool'}, - {'value': '/storage'}, - {'value': '/vms'} - ], - - constructor: function(config) { - var me = this; - - config = config || {}; - - me.callParent([config]); - - me.suspendEvents(); - PVE.data.ResourceStore.each(function(record) { - switch (record.get('type')) { - case 'node': - me.add({value: '/nodes/' + record.get('text')}); - break; - - case 'qemu': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'lxc': - me.add({value: '/vms/' + record.get('vmid')}); - break; - - case 'storage': - me.add({value: '/storage/' + record.get('storage')}); - break; - case 'pool': - me.add({value: '/pool/' + record.get('pool')}); - break; - } - }); - me.resumeEvents(); - - me.fireEvent('refresh', me); - me.fireEvent('datachanged', me); - - me.sort({ - property: 'value', - direction: 'ASC' - }); - } -}); -Ext.define('PVE.data.ResourceStore', { - extend: 'Proxmox.data.UpdateStore', - singleton: true, - - findVMID: function(vmid) { - var me = this, i; - - return (me.findExact('vmid', parseInt(vmid, 10)) >= 0); - }, - - // returns the cached data from all nodes - getNodes: function() { - var me = this; - - var nodes = []; - me.each(function(record) { - if (record.get('type') == "node") { - nodes.push( record.getData() ); - } - }); - - return nodes; - }, - - storageIsShared: function(storage_path) { - var me = this; - - var index = me.findExact('id', storage_path); - - return me.getAt(index).data.shared; - }, - - guestNode: function(vmid) { - var me = this; - - var index = me.findExact('vmid', parseInt(vmid, 10)); - - return me.getAt(index).data.node; - }, - - guestName: function(vmid) { - let me = this; - let index = me.findExact('vmid', parseInt(vmid, 10)); - if (index < 0) { - return '-'; - } - let rec = me.getAt(index).data; - if ('name' in rec) { - return rec.name; - } - return ''; - }, - - constructor: function(config) { - // fixme: how to avoid those warnings - /*jslint confusion: true */ - - var me = this; - - config = config || {}; - - var field_defaults = { - type: { - header: gettext('Type'), - type: 'string', - renderer: PVE.Utils.render_resource_type, - sortable: true, - hideable: false, - width: 100 - }, - id: { - header: 'ID', - type: 'string', - hidden: true, - sortable: true, - width: 80 - }, - running: { - header: gettext('Online'), - type: 'boolean', - renderer: Proxmox.Utils.format_boolean, - hidden: true, - convert: function(value, record) { - var info = record.data; - return (Ext.isNumeric(info.uptime) && (info.uptime > 0)); - } - }, - text: { - header: gettext('Description'), - type: 'string', - sortable: true, - width: 200, - convert: function(value, record) { - var info = record.data; - var text; - - if (value) { - return value; - } - - if (Ext.isNumeric(info.vmid) && info.vmid > 0) { - text = String(info.vmid); - if (info.name) { - text += " (" + info.name + ')'; - } - } else { // node, pool, storage - text = info[info.type] || info.id; - if (info.node && info.type !== 'node') { - text += " (" + info.node + ")"; - } - } - - return text; - } - }, - vmid: { - header: 'VMID', - type: 'integer', - hidden: true, - sortable: true, - width: 80 - }, - name: { - header: gettext('Name'), - hidden: true, - sortable: true, - type: 'string' - }, - disk: { - header: gettext('Disk usage'), - type: 'integer', - renderer: PVE.Utils.render_disk_usage, - sortable: true, - width: 100, - hidden: true - }, - diskuse: { - header: gettext('Disk usage') + " %", - type: 'number', - sortable: true, - renderer: PVE.Utils.render_disk_usage_percent, - width: 100, - calculate: PVE.Utils.calculate_disk_usage, - sortType: 'asFloat' - }, - maxdisk: { - header: gettext('Disk size'), - type: 'integer', - renderer: PVE.Utils.render_size, - sortable: true, - hidden: true, - width: 100 - }, - mem: { - header: gettext('Memory usage'), - type: 'integer', - renderer: PVE.Utils.render_mem_usage, - sortable: true, - hidden: true, - width: 100 - }, - memuse: { - header: gettext('Memory usage') + " %", - type: 'number', - renderer: PVE.Utils.render_mem_usage_percent, - calculate: PVE.Utils.calculate_mem_usage, - sortType: 'asFloat', - sortable: true, - width: 100 - }, - maxmem: { - header: gettext('Memory size'), - type: 'integer', - renderer: PVE.Utils.render_size, - hidden: true, - sortable: true, - width: 100 - }, - cpu: { - header: gettext('CPU usage'), - type: 'float', - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100 - }, - maxcpu: { - header: gettext('maxcpu'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - diskread: { - header: gettext('Total Disk Read'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - diskwrite: { - header: gettext('Total Disk Write'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netin: { - header: gettext('Total NetIn'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - netout: { - header: gettext('Total NetOut'), - type: 'integer', - hidden: true, - sortable: true, - renderer: Proxmox.Utils.format_size, - width: 100 - }, - template: { - header: gettext('Template'), - type: 'integer', - hidden: true, - sortable: true, - width: 60 - }, - uptime: { - header: gettext('Uptime'), - type: 'integer', - renderer: Proxmox.Utils.render_uptime, - sortable: true, - width: 110 - }, - node: { - header: gettext('Node'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - storage: { - header: gettext('Storage'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - pool: { - header: gettext('Pool'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - hastate: { - header: gettext('HA State'), - type: 'string', - defaultValue: 'unmanaged', - hidden: true, - sortable: true - }, - status: { - header: gettext('Status'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - }, - lock: { - header: gettext('Lock'), - type: 'string', - hidden: true, - sortable: true, - width: 110 - } - }; - - var fields = []; - var fieldNames = []; - Ext.Object.each(field_defaults, function(key, value) { - var field = {name: key, type: value.type}; - if (Ext.isDefined(value.convert)) { - field.convert = value.convert; - } - - if (Ext.isDefined(value.calculate)) { - field.calculate = value.calculate; - } - - if (Ext.isDefined(value.defaultValue)) { - field.defaultValue = value.defaultValue; - } - - fields.push(field); - fieldNames.push(key); - }); - - Ext.define('PVEResources', { - extend: "Ext.data.Model", - fields: fields, - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/resources' - } - }); - - Ext.define('PVETree', { - extend: "Ext.data.Model", - fields: fields, - proxy: { type: 'memory' } - }); - - Ext.apply(config, { - storeid: 'PVEResources', - model: 'PVEResources', - defaultColumns: function() { - var res = []; - Ext.Object.each(field_defaults, function(field, info) { - var fi = Ext.apply({ dataIndex: field }, info); - res.push(fi); - }); - return res; - }, - fieldNames: fieldNames - }); - - me.callParent([config]); - } -}); -Ext.define('pve-domains', { - extend: "Ext.data.Model", - fields: [ - 'realm', 'type', 'comment', 'default', 'tfa', - { - name: 'descr', - // Note: We use this in the RealmComboBox.js (see Bug #125) - convert: function(value, record) { - if (value) { - return value; - } - - var info = record.data; - // return realm if there is no comment - var text = info.comment || info.realm; - - if (info.tfa) { - text += " (+ " + info.tfa + ")"; - } - - return Ext.String.htmlEncode(text); - } - } - ], - idProperty: 'realm', - proxy: { - type: 'proxmox', - url: "/api2/json/access/domains" - } -}); -Ext.define('pve-rrd-node', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - { - name:'iowait', - // percentage - convert: function(value) { - return value*100; - } - }, - 'loadavg', - 'maxcpu', - 'memtotal', - 'memused', - 'netin', - 'netout', - 'roottotal', - 'rootused', - 'swaptotal', - 'swapused', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-guest', { - extend: 'Ext.data.Model', - fields: [ - { - name:'cpu', - // percentage - convert: function(value) { - return value*100; - } - }, - 'maxcpu', - 'netin', - 'netout', - 'mem', - 'maxmem', - 'disk', - 'maxdisk', - 'diskread', - 'diskwrite', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); - -Ext.define('pve-rrd-storage', { - extend: 'Ext.data.Model', - fields: [ - 'used', - 'total', - { type: 'date', dateFormat: 'timestamp', name: 'time' } - ] -}); -Ext.define('PVE.form.VlanField', { - extend: 'Ext.form.field.Number', - alias: ['widget.pveVlanField'], - - deleteEmpty: false, - - emptyText: 'no VLAN', - - fieldLabel: gettext('VLAN Tag'), - - allowBlank: true, - - getSubmitData: function() { - var me = this, - data = null, - val; - if (!me.disabled && me.submitValue) { - val = me.getSubmitValue(); - if (val) { - data = {}; - data[me.getName()] = val; - } else if (me.deleteEmpty) { - data = {}; - data['delete'] = me.getName(); - } - } - return data; - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - minValue: 1, - maxValue: 4094 - }); - - me.callParent(); - } -}); -// boolean type including 'Default' (delete property from file) -Ext.define('PVE.form.Boolean', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.booleanfield'], - comboItems: [ - ['__default__', gettext('Default')], - [1, gettext('Yes')], - [0, gettext('No')] - ] -}); -Ext.define('PVE.form.BandwidthField', { - extend: 'Ext.form.FieldContainer', - alias: 'widget.pveBandwidthField', - - mixins: ['Proxmox.Mixin.CBind' ], - - viewModel: { - data: { - unit: 'MiB', - }, - formulas: { - unitlabel: (get) => get('unit') + '/s', - } - }, - - emptyText: '', - - layout: 'hbox', - defaults: { - hideLabel: true - }, - - units: { - 'KiB': 1024, - 'MiB': 1024*1024, - 'GiB': 1024*1024*1024, - 'KB': 1000, - 'MB': 1000*1000, - 'GB': 1000*1000*1000, - }, - - // display unit (TODO: make (optionally) selectable) - unit: 'MiB', - - // use this if the backend saves values in another unit tha bytes, e.g., - // for KiB set it to 'KiB' - backendUnit: undefined, - - items: [ - { - xtype: 'numberfield', - cbind: { - name: '{name}', - emptyText: '{emptyText}', - }, - minValue: 0, - step: 1, - submitLocaleSeparator: false, - fieldStyle: 'text-align: right', - flex: 1, - enableKeyEvents: true, - setValue: function(v) { - if (!this._transformed) { - let fieldct = this.up('pveBandwidthField'); - let vm = fieldct.getViewModel(); - let unit = vm.get('unit'); - - v /= fieldct.units[unit]; - v *= fieldct.backendFactor; - - this._transformed = true; - } - - if (v == 0) v = undefined; - - return Ext.form.field.Text.prototype.setValue.call(this, v); - }, - getSubmitValue: function() { - let v = this.processRawValue(this.getRawValue()); - v = v.replace(this.decimalSeparator, '.') - - if (v === undefined) return null; - // FIXME: make it configurable, as this only works if 0 === default - if (v == 0 || v == 0.0) return null; - - let fieldct = this.up('pveBandwidthField'); - let vm = fieldct.getViewModel(); - let unit = vm.get('unit'); - - v = parseFloat(v) * fieldct.units[unit]; - v /= fieldct.backendFactor; - - return ''+ Math.floor(v); - }, - listeners: { - // our setValue gets only called if we have a value, avoid - // transformation of the first user-entered value - keydown: function () { this._transformed = true; }, - }, - }, - { - xtype: 'displayfield', - name: 'unit', - submitValue: false, - padding: '0 0 0 10', - bind: { - value: '{unitlabel}', - }, - listeners: { - change: (f, v) => f.originalValue = v, - }, - width: 40, - }, - ], - - initComponent: function() { - let me = this; - - me.unit = me.unit || 'MiB'; - if (!(me.unit in me.units)) { - throw "unknown unit: " + me.unit; - } - - me.backendFactor = 1; - if (me.backendUnit !== undefined) { - if (!(me.unit in me.units)) { - throw "unknown backend unit: " + me.backendUnit; - } - me.backendFactor = me.units[me.backendUnit]; - } - - - me.callParent(arguments); - - me.getViewModel().set('unit', me.unit); - }, -}); - -Ext.define('PVE.form.CompressionSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveCompressionSelector'], - comboItems: [ - ['0', Proxmox.Utils.noneText], - ['lzo', 'LZO (' + gettext('fast') + ')'], - ['gzip', 'GZIP (' + gettext('good') + ')'] - ] -}); -Ext.define('PVE.form.PoolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pvePoolSelector'], - - allowBlank: false, - valueField: 'poolid', - displayField: 'poolid', - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: 'poolid' - }); - - Ext.apply(me, { - store: store, - autoSelect: false, - listConfig: { - columns: [ - { - header: gettext('Pool'), - sortable: true, - dataIndex: 'poolid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-pools', { - extend: 'Ext.data.Model', - fields: [ 'poolid', 'comment' ], - proxy: { - type: 'proxmox', - url: "/api2/json/pools" - }, - idProperty: 'poolid' - }); - -}); -Ext.define('PVE.form.PrivilegesSelector', { - extend: 'Proxmox.form.KVComboBox', - xtype: 'pvePrivilegesSelector', - - multiSelect: true, - - initComponent: function() { - var me = this; - - // So me.store is available. - me.callParent(); - - Proxmox.Utils.API2Request({ - url: '/access/roles/Administrator', - method: 'GET', - success: function(response, options) { - var data = [], key; - /*jslint forin: true */ - for (key in response.result.data) { - data.push([key, key]); - } - /*jslint forin: false */ - - me.store.setData(data); - - me.store.sort({ - property: 'key', - direction: 'ASC' - }); - }, - - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } -}); -Ext.define('pve-groups', { - extend: 'Ext.data.Model', - fields: [ 'groupid', 'comment', 'users' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/groups" - }, - idProperty: 'groupid' -}); - -Ext.define('PVE.form.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveGroupSelector', - - allowBlank: false, - autoSelect: false, - valueField: 'groupid', - displayField: 'groupid', - listConfig: { - columns: [ - { - header: gettext('Group'), - sortable: true, - dataIndex: 'groupid', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - }, - { - header: gettext('Users'), - sortable: false, - dataIndex: 'users', - flex: 1 - } - ] - }, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: [{ - property: 'groupid' - }] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}); -Ext.define('PVE.form.UserSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUserSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'userid', - displayField: 'userid', - - editable: true, - anyMatch: true, - forceSelection: true, - - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-users', - sorters: [{ - property: 'userid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('User'), - sortable: true, - dataIndex: 'userid', - flex: 1 - }, - { - header: gettext('Name'), - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname', - flex: 1 - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load({ params: { enabled: 1 }}); - } - -}, function() { - - Ext.define('pve-users', { - extend: 'Ext.data.Model', - fields: [ - 'userid', 'firstname', 'lastname' , 'email', 'comment', - { type: 'boolean', name: 'enable' }, - { type: 'date', dateFormat: 'timestamp', name: 'expire' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/users" - }, - idProperty: 'userid' - }); - -}); - - -Ext.define('PVE.form.RoleSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveRoleSelector'], - - allowBlank: false, - autoSelect: false, - valueField: 'roleid', - displayField: 'roleid', - initComponent: function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: [{ - property: 'roleid' - }] - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Role'), - sortable: true, - dataIndex: 'roleid', - flex: 1 - } - ] - } - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-roles', { - extend: 'Ext.data.Model', - fields: [ 'roleid', 'privs' ], - proxy: { - type: 'proxmox', - url: "/api2/json/access/roles" - }, - idProperty: 'roleid' - }); - -}); -Ext.define('PVE.form.GuestIDSelector', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveGuestIDSelector', - - allowBlank: false, - - minValue: 100, - - maxValue: 999999999, - - validateExists: undefined, - - loadNextFreeID: false, - - guestType: undefined, - - validator: function(value) { - var me = this; - - if (!Ext.isNumeric(value) || - value < me.minValue || - value > me.maxValue) { - // check is done by ExtJS - return true; - } - - if (me.validateExists === true && !me.exists) { - return me.unknownID; - } - - if (me.validateExists === false && me.exists) { - return me.inUseID; - } - - return true; - }, - - initComponent: function() { - var me = this; - var label = '{0} ID'; - var unknownID = gettext('This {0} ID does not exist'); - var inUseID = gettext('This {0} ID is already in use'); - var type = 'CT/VM'; - - if (me.guestType === 'lxc') { - type = 'CT'; - } else if (me.guestType === 'qemu') { - type = 'VM'; - } - - me.label = Ext.String.format(label, type); - me.unknownID = Ext.String.format(unknownID, type); - me.inUseID = Ext.String.format(inUseID, type); - - Ext.apply(me, { - fieldLabel: me.label, - listeners: { - 'change': function(field, newValue, oldValue) { - if (!Ext.isDefined(me.validateExists)) { - return; - } - Proxmox.Utils.API2Request({ - params: { vmid: newValue }, - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.exists = false; - me.validate(); - }, - failure: function(response, opts) { - me.exists = true; - me.validate(); - } - }); - } - } - }); - - me.callParent(); - - if (me.loadNextFreeID) { - Proxmox.Utils.API2Request({ - url: '/cluster/nextid', - method: 'GET', - success: function(response, opts) { - me.setRawValue(response.result.data); - } - }); - } - } -}); -Ext.define('PVE.form.MemoryField', { - extend: 'Ext.form.field.Number', - alias: 'widget.pveMemoryField', - - allowBlank: false, - - hotplug: false, - - minValue: 32, - - maxValue: 4178944, - - step: 32, - - value: '512', // qm default - - allowDecimals: false, - - allowExponential: false, - - computeUpDown: function(value) { - var me = this; - - if (!me.hotplug) { - return { up: value + me.step, down: value - me.step }; - } - - var dimm_size = 512; - var prev_dimm_size = 0; - var min_size = 1024; - var current_size = min_size; - var value_up = min_size; - var value_down = min_size; - var value_start = min_size; - - var i, j; - for (j = 0; j < 9; j++) { - for (i = 0; i < 32; i++) { - if ((value >= current_size) && (value < (current_size + dimm_size))) { - value_start = current_size; - value_up = current_size + dimm_size; - value_down = current_size - ((i === 0) ? prev_dimm_size : dimm_size); - } - current_size += dimm_size; - } - prev_dimm_size = dimm_size; - dimm_size = dimm_size*2; - } - - return { up: value_up, down: value_down, start: value_start }; - }, - - onSpinUp: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.up, me.minValue, me.maxValue)); - } - }, - - onSpinDown: function() { - var me = this; - if (!me.readOnly) { - var res = me.computeUpDown(me.getValue()); - me.setValue(Ext.Number.constrain(res.down, me.minValue, me.maxValue)); - } - }, - - initComponent: function() { - var me = this; - - if (me.hotplug) { - me.minValue = 1024; - - me.on('blur', function(field) { - var value = me.getValue(); - var res = me.computeUpDown(value); - if (value === res.start || value === res.up || value === res.down) { - return; - } - field.setValue(res.up); - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.form.NetworkCardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveNetworkCardSelector', - comboItems: [ - ['e1000', 'Intel E1000'], - ['virtio', 'VirtIO (' + gettext('paravirtualized') + ')'], - ['rtl8139', 'Realtek RTL8139'], - ['vmxnet3', 'VMware vmxnet3'] - ] -}); -Ext.define('PVE.form.DiskFormatSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveDiskFormatSelector', - comboItems: [ - ['raw', gettext('Raw disk image') + ' (raw)'], - ['qcow2', gettext('QEMU image format') + ' (qcow2)'], - ['vmdk', gettext('VMware image format') + ' (vmdk)'] - ] -}); -Ext.define('PVE.form.DiskSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveDiskSelector', - - // can be - // undefined: all - // unused: only unused - // journal_disk: all disks with gpt - diskType: undefined, - - valueField: 'devpath', - displayField: 'devpath', - emptyText: gettext('No Disks unused'), - listConfig: { - width: 600, - columns: [ - { - header: gettext('Device'), - flex: 3, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Size'), - flex: 2, - sortable: false, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Serial'), - flex: 5, - sortable: true, - dataIndex: 'serial' - } - ] - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var store = Ext.create('Ext.data.Store', { - filterOnLoad: true, - model: 'pve-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list", - extraParams: { type: me.diskType } - }, - sorters: [ - { - property : 'devpath', - direction: 'ASC' - } - ] - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load(); - } -}, function() { - - Ext.define('pve-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial'], - idProperty: 'devpath' - }); -}); -Ext.define('PVE.form.BusTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: 'widget.pveBusSelector', - - noVirtIO: false, - - initComponent: function() { - var me = this; - - me.comboItems = [['ide', 'IDE'], ['sata', 'SATA']]; - - if (!me.noVirtIO) { - me.comboItems.push(['virtio', 'VirtIO Block']); - } - - me.comboItems.push(['scsi', 'SCSI']); - - me.callParent(); - } -}); -Ext.define('PVE.form.ControllerSelector', { - extend: 'Ext.form.FieldContainer', - alias: 'widget.pveControllerSelector', - - noVirtIO: false, - - vmconfig: {}, // used to check for existing devices - - sortByPreviousUsage: function(vmconfig, controllerList) { - let usedControllers = {}; - for (const type of Object.keys(PVE.Utils.diskControllerMaxIDs)) { - usedControllers[type] = 0; - } - - for (const property of Object.keys(vmconfig)) { - if (property.match(PVE.Utils.bus_match) && !vmconfig[property].match(/media=cdrom/)) { - const foundController = property.match(PVE.Utils.bus_match)[1]; - usedControllers[foundController]++; - } - } - - var sortPriority = PVE.qemu.OSDefaults.getDefaults(vmconfig.ostype).busPriority; - - var sortedList = Ext.clone(controllerList); - sortedList.sort(function(a, b) { - if (usedControllers[b] == usedControllers[a]) { - return sortPriority[b] - sortPriority[a]; - } - return usedControllers[b] - usedControllers[a]; - }); - - return sortedList; - }, - - setVMConfig: function(vmconfig, autoSelect) { - var me = this; - - me.vmconfig = Ext.apply({}, vmconfig); - - var clist = ['ide', 'virtio', 'scsi', 'sata']; - var bussel = me.down('field[name=controller]'); - var deviceid = me.down('field[name=deviceid]'); - - if (autoSelect === 'cdrom') { - if (!Ext.isDefined(me.vmconfig.ide2)) { - bussel.setValue('ide'); - deviceid.setValue(2); - return; - } - clist = ['ide', 'scsi', 'sata']; - } else { - // in most cases we want to add a disk to the same controller - // we previously used - clist = me.sortByPreviousUsage(me.vmconfig, clist); - } - -clist_loop: - for (const controller of clist) { - bussel.setValue(controller); - for (let i = 0; i < PVE.Utils.diskControllerMaxIDs[controller]; i++) { - let confid = controller + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - deviceid.setValue(i); - break clist_loop; // we found the desired controller/id combo - } - } - } - deviceid.validate(); - }, - - initComponent: function() { - var me = this; - - Ext.apply(me, { - fieldLabel: gettext('Bus/Device'), - layout: 'hbox', - defaults: { - hideLabel: true - }, - items: [ - { - xtype: 'pveBusSelector', - name: 'controller', - value: PVE.qemu.OSDefaults.generic.busType, - noVirtIO: me.noVirtIO, - allowBlank: false, - flex: 2, - listeners: { - change: function(t, value) { - if (!value) { - return; - } - var field = me.down('field[name=deviceid]'); - field.setMaxValue(PVE.Utils.diskControllerMaxIDs[value]); - field.validate(); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'deviceid', - minValue: 0, - maxValue: PVE.Utils.diskControllerMaxIDs.ide, - value: '0', - flex: 1, - allowBlank: false, - validator: function(value) { - /*jslint confusion: true */ - if (!me.rendered) { - return; - } - var field = me.down('field[name=controller]'); - var controller = field.getValue(); - var confid = controller + value; - if (Ext.isDefined(me.vmconfig[confid])) { - return "This device is already in use."; - } - return true; - } - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.EmailNotificationSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveEmailNotificationSelector'], - comboItems: [ - ['always', gettext('Always')], - ['failure', gettext('On failure only')] - ] -}); -/*global Proxmox*/ -Ext.define('PVE.form.RealmComboBox', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveRealmComboBox'], - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store.on('load', this.onLoad, view); - }, - - onLoad: function(store, records, success) { - if (!success) { - return; - } - var me = this; - var val = me.getValue(); - if (!val || !me.store.findRecord('realm', val)) { - var def = 'pam'; - Ext.each(records, function(rec) { - if (rec.data && rec.data['default']) { - def = rec.data.realm; - } - }); - me.setValue(def); - } - } - }, - - fieldLabel: gettext('Realm'), - name: 'realm', - queryMode: 'local', - allowBlank: false, - editable: false, - forceSelection: true, - autoSelect: false, - triggerAction: 'all', - valueField: 'realm', - displayField: 'descr', - getState: function() { - return { value: this.getValue() }; - }, - applyState : function(state) { - if (state && state.value) { - this.setValue(state.value); - } - }, - stateEvents: [ 'select' ], - stateful: true, // last chosen auth realm is saved between page reloads - id: 'pveloginrealm', // We need stable ids when using stateful, not autogenerated - stateID: 'pveloginrealm', - - needOTP: function(realm) { - var me = this; - // use exact match - var rec = me.store.findRecord('realm', realm, 0, false, false, true); - return rec && rec.data && rec.data.tfa ? rec.data.tfa : undefined; - }, - - store: { - model: 'pve-domains', - autoLoad: true - } -}); -/* - * Top left combobox, used to select a view of the underneath RessourceTree - */ -Ext.define('PVE.form.ViewSelector', { - extend: 'Ext.form.field.ComboBox', - alias: ['widget.pveViewSelector'], - - editable: false, - allowBlank: false, - forceSelection: true, - autoSelect: false, - valueField: 'key', - displayField: 'value', - hideLabel: true, - queryMode: 'local', - - initComponent: function() { - var me = this; - - var default_views = { - server: { - text: gettext('Server View'), - groups: ['node'] - }, - folder: { - text: gettext('Folder View'), - groups: ['type'] - }, - storage: { - text: gettext('Storage View'), - groups: ['node'], - filterfn: function(node) { - return node.data.type === 'storage' || node.data.type === 'node'; - } - }, - pool: { - text: gettext('Pool View'), - groups: ['pool'], - // Pool View only lists VMs and Containers - filterfn: function(node) { - return node.data.type === 'qemu' || node.data.type === 'lxc' || node.data.type === 'openvz' || - node.data.type === 'pool'; - } - } - }; - - var groupdef = []; - Ext.Object.each(default_views, function(viewname, value) { - groupdef.push([viewname, value.text]); - }); - - var store = Ext.create('Ext.data.Store', { - model: 'KeyValue', - proxy: { - type: 'memory', - reader: 'array' - }, - data: groupdef, - autoload: true - }); - - Ext.apply(me, { - store: store, - value: groupdef[0][0], - getViewFilter: function() { - var view = me.getValue(); - return Ext.apply({ id: view }, default_views[view] || default_views.server); - }, - - getState: function() { - return { value: me.getValue() }; - }, - - applyState : function(state, doSelect) { - var view = me.getValue(); - if (state && state.value && (view != state.value)) { - var record = store.findRecord('key', state.value); - if (record) { - me.setValue(state.value, true); - if (doSelect) { - me.fireEvent('select', me, [record]); - } - } - } - }, - stateEvents: [ 'select' ], - stateful: true, - stateId: 'pveview', - id: 'view' - }); - - me.callParent(); - - var statechange = function(sp, key, value) { - if (key === me.id) { - me.applyState(value, true); - } - }; - - var sp = Ext.state.Manager.getProvider(); - me.mon(sp, 'statechange', statechange, me); - } -}); -Ext.define('PVE.form.NodeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveNodeSelector'], - - // invalidate nodes which are offline - onlineValidator: false, - - selectCurNode: false, - - // do not allow those nodes (array) - disallowedNodes: undefined, - - // only allow those nodes (array) - allowedNodes: undefined, - // set default value to empty array, else it inits it with - // null and after the store load it is an empty array, - // triggering dirtychange - value: [], - valueField: 'node', - displayField: 'node', - store: { - fields: [ 'node', 'cpu', 'maxcpu', 'mem', 'maxmem', 'uptime' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes' - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - }, - { - property : 'mem', - direction: 'DESC' - } - ] - }, - - listConfig: { - columns: [ - { - header: gettext('Node'), - dataIndex: 'node', - sortable: true, - hideable: false, - flex: 1 - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 100, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 100, - dataIndex: 'cpu' - } - ] - }, - - validator: function(value) { - /*jslint confusion: true */ - var me = this; - if (!me.onlineValidator || (me.allowBlank && !value)) { - return true; - } - - var offline = []; - var notAllowed = []; - - Ext.Array.each(value.split(/\s*,\s*/), function(node) { - var rec = me.store.findRecord(me.valueField, node); - if (!(rec && rec.data) || rec.data.status !== 'online') { - offline.push(node); - } else if (me.allowedNodes && !Ext.Array.contains(me.allowedNodes, node)) { - notAllowed.push(node); - } - }); - - if (value && notAllowed.length !== 0) { - return "Node " + notAllowed.join(', ') + " is not allowed for this action!"; - } - - if (value && offline.length !== 0) { - return "Node " + offline.join(', ') + " seems to be offline!"; - } - return true; - }, - - initComponent: function() { - var me = this; - - if (me.selectCurNode && PVE.curSelectedNode && PVE.curSelectedNode.data.node) { - me.preferredValue = PVE.curSelectedNode.data.node; - } - - me.callParent(); - me.getStore().load(); - - // filter out disallowed nodes - me.getStore().addFilter(new Ext.util.Filter({ - filterFn: function(item) { - if (Ext.isArray(me.disallowedNodes)) { - return !Ext.Array.contains(me.disallowedNodes, item.data.node); - } else { - return true; - } - } - })); - - me.mon(me.getStore(), 'load', function(){ - me.isValid(); - }); - } -}); -Ext.define('PVE.form.FileSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFileSelector', - - editable: true, - anyMatch: true, - forceSelection: true, - - listeners: { - afterrender: function() { - var me = this; - if (!me.disabled) { - me.setStorage(me.storage, me.nodename); - } - } - }, - - setStorage: function(storage, nodename) { - var me = this; - - var change = false; - if (storage && (me.storage !== storage)) { - me.storage = storage; - change = true; - } - - if (nodename && (me.nodename !== nodename)) { - me.nodename = nodename; - change = true; - } - - if (!(me.storage && me.nodename && change)) { - return; - } - - var url = '/api2/json/nodes/' + me.nodename + '/storage/' + me.storage + '/content'; - if (me.storageContent) { - url += '?content=' + me.storageContent; - } - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - me.store.removeAll(); - me.store.load(); - }, - - setNodename: function(nodename) { - this.setStorage(undefined, nodename); - }, - - store: { - model: 'pve-storage-content' - }, - - allowBlank: false, - autoSelect: false, - valueField: 'volid', - displayField: 'text', - - listConfig: { - width: 600, - columns: [ - { - header: gettext('Name'), - dataIndex: 'text', - hideable: false, - flex: 1 - }, - { - header: gettext('Format'), - width: 60, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - dataIndex: 'size', - renderer: Proxmox.Utils.format_size - } - ] - } -}); -Ext.define('PVE.form.StorageSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveStorageSelector', - - allowBlank: false, - valueField: 'storage', - displayField: 'storage', - listConfig: { - width: 450, - columns: [ - { - header: gettext('Name'), - dataIndex: 'storage', - hideable: false, - flex: 1 - }, - { - header: gettext('Type'), - width: 75, - dataIndex: 'type' - }, - { - header: gettext('Avail'), - width: 90, - dataIndex: 'avail', - renderer: Proxmox.Utils.format_size - }, - { - header: gettext('Capacity'), - width: 90, - dataIndex: 'total', - renderer: Proxmox.Utils.format_size - } - ] - }, - - reloadStorageList: function() { - var me = this; - if (!me.nodename) { - return; - } - - var params = { - format: 1 - }; - var url = '/api2/json/nodes/' + me.nodename + '/storage'; - if (me.storageContent) { - params.content = me.storageContent; - } - if (me.targetNode) { - params.target = me.targetNode; - params.enabled = 1; // skip disabled storages - } - me.store.setProxy({ - type: 'proxmox', - url: url, - extraParams: params - }); - - me.store.load(); - - }, - - setTargetNode: function(targetNode) { - var me = this; - - if (!targetNode || (me.targetNode === targetNode)) { - return; - } - - me.targetNode = targetNode; - - me.reloadStorageList(); - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.reloadStorageList(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - var store = Ext.create('Ext.data.Store', { - model: 'pve-storage-status', - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - if (nodename) { - me.setNodename(nodename); - } - } -}, function() { - - Ext.define('pve-storage-status', { - extend: 'Ext.data.Model', - fields: [ 'storage', 'active', 'type', 'avail', 'total' ], - idProperty: 'storage' - }); - -}); -Ext.define('PVE.form.DiskStorageSelector', { - extend: 'Ext.container.Container', - alias: 'widget.pveDiskStorageSelector', - - layout: 'fit', - defaults: { - margin: '0 0 5 0' - }, - - // the fieldLabel for the storageselector - storageLabel: gettext('Storage'), - - // the content to show (e.g., images or rootdir) - storageContent: undefined, - - // if true, selects the first available storage - autoSelect: false, - - allowBlank: false, - emptyText: '', - - // hides the selection field - // this is always hidden on creation, - // and only shown when the storage needs a selection and - // hideSelection is not true - hideSelection: undefined, - - // hides the size field (e.g, for the efi disk dialog) - hideSize: false, - - // sets the initial size value - // string because else we get a type confusion - defaultSize: '32', - - changeStorage: function(f, value) { - var me = this; - var formatsel = me.getComponent('diskformat'); - var hdfilesel = me.getComponent('hdimage'); - var hdsizesel = me.getComponent('disksize'); - - // initial store load, and reset/deletion of the storage - if (!value) { - hdfilesel.setDisabled(true); - hdfilesel.setVisible(false); - - formatsel.setDisabled(true); - return; - } - - var rec = f.store.getById(value); - // if the storage is not defined, or valid, - // we cannot know what to enable/disable - if (!rec) { - return; - } - - var selectformat = false; - if (rec.data.format) { - var format = rec.data.format[0]; // 0 is the formats, 1 the default in the backend - delete format.subvol; // we never need subvol in the gui - selectformat = (Ext.Object.getSize(format) > 1); - } - - var select = !!rec.data.select_existing && !me.hideSelection; - - formatsel.setDisabled(!selectformat); - formatsel.setValue(selectformat ? 'qcow2' : 'raw'); - - hdfilesel.setDisabled(!select); - hdfilesel.setVisible(select); - if (select) { - hdfilesel.setStorage(value); - } - - hdsizesel.setDisabled(select || me.hideSize); - hdsizesel.setVisible(!select && !me.hideSize); - }, - - setNodename: function(nodename) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - var hdfilesel = me.getComponent('hdimage'); - - hdstorage.setNodename(nodename); - hdfilesel.setNodename(nodename); - }, - - setDisabled: function(value) { - var me = this; - var hdstorage = me.getComponent('hdstorage'); - - // reset on disable - if (value) { - hdstorage.setValue(); - } - hdstorage.setDisabled(value); - - // disabling does not always fire this event and we do not need - // the value of the validity - hdstorage.fireEvent('validitychange'); - }, - - initComponent: function() { - var me = this; - - me.items = [ - { - xtype: 'pveStorageSelector', - itemId: 'hdstorage', - name: 'hdstorage', - reference: 'hdstorage', - fieldLabel: me.storageLabel, - nodename: me.nodename, - storageContent: me.storageContent, - disabled: me.disabled, - autoSelect: me.autoSelect, - allowBlank: me.allowBlank, - emptyText: me.emptyText, - listeners: { - change: { - fn: me.changeStorage, - scope: me - } - } - }, - { - xtype: 'pveFileSelector', - name: 'hdimage', - reference: 'hdimage', - itemId: 'hdimage', - fieldLabel: gettext('Disk image'), - nodename: me.nodename, - disabled: true, - hidden: true - }, - { - xtype: 'numberfield', - itemId: 'disksize', - reference: 'disksize', - name: 'disksize', - fieldLabel: gettext('Disk size') + ' (GiB)', - hidden: me.hideSize, - disabled: me.hideSize, - minValue: 0.001, - maxValue: 128*1024, - decimalPrecision: 3, - value: me.defaultSize, - allowBlank: false - }, - { - xtype: 'pveDiskFormatSelector', - itemId: 'diskformat', - reference: 'diskformat', - name: 'diskformat', - fieldLabel: gettext('Format'), - nodename: me.nodename, - disabled: true, - hidden: me.storageContent === 'rootdir', - value: 'qcow2', - allowBlank: false - } - ]; - - // use it to disable the children but not ourself - me.disabled = false; - - me.callParent(); - } -}); -Ext.define('PVE.form.BridgeSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.BridgeSelector'], - - bridgeType: 'any_bridge', // bridge, OVSBridge or any_bridge - - store: { - fields: [ 'iface', 'active', 'type' ], - filterOnLoad: true, - sorters: [ - { - property : 'iface', - direction: 'ASC' - } - ] - }, - valueField: 'iface', - displayField: 'iface', - listConfig: { - columns: [ - { - header: gettext('Bridge'), - dataIndex: 'iface', - hideable: false, - width: 100 - }, - { - header: gettext('Active'), - width: 60, - dataIndex: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Comment'), - dataIndex: 'comments', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/network?type=' + - me.bridgeType - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.PCISelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pvePCISelector', - - store: { - fields: [ 'id','vendor_name', 'device_name', 'vendor', 'device', 'iommugroup', 'mdev' ], - filterOnLoad: true, - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }, - - autoSelect: false, - valueField: 'id', - displayField: 'id', - - // can contain a load callback for the store - // useful to determine the state of the IOMMU - onLoadCallBack: undefined, - - listConfig: { - width: 800, - columns: [ - { - header: 'ID', - dataIndex: 'id', - width: 100 - }, - { - header: gettext('IOMMU Group'), - dataIndex: 'iommugroup', - width: 50 - }, - { - header: gettext('Vendor'), - dataIndex: 'vendor_name', - flex: 2 - }, - { - header: gettext('Device'), - dataIndex: 'device_name', - flex: 6 - }, - { - header: gettext('Mediated Devices'), - dataIndex: 'mdev', - flex: 1, - renderer: function(val) { - return Proxmox.Utils.format_boolean(!!val); - } - } - ] - }, - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - me.nodename = undefined; - - me.callParent(); - - if (me.onLoadCallBack !== undefined) { - me.mon(me.getStore(), 'load', me.onLoadCallBack); - } - - me.setNodename(nodename); - } -}); - -Ext.define('PVE.form.MDevSelector', { - extend: 'Proxmox.form.ComboGrid', - xtype: 'pveMDevSelector', - - store: { - fields: [ 'type','available', 'description' ], - filterOnLoad: true, - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ] - }, - autoSelect: false, - valueField: 'type', - displayField: 'type', - listConfig: { - columns: [ - { - header: gettext('Type'), - dataIndex: 'type', - flex: 1 - }, - { - header: gettext('Available'), - dataIndex: 'available', - width: 80 - }, - { - header: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value) { - if (!value) { - return ''; - } - - return value.split('\n').join('
'); - } - } - ] - }, - - setPciID: function(pciid, force) { - var me = this; - - if (!force && (!pciid || (me.pciid === pciid))) { - return; - } - - me.pciid = pciid; - me.updateProxy(); - }, - - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - me.updateProxy(); - }, - - updateProxy: function() { - var me = this; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/hardware/pci/' + me.pciid + '/mdev' - }); - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw 'no node name specified'; - } - - me.callParent(); - - if (me.pciid) { - me.setPciID(me.pciid, true); - } - } -}); - -Ext.define('PVE.form.SecurityGroupsSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveSecurityGroupsSelector'], - - valueField: 'group', - displayField: 'group', - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'group', 'comment' ], - idProperty: 'group', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/groups" - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Security Group'), - dataIndex: 'group', - hideable: false, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ] - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPRefSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPRefSelector'], - - base_url: undefined, - - preferredValue: '', // hack: else Form sets dirty flag? - - ref_type: undefined, // undefined = any [undefined, 'ipset' or 'alias'] - - valueField: 'ref', - displayField: 'ref', - notFoundIsValid: true, - - initComponent: function() { - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - - var url = "/api2/json" + me.base_url; - if (me.ref_type) { - url += "?type=" + me.ref_type; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'type', 'name', 'ref', 'comment' ], - idProperty: 'ref', - proxy: { - type: 'proxmox', - url: url - }, - sorters: { - property: 'ref', - order: 'DESC' - } - }); - - var disable_query_for_ips = function(f, value) { - if (value === null || - value.match(/^\d/)) { // IP address starts with \d - f.queryDelay = 9999999999; // hack: disable with long delay - } else { - f.queryDelay = 10; - } - }; - - var columns = []; - - if (!me.ref_type) { - columns.push({ - header: gettext('Type'), - dataIndex: 'type', - hideable: false, - width: 60 - }); - } - - columns.push( - { - header: gettext('Name'), - dataIndex: 'ref', - hideable: false, - width: 140 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ); - - Ext.apply(me, { - store: store, - listConfig: { columns: columns } - }); - - me.on('change', disable_query_for_ips); - - me.callParent(); - } -}); - -Ext.define('PVE.form.IPProtocolSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveIPProtocolSelector'], - valueField: 'p', - displayField: 'p', - listConfig: { - columns: [ - { - header: gettext('Protocol'), - dataIndex: 'p', - hideable: false, - sortable: false, - width: 100 - }, - { - header: gettext('Number'), - dataIndex: 'n', - hideable: false, - sortable: false, - width: 50 - }, - { - header: gettext('Description'), - dataIndex: 'd', - hideable: false, - sortable: false, - flex: 1 - } - ] - }, - store: { - fields: [ 'p', 'd', 'n'], - data: [ - { p: 'tcp', n: 6, d: 'Transmission Control Protocol' }, - { p: 'udp', n: 17, d: 'User Datagram Protocol' }, - { p: 'icmp', n: 1, d: 'Internet Control Message Protocol' }, - { p: 'igmp', n: 2, d: 'Internet Group Management' }, - { p: 'ggp', n: 3, d: 'gateway-gateway protocol' }, - { p: 'ipencap', n: 4, d: 'IP encapsulated in IP' }, - { p: 'st', n: 5, d: 'ST datagram mode' }, - { p: 'egp', n: 8, d: 'exterior gateway protocol' }, - { p: 'igp', n: 9, d: 'any private interior gateway (Cisco)' }, - { p: 'pup', n: 12, d: 'PARC universal packet protocol' }, - { p: 'hmp', n: 20, d: 'host monitoring protocol' }, - { p: 'xns-idp', n: 22, d: 'Xerox NS IDP' }, - { p: 'rdp', n: 27, d: '"reliable datagram" protocol' }, - { p: 'iso-tp4', n: 29, d: 'ISO Transport Protocol class 4 [RFC905]' }, - { p: 'dccp', n: 33, d: 'Datagram Congestion Control Prot. [RFC4340]' }, - { p: 'xtp', n: 36, d: 'Xpress Transfer Protocol' }, - { p: 'ddp', n: 37, d: 'Datagram Delivery Protocol' }, - { p: 'idpr-cmtp', n: 38, d: 'IDPR Control Message Transport' }, - { p: 'ipv6', n: 41, d: 'Internet Protocol, version 6' }, - { p: 'ipv6-route', n: 43, d: 'Routing Header for IPv6' }, - { p: 'ipv6-frag', n: 44, d: 'Fragment Header for IPv6' }, - { p: 'idrp', n: 45, d: 'Inter-Domain Routing Protocol' }, - { p: 'rsvp', n: 46, d: 'Reservation Protocol' }, - { p: 'gre', n: 47, d: 'General Routing Encapsulation' }, - { p: 'esp', n: 50, d: 'Encap Security Payload [RFC2406]' }, - { p: 'ah', n: 51, d: 'Authentication Header [RFC2402]' }, - { p: 'skip', n: 57, d: 'SKIP' }, - { p: 'ipv6-icmp', n: 58, d: 'ICMP for IPv6' }, - { p: 'ipv6-nonxt', n: 59, d: 'No Next Header for IPv6' }, - { p: 'ipv6-opts', n: 60, d: 'Destination Options for IPv6' }, - { p: 'vmtp', n: 81, d: 'Versatile Message Transport' }, - { p: 'eigrp', n: 88, d: 'Enhanced Interior Routing Protocol (Cisco)' }, - { p: 'ospf', n: 89, d: 'Open Shortest Path First IGP' }, - { p: 'ax.25', n: 93, d: 'AX.25 frames' }, - { p: 'ipip', n: 94, d: 'IP-within-IP Encapsulation Protocol' }, - { p: 'etherip', n: 97, d: 'Ethernet-within-IP Encapsulation [RFC3378]' }, - { p: 'encap', n: 98, d: 'Yet Another IP encapsulation [RFC1241]' }, - { p: 'pim', n: 103, d: 'Protocol Independent Multicast' }, - { p: 'ipcomp', n: 108, d: 'IP Payload Compression Protocol' }, - { p: 'vrrp', n: 112, d: 'Virtual Router Redundancy Protocol [RFC5798]' }, - { p: 'l2tp', n: 115, d: 'Layer Two Tunneling Protocol [RFC2661]' }, - { p: 'isis', n: 124, d: 'IS-IS over IPv4' }, - { p: 'sctp', n: 132, d: 'Stream Control Transmission Protocol' }, - { p: 'fc', n: 133, d: 'Fibre Channel' }, - { p: 'mobility-header', n: 135, d: 'Mobility Support for IPv6 [RFC3775]' }, - { p: 'udplite', n: 136, d: 'UDP-Lite [RFC3828]' }, - { p: 'mpls-in-ip', n: 137, d: 'MPLS-in-IP [RFC4023]' }, - { p: 'hip', n: 139, d: 'Host Identity Protocol' }, - { p: 'shim6', n: 140, d: 'Shim6 Protocol [RFC5533]' }, - { p: 'wesp', n: 141, d: 'Wrapped Encapsulating Security Payload' }, - { p: 'rohc', n: 142, d: 'Robust Header Compression' } - ] - } -}); -Ext.define('PVE.form.CPUModelSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.CPUModelSelector'], - - valueField: 'value', - displayField: 'value', - - emptyText: Proxmox.Utils.defaultText + ' (kvm64)', - allowBlank: true, - - editable: true, - anyMatch: true, - forceSelection: true, - autoSelect: false, - - deleteEmpty: true, - - listConfig: { - columns: [ - { - header: gettext('Model'), - dataIndex: 'value', - hideable: false, - sortable: true, - flex: 2 - }, - { - header: gettext('Vendor'), - dataIndex: 'vendor', - hideable: false, - sortable: true, - flex: 1 - } - ], - width: 320 - }, - - store: { - fields: [ 'value', 'vendor' ], - data: [ - { - value: 'athlon', - vendor: 'AMD' - }, - { - value: 'phenom', - vendor: 'AMD' - }, - { - value: 'Opteron_G1', - vendor: 'AMD' - }, - { - value: 'Opteron_G2', - vendor: 'AMD' - }, - { - value: 'Opteron_G3', - vendor: 'AMD' - }, - { - value: 'Opteron_G4', - vendor: 'AMD' - }, - { - value: 'Opteron_G5', - vendor: 'AMD' - }, - { - value: 'EPYC', - vendor: 'AMD' - }, - { - value: '486', - vendor: 'Intel' - }, - { - value: 'core2duo', - vendor: 'Intel' - }, - { - value: 'coreduo', - vendor: 'Intel' - }, - { - value: 'pentium', - vendor: 'Intel' - }, - { - value: 'pentium2', - vendor: 'Intel' - }, - { - value: 'pentium3', - vendor: 'Intel' - }, - { - value: 'Conroe', - vendor: 'Intel' - }, - { - value: 'Penryn', - vendor: 'Intel' - }, - { - value: 'Nehalem', - vendor: 'Intel' - }, - { - value: 'Westmere', - vendor: 'Intel' - }, - { - value: 'SandyBridge', - vendor: 'Intel' - }, - { - value: 'IvyBridge', - vendor: 'Intel' - }, - { - value: 'Haswell', - vendor: 'Intel' - }, - { - value: 'Haswell-noTSX', - vendor: 'Intel' - }, - { - value: 'Broadwell', - vendor: 'Intel' - }, - { - value: 'Broadwell-noTSX', - vendor: 'Intel' - }, - { - value: 'Skylake-Client', - vendor: 'Intel' - }, - { - value: 'Skylake-Server', - vendor: 'Intel' - }, - { - value: 'Cascadelake-Server', - vendor: 'Intel' - }, - { - value: 'KnightsMill', - vendor: 'Intel' - }, - { - value: 'kvm32', - vendor: 'QEMU' - }, - { - value: 'kvm64', - vendor: 'QEMU' - }, - { - value: 'qemu32', - vendor: 'QEMU' - }, - { - value: 'qemu64', - vendor: 'QEMU' - }, - { - value: 'host', - vendor: 'Host' - } - ] - } -}); -Ext.define('PVE.form.VNCKeyboardSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.VNCKeyboardSelector'], - comboItems: PVE.Utils.kvm_keymap_array() -}); -Ext.define('PVE.form.CacheTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.CacheTypeSelector'], - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (" + gettext('No cache') + ")"], - ['directsync', 'Direct sync'], - ['writethrough', 'Write through'], - ['writeback', 'Write back'], - ['unsafe', 'Write back (' + gettext('unsafe') + ')'], - ['none', gettext('No cache')] - ] -}); -Ext.define('PVE.form.SnapshotSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.PVE.form.SnapshotSelector'], - - valueField: 'name', - displayField: 'name', - - loadStore: function(nodename, vmid) { - var me = this; - - if (!nodename) { - return; - } - - me.nodename = nodename; - - if (!vmid) { - return; - } - - me.vmid = vmid; - - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid +'/snapshot' - }); - - me.store.load(); - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.guestType) { - throw "no guest type specified"; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'name'], - filterOnLoad: true - }); - - Ext.apply(me, { - store: store, - listConfig: { - columns: [ - { - header: gettext('Snapshot'), - dataIndex: 'name', - hideable: false, - flex: 1 - } - ] - } - }); - - me.callParent(); - - me.loadStore(me.nodename, me.vmid); - } -}); -Ext.define('PVE.form.ContentTypeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveContentTypeSelector'], - - cts: undefined, - - initComponent: function() { - var me = this; - - me.comboItems = []; - - if (me.cts === undefined) { - me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir', 'snippets']; - } - - Ext.Array.each(me.cts, function(ct) { - me.comboItems.push([ct, PVE.Utils.format_content_types(ct)]); - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.HotplugFeatureSelector', { - extend: 'Ext.form.CheckboxGroup', - alias: 'widget.pveHotplugFeatureSelector', - - columns: 1, - vertical: true, - - defaults: { - name: 'hotplug', - submitValue: false - }, - items: [ - { - boxLabel: gettext('Disk'), - inputValue: 'disk', - checked: true - }, - { - boxLabel: gettext('Network'), - inputValue: 'network', - checked: true - }, - { - boxLabel: 'USB', - inputValue: 'usb', - checked: true - }, - { - boxLabel: gettext('Memory'), - inputValue: 'memory' - }, - { - boxLabel: gettext('CPU'), - inputValue: 'cpu' - } - ], - - setValue: function(value) { - var me = this; - var newVal = []; - if (value === '1') { - newVal = ['disk', 'network', 'usb']; - } else if (value !== '0') { - newVal = value.split(','); - } - me.callParent([{ hotplug: newVal }]); - }, - - // override framework function to - // assemble the hotplug value - getSubmitData: function() { - var me = this, - boxes = me.getBoxes(), - data = []; - Ext.Array.forEach(boxes, function(box){ - if (box.getValue()) { - data.push(box.inputValue); - } - }); - - /* because above is hotplug an array */ - /*jslint confusion: true*/ - if (data.length === 0) { - return { 'hotplug':'0' }; - } else { - return { 'hotplug': data.join(',') }; - } - } - -}); -Ext.define('PVE.form.AgentFeatureSelector', { - extend: 'Proxmox.panel.InputPanel', - alias: ['widget.pveAgentFeatureSelector'], - - viewModel: {}, - - items: [ - { - xtype: 'proxmoxcheckbox', - boxLabel: Ext.String.format(gettext('Use {0}'), 'QEMU Guest Agent'), - name: 'enabled', - reference: 'enabled', - uncheckedValue: 0, - }, - { - xtype: 'proxmoxcheckbox', - boxLabel: gettext('Run guest-trim after clone disk'), - name: 'fstrim_cloned_disks', - bind: { - disabled: '{!enabled.checked}', - }, - disabled: true - }, - { - xtype: 'displayfield', - userCls: 'pmx-hint', - value: gettext('Make sure the QEMU Guest Agent is installed in the VM'), - bind: { - hidden: '{!enabled.checked}', - }, - }, - ], - - advancedItems: [ - { - xtype: 'proxmoxKVComboBox', - name: 'type', - value: '__default__', - deleteEmpty: false, - fieldLabel: 'Type', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (VirtIO)"], - ['virtio', 'VirtIO'], - ['isa', 'ISA'], - ], - } - ], - - onGetValues: function(values) { - var agentstr = PVE.Parser.printPropertyString(values, 'enabled'); - return { agent: agentstr }; - }, - - setValues: function(values) { - let res = PVE.Parser.parsePropertyString(values.agent, 'enabled'); - this.callParent([res]); - } -}); -Ext.define('PVE.form.iScsiProviderSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveiScsiProviderSelector'], - comboItems: [ - ['comstar', 'Comstar'], - [ 'istgt', 'istgt'], - [ 'iet', 'IET'], - [ 'LIO', 'LIO'] - ] -}); -Ext.define('PVE.form.DayOfWeekSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveDayOfWeekSelector'], - comboItems:[], - initComponent: function(){ - var me = this; - me.comboItems = [ - ['mon', Ext.util.Format.htmlDecode(Ext.Date.dayNames[1])], - ['tue', Ext.util.Format.htmlDecode(Ext.Date.dayNames[2])], - ['wed', Ext.util.Format.htmlDecode(Ext.Date.dayNames[3])], - ['thu', Ext.util.Format.htmlDecode(Ext.Date.dayNames[4])], - ['fri', Ext.util.Format.htmlDecode(Ext.Date.dayNames[5])], - ['sat', Ext.util.Format.htmlDecode(Ext.Date.dayNames[6])], - ['sun', Ext.util.Format.htmlDecode(Ext.Date.dayNames[0])] - ]; - this.callParent(); - } -}); -Ext.define('PVE.form.BackupModeSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveBackupModeSelector'], - comboItems: [ - ['snapshot', gettext('Snapshot')], - ['suspend', gettext('Suspend')], - ['stop', gettext('Stop')] - ] -}); -Ext.define('PVE.form.ScsiHwSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveScsiHwSelector'], - comboItems: [ - ['__default__', PVE.Utils.render_scsihw('')], - ['lsi', PVE.Utils.render_scsihw('lsi')], - ['lsi53c810', PVE.Utils.render_scsihw('lsi53c810')], - ['megasas', PVE.Utils.render_scsihw('megasas')], - ['virtio-scsi-pci', PVE.Utils.render_scsihw('virtio-scsi-pci')], - ['virtio-scsi-single', PVE.Utils.render_scsihw('virtio-scsi-single')], - ['pvscsi', PVE.Utils.render_scsihw('pvscsi')] - ] -}); -Ext.define('PVE.form.FirewallPolicySelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallPolicySelector'], - comboItems: [ - ['ACCEPT', 'ACCEPT'], - ['REJECT', 'REJECT'], - [ 'DROP', 'DROP'] - ] -}); -/* - * This is a global search field - * it loads the /cluster/resources on focus - * and displays the result in a floating grid - * - * it filters and sorts the objects by the algorithm in - * the customFilter function - * - * also it does accept key up/down and enter for input - * and it opens to ctrl+shift+f and ctrl+space - */ -Ext.define('PVE.form.GlobalSearchField', { - extend: 'Ext.form.field.Text', - alias: 'widget.pveGlobalSearchField', - - emptyText: gettext('Search'), - enableKeyEvents: true, - selectOnFocus: true, - padding: '0 5 0 5', - - grid: { - xtype: 'gridpanel', - focusOnToFront: false, - floating: true, - emptyText: Proxmox.Utils.noneText, - width: 600, - height: 400, - scrollable: { - xtype: 'scroller', - y: true, - x:false - }, - store: { - model: 'PVEResources', - proxy:{ - type: 'proxmox', - url: '/api2/extjs/cluster/resources' - } - }, - plugins: { - ptype: 'bufferedrenderer', - trailingBufferZone: 20, - leadingBufferZone: 20 - }, - - hideMe: function() { - var me = this; - if (typeof me.ctxMenu !== 'undefined' && me.ctxMenu.isVisible()) { - return; - } - me.hasFocus = false; - if (!me.textfield.hasFocus) { - me.hide(); - } - }, - - setFocus: function() { - var me = this; - me.hasFocus = true; - }, - - listeners: { - rowclick: function(grid, record) { - var me = this; - me.textfield.selectAndHide(record.id); - }, - itemcontextmenu: function(v, record, item, index, event) { - var me = this; - me.ctxMenu = PVE.Utils.createCmdMenu(v, record, item, index, event); - }, - /* because of lint */ - focusleave: { - fn: 'hideMe' - }, - focusenter: 'setFocus' - }, - - columns: [ - { - text: gettext('Type'), - dataIndex: 'type', - width: 100, - renderer: PVE.Utils.render_resource_type - }, - { - text: gettext('Description'), - flex: 1, - dataIndex: 'text' - }, - { - text: gettext('Node'), - dataIndex: 'node' - }, - { - text: gettext('Pool'), - dataIndex: 'pool' - } - ] - }, - - customFilter: function(item) { - var me = this; - var match = 0; - var fieldArr = []; - var i,j, fields; - - // different types of objects have different fields to search - // for example, a node will never have a pool and vice versa - switch (item.data.type) { - case 'pool': fieldArr = ['type', 'pool', 'text']; break; - case 'node': fieldArr = ['type', 'node', 'text']; break; - case 'storage': fieldArr = ['type', 'pool', 'node', 'storage']; break; - default: fieldArr = ['name', 'type', 'node', 'pool', 'vmid']; - } - if (me.filterVal === '') { - item.data.relevance = 0; - return true; - } - - // all text is case insensitive and each word is - // searched alone - // for every partial match, the row gets - // 1 match point, for every exact match - // it gets 2 points - // - // results gets sorted by points (descending) - fields = me.filterVal.split(/\s+/); - for(i = 0; i < fieldArr.length; i++) { - var v = item.data[fieldArr[i]]; - if (v !== undefined) { - v = v.toString().toLowerCase(); - for(j = 0; j < fields.length; j++) { - if (v.indexOf(fields[j]) !== -1) { - match++; - if(v === fields[j]) { - match++; - } - } - } - } - } - // give the row the 'relevance' value - item.data.relevance = match; - return (match > 0); - }, - - updateFilter: function(field, newValue, oldValue) { - var me = this; - // parse input and filter store, - // show grid - me.grid.store.filterVal = newValue.toLowerCase().trim(); - me.grid.store.clearFilter(true); - me.grid.store.filterBy(me.customFilter); - me.grid.getSelectionModel().select(0); - }, - - selectAndHide: function(id) { - var me = this; - me.tree.selectById(id); - me.grid.hide(); - me.setValue(''); - me.blur(); - }, - - onKey: function(field, e) { - var me = this; - var key = e.getKey(); - - switch(key) { - case Ext.event.Event.ENTER: - // go to first entry if there is one - if (me.grid.store.getCount() > 0) { - me.selectAndHide(me.grid.getSelection()[0].data.id); - } - break; - case Ext.event.Event.UP: - me.grid.getSelectionModel().selectPrevious(); - break; - case Ext.event.Event.DOWN: - me.grid.getSelectionModel().selectNext(); - break; - case Ext.event.Event.ESC: - me.grid.hide(); - me.blur(); - break; - } - }, - - loadValues: function(field) { - var me = this; - var records = []; - - me.hasFocus = true; - me.grid.textfield = me; - me.grid.store.load(); - me.grid.showBy(me, 'tl-bl'); - }, - - hideGrid: function() { - var me = this; - - me.hasFocus = false; - if (!me.grid.hasFocus) { - me.grid.hide(); - } - }, - - listeners: { - change: { - fn: 'updateFilter', - buffer: 250 - }, - specialkey: 'onKey', - focusenter: 'loadValues', - focusleave: { - fn: 'hideGrid', - delay: 100 - } - }, - - toggleFocus: function() { - var me = this; - if (!me.hasFocus) { - me.focus(); - } else { - me.blur(); - } - }, - - initComponent: function() { - var me = this; - - if (!me.tree) { - throw "no tree given"; - } - - me.grid = Ext.create(me.grid); - - me.callParent(); - - /*jslint confusion: true*/ - /*because shift is also a function*/ - // bind ctrl+shift+f and ctrl+space - // to open/close the search - me.keymap = new Ext.KeyMap({ - target: Ext.get(document), - binding: [{ - key:'F', - ctrl: true, - shift: true, - fn: me.toggleFocus, - scope: me - },{ - key:' ', - ctrl: true, - fn: me.toggleFocus, - scope: me - }] - }); - - // always select first item and - // sort by relevance after load - me.mon(me.grid.store, 'load', function() { - me.grid.getSelectionModel().select(0); - me.grid.store.sort({ - property: 'relevance', - direction: 'DESC' - }); - }); - } - -}); -Ext.define('PVE.form.QemuBiosSelector', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveQemuBiosSelector'], - - initComponent: function() { - var me = this; - - me.comboItems = [ - ['__default__', PVE.Utils.render_qemu_bios('')], - ['seabios', PVE.Utils.render_qemu_bios('seabios')], - ['ovmf', PVE.Utils.render_qemu_bios('ovmf')] - ]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -/* filter is a javascript builtin, but extjs calls it also filter */ -Ext.define('PVE.form.VMSelector', { - extend: 'Ext.grid.Panel', - alias: 'widget.vmselector', - - mixins: { - field: 'Ext.form.field.Field' - }, - - allowBlank: true, - selectAll: false, - isFormField: true, - - plugins: 'gridfilters', - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - // due to EXTJS-18711 - // we have to do a static list via a store - // but to avoid creating an object, - // we have to have a pseudo un function - un: function(){} - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - flex: 1, - filter: { - type: 'list' - } - } - ], - - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE' - }, - - checkChangeEvents: [ - 'selectionchange', - 'change' - ], - - listeners: { - selectionchange: function() { - // to trigger validity and error checks - this.checkChange(); - } - }, - - getValue: function() { - var me = this; - var sm = me.getSelectionModel(); - var selection = sm.getSelection(); - var values = []; - var store = me.getStore(); - selection.forEach(function(item) { - // only add if not filtered - if (store.findExact('vmid', item.data.vmid) !== -1) { - values.push(item.data.vmid); - } - }); - return values; - }, - - setValue: function(value) { - console.log(value); - var me = this; - var sm = me.getSelectionModel(); - if (!Ext.isArray(value)) { - value = value.split(','); - } - var selection = []; - var store = me.getStore(); - - value.forEach(function(item) { - var rec = store.findRecord('vmid',item, 0, false, true, true); - console.log(store); - - if (rec) { - console.log(rec); - selection.push(rec); - } - }); - - sm.select(selection); - - return me.mixins.field.setValue.call(me, value); - }, - - getErrors: function(value) { - var me = this; - if (me.allowBlank === false && - me.getSelectionModel().getCount() === 0) { - me.addBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return [gettext('No VM selected')]; - } - - me.removeBodyCls(['x-form-trigger-wrap-default','x-form-trigger-wrap-invalid']); - return []; - }, - - initComponent: function() { - var me = this; - - me.callParent(); - - if (me.nodename) { - me.store.filters.add({ - property: 'node', - exactMatch: true, - value: me.nodename - }); - } - - // only show the relevant guests by default - if (me.action) { - var statusfilter = ''; - switch (me.action) { - case 'startall': - statusfilter = 'stopped'; - break; - case 'stopall': - statusfilter = 'running'; - break; - } - if (statusfilter !== '') { - me.store.filters.add({ - property: 'template', - value: 0 - },{ - id: 'x-gridfilter-status', - operator: 'in', - property: 'status', - value: [statusfilter] - }); - } - } - - var store = me.getStore(); - var sm = me.getSelectionModel(); - - if (me.selectAll) { - me.mon(store,'load', function(){ - me.getSelectionModel().selectAll(false); - }); - } - } -}); - - -Ext.define('PVE.form.VMComboSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.vmComboSelector', - - valueField: 'vmid', - displayField: 'vmid', - - autoSelect: false, - editable: true, - anyMatch: true, - forceSelection: true, - - store: { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [{ - property: 'type', - value: /lxc|qemu/ - }] - }, - - listConfig: { - width: 600, - plugins: 'gridfilters', - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 80, - filter: { - type: 'number' - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - filter: { - type: 'string' - } - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'status', - filter: { - type: 'list' - } - }, - { - header: gettext('Pool'), - dataIndex: 'pool', - hidden: true, - filter: { - type: 'list' - } - }, - { - header: gettext('Type'), - dataIndex: 'type', - width: 120, - renderer: function(value) { - if (value === 'qemu') { - return gettext('Virtual Machine'); - } else if (value === 'lxc') { - return gettext('LXC Container'); - } - - return ''; - }, - filter: { - type: 'list', - store: { - data: [ - {id: 'qemu', text: gettext('Virtual Machine')}, - {id: 'lxc', text: gettext('LXC Container')} - ], - un: function(){} // due to EXTJS-18711 - } - } - }, - { - header: 'HA ' + gettext('Status'), - dataIndex: 'hastate', - hidden: true, - flex: 1, - filter: { - type: 'list' - } - } - ] - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.form.VMCPUFlagSelector', { - extend: 'Ext.grid.Panel', - alias: 'widget.vmcpuflagselector', - - mixins: { - field: 'Ext.form.field.Field' - }, - - disableSelection: true, - columnLines: false, - selectable: false, - hideHeaders: true, - - scrollable: 'y', - height: 200, - - unkownFlags: [], - - store: { - type: 'store', - fields: ['flag', { name: 'state', defaultValue: '=' }, 'desc'], - data: [ - // FIXME: let qemu-server host this and autogenerate or get from API call?? - { flag: 'md-clear', desc: 'Required to let the guest OS know if MDS is mitigated correctly' }, - { flag: 'pcid', desc: 'Meltdown fix cost reduction on Westmere, Sandy-, and IvyBridge Intel CPUs' }, - { flag: 'spec-ctrl', desc: 'Allows improved Spectre mitigation with Intel CPUs' }, - { flag: 'ssbd', desc: 'Protection for "Speculative Store Bypass" for Intel models' }, - { flag: 'ibpb', desc: 'Allows improved Spectre mitigation with AMD CPUs' }, - { flag: 'virt-ssbd', desc: 'Basis for "Speculative Store Bypass" protection for AMD models' }, - { flag: 'amd-ssbd', desc: 'Improves Spectre mitigation performance with AMD CPUs, best used with "virt-ssbd"' }, - { flag: 'amd-no-ssb', desc: 'Notifies guest OS that host is not vulnerable for Spectre on AMD CPUs' }, - { flag: 'pdpe1gb', desc: 'Allow guest OS to use 1GB size pages, if host HW supports it' }, - { flag: 'hv-tlbflush', desc: 'Improve performance in overcommitted Windows guests. May lead to guest bluescreens on old CPUs.' }, - { flag: 'hv-evmcs', desc: 'Improve performance for nested virtualization. Only supported on Intel CPUs.' }, - { flag: 'aes', desc: 'Activate AES instruction set for HW acceleration.' } - ], - listeners: { - update: function() { - this.commitChanges(); - } - } - }, - - getValue: function() { - var me = this; - var store = me.getStore(); - var flags = ''; - - // ExtJS does not has a nice getAllRecords interface for stores :/ - store.queryBy(Ext.returnTrue).each(function(rec) { - var s = rec.get('state'); - if (s && s !== '=') { - var f = rec.get('flag'); - if (flags === '') { - flags = s + f; - } else { - flags += ';' + s + f; - } - } - }); - - flags += me.unkownFlags.join(';'); - - return flags; - }, - - setValue: function(value) { - var me = this; - var store = me.getStore(); - - me.value = value || ''; - - me.unkownFlags = []; - - me.getStore().queryBy(Ext.returnTrue).each(function(rec) { - rec.set('state', '='); - }); - - var flags = value ? value.split(';') : []; - flags.forEach(function(flag) { - var sign = flag.substr(0, 1); - flag = flag.substr(1); - - var rec = store.findRecord('flag', flag); - if (rec !== null) { - rec.set('state', sign); - } else { - me.unkownFlags.push(flag); - } - }); - store.reload(); - - var res = me.mixins.field.setValue.call(me, value); - - return res; - }, - columns: [ - { - dataIndex: 'state', - renderer: function(v) { - switch(v) { - case '=': return 'Default'; - case '-': return 'Off'; - case '+': return 'On'; - default: return 'Unknown'; - } - }, - width: 65 - }, - { - xtype: 'widgetcolumn', - dataIndex: 'state', - width: 95, - onWidgetAttach: function (column, widget, record) { - var val = record.get('state') || '='; - widget.down('[inputValue=' + val + ']').setValue(true); - // TODO: disable if selected CPU model and flag are incompatible - }, - widget: { - xtype: 'radiogroup', - hideLabel: true, - layout: 'hbox', - validateOnChange: false, - value: '=', - listeners: { - change: function(f, value) { - var v = Object.values(value)[0]; - f.getWidgetRecord().set('state', v); - - var view = this.up('grid'); - view.dirty = view.getValue() !== view.originalValue; - view.checkDirty(); - //view.checkChange(); - } - }, - items: [ - { - boxLabel: '-', - boxLabelAlign: 'before', - inputValue: '-' - }, - { - checked: true, - inputValue: '=' - }, - { - boxLabel: '+', - inputValue: '+' - } - ] - } - }, - { - dataIndex: 'flag', - width: 100 - }, - { - dataIndex: 'desc', - cellWrap: true, - flex: 1 - } - ], - - initComponent: function() { - var me = this; - - // static class store, thus gets not recreated, so ensure defaults are set! - me.getStore().data.forEach(function(v) { - v.state = '='; - }); - - me.value = me.originalValue = ''; - - me.callParent(arguments); - } -}); -Ext.define('PVE.form.USBSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveUSBSelector'], - - allowBlank: false, - autoSelect: false, - anyMatch: true, - displayField: 'product_and_id', - valueField: 'usbid', - editable: true, - - validator: function(value) { - var me = this; - if (!value) { - return true; // handled later by allowEmpty in the getErrors call chain - } - value = me.getValue(); // as the valueField is not the displayfield - if (me.type === 'device') { - return (/^[a-f0-9]{4}\:[a-f0-9]{4}$/i).test(value); - } else if (me.type === 'port') { - return (/^[0-9]+\-[0-9]+(\.[0-9]+)*$/).test(value); - } - return gettext("Invalid Value"); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - - if (!nodename) { - throw "no nodename specified"; - } - - if (me.type !== 'device' && me.type !== 'port') { - throw "no valid type specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-usb-' + me.type, - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/scan/usb" - }, - filters: [ - function (item) { - return !!item.data.usbpath && !!item.data.prodid && item.data['class'] != 9; - } - ] - }); - let emptyText = ''; - if (me.type === 'device') { - emptyText = gettext('Passthrough a specific device'); - } else { - emptyText = gettext('Passthrough a full port'); - } - - Ext.apply(me, { - store: store, - emptyText: emptyText, - listConfig: { - width: 520, - columns: [ - { - header: (me.type === 'device')?gettext('Device'):gettext('Port'), - sortable: true, - dataIndex: 'usbid', - width: 80 - }, - { - header: gettext('Manufacturer'), - sortable: true, - dataIndex: 'manufacturer', - width: 150 - }, - { - header: gettext('Product'), - sortable: true, - dataIndex: 'product', - flex: 1 - }, - { - header: gettext('Speed'), - width: 75, - sortable: true, - dataIndex: 'speed', - renderer: function(value) { - let speed_map = { - "10000" : "USB 3.1", - "5000" : "USB 3.0", - "480" : "USB 2.0", - "12" : "USB 1.x", - "1.5": "USB 1.x", - }; - return speed_map[value] || value + " Mbps"; - } - } - ] - }, - }); - - me.callParent(); - - store.load(); - } - -}, function() { - - Ext.define('pve-usb-device', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val, data) { - if (val) { - return val; - } - return data.get('vendid') + ':' + data.get('prodid'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' }, - { - name: 'product_and_id', - type: 'string', - convert: (v, rec) => { - let res = rec.data.product || gettext('Unkown'); - res += " (" + rec.data.usbid + ")"; - return res; - }, - }, - ] - }); - - Ext.define('pve-usb-port', { - extend: 'Ext.data.Model', - fields: [ - { - name: 'usbid', - convert: function(val,data) { - if (val) { - return val; - } - return data.get('busnum') + '-' + data.get('usbpath'); - } - }, - 'speed', 'product', 'manufacturer', 'vendid', 'prodid', 'usbpath', - { name: 'port' , type: 'number' }, - { name: 'level' , type: 'number' }, - { name: 'class' , type: 'number' }, - { name: 'devnum' , type: 'number' }, - { name: 'busnum' , type: 'number' }, - { - name: 'product_and_id', - type: 'string', - convert: (v, rec) => { - let res = rec.data.product || gettext('Unplugged'); - res += " (" + rec.data.usbid + ")"; - return res; - }, - }, - ] - }); -}); -Ext.define('PVE.form.CalendarEvent', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pveCalendarEvent', - - editable: true, - - valueField: 'value', - displayField: 'text', - queryMode: 'local', - - store: { - field: [ 'value', 'text'], - data: [ - { value: '*/30', text: Ext.String.format(gettext("Every {0} minutes"), 30) }, - { value: '*/2:00', text: gettext("Every two hours")}, - { value: '2,22:30', text: gettext("Every day") + " 02:30, 22:30"}, - { value: 'mon..fri', text: gettext("Monday to Friday") + " 00:00"}, - { value: 'mon..fri */1:00', text: gettext("Monday to Friday") + ': ' + gettext("hourly")}, - { value: 'sun 01:00', text: gettext("Sunday") + " 01:00"} - ] - }, - - tpl: [ - '
    ', - '
  • {text}
  • ', - '
' - ], - - displayTpl: [ - '', - '{value}', - '' - ] - -}); -Ext.define('PVE.form.CephPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephPoolSelector', - - allowBlank: false, - valueField: 'pool_name', - displayField: 'pool_name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/pools' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.form.PermPathSelector', { - extend: 'Ext.form.field.ComboBox', - xtype: 'pvePermPathSelector', - - valueField: 'value', - displayField: 'value', - typeAhead: true, - queryMode: 'local', - store: { - type: 'pvePermPath' - } -}); -Ext.define('PVE.form.SpiceEnhancementSelector', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveSpiceEnhancementSelector', - - viewModel: {}, - - items: [ - { - xtype: 'proxmoxcheckbox', - itemId: 'foldersharing', - name: 'foldersharing', - reference: 'foldersharing', - fieldLabel: 'Folder Sharing', - uncheckedValue: 0, - }, - { - xtype: 'proxmoxKVComboBox', - itemId: 'videostreaming', - name: 'videostreaming', - value: 'off', - fieldLabel: 'Video Streaming', - comboItems: [ - ['off', 'off'], - ['all', 'all'], - ['filter', 'filter'], - ], - }, - { - xtype: 'displayfield', - itemId: 'spicehint', - userCls: 'pmx-hint', - value: gettext('To use these features set the display to SPICE in the hardware settings of the VM.'), - hidden: true, - }, - { - xtype: 'displayfield', - itemId: 'spicefolderhint', - userCls: 'pmx-hint', - value: gettext('Make sure the SPICE WebDav daemon is installed in the VM.'), - bind: { - hidden: '{!foldersharing.checked}', - } - } - ], - - onGetValues: function(values) { - var ret = {}; - - if (values.videostreaming !== "off") { - ret.videostreaming = values.videostreaming; - } - if (values.foldersharing) { - ret.foldersharing = 1; - } - if (Ext.Object.isEmpty(ret)) { - return { 'delete': 'spice_enhancements' }; - } - var enhancements = PVE.Parser.printPropertyString(ret); - return { spice_enhancements: enhancements }; - }, - - setValues: function(values) { - var vga = PVE.Parser.parsePropertyString(values.vga, 'type'); - if (!/^qxl\d?$/.test(vga.type)) { - this.down('#spicehint').setVisible(true); - } - if (values.spice_enhancements) { - var enhancements = PVE.Parser.parsePropertyString(values.spice_enhancements); - enhancements['foldersharing'] = PVE.Parser.parseBoolean(enhancements['foldersharing'], 0); - this.callParent([enhancements]); - } - }, -}); -/* This class defines the "Tasks" tab of the bottom status panel - * Tasks are jobs with a start, end and log output - */ - -Ext.define('PVE.dc.Tasks', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterTasks'], - - initComponent : function() { - var me = this; - - var taskstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-tasks', - model: 'proxmox-tasks', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/tasks' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: taskstore, - sortAfterUpdate: true, - appendAtStart: true, - sorters: [ - { - property : 'pid', - direction: 'DESC' - }, - { - property : 'starttime', - direction: 'DESC' - } - ] - - }); - - var run_task_viewer = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: rec.data.upid - }); - win.show(); - }; - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, // does not work with getRowClass() - - getRowClass: function(record, index) { - var status = record.get('status'); - - if (status && status != 'OK') { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Start Time"), - dataIndex: 'starttime', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("End Time"), - dataIndex: 'endtime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type == "vncproxy" || - record.data.type == "vncshell" || - record.data.type == "spiceproxy") { - metaData.tdCls = "x-grid-row-console"; - } else { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Description"), - dataIndex: 'upid', - flex: 1, - renderer: Proxmox.Utils.render_upid - }, - { - header: gettext("Status"), - dataIndex: 'status', - width: 200, - renderer: function(value, metaData, record) { - if (record.data.pid) { - if (record.data.type != "vncproxy") { - metaData.tdCls = "x-grid-row-loading"; - } - return ""; - } - if (value == 'OK') { - return 'OK'; - } - // metaData.attr = 'style="color:red;"'; - return Proxmox.Utils.errorText + ': ' + value; - } - } - ], - listeners: { - itemdblclick: run_task_viewer, - show: taskstore.startUpdate, - destroy: taskstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* This class defines the "Cluster log" tab of the bottom status panel - * A log entry is a timestamp associated with an action on a cluster - */ - -Ext.define('PVE.dc.Log', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveClusterLog'], - - initComponent : function() { - var me = this; - - var logstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-cluster-log', - model: 'proxmox-cluster-log', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/log' - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: logstore, - appendAtStart: true - }); - - Ext.apply(me, { - store: store, - stateful: false, - - viewConfig: { - trackOver: false, - stripeRows: true, - - getRowClass: function(record, index) { - var pri = record.get('pri'); - - if (pri && pri <= 3) { - return "proxmox-invalid-row"; - } - } - }, - sortableColumns: false, - columns: [ - { - header: gettext("Time"), - dataIndex: 'time', - width: 150, - renderer: function(value) { - return Ext.Date.format(value, "M d H:i:s"); - } - }, - { - header: gettext("Node"), - dataIndex: 'node', - width: 150 - }, - { - header: gettext("Service"), - dataIndex: 'tag', - width: 100 - }, - { - header: "PID", - dataIndex: 'pid', - width: 100 - }, - { - header: gettext("User name"), - dataIndex: 'user', - width: 150 - }, - { - header: gettext("Severity"), - dataIndex: 'pri', - renderer: PVE.Utils.render_serverity, - width: 100 - }, - { - header: gettext("Message"), - dataIndex: 'msg', - flex: 1 - } - ], - listeners: { - activate: logstore.startUpdate, - deactivate: logstore.stopUpdate, - destroy: logstore.stopUpdate - } - }); - - me.callParent(); - } -}); -/* - * This class describes the bottom panel - */ -Ext.define('PVE.panel.StatusPanel', { - extend: 'Ext.tab.Panel', - alias: 'widget.pveStatusPanel', - - - //title: "Logs", - //tabPosition: 'bottom', - - initComponent: function() { - var me = this; - - var stateid = 'ltab'; - var sp = Ext.state.Manager.getProvider(); - - var state = sp.get(stateid); - if (state && state.value) { - me.activeTab = state.value; - } - - Ext.apply(me, { - listeners: { - tabchange: function() { - var atab = me.getActiveTab().itemId; - var state = { value: atab }; - sp.set(stateid, state); - } - }, - items: [ - { - itemId: 'tasks', - title: gettext('Tasks'), - xtype: 'pveClusterTasks' - }, - { - itemId: 'clog', - title: gettext('Cluster log'), - xtype: 'pveClusterLog' - } - ] - }); - - me.callParent(); - - me.items.get(0).fireEvent('show', me.items.get(0)); - - var statechange = function(sp, key, state) { - if (key === stateid) { - var atab = me.getActiveTab().itemId; - var ntab = state.value; - if (state && ntab && (atab != ntab)) { - me.setActiveTab(ntab); - } - } - }; - - sp.on('statechange', statechange); - me.on('destroy', function() { - sp.un('statechange', statechange); - }); - - } -}); -Ext.define('PVE.panel.StatusView', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStatusView', - - layout: { - type: 'column' - }, - - title: gettext('Status'), - - getRecordValue: function(key, store) { - if (!key) { - throw "no key given"; - } - var me = this; - - if (store === undefined) { - store = me.getStore(); - } - - var rec = store.getById(key); - if (rec) { - return rec.data.value; - } - - return ''; - }, - - fieldRenderer: function(val,max) { - if (max === undefined) { - return val; - } - - if (!Ext.isNumeric(max) || max === 1) { - return PVE.Utils.render_usage(val); - } - return PVE.Utils.render_size_usage(val,max); - }, - - fieldCalculator: function(used, max) { - if (!Ext.isNumeric(max) && Ext.isNumeric(used)) { - return used; - } else if(!Ext.isNumeric(used)) { - /* we come here if the field is from a node - * where the records are not mem and maxmem - * but mem.used and mem.total - */ - if (used.used !== undefined && - used.total !== undefined) { - return used.used/used.total; - } - } - - return used/max; - }, - - updateField: function(field) { - var me = this; - var text = ''; - var renderer = me.fieldRenderer; - if (Ext.isFunction(field.renderer)) { - renderer = field.renderer; - } - if (field.multiField === true) { - field.updateValue(renderer.call(field, me.getStore().getRecord())); - } else if (field.textField !== undefined) { - field.updateValue(renderer.call(field, me.getRecordValue(field.textField))); - } else if(field.valueField !== undefined) { - var used = me.getRecordValue(field.valueField); - /*jslint confusion: true*/ - /* string and int */ - var max = field.maxField !== undefined ? me.getRecordValue(field.maxField) : 1; - - var calculate = me.fieldCalculator; - - if (Ext.isFunction(field.calculate)) { - calculate = field.calculate; - } - field.updateValue(renderer.call(field, used,max), calculate(used,max)); - } - }, - - getStore: function() { - var me = this; - if (!me.rstore) { - throw "there is no rstore"; - } - - return me.rstore; - }, - - updateTitle: function() { - var me = this; - me.setTitle(me.getRecordValue('name')); - }, - - updateValues: function(store, records, success) { - if (!success) { - return; // do not update if store load was not successful - } - var me = this; - var itemsToUpdate = me.query('pveInfoWidget'); - - itemsToUpdate.forEach(me.updateField, me); - - me.updateTitle(store); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - if (!me.title) { - throw "no title given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.callParent(); - - me.mon(me.rstore, 'load', 'updateValues'); - } - -}); -Ext.define('PVE.panel.GuestStatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveGuestStatusView', - mixins: ['Proxmox.Mixin.CBind'], - - height: 300, - - cbindData: function (initialConfig) { - var me = this; - return { - isQemu: me.pveSelNode.data.type === 'qemu', - isLxc: me.pveSelNode.data.type === 'lxc' - }; - }, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'status', - title: gettext('Status'), - iconCls: 'fa fa-info fa-fw', - printBar: false, - multiField: true, - renderer: function(record) { - var me = this; - var text = record.data.status; - var qmpstatus = record.data.qmpstatus; - if (qmpstatus && qmpstatus !== record.data.status) { - text += ' (' + qmpstatus + ')'; - } - return text; - } - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - xtype: 'pveInfoWidget', - itemId: 'node', - iconCls: 'fa fa-building fa-fw', - title: gettext('Node'), - cbind: { - text: '{pveSelNode.data.node}' - }, - printBar: false - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpus', - renderer: PVE.Utils.render_cpu_usage, - // in this specific api call - // we already have the correct value for the usage - calculate: Ext.identityFn - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory usage'), - valueField: 'mem', - maxField: 'maxmem' - }, - { - itemId: 'swap', - xtype: 'pveInfoWidget', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'maxswap', - cbind: { - hidden: '{isQemu}', - disabled: '{isQemu}' - } - }, - { - itemId: 'rootfs', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - valueField: 'disk', - maxField: 'maxdisk', - printBar: false, - renderer: function(used, max) { - var me = this; - me.setPrintBar(used > 0); - if (used === 0) { - return PVE.Utils.render_size(max); - } else { - return PVE.Utils.render_size_usage(used,max); - } - } - }, - { - xtype: 'box', - height: 15 - }, - { - itemId: 'ips', - xtype: 'pveAgentIPView', - cbind: { - rstore: '{rstore}', - pveSelNode: '{pveSelNode}', - hidden: '{isLxc}', - disabled: '{isLxc}' - } - } - ], - - updateTitle: function() { - var me = this; - var uptime = me.getRecordValue('uptime'); - - var text = ""; - if (Number(uptime) > 0) { - text = " (" + gettext('Uptime') + ': ' + Proxmox.Utils.format_duration_long(uptime) - + ')'; - } - - me.setTitle(me.getRecordValue('name') + text); - } -}); -/* - * This is a running chart widget - * you add time datapoints to it, - * and we only show the last x of it - * used for ceph performance charts - */ -Ext.define('PVE.widget.RunningChart', { - extend: 'Ext.container.Container', - alias: 'widget.pveRunningChart', - - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - width: 80, - xtype: 'box', - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}:

' - }, - { - flex: 1, - xtype: 'cartesian', - height: '100%', - itemId: 'chart', - border: false, - axes: [ - { - type: 'numeric', - position: 'left', - hidden: true, - minimum: 0 - }, - { - type: 'numeric', - position: 'bottom', - hidden: true - } - ], - - store: { - data: {} - }, - - sprites: [{ - id: 'valueSprite', - type: 'text', - text: '0 B/s', - textAlign: 'end', - textBaseline: 'middle', - fontSize: 14 - }], - - series: [{ - type: 'line', - xField: 'time', - yField: 'val', - fill: 'true', - colors: ['#cfcfcf'], - tooltip: { - trackMouse: true, - renderer: function( tooltip, record, ctx) { - var me = this.getChart(); - var date = new Date(record.data.time); - var value = me.up().renderer(record.data.val); - tooltip.setHtml( - me.up().title + ': ' + value + '
' + - Ext.Date.format(date, 'H:i:s') - ); - } - }, - style: { - lineWidth: 1.5, - opacity: 0.60 - }, - marker: { - opacity: 0, - scaling: 0.01, - fx: { - duration: 200, - easing: 'easeOut' - } - }, - highlightCfg: { - opacity: 1, - scaling: 1.5 - } - }] - } - ], - - // the renderer for the tooltip and last value, - // default just the value - renderer: Ext.identityFn, - - // show the last x seconds - // default is 5 minutes - timeFrame: 5*60, - - addDataPoint: function(value, time) { - var me = this.chart; - var panel = me.up(); - var now = new Date(); - var begin = new Date(now.getTime() - (1000*panel.timeFrame)); - - me.store.add({ - time: time || now.getTime(), - val: value || 0 - }); - - // delete all old records when we have 20 times more datapoints - // than seconds in our timeframe (so even a subsecond graph does - // not trigger this often) - // - // records in the store do not take much space, but like this, - // we prevent a memory leak when someone has the site open for a long time - // with minimal graphical glitches - if (me.store.count() > panel.timeFrame * 20) { - var oldData = me.store.getData().createFiltered(function(item) { - return item.data.time < begin.getTime(); - }); - - me.store.remove(oldData.getRange()); - } - - me.timeaxis.setMinimum(begin.getTime()); - me.timeaxis.setMaximum(now.getTime()); - me.valuesprite.setText(panel.renderer(value || 0).toString()); - me.valuesprite.setAttributes({ - x: me.getWidth() - 15, - y: me.getHeight()/2 - }, true); - me.redraw(); - }, - - setTitle: function(title) { - this.title = title; - var me = this.getComponent('title'); - me.update({title: title}); - }, - - initComponent: function(){ - var me = this; - me.callParent(); - - if (me.title) { - me.getComponent('title').update({title: me.title}); - } - me.chart = me.getComponent('chart'); - me.chart.timeaxis = me.chart.getAxes()[1]; - me.chart.valuesprite = me.chart.getSurface('chart').get('valueSprite'); - if (me.color) { - me.chart.series[0].setStyle({ - fill: me.color, - stroke: me.color - }); - } - } -}); -Ext.define('PVE.widget.Info',{ - extend: 'Ext.container.Container', - alias: 'widget.pveInfoWidget', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - value: 0, - maximum: 1, - printBar: true, - items: [ - { - xtype: 'component', - itemId: 'label', - data: { - title: '', - usage: '', - iconCls: undefined - }, - tpl: [ - '
', - '', - ' ', - '', - '{title}
 
{usage}
' - ] - }, - { - height: 2, - border: 0 - }, - { - xtype: 'progressbar', - itemId: 'progress', - height: 5, - value: 0, - animate: true - } - ], - - warningThreshold: 0.6, - criticalThreshold: 0.9, - - setPrintBar: function(enable) { - var me = this; - me.printBar = enable; - me.getComponent('progress').setVisible(enable); - }, - - setIconCls: function(iconCls) { - var me = this; - me.getComponent('label').data.iconCls = iconCls; - }, - - updateValue: function(text, usage) { - var me = this; - var label = me.getComponent('label'); - label.update(Ext.apply(label.data, {title: me.title, usage:text})); - - if (usage !== undefined && - me.printBar && - Ext.isNumeric(usage) && - usage >= 0) { - var progressBar = me.getComponent('progress'); - progressBar.updateProgress(usage, ''); - if (usage > me.criticalThreshold) { - progressBar.removeCls('warning'); - progressBar.addCls('critical'); - } else if (usage > me.warningThreshold) { - progressBar.removeCls('critical'); - progressBar.addCls('warning'); - } else { - progressBar.removeCls('warning'); - progressBar.removeCls('critical'); - } - } - }, - - initComponent: function() { - var me = this; - - if (!me.title) { - throw "no title defined"; - } - - me.callParent(); - - me.getComponent('progress').setVisible(me.printBar); - - me.updateValue(me.text, me.value); - me.setIconCls(me.iconCls); - } - -}); -Ext.define('PVE.panel.TemplateStatusView',{ - extend: 'PVE.panel.StatusView', - alias: 'widget.pveTemplateStatusView', - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - printBar: false, - padding: '2 25' - }, - items: [ - { - xtype: 'box', - height: 20 - }, - { - itemId: 'hamanaged', - iconCls: 'fa fa-heartbeat fa-fw', - title: gettext('HA State'), - printBar: false, - textField: 'ha', - renderer: PVE.Utils.format_ha - }, - { - itemId: 'node', - iconCls: 'fa fa-fw fa-building', - title: gettext('Node') - }, - { - xtype: 'box', - height: 20 - }, - { - itemId: 'cpus', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('Processors'), - textField: 'cpus' - }, - { - itemId: 'memory', - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - title: gettext('Memory'), - textField: 'maxmem', - renderer: PVE.Utils.render_size - }, - { - itemId: 'swap', - iconCls: 'fa fa-refresh fa-fw', - title: gettext('Swap'), - textField: 'maxswap', - renderer: PVE.Utils.render_size - }, - { - itemId: 'disk', - iconCls: 'fa fa-hdd-o fa-fw', - title: gettext('Bootdisk size'), - textField: 'maxdisk', - renderer: PVE.Utils.render_size - }, - { - xtype: 'box', - height: 20 - } - ], - - initComponent: function() { - var me = this; - - var name = me.pveSelNode.data.name; - if (!name) { - throw "no name specified"; - } - - me.title = name; - - me.callParent(); - if (me.pveSelNode.data.type !== 'lxc') { - me.remove(me.getComponent('swap')); - } - me.getComponent('node').updateValue(me.pveSelNode.data.node); - } -}); -Ext.define('PVE.widget.HealthWidget', { - extend: 'Ext.Component', - alias: 'widget.pveHealthWidget', - - data: { - iconCls: PVE.Utils.get_health_icon(undefined, true), - text: '', - title: '' - }, - - style: { - 'text-align':'center' - }, - - tpl: [ - '

{title}

', - '', - '

', - '{text}' - ], - - updateHealth: function(data) { - var me = this; - me.update(Ext.apply(me.data, data)); - }, - - initComponent: function(){ - var me = this; - - if (me.title) { - me.config.data.title = me.title; - } - - me.callParent(); - } - -}); -Ext.define('PVE.qemu.Summary', { - extend: 'Ext.panel.Panel', - xtype: 'pveGuestSummary', - - scrollable: true, - bodyPadding: 5, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - if (!me.workspace) { - throw "no workspace specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var type = me.pveSelNode.data.type; - var template = !!me.pveSelNode.data.template; - var rstore = me.statusStore; - - var items = [ - { - xtype: template ? 'pveTemplateStatusView' : 'pveGuestStatusView', - flex: 1, - padding: template ? '5' : '0 5 0 0', - itemId: 'gueststatus', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'pveNotesView', - flex: 1, - padding: template ? '5' : '0 0 0 5', - itemId: 'notesview', - pveSelNode: me.pveSelNode, - }, - ]; - - var rrdstore; - if (!template) { - - // in non-template mode put the two panels always together - items = [ - { - xtype: 'container', - layout: { - type: 'hbox', - align: 'stretch', - }, - items: items - } - ]; - - rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: `/api2/json/nodes/${nodename}/${type}/${vmid}/rrddata`, - model: 'pve-rrd-guest' - }); - - items.push( - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - pveSelNode: me.pveSelNode, - fields: ['cpu'], - fieldTitles: [gettext('CPU usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - pveSelNode: me.pveSelNode, - fields: ['maxmem', 'mem'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - pveSelNode: me.pveSelNode, - fields: ['netin','netout'], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Disk IO'), - pveSelNode: me.pveSelNode, - fields: ['diskread','diskwrite'], - store: rrdstore - } - ); - - } - - Ext.apply(me, { - tbar: [ '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - itemId: 'itemcontainer', - layout: { - type: 'column' - }, - minWidth: 700, - defaults: { - minHeight: 330, - padding: 5, - }, - items: items, - listeners: { - resize: function(container) { - PVE.Utils.updateColumns(container); - } - } - } - ] - }); - - me.callParent(); - if (!template) { - rrdstore.startUpdate(); - me.on('destroy', rrdstore.stopUpdate); - } - let sp = Ext.state.Manager.getProvider(); - me.mon(sp, 'statechange', function(provider, key, value) { - if (key !== 'summarycolumns') { - return; - } - PVE.Utils.updateColumns(me.getComponent('itemcontainer')); - }); - } -}); -/*global u2f*/ -Ext.define('PVE.window.LoginWindow', { - extend: 'Ext.window.Window', - - controller: { - - xclass: 'Ext.app.ViewController', - - onLogon: function() { - var me = this; - - var form = this.lookupReference('loginForm'); - var unField = this.lookupReference('usernameField'); - var saveunField = this.lookupReference('saveunField'); - var view = this.getView(); - - if (!form.isValid()) { - return; - } - - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - - // set or clear username - var sp = Ext.state.Manager.getProvider(); - if (saveunField.getValue() === true) { - sp.set(unField.getStateId(), unField.getValue()); - } else { - sp.clear(unField.getStateId()); - } - sp.set(saveunField.getStateId(), saveunField.getValue()); - - form.submit({ - failure: function(f, resp){ - me.failure(resp); - }, - success: function(f, resp){ - view.el.unmask(); - - var data = resp.result.data; - if (Ext.isDefined(data.NeedTFA)) { - // Store first factor login information first: - data.LoggedOut = true; - Proxmox.Utils.setAuthData(data); - - if (Ext.isDefined(data.U2FChallenge)) { - me.perform_u2f(data); - } else { - me.perform_otp(); - } - } else { - me.success(data); - } - } - }); - - }, - failure: function(resp) { - var me = this; - var view = me.getView(); - view.el.unmask(); - var handler = function() { - var uf = me.lookupReference('usernameField'); - uf.focus(true, true); - }; - - let emsg = gettext("Login failed. Please try again"); - - if (resp.failureType === "connect") { - emsg = gettext("Connection failure. Network error or Proxmox VE services not running?"); - } - - Ext.MessageBox.alert(gettext('Error'), emsg, handler); - }, - success: function(data) { - var me = this; - var view = me.getView(); - var handler = view.handler || Ext.emptyFn; - handler.call(me, data); - view.close(); - }, - - perform_otp: function() { - var me = this; - var win = Ext.create('PVE.window.TFALoginWindow', { - onLogin: function(value) { - me.finish_tfa(value); - }, - onCancel: function() { - Proxmox.LoggedOut = false; - Proxmox.Utils.authClear(); - me.getView().show(); - } - }); - win.show(); - }, - - perform_u2f: function(data) { - var me = this; - // Show the message: - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Verification'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - var chlg = data.U2FChallenge; - var key = { - version: chlg.version, - keyHandle: chlg.keyHandle - }; - u2f.sign(chlg.appId, chlg.challenge, [key], function(res) { - msg.close(); - if (res.errorCode) { - Proxmox.Utils.authClear(); - Ext.Msg.alert(gettext('Error'), PVE.Utils.render_u2f_error(res.errorCode)); - return; - } - delete res.errorCode; - me.finish_tfa(JSON.stringify(res)); - }); - }, - finish_tfa: function(res) { - var me = this; - var view = me.getView(); - view.el.mask(gettext('Please wait...'), 'x-mask-loading'); - var params = { response: res }; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'POST', - timeout: 5000, // it'll delay both success & failure - success: function(resp, opts) { - view.el.unmask(); - // Fill in what we copy over from the 1st factor: - var data = resp.result.data; - data.CSRFPreventionToken = Proxmox.CSRFPreventionToken; - data.username = Proxmox.UserName; - // Finish logging in: - me.success(data); - }, - failure: function(resp, opts) { - Proxmox.Utils.authClear(); - me.failure(resp); - } - }); - }, - - control: { - 'field[name=username]': { - specialkey: function(f, e) { - if (e.getKey() === e.ENTER) { - var pf = this.lookupReference('passwordField'); - if (!pf.getValue()) { - pf.focus(false); - } - } - } - }, - 'field[name=lang]': { - change: function(f, value) { - var dt = Ext.Date.add(new Date(), Ext.Date.YEAR, 10); - Ext.util.Cookies.set('PVELangCookie', value, dt); - this.getView().mask(gettext('Please wait...'), 'x-mask-loading'); - window.location.reload(); - } - }, - 'button[reference=loginButton]': { - click: 'onLogon' - }, - '#': { - show: function() { - var sp = Ext.state.Manager.getProvider(); - var checkboxField = this.lookupReference('saveunField'); - var unField = this.lookupReference('usernameField'); - - var checked = sp.get(checkboxField.getStateId()); - checkboxField.setValue(checked); - - if(checked === true) { - var username = sp.get(unField.getStateId()); - unField.setValue(username); - var pwField = this.lookupReference('passwordField'); - pwField.focus(); - } - } - } - } - }, - - width: 400, - modal: true, - border: false, - draggable: true, - closable: false, - resizable: false, - layout: 'auto', - - title: gettext('Proxmox VE Login'), - - defaultFocus: 'usernameField', - defaultButton: 'loginButton', - - items: [{ - xtype: 'form', - layout: 'form', - url: '/api2/extjs/access/ticket', - reference: 'loginForm', - - fieldDefaults: { - labelAlign: 'right', - allowBlank: false - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('User name'), - name: 'username', - itemId: 'usernameField', - reference: 'usernameField', - stateId: 'login-username' - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - name: 'password', - reference: 'passwordField' - }, - { - xtype: 'pveRealmComboBox', - name: 'realm' - }, - { - xtype: 'proxmoxLanguageSelector', - fieldLabel: gettext('Language'), - value: Ext.util.Cookies.get('PVELangCookie') || Proxmox.defaultLang || 'en', - name: 'lang', - reference: 'langField', - submitValue: false - } - ], - buttons: [ - { - xtype: 'checkbox', - fieldLabel: gettext('Save User name'), - name: 'saveusername', - reference: 'saveunField', - stateId: 'login-saveusername', - labelWidth: 250, - labelAlign: 'right', - submitValue: false - }, - { - text: gettext('Login'), - reference: 'loginButton' - } - ] - }] - }); -Ext.define('PVE.window.TFALoginWindow', { - extend: 'Ext.window.Window', - - modal: true, - resizable: false, - title: 'Two-Factor Authentication', - layout: 'form', - defaultButton: 'loginButton', - defaultFocus: 'otpField', - - controller: { - xclass: 'Ext.app.ViewController', - login: function() { - var me = this; - var view = me.getView(); - view.onLogin(me.lookup('otpField').getValue()); - view.close(); - }, - cancel: function() { - var me = this; - var view = me.getView(); - view.onCancel(); - view.close(); - } - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Please enter your OTP verification code:'), - name: 'otp', - itemId: 'otpField', - reference: 'otpField', - allowBlank: false - } - ], - - buttons: [ - { - text: gettext('Login'), - reference: 'loginButton', - handler: 'login' - }, - { - text: gettext('Cancel'), - handler: 'cancel' - } - ] -}); -Ext.define('PVE.window.Wizard', { - extend: 'Ext.window.Window', - - activeTitle: '', // used for automated testing - - width: 700, - height: 510, - - modal: true, - border: false, - - draggable: true, - closable: true, - resizable: false, - - layout: 'border', - - getValues: function(dirtyOnly) { - var me = this; - - var values = {}; - - var form = me.down('form').getForm(); - - form.getFields().each(function(field) { - if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) { - Proxmox.Utils.assemble_field_data(values, field.getSubmitData()); - } - }); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly)); - }); - - return values; - }, - - initComponent: function() { - var me = this; - - var tabs = me.items || []; - delete me.items; - - /* - * Items may have the following functions: - * validator(): per tab custom validation - * onSubmit(): submit handler - * onGetValues(): overwrite getValues results - */ - - Ext.Array.each(tabs, function(tab) { - tab.disabled = true; - }); - tabs[0].disabled = false; - - var maxidx = 0; - var curidx = 0; - - var check_card = function(card) { - var valid = true; - var fields = card.query('field, fieldcontainer'); - if (card.isXType('fieldcontainer')) { - fields.unshift(card); - } - Ext.Array.each(fields, function(field) { - // Note: not all fielcontainer have isValid() - if (Ext.isFunction(field.isValid) && !field.isValid()) { - valid = false; - } - }); - - if (Ext.isFunction(card.validator)) { - return card.validator(); - } - - return valid; - }; - - var disable_at = function(card) { - var tp = me.down('#wizcontent'); - var idx = tp.items.indexOf(card); - for(;idx < tp.items.getCount();idx++) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }; - - var tabchange = function(tp, newcard, oldcard) { - if (newcard.onSubmit) { - me.down('#next').setVisible(false); - me.down('#submit').setVisible(true); - } else { - me.down('#next').setVisible(true); - me.down('#submit').setVisible(false); - } - var valid = check_card(newcard); - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - me.down('#back').setDisabled(tp.items.indexOf(newcard) == 0); - - var idx = tp.items.indexOf(newcard); - if (idx > maxidx) { - maxidx = idx; - } - curidx = idx; - - var next = idx + 1; - var ntab = tp.items.getAt(next); - if (valid && ntab && !newcard.onSubmit) { - ntab.enable(); - } - }; - - if (me.subject && !me.title) { - me.title = Proxmox.Utils.dialog_title(me.subject, true, false); - } - - var sp = Ext.state.Manager.getProvider(); - var advchecked = sp.get('proxmox-advanced-cb'); - - Ext.apply(me, { - items: [ - { - xtype: 'form', - region: 'center', - layout: 'fit', - border: false, - margins: '5 5 0 5', - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [{ - itemId: 'wizcontent', - xtype: 'tabpanel', - activeItem: 0, - bodyPadding: 10, - listeners: { - afterrender: function(tp) { - var atab = this.getActiveTab(); - tabchange(tp, atab); - }, - tabchange: function(tp, newcard, oldcard) { - tabchange(tp, newcard, oldcard); - } - }, - items: tabs - }] - } - ], - fbar: [ - { - xtype: 'proxmoxHelpButton', - itemId: 'help' - }, - '->', - { - xtype: 'proxmoxcheckbox', - boxLabelAlign: 'before', - boxLabel: gettext('Advanced'), - value: advchecked, - listeners: { - change: function(cb, val) { - var tp = me.down('#wizcontent'); - tp.query('inputpanel').forEach(function(ip) { - ip.setAdvancedVisible(val); - }); - - sp.set('proxmox-advanced-cb', val); - } - } - }, - { - text: gettext('Back'), - disabled: true, - itemId: 'back', - minWidth: 60, - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - var prev = tp.items.indexOf(atab) - 1; - if (prev < 0) { - return; - } - var ntab = tp.items.getAt(prev); - if (ntab) { - tp.setActiveTab(ntab); - } - } - }, - { - text: gettext('Next'), - disabled: true, - itemId: 'next', - minWidth: 60, - handler: function() { - - var form = me.down('form').getForm(); - - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - if (!check_card(atab)) { - return; - } - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - - } - }, - { - text: gettext('Finish'), - minWidth: 60, - hidden: true, - itemId: 'submit', - handler: function() { - var tp = me.down('#wizcontent'); - var atab = tp.getActiveTab(); - atab.onSubmit(); - } - } - ] - }); - me.callParent(); - - Ext.Array.each(me.query('inputpanel'), function(panel) { - panel.setAdvancedVisible(advchecked); - }); - - Ext.Array.each(me.query('field'), function(field) { - var validcheck = function() { - var tp = me.down('#wizcontent'); - - // check tabs from current to the last enabled for validity - // since we might have changed a validity on a later one - var i; - for (i = curidx; i <= maxidx && i < tp.items.getCount(); i++) { - var tab = tp.items.getAt(i); - var valid = check_card(tab); - - // only set the buttons on the current panel - if (i === curidx) { - me.down('#next').setDisabled(!valid); - me.down('#submit').setDisabled(!valid); - } - - // if a panel is invalid, then disable it and all following, - // else enable it and go to the next - var ntab = tp.items.getAt(i + 1); - if (!valid) { - disable_at(ntab); - return; - } else if (ntab && !tab.onSubmit) { - ntab.enable(); - } - } - }; - field.on('change', validcheck); - field.on('validitychange', validcheck); - }); - } -}); -Ext.define('PVE.window.NotesEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - title: gettext('Notes'), - width: 600, - height: '400px', - resizable: true, - layout: 'fit', - defaultButton: undefined, - items: { - xtype: 'textarea', - name: 'description', - height: '100%', - value: '', - hideLabel: true - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.window.Backup', { - extend: 'Ext.window.Window', - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.vmtype) { - throw "no VM type specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: me.storage, - fieldLabel: gettext('Storage'), - storageContent: 'backup', - allowBlank: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - storagesel, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'pveCompressionSelector', - name: 'compress', - value: 'lzo', - fieldLabel: gettext('Compression') - }, - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto', - emptyText: Proxmox.Utils.noneText - } - ] - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Backup'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - var params = { - storage: storage, - vmid: me.vmid, - mode: values.mode, - remove: 0 - }; - - if ( values.mailto ) { - params.mailto = values.mailto; - } - - if (values.compress) { - params.compress = values.compress; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/vzdump', - params: params, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert('Error',response.htmlStatus); - }, - success: function(response, options) { - // close later so we reload the grid - // after the task has completed - me.hide(); - - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - close: function() { - me.close(); - } - } - }); - win.show(); - } - }); - } - }); - - var helpBtn = Ext.create('Proxmox.button.Help', { - onlineHelp: 'chapter_vzdump', - listenToGlobalEvent: false, - hidden: false - }); - - var title = gettext('Backup') + " " + - ((me.vmtype === 'lxc') ? "CT" : "VM") + - " " + me.vmid; - - Ext.apply(me, { - title: title, - width: 350, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ helpBtn, '->', submitBtn ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.window.Restore', { - extend: 'Ext.window.Window', // fixme: Proxmox.window.Edit? - - resizable: false, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.volid) { - throw "no volume ID specified"; - } - - if (!me.vmtype) { - throw "no vmtype specified"; - } - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: me.nodename, - name: 'storage', - value: '', - fieldLabel: gettext('Storage'), - storageContent: (me.vmtype === 'lxc') ? 'rootdir' : 'images', - allowBlank: true - }); - - var IDfield; - if (me.vmid) { - IDfield = Ext.create('Ext.form.field.Display', { - name: 'vmid', - value: me.vmid, - fieldLabel: (me.vmtype === 'lxc') ? 'CT' : 'VM' - }); - } else { - IDfield = Ext.create('PVE.form.GuestIDSelector', { - name: 'vmid', - guestType: me.vmtype, - loadNextFreeID: true, - validateExists: false - }); - } - - var items = [ - { - xtype: 'displayfield', - value: me.volidText || me.volid, - fieldLabel: gettext('Source') - }, - storagesel, - IDfield, - { - xtype: 'pveBandwidthField', - name: 'bwlimit', - backendUnit: 'KiB', - fieldLabel: gettext('Read Limit'), - emptyText: gettext('Defaults to target storage restore limit'), - autoEl: { - tag: 'div', - 'data-qtip': gettext("Use '0' to disable all bandwidth limits.") - } - }, - { - xtype: 'fieldcontainer', - layout: 'hbox', - items: [{ - xtype: 'proxmoxcheckbox', - name: 'unique', - fieldLabel: gettext('Unique'), - hidden: !!me.vmid, - flex: 1, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses') - }, - checked: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'start', - flex: 1, - fieldLabel: gettext('Start after restore'), - labelWidth: 105, - checked: false - }], - }, - ]; - - /*jslint confusion: true*/ - if (me.vmtype === 'lxc') { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - fieldLabel: gettext('Unprivileged container') - }); - } - /*jslint confusion: false*/ - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var doRestore = function(url, params) { - Proxmox.Utils.API2Request({ - url: url, - params: params, - method: 'POST', - waitMsgTarget: me, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.close(); - } - }); - }; - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Restore'), - handler: function(){ - var storage = storagesel.getValue(); - var values = form.getValues(); - - var params = { - storage: storage, - vmid: me.vmid || values.vmid, - force: me.vmid ? 1 : 0 - }; - if (values.unique) { params.unique = 1; } - if (values.start) { params.start = 1; } - - if (values.bwlimit !== undefined) { - params.bwlimit = values.bwlimit; - } - - var url; - var msg; - if (me.vmtype === 'lxc') { - url = '/nodes/' + me.nodename + '/lxc'; - params.ostemplate = me.volid; - params.restore = 1; - if (values.unprivileged) { params.unprivileged = 1; } - msg = Proxmox.Utils.format_task_description('vzrestore', params.vmid); - } else if (me.vmtype === 'qemu') { - url = '/nodes/' + me.nodename + '/qemu'; - params.archive = me.volid; - msg = Proxmox.Utils.format_task_description('qmrestore', params.vmid); - } else { - throw 'unknown VM type'; - } - - if (me.vmid) { - msg += '. ' + gettext('This will permanently erase current VM data.'); - Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { - if (btn !== 'yes') { - return; - } - doRestore(url, params); - }); - } else { - doRestore(url, params); - } - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - var title = gettext('Restore') + ": " + ( - (me.vmtype === 'lxc') ? 'CT' : 'VM'); - - if (me.vmid) { - title += " " + me.vmid; - } - - Ext.apply(me, { - title: title, - width: 500, - modal: true, - layout: 'auto', - border: false, - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); -/* Popup a message window - * where the user has to manually enter the resource ID - * to enable the destroy button - */ -Ext.define('PVE.window.SafeDestroy', { - extend: 'Ext.window.Window', - alias: 'widget.pveSafeDestroy', - - title: gettext('Confirm'), - modal: true, - buttonAlign: 'center', - bodyPadding: 10, - width: 450, - layout: { type:'hbox' }, - defaultFocus: 'confirmField', - showProgress: false, - - config: { - item: { - id: undefined, - type: undefined - }, - url: undefined, - params: {} - }, - - getParams: function() { - var me = this; - var purgeCheckbox = me.lookupReference('purgeCheckbox'); - if (purgeCheckbox.checked) { - me.params.purge = 1; - } - if (Ext.Object.isEmpty(me.params)) { - return ''; - } - return '?' + Ext.Object.toQueryString(me.params); - }, - - controller: { - - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=confirm]': { - change: function(f, value) { - var view = this.getView(); - var removeButton = this.lookupReference('removeButton'); - if (value === view.getItem().id.toString()) { - removeButton.enable(); - } else { - removeButton.disable(); - } - }, - specialkey: function (field, event) { - var removeButton = this.lookupReference('removeButton'); - if (!removeButton.isDisabled() && event.getKey() == event.ENTER) { - removeButton.fireEvent('click', removeButton, event); - } - } - }, - 'button[reference=removeButton]': { - click: function() { - var view = this.getView(); - Proxmox.Utils.API2Request({ - url: view.getUrl() + view.getParams(), - method: 'DELETE', - waitMsgTarget: view, - failure: function(response, opts) { - view.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var hasProgressBar = view.showProgress && - response.result.data ? true : false; - - if (hasProgressBar) { - // stay around so we can trigger our close events - // when background action is completed - view.hide(); - - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: function () { - view.close(); - } - } - }); - win.show(); - } else { - view.close(); - } - } - }); - } - } - } - }, - - items: [ - { - xtype: 'component', - cls: [ Ext.baseCSSPrefix + 'message-box-icon', - Ext.baseCSSPrefix + 'message-box-warning', - Ext.baseCSSPrefix + 'dlg-icon'] - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'component', - reference: 'messageCmp' - }, - { - itemId: 'confirmField', - reference: 'confirmField', - xtype: 'textfield', - name: 'confirm', - labelWidth: 300, - hideTrigger: true, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'purge', - reference: 'purgeCheckbox', - boxLabel: gettext('Purge'), - checked: false, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Remove from replication and backup jobs') - } - } - ] - } - ], - buttons: [ - { - reference: 'removeButton', - text: gettext('Remove'), - disabled: true - } - ], - - initComponent : function() { - var me = this; - - me.callParent(); - - var item = me.getItem(); - - if (!Ext.isDefined(item.id)) { - throw "no ID specified"; - } - - if (!Ext.isDefined(item.type)) { - throw "no VM type specified"; - } - - var messageCmp = me.lookupReference('messageCmp'); - var msg; - - if (item.type === 'VM') { - msg = Proxmox.Utils.format_task_description('qmdestroy', item.id); - } else if (item.type === 'CT') { - msg = Proxmox.Utils.format_task_description('vzdestroy', item.id); - } else if (item.type === 'CephPool') { - msg = Proxmox.Utils.format_task_description('cephdestroypool', item.id); - } else if (item.type === 'Image') { - msg = Proxmox.Utils.format_task_description('unknownimgdel', item.id); - } else { - throw "unknown item type specified"; - } - - messageCmp.setHtml(msg); - - if (!(item.type === 'VM' || item.type === 'CT')) { - let purgeCheckbox = me.lookupReference('purgeCheckbox'); - purgeCheckbox.setDisabled(true); - purgeCheckbox.setHidden(true); - } - - var confirmField = me.lookupReference('confirmField'); - msg = gettext('Please enter the ID to confirm') + - ' (' + item.id + ')'; - confirmField.setFieldLabel(msg); - } -}); -Ext.define('PVE.window.BackupConfig', { - extend: 'Ext.window.Window', - title: gettext('Configuration'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: { - xtype: 'component', - itemId: 'configtext', - autoScroll: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }, - - initComponent: function() { - var me = this; - - if (!me.volume) { - throw "no volume specified"; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.callParent(); - - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/vzdump/extractconfig", - method: 'GET', - params: { - volume: me.volume - }, - failure: function(response, opts) { - me.close(); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response,options) { - me.show(); - me.down('#configtext').update(Ext.htmlEncode(response.result.data)); - } - }); - } -}); -Ext.define('PVE.window.Settings', { - extend: 'Ext.window.Window', - - width: '800px', - title: gettext('My Settings'), - iconCls: 'fa fa-gear', - modal: true, - bodyPadding: 10, - resizable: false, - - buttons: [ - { - xtype: 'proxmoxHelpButton', - onlineHelp: 'gui_my_settings', - hidden: false - }, - '->', - { - text: gettext('Close'), - handler: function() { - this.up('window').close(); - } - } - ], - - layout: { - type: 'column', - align: 'top' - }, - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - var username = sp.get('login-username') || Proxmox.Utils.noneText; - me.lookupReference('savedUserName').setValue(username); - var vncMode = sp.get('novnc-scaling'); - if (vncMode !== undefined) { - me.lookupReference('noVNCScalingGroup').setValue({ noVNCScalingField: vncMode }); - } - - let summarycolumns = sp.get('summarycolumns', 'auto'); - me.lookup('summarycolumns').setValue(summarycolumns); - - me.lookup('guestNotesCollapse').setValue(sp.get('guest-notes-collapse', 'never')); - - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var val = localStorage.getItem('pve-xterm-' + setting); - if (val !== undefined && val !== null) { - var field = me.lookup(setting); - field.setValue(val); - field.resetOriginalValue(); - } - }); - }, - - set_button_status: function() { - var me = this; - - var form = me.lookup('xtermform'); - var valid = form.isValid(); - var dirty = form.isDirty(); - - var hasvalues = false; - var values = form.getValues(); - Ext.Object.eachValue(values, function(value) { - if (value) { - hasvalues = true; - return false; - } - }); - - me.lookup('xtermsave').setDisabled(!dirty || !valid); - me.lookup('xtermreset').setDisabled(!hasvalues); - }, - - control: { - '#xtermjs form': { - dirtychange: 'set_button_status', - validitychange: 'set_button_status' - }, - '#xtermjs button': { - click: function(button) { - var me = this; - var settings = ['fontSize', 'fontFamily', 'letterSpacing', 'lineHeight']; - settings.forEach(function(setting) { - var field = me.lookup(setting); - if (button.reference === 'xtermsave') { - var value = field.getValue(); - if (value) { - localStorage.setItem('pve-xterm-' + setting, value); - } else { - localStorage.removeItem('pve-xterm-' + setting); - } - } else if (button.reference === 'xtermreset') { - field.setValue(undefined); - localStorage.removeItem('pve-xterm-' + setting); - } - field.resetOriginalValue(); - }); - me.set_button_status(); - } - }, - 'button[name=reset]': { - click: function () { - var blacklist = ['GuiCap', 'login-username', 'dash-storages']; - var sp = Ext.state.Manager.getProvider(); - var state; - for (state in sp.state) { - if (sp.state.hasOwnProperty(state)) { - if (blacklist.indexOf(state) !== -1) { - continue; - } - - sp.clear(state); - } - } - - window.location.reload(); - } - }, - 'button[name=clear-username]': { - click: function () { - var me = this; - var usernamefield = me.lookupReference('savedUserName'); - var sp = Ext.state.Manager.getProvider(); - - usernamefield.setValue(Proxmox.Utils.noneText); - sp.clear('login-username'); - } - }, - 'grid[reference=dashboard-storages]': { - selectionchange: function(grid, selected) { - var me = this; - var sp = Ext.state.Manager.getProvider(); - - // saves the selected storageids as - // "id1,id2,id3,..." - // or clears the variable - if (selected.length > 0) { - sp.set('dash-storages', - Ext.Array.pluck(selected, 'id').join(',')); - } else { - sp.clear('dash-storages'); - } - }, - afterrender: function(grid) { - var me = grid; - var sp = Ext.state.Manager.getProvider(); - var store = me.getStore(); - var items = []; - me.suspendEvent('selectionchange'); - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - // we have to get the records - // to be able to select them - if (storage !== '') { - var item = store.getById(storage); - if (item) { - items.push(item); - } - } - }); - me.getSelectionModel().select(items); - me.resumeEvent('selectionchange'); - } - }, - 'field[reference=summarycolumns]': { - change: function(el, newValue) { - var sp = Ext.state.Manager.getProvider(); - sp.set('summarycolumns', newValue); - } - }, - 'field[reference=guestNotesCollapse]': { - change: function(e, v) { - Ext.state.Manager.getProvider().set('guest-notes-collapse', v); - }, - }, - } - }, - - items: [{ - xtype: 'fieldset', - columnWidth: 0.5, - title: gettext('Webinterface Settings'), - margin: '5', - layout: { - type: 'vbox', - align: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Dashboard Storages'), - labelAlign: 'left', - labelWidth: '50%' - }, - { - xtype: 'grid', - maxHeight: 150, - reference: 'dashboard-storages', - selModel: { - selType: 'checkboxmodel' - }, - columns: [{ - header: gettext('Name'), - dataIndex: 'storage', - flex: 1 - },{ - header: gettext('Node'), - dataIndex: 'node', - flex: 1 - }], - store: { - type: 'diff', - field: ['type', 'storage', 'id', 'node'], - rstore: PVE.data.ResourceStore, - filters: [{ - property: 'type', - value: 'storage' - }], - sorters: [ 'node','storage'] - } - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'container', - layout: 'hbox', - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Saved User Name') + ':', - labelWidth: '150', - stateId: 'login-username', - reference: 'savedUserName', - flex: 1, - value: '' - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Reset'), - name: 'clear-username', - }, - ] - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'container', - layout: 'hbox', - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Layout') + ':', - flex: 1, - }, - { - xtype: 'button', - cls: 'x-btn-default-toolbar-small proxmox-inline-button', - text: gettext('Reset'), - tooltip: gettext('Reset all layout changes (for example, column widths)'), - name: 'reset', - }, - ] - }, - { - xtype: 'box', - autoEl: { tag: 'hr'} - }, - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Summary columns') + ':', - labelWidth: 150, - stateId: 'summarycolumns', - reference: 'summarycolumns', - comboItems: [ - ['auto', 'auto'], - ['1', '1'], - ['2', '2'], - ['3', '3'], - ], - }, - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Guest Notes') + ':', - labelWidth: 150, - stateId: 'guest-notes-collapse', - reference: 'guestNotesCollapse', - comboItems: [ - ['never', 'Show by default'], - ['always', 'Collapse by default'], - ['auto', 'auto (Collapse if empty)'], - ], - }, - ] - }, - { - xtype: 'container', - layout: 'vbox', - columnWidth: 0.5, - margin: '5', - defaults: { - width: '100%', - // right margin ensures that the right border of the fieldsets - // is shown - margin: '0 2 10 0' - }, - items:[ - { - xtype: 'fieldset', - itemId: 'xtermjs', - title: gettext('xterm.js Settings'), - items: [{ - xtype: 'form', - reference: 'xtermform', - border: false, - layout: { - type: 'vbox', - algin: 'left' - }, - defaults: { - width: '100%', - margin: '0 0 10 0', - }, - items: [ - { - xtype: 'textfield', - name: 'fontFamily', - reference: 'fontFamily', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Font-Family') - }, - { - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - name: 'fontSize', - reference: 'fontSize', - minValue: 1, - fieldLabel: gettext('Font-Size') - }, - { - xtype: 'numberfield', - name: 'letterSpacing', - reference: 'letterSpacing', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Letter Spacing') - }, - { - xtype: 'numberfield', - name: 'lineHeight', - minValue: 0.1, - reference: 'lineHeight', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Line Height') - }, - { - xtype: 'container', - layout: { - type: 'hbox', - pack: 'end' - }, - defaults: { - margin: '0 0 0 5', - }, - items: [ - { - xtype: 'button', - reference: 'xtermreset', - disabled: true, - text: gettext('Reset') - }, - { - xtype: 'button', - reference: 'xtermsave', - disabled: true, - text: gettext('Save') - } - ] - } - ] - }] - },{ - xtype: 'fieldset', - title: gettext('noVNC Settings'), - items: [ - { - xtype: 'radiogroup', - fieldLabel: gettext('Scaling mode'), - reference: 'noVNCScalingGroup', - height: '15px', // renders faster with value assigned - layout: { - type: 'hbox', - }, - items: [ - { - xtype: 'radiofield', - name: 'noVNCScalingField', - inputValue: 'scale', - boxLabel: 'Local Scaling', - checked: true, - },{ - xtype: 'radiofield', - name: 'noVNCScalingField', - inputValue: 'off', - boxLabel: 'Off', - margin: '0 0 0 10', - } - ], - listeners: { - change: function(el, newValue, undefined) { - var sp = Ext.state.Manager.getProvider(); - sp.set('novnc-scaling', newValue.noVNCScalingField); - } - }, - }, - ] - }, - ] - }], -}); -Ext.define('PVE.panel.StartupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'qm_startup_and_shutdown', - - onGetValues: function(values) { - var me = this; - - var res = PVE.Parser.printStartup(values); - - if (res === undefined || res === '') { - return { 'delete': 'startup' }; - } - - return { startup: res }; - }, - - setStartup: function(value) { - var me = this; - - var startup = PVE.Parser.parseStartup(value); - if (startup) { - me.setValues(startup); - } - }, - - initComponent : function() { - var me = this; - - me.items = [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - fieldLabel: gettext('Shutdown timeout') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.window.StartupEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveWindowStartupEdit', - onlineHelp: undefined, - - initComponent : function() { - - var me = this; - var ipanelConfig = me.onlineHelp ? {onlineHelp: me.onlineHelp} : {}; - var ipanel = Ext.create('PVE.panel.StartupInputPanel', ipanelConfig); - - Ext.applyIf(me, { - subject: gettext('Start/Shutdown order'), - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - ipanel.setStartup(me.vmconfig.startup); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.Install', { - extend: 'Ext.window.Window', - xtype: 'pveCephInstallWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 220, - header: false, - resizable: false, - draggable: false, - modal: true, - nodename: undefined, - shadow: false, - border: false, - bodyBorder: false, - closable: false, - cls: 'install-mask', - bodyCls: 'install-mask', - layout: { - align: 'stretch', - pack: 'center', - type: 'vbox' - }, - viewModel: { - data: { - cephVersion: 'nautilus', - isInstalled: false - }, - formulas: { - buttonText: function (get){ - if (get('isInstalled')) { - return gettext('Configure Ceph'); - } else { - return gettext('Install Ceph-') + get('cephVersion'); - } - }, - windowText: function (get) { - if (get('isInstalled')) { - return '

' + - Ext.String.format(gettext('{0} is not initialized.'), 'Ceph') + ' '+ - gettext('You need to create a initial config once.') + '

'; - } else { - return '

' + - Ext.String.format(gettext('{0} is not installed on this node.'), 'Ceph') + '
' + - gettext('Would you like to install it now?') + '

'; - } - } - } - }, - items: [ - { - bind: { - html: '{windowText}' - }, - border: false, - padding: 5, - bodyCls: 'install-mask' - - }, - { - xtype: 'button', - bind: { - text: '{buttonText}' - }, - viewModel: {}, - cbind: { - nodename: '{nodename}' - }, - handler: function() { - var me = this.up('pveCephInstallWindow'); - var win = Ext.create('PVE.ceph.CephInstallWizard',{ - nodename: me.nodename - }); - win.getViewModel().set('isInstalled', this.getViewModel().get('isInstalled')); - win.show(); - me.mon(win,'beforeClose', function(){ - me.fireEvent("cephInstallWindowClosed"); - me.close(); - }); - - } - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallEnableEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveFirewallEnableEdit'], - mixins: ['Proxmox.Mixin.CBind'], - - subject: gettext('Firewall'), - cbindData: { - defaultValue: 0 - }, - width: 350, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - uncheckedValue: 0, - cbind: { - defaultValue: '{defaultValue}', - checked: '{defaultValue}' - }, - deleteDefaultValue: false, - fieldLabel: gettext('Firewall') - }, - { - xtype: 'displayfield', - name: 'warning', - userCls: 'pmx-hint', - value: gettext('Warning: Firewall still disabled at datacenter level!'), - hidden: true - } - ], - - beforeShow: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/cluster/firewall/options', - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - if (!response.result.data.enable) { - me.down('displayfield[name=warning]').setVisible(true); - } - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.FirewallLograteInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveFirewallLograteInputPanel', - - viewModel: {}, - - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - reference: 'enable', - fieldLabel: gettext('Enable'), - value: true - }, - { - layout: 'hbox', - border: false, - items: [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Log rate limit'), - minValue: 1, - maxValue: 99, - allowBlank: false, - flex: 2, - value: 1 - }, - { - xtype: 'box', - html: '
/
' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'unit', - comboItems: [['second', 'second'], ['minute', 'minute'], - ['hour', 'hour'], ['day', 'day']], - allowBlank: false, - flex: 1, - value: 'second' - } - ] - }, - { - xtype: 'numberfield', - name: 'burst', - fieldLabel: gettext('Log burst limit'), - minValue: 1, - maxValue: 99, - value: 5 - } - ], - - onGetValues: function(values) { - var me = this; - - var vals = {}; - vals.enable = values.enable !== undefined ? 1 : 0; - vals.rate = values.rate + '/' + values.unit; - vals.burst = values.burst; - var properties = PVE.Parser.printPropertyString(vals, undefined); - if (properties == '') { - return { 'delete': 'log_ratelimit' }; - } - return { log_ratelimit: properties }; - }, - - setValues: function(values) { - var me = this; - - var properties = {}; - if (values.log_ratelimit !== undefined) { - properties = PVE.Parser.parsePropertyString(values.log_ratelimit, 'enable'); - if (properties.rate) { - var matches = properties.rate.match(/^(\d+)\/(second|minute|hour|day)$/); - if (matches) { - properties.rate = matches[1]; - properties.unit = matches[2]; - } - } - } - me.callParent([properties]); - } -}); - -Ext.define('PVE.FirewallLograteEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveFirewallLograteEdit', - - subject: gettext('Log rate limit'), - - items: [{ - xtype: 'pveFirewallLograteInputPanel' - }], - autoLoad: true -}); -Ext.define('PVE.panel.NotesView', { - extend: 'Ext.panel.Panel', - xtype: 'pveNotesView', - - title: gettext("Notes"), - bodyStyle: 'white-space:pre', - bodyPadding: 10, - scrollable: true, - animCollapse: false, - - tbar: { - itemId: 'tbar', - hidden: true, - items: [ - { - text: gettext('Edit'), - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - } - ] - }, - - run_editor: function() { - var me = this; - var win = Ext.create('PVE.window.NotesEdit', { - pveSelNode: me.pveSelNode, - url: me.url - }); - win.show(); - win.on('destroy', me.load, me); - }, - - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - me.setCollapsed(false); - }, - success: function(response, opts) { - var data = response.result.data.description || ''; - me.update(Ext.htmlEncode(data)); - - if (me.collapsible && me.collapseMode === 'auto') { - me.setCollapsed(data === ''); - } - } - }); - }, - - listeners: { - render: function(c) { - var me = this; - me.getEl().on('dblclick', me.run_editor, me); - }, - afterlayout: function() { - let me = this; - if (me.collapsible && !me.getCollapsed() && me.collapseMode === 'always') { - me.setCollapsed(true); - me.collapseMode = ''; // only once, on initial load! - } - }, - }, - - tools: [{ - type: 'gear', - handler: function() { - var me = this.up('panel'); - me.run_editor(); - } - }], - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var type = me.pveSelNode.data.type; - if (!Ext.Array.contains(['node', 'qemu', 'lxc'], type)) { - throw 'invalid type specified'; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid && type !== 'node') { - throw "no VM ID specified"; - } - - me.url = '/api2/extjs/nodes/' + nodename + '/'; - - // add the type specific path if qemu/lxc - if (type === 'qemu' || type === 'lxc') { - me.url += type + '/' + vmid + '/'; - } - - me.url += 'config'; - - me.callParent(); - if (type === 'node') { - me.down('#tbar').setVisible(true); - } else { - me.setCollapsible(true); - me.collapseDirection = 'right'; - - let sp = Ext.state.Manager.getProvider(); - me.collapseMode = sp.get('guest-notes-collapse', 'never'); - - if (me.collapseMode === 'auto') { - me.setCollapsed(true); - } - } - me.load(); - } -}); -Ext.define('PVE.grid.ResourceGrid', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveResourceGrid'], - - border: false, - defaultSorter: { - property: 'type', - direction: 'ASC' - }, - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - var coldef = rstore.defaultColumns(); - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: me.defaultSorter, - proxy: { type: 'memory' } - }); - - var textfilter = ''; - - var textfilter_match = function(item) { - var match = false; - Ext.each(['name', 'storage', 'node', 'type', 'text'], function(field) { - var v = item.data[field]; - if (v !== undefined) { - v = v.toLowerCase(); - if (v.indexOf(textfilter) >= 0) { - match = true; - return false; - } - } - }); - return match; - }; - - var updateGrid = function() { - - var filterfn = me.viewFilter ? me.viewFilter.filterfn : null; - - //console.log("START GRID UPDATE " + me.viewFilter); - - store.suspendEvents(); - - var nodeidx = {}; - var gather_child_nodes = function(cn) { - if (!cn) { - return; - } - var cs = cn.childNodes; - if (!cs) { - return; - } - var len = cs.length, i = 0, n, res; - - for (; i < len; i++) { - var child = cs[i]; - var orgnode = rstore.data.get(child.data.id); - if (orgnode) { - if ((!filterfn || filterfn(child)) && - (!textfilter || textfilter_match(child))) { - nodeidx[child.data.id] = orgnode; - } - } - gather_child_nodes(child); - } - }; - gather_child_nodes(me.pveSelNode); - - // remove vanished items - var rmlist = []; - store.each(function(olditem) { - var item = nodeidx[olditem.data.id]; - if (!item) { - //console.log("GRID REM UID: " + olditem.data.id); - rmlist.push(olditem); - } - }); - - if (rmlist.length) { - store.remove(rmlist); - } - - // add new items - var addlist = []; - var key; - for (key in nodeidx) { - if (nodeidx.hasOwnProperty(key)) { - var item = nodeidx[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var olditem = store.getById(item.data.id); - var olditem = store.data.get(item.data.id); - - if (!olditem) { - //console.log("GRID ADD UID: " + item.data.id); - var info = Ext.apply({}, item.data); - var child = Ext.create(store.model, info); - addlist.push(item); - continue; - } - // try to detect changes - var changes = false; - var fieldkeys = PVE.data.ResourceStore.fieldNames; - var fieldcount = fieldkeys.length; - var fieldind; - for (fieldind = 0; fieldind < fieldcount; fieldind++) { - var field = fieldkeys[fieldind]; - if (field != 'id' && item.data[field] != olditem.data[field]) { - changes = true; - //console.log("changed item " + item.id + " " + field + " " + item.data[field] + " != " + olditem.data[field]); - olditem.beginEdit(); - olditem.set(field, item.data[field]); - } - } - if (changes) { - olditem.endEdit(true); - olditem.commit(true); - } - } - } - - if (addlist.length) { - store.add(addlist); - } - - store.sort(); - - store.resumeEvents(); - - store.fireEvent('refresh', store); - - //console.log("END GRID UPDATE"); - }; - - var filter_task = new Ext.util.DelayedTask(function(){ - updateGrid(); - }); - - var load_cb = function() { - updateGrid(); - }; - - Ext.apply(me, { - store: store, - stateful: true, - stateId: 'grid-resource', - tbar: [ - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - value: textfilter, - enableKeyEvents: true, - listeners: { - keyup: function(field, e) { - var v = field.getValue(); - textfilter = v.toLowerCase(); - filter_task.delay(500); - } - } - } - ], - viewConfig: { - stripeRows: true - }, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - destroy: function() { - rstore.un("load", load_cb); - } - }, - columns: coldef - }); - me.callParent(); - updateGrid(); - rstore.on("load", load_cb); - } -}); -Ext.define('PVE.pool.AddVM', { - extend: 'Proxmox.window.Edit', - width: 600, - height: 400, - isAdd: true, - isCreate: true, - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - var vmsField = Ext.create('Ext.form.field.Text', { - name: 'vms', - hidden: true, - allowBlank: false - }); - - var vmStore = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property: 'vmid', - order: 'ASC' - } - ], - filters: [ - function(item) { - return ((item.data.type === 'lxc' || item.data.type === 'qemu') && item.data.pool === ''); - } - ] - }); - - var vmGrid = Ext.create('widget.grid',{ - store: vmStore, - border: true, - height: 300, - scrollable: true, - selModel: { - selType: 'checkboxmodel', - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected, opts) { - var selectedVms = []; - selected.forEach(function(vm) { - selectedVms.push(vm.data.vmid); - }); - vmsField.setValue(selectedVms); - } - } - }, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - Ext.apply(me, { - subject: gettext('Virtual Machine'), - items: [ vmsField, vmGrid ] - }); - - me.callParent(); - vmStore.load(); - } -}); - -Ext.define('PVE.pool.AddStorage', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - me.isCreate = true; - me.isAdd = true; - me.url = "/pools/" + me.pool; - me.method = 'PUT'; - - Ext.apply(me, { - subject: gettext('Storage'), - width: 350, - items: [ - { - xtype: 'pveStorageSelector', - name: 'storage', - nodename: 'localhost', - autoSelect: false, - value: '', - fieldLabel: gettext("Storage") - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.grid.PoolMembers', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pvePoolMembers'], - - // fixme: dynamic status update ? - - stateful: true, - stateId: 'grid-pool-members', - - initComponent : function() { - var me = this; - - if (!me.pool) { - throw "no pool specified"; - } - - var store = Ext.create('Ext.data.Store', { - model: 'PVEResources', - sorters: [ - { - property : 'type', - direction: 'ASC' - } - ], - proxy: { - type: 'proxmox', - root: 'data.members', - url: "/api2/json/pools/" + me.pool - } - }); - - var coldef = PVE.data.ResourceStore.defaultColumns(); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - var params = { 'delete': 1 }; - if (rec.data.type === 'storage') { - params.storage = rec.data.storage; - } else if (rec.data.type === 'qemu' || rec.data.type === 'lxc' || rec.data.type === 'openvz') { - params.vms = rec.data.vmid; - } else { - throw "unknown resource type"; - } - - Proxmox.Utils.API2Request({ - url: '/pools/' + me.pool, - method: 'PUT', - params: params, - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Virtual Machine'), - iconCls: 'pve-itype-icon-qemu', - handler: function() { - var win = Ext.create('PVE.pool.AddVM', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('Storage'), - iconCls: 'pve-itype-icon-storage', - handler: function() { - var win = Ext.create('PVE.pool.AddStorage', { pool: me.pool }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - remove_btn - ], - viewConfig: { - stripeRows: true - }, - columns: coldef, - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - itemdblclick: function(v, record) { - var ws = me.up('pveStdWorkspace'); - ws.selectById(record.data.id); - }, - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.form.FWMacroSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: 'widget.pveFWMacroSelector', - allowBlank: true, - autoSelect: false, - valueField: 'macro', - displayField: 'macro', - listConfig: { - columns: [ - { - header: gettext('Macro'), - dataIndex: 'macro', - hideable: false, - width: 100 - }, - { - header: gettext('Description'), - renderer: Ext.String.htmlEncode, - flex: 1, - dataIndex: 'descr' - } - ] - }, - initComponent: function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: true, - fields: [ 'macro', 'descr' ], - idProperty: 'macro', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/firewall/macros" - }, - sorters: { - property: 'macro', - order: 'DESC' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRulePanel', { - extend: 'Proxmox.panel.InputPanel', - - allow_iface: false, - - list_refs_url: undefined, - - onGetValues: function(values) { - var me = this; - - // hack: editable ComboGrid returns nothing when empty, so we need to set '' - // Also, disabled text fields return nothing, so we need to set '' - - Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'log'], function(key) { - if (values[key] === undefined) { - values[key] = ''; - } - }); - - delete values.modified_marker; - - return values; - }, - - initComponent : function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.column1 = [ - { - // hack: we use this field to mark the form 'dirty' when the - // record has errors- so that the user can safe the unmodified - // form again. - xtype: 'hiddenfield', - name: 'modified_marker', - value: '' - }, - { - xtype: 'proxmoxKVComboBox', - name: 'type', - value: 'in', - comboItems: [['in', 'in'], ['out', 'out']], - fieldLabel: gettext('Direction'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - name: 'action', - value: 'ACCEPT', - comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']], - fieldLabel: gettext('Action'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - me.column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } else { - me.column1.push({ - xtype: 'displayfield', - fieldLabel: '', - value: '' - }); - } - - me.column1.push( - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'pveIPRefSelector', - name: 'source', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Source') - - }, - { - xtype: 'pveIPRefSelector', - name: 'dest', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('Destination') - } - ); - - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - }, - { - xtype: 'pveFWMacroSelector', - name: 'macro', - fieldLabel: gettext('Macro'), - editable: true, - allowBlank: true, - listeners: { - change: function(f, value) { - if (value === null) { - me.down('field[name=proto]').setDisabled(false); - me.down('field[name=sport]').setDisabled(false); - me.down('field[name=dport]').setDisabled(false); - } else { - me.down('field[name=proto]').setDisabled(true); - me.down('field[name=proto]').setValue(''); - me.down('field[name=sport]').setDisabled(true); - me.down('field[name=sport]').setValue(''); - me.down('field[name=dport]').setDisabled(true); - me.down('field[name=dport]').setValue(''); - } - } - } - }, - { - xtype: 'pveIPProtocolSelector', - name: 'proto', - autoSelect: false, - editable: true, - value: '', - fieldLabel: gettext('Protocol') - }, - { - xtype: 'displayfield', - fieldLabel: '', - height: 7, - value: '' - }, - { - xtype: 'textfield', - name: 'sport', - value: '', - fieldLabel: gettext('Source port') - }, - { - xtype: 'textfield', - name: 'dport', - value: '', - fieldLabel: gettext('Dest. port') - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'pveFirewallLogLevels' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.FirewallRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - list_refs_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "no base_url specified"; - } - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.FirewallRulePanel', { - isCreate: me.isCreate, - list_refs_url: me.list_refs_url, - allow_iface: me.allow_iface, - rule_pos: me.rule_pos - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - if (values.errors) { - var field = me.query('[isFormField][name=modified_marker]')[0]; - field.setValue(1); - Ext.Function.defer(function() { - var form = ipanel.up('form').getForm(); - form.markInvalid(values.errors); - }, 100); - } - } - }); - } else if (me.rec) { - ipanel.setValues(me.rec.data); - } - } -}); - -Ext.define('PVE.FirewallGroupRuleEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - allow_iface: false, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.rule_pos === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString(); - me.method = 'PUT'; - } - - var column1 = [ - { - xtype: 'hiddenfield', - name: 'type', - value: 'group' - }, - { - xtype: 'pveSecurityGroupsSelector', - name: 'action', - value: '', - fieldLabel: gettext('Security Group'), - allowBlank: false - } - ]; - - if (me.allow_iface) { - column1.push({ - xtype: 'proxmoxtextfield', - name: 'iface', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('Interface') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('Rule'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.FirewallRules', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveFirewallRules', - - onlineHelp: 'chapter_pve_firewall', - - stateful: true, - stateId: 'grid-firewall-rules', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - groupBtn: undefined, - - tbar_prefix: undefined, - - allow_groups: true, - allow_iface: false, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - if (me.groupBtn) { - me.groupBtn.setDisabled(true); - } - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - if (me.groupBtn) { - me.groupBtn.setDisabled(false); - } - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - moveRule: function(from, to) { - var me = this; - - if (!me.base_url) { - return; - } - - Proxmox.Utils.API2Request({ - url: me.base_url + "/" + from, - method: 'PUT', - params: { moveto: to }, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - updateRule: function(rule) { - var me = this; - - if (!me.base_url) { - return; - } - - rule.enable = rule.enable ? 1 : 0; - - var pos = rule.pos; - delete rule.pos; - delete rule.errors; - - Proxmox.Utils.API2Request({ - url: me.base_url + '/' + pos.toString(), - method: 'PUT', - params: rule, - waitMsgTarget: me, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: function() { - me.store.load(); - } - }); - }, - - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-fw-rule' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type; - - var editor; - if (type === 'in' || type === 'out') { - editor = 'PVE.FirewallRuleEdit'; - } else if (type === 'group') { - editor = 'PVE.FirewallGroupRuleEdit'; - } else { - return; - } - - var win = Ext.create(editor, { - digest: rec.data.digest, - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rule_pos: rec.data.pos - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - var run_copy_editor = function() { - var rec = sm.getSelection()[0]; - - if (!rec) { - return; - } - var type = rec.data.type; - - - if (!(type === 'in' || type === 'out')) { - return; - } - - var win = Ext.create('PVE.FirewallRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url, - list_refs_url: me.list_refs_url, - rec: rec - }); - - win.show(); - win.on('destroy', reload); - }; - - me.copyBtn = Ext.create('Proxmox.button.Button',{ - text: gettext('Copy'), - selModel: sm, - enableFn: function(rec) { - return (rec.data.type === 'in' || rec.data.type === 'out'); - }, - disabled: true, - handler: run_copy_editor - }); - - if (me.allow_groups) { - me.groupBtn = Ext.create('Ext.Button', { - text: gettext('Insert') + ': ' + - gettext('Security Group'), - disabled: true, - handler: function() { - var win = Ext.create('PVE.FirewallGroupRuleEdit', { - allow_iface: me.allow_iface, - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - } - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - baseurl: me.base_url + '/', - confirmMsg: false, - getRecordName: function(rec) { - var rule = rec.data; - return rule.pos.toString() + - '?digest=' + encodeURIComponent(rule.digest); - }, - callback: function() { - me.store.load(); - } - }); - - var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : []; - tbar.push(me.addBtn, me.copyBtn); - if (me.groupBtn) { - tbar.push(me.groupBtn); - } - tbar.push(me.removeBtn, me.editBtn); - - var render_errors = function(name, value, metaData, record) { - var errors = record.data.errors; - if (errors && errors[name]) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(errors[name]) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - return value; - }; - - var columns = [ - { - // similar to xtype: 'rownumberer', - dataIndex: 'pos', - resizable: false, - width: 23, - sortable: false, - align: 'right', - hideable: false, - menuDisabled: true, - renderer: function(value, metaData, record, rowIdx, colIdx, store) { - metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special'; - if (value >= 0) { - return value; - } - return ''; - } - }, - { - xtype: 'checkcolumn', - header: gettext('Enable'), - dataIndex: 'enable', - listeners: { - checkchange: function(column, recordIndex, checked) { - var record = me.getStore().getData().items[recordIndex]; - record.commit(); - var data = {}; - Ext.Array.forEach(record.getFields(), function(field) { - data[field.name] = record.get(field.name); - }); - if (!me.allow_iface || !data.iface) { - delete data.iface; - } - me.updateRule(data); - } - }, - width: 50 - }, - { - header: gettext('Type'), - dataIndex: 'type', - renderer: function(value, metaData, record) { - return render_errors('type', value, metaData, record); - }, - width: 50 - }, - { - header: gettext('Action'), - dataIndex: 'action', - renderer: function(value, metaData, record) { - return render_errors('action', value, metaData, record); - }, - width: 80 - }, - { - header: gettext('Macro'), - dataIndex: 'macro', - renderer: function(value, metaData, record) { - return render_errors('macro', value, metaData, record); - }, - width: 80 - } - ]; - - if (me.allow_iface) { - columns.push({ - header: gettext('Interface'), - dataIndex: 'iface', - renderer: function(value, metaData, record) { - return render_errors('iface', value, metaData, record); - }, - width: 80 - }); - } - - columns.push( - { - header: gettext('Source'), - dataIndex: 'source', - renderer: function(value, metaData, record) { - return render_errors('source', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Destination'), - dataIndex: 'dest', - renderer: function(value, metaData, record) { - return render_errors('dest', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Protocol'), - dataIndex: 'proto', - renderer: function(value, metaData, record) { - return render_errors('proto', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Dest. port'), - dataIndex: 'dport', - renderer: function(value, metaData, record) { - return render_errors('dport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Source port'), - dataIndex: 'sport', - renderer: function(value, metaData, record) { - return render_errors('sport', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Log level'), - dataIndex: 'log', - renderer: function(value, metaData, record) { - return render_errors('log', value, metaData, record); - }, - width: 100 - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value, metaData, record) { - return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record); - } - } - ); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - plugins: [ - { - ptype: 'gridviewdragdrop', - dragGroup: 'FWRuleDDGroup', - dropGroup: 'FWRuleDDGroup' - } - ], - listeners: { - beforedrop: function(node, data, dropRec, dropPosition) { - if (!dropRec) { - return false; // empty view - } - var moveto = dropRec.get('pos'); - if (dropPosition === 'after') { - moveto++; - } - var pos = data.records[0].get('pos'); - me.moveRule(pos, moveto); - return 0; - }, - itemdblclick: run_editor - } - }, - sortableColumns: false, - columns: columns - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-fw-rule', { - extend: 'Ext.data.Model', - fields: [ { name: 'enable', type: 'boolean' }, - 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface', - 'dport', 'sport', 'comment', 'pos', 'digest', 'errors' ], - idProperty: 'pos' - }); - -}); -Ext.define('PVE.FirewallAliasEdit', { - extend: 'Proxmox.window.Edit', - - base_url: undefined, - - alias_name: undefined, - - width: 400, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.alias_name === undefined); - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.alias_name; - me.method = 'PUT'; - } - - var items = [ - { - xtype: 'textfield', - name: me.isCreate ? 'name' : 'rename', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'cidr', - fieldLabel: gettext('IP/CIDR'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ]; - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - items: items - }); - - Ext.apply(me, { - subject: gettext('Alias'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - values.rename = values.name; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('pve-fw-aliases', { - extend: 'Ext.data.Model', - - fields: [ 'name', 'cidr', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.FirewallAliases', { - extend: 'Ext.grid.Panel', - alias: ['widget.pveFirewallAliases'], - - onlineHelp: 'pve_firewall_ip_aliases', - - stateful: true, - stateId: 'grid-firewall-aliases', - - base_url: undefined, - - title: gettext('Alias'), - - initComponent : function() { - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-aliases', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url, - alias_name: rec.data.name - }); - - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = Ext.create('Ext.Button', { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.FirewallAliasEdit', { - base_url: me.base_url - }); - win.on('destroy', reload); - win.show(); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - - Ext.apply(me, { - store: store, - tbar: [ me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1, - }, - { - header: gettext('IP/CIDR'), - dataIndex: 'cidr', - flex: 1, - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 3, - } - ], - listeners: { - itemdblclick: run_editor - } - }); - - me.callParent(); - me.on('activate', reload); - } -}); -Ext.define('PVE.FirewallOptions', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveFirewallOptions'], - - fwtype: undefined, // 'dc', 'node' or 'vm' - - base_url: undefined, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - if (!me.base_url) { - throw "missing base_url configuration"; - } - - if (me.fwtype === 'dc' || me.fwtype === 'node' || me.fwtype === 'vm') { - if (me.fwtype === 'node') { - me.cwidth1 = 250; - } - } else { - throw "unknown firewall option type"; - } - - me.rows = {}; - - var add_boolean_row = function(name, text, defaultValue) { - me.add_boolean_row(name, text, { defaultValue: defaultValue }); - }; - var add_integer_row = function(name, text, minValue, labelWidth) { - me.add_integer_row(name, text, { - minValue: minValue, - deleteEmpty: true, - labelWidth: labelWidth, - renderer: function(value) { - if (value === undefined) { - return Proxmox.Utils.defaultText; - } - - return value; - } - }); - }; - - var add_log_row = function(name, labelWidth) { - me.rows[name] = { - header: name, - required: true, - defaultValue: 'nolog', - editor: { - xtype: 'proxmoxWindowEdit', - subject: name, - fieldDefaults: { labelWidth: labelWidth || 100 }, - items: { - xtype: 'pveFirewallLogLevels', - name: name, - fieldLabel: name - } - } - }; - }; - - if (me.fwtype === 'node') { - me.rows.enable = { - required: true, - defaultValue: 1, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 1 - } - }; - add_boolean_row('nosmurfs', gettext('SMURFS filter'), 1); - add_boolean_row('tcpflags', gettext('TCP flags filter'), 0); - add_boolean_row('ndp', 'NDP', 1); - add_integer_row('nf_conntrack_max', 'nf_conntrack_max', 32768, 120); - add_integer_row('nf_conntrack_tcp_timeout_established', - 'nf_conntrack_tcp_timeout_established', 7875, 250); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - add_log_row('tcp_flags_log_level', 120); - add_log_row('smurf_log_level'); - } else if (me.fwtype === 'vm') { - me.rows.enable = { - required: true, - defaultValue: 0, - header: gettext('Firewall'), - renderer: Proxmox.Utils.format_boolean, - editor: { - xtype: 'pveFirewallEnableEdit', - defaultValue: 0 - } - }; - add_boolean_row('dhcp', 'DHCP', 1); - add_boolean_row('ndp', 'NDP', 1); - add_boolean_row('radv', gettext('Router Advertisement'), 0); - add_boolean_row('macfilter', gettext('MAC filter'), 1); - add_boolean_row('ipfilter', gettext('IP filter'), 0); - add_log_row('log_level_in'); - add_log_row('log_level_out'); - } else if (me.fwtype === 'dc') { - add_boolean_row('enable', gettext('Firewall'), 0); - add_boolean_row('ebtables', 'ebtables', 1); - me.rows.log_ratelimit = { - header: gettext('Log rate limit'), - required: true, - defaultValue: gettext('Default') + ' (enable=1,rate1/second,burst=5)', - editor: { - xtype: 'pveFirewallLograteEdit', - defaultValue: 'enable=1' - } - }; - } - - if (me.fwtype === 'dc' || me.fwtype === 'vm') { - me.rows.policy_in = { - header: gettext('Input Policy'), - required: true, - defaultValue: 'DROP', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Input Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_in', - value: 'DROP', - fieldLabel: gettext('Input Policy') - } - } - }; - - me.rows.policy_out = { - header: gettext('Output Policy'), - required: true, - defaultValue: 'ACCEPT', - editor: { - xtype: 'proxmoxWindowEdit', - subject: gettext('Output Policy'), - items: { - xtype: 'pveFirewallPolicySelector', - name: 'policy_out', - value: 'ACCEPT', - fieldLabel: gettext('Output Policy') - } - } - }; - } - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - var rowdef = me.rows[rec.data.key]; - edit_btn.setDisabled(!rowdef.editor); - }; - - Ext.apply(me, { - url: "/api2/json" + me.base_url, - tbar: [ edit_btn ], - editorConfig: { - url: '/api2/extjs/' + me.base_url - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); - - -Ext.define('PVE.FirewallLogLevels', { - extend: 'Proxmox.form.KVComboBox', - alias: ['widget.pveFirewallLogLevels'], - - name: 'log', - fieldLabel: gettext('Log level'), - value: 'nolog', - comboItems: [['nolog', 'nolog'], ['emerg', 'emerg'], ['alert', 'alert'], - ['crit', 'crit'], ['err', 'err'], ['warning', 'warning'], - ['notice', 'notice'], ['info', 'info'], ['debug', 'debug']] -}); -/* - * Left Treepanel, containing all the resources we manage in this datacenter: server nodes, server storages, VMs and Containers - */ -Ext.define('PVE.tree.ResourceTree', { - extend: 'Ext.tree.TreePanel', - alias: ['widget.pveResourceTree'], - - statics: { - typeDefaults: { - node: { - iconCls: 'fa fa-building', - text: gettext('Nodes') - }, - pool: { - iconCls: 'fa fa-tags', - text: gettext('Resource Pool') - }, - storage: { - iconCls: 'fa fa-database', - text: gettext('Storage') - }, - qemu: { - iconCls: 'fa fa-desktop', - text: gettext('Virtual Machine') - }, - lxc: { - //iconCls: 'x-tree-node-lxc', - iconCls: 'fa fa-cube', - text: gettext('LXC Container') - }, - template: { - iconCls: 'fa fa-file-o' - } - } - }, - - useArrows: true, - - // private - nodeSortFn: function(node1, node2) { - var n1 = node1.data; - var n2 = node2.data; - - if ((n1.groupbyid && n2.groupbyid) || - !(n1.groupbyid || n2.groupbyid)) { - - var tcmp; - - var v1 = n1.type; - var v2 = n2.type; - - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - - // numeric compare for VM IDs - // sort templates after regular VMs - if (v1 === 'qemu' || v1 === 'lxc') { - if (n1.template && !n2.template) { - return 1; - } else if (n2.template && !n1.template) { - return -1; - } - v1 = n1.vmid; - v2 = n2.vmid; - if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { - return tcmp; - } - } - - return n1.id > n2.id ? 1 : (n1.id < n2.id ? -1 : 0); - } else if (n1.groupbyid) { - return -1; - } else if (n2.groupbyid) { - return 1; - } - }, - - // private: fast binary search - findInsertIndex: function(node, child, start, end) { - var me = this; - - var diff = end - start; - - var mid = start + (diff>>1); - - if (diff <= 0) { - return start; - } - - var res = me.nodeSortFn(child, node.childNodes[mid]); - if (res <= 0) { - return me.findInsertIndex(node, child, start, mid); - } else { - return me.findInsertIndex(node, child, mid + 1, end); - } - }, - - setIconCls: function(info) { - var me = this; - - var cls = PVE.Utils.get_object_icon_class(info.type, info); - - if (cls !== '') { - info.iconCls = cls; - } - }, - - // add additional elements to text - // at the moment only the usage indicator for storages - setText: function(info) { - var me = this; - - var status = ''; - if (info.type === 'storage') { - var maxdisk = info.maxdisk; - var disk = info.disk; - var usage = disk/maxdisk; - var cls = ''; - if (usage <= 1.0 && usage >= 0.0) { - var height = (usage*100).toFixed(0); - var neg_height = (100-usage*100).toFixed(0); - status = '
'; - status += '
'; - status += '
'; - status += '
'; - } - } - - info.text = status + info.text; - }, - - setToolTip: function(info) { - if (info.type === 'pool' || info.groupbyid !== undefined) { - return; - } - - var qtips = [gettext('Status') + ': ' + (info.qmpstatus || info.status)]; - if (info.lock) { - qtips.push('Config locked (' + info.lock + ')'); - } - if (info.hastate != 'unmanaged') { - qtips.push(gettext('HA State') + ": " + info.hastate); - } - - info.qtip = qtips.join(', '); - }, - - // private - addChildSorted: function(node, info) { - var me = this; - - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - - var defaults; - if (info.groupbyid) { - info.text = info.groupbyid; - if (info.type === 'type') { - defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid]; - if (defaults && defaults.text) { - info.text = defaults.text; - } - } - } - var child = Ext.create('PVETree', info); - - var cs = node.childNodes; - var pos; - if (cs) { - pos = cs[me.findInsertIndex(node, child, 0, cs.length)]; - } - - node.insertBefore(child, pos); - - return child; - }, - - // private - groupChild: function(node, info, groups, level) { - var me = this; - - var groupby = groups[level]; - var v = info[groupby]; - - if (v) { - var group = node.findChild('groupbyid', v); - if (!group) { - var groupinfo; - if (info.type === groupby) { - groupinfo = info; - } else { - groupinfo = { - type: groupby, - id : groupby + "/" + v - }; - if (groupby !== 'type') { - groupinfo[groupby] = v; - } - } - groupinfo.leaf = false; - groupinfo.groupbyid = v; - group = me.addChildSorted(node, groupinfo); - } - if (info.type === groupby) { - return group; - } - if (group) { - return me.groupChild(group, info, groups, level + 1); - } - } - - return me.addChildSorted(node, info); - }, - - initComponent : function() { - var me = this; - - var rstore = PVE.data.ResourceStore; - var sp = Ext.state.Manager.getProvider(); - - if (!me.viewFilter) { - me.viewFilter = {}; - } - - var pdata = { - dataIndex: {}, - updateCount: 0 - }; - - var store = Ext.create('Ext.data.TreeStore', { - model: 'PVETree', - root: { - expanded: true, - id: 'root', - text: gettext('Datacenter'), - iconCls: 'fa fa-server' - } - }); - - var stateid = 'rid'; - - var updateTree = function() { - var tmp; - - store.suspendEvents(); - - var rootnode = me.store.getRootNode(); - // remember selected node (and all parents) - var sm = me.getSelectionModel(); - - var lastsel = sm.getSelection()[0]; - var reselect = false; - var parents = []; - var p = lastsel; - while (p && !!(p = p.parentNode)) { - parents.push(p); - } - - var index = pdata.dataIndex; - - var groups = me.viewFilter.groups || []; - var filterfn = me.viewFilter.filterfn; - - // remove vanished or moved items - // update in place changed items - var key; - for (key in index) { - if (index.hasOwnProperty(key)) { - var olditem = index[key]; - - // getById() use find(), which is slow (ExtJS4 DP5) - //var item = rstore.getById(olditem.data.id); - var item = rstore.data.get(olditem.data.id); - - var changed = false; - var moved = false; - if (item) { - // test if any grouping attributes changed - // this will also catch migrated nodes - // in server view - var i, len; - for (i = 0, len = groups.length; i < len; i++) { - var attr = groups[i]; - if (item.data[attr] != olditem.data[attr]) { - //console.log("changed " + attr); - moved = true; - break; - } - } - - // explicitly check for node, since - // in some views, node is not a grouping - // attribute - if (!moved && item.data.node !== olditem.data.node) { - moved = true; - } - - // tree item has been updated - var fields = [ - 'text', 'running', 'template', 'status', - 'qmpstatus', 'hastate', 'lock' - ]; - - var field; - for (i = 0; i < fields.length; i++) { - field = fields[i]; - if (item.data[field] !== olditem.data[field]) { - changed = true; - break; - } - } - - // fixme: also test filterfn()? - } - - if (changed) { - olditem.beginEdit(); - //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running); - var info = olditem.data; - Ext.apply(info, item.data); - me.setIconCls(info); - me.setText(info); - me.setToolTip(info); - olditem.commit(); - } - if ((!item || moved) && olditem.isLeaf()) { - //console.log("REM UID: " + key + " ITEM " + olditem.data.id); - delete index[key]; - var parentNode = olditem.parentNode; - // when the selected item disappears, - // we have to deselect it here, and reselect it - // later - if (lastsel && olditem.data.id === lastsel.data.id) { - reselect = true; - sm.deselect(olditem); - } - // since the store events are suspended, we - // manually remove the item from the store also - store.remove(olditem); - parentNode.removeChild(olditem, true); - } - } - } - - // add new items - rstore.each(function(item) { - var olditem = index[item.data.id]; - if (olditem) { - return; - } - - if (filterfn && !filterfn(item)) { - return; - } - - //console.log("ADD UID: " + item.data.id); - - var info = Ext.apply({ leaf: true }, item.data); - - var child = me.groupChild(rootnode, info, groups, 0); - if (child) { - index[item.data.id] = child; - } - }); - - store.resumeEvents(); - store.fireEvent('refresh', store); - - // select parent node is selection vanished - if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) { - lastsel = rootnode; - while (!!(p = parents.shift())) { - if (!!(tmp = rootnode.findChild('id', p.data.id, true))) { - lastsel = tmp; - break; - } - } - me.selectById(lastsel.data.id); - } else if (lastsel && reselect) { - me.selectById(lastsel.data.id); - } - - // on first tree load set the selection from the stateful provider - if (!pdata.updateCount) { - rootnode.expand(); - me.applyState(sp.get(stateid)); - } - - pdata.updateCount++; - }; - - var statechange = function(sp, key, value) { - if (key === stateid) { - me.applyState(value); - } - }; - - sp.on('statechange', statechange); - - Ext.apply(me, { - allowSelection: true, - store: store, - viewConfig: { - // note: animate cause problems with applyState - animate: false - }, - //useArrows: true, - //rootVisible: false, - //title: 'Resource Tree', - listeners: { - itemcontextmenu: PVE.Utils.createCmdMenu, - destroy: function() { - rstore.un("load", updateTree); - }, - beforecellmousedown: function (tree, td, cellIndex, record, tr, rowIndex, ev) { - var sm = me.getSelectionModel(); - // disable selection when right clicking - // except the record is already selected - me.allowSelection = (ev.button !== 2) || sm.isSelected(record); - }, - beforeselect: function (tree, record, index, eopts) { - var allow = me.allowSelection; - me.allowSelection = true; - return allow; - }, - itemdblclick: PVE.Utils.openTreeConsole - }, - setViewFilter: function(view) { - me.viewFilter = view; - me.clearTree(); - updateTree(); - }, - setDatacenterText: function(clustername) { - var rootnode = me.store.getRootNode(); - - var rnodeText = gettext('Datacenter'); - if (clustername !== undefined) { - rnodeText += ' (' + clustername + ')'; - } - - rootnode.beginEdit(); - rootnode.data.text = rnodeText; - rootnode.commit(); - }, - clearTree: function() { - pdata.updateCount = 0; - var rootnode = me.store.getRootNode(); - rootnode.collapse(); - rootnode.removeAll(); - pdata.dataIndex = {}; - me.getSelectionModel().deselectAll(); - }, - selectExpand: function(node) { - var sm = me.getSelectionModel(); - if (!sm.isSelected(node)) { - sm.select(node); - var cn = node; - while (!!(cn = cn.parentNode)) { - if (!cn.isExpanded()) { - cn.expand(); - } - } - me.getView().focusRow(node); - } - }, - selectById: function(nodeid) { - var rootnode = me.store.getRootNode(); - var sm = me.getSelectionModel(); - var node; - if (nodeid === 'root') { - node = rootnode; - } else { - node = rootnode.findChild('id', nodeid, true); - } - if (node) { - me.selectExpand(node); - } - return node; - }, - applyState : function(state) { - var sm = me.getSelectionModel(); - if (state && state.value) { - me.selectById(state.value); - } else { - sm.deselectAll(); - } - } - }); - - me.callParent(); - - var sm = me.getSelectionModel(); - sm.on('select', function(sm, n) { - sp.set(stateid, { value: n.data.id}); - }); - - rstore.on("load", updateTree); - rstore.startUpdate(); - //rstore.stopUpdate(); - } - -}); -Ext.define('PVE.guest.SnapshotTree', { - extend: 'Ext.tree.Panel', - xtype: 'pveGuestSnapshotTree', - - stateful: true, - stateId: 'grid-snapshots', - - viewModel: { - data: { - // should be 'qemu' or 'lxc' - type: undefined, - nodename: undefined, - vmid: undefined, - snapshotAllowed: false, - rollbackAllowed: false, - snapshotFeature: false, - running: false, - selected: '', - load_delay: 3000, - }, - formulas: { - canSnapshot: (get) => get('snapshotAllowed') && get('snapshotFeature'), - canRollback: (get) => get('rollbackAllowed') && get('isSnapshot'), - canRemove: (get) => get('snapshotAllowed') && get('isSnapshot'), - isSnapshot: (get) => get('selected') && get('selected') !== 'current', - buttonText: (get) => get('snapshotAllowed') ? gettext('Edit') : gettext('View'), - showMemory: (get) => get('type') === 'qemu', - }, - }, - - controller: { - xclass: 'Ext.app.ViewController', - - newSnapshot: function() { - this.run_editor(false); - }, - - editSnapshot: function() { - this.run_editor(true); - }, - - run_editor: function(edit) { - let me = this; - let vm = me.getViewModel(); - let snapname; - if (edit) { - snapname = vm.get('selected'); - if (!snapname || snapname === 'current') { return; } - } - let win = Ext.create('PVE.window.Snapshot', { - nodename: vm.get('nodename'), - vmid: vm.get('vmid'), - viewonly: !vm.get('snapshotAllowed'), - type: vm.get('type'), - isCreate: !edit, - submitText: !edit ? gettext('Take Snapshot') : undefined, - snapname: snapname, - running: vm.get('running'), - }); - win.show(); - me.mon(win, 'destroy', me.reload, me); - }, - - snapshotAction: function(action, method) { - let me = this; - let view = me.getView(); - let vm = me.getViewModel(); - let snapname = vm.get('selected'); - if (!snapname) { return; } - - let nodename = vm.get('nodename'); - let type = vm.get('type'); - let vmid = vm.get('vmid'); - - Proxmox.Utils.API2Request({ - url: `/nodes/${nodename}/${type}/${vmid}/snapshot/${snapname}/${action}`, - method: method, - waitMsgTarget: view, - callback: function() { - me.reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - } - }); - }, - - rollback: function() { - this.snapshotAction('rollback', 'POST'); - }, - remove: function() { - this.snapshotAction('', 'DELETE'); - }, - cancel: function() { - this.load_task.cancel(); - }, - - reload: function() { - let me = this; - let view = me.getView(); - let vm = me.getViewModel(); - let nodename = vm.get('nodename'); - let vmid = vm.get('vmid'); - let type = vm.get('type'); - let load_delay = vm.get('load_delay'); - - Proxmox.Utils.API2Request({ - url: `/nodes/${nodename}/${type}/${vmid}/snapshot`, - method: 'GET', - failure: function(response, opts) { - if (me.destroyed) return; - Proxmox.Utils.setErrorMask(view, response.htmlStatus); - me.load_task.delay(load_delay); - }, - success: function(response, opts) { - if (me.destroyed) { - // this is in a delayed task, avoid dragons if view has - // been destroyed already and go home. - return; - } - Proxmox.Utils.setErrorMask(view, false); - var digest = 'invalid'; - var idhash = {}; - var root = { name: '__root', expanded: true, children: [] }; - Ext.Array.each(response.result.data, function(item) { - item.leaf = true; - item.children = []; - if (item.name === 'current') { - vm.set('running', !!item.running); - digest = item.digest + item.running; - item.iconCls = PVE.Utils.get_object_icon_class(vm.get('type'), item); - } else { - item.iconCls = 'fa fa-fw fa-history x-fa-tree'; - } - idhash[item.name] = item; - }); - - if (digest !== me.old_digest) { - me.old_digest = digest; - - Ext.Array.each(response.result.data, function(item) { - if (item.parent && idhash[item.parent]) { - var parent_item = idhash[item.parent]; - parent_item.children.push(item); - parent_item.leaf = false; - parent_item.expanded = true; - parent_item.expandable = false; - } else { - root.children.push(item); - } - }); - - me.getView().setRootNode(root); - } - - me.load_task.delay(load_delay); - } - }); - - // if we do not have the permissions, we don't have to check - // if we can create a snapshot, since the butten stays disabled - if (!vm.get('snapshotAllowed')) { - return; - } - - Proxmox.Utils.API2Request({ - url: `/nodes/${nodename}/${type}/${vmid}/feature`, - params: { feature: 'snapshot' }, - method: 'GET', - success: function(response, options) { - if (me.destroyed) { - // this is in a delayed task, the current view could been - // destroyed already; then we mustn't do viemodel set - return; - } - let res = response.result.data; - vm.set('snapshotFeature', !!res.hasFeature); - } - }); - }, - - select: function(grid, val) { - let vm = this.getViewModel(); - if (val.length < 1) { - vm.set('selected', ''); - return; - } - vm.set('selected', val[0].data.name); - }, - - init: function(view) { - let me = this; - let vm = me.getViewModel(); - me.load_task = new Ext.util.DelayedTask(me.reload, me); - - if (!view.type) { - throw 'guest type not set'; - } - vm.set('type', view.type); - - if (!view.pveSelNode.data.node) { - throw "no node name specified"; - } - vm.set('nodename', view.pveSelNode.data.node); - - if (!view.pveSelNode.data.vmid) { - throw "no VM ID specified"; - } - vm.set('vmid', view.pveSelNode.data.vmid); - - let caps = Ext.state.Manager.get('GuiCap'); - vm.set('snapshotAllowed', !!caps.vms['VM.Snapshot']); - vm.set('rollbackAllowed', !!caps.vms['VM.Snapshot.Rollback']); - - view.getStore().sorters.add({ - property: 'order', - direction: 'ASC', - }); - - me.reload(); - }, - }, - - listeners: { - selectionchange: 'select', - itemdblclick: 'editSnapshot', - destroy: 'cancel', - }, - - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - - tbar: [ - { - xtype: 'proxmoxButton', - text: gettext('Take Snapshot'), - disabled: true, - bind: { - disabled: "{!canSnapshot}", - }, - handler: 'newSnapshot', - }, - '-', - { - xtype: 'proxmoxButton', - text: gettext('Rollback'), - disabled: true, - bind: { - disabled: '{!canRollback}', - }, - confirmMsg: function() { - let view = this.up('treepanel'); - let rec = view.getSelection()[0]; - let vmid = view.getViewModel().get('vmid'); - return Proxmox.Utils.format_task_description('qmrollback', vmid) + - " '" + rec.data.name + "'"; - }, - handler: 'rollback', - }, - { - xtype: 'proxmoxButton', - text: gettext('Remove'), - disabled: true, - bind: { - disabled: '{!canRemove}', - }, - confirmMsg: function() { - let view = this.up('treepanel'); - let rec = view.getSelection()[0]; - return Ext.String.format( - gettext('Are you sure you want to remove entry {0}'), - `'${rec.data.name}'` - ); - }, - handler: 'remove', - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - bind: { - text: '{buttonText}', - disabled: '{!isSnapshot}', - }, - disabled: true, - edit: true, - handler: 'editSnapshot', - }, - { - xtype: 'label', - text: gettext("The current guest configuration does not support taking new snapshots"), - hidden: true, - bind: { - hidden: "{canSnapshot}", - }, - }, - ], - - columnLines: true, - - fields: [ - 'name', 'description', 'snapstate', 'vmstate', 'running', - { name: 'snaptime', type: 'date', dateFormat: 'timestamp' }, - { - name: 'order', - calculate: function(data) { - return data.snaptime || (data.name === 'current' ? 'ZZZ' : data.snapstate); - } - } - ], - - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - width: 200, - renderer: function(value, metaData, record) { - if (value === 'current') { - return gettext('NOW'); - } else { - return value; - } - } - }, - { - text: gettext('RAM'), - hidden: true, - bind: { - hidden: '{!showMemory}', - }, - align: 'center', - resizable: false, - dataIndex: 'vmstate', - width: 50, - renderer: function(value, metaData, record) { - if (record.data.name !== 'current') { - return Proxmox.Utils.format_boolean(value); - } - } - }, - { - text: gettext('Date') + "/" + gettext("Status"), - dataIndex: 'snaptime', - width: 150, - renderer: function(value, metaData, record) { - if (record.data.snapstate) { - return record.data.snapstate; - } - if (value) { - return Ext.Date.format(value,'Y-m-d H:i:s'); - } - } - }, - { - text: gettext('Description'), - dataIndex: 'description', - flex: 1, - renderer: function(value, metaData, record) { - if (record.data.name === 'current') { - return gettext("You are here!"); - } else { - return Ext.String.htmlEncode(value); - } - } - } - ], - -}); -Ext.define('pve-fw-ipsets', { - extend: 'Ext.data.Model', - fields: [ 'name', 'comment', 'digest' ], - idProperty: 'name' -}); - -Ext.define('PVE.IPSetList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetList', - - stateful: true, - stateId: 'grid-firewall-ipsetlist', - - ipset_panel: undefined, - - base_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - initComponent: function() { - - var me = this; - - if (me.ipset_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-fw-ipsets', - proxy: { - type: 'proxmox', - url: "/api2/json" + me.base_url - }, - sorters: { - property: 'name', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('name', oldrec.data.name); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('Proxmox.window.Edit', { - subject: "IPSet '" + rec.data.name + "'", - url: me.base_url, - method: 'POST', - digest: rec.data.digest, - items: [ - { - xtype: 'hiddenfield', - name: 'rename', - value: rec.data.name - }, - { - xtype: 'textfield', - name: 'name', - value: rec.data.name, - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: rec.data.comment, - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('Proxmox.window.Edit', { - subject: 'IPSet', - url: me.base_url, - method: 'POST', - items: [ - { - xtype: 'textfield', - name: 'name', - value: '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - win.show(); - win.on('destroy', reload); - - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - Ext.apply(me, { - store: store, - tbar: [ 'IPSet:', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: 'IPSet', dataIndex: 'name', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = me.base_url + '/' + rec.data.name; - me.ipset_panel.setBaseUrl(url); - }, - deselect: function() { - me.ipset_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.IPSetCidrEdit', { - extend: 'Proxmox.window.Edit', - - cidr: undefined, - - initComponent : function() { - - var me = this; - - me.isCreate = (me.cidr === undefined); - - - if (me.isCreate) { - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - } else { - me.url = '/api2/extjs' + me.base_url + '/' + me.cidr; - me.method = 'PUT'; - } - - var column1 = []; - - if (me.isCreate) { - if (!me.list_refs_url) { - throw "no alias_base_url specified"; - } - - column1.push({ - xtype: 'pveIPRefSelector', - name: 'cidr', - ref_type: 'alias', - autoSelect: false, - editable: true, - base_url: me.list_refs_url, - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } else { - column1.push({ - xtype: 'displayfield', - name: 'cidr', - value: '', - fieldLabel: gettext('IP/CIDR') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - isCreate: me.isCreate, - column1: column1, - column2: [ - { - xtype: 'proxmoxcheckbox', - name: 'nomatch', - checked: false, - uncheckedValue: 0, - fieldLabel: 'nomatch' - } - ], - columnB: [ - { - xtype: 'textfield', - name: 'comment', - value: '', - fieldLabel: gettext('Comment') - } - ] - }); - - Ext.apply(me, { - subject: gettext('IP/CIDR'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - ipanel.setValues(values); - } - }); - } - } -}); - -Ext.define('PVE.IPSetGrid', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveIPSetGrid', - - stateful: true, - stateId: 'grid-firewall-ipsets', - - base_url: undefined, - list_refs_url: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - setBaseUrl: function(url) { - var me = this; - - me.base_url = url; - - if (url === undefined) { - me.addBtn.setDisabled(true); - me.store.removeAll(); - } else { - me.addBtn.setDisabled(false); - me.removeBtn.baseurl = url + '/'; - me.store.setProxy({ - type: 'proxmox', - url: '/api2/json' + url - }); - - me.store.load(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.list_refs_url) { - throw "no1 list_refs_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-ipset' - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - cidr: rec.data.cidr - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Add'), - disabled: true, - handler: function() { - if (!me.base_url) { - return; - } - var win = Ext.create('PVE.IPSetCidrEdit', { - base_url: me.base_url, - list_refs_url: me.list_refs_url - }); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - callback: reload - }); - - var render_errors = function(value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors.cidr || errors.nomatch; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - Ext.apply(me, { - tbar: [ 'IP/CIDR:', me.addBtn, me.removeBtn, me.editBtn ], - store: store, - selModel: sm, - listeners: { - itemdblclick: run_editor - }, - columns: [ - { - xtype: 'rownumberer' - }, - { - header: gettext('IP/CIDR'), - dataIndex: 'cidr', - width: 150, - renderer: function(value, metaData, record) { - value = render_errors(value, metaData, record); - if (record.data.nomatch) { - return '! ' + value; - } - return value; - } - }, - { - header: gettext('Comment'), - dataIndex: 'comment', - flex: 1, - renderer: function(value) { - return Ext.util.Format.htmlEncode(value); - } - } - ] - }); - - me.callParent(); - - if (me.base_url) { - me.setBaseUrl(me.base_url); // load - } - } -}, function() { - - Ext.define('pve-ipset', { - extend: 'Ext.data.Model', - fields: [ { name: 'nomatch', type: 'boolean' }, - 'cidr', 'comment', 'errors' ], - idProperty: 'cidr' - }); - -}); - -Ext.define('PVE.IPSet', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveIPSet', - - title: 'IPSet', - - onlineHelp: 'pve_firewall_ip_sets', - - list_refs_url: undefined, - - initComponent: function() { - var me = this; - - if (!me.list_refs_url) { - throw "no list_refs_url specified"; - } - - var ipset_panel = Ext.createWidget('pveIPSetGrid', { - region: 'center', - list_refs_url: me.list_refs_url, - border: false - }); - - var ipset_list = Ext.createWidget('pveIPSetList', { - region: 'west', - ipset_panel: ipset_panel, - base_url: me.base_url, - width: '50%', - border: false, - split: true - }); - - Ext.apply(me, { - layout: 'border', - items: [ ipset_list, ipset_panel ], - listeners: { - show: function() { - ipset_list.fireEvent('show', ipset_list); - } - } - }); - - me.callParent(); - } -}); -/* - * Base class for all the multitab config panels - * - * How to use this: - * - * You create a subclass of this, and then define your wanted tabs - * as items like this: - * - * items: [{ - * title: "myTitle", - * xytpe: "somextype", - * iconCls: 'fa fa-icon', - * groups: ['somegroup'], - * expandedOnInit: true, - * itemId: 'someId' - * }] - * - * this has to be in the declarative syntax, else we - * cannot save them for later - * (so no Ext.create or Ext.apply of an item in the subclass) - * - * the groups array expects the itemids of the items - * which are the parents, which have to come before they - * are used - * - * if you want following the tree: - * - * Option1 - * Option2 - * -> SubOption1 - * -> SubSubOption1 - * - * the suboption1 group array has to look like this: - * groups: ['itemid-of-option2'] - * - * and of subsuboption1: - * groups: ['itemid-of-option2', 'itemid-of-suboption1'] - * - * setting the expandedOnInit determines if the item/group is expanded - * initially (false by default) - */ -Ext.define('PVE.panel.Config', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePanelConfig', - - showSearch: true, // add a resource grid with a search button as first tab - viewFilter: undefined, // a filter to pass to that resource grid - - tbarSpacing: true, // if true, adds a spacer after the title in tbar - - dockedItems: [{ - // this is needed for the overflow handler - xtype: 'toolbar', - overflowHandler: 'scroller', - dock: 'left', - style: { - backgroundColor: '#f5f5f5', - padding: 0, - margin: 0 - }, - items: { - xtype: 'treelist', - itemId: 'menu', - ui: 'nav', - expanderOnly: true, - expanderFirst: false, - animation: false, - singleExpand: false, - listeners: { - selectionchange: function(treeList, selection) { - var me = this.up('panel'); - me.suspendLayout = true; - me.activateCard(selection.data.id); - me.suspendLayout = false; - me.updateLayout(); - }, - itemclick: function(treelist, info) { - var olditem = treelist.getSelection(); - var newitem = info.node; - - // when clicking on the expand arrow, - // we don't select items, but still want - // the original behaviour - if (info.select === false) { - return; - } - - // if you click on a different item which is open, - // leave it open - // else toggle the clicked item - if (olditem.data.id !== newitem.data.id && - newitem.data.expanded === true) { - info.toggle = false; - } else { - info.toggle = true; - } - } - } - } - }, - { - xtype: 'toolbar', - itemId: 'toolbar', - dock: 'top', - height: 36, - overflowHandler: 'scroller' - }], - - firstItem: '', - layout: 'card', - border: 0, - - // used for automated test - selectById: function(cardid) { - var me = this; - - var root = me.store.getRoot(); - var selection = root.findChild('id', cardid, true); - - if (selection) { - selection.expand(); - var menu = me.down('#menu'); - menu.setSelection(selection); - return cardid; - } - }, - - activateCard: function(cardid) { - var me = this; - if (me.savedItems[cardid]) { - var curcard = me.getLayout().getActiveItem(); - var newcard = me.add(me.savedItems[cardid]); - me.helpButton.setOnlineHelp(newcard.onlineHelp || me.onlineHelp); - if (curcard) { - me.setActiveItem(cardid); - me.remove(curcard, true); - - // trigger state change - - var ncard = cardid; - // Note: '' is alias for first tab. - // First tab can be 'search' or something else - if (cardid === me.firstItem) { - ncard = ''; - } - if (me.hstateid) { - me.sp.set(me.hstateid, { value: ncard }); - } - } - } - }, - - initComponent: function() { - var me = this; - - var stateid = me.hstateid; - - me.sp = Ext.state.Manager.getProvider(); - - var activeTab; // leaving this undefined means items[0] will be the default tab - - if (stateid) { - var state = me.sp.get(stateid); - if (state && state.value) { - // if this tab does not exist, it chooses the first - activeTab = state.value; - } - } - - // get title - var title = me.title || me.pveSelNode.data.text; - me.title = undefined; - - // create toolbar - var tbar = me.tbar || []; - me.tbar = undefined; - - if (!me.onlineHelp) { - switch(me.pveSelNode.data.id) { - case 'type/storage':me.onlineHelp = 'chapter-pvesm.html'; break; - case 'type/qemu':me.onlineHelp = 'chapter-qm.html'; break; - case 'type/lxc':me.onlineHelp = 'chapter-pct.html'; break; - case 'type/pool':me.onlineHelp = 'chapter-pveum.html#_pools'; break; - case 'type/node':me.onlineHelp = 'chapter-sysadmin.html'; break; - } - } - - if (me.tbarSpacing) { - tbar.unshift('->'); - } - tbar.unshift({ - xtype: 'tbtext', - text: title, - baseCls: 'x-panel-header-text' - }); - - me.helpButton = Ext.create('Proxmox.button.Help', { - hidden: false, - listenToGlobalEvent: false, - onlineHelp: me.onlineHelp || undefined - }); - - tbar.push(me.helpButton); - - me.dockedItems[1].items = tbar; - - // include search tab - me.items = me.items || []; - if (me.showSearch) { - me.items.unshift({ - itemId: 'search', - title: gettext('Search'), - iconCls: 'fa fa-search', - xtype: 'pveResourceGrid', - pveSelNode: me.pveSelNode - }); - } - - me.savedItems = {}; - /*jslint confusion:true*/ - if (me.items[0]) { - me.firstItem = me.items[0].itemId; - } - /*jslint confusion:false*/ - - me.store = Ext.create('Ext.data.TreeStore', { - root: { - expanded: true - } - }); - var root = me.store.getRoot(); - me.items.forEach(function(item){ - var treeitem = Ext.create('Ext.data.TreeModel',{ - id: item.itemId, - text: item.title, - iconCls: item.iconCls, - leaf: true, - expanded: item.expandedOnInit - }); - item.header = false; - if (me.savedItems[item.itemId] !== undefined) { - throw "itemId already exists, please use another"; - } - me.savedItems[item.itemId] = item; - - var group; - var curnode = root; - - // get/create the group items - while (Ext.isArray(item.groups) && item.groups.length > 0) { - group = item.groups.shift(); - - var child = curnode.findChild('id', group); - if (child === null) { - // did not find the group item - // so add it where we are - break; - } - curnode = child; - } - - // insert the item - - // lets see if it already exists - var node = curnode.findChild('id', item.itemId); - - if (node === null) { - curnode.appendChild(treeitem); - } else { - // should not happen! - throw "id already exists"; - } - }); - - delete me.items; - me.defaults = me.defaults || {}; - Ext.apply(me.defaults, { - pveSelNode: me.pveSelNode, - viewFilter: me.viewFilter, - workspace: me.workspace, - border: 0 - }); - - me.callParent(); - - var menu = me.down('#menu'); - var selection = root.findChild('id', activeTab, true) || root.firstChild; - var node = selection; - while (node !== root) { - node.expand(); - node = node.parentNode; - } - menu.setStore(me.store); - menu.setSelection(selection); - - // on a state change, - // select the new item - var statechange = function(sp, key, state) { - // it the state change is for this panel - if (stateid && (key === stateid) && state) { - // get active item - var acard = me.getLayout().getActiveItem().itemId; - // get the itemid of the new value - var ncard = state.value || me.firstItem; - if (ncard && (acard != ncard)) { - // select the chosen item - menu.setSelection(root.findChild('id', ncard, true) || root.firstChild); - } - } - }; - - if (stateid) { - me.mon(me.sp, 'statechange', statechange); - } - } -}); -Ext.define('PVE.grid.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveBackupView'], - - onlineHelp: 'chapter_vzdump', - - stateful: true, - stateId: 'grid-guest-backup', - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var vmtype = me.pveSelNode.data.type; - if (!vmtype) { - throw "no VM type specified"; - } - - var vmtypeFilter; - if (vmtype === 'lxc' || vmtype === 'openvz') { - vmtypeFilter = function(item) { - return PVE.Utils.volume_is_lxc_backup(item.data.volid, item.data.format); - }; - } else if (vmtype === 'qemu') { - vmtypeFilter = function(item) { - return PVE.Utils.volume_is_qemu_backup(item.data.volid, item.data.format); - }; - } else { - throw "unsupported VM type '" + vmtype + "'"; - } - - var searchFilter = { - property: 'volid', - // on initial store display only our vmid backups - // surround with minus sign to prevent the 2016 VMID bug - value: vmtype + '-' + vmid + '-', - anyMatch: true, - caseSensitive: false - }; - - me.store = Ext.create('Ext.data.Store', { - model: 'pve-storage-content', - sorters: { - property: 'volid', - order: 'DESC' - }, - filters: [ - vmtypeFilter, - searchFilter - ] - }); - - var reload = Ext.Function.createBuffered(function() { - if (me.store) { - me.store.load(); - } - }, 100); - - var setStorage = function(storage) { - var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content'; - url += '?content=backup'; - - me.store.setProxy({ - type: 'proxmox', - url: url - }); - - reload(); - }; - - var storagesel = Ext.create('PVE.form.StorageSelector', { - nodename: nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'backup', - allowBlank: false, - listeners: { - change: function(f, value) { - setStorage(value); - } - } - }); - - var storagefilter = Ext.create('Ext.form.field.Text', { - fieldLabel: gettext('Search'), - labelWidth: 50, - labelAlign: 'right', - enableKeyEvents: true, - value: searchFilter.value, - listeners: { - buffer: 500, - keyup: function(field) { - me.store.clearFilter(true); - searchFilter.value = field.getValue(); - me.store.filter([ - vmtypeFilter, - searchFilter - ]); - } - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var backup_btn = Ext.create('Ext.button.Button', { - text: gettext('Backup now'), - handler: function() { - var win = Ext.create('PVE.window.Backup', { - nodename: nodename, - vmid: vmid, - vmtype: vmtype, - storage: storagesel.getValue(), - listeners : { - close: function() { - reload(); - } - } - }); - win.show(); - } - }); - - var restore_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Restore'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var volid = rec.data.volid; - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - vmid: vmid, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }); - - var delete_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - dangerous: true, - delay: 5, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.volid + "'"); - msg += " " + gettext('This will permanently erase all data.'); - - return msg; - }, - getUrl: function(rec) { - var storage = storagesel.getValue(); - return '/nodes/' + nodename + '/storage/' + storage + '/content/' + rec.data.volid; - }, - callback: function() { - reload(); - } - }); - - var config_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!rec; - }, - handler: function(b, e, rec) { - var storage = storagesel.getValue(); - if (!storage) { - return; - } - - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }); - - Ext.apply(me, { - selModel: sm, - tbar: [ backup_btn, restore_btn, delete_btn,config_btn, '->', storagesel, storagefilter ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'volid' - }, - { - header: gettext('Date'), - width: 150, - dataIndex: 'vdate' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.CephCreateService', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCephCreateService', - - showProgress: true, - - setNode: function(nodename) { - var me = this; - - me.nodename = nodename; - me.url = "/nodes/" + nodename + "/ceph/" + me.type + "/" + nodename; - }, - - method: 'POST', - isCreate: true, - - items: [ - { - xtype: 'pveNodeSelector', - submitValue: false, - fieldLabel: gettext('Host'), - selectCurNode: true, - allowBlank: false, - listeners: { - change: function(f, value) { - var me = this.up('pveCephCreateService'); - me.setNode(value); - } - } - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.type) { - throw "no type specified"; - } - - me.setNode(me.nodename); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephServiceList', { - extend: 'Ext.grid.GridPanel', - xtype: 'pveNodeCephServiceList', - - onlineHelp: 'chapter_pveceph', - emptyText: gettext('No such service configured.'), - - stateful: true, - - // will be called when the store loads - storeLoadCallback: Ext.emptyFn, - - // if set to true, does shows the ceph install mask if needed - showCephInstallMask: false, - - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - if (view.pveSelNode) { - view.nodename = view.pveSelNode.data.node; - } - if (!view.nodename) { - throw "no node name specified"; - } - - if (!view.type) { - throw "no type specified"; - } - - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - autoStart: true, - interval: 3000, - storeid: 'ceph-' + view.type + '-list' + view.nodename, - model: 'ceph-service-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + view.nodename + "/ceph/" + view.type - } - }); - - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: [{ property: 'name' }] - })); - - if (view.storeLoadCallback) { - view.rstore.on('load', view.storeLoadCallback, this); - } - view.on('destroy', view.rstore.stopUpdate); - - if (view.showCephInstallMask) { - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error) { - view.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(view.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - view.rstore.startUpdate(); - }); - } - ); - }); - } - }, - - service_cmd: function(rec, cmd) { - var view = this.getView(); - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/" + cmd, - method: 'POST', - params: { service: view.type + '.' + rec.data.name }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - taskDone: function() { - view.rstore.load(); - } - }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - onChangeService: function(btn) { - var me = this; - var view = this.getView(); - var cmd = btn.action; - var rec = view.getSelection()[0]; - me.service_cmd(rec, cmd); - }, - - showSyslog: function() { - var view = this.getView(); - var rec = view.getSelection()[0]; - var servicename = 'ceph-' + view.type + '@' + rec.data.name; - var url = "/api2/extjs/nodes/" + rec.data.host + "/syslog?service=" + encodeURIComponent(servicename); - var win = Ext.create('Ext.window.Window', { - title: gettext('Syslog') + ': ' + servicename, - modal: true, - width: 800, - height: 400, - layout: 'fit', - items: [{ - xtype: 'proxmoxLogView', - url: url, - log_select_timespan: 1 - }] - }); - win.show(); - }, - - onCreate: function() { - var view = this.getView(); - var win = Ext.create('PVE.CephCreateService', { - autoShow: true, - nodename: view.nodename, - subject: view.getTitle(), - type: view.type, - taskDone: function() { - view.rstore.load(); - } - }); - } - }, - - tbar: [ - { - xtype: 'proxmoxButton', - text: gettext('Start'), - iconCls: 'fa fa-play', - action: 'start', - disabled: true, - enableFn: function(rec) { - return rec.data.state === 'stopped' || - rec.data.state === 'unknown'; - }, - handler: 'onChangeService' - }, - { - xtype: 'proxmoxButton', - text: gettext('Stop'), - iconCls: 'fa fa-stop', - action: 'stop', - enableFn: function(rec) { - return rec.data.state !== 'stopped'; - }, - disabled: true, - handler: 'onChangeService' - }, - { - xtype: 'proxmoxButton', - text: gettext('Restart'), - iconCls: 'fa fa-refresh', - action: 'restart', - disabled: true, - enableFn: function(rec) { - return rec.data.state !== 'stopped'; - }, - handler: 'onChangeService' - }, - '-', - { - text: gettext('Create'), - reference: 'createButton', - handler: 'onCreate' - }, - { - text: gettext('Destroy'), - xtype: 'proxmoxStdRemoveButton', - getUrl: function(rec) { - var view = this.up('grid'); - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - return "/nodes/" + rec.data.host + "/ceph/" + view.type + "/" + rec.data.name; - }, - callback: function(options, success, response) { - var view = this.up('grid'); - if (!success) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - return; - } - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - taskDone: function() { - view.rstore.load(); - } - }); - win.show(); - } - }, - '-', - { - xtype: 'proxmoxButton', - text: gettext('Syslog'), - disabled: true, - handler: 'showSyslog' - } - ], - - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: function(v) { - return this.type + '.' + v; - }, - dataIndex: 'name' - }, - { - header: gettext('Host'), - flex: 1, - sortable: true, - renderer: function(v) { - return v || Proxmox.Utils.unknownText; - }, - dataIndex: 'host' - }, - { - header: gettext('Status'), - flex: 1, - sortable: false, - dataIndex: 'state' - }, - { - header: gettext('Address'), - flex: 3, - sortable: true, - renderer: function(v) { - return v || Proxmox.Utils.unknownText; - }, - dataIndex: 'addr' - }, - { - header: gettext('Version'), - flex: 3, - sortable: true, - dataIndex: 'version' - } - ], - - initComponent: function() { - var me = this; - - if (me.additionalColumns) { - me.columns = me.columns.concat(me.additionalColumns); - } - - me.callParent(); - } - -}, function() { - - Ext.define('ceph-service-list', { - extend: 'Ext.data.Model', - fields: [ 'addr', 'name', 'rank', 'host', 'quorum', 'state', - 'ceph_version', 'ceph_version_short', - { type: 'string', name: 'version', calculate: function(data) { - return PVE.Utils.parse_ceph_version(data); - } } - ], - idProperty: 'name' - }); -}); -/*jslint confusion: true */ -Ext.define('PVE.CephCreateFS', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreateFS', - - showTaskViewer: true, - onlineHelp: 'pveceph_fs_create', - - subject: 'Ceph FS', - isCreate: true, - method: 'POST', - - setFSName: function(fsName) { - var me = this; - - if (fsName === '' || fsName === undefined) { - fsName = 'cephfs'; - } - - me.url = "/nodes/" + me.nodename + "/ceph/fs/" + fsName; - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - value: 'cephfs', - listeners: { - change: function(f, value) { - this.up('pveCephCreateFS').setFSName(value); - } - }, - submitValue: false, // already encoded in apicall URL - emptyText: 'cephfs' - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'Placement Groups', - name: 'pg_num', - value: 128, - emptyText: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add as Storage'), - value: true, - name: 'add-storage', - autoEl: { - tag: 'div', - 'data-qtip': gettext('Add the new CephFS to the cluster storage configuration.'), - }, - } - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - me.setFSName(); - - me.callParent(); - } -}); - -Ext.define('PVE.NodeCephFSPanel', { - extend: 'Ext.panel.Panel', - xtype: 'pveNodeCephFSPanel', - mixins: ['Proxmox.Mixin.CBind'], - - title: gettext('CephFS'), - onlineHelp: 'pveceph_fs', - - border: false, - defaults: { - border: false, - cbind: { - nodename: '{nodename}' - } - }, - - viewModel: { - parent: null, - data: { - cephfsConfigured: false, - mdsCount: 0 - }, - formulas: { - canCreateFS: function(get) { - return (!get('cephfsConfigured') && get('mdsCount') > 0); - } - } - }, - - items: [ - { - xtype: 'grid', - emptyText: Ext.String.format(gettext('No {0} configured.'), 'CephFS'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-ceph-fs', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + view.nodename + '/ceph/fs' - }, - model: 'pve-ceph-fs' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'name', - order: 'DESC' - } - })); - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){ - me.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me.ownerCt, error.statusText, view.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.rstore.startUpdate(); - }); - } - ); - }); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onCreate: function() { - var view = this.getView(); - view.rstore.stopUpdate(); - var win = Ext.create('PVE.CephCreateFS', { - autoShow: true, - nodename: view.nodename, - listeners: { - destroy: function() { - view.rstore.startUpdate(); - } - } - }); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!(success && records && records.length > 0)) { - vm.set('cephfsConfigured', false); - return; - } - vm.set('cephfsConfigured', true); - } - }, - tbar: [ - { - text: gettext('Create CephFS'), - reference: 'createButton', - handler: 'onCreate', - bind: { - // only one CephFS per Ceph cluster makes sense for now - disabled: '{!canCreateFS}' - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - dataIndex: 'name' - }, - { - header: 'Data Pool', - flex: 1, - dataIndex: 'data_pool' - }, - { - header: 'Metadata Pool', - flex: 1, - dataIndex: 'metadata_pool' - } - ], - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'pveNodeCephServiceList', - title: gettext('Metadata Servers'), - stateId: 'grid-ceph-mds', - type: 'mds', - storeLoadCallback: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records) { - vm.set('mdsCount', 0); - return; - } - vm.set('mdsCount', records.length); - }, - cbind: { - nodename: '{nodename}' - } - } - ] -}, function() { - Ext.define('pve-ceph-fs', { - extend: 'Ext.data.Model', - fields: [ 'name', 'data_pool', 'metadata_pool' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/localhost/ceph/fs" - }, - idProperty: 'name' - }); -}); -Ext.define('PVE.CephCreatePool', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreatePool', - - showProgress: true, - onlineHelp: 'pve_ceph_pools', - - subject: 'Ceph Pool', - isCreate: true, - method: 'POST', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Size'), - name: 'size', - value: 3, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Min. Size'), - name: 'min_size', - value: 2, - minValue: 1, - maxValue: 7, - allowBlank: false - }, - { - xtype: 'pveCephRuleSelector', - fieldLabel: 'Crush Rule', // do not localize - name: 'crush_rule', - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: 'pg_num', - name: 'pg_num', - value: 128, - minValue: 8, - maxValue: 32768, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add as Storage'), - value: true, - name: 'add_storages', - autoEl: { - tag: 'div', - 'data-qtip': gettext('Add the new pool to the cluster storage configuration.'), - }, - } - ], - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: "/nodes/" + me.nodename + "/ceph/pools", - defaults: { - nodename: me.nodename - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephPoolList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeCephPoolList', - - onlineHelp: 'chapter_pveceph', - - stateful: true, - stateId: 'grid-ceph-pools', - bufferedRenderer: false, - - features: [ { ftype: 'summary'} ], - - columns: [ - { - header: gettext('Name'), - width: 120, - sortable: true, - dataIndex: 'pool_name' - }, - { - header: gettext('Size') + '/min', - width: 100, - align: 'right', - renderer: function(v, meta, rec) { - return v + '/' + rec.data.min_size; - }, - dataIndex: 'size' - }, - { - text: '# Placement Groups', // pg_num', - width: 180, - align: 'right', - dataIndex: 'pg_num' - }, - { - text: 'CRUSH Rule', - columns: [ - { - text: 'ID', - align: 'right', - width: 50, - dataIndex: 'crush_rule' - }, - { - text: gettext('Name'), - width: 150, - dataIndex: 'crush_rule_name', - }, - ] - }, - { - text: gettext('Used'), - columns: [ - { - text: '%', - width: 100, - sortable: true, - align: 'right', - renderer: function(val) { - return Ext.util.Format.percent(val, '0.00'); - }, - dataIndex: 'percent_used', - summaryType: 'sum', - summaryRenderer: function(val) { - return Ext.util.Format.percent(val, '0.00'); - }, - }, - { - text: gettext('Total'), - width: 100, - sortable: true, - renderer: PVE.Utils.render_size, - align: 'right', - dataIndex: 'bytes_used', - summaryType: 'sum', - summaryRenderer: PVE.Utils.render_size - } - ] - } - ], - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'ceph-pool-list' + nodename, - model: 'ceph-pool-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/ceph/pools" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore }); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ - me.store.rstore.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.rstore.startUpdate(); - }); - } - ); - }); - - var create_btn = new Ext.Button({ - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.CephCreatePool', { - nodename: nodename - }); - win.show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - var destroy_btn = Ext.create('Proxmox.button.Button', { - text: gettext('Destroy'), - selModel: sm, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - if (!rec.data.pool_name) { - return; - } - var base_url = '/nodes/' + nodename + '/ceph/pools/' + - rec.data.pool_name; - - var win = Ext.create('PVE.window.SafeDestroy', { - showProgress: true, - url: base_url, - params: { - remove_storages: 1 - }, - item: { type: 'CephPool', id: rec.data.pool_name } - }).show(); - win.on('destroy', function() { - rstore.load(); - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ create_btn, destroy_btn ], - listeners: { - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('ceph-pool-list', { - extend: 'Ext.data.Model', - fields: [ 'pool_name', - { name: 'pool', type: 'integer'}, - { name: 'size', type: 'integer'}, - { name: 'min_size', type: 'integer'}, - { name: 'pg_num', type: 'integer'}, - { name: 'bytes_used', type: 'integer'}, - { name: 'percent_used', type: 'number'}, - { name: 'crush_rule', type: 'integer'}, - { name: 'crush_rule_name', type: 'string'} - ], - idProperty: 'pool_name' - }); -}); - -Ext.define('PVE.form.CephRuleSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCephRuleSelector', - - allowBlank: false, - valueField: 'name', - displayField: 'name', - editable: false, - queryMode: 'local', - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['name'], - sorters: 'name', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/ceph/rules' - } - }); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - - store.load({ - callback: function(rec, op, success){ - if (success && rec.length > 0) { - me.select(rec[0]); - } - } - }); - } - -}); -Ext.define('PVE.CephCreateOsd', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCephCreateOsd', - - subject: 'Ceph OSD', - - showProgress: true, - - onlineHelp: 'pve_ceph_osds', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd", - method: 'POST', - items: [ - { - xtype: 'inputpanel', - onGetValues: function(values) { - Object.keys(values || {}).forEach(function(name) { - if (values[name] === '') { - delete values[name]; - } - }); - - return values; - }, - column1: [ - { - xtype: 'pveDiskSelector', - name: 'dev', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - } - ], - column2: [ - { - xtype: 'pveDiskSelector', - name: 'db_dev', - nodename: me.nodename, - diskType: 'journal_disks', - fieldLabel: gettext('DB Disk'), - value: '', - autoSelect: false, - allowBlank: true, - emptyText: 'use OSD disk', - listeners: { - change: function(field, val) { - me.down('field[name=db_size]').setDisabled(!val); - } - } - }, - { - xtype: 'numberfield', - name: 'db_size', - fieldLabel: gettext('DB size') + ' (GiB)', - minValue: 1, - maxValue: 128*1024, - decimalPrecision: 2, - allowBlank: true, - disabled: true, - emptyText: gettext('Automatic') - } - ], - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'encrypted', - fieldLabel: gettext('Encrypt OSD') - }, - ], - advancedColumn2: [ - { - xtype: 'pveDiskSelector', - name: 'wal_dev', - nodename: me.nodename, - diskType: 'journal_disks', - fieldLabel: gettext('WAL Disk'), - value: '', - autoSelect: false, - allowBlank: true, - emptyText: 'use OSD/DB disk', - listeners: { - change: function(field, val) { - me.down('field[name=wal_size]').setDisabled(!val); - } - } - }, - { - xtype: 'numberfield', - name: 'wal_size', - fieldLabel: gettext('WAL size') + ' (GiB)', - minValue: 0.5, - maxValue: 128*1024, - decimalPrecision: 2, - allowBlank: true, - disabled: true, - emptyText: gettext('Automatic') - } - ] - }, - { - xtype: 'displayfield', - padding: '5 0 0 0', - userCls: 'pmx-hint', - value: 'Note: Ceph is not compatible with disks backed by a hardware ' + - 'RAID controller. For details see ' + - 'the reference documentation.', - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.CephRemoveOsd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephRemoveOsd'], - - isRemove: true, - - showProgress: true, - method: 'DELETE', - items: [ - { - xtype: 'proxmoxcheckbox', - name: 'cleanup', - checked: true, - labelWidth: 130, - fieldLabel: gettext('Cleanup Disks') - } - ], - initComponent : function() { - - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - if (me.osdid === undefined || me.osdid < 0) { - throw "no osdid specified"; - } - - me.isCreate = true; - - me.title = gettext('Destroy') + ': Ceph OSD osd.' + me.osdid.toString(); - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/ceph/osd/" + me.osdid.toString() - }); - - me.callParent(); - } -}); - -Ext.define('PVE.CephSetFlags', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCephSetFlags', - - showProgress: true, - - width: 720, - layout: 'fit', - - onlineHelp: 'pve_ceph_osds', - isCreate: true, - title: Ext.String.format(gettext('Manage {0}'), 'Global OSD Flags'), - submitText: gettext('Apply'), - - items: [ - { - xtype: 'inputpanel', - onGetValues: function(values) { - var me = this; - var val = {}; - var data = me.down('#flaggrid').getStore().each((rec) => { - val[rec.data.name] = rec.data.value ? 1 : 0; - }); - - return val; - }, - items: [ - { - xtype: 'grid', - itemId: 'flaggrid', - store: { - listeners: { - update: function() { - this.commitChanges(); - } - } - }, - - columns: [ - { - text: gettext('Enable'), - xtype: 'checkcolumn', - width: 75, - dataIndex: 'value', - }, - { - text: 'Name', - dataIndex: 'name', - }, - { - text: 'Description', - flex: 1, - dataIndex: 'description', - }, - ] - }, - ], - }, - ], - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.applyIf(me, { - url: "/cluster/ceph/flags", - method: 'PUT', - }); - - me.callParent(); - - var grid = me.down('#flaggrid'); - me.load({ - success: function(response, options) { - var data = response.result.data; - grid.getStore().setData(data); - // re-align after store load, else the window is not centered - me.alignTo(Ext.getBody(), 'c-c'); - } - }); - } -}); - -Ext.define('PVE.node.CephOsdTree', { - extend: 'Ext.tree.Panel', - alias: ['widget.pveNodeCephOsdTree'], - onlineHelp: 'chapter_pveceph', - - viewModel: { - data: { - nodename: '', - flags: [], - maxversion: '0', - mixedversions: false, - versions: {}, - isOsd: false, - downOsd: false, - upOsd: false, - inOsd: false, - outOsd: false, - osdid: '', - osdhost: '', - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - - reload: function() { - var me = this.getView(); - var vm = this.getViewModel(); - var nodename = vm.get('nodename'); - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + nodename + "/ceph/osd", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me, msg, nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', this.reload); - } - ); - }, - success: function(response, opts) { - var data = response.result.data; - var selected = me.getSelection(); - var name; - if (selected.length) { - name = selected[0].data.name; - } - vm.set('versions', data.versions); - // extract max version - var maxversion = "0"; - var mixedversions = false; - var traverse; - traverse = function(node, fn) { - fn(node); - if (Array.isArray(node.children)) { - node.children.forEach(c => { traverse(c, fn); }); - } - }; - traverse(data.root, node => { - // compatibility for old api call - if (node.type === 'host' && !node.version) { - node.version = data.versions[node.name]; - } - - if (node.version === undefined) { - return; - } - - if (node.version !== maxversion && maxversion !== "0") { - mixedversions = true; - } - - if (PVE.Utils.compare_ceph_versions(node.version, maxversion) > 0) { - maxversion = node.version; - } - - }); - vm.set('maxversion', maxversion); - vm.set('mixedversions', mixedversions); - sm.deselectAll(); - me.setRootNode(data.root); - me.expandAll(); - if (name) { - var node = me.getRootNode().findChild('name', name, true); - if (node) { - me.setSelection([node]); - } - } - - var flags = data.flags.split(','); - vm.set('flags', flags); - } - }); - }, - - osd_cmd: function(comp) { - var me = this; - var vm = this.getViewModel(); - var cmd = comp.cmd; - var params = comp.params || {}; - var osdid = vm.get('osdid'); - - var doRequest = function() { - Proxmox.Utils.API2Request({ - url: "/nodes/" + vm.get('osdhost') + "/ceph/osd/" + osdid + '/' + cmd, - waitMsgTarget: me.getView(), - method: 'POST', - params: params, - success: () => { me.reload(); }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - if (cmd === 'scrub') { - Ext.MessageBox.defaultButton = params.deep === 1 ? 2 : 1; - Ext.Msg.show({ - title: gettext('Confirm'), - icon: params.deep === 1 ? Ext.Msg.WARNING : Ext.Msg.QUESTION, - msg: params.deep !== 1 ? - Ext.String.format(gettext("Scrub OSD.{0}"), osdid) : - Ext.String.format(gettext("Deep Scrub OSD.{0}"), osdid) + - "
Caution: This can reduce performance while it is running.", - buttons: Ext.Msg.YESNO, - callback: function(btn) { - if (btn !== 'yes') { - return; - } - doRequest(); - } - }); - } else { - doRequest(); - } - }, - - create_osd: function() { - var me = this; - var vm = this.getViewModel(); - Ext.create('PVE.CephCreateOsd', { - nodename: vm.get('nodename'), - taskDone: () => { me.reload(); } - }).show(); - }, - - destroy_osd: function() { - var me = this; - var vm = this.getViewModel(); - Ext.create('PVE.CephRemoveOsd', { - nodename: vm.get('osdhost'), - osdid: vm.get('osdid'), - taskDone: () => { me.reload(); } - }).show(); - }, - - set_flags: function() { - var me = this; - var vm = this.getViewModel(); - Ext.create('PVE.CephSetFlags', { - nodename: vm.get('nodename'), - taskDone: () => { me.reload(); } - }).show(); - }, - - service_cmd: function(comp) { - var me = this; - var vm = this.getViewModel(); - var cmd = comp.cmd || comp; - Proxmox.Utils.API2Request({ - url: "/nodes/" + vm.get('osdhost') + "/ceph/" + cmd, - params: { service: "osd." + vm.get('osdid') }, - waitMsgTarget: me.getView(), - method: 'POST', - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - taskDone: () => { me.reload(); } - }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - set_selection_status: function(tp, selection) { - if (selection.length < 1) { - return; - } - var rec = selection[0]; - var vm = this.getViewModel(); - - var isOsd = (rec.data.host && (rec.data.type === 'osd') && (rec.data.id >= 0)); - - vm.set('isOsd', isOsd); - vm.set('downOsd', isOsd && rec.data.status === 'down'); - vm.set('upOsd', isOsd && rec.data.status !== 'down'); - vm.set('inOsd', isOsd && rec.data.in); - vm.set('outOsd', isOsd && !rec.data.in); - vm.set('osdid', isOsd ? rec.data.id : undefined); - vm.set('osdhost', isOsd ? rec.data.host : undefined); - }, - - render_status: function(value, metaData, rec) { - if (!value) { - return value; - } - var inout = rec.data['in'] ? 'in' : 'out'; - var updownicon = value === 'up' ? 'good fa-arrow-circle-up' : - 'critical fa-arrow-circle-down'; - - var inouticon = rec.data['in'] ? 'good fa-circle' : - 'warning fa-circle-o'; - - var text = value + ' / ' + - inout + ' '; - - return text; - }, - - render_wal: function(value, metaData, rec) { - if (!value && - rec.data.osdtype === 'bluestore' && - rec.data.type === 'osd') { - return 'N/A'; - } - return value; - }, - - render_version: function(value, metadata, rec) { - var vm = this.getViewModel(); - var versions = vm.get('versions'); - var icon = ""; - var version = value || ""; - var maxversion = vm.get('maxversion'); - if (value && value != maxversion) { - if (rec.data.type === 'host' || versions[rec.data.host] !== maxversion) { - icon = PVE.Utils.get_ceph_icon_html('HEALTH_UPGRADE'); - } else { - icon = PVE.Utils.get_ceph_icon_html('HEALTH_OLD'); - } - } else if (value && vm.get('mixedversions')) { - icon = PVE.Utils.get_ceph_icon_html('HEALTH_OK'); - } - - return icon + version; - }, - - render_osd_val: function(value, metaData, rec) { - return (rec.data.type === 'osd') ? value : ''; - }, - render_osd_weight: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return Ext.util.Format.number(value, '0.00###'); - }, - - render_osd_latency: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - let commit_ms = rec.data.commit_latency_ms, - apply_ms = rec.data.apply_latency_ms; - return apply_ms + ' / ' + commit_ms; - }, - - render_osd_size: function(value, metaData, rec) { - return this.render_osd_val(PVE.Utils.render_size(value), metaData, rec); - }, - - control: { - '#': { - selectionchange: 'set_selection_status' - } - }, - - init: function(view) { - var me = this; - var vm = this.getViewModel(); - - if (!view.pveSelNode.data.node) { - throw "no node name specified"; - } - - vm.set('nodename', view.pveSelNode.data.node); - - me.callParent(); - me.reload(); - } - }, - - stateful: true, - stateId: 'grid-ceph-osd', - rootVisible: false, - useArrows: true, - - columns: [ - { - xtype: 'treecolumn', - text: 'Name', - dataIndex: 'name', - width: 150 - }, - { - text: 'Type', - dataIndex: 'type', - hidden: true, - align: 'right', - width: 75 - }, - { - text: gettext("Class"), - dataIndex: 'device_class', - align: 'right', - width: 75 - }, - { - text: "OSD Type", - dataIndex: 'osdtype', - align: 'right', - width: 100 - }, - { - text: "Bluestore Device", - dataIndex: 'blfsdev', - align: 'right', - width: 75, - hidden: true - }, - { - text: "DB Device", - dataIndex: 'dbdev', - align: 'right', - width: 75, - hidden: true - }, - { - text: "WAL Device", - dataIndex: 'waldev', - align: 'right', - renderer: 'render_wal', - width: 75, - hidden: true - }, - { - text: 'Status', - dataIndex: 'status', - align: 'right', - renderer: 'render_status', - width: 120 - }, - { - text: gettext('Version'), - dataIndex: 'version', - align: 'right', - renderer: 'render_version' - }, - { - text: 'weight', - dataIndex: 'crush_weight', - align: 'right', - renderer: 'render_osd_weight', - width: 90 - }, - { - text: 'reweight', - dataIndex: 'reweight', - align: 'right', - renderer: 'render_osd_weight', - width: 90 - }, - { - text: gettext('Used') + ' (%)', - dataIndex: 'percent_used', - align: 'right', - renderer: function(value, metaData, rec) { - if (rec.data.type !== 'osd') { - return ''; - } - return Ext.util.Format.number(value, '0.00'); - }, - width: 100 - }, - { - text: gettext('Total'), - dataIndex: 'total_space', - align: 'right', - renderer: 'render_osd_size', - width: 100 - }, - { - text: 'Apply/Commit
Latency (ms)', - dataIndex: 'apply_latency_ms', - align: 'right', - renderer: 'render_osd_latency', - width: 120 - } - ], - - - tbar: { - items: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: 'reload' - }, - '-', - { - text: gettext('Create') + ': OSD', - handler: 'create_osd', - }, - { - text: Ext.String.format(gettext('Manage {0}'), 'Global Flags'), - handler: 'set_flags', - }, - '->', - { - xtype: 'tbtext', - data: { - osd: undefined - }, - bind: { - data: { - osd: "{osdid}" - } - }, - tpl: [ - '', - 'osd.{osd}:', - '', - gettext('No OSD selected'), - '' - ] - }, - { - text: gettext('Start'), - iconCls: 'fa fa-play', - disabled: true, - bind: { - disabled: '{!downOsd}' - }, - cmd: 'start', - handler: 'service_cmd' - }, - { - text: gettext('Stop'), - iconCls: 'fa fa-stop', - disabled: true, - bind: { - disabled: '{!upOsd}' - }, - cmd: 'stop', - handler: 'service_cmd' - }, - { - text: gettext('Restart'), - iconCls: 'fa fa-refresh', - disabled: true, - bind: { - disabled: '{!upOsd}' - }, - cmd: 'restart', - handler: 'service_cmd' - }, - '-', - { - text: 'Out', - iconCls: 'fa fa-circle-o', - disabled: true, - bind: { - disabled: '{!inOsd}' - }, - cmd: 'out', - handler: 'osd_cmd' - }, - { - text: 'In', - iconCls: 'fa fa-circle', - disabled: true, - bind: { - disabled: '{!outOsd}' - }, - cmd: 'in', - handler: 'osd_cmd' - }, - '-', - { - text: gettext('More'), - iconCls: 'fa fa-bars', - disabled: true, - bind: { - disabled: '{!isOsd}' - }, - menu: [ - { - text: gettext('Scrub'), - iconCls: 'fa fa-shower', - cmd: 'scrub', - handler: 'osd_cmd' - }, - { - text: gettext('Deep Scrub'), - iconCls: 'fa fa-bath', - cmd: 'scrub', - params: { - deep: 1, - }, - handler: 'osd_cmd' - }, - { - text: gettext('Destroy'), - itemId: 'remove', - iconCls: 'fa fa-fw fa-trash-o', - bind: { - disabled: '{!downOsd}' - }, - handler: 'destroy_osd' - } - ], - } - ] - }, - - fields: [ - 'name', 'type', 'status', 'host', 'in', 'id' , - { type: 'number', name: 'reweight' }, - { type: 'number', name: 'percent_used' }, - { type: 'integer', name: 'bytes_used' }, - { type: 'integer', name: 'total_space' }, - { type: 'integer', name: 'apply_latency_ms' }, - { type: 'integer', name: 'commit_latency_ms' }, - { type: 'string', name: 'device_class' }, - { type: 'string', name: 'osdtype' }, - { type: 'string', name: 'blfsdev' }, - { type: 'string', name: 'dbdev' }, - { type: 'string', name: 'waldev' }, - { type: 'string', name: 'version', calculate: function(data) { - return PVE.Utils.parse_ceph_version(data); - } }, - { type: 'string', name: 'iconCls', calculate: function(data) { - var iconMap = { - host: 'fa-building', - osd: 'fa-hdd-o', - root: 'fa-server', - }; - return 'fa x-fa-tree ' + iconMap[data.type]; - } }, - { type: 'number', name: 'crush_weight' } - ], -}); -Ext.define('PVE.node.CephMonMgrList', { - extend: 'Ext.container.Container', - xtype: 'pveNodeCephMonMgr', - - mixins: ['Proxmox.Mixin.CBind' ], - - onlineHelp: 'chapter_pveceph', - - defaults: { - border: false, - onlineHelp: 'chapter_pveceph', - flex: 1 - }, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - items: [ - { - xtype: 'pveNodeCephServiceList', - cbind: { pveSelNode: '{pveSelNode}' }, - type: 'mon', - additionalColumns: [ - { - header: gettext('Quorum'), - width: 70, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'quorum' - } - ], - stateId: 'grid-ceph-monitor', - showCephInstallMask: true, - title: gettext('Monitor') - }, - { - xtype: 'pveNodeCephServiceList', - type: 'mgr', - stateId: 'grid-ceph-manager', - cbind: { pveSelNode: '{pveSelNode}' }, - title: gettext('Manager') - } - ] -}); -Ext.define('PVE.node.CephCrushMap', { - extend: 'Ext.panel.Panel', - alias: ['widget.pveNodeCephCrushMap'], - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - stateful: true, - stateId: 'layout-ceph-crush', - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/crush', - - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.node.CephStatus', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephStatus', - - onlineHelp: 'chapter_pveceph', - - scrollable: true, - - bodyPadding: 5, - - layout: { - type: 'column' - }, - - defaults: { - padding: 5 - }, - - items: [ - { - xtype: 'panel', - title: gettext('Health'), - bodyPadding: 10, - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - minHeight: 230, - columnWidth: 1 - }, - 'width >= 1900': { - minHeight: 500, - columnWidth: 0.5 - } - }, - layout: { - type: 'hbox', - align: 'stretch' - }, - items: [ - { - flex: 1, - itemId: 'overallhealth', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - flex: 2, - itemId: 'warnings', - stateful: true, - stateId: 'ceph-status-warnings', - xtype: 'grid', - // since we load the store manually, - // to show the emptytext, we have to - // specify an empty store - store: { data:[] }, - emptyText: gettext('No Warnings/Errors'), - columns: [ - { - dataIndex: 'severity', - header: gettext('Severity'), - align: 'center', - width: 70, - renderer: function(value) { - var health = PVE.Utils.map_ceph_health[value]; - var classes = PVE.Utils.get_health_icon(health); - - return ''; - }, - sorter: { - sorterFn: function(a,b) { - var healthArr = ['HEALTH_ERR', 'HEALTH_WARN', 'HEALTH_OK']; - return healthArr.indexOf(b.data.severity) - healthArr.indexOf(a.data.severity); - } - } - }, - { - dataIndex: 'summary', - header: gettext('Summary'), - flex: 1 - }, - { - xtype: 'actioncolumn', - width: 40, - align: 'center', - tooltip: gettext('Detail'), - items: [ - { - iconCls: 'x-fa fa-info-circle', - handler: function(grid, rowindex, colindex, item, e, record) { - var win = Ext.create('Ext.window.Window', { - title: gettext('Detail'), - resizable: true, - modal: true, - width: 650, - height: 400, - layout: { - type: 'fit' - }, - items: [{ - scrollable: true, - padding: 10, - xtype: 'box', - html: [ - '' + Ext.htmlEncode(record.data.summary) + '', - '
' + Ext.htmlEncode(record.data.detail) + '
' - ] - }] - }); - win.show(); - } - } - ] - } - ] - } - ] - }, - { - xtype: 'pveCephStatusDetail', - itemId: 'statusdetail', - plugins: 'responsive', - responsiveConfig: { - 'width < 1900': { - columnWidth: 1, - minHeight: 250 - }, - 'width >= 1900': { - columnWidth: 0.5, - minHeight: 300 - } - }, - title: gettext('Status') - }, - { - title: gettext('Services'), - xtype: 'pveCephServices', - itemId: 'services', - plugins: 'responsive', - layout: { - type: 'hbox', - align: 'stretch' - }, - responsiveConfig: { - 'width < 1900': { - columnWidth: 1, - minHeight: 200 - }, - 'width >= 1900': { - columnWidth: 0.5, - minHeight: 200 - } - } - }, - { - xtype: 'panel', - title: gettext('Performance'), - columnWidth: 1, - bodyPadding: 5, - layout: { - type: 'hbox', - align: 'center' - }, - items: [ - { - flex: 1, - xtype: 'proxmoxGauge', - itemId: 'space', - title: gettext('Usage') - }, - { - flex: 2, - xtype: 'container', - defaults: { - padding: 0, - height: 100 - }, - items: [ - { - itemId: 'reads', - xtype: 'pveRunningChart', - title: gettext('Reads'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'writes', - xtype: 'pveRunningChart', - title: gettext('Writes'), - renderer: PVE.Utils.render_bandwidth - }, - { - itemId: 'iops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS', // do not localize - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'readiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Reads'), - renderer: Ext.util.Format.numberRenderer('0,000') - }, - { - itemId: 'writeiops', - xtype: 'pveRunningChart', - hidden: true, - title: 'IOPS: ' + gettext('Writes'), - renderer: Ext.util.Format.numberRenderer('0,000') - } - ] - } - ] - } - ], - - generateCheckData: function(health) { - var result = []; - var checks = health.checks || {}; - var keys = Ext.Object.getKeys(checks).sort(); - - Ext.Array.forEach(keys, function(key) { - var details = checks[key].detail || []; - result.push({ - id: key, - summary: checks[key].summary.message, - detail: Ext.Array.reduce( - checks[key].detail, - function(first, second) { - return first + '\n' + second.message; - }, - '' - ), - severity: checks[key].severity - }); - }); - - return result; - }, - - updateAll: function(store, records, success) { - if (!success || records.length === 0) { - return; - } - - var me = this; - var rec = records[0]; - me.status = rec.data; - - // add health panel - me.down('#overallhealth').updateHealth(PVE.Utils.render_ceph_health(rec.data.health || {})); - // add errors to gridstore - me.down('#warnings').getStore().loadRawData(me.generateCheckData(rec.data.health || {}), false); - - // update services - me.getComponent('services').updateAll(me.metadata || {}, rec.data); - - // update detailstatus panel - me.getComponent('statusdetail').updateAll(me.metadata || {}, rec.data); - - // add performance data - var used = rec.data.pgmap.bytes_used; - var total = rec.data.pgmap.bytes_total; - - var text = Ext.String.format(gettext('{0} of {1}'), - PVE.Utils.render_size(used), - PVE.Utils.render_size(total) - ); - - // update the usage widget - me.down('#space').updateValue(used/total, text); - - // TODO: logic for jewel (iops split in read/write) - - var iops = rec.data.pgmap.op_per_sec; - var readiops = rec.data.pgmap.read_op_per_sec; - var writeiops = rec.data.pgmap.write_op_per_sec; - var reads = rec.data.pgmap.read_bytes_sec || 0; - var writes = rec.data.pgmap.write_bytes_sec || 0; - - if (iops !== undefined && me.version !== 'hammer') { - me.change_version('hammer'); - } else if((readiops !== undefined || writeiops !== undefined) && me.version !== 'jewel') { - me.change_version('jewel'); - } - // update the graphs - me.reads.addDataPoint(reads); - me.writes.addDataPoint(writes); - me.iops.addDataPoint(iops); - me.readiops.addDataPoint(readiops); - me.writeiops.addDataPoint(writeiops); - }, - - change_version: function(version) { - var me = this; - me.version = version; - me.sp.set('ceph-version', version); - me.iops.setVisible(version === 'hammer'); - me.readiops.setVisible(version === 'jewel'); - me.writeiops.setVisible(version === 'jewel'); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - - me.callParent(); - var baseurl = '/api2/json' + (nodename ? '/nodes/' + nodename : '/cluster') + '/ceph'; - me.store = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + (nodename || 'cluster'), - interval: 5000, - proxy: { - type: 'proxmox', - url: baseurl + '/status' - } - }); - - me.metadatastore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-metadata-' + (nodename || 'cluster'), - interval: 15*1000, - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/ceph/metadata' - } - }); - - // save references for the updatefunction - me.iops = me.down('#iops'); - me.readiops = me.down('#readiops'); - me.writeiops = me.down('#writeiops'); - me.reads = me.down('#reads'); - me.writes = me.down('#writes'); - - // get ceph version - me.sp = Ext.state.Manager.getProvider(); - me.version = me.sp.get('ceph-version'); - me.change_version(me.version); - - var regex = new RegExp("not (installed|initialized)", "i"); - PVE.Utils.handleStoreErrorOrMask(me, me.store, regex, function(me, error){ - me.store.stopUpdate(); - PVE.Utils.showCephInstallOrMask(me, error.statusText, (nodename || 'localhost'), - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.store.startUpdate(); - }); - } - ); - }); - - me.mon(me.store, 'load', me.updateAll, me); - me.mon(me.metadatastore, 'load', function(store, records, success) { - if (!success || records.length < 1) { - return; - } - var rec = records[0]; - me.metadata = rec.data; - - // update services - me.getComponent('services').updateAll(rec.data, me.status || {}); - - // update detailstatus panel - me.getComponent('statusdetail').updateAll(rec.data, me.status || {}); - - }, me); - - me.on('destroy', me.store.stopUpdate); - me.on('destroy', me.metadatastore.stopUpdate); - me.store.startUpdate(); - me.metadatastore.startUpdate(); - } - -}); -Ext.define('PVE.ceph.StatusDetail', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveCephStatusDetail', - - layout: { - type: 'hbox', - align: 'stretch' - }, - - bodyPadding: '0 5', - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [{ - flex: 1, - itemId: 'osds', - maxHeight: 250, - scrollable: true, - padding: '0 10 5 10', - data: { - total: 0, - upin: 0, - upout: 0, - downin: 0, - downout: 0, - oldosds: [] - }, - tpl: [ - '

' + 'OSDs' + '

', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '
', - gettext('In'), - '', - gettext('Out'), - '
', - gettext('Up'), - '{upin}{upout}
', - gettext('Down'), - '{downin}{downout}
', - '
', - gettext('Total'), - ': {total}', - '

', - '', - ' ' + gettext('Outdated OSDs') + "
", - '
', - '', - '
osd.{id}:
', - '
{version}

', - '
', - '
', - '
', - '
' - ] - }, - { - flex: 1, - border: false, - itemId: 'pgchart', - xtype: 'polar', - height: 184, - innerPadding: 5, - insetPadding: 5, - colors: [ - '#CFCFCF', - '#21BF4B', - '#FFCC00', - '#FF6C59' - ], - store: { }, - series: [ - { - type: 'pie', - donut: 60, - angleField: 'count', - tooltip: { - trackMouse: true, - renderer: function(tooltip, record, ctx) { - var html = record.get('text'); - html += '
'; - record.get('states').forEach(function(state) { - html += '
' + - state.state_name + ': ' + state.count.toString(); - }); - tooltip.setHtml(html); - } - }, - subStyle: { - strokeStyle: false - } - } - ] - }, - { - flex: 1.6, - itemId: 'pgs', - padding: '0 10', - maxHeight: 250, - scrollable: true, - data: { - states: [] - }, - tpl: [ - '

' + 'PGs' + '

', - '', - '
{state_name}:
', - '
{count}

', - '
', - '
' - ] - }], - - // similar to mgr dashboard - pgstates: { - // clean - clean: 1, - active: 1, - - // working - activating: 2, - backfill_wait: 2, - backfilling: 2, - creating: 2, - deep: 2, - degraded: 2, - forced_backfill: 2, - forced_recovery: 2, - peered: 2, - peering: 2, - recovering: 2, - recovery_wait: 2, - repair: 2, - scrubbing: 2, - snaptrim: 2, - snaptrim_wait: 2, - - // error - backfill_toofull: 3, - backfill_unfound: 3, - down: 3, - incomplete: 3, - inconsistent: 3, - recovery_toofull: 3, - recovery_unfound: 3, - remapped: 3, - snaptrim_error: 3, - stale: 3, - undersized: 3 - }, - - statecategories: [ - { - text: gettext('Unknown'), - count: 0, - states: [], - cls: 'faded' - }, - { - text: gettext('Clean'), - cls: 'good' - }, - { - text: gettext('Working'), - cls: 'warning' - }, - { - text: gettext('Error'), - cls: 'critical' - } - ], - - updateAll: function(metadata, status) { - var me = this; - me.suspendLayout = true; - - var maxversion = "0"; - Object.values(metadata.version || {}).forEach(function(version) { - if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) { - maxversion = version; - } - }); - - var oldosds = []; - - if (metadata.osd) { - metadata.osd.forEach(function(osd) { - var version = PVE.Utils.parse_ceph_version(osd); - if (version != maxversion) { - oldosds.push({ - id: osd.id, - version: version - }); - } - }); - } - - var pgmap = status.pgmap || {}; - var health = status.health || {}; - var osdmap = status.osdmap || { osdmap: {} }; - - - // update pgs sorted - var pgs_by_state = pgmap.pgs_by_state || []; - pgs_by_state.sort(function(a,b){ - return (a.state_name < b.state_name)?-1:(a.state_name === b.state_name)?0:1; - }); - - me.statecategories.forEach(function(cat) { - cat.count = 0; - cat.states = []; - }); - - pgs_by_state.forEach(function(state) { - var i; - var states = state.state_name.split(/[^a-z]+/); - var result = 0; - for (i = 0; i < states.length; i++) { - if (me.pgstates[states[i]] > result) { - result = me.pgstates[states[i]]; - } - } - // for the list - state.cls = me.statecategories[result].cls; - - me.statecategories[result].count += state.count; - me.statecategories[result].states.push(state); - }); - - me.getComponent('pgchart').getStore().setData(me.statecategories); - me.getComponent('pgs').update({states: pgs_by_state}); - - var downinregex = /(\d+) osds down/; - var downin_osds = 0; - - // we collect monitor/osd information from the checks - Ext.Object.each(health.checks, function(key, value, obj) { - var found = null; - if (key === 'OSD_DOWN') { - found = value.summary.message.match(downinregex); - if (found !== null) { - downin_osds = parseInt(found[1],10); - } - } - }); - - // update osds counts - - var total_osds = osdmap.osdmap.num_osds || 0; - var in_osds = osdmap.osdmap.num_in_osds || 0; - var up_osds = osdmap.osdmap.num_up_osds || 0; - var out_osds = total_osds - in_osds; - var down_osds = total_osds - up_osds; - - var downout_osds = down_osds - downin_osds; - var upin_osds = in_osds - downin_osds; - var upout_osds = up_osds - upin_osds; - var osds = { - total: total_osds, - upin: upin_osds, - upout: upout_osds, - downin: downin_osds, - downout: downout_osds, - oldosds: oldosds - }; - var osdcomponent = me.getComponent('osds'); - osdcomponent.update(Ext.apply(osdcomponent.data, osds)); - - me.suspendLayout = false; - me.updateLayout(); - } -}); - -Ext.define('PVE.ceph.Services', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveCephServices', - - layout: { - type: 'hbox', - align: 'stretch' - }, - - bodyPadding: '0 5 20', - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [ - { - flex: 1, - xtype: 'pveCephServiceList', - itemId: 'mons', - title: gettext('Monitors') - }, - { - flex: 1, - xtype: 'pveCephServiceList', - itemId: 'mgrs', - title: gettext('Managers') - }, - { - flex: 1, - xtype: 'pveCephServiceList', - itemId: 'mdss', - title: gettext('Meta Data Servers') - } - ], - - updateAll: function(metadata, status) { - var me = this; - - var healthstates = { - 'HEALTH_UNKNOWN': 0, - 'HEALTH_ERR': 1, - 'HEALTH_WARN': 2, - 'HEALTH_UPGRADE': 3, - 'HEALTH_OLD': 4, - 'HEALTH_OK': 5 - }; - var healthmap = [ - 'HEALTH_UNKNOWN', - 'HEALTH_ERR', - 'HEALTH_WARN', - 'HEALTH_UPGRADE', - 'HEALTH_OLD', - 'HEALTH_OK' - ]; - var reduceFn = function(first, second) { - return first + '\n' + second.message; - }; - var maxversion = "00.0.00"; - Object.values(metadata.version || {}).forEach(function(version) { - if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) { - maxversion = version; - } - }); - var i; - var quorummap = (status && status.quorum_names) ? status.quorum_names : []; - var monmessages = {}; - var mgrmessages = {}; - var mdsmessages = {}; - if (status) { - if (status.health) { - Ext.Object.each(status.health.checks, function(key, value, obj) { - if (!Ext.String.startsWith(key, "MON_")) { - return; - } - - var i; - for (i = 0; i < value.detail.length; i++) { - var match = value.detail[i].message.match(/mon.([a-zA-Z0-9\-\.]+)/); - if (!match) { - continue; - } - var monid = match[1]; - - if (!monmessages[monid]) { - monmessages[monid] = { - worstSeverity: healthstates.HEALTH_OK, - messages: [] - }; - } - - - monmessages[monid].messages.push( - PVE.Utils.get_ceph_icon_html(value.severity, true) + - Ext.Array.reduce(value.detail, reduceFn, '') - ); - if (healthstates[value.severity] < monmessages[monid].worstSeverity) { - monmessages[monid].worstSeverity = healthstates[value.severity]; - } - } - }); - } - - if (status.mgrmap) { - mgrmessages[status.mgrmap.active_name] = "active"; - status.mgrmap.standbys.forEach(function(mgr) { - mgrmessages[mgr.name] = "standby"; - }); - } - - if (status.fsmap) { - status.fsmap.by_rank.forEach(function(mds) { - mdsmessages[mds.name] = 'rank: ' + mds.rank + "; " + mds.status; - }); - } - } - - var checks = { - mon: function(mon) { - if (quorummap.indexOf(mon.name) !== -1) { - mon.health = healthstates.HEALTH_OK; - } else { - mon.health = healthstates.HEALTH_ERR; - } - if (monmessages[mon.name]) { - if (monmessages[mon.name].worstSeverity < mon.health) { - mon.health = monmessages[mon.name].worstSeverity; - } - Array.prototype.push.apply(mon.messages, monmessages[mon.name].messages); - } - return mon; - }, - mgr: function(mgr) { - if (mgrmessages[mgr.name] === 'active') { - mgr.title = '' + mgr.title + ''; - mgr.statuses.push(gettext('Status') + ': active'); - } else if (mgrmessages[mgr.name] === 'standby') { - mgr.statuses.push(gettext('Status') + ': standby'); - } else if (mgr.health > healthstates.HEALTH_WARN) { - mgr.health = healthstates.HEALTH_WARN; - } - - return mgr; - }, - mds: function(mds) { - if (mdsmessages[mds.name]) { - mds.title = '' + mds.title + ''; - mds.statuses.push(gettext('Status') + ': ' + mdsmessages[mds.name]+""); - } else if (mds.addr !== Proxmox.Utils.unknownText) { - mds.statuses.push(gettext('Status') + ': standby'); - } - - return mds; - } - }; - - for (let type of ['mon', 'mgr', 'mds']) { - var ids = Object.keys(metadata[type] || {}); - me[type] = {}; - - for (let id of ids) { - var tmp = id.split('@'); - var name = tmp[0]; - var host = tmp[1]; - var result = { - id: id, - health: healthstates.HEALTH_OK, - statuses: [], - messages: [], - name: name, - title: metadata[type][id].name || name, - host: host, - version: PVE.Utils.parse_ceph_version(metadata[type][id]), - service: metadata[type][id].service, - addr: metadata[type][id].addr || metadata[type][id].addrs || Proxmox.Utils.unknownText - }; - - result.statuses = [ - gettext('Host') + ": " + result.host, - gettext('Address') + ": " + result.addr - ]; - - if (checks[type]) { - result = checks[type](result); - } - - if (result.service && !result.version) { - result.messages.push( - PVE.Utils.get_ceph_icon_html('HEALTH_UNKNOWN', true) + - gettext('Stopped') - ); - result.health = healthstates.HEALTH_UNKNOWN; - } - - if (!result.version && result.addr === Proxmox.Utils.unknownText) { - result.health = healthstates.HEALTH_UNKNOWN; - } - - if (result.version) { - result.statuses.push(gettext('Version') + ": " + result.version); - - if (result.version != maxversion) { - if (metadata.version[result.host] === maxversion) { - if (result.health > healthstates.HEALTH_OLD) { - result.health = healthstates.HEALTH_OLD; - } - result.messages.push( - PVE.Utils.get_ceph_icon_html('HEALTH_OLD', true) + - gettext('A newer version was installed but old version still running, please restart') - ); - } else { - if (result.health > healthstates.HEALTH_UPGRADE) { - result.health = healthstates.HEALTH_UPGRADE; - } - result.messages.push( - PVE.Utils.get_ceph_icon_html('HEALTH_UPGRADE', true) + - gettext('Other cluster members use a newer version of this service, please upgrade and restart') - ); - } - } - } - - result.statuses.push(''); // empty line - result.text = result.statuses.concat(result.messages).join('
'); - - result.health = healthmap[result.health]; - - me[type][id] = result; - } - } - - me.getComponent('mons').updateAll(Object.values(me.mon)); - me.getComponent('mgrs').updateAll(Object.values(me.mgr)); - me.getComponent('mdss').updateAll(Object.values(me.mds)); - } -}); - -Ext.define('PVE.ceph.ServiceList', { - extend: 'Ext.container.Container', - xtype: 'pveCephServiceList', - - style: { - 'text-align':'center' - }, - defaults: { - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - items: [ - { - itemId: 'title', - data: { - title: '' - }, - tpl: '

{title}

' - } - ], - - updateAll: function(list) { - var me = this; - me.suspendLayout = true; - - var i; - list.sort((a, b) => a.id > b.id ? 1 : a.id < b.id ? -1 : 0); - var ids = {}; - if (me.ids) { - me.ids.forEach(id => ids[id] = true); - } - for (i = 0; i < list.length; i++) { - var service = me.getComponent(list[i].id); - if (!service) { - // since services are already sorted, and - // we always have a sorted list - // we can add it at the service+1 position (because of the title) - service = me.insert(i+1, { - xtype: 'pveCephServiceWidget', - itemId: list[i].id - }); - if (!me.ids) { - me.ids = []; - } - me.ids.push(list[i].id); - } else { - delete ids[list[i].id]; - } - service.updateService(list[i].title, list[i].text, list[i].health); - } - - Object.keys(ids).forEach(function(id) { - me.remove(id); - }); - me.suspendLayout = false; - me.updateLayout(); - }, - - initComponent: function() { - var me = this; - me.callParent(); - me.getComponent('title').update({ - title: me.title - }); - } -}); - -/*jslint confusion: true*/ -Ext.define('PVE.ceph.ServiceWidget', { - extend: 'Ext.Component', - alias: 'widget.pveCephServiceWidget', - - userCls: 'monitor inline-block', - data: { - title: '0', - health: 'HEALTH_ERR', - text: '', - iconCls: PVE.Utils.get_health_icon() - }, - - tpl: [ - '{title}: ', - '' - ], - - updateService: function(title, text, health) { - var me = this; - - me.update(Ext.apply(me.data, { - health: health, - text: text, - title: title, - iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[health]) - })); - - if (me.tooltip) { - me.tooltip.setHtml(text); - } - }, - - listeners: { - destroy: function() { - var me = this; - if (me.tooltip) { - me.tooltip.destroy(); - delete me.tooltip; - } - }, - mouseenter: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (!me) { - return; - } - if (!me.tooltip) { - me.tooltip = Ext.create('Ext.tip.ToolTip', { - target: me.el, - trackMouse: true, - dismissDelay: 0, - renderTo: Ext.getBody(), - html: me.data.text - }); - } - me.tooltip.show(); - } - }, - mouseleave: { - element: 'el', - fn: function(events, element) { - var me = this.component; - if (me.tooltip) { - me.tooltip.destroy(); - delete me.tooltip; - } - } - } - } -}); -Ext.define('PVE.node.CephConfigDb', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveNodeCephConfigDb', - - border: false, - store: { - proxy: { - type: 'proxmox' - } - }, - - columns: [ - { - dataIndex: 'section', - text: 'WHO', - width: 100, - }, - { - dataIndex: 'mask', - text: 'MASK', - hidden: true, - width: 80, - }, - { - dataIndex: 'level', - hidden: true, - text: 'LEVEL', - }, - { - dataIndex: 'name', - flex: 1, - text: 'OPTION', - }, - { - dataIndex: 'value', - flex: 1, - text: 'VALUE', - }, - { - dataIndex: 'can_update_at_runtime', - text: 'Runtime Updatable', - hidden: true, - width: 80, - renderer: Proxmox.Utils.format_boolean - }, - ], - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.store.proxy.url = '/api2/json/nodes/' + nodename + '/ceph/configdb'; - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore()); - me.getStore().load(); - } -}); -Ext.define('PVE.node.CephConfig', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfig', - - bodyStyle: 'white-space:pre', - bodyPadding: 5, - border: false, - scrollable: true, - load: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - failure: function(response, opts) { - me.update(gettext('Error') + " " + response.htmlStatus); - var msg = response.htmlStatus; - PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.load(); - }); - } - ); - - }, - success: function(response, opts) { - var data = response.result.data; - me.update(Ext.htmlEncode(data)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - url: '/nodes/' + nodename + '/ceph/config', - listeners: { - activate: function() { - me.load(); - } - } - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.CephConfigCrush', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeCephConfigCrush', - - onlineHelp: 'chapter_pveceph', - - layout: 'border', - items: [{ - title: gettext('Configuration'), - xtype: 'pveNodeCephConfig', - region: 'center' - }, - { - title: 'Crush Map', // do not localize - xtype: 'pveNodeCephCrushMap', - region: 'east', - split: true, - width: '50%' - }, - { - title: gettext('Configuration Database'), - xtype: 'pveNodeCephConfigDb', - region: 'south', - split: true, - weight: -30, - height: '50%' - }], - - initComponent: function() { - var me = this; - me.defaults = { - pveSelNode: me.pveSelNode - }; - me.callParent(); - } -}); -Ext.define('PVE.ceph.Log', { - extend: 'Proxmox.panel.LogView', - xtype: 'cephLogView', - nodename: undefined, - failCallback: function(response) { - var me = this; - var msg = response.htmlStatus; - var windowShow = PVE.Utils.showCephInstallOrMask(me, msg, me.nodename, - function(win){ - me.mon(win, 'cephInstallWindowClosed', function(){ - me.loadTask.delay(200); - }); - } - ); - if (!windowShow) { - Proxmox.Utils.setErrorMask(me, msg); - } - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.ceph.CephInstallWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveCephInstallWizard', - mixins: ['Proxmox.Mixin.CBind'], - resizable: false, - nodename: undefined, - viewModel: { - data: { - nodename: '', - configuration: true, - isInstalled: false - } - }, - cbindData: { - nodename: undefined - }, - title: gettext('Setup'), - navigateNext: function() { - var tp = this.down('#wizcontent'); - var atab = tp.getActiveTab(); - - var next = tp.items.indexOf(atab) + 1; - var ntab = tp.items.getAt(next); - if (ntab) { - ntab.enable(); - tp.setActiveTab(ntab); - } - }, - setInitialTab: function (index) { - var tp = this.down('#wizcontent'); - var initialTab = tp.items.getAt(index); - initialTab.enable(); - tp.setActiveTab(initialTab); - }, - onShow: function() { - this.callParent(arguments); - var isInstalled = this.getViewModel().get('isInstalled'); - if (isInstalled) { - this.getViewModel().set('configuration', false); - this.setInitialTab(2); - } - }, - items: [ - { - title: gettext('Info'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'chapter_pveceph', - html: '

Ceph?

'+ - '

"Ceph is a unified, distributed storage system designed for excellent performance, reliability and scalability."

'+ - '

Ceph is currently not installed on this node, click on the next button below to start the installation.'+ - ' This wizard will guide you through the necessary steps, after the initial installation you will be offered to create an initial configuration.'+ - ' The configuration step is only needed once per cluster and will be skipped if a config is already present.

'+ - '

Please take a look at our documentation, by clicking the help button below, before starting the installation, '+ - 'if you want to gain deeper knowledge about Ceph visit ceph.com.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#back').hide(true); - this.up('pveCephInstallWizard').down('#next').setText(gettext('Start installation')); - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - this.up('pveCephInstallWizard').down('#next').setText(gettext('Next')); - } - } - }, - { - title: gettext('Installation'), - xtype: 'panel', - layout: 'fit', - cbind:{ - nodename: '{nodename}' - }, - viewModel: {}, // needed to inherit parent viewModel data - listeners: { - afterrender: function() { - var me = this; - if (this.getViewModel().get('isInstalled')) { - this.mask("Ceph is already installed, click next to create your configuration.",['pve-static-mask']); - } else { - me.down('pveNoVncConsole').fireEvent('activate'); - } - }, - activate: function() { - var me = this; - var nodename = me.nodename; - me.updateStore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'ceph-status-' + nodename, - interval: 1000, - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/ceph/status' - }, - listeners: { - load: function(rec, response, success, operation) { - - if (success) { - me.updateStore.stopUpdate(); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("not initialized", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',false); - me.down('textfield').setValue('success'); - } else if (operation.error.statusText.match("rados_connect failed", "i")) { - me.updateStore.stopUpdate(); - me.up('pveCephInstallWizard').getViewModel().set('configuration',true); - me.down('textfield').setValue('success'); - } else if (!operation.error.statusText.match("not installed", "i")) { - Proxmox.Utils.setErrorMask(me, operation.error.statusText); - } - } - } - }); - me.updateStore.startUpdate(); - }, - destroy: function() { - var me = this; - if (me.updateStore) { - me.updateStore.stopUpdate(); - } - } - }, - items: [ - { - itemId: 'jsconsole', - consoleType: 'cmd', - xtermjs: true, - xtype: 'pveNoVncConsole', - cbind:{ - nodename: '{nodename}' - }, - cmd: 'ceph_install' - }, - { - xtype: 'textfield', - name: 'installSuccess', - value: '', - allowBlank: false, - submitValue: false, - hidden: true - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Configuration'), - onlineHelp: 'chapter_pveceph', - cbind: { - nodename: '{nodename}' - }, - viewModel: { - data: { - replicas: undefined, - minreplicas: undefined - } - }, - listeners: { - activate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Next')); - }, - beforeshow: function() { - if (this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - this.mask("Configuration already initialized",['pve-static-mask']); - } else { - this.unmask(); - } - }, - deactivate: function() { - this.up('pveCephInstallWizard').down('#submit').setText(gettext('Finish')); - } - }, - column1: [ - { - xtype: 'displayfield', - value: gettext('Ceph cluster configuration') + ':' - }, - { - xtype: 'proxmoxNetworkSelector', - name: 'network', - value: '', - fieldLabel: 'Public Network IP/CIDR', - bind: { - allowBlank: '{configuration}' - }, - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'proxmoxNetworkSelector', - name: 'cluster-network', - fieldLabel: 'Cluster Network IP/CIDR', - allowBlank: true, - autoSelect: false, - emptyText: gettext('Same as Public Network'), - cbind: { - nodename: '{nodename}' - } - } - // FIXME: add hint about cluster network and/or reference user to docs?? - ], - column2: [ - { - xtype: 'displayfield', - value: gettext('First Ceph monitor') + ':' - }, - { - xtype: 'pveNodeSelector', - fieldLabel: gettext('Monitor node'), - name: 'mon-node', - selectCurNode: true, - allowBlank: false - }, - { - xtype: 'displayfield', - value: gettext('Additional monitors are recommended. They can be created at any time in the Monitor tab.'), - userCls: 'pmx-hint' - } - ], - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'size', - fieldLabel: 'Number of replicas', - bind: { - value: '{replicas}' - }, - maxValue: 7, - minValue: 2, - emptyText: '3' - }, - { - xtype: 'numberfield', - name: 'min_size', - fieldLabel: 'Minimum replicas', - bind: { - maxValue: '{replicas}', - value: '{minreplicas}' - }, - minValue: 2, - maxValue: 3, - setMaxValue: function(value) { - this.maxValue = Ext.Number.from(value, 2); - // allow enough to avoid split brains with max 'size', but more makes simply no sense - if (this.maxValue > 4) { - this.maxValue = 4; - } - this.toggleSpinners(); - this.validate(); - }, - emptyText: '2' - } - ], - onGetValues: function(values) { - ['cluster-network', 'size', 'min_size'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - return values; - }, - onSubmit: function() { - var me = this; - if (!this.up('pveCephInstallWizard').getViewModel().get('configuration')) { - var wizard = me.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - var monNode = kv['mon-node']; - delete kv['mon-node']; - var nodename = me.nodename; - delete kv.nodename; - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/ceph/init', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function() { - Proxmox.Utils.API2Request({ - url: '/nodes/' + monNode + '/ceph/mon/' + monNode, - waitMsgTarget: wizard, - method: 'POST', - success: function() { - me.up('pveCephInstallWizard').navigateNext(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - - } else { - me.up('pveCephInstallWizard').navigateNext(); - } - } - }, - { - title: gettext('Success'), - xtype: 'panel', - border: false, - bodyBorder: false, - onlineHelp: 'pve_ceph_install', - html: '

Installation successful!

'+ - '

The basic installation and configuration is completed, depending on your setup some of the following steps are required to start using Ceph:

'+ - '
  1. Install Ceph on other nodes
  2. '+ - '
  3. Create additional Ceph Monitors
  4. '+ - '
  5. Create Ceph OSDs
  6. '+ - '
  7. Create Ceph Pools
'+ - '

To learn more click on the help button below.

', - listeners: { - activate: function() { - // notify owning container that it should display a help button - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); - } - - var tp = this.up('#wizcontent'); - var idx = tp.items.indexOf(this)-1; - for(;idx >= 0;idx--) { - var nc = tp.items.getAt(idx); - if (nc) { - nc.disable(); - } - } - }, - deactivate: function() { - if (this.onlineHelp) { - Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); - } - } - }, - onSubmit: function() { - var wizard = this.up('pveCephInstallWizard'); - wizard.close(); - } - } - ] - }); -Ext.define('PVE.node.DiskList', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveNodeDiskList', - - emptyText: gettext('No Disks found'), - - stateful: true, - stateId: 'grid-node-disks', - - columns: [ - { - header: gettext('Device'), - width: 150, - sortable: true, - dataIndex: 'devpath' - }, - { - header: gettext('Type'), - width: 80, - sortable: true, - dataIndex: 'type', - renderer: function(v) { - if (v === 'ssd') { - return 'SSD'; - } else if (v === 'hdd') { - return 'Hard Disk'; - } else if (v === 'usb'){ - return 'USB'; - } else { - return gettext('Unknown'); - } - } - }, - { - header: gettext('Usage'), - width: 150, - sortable: false, - renderer: function(v, metaData, rec) { - if (rec) { - if (rec.data.osdid >= 0) { - var bluestore = ''; - if (rec.data.bluestore === 1) { - bluestore = ' (Bluestore)'; - } - return "Ceph osd." + rec.data.osdid.toString() + bluestore; - } - - var types = []; - if (rec.data.journals > 0) { - types.push('Journal'); - } - - if (rec.data.db > 0) { - types.push('DB'); - } - - if (rec.data.wal > 0) { - types.push('WAL'); - } - - if (types.length > 0) { - return 'Ceph (' + types.join(', ') + ')'; - } - } - - return v || Proxmox.Utils.noText; - }, - dataIndex: 'used' - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: 'GPT', - width: 60, - align: 'right', - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'gpt' - }, - { - header: gettext('Vendor'), - width: 100, - sortable: true, - hidden: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'vendor' - }, - { - header: gettext('Model'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'model' - }, - { - header: gettext('Serial'), - width: 200, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'serial' - }, - { - header: 'S.M.A.R.T.', - width: 100, - sortable: true, - renderer: Ext.String.htmlEncode, - dataIndex: 'health' - }, - { - header: 'Wearout', - width: 90, - sortable: true, - align: 'right', - dataIndex: 'wearout', - renderer: function(value) { - if (Ext.isNumeric(value)) { - return (100 - value).toString() + '%'; - } - return 'N/A'; - } - } - ], - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var store = Ext.create('Ext.data.Store', { - storeid: 'node-disk-list' + nodename, - model: 'node-disk-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/list" - }, - sorters: [ - { - property : 'dev', - direction: 'ASC' - } - ] - }); - - var reloadButton = Ext.create('Proxmox.button.Button', { - text: gettext('Reload'), - handler: function() { - me.store.load(); - } - }); - - var smartButton = Ext.create('Proxmox.button.Button', { - text: gettext('Show S.M.A.R.T. values'), - selModel: sm, - enableFn: function() { - return !!sm.getSelection().length; - }, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - }); - - var initButton = Ext.create('Proxmox.button.Button', { - text: gettext('Initialize Disk with GPT'), - selModel: sm, - enableFn: function() { - var selection = sm.getSelection(); - - if (!selection.length || selection[0].data.used) { - return false; - } else { - return true; - } - }, - disabled: true, - - handler: function() { - var rec = sm.getSelection()[0]; - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + nodename + '/disks/initgpt', - waitMsgTarget: me, - method: 'POST', - params: { disk: rec.data.devpath}, - failure: function(response, options) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid - }); - win.show(); - } - }); - } - }); - - me.loadCount = 1; // avoid duplicate loadmask - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ reloadButton, smartButton, initButton ], - listeners: { - itemdblclick: function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.DiskSmartWindow', { - nodename: nodename, - dev: rec.data.devpath - }); - win.show(); - } - } - }); - - - me.callParent(); - me.store.load(); - } -}, function() { - - Ext.define('node-disk-list', { - extend: 'Ext.data.Model', - fields: [ 'devpath', 'used', { name: 'size', type: 'number'}, - {name: 'osdid', type: 'number'}, - 'vendor', 'model', 'serial', 'rpm', 'type', 'health', 'wearout' ], - idProperty: 'devpath' - }); -}); - -Ext.define('PVE.DiskSmartWindow', { - extend: 'Ext.window.Window', - alias: 'widget.pveSmartWindow', - - modal: true, - - items: [ - { - xtype: 'gridpanel', - layout: { - type: 'fit' - }, - emptyText: gettext('No S.M.A.R.T. Values'), - scrollable: true, - flex: 1, - itemId: 'smarts', - reserveScrollbar: true, - columns: [ - { text: 'ID', dataIndex: 'id', width: 50 }, - { text: gettext('Attribute'), flex: 1, dataIndex: 'name', renderer: Ext.String.htmlEncode }, - { text: gettext('Value'), dataIndex: 'raw', renderer: Ext.String.htmlEncode }, - { text: gettext('Normalized'), dataIndex: 'value', width: 60}, - { text: gettext('Threshold'), dataIndex: 'threshold', width: 60}, - { text: gettext('Worst'), dataIndex: 'worst', width: 60}, - { text: gettext('Flags'), dataIndex: 'flags'}, - { text: gettext('Failing'), dataIndex: 'fail', renderer: Ext.String.htmlEncode } - ] - }, - { - xtype: 'component', - itemId: 'text', - layout: { - type: 'fit' - }, - hidden: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace' - } - } - ], - - buttons: [ - { - text: gettext('Reload'), - name: 'reload', - handler: function() { - var me = this; - me.up('window').store.reload(); - } - }, - { - text: gettext('Close'), - name: 'close', - handler: function() { - var me = this; - me.up('window').close(); - } - } - ], - - layout: { - type: 'vbox', - align: 'stretch' - }, - width: 800, - height: 500, - minWidth: 600, - minHeight: 400, - bodyPadding: 5, - title: gettext('S.M.A.R.T. Values'), - - initComponent: function() { - var me = this; - - var nodename = me.nodename; - if (!nodename) { - throw "no node name specified"; - } - - var dev = me.dev; - if (!dev) { - throw "no device specified"; - } - - me.store = Ext.create('Ext.data.Store', { - model: 'disk-smart', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/disks/smart?disk=" + dev - } - }); - - me.callParent(); - var grid = me.down('#smarts'); - var text = me.down('#text'); - - Proxmox.Utils.monStoreErrors(grid, me.store); - me.mon(me.store, 'load', function(s, records, success) { - if (success && records.length > 0) { - var rec = records[0]; - switch (rec.data.type) { - case 'text': - grid.setVisible(false); - text.setVisible(true); - text.setHtml(Ext.String.htmlEncode(rec.data.text)); - break; - default: - // includes 'ata' - // cannot use empty case because - // of jslint - grid.setVisible(true); - text.setVisible(false); - grid.setStore(rec.attributes()); - break; - } - } - }); - - me.store.load(); - } -}, function() { - - Ext.define('disk-smart', { - extend: 'Ext.data.Model', - fields: [ - { name:'health'}, - { name:'type'}, - { name:'text'} - ], - hasMany: {model: 'smart-attribute', name: 'attributes'} - }); - Ext.define('smart-attribute', { - extend: 'Ext.data.Model', - fields: [ - { name:'id', type:'number' }, 'name', 'value', 'worst', 'threshold', 'flags', 'fail', 'raw' - ] - }); -}); -Ext.define('PVE.node.CreateLVM', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVM', - - subject: 'LVM Volume Group', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvm", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMList', { - extend: 'Ext.tree.Panel', - xtype: 'pveLVMList', - emptyText: gettext('No Volume Groups found'), - stateful: true, - stateId: 'grid-node-lvm', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Number of LVs'), - dataIndex: 'lvcount', - width: 150, - align: 'right' - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Volume Group', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVM', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/lvm", - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'size', 'free', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - txt += (data.leaf) ? 'hdd-o' : 'object-group'; - return txt; - } - }, - { - type: 'number', - name: 'usage', - calculate: function(data) { - return ((data.size-data.free)/data.size); - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.CreateLVMThin', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateLVMThin', - - subject: 'LVM Thinpool', - - showProgress: true, - - onlineHelp: 'chapter_lvm', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/lvmthin", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.LVMThinList', { - extend: 'Ext.grid.Panel', - xtype: 'pveLVMThinList', - - emptyText: gettext('No thinpools found'), - stateful: true, - stateId: 'grid-node-lvmthin', - columns: [ - { - text: gettext('Name'), - dataIndex: 'lv', - flex: 1 - }, - { - header: gettext('Usage'), - width: 110, - dataIndex: 'usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Size'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'lv_size' - }, - { - header: gettext('Used'), - width: 100, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'used' - }, - { - header: gettext('Metadata Usage'), - width: 120, - dataIndex: 'metadata_usage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Metadata Size'), - width: 120, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_size' - }, - { - header: gettext('Metadata Used'), - width: 125, - align: 'right', - sortable: true, - renderer: Proxmox.Utils.format_size, - dataIndex: 'metadata_used' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Thinpool', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateLVMThin', { - nodename: me.nodename, - taskDone: function() { - me.reload(); - } - }).show(); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['lv', 'lv_size', 'used', 'metadata_size', 'metadata_used', - { - type: 'number', - name: 'usage', - calculate: function(data) { - return data.used/data.lv_size; - } - }, - { - type: 'number', - name: 'metadata_usage', - calculate: function(data) { - return data.metadata_used/data.metadata_size; - } - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/lvmthin' - }, - sorters: 'lv' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.CreateDirectory', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateDirectory', - - subject: Proxmox.Utils.directoryText, - - showProgress: true, - - onlineHelp: 'chapter_storage', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - Ext.applyIf(me, { - url: "/nodes/" + me.nodename + "/disks/directory", - method: 'POST', - items: [ - { - xtype: 'pveDiskSelector', - name: 'device', - nodename: me.nodename, - diskType: 'unused', - fieldLabel: gettext('Disk'), - allowBlank: false - }, - { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['ext4', 'ext4'], - ['xfs', 'xfs'] - ], - fieldLabel: gettext('Filesystem'), - name: 'filesystem', - value: '', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.Directorylist', { - extend: 'Ext.grid.Panel', - xtype: 'pveDirectoryList', - - stateful: true, - stateId: 'grid-node-directory', - columns: [ - { - text: gettext('Path'), - dataIndex: 'path', - flex: 1 - }, - { - header: gettext('Device'), - flex: 1, - dataIndex: 'device' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'type' - }, - { - header: gettext('Options'), - width: 100, - dataIndex: 'options' - }, - { - header: gettext('Unit File'), - hidden: true, - dataIndex: 'unitfile' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': Directory', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateDirectory', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - } - ], - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['path', 'device', 'type', 'options', 'unitfile' ], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/directory' - }, - sorters: 'path' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -/*jslint confusion: true*/ -Ext.define('PVE.node.CreateZFS', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCreateZFS', - - subject: 'ZFS', - - showProgress: true, - - onlineHelp: 'chapter_zfs', - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.isCreate = true; - - var update_disklist = function() { - var grid = me.down('#disklist'); - var disks = grid.getSelection(); - - var val = []; - disks.sort(function(a,b) { - var aorder = a.get('order') || 0; - var border = b.get('order') || 0; - return (aorder - border); - }); - - disks.forEach(function(disk) { - val.push(disk.get('devpath')); - }); - - me.down('field[name=devices]').setValue(val.join(',')); - }; - - Ext.apply(me, { - url: '/nodes/' + me.nodename + '/disks/zfs', - method: 'POST', - items: [ - { - xtype: 'inputpanel', - onGetValues: function(values) { - return values; - }, - column1: [ - { - xtype: 'textfield', - hidden: true, - name: 'devices', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - name: 'name', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'add_storage', - fieldLabel: gettext('Add Storage'), - value: '1' - } - ], - column2: [ - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('RAID Level'), - name: 'raidlevel', - value: 'single', - comboItems: [ - ['single', gettext('Single Disk')], - ['mirror', 'Mirror'], - ['raid10', 'RAID10'], - ['raidz', 'RAIDZ'], - ['raidz2', 'RAIDZ2'], - ['raidz3', 'RAIDZ3'] - ] - }, - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Compression'), - name: 'compression', - value: 'on', - comboItems: [ - ['on', 'on'], - ['off', 'off'], - ['gzip', 'gzip'], - ['lz4', 'lz4'], - ['lzjb', 'lzjb'], - ['zle', 'zle'] - ] - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('ashift'), - minValue: 9, - maxValue: 16, - value: '12', - name: 'ashift' - } - ], - columnB: [ - { - xtype: 'grid', - height: 200, - emptyText: gettext('No Disks unused'), - itemId: 'disklist', - selModel: 'checkboxmodel', - listeners: { - selectionchange: update_disklist - }, - store: { - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/disks/list?type=unused' - } - }, - columns: [ - { - text: gettext('Device'), - dataIndex: 'devpath', - flex: 1 - }, - { - text: gettext('Serial'), - dataIndex: 'serial' - }, - { - text: gettext('Size'), - dataIndex: 'size', - renderer: PVE.Utils.render_size - }, - { - header: gettext('Order'), - xtype: 'widgetcolumn', - dataIndex: 'order', - sortable: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 1, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('order', value); - update_disklist(record); - } - } - } - } - ] - } - ] - }, - { - xtype: 'displayfield', - padding: '5 0 0 0', - userCls: 'pmx-hint', - value: 'Note: ZFS is not compatible with disks backed by a hardware ' + - 'RAID controller. For details see ' + - 'the reference documentation.', - } - ] - }); - - me.callParent(); - me.down('#disklist').getStore().load(); - } -}); - -Ext.define('PVE.node.ZFSDevices', { - extend: 'Ext.tree.Panel', - xtype: 'pveZFSDevices', - stateful: true, - stateId: 'grid-node-zfsstatus', - columns: [ - { - xtype: 'treecolumn', - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - text: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'state' - }, - { - text: 'READ', - dataIndex: 'read' - }, - { - text: 'WRITE', - dataIndex: 'write' - }, - { - text: 'CKSUM', - dataIndex: 'cksum' - }, - { - text: gettext('Message'), - dataIndex: 'msg' - } - ], - - rootVisible: true, - - reload: function() { - var me = this; - var sm = me.getSelectionModel(); - Proxmox.Utils.API2Request({ - url: "/nodes/" + me.nodename + "/disks/zfs/" + me.zpool, - waitMsgTarget: me, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - }, - success: function(response, opts) { - sm.deselectAll(); - me.setRootNode(response.result.data); - me.expandAll(); - } - }); - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - var sm = Ext.create('Ext.selection.TreeModel', {}); - - Ext.apply(me, { - selModel: sm, - fields: ['name', 'status', - { - type: 'string', - name: 'iconCls', - calculate: function(data) { - var txt = 'fa x-fa-tree fa-'; - if (data.leaf) { - return txt + 'hdd-o'; - } - } - } - ], - sorters: 'name' - }); - - me.callParent(); - - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSStatus', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveZFSStatus', - layout: 'fit', - border: false, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.zpool) { - throw "no zpool specified"; - } - - me.url = "/api2/extjs/nodes/" + me.nodename + "/disks/zfs/" + me.zpool; - - me.rows = { - scan: { - header: gettext('Scan') - }, - status: { - header: gettext('Status') - }, - action: { - header: gettext('Action') - }, - errors: { - header: gettext('Errors') - } - }; - - me.callParent(); - me.reload(); - } -}); - -Ext.define('PVE.node.ZFSList', { - extend: 'Ext.grid.Panel', - xtype: 'pveZFSList', - - stateful: true, - stateId: 'grid-node-zfs', - columns: [ - { - text: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Size'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - }, - { - header: gettext('Free'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'free' - }, - { - header: gettext('Allocated'), - renderer: Proxmox.Utils.format_size, - dataIndex: 'alloc' - }, - { - header: gettext('Fragmentation'), - renderer: function(value) { - return value.toString() + '%'; - }, - dataIndex: 'frag' - }, - { - header: gettext('Health'), - renderer: PVE.Utils.render_zfs_health, - dataIndex: 'health' - }, - { - header: gettext('Deduplication'), - hidden: true, - renderer: function(value) { - return value.toFixed(2).toString() + 'x'; - }, - dataIndex: 'dedup' - } - ], - - rootVisible: false, - useArrows: true, - - tbar: [ - { - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - var me = this.up('panel'); - me.reload(); - } - }, - { - text: gettext('Create') + ': ZFS', - handler: function() { - var me = this.up('panel'); - var win = Ext.create('PVE.node.CreateZFS', { - nodename: me.nodename - }).show(); - win.on('destroy', function() { me.reload(); }); - } - }, - { - text: gettext('Detail'), - itemId: 'detailbtn', - disabled: true, - handler: function() { - var me = this.up('panel'); - var selection = me.getSelection(); - if (selection.length < 1) { - return; - } - me.show_detail(selection[0].get('name')); - } - } - ], - - show_detail: function(zpool) { - var me = this; - - var detailsgrid = Ext.create('PVE.node.ZFSStatus', { - layout: 'fit', - nodename: me.nodename, - flex: 0, - zpool: zpool - }); - - var devicetree = Ext.create('PVE.node.ZFSDevices', { - title: gettext('Devices'), - nodename: me.nodename, - flex: 1, - zpool: zpool - }); - - - var win = Ext.create('Ext.window.Window', { - modal: true, - width: 800, - height: 400, - resizable: true, - layout: 'fit', - title: gettext('Status') + ': ' + zpool, - items:[{ - xtype: 'panel', - region: 'center', - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [detailsgrid, devicetree], - tbar: [{ - text: gettext('Reload'), - iconCls: 'fa fa-refresh', - handler: function() { - - devicetree.reload(); - detailsgrid.reload(); - } - }] - }] - }).show(); - }, - - set_button_status: function() { - var me = this; - var selection = me.getSelection(); - me.down('#detailbtn').setDisabled(selection.length === 0); - }, - - reload: function() { - var me = this; - me.store.load(); - me.store.sort(); - }, - - listeners: { - activate: function() { - var me = this; - me.reload(); - }, - selectionchange: function() { - this.set_button_status(); - }, - itemdblclick: function(grid, record) { - var me = this; - me.show_detail(record.get('name')); - } - }, - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - Ext.apply(me, { - store: { - fields: ['name', 'size', 'free', 'alloc', 'dedup', 'frag', 'health'], - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + me.nodename + '/disks/zfs' - }, - sorters: 'name' - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, me.getStore(), true); - me.reload(); - } -}); - -Ext.define('PVE.node.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveNodeStatus', - - height: 300, - bodyPadding: '20 15 20 15', - - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 15 5 15' - }, - - items: [ - { - itemId: 'cpu', - iconCls: 'fa fa-fw pve-itype-icon-processor pve-icon', - title: gettext('CPU usage'), - valueField: 'cpu', - maxField: 'cpuinfo', - renderer: PVE.Utils.render_node_cpu_usage - }, - { - itemId: 'wait', - iconCls: 'fa fa-fw fa-clock-o', - title: gettext('IO delay'), - valueField: 'wait', - rowspan: 2 - }, - { - itemId: 'load', - iconCls: 'fa fa-fw fa-tasks', - title: gettext('Load average'), - printBar: false, - textField: 'loadavg' - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - iconCls: 'fa fa-fw pve-itype-icon-memory pve-icon', - itemId: 'memory', - title: gettext('RAM usage'), - valueField: 'memory', - maxField: 'memory', - renderer: PVE.Utils.render_node_size_usage - }, - { - itemId: 'ksm', - printBar: false, - title: gettext('KSM sharing'), - textField: 'ksm', - renderer: function(record) { - return PVE.Utils.render_size(record.shared); - }, - padding: '0 15 10 15' - }, - { - iconCls: 'fa fa-fw fa-hdd-o', - itemId: 'rootfs', - title: gettext('HD space') + '(root)', - valueField: 'rootfs', - maxField: 'rootfs', - renderer: PVE.Utils.render_node_size_usage - }, - { - iconCls: 'fa fa-fw fa-refresh', - itemId: 'swap', - printSize: true, - title: gettext('SWAP usage'), - valueField: 'swap', - maxField: 'swap', - renderer: PVE.Utils.render_node_size_usage - }, - { - xtype: 'box', - colspan: 2, - padding: '0 0 20 0' - }, - { - itemId: 'cpus', - colspan: 2, - printBar: false, - title: gettext('CPU(s)'), - textField: 'cpuinfo', - renderer: function(cpuinfo) { - return cpuinfo.cpus + " x " + cpuinfo.model + " (" + - cpuinfo.sockets.toString() + " " + - (cpuinfo.sockets > 1 ? - gettext('Sockets') : - gettext('Socket') - ) + ")"; - }, - value: '' - }, - { - itemId: 'kversion', - colspan: 2, - title: gettext('Kernel Version'), - printBar: false, - textField: 'kversion', - value: '' - }, - { - itemId: 'version', - colspan: 2, - printBar: false, - title: gettext('PVE Manager Version'), - textField: 'pveversion', - value: '' - } - ], - - updateTitle: function() { - var me = this; - var uptime = Proxmox.Utils.render_uptime(me.getRecordValue('uptime')); - me.setTitle(me.pveSelNode.data.node + ' (' + gettext('Uptime') + ': ' + uptime + ')'); - } - -}); -Ext.define('PVE.node.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveNodeSummary', - - scrollable: true, - bodyPadding: 5, - - showVersions: function() { - var me = this; - - // Note: we use simply text/html here, because ExtJS grid has problems - // with cut&paste - - var nodename = me.pveSelNode.data.node; - - var view = Ext.createWidget('component', { - autoScroll: true, - padding: 5, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace' - } - }); - - var win = Ext.create('Ext.window.Window', { - title: gettext('Package versions'), - width: 600, - height: 400, - layout: 'fit', - modal: true, - items: [ view ] - }); - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: "/nodes/" + nodename + "/apt/versions", - method: 'GET', - failure: function(response, opts) { - win.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - win.show(); - var text = ''; - - Ext.Array.each(response.result.data, function(rec) { - var version = "not correctly installed"; - var pkg = rec.Package; - if (rec.OldVersion && rec.CurrentState === 'Installed') { - version = rec.OldVersion; - } - if (rec.RunningKernel) { - text += pkg + ': ' + version + ' (running kernel: ' + - rec.RunningKernel + ')\n'; - } else if (rec.ManagerVersion) { - text += pkg + ': ' + version + ' (running version: ' + - rec.ManagerVersion + ')\n'; - } else { - text += pkg + ': ' + version + '\n'; - } - }); - - view.update(Ext.htmlEncode(text)); - } - }); - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - if (!me.statusStore) { - throw "no status storage specified"; - } - - var rstore = me.statusStore; - - var version_btn = new Ext.Button({ - text: gettext('Package versions'), - handler: function(){ - Proxmox.Utils.checked_command(function() { me.showVersions(); }); - } - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/rrddata", - model: 'pve-rrd-node' - }); - - Ext.apply(me, { - tbar: [version_btn, '->', { xtype: 'proxmoxRRDTypeSelector' } ], - items: [ - { - xtype: 'container', - itemId: 'itemcontainer', - layout: 'column', - minWidth: 700, - defaults: { - minHeight: 320, - padding: 5, - columnWidth: 1 - }, - items: [ - { - xtype: 'pveNodeStatus', - rstore: rstore, - width: 770, - pveSelNode: me.pveSelNode - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('CPU usage'), - fields: ['cpu','iowait'], - fieldTitles: [gettext('CPU usage'), gettext('IO delay')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Server load'), - fields: ['loadavg'], - fieldTitles: [gettext('Load average')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Memory usage'), - fields: ['memtotal','memused'], - fieldTitles: [gettext('Total'), gettext('RAM usage')], - store: rrdstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Network traffic'), - fields: ['netin','netout'], - store: rrdstore - } - ], - listeners: { - resize: function(panel) { - PVE.Utils.updateColumns(panel); - }, - }, - }, - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - - let sp = Ext.state.Manager.getProvider(); - me.mon(sp, 'statechange', function(provider, key, value) { - if (key !== 'summarycolumns') { - return; - } - PVE.Utils.updateColumns(me.getComponent('itemcontainer')); - }); - } -}); -/*global Blob*/ -Ext.define('PVE.node.SubscriptionKeyEdit', { - extend: 'Proxmox.window.Edit', - title: gettext('Upload Subscription Key'), - width: 300, - items: { - xtype: 'textfield', - name: 'key', - value: '', - fieldLabel: gettext('Subscription Key') - }, - initComponent : function() { - var me = this; - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.node.Subscription', { - extend: 'Proxmox.grid.ObjectGrid', - - alias: ['widget.pveNodeSubscription'], - - onlineHelp: 'getting_help', - - viewConfig: { - enableTextSelection: true - }, - - showReport: function() { - var me = this; - var nodename = me.pveSelNode.data.node; - - var getReportFileName = function() { - var now = Ext.Date.format(new Date(), 'D-d-F-Y-G-i'); - return me.nodename + '-report-' + now + '.txt'; - }; - - var view = Ext.createWidget('component', { - itemId: 'system-report-view', - scrollable: true, - style: { - 'background-color': 'white', - 'white-space': 'pre', - 'font-family': 'monospace', - padding: '5px' - } - }); - - var reportWindow = Ext.create('Ext.window.Window', { - title: gettext('System Report'), - width: 1024, - height: 600, - layout: 'fit', - modal: true, - buttons: [ - '->', - { - text: gettext('Download'), - handler: function() { - var fileContent = Ext.String.htmlDecode(reportWindow.getComponent('system-report-view').html); - var fileName = getReportFileName(); - - // Internet Explorer - if (window.navigator.msSaveOrOpenBlob) { - navigator.msSaveOrOpenBlob(new Blob([fileContent]), fileName); - } else { - var element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset=utf-8,' - + encodeURIComponent(fileContent)); - element.setAttribute('download', fileName); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - } - } - } - ], - items: view - }); - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/nodes/' + me.nodename + '/report', - method: 'GET', - waitMsgTarget: me, - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var report = Ext.htmlEncode(response.result.data); - reportWindow.show(); - view.update(report); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var reload = function() { - me.rstore.load(); - }; - - var baseurl = '/nodes/' + me.nodename + '/subscription'; - - var render_status = function(value) { - - var message = me.getObjectValue('message'); - - if (message) { - return value + ": " + message; - } - return value; - }; - - var rows = { - productname: { - header: gettext('Type') - }, - key: { - header: gettext('Subscription Key') - }, - status: { - header: gettext('Status'), - renderer: render_status - }, - message: { - visible: false - }, - serverid: { - header: gettext('Server ID') - }, - sockets: { - header: gettext('Sockets') - }, - checktime: { - header: gettext('Last checked'), - renderer: Proxmox.Utils.render_timestamp - }, - nextduedate: { - header: gettext('Next due date') - } - }; - - Ext.apply(me, { - url: '/api2/json' + baseurl, - cwidth1: 170, - tbar: [ - { - text: gettext('Upload Subscription Key'), - handler: function() { - var win = Ext.create('PVE.node.SubscriptionKeyEdit', { - url: '/api2/extjs/' + baseurl - }); - win.show(); - win.on('destroy', reload); - } - }, - { - text: gettext('Check'), - handler: function() { - Proxmox.Utils.API2Request({ - params: { force: 1 }, - url: baseurl, - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - callback: reload - }); - } - }, - { - text: gettext('System Report'), - handler: function() { - Proxmox.Utils.checked_command(function (){ me.showReport(); }); - } - } - ], - rows: rows, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.node.CertificateView', { - extend: 'Ext.container.Container', - xtype: 'pveCertificatesView', - - onlineHelp: 'sysadmin_certificate_management', - - mixins: ['Proxmox.Mixin.CBind' ], - - items: [ - { - xtype: 'pveCertView', - border: 0, - cbind: { - nodename: '{nodename}' - } - }, - { - xtype: 'pveACMEView', - border: 0, - cbind: { - nodename: '{nodename}' - } - } - ] - -}); - -Ext.define('PVE.node.CertificateViewer', { - extend: 'Proxmox.window.Edit', - - title: gettext('Certificate'), - - fieldDefaults: { - labelWidth: 120 - }, - width: 800, - resizable: true, - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Name'), - name: 'filename' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Fingerprint'), - name: 'fingerprint' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Issuer'), - name: 'issuer' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject'), - name: 'subject' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Public Key Type'), - name: 'public-key-type' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Public Key Size'), - name: 'public-key-bits' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Valid Since'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notbefore' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Expires'), - renderer: Proxmox.Utils.render_timestamp, - name: 'notafter' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Subject Alternative Names'), - name: 'san', - renderer: PVE.Utils.render_san - }, - { - xtype: 'textarea', - editable: false, - grow: true, - growMax: 200, - fieldLabel: gettext('Certificate'), - name: 'pem' - } - ], - - initComponent: function() { - var me = this; - - if (!me.cert) { - throw "no cert given"; - } - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/info'; - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - if (Ext.isArray(response.result.data)) { - Ext.Array.each(response.result.data, function(item) { - if (item.filename === me.cert) { - me.setValues(item); - return false; - } - }); - } - } - }); - } -}); - -Ext.define('PVE.node.CertUpload', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCertUpload', - - title: gettext('Upload Custom Certificate'), - resizable: false, - isCreate: true, - submitText: gettext('Upload'), - method: 'POST', - width: 600, - - apiCallDone: function(success, response, options) { - if (!success) { - return; - } - - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - items: [ - { - fieldLabel: gettext('Private Key (Optional)'), - labelAlign: 'top', - emptyText: gettext('No change'), - name: 'key', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=key]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'box', - autoEl: 'hr' - }, - { - fieldLabel: gettext('Certificate Chain'), - labelAlign: 'top', - allowBlank: false, - name: 'certificates', - xtype: 'textarea' - }, - { - xtype: 'filebutton', - text: gettext('From File'), - listeners: { - change: function(btn, e, value) { - var me = this.up('form'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - me.down('field[name=certificates]').setValue(res); - }); - }); - btn.reset(); - } - } - }, - { - xtype: 'hidden', - name: 'restart', - value: '1' - }, - { - xtype: 'hidden', - name: 'force', - value: '1' - } - ], - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/nodes/' + me.nodename + '/certificates/custom'; - - me.callParent(); - } -}); - -Ext.define('pve-certificate', { - extend: 'Ext.data.Model', - - fields: [ 'filename', 'fingerprint', 'issuer', 'notafter', 'notbefore', 'subject', 'san', 'public-key-bits', 'public-key-type' ], - idProperty: 'filename' -}); - -Ext.define('PVE.node.Certificates', { - extend: 'Ext.grid.Panel', - xtype: 'pveCertView', - - tbar: [ - { - xtype: 'button', - text: gettext('Upload Custom Certificate'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.CertUpload', { - nodename: me.nodename - }); - win.show(); - win.on('destroy', me.reload, me); - } - }, - { - xtype: 'button', - itemId: 'deletebtn', - text: gettext('Delete Custom Certificate'), - handler: function() { - var me = this.up('grid'); - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/certificates/custom?restart=1', - method: 'DELETE', - success: function(response, opt) { - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - '-', - { - xtype: 'proxmoxButton', - itemId: 'viewbtn', - disabled: true, - text: gettext('View Certificate'), - handler: function() { - var me = this.up('grid'); - me.view_certificate(); - } - } - ], - - columns: [ - { - header: gettext('File'), - width: 150, - dataIndex: 'filename' - }, - { - header: gettext('Issuer'), - flex: 1, - dataIndex: 'issuer' - }, - { - header: gettext('Subject'), - flex: 1, - dataIndex: 'subject' - }, - { - header: gettext('Public Key Alogrithm'), - flex: 1, - dataIndex: 'public-key-type', - hidden: true - }, - { - header: gettext('Public Key Size'), - flex: 1, - dataIndex: 'public-key-bits', - hidden: true - }, - { - header: gettext('Valid Since'), - width: 150, - dataIndex: 'notbefore', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Expires'), - width: 150, - dataIndex: 'notafter', - renderer: Proxmox.Utils.render_timestamp - }, - { - header: gettext('Subject Alternative Names'), - flex: 1, - dataIndex: 'san', - renderer: PVE.Utils.render_san - }, - { - header: gettext('Fingerprint'), - dataIndex: 'fingerprint', - hidden: true - }, - { - header: gettext('PEM'), - dataIndex: 'pem', - hidden: true - } - ], - - reload: function() { - var me = this; - me.rstore.load(); - }, - - set_button_status: function() { - var me = this; - var rec = me.rstore.getById('pveproxy-ssl.pem'); - - me.down('#deletebtn').setDisabled(!rec); - }, - - view_certificate: function() { - var me = this; - var selection = me.getSelection(); - if (!selection || selection.length < 1) { - return; - } - var win = Ext.create('PVE.node.CertificateViewer', { - cert: selection[0].data.filename, - nodename : me.nodename - }); - win.show(); - }, - - listeners: { - itemdblclick: 'view_certificate' - }, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'certs-' + me.nodename, - model: 'pve-certificate', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/certificates/info' - } - }); - - me.store = { - type: 'diff', - rstore: me.rstore - }; - - me.callParent(); - - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - } -}); -Ext.define('PVE.node.ACMEEditor', { - extend: 'Proxmox.window.Edit', - xtype: 'pveACMEEditor', - - subject: gettext('Domains'), - items: [ - { - xtype: 'inputpanel', - items: [ - { - xtype: 'textarea', - fieldLabel: gettext('Domains'), - emptyText: "domain1.example.com\ndomain2.example.com", - name: 'domains' - } - ], - onGetValues: function(values) { - if (!values.domains) { - return { - 'delete': 'acme' - }; - } - var domains = values.domains.split(/\n/).join(';'); - return { - 'acme': 'domains=' + domains - }; - } - } - ], - - initComponent: function() { - var me = this; - me.callParent(); - - me.load({ - success: function(response, opts) { - var res = PVE.Parser.parseACME(response.result.data.acme); - if (res) { - res.domains = res.domains.join(' '); - me.setValues(res); - } - } - }); - } -}); - -Ext.define('PVE.node.ACMEAccountCreate', { - extend: 'Proxmox.window.Edit', - - width: 400, - title: gettext('Register Account'), - isCreate: true, - method: 'POST', - submitText: gettext('Register'), - url: '/cluster/acme/account', - showTaskViewer: true, - - items: [ - { - xtype: 'proxmoxComboGrid', - name: 'directory', - allowBlank: false, - valueField: 'url', - displayField: 'name', - fieldLabel: gettext('ACME Directory'), - store: { - autoLoad: true, - fields: ['name', 'url'], - idProperty: ['name'], - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/acme/directories' - }, - sorters: { - property: 'name', - order: 'ASC' - } - }, - listConfig: { - columns: [ - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('URL'), - dataIndex: 'url', - flex: 1 - } - ] - }, - listeners: { - change: function(combogrid, value) { - var me = this; - if (!value) { - return; - } - - var disp = me.up('window').down('#tos_url_display'); - var field = me.up('window').down('#tos_url'); - var checkbox = me.up('window').down('#tos_checkbox'); - - disp.setValue(gettext('Loading')); - field.setValue(undefined); - checkbox.setValue(undefined); - - Proxmox.Utils.API2Request({ - url: '/cluster/acme/tos', - method: 'GET', - params: { - directory: value - }, - success: function(response, opt) { - me.up('window').down('#tos_url').setValue(response.result.data); - me.up('window').down('#tos_url_display').setValue(response.result.data); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - }, - { - xtype: 'displayfield', - itemId: 'tos_url_display', - fieldLabel: gettext('Terms of Service'), - renderer: PVE.Utils.render_optional_url, - name: 'tos_url_display' - }, - { - xtype: 'hidden', - itemId: 'tos_url', - name: 'tos_url' - }, - { - xtype: 'proxmoxcheckbox', - itemId: 'tos_checkbox', - fieldLabel: gettext('Accept TOS'), - submitValue: false, - validateValue: function(value) { - if (value && this.checked) { - return true; - } - return false; - } - }, - { - xtype: 'textfield', - name: 'contact', - vtype: 'email', - allowBlank: false, - fieldLabel: gettext('E-Mail') - } - ] - -}); - -Ext.define('PVE.node.ACMEAccountView', { - extend: 'Proxmox.window.Edit', - - width: 600, - fieldDefaults: { - labelWidth: 140 - }, - - title: gettext('Account'), - - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('E-Mail'), - name: 'email' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Created'), - name: 'createdAt' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Status'), - name: 'status' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Directory'), - renderer: PVE.Utils.render_optional_url, - name: 'directory' - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Terms of Services'), - renderer: PVE.Utils.render_optional_url, - name: 'tos' - } - ], - - initComponent: function() { - var me = this; - - if (!me.accountname) { - throw "no account name defined"; - } - - me.url = '/cluster/acme/account/' + me.accountname; - - me.callParent(); - - // hide OK/Reset button, because we just want to show data - me.down('toolbar[dock=bottom]').setVisible(false); - - me.load({ - success: function(response) { - var data = response.result.data; - data.email = data.account.contact[0]; - data.createdAt = data.account.createdAt; - data.status = data.account.status; - me.setValues(data); - } - }); - } -}); - -Ext.define('PVE.node.ACME', { - extend: 'Proxmox.grid.ObjectGrid', - xtype: 'pveACMEView', - - margin: '10 0 0 0', - title: 'ACME', - - tbar: [ - { - xtype: 'button', - itemId: 'edit', - text: gettext('Edit Domains'), - handler: function() { - this.up('grid').run_editor(); - } - }, - { - xtype: 'button', - itemId: 'createaccount', - text: gettext('Register Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountCreate', { - taskDone: function() { - me.load_account(); - me.reload(); - } - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'viewaccount', - text: gettext('View Account'), - handler: function() { - var me = this.up('grid'); - var win = Ext.create('PVE.node.ACMEAccountView', { - accountname: 'default' - }); - win.show(); - } - }, - { - xtype: 'button', - itemId: 'order', - text: gettext('Order Certificate'), - handler: function() { - var me = this.up('grid'); - - Proxmox.Utils.API2Request({ - method: 'POST', - params: { - force: 1 - }, - url: '/nodes/' + me.nodename + '/certificates/acme/certificate', - success: function(response, opt) { - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: response.result.data, - taskDone: function(success) { - me.certificate_order_finished(success); - } - }); - win.show(); - }, - failure: function(response, opt) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ], - - certificate_order_finished: function(success) { - if (!success) { - return; - } - var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!'); - Ext.getBody().mask(txt, ['pve-static-mask']); - // reload after 10 seconds automatically - Ext.defer(function() { - window.location.reload(true); - }, 10000); - }, - - set_button_status: function() { - var me = this; - - var account = !!me.account; - var acmeObj = PVE.Parser.parseACME(me.getObjectValue('acme')); - var domains = acmeObj ? acmeObj.domains.length : 0; - - var order = me.down('#order'); - order.setVisible(account); - order.setDisabled(!account || !domains); - - me.down('#createaccount').setVisible(!account); - me.down('#viewaccount').setVisible(account); - }, - - load_account: function() { - var me = this; - - // for now we only use the 'default' account - Proxmox.Utils.API2Request({ - url: '/cluster/acme/account/default', - success: function(response, opt) { - me.account = response.result.data; - me.set_button_status(); - }, - failure: function(response, opt) { - me.account = undefined; - me.set_button_status(); - } - }); - }, - - run_editor: function() { - var me = this; - var win = Ext.create(me.rows.acme.editor, me.editorConfig); - win.show(); - win.on('destroy', me.reload, me); - }, - - listeners: { - itemdblclick: 'run_editor' - }, - - // account data gets loaded here - account: undefined, - - disableSelection: true, - - initComponent: function() { - var me = this; - - if (!me.nodename) { - throw "no nodename given"; - } - - me.url = '/api2/json/nodes/' + me.nodename + '/config'; - - me.editorConfig = { - url: '/api2/extjs/nodes/' + me.nodename + '/config' - }; - /*jslint confusion: true*/ - /*acme is a string above*/ - me.rows = { - acme: { - defaultValue: '', - header: gettext('Domains'), - editor: 'PVE.node.ACMEEditor', - renderer: function(value) { - var acmeObj = PVE.Parser.parseACME(value); - if (acmeObj) { - return acmeObj.domains.join('
'); - } - return Proxmox.Utils.noneText; - } - } - }; - /*jslint confusion: false*/ - - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - me.rstore.startUpdate(); - me.load_account(); - } -}); -Ext.define('PVE.node.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.node.Config', - - onlineHelp: 'chapter_system_administration', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/status", - interval: 1000 - }); - - var node_command = function(cmd) { - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/status', - method: 'POST', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var actionBtn = Ext.create('Ext.Button', { - text: gettext('Bulk Actions'), - iconCls: 'fa fa-fw fa-ellipsis-v', - disabled: !caps.nodes['Sys.PowerMgmt'], - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Bulk Start'), - iconCls: 'fa fa-fw fa-play', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Start'), - btnText: gettext('Start'), - action: 'startall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Stop'), - iconCls: 'fa fa-fw fa-stop', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Stop'), - btnText: gettext('Stop'), - action: 'stopall' - }); - win.show(); - } - }, - { - text: gettext('Bulk Migrate'), - iconCls: 'fa fa-fw fa-send-o', - handler: function() { - var win = Ext.create('PVE.window.BulkAction', { - nodename: nodename, - title: gettext('Bulk Migrate'), - btnText: gettext('Migrate'), - action: 'migrateall' - }); - win.show(); - } - } - ] - }) - }); - - var restartBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Reboot'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Reboot node '{0}'?"), nodename), - handler: function() { - node_command('reboot'); - }, - iconCls: 'fa fa-undo' - }); - - var shutdownBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Shutdown'), - disabled: !caps.nodes['Sys.PowerMgmt'], - dangerous: true, - confirmMsg: Ext.String.format(gettext("Shutdown node '{0}'?"), nodename), - handler: function() { - node_command('shutdown'); - }, - iconCls: 'fa fa-power-off' - }); - - var shellBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.nodes['Sys.Console'], - text: gettext('Shell'), - consoleType: 'shell', - nodename: nodename - }); - - me.items = []; - - Ext.apply(me, { - title: gettext('Node') + " '" + nodename + "'", - hstateid: 'nodetab', - defaults: { statusStore: me.statusStore }, - tbar: [ restartBtn, shutdownBtn, shellBtn, actionBtn] - }); - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - itemId: 'summary', - xtype: 'pveNodeSummary' - }, - { - title: gettext('Notes'), - iconCls: 'fa fa-sticky-note-o', - itemId: 'notes', - xtype: 'pveNotesView' - } - ); - } - - if (caps.nodes['Sys.Console']) { - me.items.push( - { - title: gettext('Shell'), - iconCls: 'fa fa-terminal', - itemId: 'jsconsole', - xtype: 'pveNoVncConsole', - consoleType: 'shell', - xtermjs: true, - nodename: nodename - } - ); - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('System'), - iconCls: 'fa fa-cogs', - itemId: 'services', - expandedOnInit: true, - startOnlyServices: { - 'pveproxy': true, - 'pvedaemon': true, - 'pve-cluster': true - }, - nodename: nodename, - onlineHelp: 'pve_service_daemons', - xtype: 'proxmoxNodeServiceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - showApplyBtn: true, - groups: ['services'], - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeNetworkView' - }, - { - title: gettext('Certificates'), - iconCls: 'fa fa-certificate', - itemId: 'certificates', - groups: ['services'], - nodename: nodename, - xtype: 'pveCertificatesView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'dns', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeDNSView' - }, - { - title: gettext('Hosts'), - iconCls: 'fa fa-globe', - groups: ['services'], - itemId: 'hosts', - nodename: nodename, - onlineHelp: 'sysadmin_network_configuration', - xtype: 'proxmoxNodeHostsView' - }, - { - title: gettext('Time'), - itemId: 'time', - groups: ['services'], - nodename: nodename, - xtype: 'proxmoxNodeTimeView', - iconCls: 'fa fa-clock-o' - }); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push({ - title: 'Syslog', - iconCls: 'fa fa-list', - groups: ['services'], - disabled: !caps.nodes['Sys.Syslog'], - itemId: 'syslog', - xtype: 'proxmoxJournalView', - url: "/api2/extjs/nodes/" + nodename + "/journal" - }); - - if (caps.nodes['Sys.Modify']) { - me.items.push({ - title: gettext('Updates'), - iconCls: 'fa fa-refresh', - disabled: !caps.nodes['Sys.Console'], - // do we want to link to system updates instead? - itemId: 'apt', - xtype: 'proxmoxNodeAPT', - upgradeBtn: { - xtype: 'pveConsoleButton', - disabled: Proxmox.UserName !== 'root@pam', - text: gettext('Upgrade'), - consoleType: 'upgrade', - nodename: nodename - }, - nodename: nodename - }); - } - } - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - xtype: 'pveFirewallRules', - iconCls: 'fa fa-shield', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/nodes/' + nodename + '/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_host_specific_configuration', - groups: ['firewall'], - base_url: '/nodes/' + nodename + '/firewall/options', - fwtype: 'node', - itemId: 'firewall-options' - }); - } - - - if (caps.nodes['Sys.Audit']) { - me.items.push( - { - title: gettext('Disks'), - itemId: 'storage', - expandedOnInit: true, - iconCls: 'fa fa-hdd-o', - xtype: 'pveNodeDiskList' - }, - { - title: 'LVM', - itemId: 'lvm', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square', - groups: ['storage'], - xtype: 'pveLVMList' - }, - { - title: 'LVM-Thin', - itemId: 'lvmthin', - onlineHelp: 'chapter_lvm', - iconCls: 'fa fa-square-o', - groups: ['storage'], - xtype: 'pveLVMThinList' - }, - { - title: Proxmox.Utils.directoryText, - itemId: 'directory', - onlineHelp: 'chapter_storage', - iconCls: 'fa fa-folder', - groups: ['storage'], - xtype: 'pveDirectoryList' - }, - { - title: 'ZFS', - itemId: 'zfs', - onlineHelp: 'chapter_zfs', - iconCls: 'fa fa-th-large', - groups: ['storage'], - xtype: 'pveZFSList' - }, - { - title: 'Ceph', - itemId: 'ceph', - iconCls: 'fa fa-ceph', - xtype: 'pveNodeCephStatus' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveNodeCephConfigCrush', - title: gettext('Configuration'), - iconCls: 'fa fa-gear', - groups: ['ceph'], - itemId: 'ceph-config' - }, - { - xtype: 'pveNodeCephMonMgr', - title: gettext('Monitor'), - iconCls: 'fa fa-tv', - groups: ['ceph'], - itemId: 'ceph-monlist' - }, - { - xtype: 'pveNodeCephOsdTree', - title: 'OSD', - iconCls: 'fa fa-hdd-o', - groups: ['ceph'], - itemId: 'ceph-osdtree' - }, - { - xtype: 'pveNodeCephFSPanel', - title: 'CephFS', - iconCls: 'fa fa-folder', - groups: ['ceph'], - nodename: nodename, - itemId: 'ceph-cephfspanel' - }, - { - xtype: 'pveNodeCephPoolList', - title: 'Pools', - iconCls: 'fa fa-sitemap', - groups: ['ceph'], - itemId: 'ceph-pools' - } - ); - } - - if (caps.nodes['Sys.Syslog']) { - me.items.push( - { - xtype: 'proxmoxLogView', - title: gettext('Log'), - iconCls: 'fa fa-list', - groups: ['firewall'], - onlineHelp: 'chapter_pve_firewall', - url: '/api2/extjs/nodes/' + nodename + '/firewall/log', - itemId: 'firewall-fwlog' - }, - { - title: gettext('Log'), - itemId: 'ceph-log', - iconCls: 'fa fa-list', - groups: ['ceph'], - onlineHelp: 'chapter_pveceph', - xtype: 'cephLogView', - url: "/api2/extjs/nodes/" + nodename + "/ceph/log", - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Task History'), - iconCls: 'fa fa-list', - itemId: 'tasks', - nodename: nodename, - xtype: 'proxmoxNodeTasks' - }, - { - title: gettext('Subscription'), - iconCls: 'fa fa-support', - itemId: 'support', - xtype: 'pveNodeSubscription', - nodename: nodename - } - ); - - me.callParent(); - - me.mon(me.statusStore, 'load', function(s, records, success) { - var uptimerec = s.data.get('uptime'); - var powermgmt = uptimerec ? uptimerec.data.value : false; - if (!caps.nodes['Sys.PowerMgmt']) { - powermgmt = false; - } - restartBtn.setDisabled(!powermgmt); - shutdownBtn.setDisabled(!powermgmt); - shellBtn.setDisabled(!powermgmt); - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.window.Migrate', { - extend: 'Ext.window.Window', - - vmtype: undefined, - nodename: undefined, - vmid: undefined, - - viewModel: { - data: { - vmid: undefined, - nodename: undefined, - vmtype: undefined, - running: false, - qemu: { - onlineHelp: 'qm_migration', - commonName: 'VM' - }, - lxc: { - onlineHelp: 'pct_migration', - commonName: 'CT' - }, - migration: { - possible: true, - preconditions: [], - 'with-local-disks': 0, - mode: undefined, - allowedNodes: undefined, - overwriteLocalResourceCheck: false, - hasLocalResources: false - } - - }, - - formulas: { - setMigrationMode: function(get) { - if (get('running')){ - if (get('vmtype') === 'qemu') { - return gettext('Online'); - } else { - return gettext('Restart Mode'); - } - } else { - return gettext('Offline'); - } - }, - setStorageselectorHidden: function(get) { - if (get('migration.with-local-disks') && get('running')) { - return false; - } else { - return true; - } - }, - setLocalResourceCheckboxHidden: function(get) { - if (get('running') || !get('migration.hasLocalResources') || - Proxmox.UserName !== 'root@pam') { - return true; - } else { - return false; - } - } - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=formPanel]': { - validityChange: function(panel, isValid) { - this.getViewModel().set('migration.possible', isValid); - this.checkMigratePreconditions(); - } - } - }, - - init: function(view) { - var me = this, - vm = view.getViewModel(); - - if (!view.nodename) { - throw "missing custom view config: nodename"; - } - vm.set('nodename', view.nodename); - - if (!view.vmid) { - throw "missing custom view config: vmid"; - } - vm.set('vmid', view.vmid); - - if (!view.vmtype) { - throw "missing custom view config: vmtype"; - } - vm.set('vmtype', view.vmtype); - - - view.setTitle( - Ext.String.format('{0} {1} {2}', gettext('Migrate'), vm.get(view.vmtype).commonName, view.vmid) - ); - me.lookup('proxmoxHelpButton').setHelpConfig({ - onlineHelp: vm.get(view.vmtype).onlineHelp - }); - me.checkMigratePreconditions(); - me.lookup('formPanel').isValid(); - - }, - - onTargetChange: function (nodeSelector) { - //Always display the storages of the currently seleceted migration target - this.lookup('pveDiskStorageSelector').setNodename(nodeSelector.value); - this.checkMigratePreconditions(); - }, - - startMigration: function() { - var me = this, - view = me.getView(), - vm = me.getViewModel(); - - var values = me.lookup('formPanel').getValues(); - var params = { - target: values.target - }; - - if (vm.get('migration.mode')) { - params[vm.get('migration.mode')] = 1; - } - if (vm.get('migration.with-local-disks')) { - params['with-local-disks'] = 1; - } - //only submit targetstorage if vm is running, storage migration to different storage is only possible online - if (vm.get('migration.with-local-disks') && vm.get('running')) { - params.targetstorage = values.targetstorage; - } - - if (vm.get('migration.overwriteLocalResourceCheck')) { - params['force'] = 1; - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate', - waitMsgTarget: view, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var extraTitle = Ext.String.format(' ({0} ---> {1})', vm.get('nodename'), params.target); - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - extraTitle: extraTitle - }).show(); - - view.close(); - } - }); - - }, - - checkMigratePreconditions: function(resetMigrationPossible) { - var me = this, - vm = me.getViewModel(); - - var vmrec = PVE.data.ResourceStore.findRecord('vmid', vm.get('vmid'), - 0, false, false, true); - if (vmrec && vmrec.data && vmrec.data.running) { - vm.set('running', true); - } - - if (vm.get('vmtype') === 'qemu') { - me.checkQemuPreconditions(resetMigrationPossible); - } else { - me.checkLxcPreconditions(resetMigrationPossible); - } - me.lookup('pveNodeSelector').disallowedNodes = [vm.get('nodename')]; - - // Only allow nodes where the local storage is available in case of offline migration - // where storage migration is not possible - me.lookup('pveNodeSelector').allowedNodes = vm.get('migration.allowedNodes'); - - me.lookup('formPanel').isValid(); - - }, - - checkQemuPreconditions: function(resetMigrationPossible) { - var me = this, - vm = me.getViewModel(), - migrateStats; - - if (vm.get('running')) { - vm.set('migration.mode', 'online'); - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate', - method: 'GET', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - migrateStats = response.result.data; - if (migrateStats.running) { - vm.set('running', true); - } - // Get migration object from viewmodel to prevent - // to many bind callbacks - var migration = vm.get('migration'); - if (resetMigrationPossible) migration.possible = true; - migration.preconditions = []; - - if (migrateStats.allowed_nodes) { - migration.allowedNodes = migrateStats.allowed_nodes; - var target = me.lookup('pveNodeSelector').value; - if (target.length && !migrateStats.allowed_nodes.includes(target)) { - let disallowed = migrateStats.not_allowed_nodes[target]; - let missing_storages = disallowed.unavailable_storages.join(', '); - - migration.possible = false; - migration.preconditions.push({ - text: 'Storage (' + missing_storages + ') not available on selected target. ' + - 'Start VM to use live storage migration or select other target node', - severity: 'error' - }); - } - } - - if (migrateStats.local_resources.length) { - migration.hasLocalResources = true; - if(!migration.overwriteLocalResourceCheck || vm.get('running')){ - migration.possible = false; - migration.preconditions.push({ - text: Ext.String.format('Can\'t migrate VM with local resources: {0}', - migrateStats.local_resources.join(', ')), - severity: 'error' - }); - } else { - migration.preconditions.push({ - text: Ext.String.format('Migrate VM with local resources: {0}. ' + - 'This might fail if resources aren\'t available on the target node.', - migrateStats.local_resources.join(', ')), - severity: 'warning' - }); - } - } - - if (migrateStats.local_disks.length) { - - migrateStats.local_disks.forEach(function (disk) { - if (disk.cdrom && disk.cdrom === 1) { - if (disk.volid.includes('vm-'+vm.get('vmid')+'-cloudinit')) { - if (migrateStats.running) { - migration.possible = false; - migration.preconditions.push({ - text: "Can't live migrate VM with local cloudinit disk, use shared storage instead", - severity: 'error' - }); - } else { - return; - } - } else { - migration.possible = false; - migration.preconditions.push({ - text: "Can't migrate VM with local CD/DVD", - severity: 'error' - }); - } - - } else if (!disk.referenced_in_config) { - migration.possible = false; - migration.preconditions.push({ - text: 'Found not referenced/unused disk via storage: '+ disk.volid, - severity: 'error' - }); - } else { - migration['with-local-disks'] = 1; - migration.preconditions.push({ - text:'Migration with local disk might take long: ' + disk.volid - +' (' + PVE.Utils.render_size(disk.size) + ')', - severity: 'warning' - }); - } - }); - - } - - vm.set('migration', migration); - - } - }); - }, - checkLxcPreconditions: function(resetMigrationPossible) { - var me = this, - vm = me.getViewModel(); - if (vm.get('running')) { - vm.set('migration.mode', 'restart'); - } - } - - - }, - - width: 600, - modal: true, - layout: { - type: 'vbox', - align: 'stretch' - }, - border: false, - items: [ - { - xtype: 'form', - reference: 'formPanel', - bodyPadding: 10, - border: false, - layout: { - type: 'column' - }, - items: [ - { - xtype: 'container', - columnWidth: 0.5, - items: [{ - xtype: 'displayfield', - name: 'source', - fieldLabel: gettext('Source node'), - bind: { - value: '{nodename}' - } - }, - { - xtype: 'displayfield', - reference: 'migrationMode', - fieldLabel: gettext('Mode'), - bind: { - value: '{setMigrationMode}' - } - }] - }, - { - xtype: 'container', - columnWidth: 0.5, - items: [{ - xtype: 'pveNodeSelector', - reference: 'pveNodeSelector', - name: 'target', - fieldLabel: gettext('Target node'), - allowBlank: false, - disallowedNodes: undefined, - onlineValidator: true, - listeners: { - change: 'onTargetChange' - } - }, - { - xtype: 'pveStorageSelector', - reference: 'pveDiskStorageSelector', - name: 'targetstorage', - fieldLabel: gettext('Target storage'), - storageContent: 'images', - bind: { - hidden: '{setStorageselectorHidden}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'overwriteLocalResourceCheck', - fieldLabel: gettext('Force'), - autoEl: { - tag: 'div', - 'data-qtip': 'Overwrite local resources unavailable check' - }, - bind: { - hidden: '{setLocalResourceCheckboxHidden}', - value: '{migration.overwriteLocalResourceCheck}' - }, - listeners: { - change: {fn: 'checkMigratePreconditions', extraArg: true} - } - }] - } - ] - }, - { - xtype: 'gridpanel', - reference: 'preconditionGrid', - selectable: false, - flex: 1, - columns: [{ - text: '', - dataIndex: 'severity', - renderer: function(v) { - switch (v) { - case 'warning': - return ' '; - case 'error': - return ''; - default: - return v; - } - }, - width: 35 - }, - { - text: 'Info', - dataIndex: 'text', - cellWrap: true, - flex: 1 - }], - bind: { - hidden: '{!migration.preconditions.length}', - store: { - fields: ['severity','text'], - data: '{migration.preconditions}' - } - } - } - - ], - buttons: [ - { - xtype: 'proxmoxHelpButton', - reference: 'proxmoxHelpButton', - onlineHelp: 'pct_migration', - listenToGlobalEvent: false, - hidden: false - }, - '->', - { - xtype: 'button', - reference: 'submitButton', - text: gettext('Migrate'), - handler: 'startMigration', - bind: { - disabled: '{!migration.possible}' - } - } - ] -}); -Ext.define('PVE.window.BulkAction', { - extend: 'Ext.window.Window', - - resizable: true, - width: 800, - modal: true, - layout: { - type: 'fit' - }, - border: false, - - // the action to be set - // currently there are - // startall - // migrateall - // stopall - action: undefined, - - submit: function(params) { - var me = this; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.action, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - me.hide(); - win.on('destroy', function() { - me.close(); - }); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - if (!me.action) { - throw "no action specified"; - } - if (!me.btnText) { - throw "no button text specified"; - } - if (!me.title) { - throw "no title specified"; - } - - var items = []; - - if (me.action === 'migrateall') { - /*jslint confusion: true*/ - /*value is string and number*/ - items.push( - { - xtype: 'pveNodeSelector', - name: 'target', - disallowedNodes: [me.nodename], - fieldLabel: gettext('Target node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'maxworkers', - minValue: 1, - maxValue: 100, - value: 1, - fieldLabel: gettext('Parallel jobs'), - allowBlank: false - }, - { - xtype: 'fieldcontainer', - fieldLabel: gettext('Allow local disk migration'), - layout: 'hbox', - items: [{ - xtype: 'proxmoxcheckbox', - name: 'with-local-disks', - checked: true, - uncheckedValue: 0, - listeners: { - change: (cb, val) => me.down('#localdiskwarning').setVisible(val), - } - - }, - { - itemId: 'localdiskwarning', - xtype: 'displayfield', - flex: 1, - padding: '0 0 0 10', - userCls: 'pmx-hint', - value: 'Note: Migration with local disks might take long.', - }], - }, - { - itemId: 'lxcwarning', - xtype: 'displayfield', - userCls: 'pmx-hint', - value: 'Warning: Running CTs will be migrated in Restart Mode.', - hidden: true // only visible if running container chosen - } - ); - /*jslint confusion: false*/ - } else if (me.action === 'startall') { - items.push({ - xtype: 'hiddenfield', - name: 'force', - value: 1 - }); - } - - items.push({ - xtype: 'vmselector', - itemId: 'vms', - name: 'vms', - flex: 1, - height: 300, - selectAll: true, - allowBlank: false, - nodename: me.nodename, - action: me.action, - listeners: { - selectionchange: function(vmselector, records) { - if (me.action == 'migrateall') { - var showWarning = records.some(function(item) { - return (item.data.type == 'lxc' && - item.data.status == 'running'); - }); - me.down('#lxcwarning').setVisible(showWarning); - } - } - } - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - layout: { - type: 'vbox', - align: 'stretch' - }, - fieldDefaults: { - labelWidth: 300, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn = Ext.create('Ext.Button', { - text: me.btnText, - handler: function() { - form.isValid(); - me.submit(form.getValues()); - } - }); - - Ext.apply(me, { - items: [ me.formPanel ], - buttons: [ submitBtn ] - }); - - me.callParent(); - - form.on('validitychange', function() { - var valid = form.isValid(); - submitBtn.setDisabled(!valid); - }); - form.isValid(); - } -}); -Ext.define('PVE.window.Clone', { - extend: 'Ext.window.Window', - - resizable: false, - - isTemplate: false, - - onlineHelp: 'qm_copy_and_clone', - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'panel[reference=cloneform]': { - validitychange: 'disableSubmit' - } - }, - disableSubmit: function(form) { - this.lookupReference('submitBtn').setDisabled(!form.isValid()); - } - }, - - statics: { - // display a snapshot selector only if needed - wrap: function(nodename, vmid, isTemplate, guestType) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/' + guestType + '/' + vmid +'/snapshot', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, opts) { - var snapshotList = response.result.data; - var hasSnapshots = snapshotList.length === 1 && - snapshotList[0].name === 'current' ? false : true; - - Ext.create('PVE.window.Clone', { - nodename: nodename, - guestType: guestType, - vmid: vmid, - isTemplate: isTemplate, - hasSnapshots: hasSnapshots - }).show(); - } - }); - } - }, - - create_clone: function(values) { - var me = this; - - var params = { newid: values.newvmid }; - - if (values.snapname && values.snapname !== 'current') { - params.snapname = values.snapname; - } - - if (values.pool) { - params.pool = values.pool; - } - - if (values.name) { - if (me.guestType === 'lxc') { - params.hostname = values.name; - } else { - params.name = values.name; - } - } - - if (values.target) { - params.target = values.target; - } - - if (values.clonemode === 'copy') { - params.full = 1; - if (values.hdstorage) { - params.storage = values.hdstorage; - if (values.diskformat && me.guestType !== 'lxc') { - params.format = values.diskformat; - } - } - } - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/clone', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - - }, - - // disable the Storage selector when clone mode is linked clone - updateVisibility: function() { - var me = this; - var clonemode = me.lookupReference('clonemodesel').getValue(); - var disksel = me.lookup('diskselector'); - disksel.setDisabled(clonemode === 'clone'); - }, - - // add to the list of valid nodes each node where - // all the VM disks are available - verifyFeature: function() { - var me = this; - - var snapname = me.lookupReference('snapshotsel').getValue(); - var clonemode = me.lookupReference('clonemodesel').getValue(); - - var params = { feature: clonemode }; - if (snapname !== 'current') { - params.snapname = snapname; - } - - Proxmox.Utils.API2Request({ - waitMsgTarget: me, - url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/feature', - params: params, - method: 'GET', - failure: function(response, opts) { - me.lookupReference('submitBtn').setDisabled(true); - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var res = response.result.data; - - me.lookupReference('targetsel').allowedNodes = res.nodes; - me.lookupReference('targetsel').validate(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.snapname) { - me.snapname = 'current'; - } - - if (!me.guestType) { - throw "no Guest Type specified"; - } - - var titletext = me.guestType === 'lxc' ? 'CT' : 'VM'; - if (me.isTemplate) { - titletext += ' Template'; - } - me.title = "Clone " + titletext + " " + me.vmid; - - var col1 = []; - var col2 = []; - - col1.push({ - xtype: 'pveNodeSelector', - name: 'target', - reference: 'targetsel', - fieldLabel: gettext('Target node'), - selectCurNode: true, - allowBlank: false, - onlineValidator: true, - listeners: { - change: function(f, value) { - me.lookupReference('hdstorage').setTargetNode(value); - } - } - }); - - var modelist = [['copy', gettext('Full Clone')]]; - if (me.isTemplate) { - modelist.push(['clone', gettext('Linked Clone')]); - } - - col1.push({ - xtype: 'pveGuestIDSelector', - name: 'newvmid', - guestType: me.guestType, - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - allowBlank: true, - fieldLabel: me.guestType === 'lxc' ? gettext('Hostname') : gettext('Name') - }, - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ); - - col2.push({ - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('Mode'), - name: 'clonemode', - reference: 'clonemodesel', - allowBlank: false, - hidden: !me.isTemplate, - value: me.isTemplate ? 'clone' : 'copy', - comboItems: modelist, - listeners: { - change: function(t, value) { - me.updateVisibility(); - me.verifyFeature(); - } - } - }, - { - xtype: 'PVE.form.SnapshotSelector', - name: 'snapname', - reference: 'snapshotsel', - fieldLabel: gettext('Snapshot'), - nodename: me.nodename, - guestType: me.guestType, - vmid: me.vmid, - hidden: me.isTemplate || !me.hasSnapshots ? true : false, - disabled: false, - allowBlank: false, - value : me.snapname, - listeners: { - change: function(f, value) { - me.verifyFeature(); - } - } - }, - { - xtype: 'pveDiskStorageSelector', - reference: 'diskselector', - nodename: me.nodename, - autoSelect: false, - hideSize: true, - hideSelection: true, - storageLabel: gettext('Target Storage'), - allowBlank: true, - storageContent: me.guestType === 'qemu' ? 'images' : 'rootdir', - emptyText: gettext('Same as source'), - disabled: me.isTemplate ? true : false // because default mode is clone for templates - }); - - var formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - reference: 'cloneform', - border: false, - layout: 'column', - defaultType: 'container', - columns: 2, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - columnWidth: 0.5, - padding: '0 10 0 0', - layout: 'anchor', - items: col1 - }, - { - columnWidth: 0.5, - padding: '0 0 0 10', - layout: 'anchor', - items: col2 - } - ] - }); - - Ext.apply(me, { - modal: true, - width: 600, - height: 250, - border: false, - layout: 'fit', - buttons: [ { - xtype: 'proxmoxHelpButton', - listenToGlobalEvent: false, - hidden: false, - onlineHelp: me.onlineHelp - }, - '->', - { - reference: 'submitBtn', - text: gettext('Clone'), - disabled: true, - handler: function() { - var cloneForm = me.lookupReference('cloneform'); - if (cloneForm.isValid()) { - me.create_clone(cloneForm.getValues()); - } - } - } ], - items: [ formPanel ] - }); - - me.callParent(); - - me.verifyFeature(); - } -}); -Ext.define('PVE.window.Snapshot', { - extend: 'Proxmox.window.Edit', - - viewModel: { - data: { - type: undefined, - isCreate: undefined, - running: false, - guestAgentEnabled: false, - }, - formulas: { - runningWithoutGuestAgent: (get) => get('type') === 'qemu' && get('running') && !get('guestAgentEnabled'), - shouldWarnAboutFS: (get) => get('isCreate') && get('runningWithoutGuestAgent') && get('!vmstate.checked'), - }, - }, - - onGetValues: function(values) { - let me = this; - - if (me.type === 'lxc') { - delete values.vmstate; - } - - return values; - }, - - initComponent : function() { - var me = this; - var vm = me.getViewModel(); - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.type) { - throw "no type specified"; - } - - vm.set('type', me.type); - vm.set('running', me.running); - vm.set('isCreate', me.isCreate); - - if (me.type === 'qemu' && me.isCreate) { - Proxmox.Utils.API2Request({ - url: `/nodes/${me.nodename}/${me.type}/${me.vmid}/config`, - params: { 'current': '1' }, - method: 'GET', - success: function(response, options) { - let res = response.result.data; - let enabled = PVE.Parser.parsePropertyString(res.agent, 'enabled'); - vm.set('guestAgentEnabled', !!PVE.Parser.parseBoolean(enabled.enabled)); - } - }); - } - - me.items = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'snapname', - value: me.snapname, - fieldLabel: gettext('Name'), - vtype: 'ConfigId', - allowBlank: false - }, - { - xtype: 'displayfield', - hidden: me.isCreate, - disabled: me.isCreate, - name: 'snaptime', - renderer: PVE.Utils.render_timestamp_human_readable, - fieldLabel: gettext('Timestamp') - }, - { - xtype: 'proxmoxcheckbox', - hidden: me.type !== 'qemu' || !me.isCreate || !me.running, - disabled: me.type !== 'qemu' || !me.isCreate || !me.running, - name: 'vmstate', - reference: 'vmstate', - uncheckedValue: 0, - defaultValue: 0, - checked: 1, - fieldLabel: gettext('Include RAM') - }, - { - xtype: 'textareafield', - grow: true, - editable: !me.viewonly, - name: 'description', - fieldLabel: gettext('Description') - }, - { - xtype: 'displayfield', - userCls: 'pmx-hint', - name: 'fswarning', - hidden: true, - value: gettext('It is recommended to either include the RAM or use the QEMU Guest Agent when taking a snapshot of a running VM to avoid inconsistencies.'), - bind: { - hidden: '{!shouldWarnAboutFS}', - }, - }, - { - title: gettext('Settings'), - hidden: me.isCreate, - xtype: 'grid', - itemId: 'summary', - border: true, - height: 200, - store: { - model: 'KeyValue', - sorters: [ - { - property : 'key', - direction: 'ASC' - } - ] - }, - columns: [ - { - header: gettext('Key'), - width: 150, - dataIndex: 'key', - }, - { - header: gettext('Value'), - flex: 1, - dataIndex: 'value', - } - ] - } - ]; - - me.url = `/nodes/${me.nodename}/${me.type}/${me.vmid}/snapshot`; - - let subject; - if (me.isCreate) { - subject = (me.type === 'qemu' ? 'VM' : 'CT') + me.vmid + ' ' + gettext('Snapshot'); - me.method = 'POST'; - me.showProgress = true; - } else { - subject = `${gettext('Snapshot')} ${me.snapname}`; - me.url += `/${me.snapname}/config`; - } - - Ext.apply(me, { - subject: subject, - width: me.isCreate ? 450 : 620, - height: me.isCreate ? undefined : 420, - }); - - me.callParent(); - - if (!me.snapname) { - return; - } - - me.load({ - success: function(response) { - let kvarray = []; - Ext.Object.each(response.result.data, function(key, value) { - if (key === 'description' || key === 'snaptime') { - return; - } - kvarray.push({ key: key, value: value }); - }); - - let summarystore = me.down('#summary').getStore(); - summarystore.suspendEvents(); - summarystore.add(kvarray); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh', summarystore); - - me.setValues(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.Monitor', { - extend: 'Ext.panel.Panel', - - alias: 'widget.pveQemuMonitor', - - maxLines: 500, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var history = []; - var histNum = -1; - var lines = []; - - var textbox = Ext.createWidget('panel', { - region: 'center', - xtype: 'panel', - autoScroll: true, - border: true, - margins: '5 5 5 5', - bodyStyle: 'font-family: monospace;' - }); - - var scrollToEnd = function() { - var el = textbox.getTargetEl(); - var dom = Ext.getDom(el); - - var clientHeight = dom.clientHeight; - // BrowserBug: clientHeight reports 0 in IE9 StrictMode - // Instead we are using offsetHeight and hardcoding borders - if (Ext.isIE9 && Ext.isStrict) { - clientHeight = dom.offsetHeight + 2; - } - dom.scrollTop = dom.scrollHeight - clientHeight; - }; - - var refresh = function() { - textbox.update('
' + lines.join('\n') + '
'); - scrollToEnd(); - }; - - var addLine = function(line) { - lines.push(line); - if (lines.length > me.maxLines) { - lines.shift(); - } - }; - - var executeCmd = function(cmd) { - addLine("# " + Ext.htmlEncode(cmd)); - if (cmd) { - history.unshift(cmd); - if (history.length > 20) { - history.splice(20); - } - } - histNum = -1; - - refresh(); - Proxmox.Utils.API2Request({ - params: { command: cmd }, - url: '/nodes/' + nodename + '/qemu/' + vmid + "/monitor", - method: 'POST', - waitMsgTarget: me, - success: function(response, opts) { - var res = response.result.data; - Ext.Array.each(res.split('\n'), function(line) { - addLine(Ext.htmlEncode(line)); - }); - refresh(); - }, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - textbox, - { - region: 'south', - margins:'0 5 5 5', - border: false, - xtype: 'textfield', - name: 'cmd', - value: '', - fieldStyle: 'font-family: monospace;', - allowBlank: true, - listeners: { - afterrender: function(f) { - f.focus(false); - addLine("Type 'help' for help."); - refresh(); - }, - specialkey: function(f, e) { - var key = e.getKey(); - switch (key) { - case e.ENTER: - var cmd = f.getValue(); - f.setValue(''); - executeCmd(cmd); - break; - case e.PAGE_UP: - textbox.scrollBy(0, -0.9*textbox.getHeight(), false); - break; - case e.PAGE_DOWN: - textbox.scrollBy(0, 0.9*textbox.getHeight(), false); - break; - case e.UP: - if (histNum + 1 < history.length) { - f.setValue(history[++histNum]); - } - e.preventDefault(); - break; - case e.DOWN: - if (histNum > 0) { - f.setValue(history[--histNum]); - } - e.preventDefault(); - break; - default: - break; - } - } - } - } - ], - listeners: { - show: function() { - var field = me.query('textfield[name="cmd"]')[0]; - field.focus(false, true); - } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.qemu.OSTypeInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuOSTypePanel', - onlineHelp: 'qm_os_settings', - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'combobox[name=osbase]': { - change: 'onOSBaseChange' - }, - 'combobox[name=ostype]': { - afterrender: 'onOSTypeChange', - change: 'onOSTypeChange' - } - }, - onOSBaseChange: function(field, value) { - this.lookup('ostype').getStore().setData(PVE.Utils.kvm_ostypes[value]); - }, - onOSTypeChange: function(field) { - var me = this, ostype = field.getValue(); - if (!me.getView().insideWizard) { - return; - } - var targetValues = PVE.qemu.OSDefaults.getDefaults(ostype); - - me.setWidget('pveBusSelector', targetValues.busType); - me.setWidget('pveNetworkCardSelector', targetValues.networkCard); - var scsihw = targetValues.scsihw || '__default__'; - this.getViewModel().set('current.scsihw', scsihw); - }, - setWidget: function(widget, newValue) { - // changing a widget is safe only if ComponentQuery.query returns us - // a single value array - var widgets = Ext.ComponentQuery.query('pveQemuCreateWizard ' + widget); - if (widgets.length === 1) { - widgets[0].setValue(newValue); - } else { - throw 'non unique widget :' + widget + ' in Wizard'; - } - } - }, - - initComponent : function() { - var me = this; - - /*jslint confusion: true */ - me.items = [ - { - xtype: 'displayfield', - value: gettext('Guest OS') + ':', - hidden: !me.insideWizard - }, - { - xtype: 'combobox', - submitValue: false, - name: 'osbase', - fieldLabel: gettext('Type'), - editable: false, - queryMode: 'local', - value: 'Linux', - store: Object.keys(PVE.Utils.kvm_ostypes) - }, - { - xtype: 'combobox', - name: 'ostype', - reference: 'ostype', - fieldLabel: gettext('Version'), - value: 'l26', - allowBlank : false, - editable: false, - queryMode: 'local', - valueField: 'val', - displayField: 'desc', - store: { - fields: ['desc', 'val'], - data: PVE.Utils.kvm_ostypes.Linux, - listeners: { - datachanged: function (store) { - var ostype = me.lookup('ostype'); - var old_val = ostype.getValue(); - if (!me.insideWizard && old_val && store.find('val', old_val) != -1) { - ostype.setValue(old_val); - } else { - ostype.setValue(store.getAt(0)); - } - } - } - } - } - ]; - /*jslint confusion: false */ - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.OSTypeEdit', { - extend: 'Proxmox.window.Edit', - - subject: 'OS Type', - - items: [{ xtype: 'pveQemuOSTypePanel' }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response, options) { - var value = response.result.data.ostype || 'other'; - var osinfo = PVE.Utils.get_kvm_osinfo(value); - me.setValues({ ostype: value, osbase: osinfo.base }); - } - }); - } -}); -/* - * This class holds performance *recommended* settings for the PVE Qemu wizards - * the *mandatory* settings are set in the PVE::QemuServer - * config_to_command sub - * We store this here until we get the data from the API server -*/ - -// this is how you would add an hypothetic FreeBSD > 10 entry -// -//virtio-blk is stable but virtIO net still -// problematic as of 10.3 -// see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059 -// addOS({ -// parent: 'generic', // inherits defaults -// pveOS: 'freebsd10', // must match a radiofield in OSTypeEdit.js -// busType: 'virtio' // must match a pveBusController value -// // networkCard muss match a pveNetworkCardSelector - - -Ext.define('PVE.qemu.OSDefaults', { - singleton: true, // will also force creation when loaded - - constructor: function() { - var me = this; - - var addOS = function(settings) { - if (me.hasOwnProperty(settings.parent)) { - var child = Ext.clone(me[settings.parent]); - me[settings.pveOS] = Ext.apply(child, settings); - - } else { - throw("Could not find your genitor"); - } - }; - - // default values - me.generic = { - busType: 'ide', - networkCard: 'e1000', - busPriority: { - ide: 4, - sata: 3, - scsi: 2, - virtio: 1 - }, - scsihw: 'virtio-scsi-pci' - }; - - // virtio-net is in kernel since 2.6.25 - // virtio-scsi since 3.2 but backported in RHEL with 2.6 kernel - addOS({ - pveOS: 'l26', - parent : 'generic', - busType: 'scsi', - busPriority: { - scsi: 4, - virtio: 3, - sata: 2, - ide: 1 - }, - networkCard: 'virtio' - }); - - // recommandation from http://wiki.qemu.org/Windows2000 - addOS({ - pveOS: 'w2k', - parent : 'generic', - networkCard: 'rtl8139', - scsihw: '' - }); - // https://pve.proxmox.com/wiki/Windows_XP_Guest_Notes - addOS({ - pveOS: 'wxp', - parent : 'w2k' - }); - - me.getDefaults = function(ostype) { - if (PVE.qemu.OSDefaults[ostype]) { - return PVE.qemu.OSDefaults[ostype]; - } else { - return PVE.qemu.OSDefaults.generic; - } - }; - } -}); -Ext.define('PVE.qemu.ProcessorInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuProcessorPanel', - onlineHelp: 'qm_cpu', - - insideWizard: false, - - controller: { - xclass: 'Ext.app.ViewController', - - updateCores: function() { - var me = this.getView(); - var sockets = me.down('field[name=sockets]').getValue(); - var cores = me.down('field[name=cores]').getValue(); - me.down('field[name=totalcores]').setValue(sockets*cores); - var vcpus = me.down('field[name=vcpus]'); - vcpus.setMaxValue(sockets*cores); - vcpus.setEmptyText(sockets*cores); - vcpus.validate(); - }, - - control: { - 'field[name=sockets]': { - change: 'updateCores' - }, - 'field[name=cores]': { - change: 'updateCores' - } - } - }, - - onGetValues: function(values) { - var me = this; - - if (Array.isArray(values['delete'])) { - values['delete'] = values['delete'].join(','); - } - - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - // build the cpu options: - me.cpu.cputype = values.cputype; - - if (values.flags) { - me.cpu.flags = values.flags; - } else { - delete me.cpu.flags; - } - - delete values.cputype; - delete values.flags; - var cpustring = PVE.Parser.printQemuCpu(me.cpu); - - // remove cputype delete request: - var del = values['delete']; - delete values['delete']; - if (del) { - del = del.split(','); - Ext.Array.remove(del, 'cputype'); - } else { - del = []; - } - - if (cpustring) { - values.cpu = cpustring; - } else { - del.push('cpu'); - } - - var delarr = del.join(','); - if (delarr) { - values['delete'] = delarr; - } - - return values; - }, - - cpu: {}, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'sockets', - minValue: 1, - maxValue: 4, - value: '1', - fieldLabel: gettext('Sockets'), - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: '1', - fieldLabel: gettext('Cores'), - allowBlank: false - } - ], - - column2: [ - { - xtype: 'CPUModelSelector', - name: 'cputype', - fieldLabel: gettext('Type') - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Total cores'), - name: 'totalcores', - value: '1' - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxintegerfield', - name: 'vcpus', - minValue: 1, - maxValue: 1, - value: '', - fieldLabel: gettext('VCPUs'), - deleteEmpty: true, - allowBlank: true, - emptyText: '1' - }, - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - maxValue: 128, // api maximum - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - minValue: 8, - maxValue: 500000, - value: '1024', - deleteEmpty: true, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable NUMA'), - name: 'numa', - uncheckedValue: 0 - } - ], - advancedColumnB: [ - { - xtype: 'label', - text: 'Extra CPU Flags:' - }, - { - xtype: 'vmcpuflagselector', - name: 'flags' - } - ] -}); - -Ext.define('PVE.qemu.ProcessorEdit', { - extend: 'Proxmox.window.Edit', - - width: 700, - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.ProcessorInputPanel'); - - Ext.apply(me, { - subject: gettext('Processors'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - var value = data.cpu; - if (value) { - var cpu = PVE.Parser.parseQemuCpu(value); - ipanel.cpu = cpu; - data.cputype = cpu.cputype; - if (cpu.flags) { - data.flags = cpu.flags; - } - } - me.setValues(data); - } - }); - } -}); -Ext.define('PVE.qemu.BootOrderPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuBootOrderPanel', - vmconfig: {}, // store loaded vm config - - bootdisk: undefined, - selection: [], - list: [], - comboboxes: [], - - isBootDisk: function(value) { - return PVE.Utils.bus_match.test(value); - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - var order = me.vmconfig.boot || 'cdn'; - me.bootdisk = me.vmconfig.bootdisk || undefined; - - // get the first 3 characters - // ignore the rest (there should never be more than 3) - me.selection = order.split('').slice(0,3); - - // build bootdev list - me.list = []; - Ext.Object.each(me.vmconfig, function(key, value) { - if (me.isBootDisk(key) && - !(/media=cdrom/).test(value)) { - me.list.push([key, "Disk '" + key + "'"]); - } - }); - - me.list.push(['d', 'CD-ROM']); - me.list.push(['n', gettext('Network')]); - me.list.push(['__none__', Proxmox.Utils.noneText]); - - me.recomputeList(); - - me.comboboxes.forEach(function(box) { - box.resetOriginalValue(); - }); - }, - - onGetValues: function(values) { - var me = this; - var order = me.selection.join(''); - var res = { boot: order }; - - if (me.bootdisk && order.indexOf('c') !== -1) { - res.bootdisk = me.bootdisk; - } else { - res['delete'] = 'bootdisk'; - } - - return res; - }, - - recomputeSelection: function(combobox, newVal, oldVal) { - var me = this.up('#inputpanel'); - me.selection = []; - me.comboboxes.forEach(function(item) { - var val = item.getValue(); - - // when selecting an already selected item, - // switch it around - if ((val === newVal || (me.isBootDisk(val) && me.isBootDisk(newVal))) && - item.name !== combobox.name && - newVal !== '__none__') { - // swap items - val = oldVal; - } - - // push 'c','d' or 'n' in the array - if (me.isBootDisk(val)) { - me.selection.push('c'); - me.bootdisk = val; - } else if (val === 'd' || - val === 'n') { - me.selection.push(val); - } - }); - - me.recomputeList(); - }, - - recomputeList: function(){ - var me = this; - // set the correct values in the kvcomboboxes - var cnt = 0; - me.comboboxes.forEach(function(item) { - if (cnt === 0) { - // never show 'none' on first combobox - item.store.loadData(me.list.slice(0, me.list.length-1)); - } else { - item.store.loadData(me.list); - } - item.suspendEvent('change'); - if (cnt < me.selection.length) { - item.setValue((me.selection[cnt] !== 'c')?me.selection[cnt]:me.bootdisk); - } else if (cnt === 0){ - item.setValue(''); - } else { - item.setValue('__none__'); - } - cnt++; - item.resumeEvent('change'); - item.validate(); - }); - }, - - initComponent : function() { - var me = this; - - // this has to be done here, because of - // the way our inputPanel class handles items - me.comboboxes = [ - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 1", - labelWidth: 120, - name: 'bd1', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 2", - labelWidth: 120, - name: 'bd2', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }), - Ext.createWidget('proxmoxKVComboBox', { - fieldLabel: gettext('Boot device') + " 3", - labelWidth: 120, - name: 'bd3', - allowBlank: false, - listeners: { - change: me.recomputeSelection - } - }) - ]; - Ext.apply(me, { items: me.comboboxes }); - me.callParent(); - } -}); - -Ext.define('PVE.qemu.BootOrderEdit', { - extend: 'Proxmox.window.Edit', - - items: [{ - xtype: 'pveQemuBootOrderPanel', - itemId: 'inputpanel' - }], - - subject: gettext('Boot Order'), - - initComponent : function() { - var me = this; - me.callParent(); - me.load({ - success: function(response, options) { - me.down('#inputpanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuMemoryPanel', - onlineHelp: 'qm_memory', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var res = {}; - - res.memory = values.memory; - res.balloon = values.balloon; - - if (!values.ballooning) { - res.balloon = 0; - res['delete'] = 'shares'; - } else if (values.memory === values.balloon) { - delete res.balloon; - res['delete'] = 'balloon,shares'; - } else if (Ext.isDefined(values.shares) && (values.shares !== "")) { - res.shares = values.shares; - } else { - res['delete'] = "shares"; - } - - return res; - }, - - initComponent: function() { - var me = this; - var labelWidth = 160; - - me.items= [ - { - xtype: 'pveMemoryField', - labelWidth: labelWidth, - fieldLabel: gettext('Memory') + ' (MiB)', - name: 'memory', - minValue: 1, - step: 32, - hotplug: me.hotplug, - listeners: { - change: function(f, value, old) { - var bf = me.down('field[name=balloon]'); - var balloon = bf.getValue(); - bf.setMaxValue(value); - if (balloon === old) { - bf.setValue(value); - } - bf.validate(); - } - } - } - ]; - - me.advancedItems= [ - { - xtype: 'pveMemoryField', - name: 'balloon', - minValue: 1, - maxValue: 512, - step: 32, - fieldLabel: gettext('Minimum memory') + ' (MiB)', - hotplug: me.hotplug, - labelWidth: labelWidth, - allowBlank: false, - listeners: { - change: function(f, value) { - var memory = me.down('field[name=memory]').getValue(); - var shares = me.down('field[name=shares]'); - shares.setDisabled(value === memory); - } - } - }, - { - xtype: 'proxmoxintegerfield', - name: 'shares', - disabled: true, - minValue: 0, - maxValue: 50000, - value: '', - step: 10, - fieldLabel: gettext('Shares'), - labelWidth: labelWidth, - allowBlank: true, - emptyText: Proxmox.Utils.defaultText + ' (1000)', - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - labelWidth: labelWidth, - value: '1', - name: 'ballooning', - fieldLabel: gettext('Ballooning Device'), - listeners: { - change: function(f, value) { - var bf = me.down('field[name=balloon]'); - var shares = me.down('field[name=shares]'); - var memory = me.down('field[name=memory]'); - bf.setDisabled(!value); - shares.setDisabled(!value || (bf.getValue() === memory.getValue())); - } - } - } - ]; - - if (me.insideWizard) { - me.column1 = me.items; - me.items = undefined; - me.advancedColumn1 = me.advancedItems; - me.advancedItems = undefined; - } - me.callParent(); - } - -}); - -Ext.define('PVE.qemu.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent: function() { - var me = this; - - var memoryhotplug; - if(me.hotplug) { - Ext.each(me.hotplug.split(','), function(el) { - if (el === 'memory') { - memoryhotplug = 1; - } - }); - } - - var ipanel = Ext.create('PVE.qemu.MemoryInputPanel', { - hotplug: memoryhotplug - }); - - Ext.apply(me, { - subject: gettext('Memory'), - items: [ ipanel ], - // uncomment the following to use the async configiguration API - // backgroundDelay: 5, - width: 400 - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var data = response.result.data; - - var values = { - ballooning: data.balloon === 0 ? '0' : '1', - shares: data.shares, - memory: data.memory || '512', - balloon: data.balloon > 0 ? data.balloon : (data.memory || '512') - }; - - ipanel.setValues(values); - } - }); - } -}); -Ext.define('PVE.qemu.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuNetworkInputPanel', - onlineHelp: 'qm_network_device', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - me.network.model = values.model; - if (values.nonetwork) { - return {}; - } else { - me.network.bridge = values.bridge; - me.network.tag = values.tag; - me.network.firewall = values.firewall; - } - me.network.macaddr = values.macaddr; - me.network.disconnect = values.disconnect; - me.network.queues = values.queues; - - if (values.rate) { - me.network.rate = values.rate; - } else { - delete me.network.rate; - } - - var params = {}; - - params[me.confid] = PVE.Parser.printQemuNetwork(me.network); - - return params; - }, - - setNetwork: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data) { - data.networkmode = data.bridge ? 'bridge' : 'nat'; - } else { - data = {}; - data.networkmode = 'bridge'; - } - me.network = data; - - me.setValues(me.network); - }, - - setNodename: function(nodename) { - var me = this; - - me.bridgesel.setNodename(nodename); - }, - - initComponent : function() { - var me = this; - - me.network = {}; - me.confid = 'net0'; - - me.column1 = []; - me.column2 = []; - - me.bridgesel = Ext.create('PVE.form.BridgeSelector', { - name: 'bridge', - fieldLabel: gettext('Bridge'), - nodename: me.nodename, - autoSelect: true, - allowBlank: false - }); - - me.column1 = [ - me.bridgesel, - { - xtype: 'pveVlanField', - name: 'tag', - value: '' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - checked: (me.insideWizard || me.isCreate) - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Disconnect'), - name: 'disconnect' - } - ]; - - if (me.insideWizard) { - me.column1.unshift({ - xtype: 'checkbox', - name: 'nonetwork', - inputValue: 'none', - boxLabel: gettext('No network device'), - listeners: { - change: function(cb, value) { - var fields = [ - 'disconnect', - 'bridge', - 'tag', - 'firewall', - 'model', - 'macaddr', - 'rate', - 'queues' - ]; - fields.forEach(function(fieldname) { - me.down('field[name='+fieldname+']').setDisabled(value); - }); - me.down('field[name=bridge]').validate(); - } - } - }); - me.column2.unshift({ - xtype: 'displayfield' - }); - } - - me.column2.push( - { - xtype: 'pveNetworkCardSelector', - name: 'model', - fieldLabel: gettext('Model'), - value: PVE.qemu.OSDefaults.generic.networkCard, - allowBlank: false - }, - { - xtype: 'textfield', - name: 'macaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - allowBlank: true, - emptyText: 'auto' - }); - me.advancedColumn2 = [ - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: '', - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'queues', - fieldLabel: 'Multiqueue', - minValue: 1, - maxValue: 8, - value: '', - allowBlank: true - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.NetworkInputPanel', { - confid: me.confid, - nodename: nodename, - isCreate: me.isCreate - }); - - Ext.applyIf(me, { - subject: gettext('Network Device'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - if (!me.isCreate) { - var value = me.vmconfig[me.confid]; - var network = PVE.Parser.parseQemuNetwork(me.confid, value); - if (!network) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse network options'); - me.close(); - return; - } - ipanel.setNetwork(me.confid, network); - } else { - for (i = 0; i < 100; i++) { - confid = 'net' + i.toString(); - if (!Ext.isDefined(me.vmconfig[confid])) { - me.confid = confid; - break; - } - } - ipanel.setNetwork(me.confid); - } - } - }); - } -}); -Ext.define('PVE.qemu.Smbios1InputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.PVE.qemu.Smbios1InputPanel', - - insideWizard: false, - - smbios1: {}, - - onGetValues: function(values) { - var me = this; - - var params = { - smbios1: PVE.Parser.printQemuSmbios1(values) - }; - - return params; - }, - - setSmbios1: function(data) { - var me = this; - - me.smbios1 = data; - - me.setValues(me.smbios1); - }, - - items: [ - { - xtype: 'textfield', - fieldLabel: 'UUID', - regex: /^[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$/, - name: 'uuid' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Manufacturer'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'manufacturer' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Product'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'product' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Version'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'version' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Serial'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'serial' - }, - { - xtype: 'textareafield', - fieldLabel: 'SKU', - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'sku' - }, - { - xtype: 'textareafield', - fieldLabel: gettext('Family'), - fieldStyle: { - height: '2em', - minHeight: '2em' - }, - name: 'family' - } - ] -}); - -Ext.define('PVE.qemu.Smbios1Edit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - var ipanel = Ext.create('PVE.qemu.Smbios1InputPanel', {}); - - Ext.applyIf(me, { - subject: gettext('SMBIOS settings (type1)'), - width: 450, - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - var i, confid; - me.vmconfig = response.result.data; - var value = me.vmconfig.smbios1; - if (value) { - var data = PVE.Parser.parseQemuSmbios1(value); - if (!data) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse smbios options'); - me.close(); - return; - } - ipanel.setSmbios1(data); - } - } - }); - } -}); -Ext.define('PVE.qemu.CDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuCDInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || (values.controller + values.deviceid); - - me.drive.media = 'cdrom'; - if (values.mediaType === 'iso') { - me.drive.file = values.cdimage; - } else if (values.mediaType === 'cdrom') { - me.drive.file = 'cdrom'; - } else { - me.drive.file = 'none'; - } - - var params = {}; - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig, 'cdrom'); - } - }, - - setDrive: function(drive) { - var me = this; - - var values = {}; - if (drive.file === 'cdrom') { - values.mediaType = 'cdrom'; - } else if (drive.file === 'none') { - values.mediaType = 'none'; - } else { - values.mediaType = 'iso'; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.cdstorage = match[1]; - values.cdimage = drive.file; - } - } - - me.drive = drive; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - - me.cdstoragesel.setNodename(nodename); - me.cdfilesel.setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - var items = []; - - if (!me.confid) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - noVirtIO: true - }); - items.push(me.bussel); - } - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'iso', - boxLabel: gettext('Use CD/DVD disc image file (iso)'), - checked: true, - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=cdstorage]').setDisabled(!value); - var cdImageField = me.down('field[name=cdimage]'); - cdImageField.setDisabled(!value); - if(value) { - cdImageField.validate(); - } else { - cdImageField.reset(); - } - } - } - }); - - me.cdfilesel = Ext.create('PVE.form.FileSelector', { - name: 'cdimage', - nodename: me.nodename, - storageContent: 'iso', - fieldLabel: gettext('ISO image'), - labelAlign: 'right', - allowBlank: false - }); - - me.cdstoragesel = Ext.create('PVE.form.StorageSelector', { - name: 'cdstorage', - nodename: me.nodename, - fieldLabel: gettext('Storage'), - labelAlign: 'right', - storageContent: 'iso', - allowBlank: false, - autoSelect: me.insideWizard, - listeners: { - change: function(f, value) { - me.cdfilesel.setStorage(value); - } - } - }); - - items.push(me.cdstoragesel); - items.push(me.cdfilesel); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'cdrom', - boxLabel: gettext('Use physical CD/DVD Drive') - }); - - items.push({ - xtype: 'radiofield', - name: 'mediaType', - inputValue: 'none', - boxLabel: gettext('Do not use any media') - }); - - me.items = items; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CDEdit', { - extend: 'Proxmox.window.Edit', - - width: 400, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.CDInputPanel', { - confid: me.confid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: 'CD/DVD Drive', - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert('Error', 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - } - } - }); - } -}); -/*jslint confusion: true */ -/* 'change' property is assigned a string and then a function */ -Ext.define('PVE.qemu.HDInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveQemuHDInputPanel', - onlineHelp: 'qm_hard_disk', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - viewModel: {}, - - controller: { - - xclass: 'Ext.app.ViewController', - - onControllerChange: function(field) { - var value = field.getValue(); - - var allowIOthread = value.match(/^(virtio|scsi)/); - this.lookup('iothread').setDisabled(!allowIOthread); - if (!allowIOthread) { - this.lookup('iothread').setValue(false); - } - - var virtio = value.match(/^virtio/); - this.lookup('ssd').setDisabled(virtio); - if (virtio) { - this.lookup('ssd').setValue(false); - } - - this.lookup('scsiController').setVisible(value.match(/^scsi/)); - }, - - control: { - 'field[name=controller]': { - change: 'onControllerChange', - afterrender: 'onControllerChange' - }, - 'field[name=iothread]' : { - change: function(f, value) { - if (!this.getView().insideWizard) { - return; - } - var vmScsiType = value ? 'virtio-scsi-single': 'virtio-scsi-pci'; - this.lookupReference('scsiController').setValue(vmScsiType); - } - } - }, - - init: function(view) { - var vm = this.getViewModel(); - if (view.isCreate) { - vm.set('isIncludedInBackup', true); - } - } - }, - - onGetValues: function(values) { - var me = this; - - var params = {}; - var confid = me.confid || (values.controller + values.deviceid); - - if (me.unused) { - me.drive.file = me.vmconfig[values.unusedId]; - confid = values.controller + values.deviceid; - } else if (me.isCreate) { - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - me.drive.file = values.hdstorage + ":" + values.disksize; - } - me.drive.format = values.diskformat; - } - - PVE.Utils.propertyStringSet(me.drive, !values.backup, 'backup', '0'); - PVE.Utils.propertyStringSet(me.drive, values.noreplicate, 'replicate', 'no'); - PVE.Utils.propertyStringSet(me.drive, values.discard, 'discard', 'on'); - PVE.Utils.propertyStringSet(me.drive, values.ssd, 'ssd', 'on'); - PVE.Utils.propertyStringSet(me.drive, values.iothread, 'iothread', 'on'); - PVE.Utils.propertyStringSet(me.drive, values.cache, 'cache'); - - var names = ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr']; - Ext.Array.each(names, function(name) { - var burst_name = name + '_max'; - PVE.Utils.propertyStringSet(me.drive, values[name], name); - PVE.Utils.propertyStringSet(me.drive, values[burst_name], burst_name); - }); - - - params[confid] = PVE.Parser.printQemuDrive(me.drive); - - return params; - }, - - setVMConfig: function(vmconfig) { - var me = this; - - me.vmconfig = vmconfig; - - if (me.bussel) { - me.bussel.setVMConfig(vmconfig); - me.scsiController.setValue(vmconfig.scsihw); - } - if (me.unusedDisks) { - var disklist = []; - Ext.Object.each(vmconfig, function(key, value) { - if (key.match(/^unused\d+$/)) { - disklist.push([key, value]); - } - }); - me.unusedDisks.store.loadData(disklist); - me.unusedDisks.setValue(me.confid); - } - }, - - setDrive: function(drive) { - var me = this; - - me.drive = drive; - - var values = {}; - var match = drive.file.match(/^([^:]+):/); - if (match) { - values.hdstorage = match[1]; - } - - values.hdimage = drive.file; - values.backup = PVE.Parser.parseBoolean(drive.backup, 1); - values.noreplicate = !PVE.Parser.parseBoolean(drive.replicate, 1); - values.diskformat = drive.format || 'raw'; - values.cache = drive.cache || '__default__'; - values.discard = (drive.discard === 'on'); - values.ssd = PVE.Parser.parseBoolean(drive.ssd); - values.iothread = PVE.Parser.parseBoolean(drive.iothread); - - values.mbps_rd = drive.mbps_rd; - values.mbps_wr = drive.mbps_wr; - values.iops_rd = drive.iops_rd; - values.iops_wr = drive.iops_wr; - values.mbps_rd_max = drive.mbps_rd_max; - values.mbps_wr_max = drive.mbps_wr_max; - values.iops_rd_max = drive.iops_rd_max; - values.iops_wr_max = drive.iops_wr_max; - - me.setValues(values); - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - var labelWidth = 140; - - me.drive = {}; - - me.column1 = []; - me.column2 = []; - - me.advancedColumn1 = []; - me.advancedColumn2 = []; - - if (!me.confid || me.unused) { - me.bussel = Ext.create('PVE.form.ControllerSelector', { - vmconfig: me.insideWizard ? {ide2: 'cdrom'} : {} - }); - me.column1.push(me.bussel); - - me.scsiController = Ext.create('Ext.form.field.Display', { - fieldLabel: gettext('SCSI Controller'), - reference: 'scsiController', - bind: me.insideWizard ? { - value: '{current.scsihw}' - } : undefined, - renderer: PVE.Utils.render_scsihw, - submitValue: false, - hidden: true - }); - me.column1.push(me.scsiController); - } - - if (me.unused) { - me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', { - name: 'unusedId', - fieldLabel: gettext('Disk image'), - matchFieldWidth: false, - listConfig: { - width: 350 - }, - data: [], - allowBlank: false - }); - me.column1.push(me.unusedDisks); - } else if (me.isCreate) { - me.column1.push({ - xtype: 'pveDiskStorageSelector', - storageContent: 'images', - name: 'disk', - nodename: me.nodename, - autoSelect: me.insideWizard - }); - } else { - me.column1.push({ - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'hdimage' - }); - } - - me.column2.push( - { - xtype: 'CacheTypeSelector', - name: 'cache', - value: '__default__', - fieldLabel: gettext('Cache') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Discard'), - reference: 'discard', - name: 'discard' - } - ); - - me.advancedColumn1.push( - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && me.confid.match(/^virtio/), - fieldLabel: gettext('SSD emulation'), - labelWidth: labelWidth, - name: 'ssd', - reference: 'ssd' - }, - { - xtype: 'proxmoxcheckbox', - disabled: me.confid && !me.confid.match(/^(virtio|scsi)/), - fieldLabel: 'IO thread', - labelWidth: labelWidth, - reference: 'iothread', - name: 'iothread' - }, - { - xtype: 'numberfield', - name: 'mbps_rd', - minValue: 1, - step: 1, - fieldLabel: gettext('Read limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'numberfield', - name: 'mbps_wr', - minValue: 1, - step: 1, - fieldLabel: gettext('Write limit') + ' (MB/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd', - minValue: 10, - step: 10, - fieldLabel: gettext('Read limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr', - minValue: 10, - step: 10, - fieldLabel: gettext('Write limit') + ' (ops/s)', - labelWidth: labelWidth, - emptyText: gettext('unlimited') - } - ); - - me.advancedColumn2.push( - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Backup'), - autoEl: { - tag: 'div', - 'data-qtip': gettext('Include volume in backup job'), - }, - labelWidth: labelWidth, - name: 'backup', - bind: { - value: '{isIncludedInBackup}', - }, - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Skip replication'), - labelWidth: labelWidth, - name: 'noreplicate' - }, - { - xtype: 'numberfield', - name: 'mbps_rd_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Read max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'numberfield', - name: 'mbps_wr_max', - minValue: 1, - step: 1, - fieldLabel: gettext('Write max burst') + ' (MB)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_rd_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Read max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - }, - { - xtype: 'proxmoxintegerfield', - name: 'iops_wr_max', - minValue: 10, - step: 10, - fieldLabel: gettext('Write max burst') + ' (ops)', - labelWidth: labelWidth, - emptyText: gettext('default') - } - ); - - me.callParent(); - } -}); -/*jslint confusion: false */ - -Ext.define('PVE.qemu.HDEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - backgroundDelay: 5, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.qemu.HDInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - me.subject = gettext('Unused Disk'); - } else if (me.isCreate) { - me.subject = gettext('Hard Disk'); - } else { - me.subject = gettext('Hard Disk') + ' (' + me.confid + ')'; - } - - me.items = [ ipanel ]; - - me.callParent(); - /*jslint confusion: true*/ - /* 'data' is assigned an empty array in same file, and here we - * use it like an object - */ - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - var value = response.result.data[me.confid]; - var drive = PVE.Parser.parseQemuDrive(me.confid, value); - if (!drive) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options'); - me.close(); - return; - } - ipanel.setDrive(drive); - me.isValid(); // trigger validation - } - } - }); - /*jslint confusion: false*/ - } -}); -Ext.define('PVE.window.HDResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 140, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 250, - height: 150, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -Ext.define('PVE.window.HDMove', { - extend: 'Ext.window.Window', - - resizable: false, - - - move_disk: function(disk, storage, format, delete_disk) { - var me = this; - var qemu = (me.type === 'qemu'); - var params = {}; - params.storage = storage; - params[qemu ? 'disk':'volume'] = disk; - - if (format && qemu) { - params.format = format; - } - - if (delete_disk) { - params['delete'] = 1; - } - - var url = '/nodes/' + me.nodename + '/' + me.type + '/' + me.vmid + '/'; - url += qemu ? 'move_disk' : 'move_volume'; - - Proxmox.Utils.API2Request({ - params: params, - url: url, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - win.on('destroy', function() { me.close(); }); - } - }); - - }, - - initComponent : function() { - var me = this; - - var diskarray = []; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - if (!me.type) { - me.type = 'qemu'; - } - - var qemu = (me.type === 'qemu'); - - var items = [ - { - xtype: 'displayfield', - name: qemu ? 'disk' : 'volume', - value: me.disk, - fieldLabel: qemu ? gettext('Disk') : gettext('Mount Point'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - items.push({ - xtype: 'pveDiskStorageSelector', - storageLabel: gettext('Target Storage'), - nodename: me.nodename, - storageContent: qemu ? 'images' : 'rootdir', - hideSize: true - }); - - items.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Delete source'), - name: 'deleteDisk', - uncheckedValue: 0, - checked: false - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = qemu ? gettext("Move disk") : gettext('Move Volume'); - submitBtn = Ext.create('Ext.Button', { - text: me.title, - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.move_disk(me.disk, values.hdstorage, values.diskformat, - values.deleteDisk); - } - } - }); - - Ext.apply(me, { - modal: true, - width: 350, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - me.mon(me.formPanel, 'validitychange', function(fp, isValid) { - submitBtn.setDisabled(!isValid); - }); - - me.formPanel.isValid(); - } -}); -Ext.define('PVE.qemu.EFIDiskInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveEFIDiskInputPanel', - - insideWizard: false, - - unused: false, // ADD usused disk imaged - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var confid = 'efidisk0'; - - if (values.hdimage) { - me.drive.file = values.hdimage; - } else { - // we use 1 here, because for efi the size gets overridden from the backend - me.drive.file = values.hdstorage + ":1"; - } - - me.drive.format = values.diskformat; - var params = {}; - params[confid] = PVE.Parser.printQemuDrive(me.drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items= [ - { - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - }, - { - xtype: 'label', - text: gettext("Warning: The VM currently does not uses 'OVMF (UEFI)' as BIOS."), - userCls: 'pmx-hint', - hidden: me.usesEFI, - }, - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.EFIDiskEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - subject: gettext('EFI Disk'), - - width: 450, - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveEFIDiskInputPanel', - onlineHelp: 'qm_bios_and_uefi', - confid: me.confid, - nodename: nodename, - usesEFI: me.usesEFI, - isCreate: true, - }]; - - me.callParent(); - } -}); -Ext.define('PVE.qemu.DisplayInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveDisplayInputPanel', - onlineHelp: 'qm_display', - - onGetValues: function(values) { - var ret = PVE.Parser.printPropertyString(values, 'type'); - if (ret === '') { - return { - 'delete': 'vga' - }; - } - return { - vga: ret - }; - }, - - items: [{ - name: 'type', - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - comboItems: PVE.Utils.kvm_vga_driver_array(), - validator: function() { - var v = this.getValue(); - var cfg = this.up('proxmoxWindowEdit').vmconfig || {}; - - if (v.match(/^serial\d+$/) && (!cfg[v] || cfg[v] !== 'socket')) { - var fmt = gettext("Serial interface '{0}' is not correctly configured."); - return Ext.String.format(fmt, v); - } - return true; - }, - listeners: { - change: function(cb, val) { - var me = this.up('panel'); - if (!val) { - return; - } - var disable = false; - var emptyText = Proxmox.Utils.defaultText; - switch (val) { - case "cirrus": - emptyText = "4"; - break; - case "std": - emptyText = "16"; - break; - case "qxl": - case "qxl2": - case "qxl3": - case "qxl4": - emptyText = "16"; - break; - case "vmware": - emptyText = "16"; - break; - case "none": - case "serial0": - case "serial1": - case "serial2": - case "serial3": - emptyText = 'N/A'; - disable = true; - break; - case "virtio": - emptyText = "256"; - break; - default: - break; - } - var memoryfield = me.down('field[name=memory]'); - memoryfield.setEmptyText(emptyText); - memoryfield.setDisabled(disable); - } - } - },{ - xtype: 'proxmoxintegerfield', - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('Memory') + ' (MiB)', - minValue: 4, - maxValue: 512, - step: 4, - name: 'memory' - }] -}); - -Ext.define('PVE.qemu.DisplayEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - subject: gettext('Display'), - width: 350, - - items: [{ - xtype: 'pveDisplayInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response) { - me.vmconfig = response.result.data; - var vga = me.vmconfig.vga || '__default__'; - me.setValues(PVE.Parser.parsePropertyString(vga, 'type')); - } - }); - } -}); -Ext.define('PVE.qemu.KeyboardEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('Keyboard Layout'), - items: { - xtype: 'VNCKeyboardSelector', - name: 'keyboard', - value: '__default__', - fieldLabel: gettext('Keyboard Layout') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.HardwareView', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.HardwareView'], - - onlineHelp: 'qm_virtual_machines_settings', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - var iconCls = rowdef.iconCls; - var icon = ''; - var txt = (rowdef.header || key); - - metaData.tdAttr = "valign=middle"; - - if (rowdef.isOnStorageBus) { - var value = me.getObjectValue(key, '', false); - if (value === '') { - value = me.getObjectValue(key, '', true); - } - if (value.match(/vm-.*-cloudinit/)) { - iconCls = 'cloud'; - txt = rowdef.cloudheader; - } else if (value.match(/media=cdrom/)) { - metaData.tdCls = 'pve-itype-icon-cdrom'; - return rowdef.cdheader; - } - } - - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - } else if (iconCls) { - icon = ""; - metaData.tdCls += " pve-itype-fa"; - } - - // only return icons in grid but not remove dialog - if (rowIndex !== undefined) { - return icon + txt; - } else { - return txt; - } - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - /*jslint confusion: true */ - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined, - never_delete: true, - defaultValue: '512', - tdCls: 'pve-itype-icon-memory', - group: 2, - multiKey: ['memory', 'balloon', 'shares'], - renderer: function(value, metaData, record, ri, ci, store, pending) { - var res = ''; - - var max = me.getObjectValue('memory', 512, pending); - var balloon = me.getObjectValue('balloon', undefined, pending); - var shares = me.getObjectValue('shares', undefined, pending); - - res = Proxmox.Utils.format_size(max*1024*1024); - - if (balloon !== undefined && balloon > 0) { - res = Proxmox.Utils.format_size(balloon*1024*1024) + "/" + res; - - if (shares) { - res += ' [shares=' + shares +']'; - } - } else if (balloon === 0) { - res += ' [balloon=0]'; - } - return res; - } - }, - sockets: { - header: gettext('Processors'), - never_delete: true, - editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ? - 'PVE.qemu.ProcessorEdit' : undefined, - tdCls: 'pve-itype-icon-processor', - group: 3, - defaultValue: '1', - multiKey: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'], - renderer: function(value, metaData, record, rowIndex, colIndex, store, pending) { - - var sockets = me.getObjectValue('sockets', 1, pending); - var model = me.getObjectValue('cpu', undefined, pending); - var cores = me.getObjectValue('cores', 1, pending); - var numa = me.getObjectValue('numa', undefined, pending); - var vcpus = me.getObjectValue('vcpus', undefined, pending); - var cpulimit = me.getObjectValue('cpulimit', undefined, pending); - var cpuunits = me.getObjectValue('cpuunits', undefined, pending); - - var res = Ext.String.format('{0} ({1} sockets, {2} cores)', - sockets*cores, sockets, cores); - - if (model) { - res += ' [' + model + ']'; - } - - if (numa) { - res += ' [numa=' + numa +']'; - } - - if (vcpus) { - res += ' [vcpus=' + vcpus +']'; - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit +']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits +']'; - } - - return res; - } - }, - bios: { - header: 'BIOS', - group: 4, - never_delete: true, - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.BiosEdit' : undefined, - defaultValue: '', - iconCls: 'microchip', - renderer: PVE.Utils.render_qemu_bios - }, - vga: { - header: gettext('Display'), - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined, - never_delete: true, - iconCls: 'desktop', - group:5, - defaultValue: '', - renderer: PVE.Utils.render_kvm_vga_driver - }, - machine: { - header: gettext('Machine'), - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Machine'), - width: 350, - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - }]} : undefined, - iconCls: 'cogs', - never_delete: true, - group: 6, - defaultValue: '', - renderer: PVE.Utils.render_qemu_machine - }, - scsihw: { - header: gettext('SCSI Controller'), - iconCls: 'database', - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.ScsiHwEdit' : undefined, - renderer: PVE.Utils.render_scsihw, - group: 7, - never_delete: true, - defaultValue: '' - }, - vmstate: { - header: gettext('Hibernation VM State'), - iconCls: 'download', - del_extra_msg: gettext('The saved VM state will be permanently lost.'), - group: 100, - }, - cores: { - visible: false - }, - cpu: { - visible: false - }, - numa: { - visible: false - }, - balloon: { - visible: false - }, - hotplug: { - visible: false - }, - vcpus: { - visible: false - }, - cpuunits: { - visible: false - }, - cpulimit: { - visible: false - }, - shares: { - visible: false - } - }; - /*jslint confusion: false */ - - PVE.Utils.forEachBus(undefined, function(type, id) { - var confid = type + id; - rows[confid] = { - group: 10, - iconCls: 'hdd-o', - editor: 'PVE.qemu.HDEdit', - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - isOnStorageBus: true, - header: gettext('Hard Disk') + ' (' + confid +')', - cdheader: gettext('CD/DVD Drive') + ' (' + confid +')', - cloudheader: gettext('CloudInit Drive') + ' (' + confid + ')' - }; - }); - for (i = 0; i < PVE.Utils.hardware_counts.net; i++) { - confid = "net" + i.toString(); - rows[confid] = { - group: 15, - order: i, - iconCls: 'exchange', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined, - never_delete: caps.vms['VM.Config.Network'] ? false : true, - header: gettext('Network Device') + ' (' + confid +')' - }; - } - rows.efidisk0 = { - group: 20, - iconCls: 'hdd-o', - editor: null, - never_delete: caps.vms['VM.Config.Disk'] ? false : true, - header: gettext('EFI Disk') - }; - for (i = 0; i < PVE.Utils.hardware_counts.usb; i++) { - confid = "usb" + i.toString(); - rows[confid] = { - group: 25, - order: i, - iconCls: 'usb', - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined, - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('USB Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < PVE.Utils.hardware_counts.hostpci; i++) { - confid = "hostpci" + i.toString(); - rows[confid] = { - group: 30, - order: i, - tdCls: 'pve-itype-icon-pci', - never_delete: caps.nodes['Sys.Console'] ? false : true, - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.PCIEdit' : undefined, - header: gettext('PCI Device') + ' (' + confid + ')' - }; - } - for (i = 0; i < PVE.Utils.hardware_counts.serial; i++) { - confid = "serial" + i.toString(); - rows[confid] = { - group: 35, - order: i, - tdCls: 'pve-itype-icon-serial', - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext('Serial Port') + ' (' + confid + ')' - }; - } - rows.audio0 = { - group: 40, - iconCls: 'volume-up', - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.AudioEdit' : undefined, - never_delete: caps.vms['VM.Config.HWType'] ? false : true, - header: gettext('Audio Device') - }; - for (i = 0; i < 256; i++) { - rows["unused" + i.toString()] = { - group: 99, - order: i, - iconCls: 'hdd-o', - del_extra_msg: gettext('This will permanently erase all data.'), - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined, - header: gettext('Unused Disk') + ' ' + i.toString() - }; - } - rows.rng0 = { - group: 45, - tdCls: 'pve-itype-icon-die', - editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.RNGEdit' : undefined, - never_delete: caps.nodes['Sys.Console'] ? false : true, - header: gettext("VirtIO RNG") - }; - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var editor = rowdef.editor; - if (rowdef.isOnStorageBus) { - var value = me.getObjectValue(rec.data.key, '', true); - if (value.match(/vm-.*-cloudinit/)) { - return; - } else if (value.match(/media=cdrom/)) { - editor = 'PVE.qemu.CDEdit'; - } else if (!diskCap) { - return; - } - } - - var win; - - if (Ext.isString(editor)) { - win = Ext.create(editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/' + baseurl - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - - win.show(); - win.on('destroy', me.reload, me); - }; - - var run_resize = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', me.reload, me); - }; - - var run_move = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - - win.on('destroy', me.reload, me); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - handler: run_editor - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: sm, - disabled: true, - handler: run_resize - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move disk'), - selModel: sm, - disabled: true, - handler: run_move - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - defaultText: gettext('Remove'), - altText: gettext('Detach'), - selModel: sm, - disabled: true, - dangerous: true, - RESTMethod: 'PUT', - confirmMsg: function(rec) { - var warn = gettext('Are you sure you want to remove entry {0}'); - if (this.text === this.altText) { - warn = gettext('Are you sure you want to detach entry {0}'); - } - var key = rec.data.key; - var entry = rows[key]; - - var rendered = me.renderKey(key, {}, rec); - var msg = Ext.String.format(warn, "'" + rendered + "'"); - - if (entry.del_extra_msg) { - msg += '
' + entry.del_extra_msg; - } - - return msg; - }, - handler: function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: b.RESTMethod, - params: { - 'delete': rec.data.key - }, - callback: () => me.reload(), - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - success: function(response, options) { - if (b.RESTMethod === 'POST') { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { - upid: upid, - listeners: { - destroy: () => me.reload(), - } - }); - win.show(); - } - } - }); - }, - listeners: { - render: function(btn) { - // hack: calculate an optimal button width on first display - // to prevent the whole toolbar to move when we switch - // between the "Remove" and "Detach" labels - var def = btn.getSize().width; - - btn.setText(btn.altText); - var alt = btn.getSize().width; - - btn.setText(btn.defaultText); - - var optimal = alt > def ? alt : def; - btn.setSize({ width: optimal }); - } - } - }); - - var revert_btn = new PVE.button.PendingRevert({ - apiurl: '/api2/extjs/' + baseurl, - }); - - var efidisk_menuitem = Ext.create('Ext.menu.Item',{ - text: gettext('EFI Disk'), - iconCls: 'fa fa-fw fa-hdd-o black', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - let bios = me.rstore.getData().map.bios; - let usesEFI = bios && (bios.data.value === 'ovmf' || bios.data.pending === 'ovmf'); - - var win = Ext.create('PVE.qemu.EFIDiskEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode, - usesEFI: usesEFI, - }); - win.on('destroy', me.reload, me); - win.show(); - } - }); - - let counts = {}; - let isAtLimit = (type) => (counts[type] >= PVE.Utils.hardware_counts[type]); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - // en/disable hardwarebuttons - counts = {}; - var hasCloudInit = false; - me.rstore.getData().items.forEach(function(item){ - if (!hasCloudInit && ( - /vm-.*-cloudinit/.test(item.data.value) || - /vm-.*-cloudinit/.test(item.data.pending) - )) { - hasCloudInit = true; - return; - } - - let match = item.id.match(/^([^\d]+)\d+$/); - let type; - if (match && PVE.Utils.hardware_counts[match[1]] !== undefined) { - type = match[1]; - } else { - return; - } - - counts[type] = (counts[type] || 0) + 1; - }); - - // heuristic only for disabling some stuff, the backend has the final word. - var noSysConsolePerm = !caps.nodes['Sys.Console']; - var noVMConfigHWTypePerm = !caps.vms['VM.Config.HWType']; - var noVMConfigNetPerm = !caps.vms['VM.Config.Network']; - - - me.down('#addusb').setDisabled(noSysConsolePerm || isAtLimit('usb')); - me.down('#addpci').setDisabled(noSysConsolePerm || isAtLimit('hostpci')); - me.down('#addaudio').setDisabled(noVMConfigHWTypePerm || isAtLimit('audio')); - me.down('#addserial').setDisabled(noVMConfigHWTypePerm || isAtLimit('serial')); - me.down('#addnet').setDisabled(noVMConfigNetPerm || isAtLimit('net')); - me.down('#addrng').setDisabled(noSysConsolePerm || isAtLimit('rng')); - efidisk_menuitem.setDisabled(isAtLimit('efidisk')); - me.down('#addci').setDisabled(noSysConsolePerm || hasCloudInit); - - if (!rec) { - remove_btn.disable(); - edit_btn.disable(); - resize_btn.disable(); - move_btn.disable(); - revert_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var isCDRom = (value && !!value.toString().match(/media=cdrom/)); - var isUnusedDisk = key.match(/^unused\d+/); - var isUsedDisk = !isUnusedDisk && rowdef.isOnStorageBus && !isCDRom; - - var isCloudInit = (value && value.toString().match(/vm-.*-cloudinit/)); - - var isEfi = (key === 'efidisk0'); - - remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete === true) || (isUnusedDisk && !diskCap)); - remove_btn.setText((isUsedDisk && !isCloudInit) ? remove_btn.altText : remove_btn.defaultText); - remove_btn.RESTMethod = isUnusedDisk ? 'POST':'PUT'; - - edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor || isCloudInit || (!isCDRom && !diskCap)); - - resize_btn.setDisabled(pending || !isUsedDisk || !diskCap); - - move_btn.setDisabled(pending || !(isUsedDisk || isEfi) || !diskCap); - - revert_btn.setDisabled(!pending); - - }; - - Ext.apply(me, { - url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + '/pending', - interval: 5000, - selModel: sm, - run_editor: run_editor, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - cls: 'pve-add-hw-menu', - items: [ - { - text: gettext('Hard Disk'), - iconCls: 'fa fa-fw fa-hdd-o black', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.HDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', me.reload, me); - win.show(); - } - }, - { - text: gettext('CD/DVD Drive'), - iconCls: 'pve-itype-icon-cdrom', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.qemu.CDEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', me.reload, me); - win.show(); - } - }, - { - text: gettext('Network Device'), - itemId: 'addnet', - iconCls: 'fa fa-fw fa-exchange black', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.qemu.NetworkEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode, - isCreate: true - }); - win.on('destroy', me.reload, me); - win.show(); - } - }, - efidisk_menuitem, - { - text: gettext('USB Device'), - itemId: 'addusb', - iconCls: 'fa fa-fw fa-usb black', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.USBEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', me.reload, me); - win.show(); - } - }, - { - text: gettext('PCI Device'), - itemId: 'addpci', - iconCls: 'pve-itype-icon-pci', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.PCIEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', me.reload, me); - win.show(); - } - }, - { - text: gettext('Serial Port'), - itemId: 'addserial', - iconCls: 'pve-itype-icon-serial', - disabled: !caps.vms['VM.Config.Options'], - handler: function() { - var win = Ext.create('PVE.qemu.SerialEdit', { - url: '/api2/extjs/' + baseurl - }); - win.on('destroy', me.reload, me); - win.show(); - } - }, - { - text: gettext('CloudInit Drive'), - itemId: 'addci', - iconCls: 'fa fa-fw fa-cloud black', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.CIDriveEdit', { - url: '/api2/extjs/' + baseurl, - pveSelNode: me.pveSelNode - }); - win.on('destroy', me.reload, me); - win.show(); - } - }, - { - text: gettext('Audio Device'), - itemId: 'addaudio', - iconCls: 'fa fa-fw fa-volume-up black', - disabled: !caps.vms['VM.Config.HWType'], - handler: function() { - var win = Ext.create('PVE.qemu.AudioEdit', { - url: '/api2/extjs/' + baseurl, - isCreate: true, - isAdd: true - }); - win.on('destroy', me.reload, me); - win.show(); - } - }, - { - text: gettext("VirtIO RNG"), - itemId: 'addrng', - iconCls: 'pve-itype-icon-die', - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.qemu.RNGEdit', { - url: '/api2/extjs/' + baseurl, - isCreate: true, - isAdd: true - }); - win.on('destroy', me.reload, me); - win.show(); - } - } - ] - }) - }, - remove_btn, - edit_btn, - resize_btn, - move_btn, - revert_btn - ], - rows: rows, - sorterFn: sorterFn, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - me.mon(me.getStore(), 'datachanged', function() { - set_button_status(); - }); - } -}); -Ext.define('PVE.qemu.ScsiHwEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.applyIf(me, { - subject: gettext('SCSI Controller Type'), - items: { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - fieldLabel: gettext('Type') - } - }); - - me.callParent(); - - me.load(); - } -}); -Ext.define('PVE.qemu.BiosEdit', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveQemuBiosEdit', - - onlineHelp: 'qm_bios_and_uefi', - subject: 'BIOS', - autoLoad: true, - - viewModel: { - data: { - bios: '__default__', - efidisk0: false, - }, - formulas: { - showEFIDiskHint: (get) => get('bios') === 'ovmf' && !get('efidisk0'), - }, - }, - - items: [ - { - xtype: 'pveQemuBiosSelector', - onlineHelp: 'qm_bios_and_uefi', - name: 'bios', - value: '__default__', - bind: '{bios}', - fieldLabel: 'BIOS', - }, - { - xtype: 'displayfield', - name: 'efidisk0', - bind: '{efidisk0}', - hidden: true, - }, - { - xtype: 'displayfield', - userCls: 'pmx-hint', - value: gettext('You need to add an EFI disk for storing the EFI settings. See the online help for details.'), - bind: { - hidden: '{!showEFIDiskHint}', - }, - }, - ], -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.Options', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.PVE.qemu.Options'], - - onlineHelp: 'qm_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - name: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Name'), - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Name'), - items: { - xtype: 'inputpanel', - items:{ - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - }, - onGetValues: function(values) { - var params = values; - if (values.name === undefined || - values.name === null || - values.name === '') { - params = { 'delete':'name'}; - } - return params; - } - } - } : undefined - }, - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'qm_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.OSTypeEdit' : undefined, - renderer: PVE.Utils.render_kvm_ostype, - defaultValue: 'other' - }, - bootdisk: { - visible: false - }, - boot: { - header: gettext('Boot Order'), - defaultValue: 'cdn', - editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.BootOrderEdit' : undefined, - multiKey: ['boot', 'bootdisk'], - renderer: function(order, metaData, record, rowIndex, colIndex, store, pending) { - var i; - var text = ''; - var bootdisk = me.getObjectValue('bootdisk', undefined, pending); - order = order || 'cdn'; - for (i = 0; i < order.length; i++) { - var sel = order.substring(i, i + 1); - if (text) { - text += ', '; - } - if (sel === 'c') { - if (bootdisk) { - text += "Disk '" + bootdisk + "'"; - } else { - text += "Disk"; - } - } else if (sel === 'n') { - text += 'Network'; - } else if (sel === 'a') { - text += 'Floppy'; - } else if (sel === 'd') { - text += 'CD-ROM'; - } else { - text += sel; - } - } - return text; - } - }, - tablet: { - header: gettext('Use tablet for pointer'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use tablet for pointer'), - items: { - xtype: 'proxmoxcheckbox', - name: 'tablet', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - hotplug: { - header: gettext('Hotplug'), - defaultValue: 'disk,network,usb', - renderer: PVE.Utils.render_hotplug_features, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hotplug'), - items: { - xtype: 'pveHotplugFeatureSelector', - name: 'hotplug', - value: '', - multiSelect: true, - fieldLabel: gettext('Hotplug'), - allowBlank: true - } - } : undefined - }, - acpi: { - header: gettext('ACPI support'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('ACPI support'), - items: { - xtype: 'proxmoxcheckbox', - name: 'acpi', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - kvm: { - header: gettext('KVM hardware virtualization'), - defaultValue: true, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.HWType'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('KVM hardware virtualization'), - items: { - xtype: 'proxmoxcheckbox', - name: 'kvm', - checked: true, - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - freeze: { - header: gettext('Freeze CPU at startup'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.PowerMgmt'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Freeze CPU at startup'), - items: { - xtype: 'proxmoxcheckbox', - name: 'freeze', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Freeze CPU at startup') - } - } : undefined - }, - localtime: { - header: gettext('Use local time for RTC'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Use local time for RTC'), - items: { - xtype: 'proxmoxcheckbox', - name: 'localtime', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - labelWidth: 140, - fieldLabel: gettext('Use local time for RTC') - } - } : undefined - }, - startdate: { - header: gettext('RTC start date'), - defaultValue: 'now', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('RTC start date'), - items: { - xtype: 'proxmoxtextfield', - name: 'startdate', - deleteEmpty: true, - value: 'now', - fieldLabel: gettext('RTC start date'), - vtype: 'QemuStartDate', - allowBlank: true - } - } : undefined - }, - smbios1: { - header: gettext('SMBIOS settings (type1)'), - defaultValue: '', - renderer: Ext.String.htmlEncode, - editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.Smbios1Edit' : undefined - }, - agent: { - header: 'QEMU Guest Agent', - defaultValue: false, - renderer: PVE.Utils.render_qga_features, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Qemu Agent'), - width: 350, - items: { - xtype: 'pveAgentFeatureSelector', - name: 'agent' - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - spice_enhancements: { - header: gettext('Spice Enhancements'), - defaultValue: false, - renderer: PVE.Utils.render_spice_enhancements, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Spice Enhancements'), - onlineHelp: 'qm_spice_enhancements', - items: { - xtype: 'pveSpiceEnhancementSelector', - name: 'spice_enhancements', - } - } : undefined - }, - vmstatestorage: { - header: gettext('VM State storage'), - defaultValue: '', - renderer: val => val || gettext('Automatic'), - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('VM State storage'), - onlineHelp: 'chapter_virtual_machines', // FIXME: use 'qm_vmstatestorage' once available - width: 350, - items: { - xtype: 'pveStorageSelector', - storageContent: 'images', - allowBlank: true, - emptyText: gettext("Automatic (Storage used by the VM, or 'local')"), - autoSelect: false, - deleteEmpty: true, - skipEmptyText: true, - nodename: nodename, - name: 'vmstatestorage', - } - } : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config'; - - var edit_btn = new Ext.Button({ - text: gettext('Edit'), - disabled: true, - handler: function() { me.run_editor(); } - }); - - var revert_btn = new PVE.button.PendingRevert(); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - - var key = rec.data.key; - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var rowdef = rows[key]; - - edit_btn.setDisabled(!rowdef.editor); - revert_btn.setDisabled(!pending); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/qemu/" + vmid + "/pending", - interval: 5000, - cwidth1: 250, - tbar: [ edit_btn, revert_btn ], - rows: rows, - editorConfig: { - url: "/api2/extjs/" + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - me.mon(me.getStore(), 'datachanged', function() { - set_button_status(); - }); - } -}); - -Ext.define('PVE.qemu.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.qemu.Config', - - onlineHelp: 'chapter_virtual_machines', - - initComponent: function() { - var me = this; - var vm = me.pveSelNode.data; - - var nodename = vm.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = vm.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!vm.template; - - var running = !!vm.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + "/qemu/" + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + '/status/' + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var resumeBtn = Ext.create('Ext.Button', { - text: gettext('Resume'), - disabled: !caps.vms['VM.PowerMgmt'], - hidden: true, - handler: function() { - vm_command('resume'); - }, - iconCls: 'fa fa-play' - }); - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'qemu', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'qemu'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('qmtemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = vm.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - itemId: 'removeBtn', - disabled: !caps.vms['VM.Allocate'], - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'VM', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('qmshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items: [{ - text: gettext('Reboot'), - disabled: !caps.vms['VM.PowerMgmt'], - tooltip: Ext.String.format(gettext('Shutdown, apply pending changes and reboot {0}'), 'VM'), - confirmMsg: Proxmox.Utils.format_task_description('qmreboot', vmid), - handler: function() { - vm_command("reboot"); - }, - iconCls: 'fa fa-refresh' - },{ - text: gettext('Pause'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmpause', vmid), - handler: function() { - vm_command("suspend"); - }, - iconCls: 'fa fa-pause' - },{ - text: gettext('Hibernate'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('qmsuspend', vmid), - tooltip: gettext('Suspend to disk'), - handler: function() { - vm_command("suspend", { todisk: 1 }); - }, - iconCls: 'fa fa-download' - },{ - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - dangerous: true, - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'), - confirmMsg: Proxmox.Utils.format_task_description('qmstop', vmid), - handler: function() { - vm_command("stop", { timeout: 30 }); - }, - iconCls: 'fa fa-stop' - },{ - text: gettext('Reset'), - disabled: !caps.vms['VM.PowerMgmt'], - tooltip: Ext.String.format(gettext('Reset {0} immediately'), 'VM'), - confirmMsg: Proxmox.Utils.format_task_description('qmreset', vmid), - handler: function() { - vm_command("reset"); - }, - iconCls: 'fa fa-bolt' - }] - }, - iconCls: 'fa fa-power-off' - }); - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - hidden: template, - consoleType: 'kvm', - consoleName: vm.name, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - Ext.apply(me, { - title: Ext.String.format(gettext("Virtual Machine {0} on node '{1}'"), vm.text, nodename), - hstateid: 'kvmtab', - tbarSpacing: false, - tbar: [ statusTxt, '->', resumeBtn, startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveGuestSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push({ - title: gettext('Console'), - itemId: 'console', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'kvm', - nodename: nodename - }); - } - - me.items.push( - { - title: gettext('Hardware'), - itemId: 'hardware', - iconCls: 'fa fa-desktop', - xtype: 'PVE.qemu.HardwareView' - }, - { - title: 'Cloud-Init', - itemId: 'cloudinit', - iconCls: 'fa fa-cloud', - xtype: 'pveCiPanel' - }, - { - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options', - xtype: 'PVE.qemu.Options' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - xtype: 'proxmoxNodeTasks', - iconCls: 'fa fa-list', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Monitor'] && !template) { - me.items.push({ - title: gettext('Monitor'), - iconCls: 'fa fa-eye', - itemId: 'monitor', - xtype: 'pveQemuMonitor' - }); - } - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback'] || - caps.vms['VM.Audit']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - type: 'qemu', - xtype: 'pveGuestSnapshotTree', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - var prevQMPStatus = 'unknown'; - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var qmpstatus; - var spice = false; - var xtermjs = false; - var lock; - - if (!success) { - status = qmpstatus = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('qmpstatus'); - qmpstatus = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - - spice = s.data.get('spice') ? true : false; - xtermjs = s.data.get('serial') ? true : false; - - } - - if (template) { - return; - } - - var resume = (['prelaunch', 'paused', 'suspended'].indexOf(qmpstatus) !== -1); - - if (resume || lock === 'suspended') { - startBtn.setVisible(false); - resumeBtn.setVisible(true); - } else { - startBtn.setVisible(true); - resumeBtn.setVisible(false); - } - - consoleBtn.setEnableSpice(spice); - consoleBtn.setEnableXtermJS(xtermjs); - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - - let wasStopped = ['prelaunch', 'stopped', 'suspended'].indexOf(prevQMPStatus) !== -1; - if (wasStopped && qmpstatus === 'running') { - let con = me.down('#console'); - if (con) { - con.reload(); - } - } - - prevQMPStatus = qmpstatus; - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.CreateWizard', { - extend: 'PVE.window.Wizard', - alias: 'widget.pveQemuCreateWizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - current: { - scsihw: '' - } - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('Virtual Machine'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'qm_general_settings', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', - guestType: 'qemu', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'textfield', - name: 'name', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Name'), - allowBlank: true - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - } - ], - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Start at boot') - } - ], - advancedColumn2: [ - { - xtype: 'textfield', - name: 'order', - defaultValue: '', - emptyText: 'any', - labelWidth: 120, - fieldLabel: gettext('Start/Shutdown order') - }, - { - xtype: 'textfield', - name: 'up', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Startup delay') - }, - { - xtype: 'textfield', - name: 'down', - defaultValue: '', - emptyText: 'default', - labelWidth: 120, - fieldLabel: gettext('Shutdown timeout') - } - ], - onGetValues: function(values) { - - ['name', 'pool', 'onboot', 'agent'].forEach(function(field) { - if (!values[field]) { - delete values[field]; - } - }); - - var res = PVE.Parser.printStartup({ - order: values.order, - up: values.up, - down: values.down - }); - - if (res) { - values.startup = res; - } - - delete values.order; - delete values.up; - delete values.down; - - return values; - } - }, - { - xtype: 'container', - layout: 'hbox', - defaults: { - flex: 1, - padding: '0 10' - }, - title: gettext('OS'), - items: [ - { - xtype: 'pveQemuCDInputPanel', - bind: { - nodename: '{nodename}' - }, - confid: 'ide2', - insideWizard: true - }, - { - xtype: 'pveQemuOSTypePanel', - insideWizard: true - } - ] - }, - { - xtype: 'pveQemuSystemPanel', - title: gettext('System'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuHDInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Hard Disk'), - isCreate: true, - insideWizard: true - }, - { - xtype: 'pveQemuProcessorPanel', - insideWizard: true, - title: gettext('CPU') - }, - { - xtype: 'pveQemuMemoryPanel', - insideWizard: true, - title: gettext('Memory') - }, - { - xtype: 'pveQemuNetworkInputPanel', - bind: { - nodename: '{nodename}' - }, - title: gettext('Network'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var kv = this.up('window').getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete') { // ignore - return; - } - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/qemu', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response){ - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - - -Ext.define('PVE.qemu.USBInputPanel', { - extend: 'Proxmox.panel.InputPanel', - mixins: ['Proxmox.Mixin.CBind' ], - - autoComplete: false, - onlineHelp: 'qm_usb_passthrough', - - viewModel: { - data: {} - }, - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - }, - - onGetValues: function(values) { - var me = this; - if (!me.confid) { - for (let i = 0; i < 6; i++) { - let id = 'usb' + i.toString(); - if (!me.vmconfig[id]) { - me.confid = id; - break; - } - } - } - var val = ""; - var type = me.down('radiofield').getGroupValue(); - switch (type) { - case 'spice': - val = 'spice'; - break; - case 'hostdevice': - case 'port': - val = 'host=' + values[type]; - delete values[type]; - break; - default: - throw "invalid type selected"; - } - - if (values.usb3) { - delete values.usb3; - val += ',usb3=1'; - } - values[me.confid] = val; - return values; - }, - - items: [ - { - xtype: 'fieldcontainer', - defaultType: 'radiofield', - layout: 'fit', - items: [ - { - name: 'usb', - inputValue: 'spice', - boxLabel: gettext('Spice Port'), - submitValue: false, - checked: true - }, - { - name: 'usb', - inputValue: 'hostdevice', - boxLabel: gettext('Use USB Vendor/Device ID'), - reference: 'hostdevice', - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - type: 'device', - name: 'hostdevice', - cbind: { pveSelNode: '{pveSelNode}' }, - bind: { disabled: '{!hostdevice.checked}' }, - editable: true, - allowBlank: false, - fieldLabel: gettext('Choose Device'), - labelAlign: 'right', - }, - { - name: 'usb', - inputValue: 'port', - boxLabel: gettext('Use USB Port'), - reference: 'port', - submitValue: false - }, - { - xtype: 'pveUSBSelector', - disabled: true, - name: 'port', - cbind: { pveSelNode: '{pveSelNode}' }, - bind: { disabled: '{!port.checked}' }, - editable: true, - type: 'port', - allowBlank: false, - fieldLabel: gettext('Choose Port'), - labelAlign: 'right', - }, - { - xtype: 'checkbox', - name: 'usb3', - inputValue: true, - checked: true, - reference: 'usb3', - fieldLabel: gettext('Use USB3') - } - ] - } - ] -}); - -Ext.define('PVE.qemu.USBEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - width: 400, - subject: gettext('USB Device'), - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.USBInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.isCreate) { - return; - } - - var data = response.result.data[me.confid].split(','); - var port, hostdevice, usb3 = false; - var type = 'spice'; - - for (let i = 0; i < data.length; i++) { - if (/^(host=)?(0x)?[a-zA-Z0-9]{4}\:(0x)?[a-zA-Z0-9]{4}$/.test(data[i])) { - hostdevice = data[i]; - hostdevice = hostdevice.replace('host=', '').replace('0x',''); - type = 'hostdevice'; - } else if (/^(host=)?(\d+)\-(\d+(\.\d+)*)$/.test(data[i])) { - port = data[i]; - port = port.replace('host=', ''); - type = 'port'; - } - - if (/^usb3=(1|on|true)$/.test(data[i])) { - usb3 = true; - } - } - var values = { - usb : type, - hostdevice: hostdevice, - port: port, - usb3: usb3 - }; - - ipanel.setValues(values); - } - }); - } -}); -Ext.define('PVE.qemu.PCIInputPanel', { - extend: 'Proxmox.panel.InputPanel', - - onlineHelp: 'qm_pci_passthrough', - - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; - - var hostpci = me.vmconfig[me.confid] || ''; - - var values = PVE.Parser.parsePropertyString(hostpci, 'host'); - if (values.host) { - if (!values.host.match(/^[0-9a-f]{4}:/i)) { // add optional domain - values.host = "0000:" + values.host; - } - if (values.host.length < 11) { // 0000:00:00 format not 0000:00:00.0 - values.host += ".0"; - values.multifunction = true; - } - } - - values['x-vga'] = PVE.Parser.parseBoolean(values['x-vga'], 0); - values.pcie = PVE.Parser.parseBoolean(values.pcie, 0); - values.rombar = PVE.Parser.parseBoolean(values.rombar, 1); - - me.setValues(values); - if (!me.vmconfig.machine || me.vmconfig.machine.indexOf('q35') === -1) { - // machine is not set to some variant of q35, so we disable pcie - var pcie = me.down('field[name=pcie]'); - pcie.setDisabled(true); - pcie.setBoxLabel(gettext('Q35 only')); - } - - if (values.romfile) { - me.down('field[name=romfile]').setVisible(true); - } - }, - - onGetValues: function(values) { - var me = this; - var ret = {}; - if(!me.confid) { - var i; - for (i = 0; i < 5; i++) { - if (!me.vmconfig['hostpci' + i.toString()]) { - me.confid = 'hostpci' + i.toString(); - break; - } - } - } - // remove optional '0000' domain - if (values.host.substring(0,5) === '0000:') { - values.host = values.host.substring(5); - } - if (values.multifunction) { - // modify host to skip the '.X' - values.host = values.host.substring(0, values.host.indexOf('.')); - delete values.multifunction; - } - - if (values.rombar) { - delete values.rombar; - } else { - values.rombar = 0; - } - - if (!values.romfile) { - delete values.romfile; - } - - ret[me.confid] = PVE.Parser.printPropertyString(values, 'host'); - return ret; - }, - - initComponent: function() { - var me = this; - - me.nodename = me.pveSelNode.data.node; - if (!me.nodename) { - throw "no node name specified"; - } - - me.column1 = [ - { - xtype: 'pvePCISelector', - fieldLabel: gettext('Device'), - name: 'host', - nodename: me.nodename, - allowBlank: false, - onLoadCallBack: function(store, records, success) { - if (!success || !records.length) { - return; - } - - var first = records[0]; - if (first.data.iommugroup === -1) { - // no iommu groups - var warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - value: 'No IOMMU detected, please activate it.' + - 'See Documentation for further information.', - userCls: 'pmx-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } - }, - listeners: { - change: function(pcisel, value) { - if (!value) { - return; - } - var pcidev = pcisel.getStore().getById(value); - var mdevfield = me.down('field[name=mdev]'); - mdevfield.setDisabled(!pcidev || !pcidev.data.mdev); - if (!pcidev) { - return; - } - var id = pcidev.data.id.substring(0,5); // 00:00 - var iommu = pcidev.data.iommugroup; - // try to find out if there are more devices - // in that iommu group - if (iommu !== -1) { - var count = 0; - pcisel.getStore().each(function(record) { - if (record.data.iommugroup === iommu && - record.data.id.substring(0,5) !== id) - { - count++; - return false; - } - }); - var warning = me.down('#iommuwarning'); - if (count && !warning) { - warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - itemId: 'iommuwarning', - value: 'The selected Device is not in a seperate' + - 'IOMMU group, make sure this is intended.', - userCls: 'pmx-hint' - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } else if (!count && warning) { - me.remove(warning); - } - } - if (pcidev.data.mdev) { - mdevfield.setPciID(value); - } - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('All Functions'), - name: 'multifunction' - } - ]; - - me.column2 = [ - { - xtype: 'pveMDevSelector', - name: 'mdev', - disabled: true, - fieldLabel: gettext('MDev Type'), - nodename: me.nodename, - listeners: { - change: function(field, value) { - var mf = me.down('field[name=multifunction]'); - if (!!value) { - mf.setValue(false); - } - mf.setDisabled(!!value); - } - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Primary GPU'), - name: 'x-vga' - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'ROM-Bar', - name: 'rombar' - }, - { - xtype: 'displayfield', - submitValue: true, - hidden: true, - fieldLabel: 'ROM-File', - name: 'romfile' - } - ]; - - me.advancedColumn2 = [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'PCI-Express', - name: 'pcie' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.PCIEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('PCI Device'), - - - initComponent : function() { - var me = this; - - me.isCreate = !me.confid; - - var ipanel = Ext.create('PVE.qemu.PCIInputPanel', { - confid: me.confid, - pveSelNode: me.pveSelNode - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -/*jslint confusion: true */ -Ext.define('PVE.qemu.SerialnputPanel', { - extend: 'Proxmox.panel.InputPanel', - - autoComplete: false, - - setVMConfig: function(vmconfig) { - var me = this, i; - me.vmconfig = vmconfig; - - for (i = 0; i < 4; i++) { - var port = 'serial' + i.toString(); - if (!me.vmconfig[port]) { - me.down('field[name=serialid]').setValue(i); - break; - } - } - - }, - - onGetValues: function(values) { - var me = this; - - var id = 'serial' + values.serialid; - delete values.serialid; - values[id] = 'socket'; - return values; - }, - - items: [ - { - xtype: 'proxmoxintegerfield', - name: 'serialid', - fieldLabel: gettext('Serial Port'), - minValue: 0, - maxValue: 3, - allowBlank: false, - validator: function(id) { - if (!this.rendered) { - return true; - } - var me = this.up('panel'); - if (me.vmconfig !== undefined && Ext.isDefined(me.vmconfig['serial' + id])) { - return "This device is already in use."; - } - return true; - } - } - ] -}); - -Ext.define('PVE.qemu.SerialEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - isAdd: true, - - subject: gettext('Serial Port'), - - initComponent : function() { - var me = this; - - // for now create of (socket) serial port only - me.isCreate = true; - - var ipanel = Ext.create('PVE.qemu.SerialnputPanel', {}); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.window.IPInfo', { - extend: 'Ext.window.Window', - width: 600, - title: gettext('Guest Agent Network Information'), - height: 300, - layout: { - type: 'fit' - }, - modal: true, - items: [ - { - xtype: 'grid', - store: {}, - emptyText: gettext('No network information'), - columns: [ - { - dataIndex: 'name', - text: gettext('Name'), - flex: 3 - }, - { - dataIndex: 'hardware-address', - text: gettext('MAC address'), - width: 140 - }, - { - dataIndex: 'ip-addresses', - text: gettext('IP address'), - align: 'right', - flex: 4, - renderer: function(val) { - if (!Ext.isArray(val)) { - return ''; - } - var ips = []; - val.forEach(function(ip) { - var addr = ip['ip-address']; - var pref = ip.prefix; - if (addr && pref) { - ips.push(addr + '/' + pref); - } - }); - return ips.join('
'); - } - } - ] - } - ] -}); - -Ext.define('PVE.qemu.AgentIPView', { - extend: 'Ext.container.Container', - xtype: 'pveAgentIPView', - - layout: { - type: 'hbox', - align: 'top' - }, - - nics: [], - - items: [ - { - xtype: 'box', - html: ' IPs' - }, - { - xtype: 'container', - flex: 1, - layout: { - type: 'vbox', - align: 'right', - pack: 'end' - }, - items: [ - { - xtype: 'label', - flex: 1, - itemId: 'ipBox', - style: { - 'text-align': 'right' - } - }, - { - xtype: 'button', - itemId: 'moreBtn', - hidden: true, - ui: 'default-toolbar', - handler: function(btn) { - var me = this.up('pveAgentIPView'); - - var win = Ext.create('PVE.window.IPInfo'); - win.down('grid').getStore().setData(me.nics); - win.show(); - }, - text: gettext('More') - } - ] - } - ], - - getDefaultIps: function(nics) { - var me = this; - var ips = []; - nics.forEach(function(nic) { - if (nic['hardware-address'] && - nic['hardware-address'] != '00:00:00:00:00:00') { - - var nic_ips = nic['ip-addresses'] || []; - nic_ips.forEach(function(ip) { - var p = ip['ip-address']; - // show 2 ips at maximum - if (ips.length < 2) { - ips.push(p); - } - }); - } - }); - - return ips; - }, - - startIPStore: function(store, records, success) { - var me = this; - let agentRec = store.getById('agent'); - let state = store.getById('status'); - - me.agent = (agentRec && agentRec.data.value === 1); - me.running = (state && state.data.value === 'running'); - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!caps.vms['VM.Monitor']) { - var errorText = gettext("Requires '{0}' Privileges"); - me.updateStatus(false, Ext.String.format(errorText, 'VM.Monitor')); - return; - } - - if (me.agent && me.running && me.ipStore.isStopped) { - me.ipStore.startUpdate(); - } else if (me.ipStore.isStopped) { - me.updateStatus(); - } - }, - - updateStatus: function(unsuccessful, defaulttext) { - var me = this; - var text = defaulttext || gettext('No network information'); - var more = false; - if (unsuccessful) { - text = gettext('Guest Agent not running'); - } else if (me.agent && me.running) { - if (Ext.isArray(me.nics) && me.nics.length) { - more = true; - var ips = me.getDefaultIps(me.nics); - if (ips.length !== 0) { - text = ips.join('
'); - } - } else if (me.nics && me.nics.error) { - var msg = gettext('Cannot get info from Guest Agent
Error: {0}'); - text = Ext.String.format(text, me.nics.error.desc); - } - } else if (me.agent) { - text = gettext('Guest Agent not running'); - } else { - text = gettext('No Guest Agent configured'); - } - - var ipBox = me.down('#ipBox'); - ipBox.update(text); - - var moreBtn = me.down('#moreBtn'); - moreBtn.setVisible(more); - }, - - initComponent: function() { - var me = this; - - if (!me.rstore) { - throw 'rstore not given'; - } - - if (!me.pveSelNode) { - throw 'pveSelNode not given'; - } - - var nodename = me.pveSelNode.data.node; - var vmid = me.pveSelNode.data.vmid; - - me.ipStore = Ext.create('Proxmox.data.UpdateStore', { - interval: 10000, - storeid: 'pve-qemu-agent-' + vmid, - method: 'POST', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + nodename + '/qemu/' + vmid + '/agent/network-get-interfaces' - } - }); - - me.callParent(); - - me.mon(me.ipStore, 'load', function(store, records, success) { - if (records && records.length) { - me.nics = records[0].data.result; - } else { - me.nics = undefined; - } - me.updateStatus(!success); - }); - - me.on('destroy', me.ipStore.stopUpdate); - - // if we already have info about the vm, use it immediately - if (me.rstore.getCount()) { - me.startIPStore(me.rstore, me.rstore.getData(), false); - } - - // check if the guest agent is there on every statusstore load - me.mon(me.rstore, 'load', me.startIPStore, me); - } -}); -Ext.define('PVE.qemu.CloudInit', { - extend: 'Proxmox.grid.PendingObjectGrid', - xtype: 'pveCiPanel', - - onlineHelp: 'qm_cloud_init', - - tbar: [ - { - xtype: 'proxmoxButton', - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var me = this.up('grid'); - var warn = gettext('Are you sure you want to remove entry {0}'); - - var entry = rec.data.key; - var msg = Ext.String.format(warn, "'" - + me.renderKey(entry, {}, rec) + "'"); - - return msg; - }, - enableFn: function(record) { - var me = this.up('grid'); - var caps = Ext.state.Manager.get('GuiCap'); - if (me.rows[record.data.key].never_delete || - !caps.vms['VM.Config.Network']) { - return false; - } - - if (record.data.key === 'cipassword' && !record.data.value) { - return false; - } - return true; - }, - handler: function() { - var me = this.up('grid'); - var records = me.getSelection(); - if (!records || !records.length) { - return; - } - - var id = records[0].data.key; - var match = id.match(/^net(\d+)$/); - if (match) { - id = 'ipconfig' + match[1]; - } - - var params = {}; - params['delete'] = id; - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: params, - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }, - callback: function() { - me.reload(); - } - }); - }, - text: gettext('Remove') - }, - { - xtype: 'proxmoxButton', - disabled: true, - enableFn: function(rec) { - let me = this.up('pveCiPanel'); - return !!me.rows[rec.data.key].editor; - }, - handler: function() { - var me = this.up('grid'); - me.run_editor(); - }, - text: gettext('Edit') - }, - '-', - { - xtype: 'button', - itemId: 'savebtn', - text: gettext('Regenerate Image'), - handler: function() { - var me = this.up('grid'); - var eject_params = {}; - var insert_params = {}; - var disk = PVE.Parser.parseQemuDrive(me.ciDriveId, me.ciDrive); - var storage = ''; - var stormatch = disk.file.match(/^([^\:]+)\:/); - if (stormatch) { - storage = stormatch[1]; - } - eject_params[me.ciDriveId] = 'none,media=cdrom'; - insert_params[me.ciDriveId] = storage + ':cloudinit'; - - var failure = function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - }; - - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: eject_params, - failure: failure, - callback: function() { - Proxmox.Utils.API2Request({ - url: me.baseurl + '/config', - waitMsgTarget: me, - method: 'PUT', - params: insert_params, - failure: failure, - callback: function() { - me.reload(); - } - }); - } - }); - } - } - ], - - border: false, - - set_button_status: function(rstore, records, success) { - if (!success || records.length < 1) { - return; - } - var me = this; - var found; - records.forEach(function(record) { - if (found) { - return; - } - var id = record.data.key; - var value = record.data.value; - var ciregex = new RegExp("vm-" + me.pveSelNode.data.vmid + "-cloudinit"); - if (id.match(/^(ide|scsi|sata)\d+$/) && ciregex.test(value)) { - found = id; - me.ciDriveId = found; - me.ciDrive = value; - } - }); - - me.down('#savebtn').setDisabled(!found); - me.setDisabled(!found); - if (!found) { - me.getView().mask(gettext('No CloudInit Drive found'), ['pve-static-mask']); - } else { - me.getView().unmask(); - } - }, - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rows = me.rows; - var rowdef = rows[key] || {}; - - var icon = ""; - if (rowdef.iconCls) { - icon = ' '; - } - return icon + (rowdef.header || key); - }, - - listeners: { - activate: function () { - var me = this; - me.rstore.startUpdate(); - }, - itemdblclick: function() { - var me = this; - me.run_editor(); - } - }, - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - var caps = Ext.state.Manager.get('GuiCap'); - me.baseurl = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid; - me.url = me.baseurl + '/pending'; - me.editorConfig.url = me.baseurl + '/config'; - me.editorConfig.pveSelNode = me.pveSelNode; - - /*jslint confusion: true*/ - /* editor is string and object */ - me.rows = { - ciuser: { - header: gettext('User'), - iconCls: 'fa fa-user', - never_delete: true, - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('User'), - items: [ - { - xtype: 'proxmoxtextfield', - deleteEmpty: true, - emptyText: Proxmox.Utils.defaultText, - fieldLabel: gettext('User'), - name: 'ciuser' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.defaultText; - } - }, - cipassword: { - header: gettext('Password'), - iconCls: 'fa fa-unlock', - defaultValue: '', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Password'), - items: [ - { - xtype: 'proxmoxtextfield', - inputType: 'password', - deleteEmpty: true, - emptyText: Proxmox.Utils.noneText, - fieldLabel: gettext('Password'), - name: 'cipassword' - } - ] - } : undefined, - renderer: function(value) { - return value || Proxmox.Utils.noneText; - } - }, - searchdomain: { - header: gettext('DNS domain'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - nameserver: { - header: gettext('DNS servers'), - iconCls: 'fa fa-globe', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - never_delete: true, - defaultValue: gettext('use host settings') - }, - sshkeys: { - header: gettext('SSH public key'), - iconCls: 'fa fa-key', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.SSHKeyEdit' : undefined, - never_delete: true, - renderer: function(value) { - value = decodeURIComponent(value); - var keys = value.split('\n'); - var text = []; - keys.forEach(function(key) { - if (key.length) { - // First erase all quoted strings (eg. command="foo" - var v = key.replace(/"(?:\\.|[^"\\])*"/g, ''); - // Now try to detect the comment: - var res = v.match(/^\s*(\S+\s+)?(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)\s+\S+\s+(.*?)\s*$/, ''); - if (res) { - key = Ext.String.htmlEncode(res[2]); - if (res[1]) { - key += ' (' + gettext('with options') + ')'; - } - text.push(key); - return; - } - // Most likely invalid at this point, so just stick to - // the old value. - text.push(Ext.String.htmlEncode(key)); - } - }); - if (text.length) { - return text.join('
'); - } else { - return Proxmox.Utils.noneText; - } - }, - defaultValue: '' - } - }; - var i; - var ipconfig_renderer = function(value, md, record, ri, ci, store, pending) { - var id = record.data.key; - var match = id.match(/^net(\d+)$/); - var val = ''; - if (match) { - val = me.getObjectValue('ipconfig'+match[1], '', pending); - } - return val; - }; - for (i = 0; i < 32; i++) { - // we want to show an entry for every network device - // even if it is empty - me.rows['net' + i.toString()] = { - multiKey: ['ipconfig' + i.toString(), 'net' + i.toString()], - header: gettext('IP Config') + ' (net' + i.toString() +')', - editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.IPConfigEdit' : undefined, - iconCls: 'fa fa-exchange', - renderer: ipconfig_renderer - }; - me.rows['ipconfig' + i.toString()] = { - visible: false - }; - } - /*jslint confusion: false*/ - - PVE.Utils.forEachBus(['ide', 'scsi', 'sata'], function(type, id) { - me.rows[type+id] = { - visible: false - }; - }); - me.callParent(); - me.mon(me.rstore, 'load', me.set_button_status, me); - } -}); -Ext.define('PVE.qemu.CIDriveInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveCIDriveInputPanel', - - insideWizard: false, - - vmconfig: {}, // used to select usused disks - - onGetValues: function(values) { - var me = this; - - var drive = {}; - var params = {}; - drive.file = values.hdstorage + ":cloudinit"; - drive.format = values.diskformat; - params[values.controller + values.deviceid] = PVE.Parser.printQemuDrive(drive); - return params; - }, - - setNodename: function(nodename) { - var me = this; - me.down('#hdstorage').setNodename(nodename); - me.down('#hdimage').setStorage(undefined, nodename); - }, - - setVMConfig: function(config) { - var me = this; - me.down('#drive').setVMConfig(config, 'cdrom'); - }, - - initComponent : function() { - var me = this; - - me.drive = {}; - - me.items = [ - { - xtype: 'pveControllerSelector', - noVirtIO: true, - itemId: 'drive', - fieldLabel: gettext('CloudInit Drive'), - name: 'drive' - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'storselector', - storageContent: 'images', - nodename: me.nodename, - hideSize: true - } - ]; - me.callParent(); - } -}); - -Ext.define('PVE.qemu.CIDriveEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveCIDriveEdit', - - isCreate: true, - subject: gettext('CloudInit Drive'), - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.items = [{ - xtype: 'pveCIDriveInputPanel', - itemId: 'cipanel', - nodename: nodename - }]; - - me.callParent(); - - me.load({ - success: function(response, opts) { - me.down('#cipanel').setVMConfig(response.result.data); - } - }); - } -}); -Ext.define('PVE.qemu.SSHKeyInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSSHKeyInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - if (values.sshkeys) { - values.sshkeys.trim(); - } - if (!values.sshkeys.length) { - values = {}; - values['delete'] = 'sshkeys'; - return values; - } else { - values.sshkeys = encodeURIComponent(values.sshkeys); - } - return values; - }, - - items: [ - { - xtype: 'textarea', - itemId: 'sshkeys', - name: 'sshkeys', - height: 250 - }, - { - xtype: 'filebutton', - itemId: 'filebutton', - name: 'file', - text: gettext('Load SSH Key File'), - fieldLabel: 'test', - listeners: { - change: function(btn, e, value) { - var me = this.up('inputpanel'); - e = e.event; - Ext.Array.each(e.target.files, function(file) { - PVE.Utils.loadSSHKeyFromFile(file, function(res) { - var keysField = me.down('#sshkeys'); - var old = keysField.getValue(); - keysField.setValue(old + res); - }); - }); - btn.reset(); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.callParent(); - if (!window.FileReader) { - me.down('#filebutton').setVisible(false); - } - - } -}); - -Ext.define('PVE.qemu.SSHKeyEdit', { - extend: 'Proxmox.window.Edit', - - width: 800, - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.SSHKeyInputPanel'); - - Ext.apply(me, { - subject: gettext('SSH Keys'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.create) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (data.sshkeys) { - data.sshkeys = decodeURIComponent(data.sshkeys); - ipanel.setValues(data); - } - } - }); - } - } -}); -Ext.define('PVE.qemu.IPConfigPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveIPConfigPanel', - - insideWizard: false, - - vmconfig: {}, - - onGetValues: function(values) { - var me = this; - - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - - var params = {}; - - var cfg = PVE.Parser.printIPConfig(values); - if (cfg === '') { - params['delete'] = [me.confid]; - } else { - params[me.confid] = cfg; - } - return params; - }, - - setVMConfig: function(config) { - var me = this; - me.vmconfig = config; - }, - - setIPConfig: function(confid, data) { - var me = this; - - me.confid = confid; - - if (data.ip === 'dhcp') { - data.ipv4mode = data.ip; - data.ip = ''; - } else { - data.ipv4mode = 'static'; - } - if (data.ip6 === 'dhcp' || data.ip6 === 'auto') { - data.ipv6mode = data.ip6; - data.ip6 = ''; - } else { - data.ipv6mode = 'static'; - } - - me.ipconfig = data; - me.setValues(me.ipconfig); - }, - - initComponent : function() { - var me = this; - - me.ipconfig = {}; - - me.column1 = [ - { - xtype: 'displayfield', - fieldLabel: gettext('Network Device'), - value: me.netid - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv4') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv4mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: '', - disabled: true, - fieldLabel: gettext('IPv4/CIDR') - }, - { - xtype: 'textfield', - name: 'gw', - value: '', - vtype: 'IPAddress', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv4') +')' - } - ]; - - me.column2 = [ - { - xtype: 'displayfield' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: gettext('IPv6') + ':' - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: false, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: gettext('DHCP'), - name: 'ipv6mode', - inputValue: 'dhcp', - checked: false, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: '', - vtype: 'IP6CIDRAddress', - disabled: true, - fieldLabel: gettext('IPv6/CIDR') - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: '', - disabled: true, - fieldLabel: gettext('Gateway') + ' (' + gettext('IPv6') +')' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.qemu.IPConfigEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - /*jslint confusion: true */ - - var me = this; - - // convert confid from netX to ipconfigX - var match = me.confid.match(/^net(\d+)$/); - if (match) { - me.netid = me.confid; - me.confid = 'ipconfig' + match[1]; - } - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - me.isCreate = me.confid ? false : true; - - var ipanel = Ext.create('PVE.qemu.IPConfigPanel', { - confid: me.confid, - netid: me.netid, - nodename: nodename - }); - - Ext.applyIf(me, { - subject: gettext('Network Config'), - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - me.vmconfig = response.result.data; - var ipconfig = {}; - var value = me.vmconfig[me.confid]; - if (value) { - ipconfig = PVE.Parser.parseIPConfig(me.confid, value); - if (!ipconfig) { - Ext.Msg.alert(gettext('Error'), gettext('Unable to parse network configuration')); - me.close(); - return; - } - } - ipanel.setIPConfig(me.confid, ipconfig); - ipanel.setVMConfig(me.vmconfig); - } - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.qemu.SystemInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveQemuSystemPanel', - - onlineHelp: 'qm_system_settings', - - viewModel: { - data: { - efi: false, - addefi: true - }, - - formulas: { - efidisk: function(get) { - return get('efi') && get('addefi'); - } - } - }, - - onGetValues: function(values) { - if (values.vga && values.vga.substr(0,6) === 'serial') { - values['serial' + values.vga.substr(6,1)] = 'socket'; - } - - var efidrive = {}; - if (values.hdimage) { - efidrive.file = values.hdimage; - } else if (values.hdstorage) { - efidrive.file = values.hdstorage + ":1"; - } - - if (values.diskformat) { - efidrive.format = values.diskformat; - } - - delete values.hdimage; - delete values.hdstorage; - delete values.diskformat; - - if (efidrive.file) { - values.efidisk0 = PVE.Parser.printQemuDrive(efidrive); - } - - return values; - }, - - controller: { - xclass: 'Ext.app.ViewController', - - scsihwChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('current.scsihw', value); - } - }, - - biosChange: function(field, value) { - var me = this; - if (me.getView().insideWizard) { - me.getViewModel().set('efi', value === 'ovmf'); - } - }, - - control: { - 'pveScsiHwSelector': { - change: 'scsihwChange' - }, - 'pveQemuBiosSelector': { - change: 'biosChange' - } - } - }, - - column1: [ - { - xtype: 'proxmoxKVComboBox', - value: '__default__', - deleteEmpty: false, - fieldLabel: gettext('Graphic card'), - name: 'vga', - comboItems: PVE.Utils.kvm_vga_driver_array() - }, - { - xtype: 'proxmoxcheckbox', - name: 'agent', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Qemu Agent') - } - ], - - column2: [ - { - xtype: 'pveScsiHwSelector', - name: 'scsihw', - value: '__default__', - bind: { - value: '{current.scsihw}' - }, - fieldLabel: gettext('SCSI Controller') - } - ], - - advancedColumn1: [ - { - xtype: 'pveQemuBiosSelector', - name: 'bios', - value: '__default__', - fieldLabel: 'BIOS' - }, - { - xtype: 'proxmoxcheckbox', - bind: { - value: '{addefi}', - hidden: '{!efi}', - disabled: '{!efi}' - }, - hidden: true, - submitValue: false, - disabled: true, - fieldLabel: gettext('Add EFI Disk') - }, - { - xtype: 'pveDiskStorageSelector', - name: 'efidisk0', - storageContent: 'images', - bind: { - nodename: '{nodename}', - hidden: '{!efi}', - disabled: '{!efidisk}' - }, - autoSelect: false, - disabled: true, - hidden: true, - hideSize: true - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'machine', - value: '__default__', - fieldLabel: gettext('Machine'), - comboItems: [ - ['__default__', PVE.Utils.render_qemu_machine('')], - ['q35', 'q35'] - ] - } - ] - -}); -Ext.define('PVE.qemu.AudioInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveAudioInputPanel', - - // FIXME: enable once we bumped doc-gen so this ref is included - //onlineHelp: 'qm_audio_device', - - onGetValues: function(values) { - var ret = PVE.Parser.printPropertyString(values); - if (ret === '') { - return { - 'delete': 'audio0' - }; - } - return { - audio0: ret - }; - }, - - items: [{ - name: 'device', - xtype: 'proxmoxKVComboBox', - value: 'ich9-intel-hda', - fieldLabel: gettext('Audio Device'), - comboItems: [ - ['ich9-intel-hda', 'ich9-intel-hda'], - ['intel-hda', 'intel-hda'], - ['AC97', 'AC97'] - ] - }, { - name: 'driver', - xtype: 'displayfield', - value: 'spice', - submitValue: true, - fieldLabel: gettext('Backend Driver'), - }] -}); - -Ext.define('PVE.qemu.AudioEdit', { - extend: 'Proxmox.window.Edit', - - vmconfig: undefined, - - subject: gettext('Audio Device'), - - items: [{ - xtype: 'pveAudioInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - me.load({ - success: function(response) { - me.vmconfig = response.result.data; - - var audio0 = me.vmconfig.audio0; - if (audio0) { - me.setValues(PVE.Parser.parsePropertyString(audio0)); - } - } - }); - } -}); -Ext.define('PVE.qemu.RNGInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveRNGInputPanel', - - // FIXME: enable once we bumped doc-gen so this ref is included - //onlineHelp: 'qm_virtio_rng', - - onGetValues: function(values) { - if (values.max_bytes === "") { - values.max_bytes = "0"; - } else if (values.max_bytes === "1024" && values.period === "") { - delete values.max_bytes; - } - - var ret = PVE.Parser.printPropertyString(values); - - return { - rng0: ret - }; - }, - - setValues: function(values) { - if (values.max_bytes == 0) { - values.max_bytes = null; - } - - this.callParent(arguments); - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - '#max_bytes': { - change: function(el, newVal) { - let limitWarning = this.lookupReference('limitWarning'); - limitWarning.setHidden(!!newVal); - } - }, - '#source': { - change: function(el, newVal) { - let limitWarning = this.lookupReference('sourceWarning'); - limitWarning.setHidden(newVal !== '/dev/random'); - } - } - } - }, - - items: [{ - itemId: 'source', - name: 'source', - xtype: 'proxmoxKVComboBox', - value: '/dev/urandom', - fieldLabel: gettext('Entropy source'), - labelWidth: 130, - comboItems: [ - ['/dev/urandom', '/dev/urandom'], - ['/dev/random', '/dev/random'], - ['/dev/hwrng', '/dev/hwrng'] - ] - }, - { - xtype: 'numberfield', - itemId: 'max_bytes', - name: 'max_bytes', - minValue: 0, - step: 1, - value: 1024, - fieldLabel: gettext('Limit (Bytes/Period)'), - labelWidth: 130, - emptyText: gettext('unlimited') - }, - { - xtype: 'numberfield', - name: 'period', - minValue: 1, - step: 1, - fieldLabel: gettext('Period') + ' (ms)', - labelWidth: 130, - emptyText: gettext('1000') - }, - { - xtype: 'displayfield', - reference: 'sourceWarning', - value: gettext('Using /dev/random as entropy source is discouraged, as it can lead to host entropy starvation. /dev/urandom is preferred, and does not lead to a decrease in security in practice.'), - userCls: 'pmx-hint', - hidden: true - }, - { - xtype: 'displayfield', - reference: 'limitWarning', - value: gettext('Disabling the limiter can potentially allow a guest to overload the host. Proceed with caution.'), - userCls: 'pmx-hint', - hidden: true - }] -}); - -Ext.define('PVE.qemu.RNGEdit', { - extend: 'Proxmox.window.Edit', - - subject: gettext('VirtIO RNG'), - - items: [{ - xtype: 'pveRNGInputPanel' - }], - - initComponent : function() { - var me = this; - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response) { - me.vmconfig = response.result.data; - - var rng0 = me.vmconfig.rng0; - if (rng0) { - me.setValues(PVE.Parser.parsePropertyString(rng0)); - } - } - }); - } - } -}); -Ext.define('PVE.lxc.NetworkInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcNetworkInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_network', - - setNodename: function(nodename) { - var me = this; - - if (!nodename || (me.nodename === nodename)) { - return; - } - - me.nodename = nodename; - - var bridgesel = me.query("[isFormField][name=bridge]")[0]; - bridgesel.setNodename(nodename); - }, - - onGetValues: function(values) { - var me = this; - - var id; - if (me.isCreate) { - id = values.id; - delete values.id; - } else { - id = me.ifname; - } - - if (!id) { - return {}; - } - - var newdata = {}; - - if (values.ipv6mode !== 'static') { - values.ip6 = values.ipv6mode; - } - if (values.ipv4mode !== 'static') { - values.ip = values.ipv4mode; - } - newdata[id] = PVE.Parser.printLxcNetwork(values); - return newdata; - }, - - initComponent : function() { - var me = this; - - var cdata = {}; - - if (me.insideWizard) { - me.ifname = 'net0'; - cdata.name = 'eth0'; - me.dataCache = {}; - } - cdata.firewall = (me.insideWizard || me.isCreate); - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.isCreate) { - if (!me.ifname) { - throw "no interface name specified"; - } - if (!me.dataCache[me.ifname]) { - throw "no such interface '" + me.ifname + "'"; - } - - cdata = PVE.Parser.parseLxcNetwork(me.dataCache[me.ifname]); - } - - var i; - for (i = 0; i < 10; i++) { - if (me.isCreate && !me.dataCache['net'+i.toString()]) { - me.ifname = 'net' + i.toString(); - break; - } - } - - var idselector = { - xtype: 'hidden', - name: 'id', - value: me.ifname - }; - - me.column1 = [ - idselector, - { - xtype: 'textfield', - name: 'name', - fieldLabel: gettext('Name'), - emptyText: '(e.g., eth0)', - allowBlank: false, - value: cdata.name, - validator: function(value) { - var result = ''; - Ext.Object.each(me.dataCache, function(key, netstr) { - if (!key.match(/^net\d+/) || key === me.ifname) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(netstr); - if (net.name === value) { - result = "interface name already in use"; - return false; - } - }); - if (result !== '') { - return result; - } - // validator can return bool/string - /*jslint confusion:true*/ - return true; - } - }, - { - xtype: 'textfield', - name: 'hwaddr', - fieldLabel: gettext('MAC address'), - vtype: 'MacAddress', - value: cdata.hwaddr, - allowBlank: true, - emptyText: 'auto' - }, - { - xtype: 'PVE.form.BridgeSelector', - name: 'bridge', - nodename: me.nodename, - fieldLabel: gettext('Bridge'), - value: cdata.bridge, - allowBlank: false - }, - { - xtype: 'pveVlanField', - name: 'tag', - value: cdata.tag - }, - { - xtype: 'numberfield', - name: 'rate', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - minValue: 0, - maxValue: 10*1024, - value: cdata.rate, - emptyText: 'unlimited', - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Firewall'), - name: 'firewall', - value: cdata.firewall - } - ]; - - var dhcp4 = (cdata.ip === 'dhcp'); - if (dhcp4) { - cdata.ip = ''; - cdata.gw = ''; - } - - var auto6 = (cdata.ip6 === 'auto'); - var dhcp6 = (cdata.ip6 === 'dhcp'); - if (auto6 || dhcp6) { - cdata.ip6 = ''; - cdata.gw6 = ''; - } - - me.column2 = [ - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv4:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv4mode', - inputValue: 'static', - checked: !dhcp4, - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip]').setEmptyText( - !!value ? Proxmox.Utils.NoneText : "" - ); - me.down('field[name=ip]').setDisabled(!value); - me.down('field[name=gw]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv4mode', - inputValue: 'dhcp', - checked: dhcp4, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip', - vtype: 'IPCIDRAddress', - value: cdata.ip, - emptyText: dhcp4 ? '' : Proxmox.Utils.NoneText, - disabled: dhcp4, - fieldLabel: 'IPv4/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw', - value: cdata.gw, - vtype: 'IPAddress', - disabled: dhcp4, - fieldLabel: gettext('Gateway') + ' (IPv4)', - margin: '0 0 3 0' // override bottom margin to account for the menuseparator - }, - { - xtype: 'menuseparator', - height: '3', - margin: '0' - }, - { - layout: { - type: 'hbox', - align: 'middle' - }, - border: false, - margin: '0 0 5 0', - items: [ - { - xtype: 'label', - text: 'IPv6:' // do not localize - }, - { - xtype: 'radiofield', - boxLabel: gettext('Static'), - name: 'ipv6mode', - inputValue: 'static', - checked: !(auto6 || dhcp6), - margin: '0 0 0 10', - listeners: { - change: function(cb, value) { - me.down('field[name=ip6]').setEmptyText( - !!value ? Proxmox.Utils.NoneText : "" - ); - me.down('field[name=ip6]').setDisabled(!value); - me.down('field[name=gw6]').setDisabled(!value); - } - } - }, - { - xtype: 'radiofield', - boxLabel: 'DHCP', // do not localize - name: 'ipv6mode', - inputValue: 'dhcp', - checked: dhcp6, - margin: '0 0 0 10' - }, - { - xtype: 'radiofield', - boxLabel: 'SLAAC', // do not localize - name: 'ipv6mode', - inputValue: 'auto', - checked: auto6, - margin: '0 0 0 10' - } - ] - }, - { - xtype: 'textfield', - name: 'ip6', - value: cdata.ip6, - emptyText: dhcp6 || auto6 ? '' : Proxmox.Utils.NoneText, - vtype: 'IP6CIDRAddress', - disabled: (dhcp6 || auto6), - fieldLabel: 'IPv6/CIDR' // do not localize - }, - { - xtype: 'textfield', - name: 'gw6', - vtype: 'IP6Address', - value: cdata.gw6, - disabled: (dhcp6 || auto6), - fieldLabel: gettext('Gateway') + ' (IPv6)' - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.NetworkEdit', { - extend: 'Proxmox.window.Edit', - - isAdd: true, - - initComponent : function() { - var me = this; - - if (!me.dataCache) { - throw "no dataCache specified"; - } - - if (!me.nodename) { - throw "no node name specified"; - } - - var ipanel = Ext.create('PVE.lxc.NetworkInputPanel', { - ifname: me.ifname, - nodename: me.nodename, - dataCache: me.dataCache, - isCreate: me.isCreate - }); - - Ext.apply(me, { - subject: gettext('Network Device') + ' (veth)', - digest: me.dataCache.digest, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.NetworkView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveLxcNetworkView', - - onlineHelp: 'pct_container_network', - - dataCache: {}, // used to store result of last load - - stateful: true, - stateId: 'grid-lxc-network', - - load: function() { - var me = this; - - Proxmox.Utils.setErrorMask(me, true); - - Proxmox.Utils.API2Request({ - url: me.url, - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, gettext('Error') + ': ' + response.htmlStatus); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var result = Ext.decode(response.responseText); - var data = result.data || {}; - me.dataCache = data; - var records = []; - Ext.Object.each(data, function(key, value) { - if (!key.match(/^net\d+/)) { - return; // continue - } - var net = PVE.Parser.parseLxcNetwork(value); - net.id = key; - records.push(net); - }); - me.store.loadData(records); - me.down('button[name=addButton]').setDisabled((records.length >= 10)); - } - }); - }, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - me.url = '/nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var store = new Ext.data.Store({ - model: 'pve-lxc-network', - sorters: [ - { - property : 'id', - direction: 'ASC' - } - ] - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return !!caps.vms['VM.Config.Network']; - }, - confirmMsg: function (rec) { - return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + rec.data.id + "'"); - }, - handler: function(btn, event, rec) { - Proxmox.Utils.API2Request({ - url: me.url, - waitMsgTarget: me, - method: 'PUT', - params: { 'delete': rec.data.id, digest: me.dataCache.digest }, - callback: function() { - me.load(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (!caps.vms['VM.Config.Network']) { - return false; - } - - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - dataCache: me.dataCache, - ifname: rec.data.id - }); - win.on('destroy', me.load, me); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - if (!caps.vms['VM.Config.Network']) { - return false; - } - return true; - }, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - name: 'addButton', - disabled: !caps.vms['VM.Config.Network'], - handler: function() { - var win = Ext.create('PVE.lxc.NetworkEdit', { - url: me.url, - nodename: nodename, - isCreate: true, - dataCache: me.dataCache - }); - win.on('destroy', me.load, me); - win.show(); - } - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - width: 50, - dataIndex: 'id' - }, - { - header: gettext('Name'), - width: 80, - dataIndex: 'name' - }, - { - header: gettext('Bridge'), - width: 80, - dataIndex: 'bridge' - }, - { - header: gettext('Firewall'), - width: 80, - dataIndex: 'firewall', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('VLAN Tag'), - width: 80, - dataIndex: 'tag' - }, - { - header: gettext('MAC address'), - width: 110, - dataIndex: 'hwaddr' - }, - { - header: gettext('IP address'), - width: 150, - dataIndex: 'ip', - renderer: function(value, metaData, rec) { - if (rec.data.ip && rec.data.ip6) { - return rec.data.ip + "
" + rec.data.ip6; - } else if (rec.data.ip6) { - return rec.data.ip6; - } else { - return rec.data.ip; - } - } - }, - { - header: gettext('Gateway'), - width: 150, - dataIndex: 'gw', - renderer: function(value, metaData, rec) { - if (rec.data.gw && rec.data.gw6) { - return rec.data.gw + "
" + rec.data.gw6; - } else if (rec.data.gw6) { - return rec.data.gw6; - } else { - return rec.data.gw; - } - } - } - ], - listeners: { - activate: me.load, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-lxc-network', { - extend: "Ext.data.Model", - proxy: { type: 'memory' }, - fields: [ 'id', 'name', 'hwaddr', 'bridge', - 'ip', 'gw', 'ip6', 'gw6', 'tag', 'firewall' ] - }); - -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.RessourceView', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.pveLxcRessourceView'], - - onlineHelp: 'pct_configuration', - - renderKey: function(key, metaData, rec, rowIndex, colIndex, store) { - var me = this; - var rowdef = me.rows[key] || {}; - - metaData.tdAttr = "valign=middle"; - if (rowdef.tdCls) { - metaData.tdCls = rowdef.tdCls; - } - return rowdef.header || key; - }, - - initComponent : function() { - var me = this; - var i, confid; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - var diskCap = caps.vms['VM.Config.Disk']; - - var mpeditor = caps.vms['VM.Config.Disk'] ? 'PVE.lxc.MountPointEdit' : undefined; - - var rows = { - memory: { - header: gettext('Memory'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-memory', - group: 1, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - swap: { - header: gettext('Swap'), - editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined, - defaultValue: 512, - tdCls: 'pve-itype-icon-swap', - group: 2, - renderer: function(value) { - return Proxmox.Utils.format_size(value*1024*1024); - } - }, - cores: { - header: gettext('Cores'), - editor: caps.vms['VM.Config.CPU'] ? 'PVE.lxc.CPUEdit' : undefined, - defaultValue: '', - tdCls: 'pve-itype-icon-processor', - group: 3, - renderer: function(value) { - var cpulimit = me.getObjectValue('cpulimit'); - var cpuunits = me.getObjectValue('cpuunits'); - var res; - if (value) { - res = value; - } else { - res = gettext('unlimited'); - } - - if (cpulimit) { - res += ' [cpulimit=' + cpulimit + ']'; - } - - if (cpuunits) { - res += ' [cpuunits=' + cpuunits + ']'; - } - return res; - } - }, - rootfs: { - header: gettext('Root Disk'), - defaultValue: Proxmox.Utils.noneText, - editor: mpeditor, - tdCls: 'pve-itype-icon-storage', - group: 4 - }, - cpulimit: { - visible: false - }, - cpuunits: { - visible: false - }, - unprivileged: { - visible: false - } - }; - - PVE.Utils.forEachMP(function(bus, i) { - confid = bus + i; - var group = 5; - var header; - if (bus === 'mp') { - header = gettext('Mount Point') + ' (' + confid + ')'; - } else { - header = gettext('Unused Disk') + ' ' + i; - group += 1; - } - rows[confid] = { - group: group, - order: i, - tdCls: 'pve-itype-icon-storage', - editor: mpeditor, - header: header - }; - }, true); - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - var run_resize = function() { - var rec = me.selModel.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.MPResize', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid - }); - - win.show(); - }; - - var run_remove = function(b, e, rec) { - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: 'PUT', - params: { - 'delete': rec.data.key - }, - failure: function (response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var run_move = function(b, e, rec) { - if (!rec) { - return; - } - - var win = Ext.create('PVE.window.HDMove', { - disk: rec.data.key, - nodename: nodename, - vmid: vmid, - type: 'lxc' - }); - - win.show(); - - win.on('destroy', me.reload, me); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - selModel: me.selModel, - disabled: true, - enableFn: function(rec) { - if (!rec) { - return false; - } - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - var resize_btn = new Proxmox.button.Button({ - text: gettext('Resize disk'), - selModel: me.selModel, - disabled: true, - handler: run_resize - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - selModel: me.selModel, - disabled: true, - dangerous: true, - confirmMsg: function(rec) { - var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), - "'" + me.renderKey(rec.data.key, {}, rec) + "'"); - if (rec.data.key.match(/^unused\d+$/)) { - msg += " " + gettext('This will permanently erase all data.'); - } - - return msg; - }, - handler: run_remove - }); - - var move_btn = new Proxmox.button.Button({ - text: gettext('Move Volume'), - selModel: me.selModel, - disabled: true, - dangerous: true, - handler: run_move - }); - - var revert_btn = new PVE.button.PendingRevert(); - - var set_button_status = function() { - var rec = me.selModel.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - remove_btn.disable(); - resize_btn.disable(); - revert_btn.disable(); - return; - } - var key = rec.data.key; - var value = rec.data.value; - var rowdef = rows[key]; - - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var isDisk = (rowdef.tdCls == 'pve-itype-icon-storage'); - var isUnusedDisk = key.match(/^unused\d+/); - - var noedit = rec.data['delete'] || !rowdef.editor; - if (!noedit && Proxmox.UserName !== 'root@pam' && key.match(/^mp\d+$/)) { - var mp = PVE.Parser.parseLxcMountPoint(value); - if (mp.type !== 'volume') { - noedit = true; - } - } - edit_btn.setDisabled(noedit); - - remove_btn.setDisabled(!isDisk || rec.data.key === 'rootfs' || !diskCap || pending); - resize_btn.setDisabled(!isDisk || !diskCap || isUnusedDisk); - move_btn.setDisabled(!isDisk || !diskCap); - revert_btn.setDisabled(!pending); - - }; - - var sorterFn = function(rec1, rec2) { - var v1 = rec1.data.key; - var v2 = rec2.data.key; - var g1 = rows[v1].group || 0; - var g2 = rows[v2].group || 0; - var order1 = rows[v1].order || 0; - var order2 = rows[v2].order || 0; - - if ((g1 - g2) !== 0) { - return g1 - g2; - } - - if ((order1 - order2) !== 0) { - return order1 - order2; - } - - if (v1 > v2) { - return 1; - } else if (v1 < v2) { - return -1; - } else { - return 0; - } - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/pending", - selModel: me.selModel, - interval: 2000, - cwidth1: 170, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Mount Point'), - iconCls: 'pve-itype-icon-storage', - disabled: !caps.vms['VM.Config.Disk'], - handler: function() { - var win = Ext.create('PVE.lxc.MountPointEdit', { - url: '/api2/extjs/' + baseurl, - unprivileged: me.getObjectValue('unprivileged'), - pveSelNode: me.pveSelNode - }); - win.on('destroy', me.reload, me); - win.show(); - } - } - ] - }) - }, - edit_btn, - remove_btn, - resize_btn, - move_btn, - revert_btn - ], - rows: rows, - sorterFn: sorterFn, - editorConfig: { - pveSelNode: me.pveSelNode, - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - me.mon(me.getStore(), 'datachanged', function() { - set_button_status(); - }); - - Ext.apply(me.editorConfig, { unprivileged: me.getObjectValue('unprivileged') }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.FeaturesInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcFeaturesInputPanel', - - // used to save the mounts fstypes until sending - mounts: [], - - fstypes: ['nfs', 'cifs'], - - viewModel: { - parent: null, - data: { - unprivileged: false - }, - formulas: { - privilegedOnly: function(get) { - return (get('unprivileged') ? gettext('privileged only') : ''); - }, - unprivilegedOnly: function(get) { - return (!get('unprivileged') ? gettext('unprivileged only') : ''); - } - } - }, - - items: [ - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('keyctl'), - name: 'keyctl', - bind: { - disabled: '{!unprivileged}', - boxLabel: '{unprivilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Nesting'), - name: 'nesting' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nfs', - fieldLabel: 'NFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'cifs', - fieldLabel: 'CIFS', - bind: { - disabled: '{unprivileged}', - boxLabel: '{privilegedOnly}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'fuse', - fieldLabel: 'FUSE' - }, - { - xtype: 'proxmoxcheckbox', - name: 'mknod', - fieldLabel: gettext('Create Device Nodes'), - boxLabel: gettext('Experimental'), - }, - ], - - onGetValues: function(values) { - var me = this; - var mounts = me.mounts; - me.fstypes.forEach(function(fs) { - if (values[fs]) { - mounts.push(fs); - } - delete values[fs]; - }); - - if (mounts.length) { - values.mount = mounts.join(';'); - } - - var featuresstring = PVE.Parser.printPropertyString(values, undefined); - if (featuresstring == '') { - return { 'delete': 'features' }; - } - return { features: featuresstring }; - }, - - setValues: function(values) { - var me = this; - - me.viewModel.set('unprivileged', values.unprivileged); - - if (values.features) { - var res = PVE.Parser.parsePropertyString(values.features); - me.mounts = []; - if (res.mount) { - res.mount.split(/[; ]/).forEach(function(item) { - if (me.fstypes.indexOf(item) === -1) { - me.mounts.push(item); - } else { - res[item] = 1; - } - }); - } - this.callParent([res]); - } - } -}); - -Ext.define('PVE.lxc.FeaturesEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveLxcFeaturesEdit', - - subject: gettext('Features'), - autoLoad: true, - width: 350, - - items: [{ - xtype: 'pveLxcFeaturesInputPanel' - }], -}); -/*jslint confusion: true */ -Ext.define('PVE.lxc.Options', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.pveLxcOptions'], - - onlineHelp: 'pct_options', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - onboot: { - header: gettext('Start at boot'), - defaultValue: '', - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Start at boot'), - items: { - xtype: 'proxmoxcheckbox', - name: 'onboot', - uncheckedValue: 0, - defaultValue: 0, - fieldLabel: gettext('Start at boot') - } - } : undefined - }, - startup: { - header: gettext('Start/Shutdown order'), - defaultValue: '', - renderer: PVE.Utils.render_kvm_startup, - editor: caps.vms['VM.Config.Options'] && caps.nodes['Sys.Modify'] ? - { - xtype: 'pveWindowStartupEdit', - onlineHelp: 'pct_startup_and_shutdown' - } : undefined - }, - ostype: { - header: gettext('OS Type'), - defaultValue: Proxmox.Utils.unknownText - }, - arch: { - header: gettext('Architecture'), - defaultValue: Proxmox.Utils.unknownText - }, - console: { - header: '/dev/console', - defaultValue: 1, - renderer: Proxmox.Utils.format_enabled_toggle, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: '/dev/console', - items: { - xtype: 'proxmoxcheckbox', - name: 'console', - uncheckedValue: 0, - defaultValue: 1, - deleteDefaultValue: true, - checked: true, - fieldLabel: '/dev/console' - } - } : undefined - }, - tty: { - header: gettext('TTY count'), - defaultValue: 2, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('TTY count'), - items: { - xtype: 'proxmoxintegerfield', - name: 'tty', - minValue: 0, - maxValue: 6, - value: 2, - fieldLabel: gettext('TTY count'), - emptyText: gettext('Default'), - deleteEmpty: true - } - } : undefined - }, - cmode: { - header: gettext('Console mode'), - defaultValue: 'tty', - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Console mode'), - items: { - xtype: 'proxmoxKVComboBox', - name: 'cmode', - deleteEmpty: true, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + " (tty)"], - ['tty', "/dev/tty[X]"], - ['console', "/dev/console"], - ['shell', "shell"] - ], - fieldLabel: gettext('Console mode') - } - } : undefined - }, - protection: { - header: gettext('Protection'), - defaultValue: false, - renderer: Proxmox.Utils.format_boolean, - editor: caps.vms['VM.Config.Options'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Protection'), - items: { - xtype: 'proxmoxcheckbox', - name: 'protection', - uncheckedValue: 0, - defaultValue: 0, - deleteDefaultValue: true, - fieldLabel: gettext('Enabled') - } - } : undefined - }, - unprivileged: { - header: gettext('Unprivileged container'), - renderer: Proxmox.Utils.format_boolean, - defaultValue: 0 - }, - features: { - header: gettext('Features'), - defaultValue: Proxmox.Utils.noneText, - editor: Proxmox.UserName === 'root@pam' ? - 'PVE.lxc.FeaturesEdit' : undefined - }, - hookscript: { - header: gettext('Hookscript') - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: function() { me.run_editor(); } - }); - - var revert_btn = new PVE.button.PendingRevert(); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - - var key = rec.data.key; - var pending = rec.data['delete'] || me.hasPendingChanges(key); - var rowdef = rows[key]; - - edit_btn.setDisabled(!rowdef.editor); - revert_btn.setDisabled(!pending); - }; - - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/pending", - selModel: sm, - interval: 5000, - tbar: [ edit_btn, revert_btn ], - rows: rows, - editorConfig: { - url: '/api2/extjs/' + baseurl - }, - listeners: { - itemdblclick: me.run_editor, - selectionchange: set_button_status - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - me.mon(me.getStore(), 'datachanged', function() { - set_button_status(); - }); - - } -}); - -Ext.define('PVE.lxc.DNSInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcDNSInputPanel', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - var deletes = []; - if (!values.searchdomain && !me.insideWizard) { - deletes.push('searchdomain'); - } - - if (values.nameserver) { - var list = values.nameserver.split(/[\ \,\;]+/); - values.nameserver = list.join(' '); - } else if(!me.insideWizard) { - deletes.push('nameserver'); - } - - if (deletes.length) { - values['delete'] = deletes.join(','); - } - - return values; - }, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxtextfield', - name: 'searchdomain', - skipEmptyText: true, - fieldLabel: gettext('DNS domain'), - emptyText: gettext('use host settings'), - allowBlank: true - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('DNS servers'), - vtype: 'IP64AddressList', - allowBlank: true, - emptyText: gettext('use host settings'), - name: 'nameserver', - itemId: 'nameserver' - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.DNSEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.lxc.DNSInputPanel'); - - Ext.apply(me, { - subject: gettext('Resources'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - if (values.nameserver) { - values.nameserver.replace(/[,;]/, ' '); - values.nameserver.replace(/^\s+/, ''); - } - - ipanel.setValues(values); - } - }); - } - } -}); - -/*jslint confusion: true */ -Ext.define('PVE.lxc.DNS', { - extend: 'Proxmox.grid.PendingObjectGrid', - alias: ['widget.pveLxcDNS'], - - onlineHelp: 'pct_container_network', - - initComponent : function() { - var me = this; - var i; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = me.pveSelNode.data.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var caps = Ext.state.Manager.get('GuiCap'); - - var rows = { - hostname: { - required: true, - defaultValue: me.pveSelNode.data.name, - header: gettext('Hostname'), - editor: caps.vms['VM.Config.Network'] ? { - xtype: 'proxmoxWindowEdit', - subject: gettext('Hostname'), - items: { - xtype: 'inputpanel', - items:{ - fieldLabel: gettext('Hostname'), - xtype: 'textfield', - name: 'hostname', - vtype: 'DnsName', - allowBlank: true, - emptyText: 'CT' + vmid.toString() - }, - onGetValues: function(values) { - var params = values; - if (values.hostname === undefined || - values.hostname === null || - values.hostname === '') { - params = { hostname: 'CT'+vmid.toString()}; - } - return params; - } - } - } : undefined - }, - searchdomain: { - header: gettext('DNS domain'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - }, - nameserver: { - header: gettext('DNS server'), - defaultValue: '', - editor: caps.vms['VM.Config.Network'] ? 'PVE.lxc.DNSEdit' : undefined, - renderer: function(value) { - return value || gettext('use host settings'); - } - } - }; - - var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config'; - - var reload = function() { - me.rstore.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var rowdef = rows[rec.data.key]; - if (!rowdef.editor) { - return; - } - - var win; - if (Ext.isString(rowdef.editor)) { - win = Ext.create(rowdef.editor, { - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/nodes/' + nodename + '/lxc/' + vmid + '/config' - }); - } else { - var config = Ext.apply({ - pveSelNode: me.pveSelNode, - confid: rec.data.key, - url: '/api2/extjs/nodes/' + nodename + '/lxc/' + vmid + '/config' - }, rowdef.editor); - win = Ext.createWidget(rowdef.editor.xtype, config); - win.load(); - } - //win.load(); - win.show(); - win.on('destroy', reload); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - var rowdef = rows[rec.data.key]; - return !!rowdef.editor; - }, - handler: run_editor - }); - - var revert_btn = new PVE.button.PendingRevert(); - - var set_button_status = function() { - var sm = me.getSelectionModel(); - var rec = sm.getSelection()[0]; - - if (!rec) { - edit_btn.disable(); - return; - } - let key = rec.data.key; - - let rowdef = rows[key]; - edit_btn.setDisabled(!rowdef.editor); - - let pending = rec.data['delete'] || me.hasPendingChanges(key); - revert_btn.setDisabled(!pending); - }; - - Ext.apply(me, { - url: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/pending", - selModel: sm, - cwidth1: 150, - interval: 5000, - run_editor: run_editor, - tbar: [ edit_btn, revert_btn ], - rows: rows, - editorConfig: { - url: "/api2/extjs/" + baseurl - }, - listeners: { - itemdblclick: run_editor, - selectionchange: set_button_status, - activate: reload - } - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - - me.mon(me.getStore(), 'datachanged', function() { - set_button_status(); - }); - } -}); -Ext.define('PVE.lxc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.lxc.Config', - - onlineHelp: 'chapter_pct', - - initComponent: function() { - var me = this; - var vm = me.pveSelNode.data; - - var nodename = vm.node; - if (!nodename) { - throw "no node name specified"; - } - - var vmid = vm.vmid; - if (!vmid) { - throw "no VM ID specified"; - } - - var template = !!vm.template; - - var running = !!vm.uptime; - - var caps = Ext.state.Manager.get('GuiCap'); - - var base_url = '/nodes/' + nodename + '/lxc/' + vmid; - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json' + base_url + '/status/current', - interval: 1000 - }); - - var vm_command = function(cmd, params) { - Proxmox.Utils.API2Request({ - params: params, - url: base_url + "/status/" + cmd, - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - }; - - var startBtn = Ext.create('Ext.Button', { - text: gettext('Start'), - disabled: !caps.vms['VM.PowerMgmt'] || running, - hidden: template, - handler: function() { - vm_command('start'); - }, - iconCls: 'fa fa-play' - }); - - var shutdownBtn = Ext.create('PVE.button.Split', { - text: gettext('Shutdown'), - disabled: !caps.vms['VM.PowerMgmt'] || !running, - hidden: template, - confirmMsg: Proxmox.Utils.format_task_description('vzshutdown', vmid), - handler: function() { - vm_command('shutdown'); - }, - menu: { - items:[{ - text: gettext('Reboot'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('vzreboot', vmid), - tooltip: Ext.String.format(gettext('Reboot {0}'), 'CT'), - handler: function() { - vm_command("reboot"); - }, - iconCls: 'fa fa-refresh' - }, - { - text: gettext('Stop'), - disabled: !caps.vms['VM.PowerMgmt'], - confirmMsg: Proxmox.Utils.format_task_description('vzstop', vmid), - tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'), - dangerous: true, - handler: function() { - vm_command("stop"); - }, - iconCls: 'fa fa-stop' - }] - }, - iconCls: 'fa fa-power-off' - }); - - var migrateBtn = Ext.create('Ext.Button', { - text: gettext('Migrate'), - disabled: !caps.vms['VM.Migrate'], - hidden: PVE.data.ResourceStore.getNodes().length < 2, - handler: function() { - var win = Ext.create('PVE.window.Migrate', { - vmtype: 'lxc', - nodename: nodename, - vmid: vmid - }); - win.show(); - }, - iconCls: 'fa fa-send-o' - }); - - var moreBtn = Ext.create('Proxmox.button.Button', { - text: gettext('More'), - menu: { items: [ - { - text: gettext('Clone'), - iconCls: 'fa fa-fw fa-clone', - hidden: caps.vms['VM.Clone'] ? false : true, - handler: function() { - PVE.window.Clone.wrap(nodename, vmid, template, 'lxc'); - } - }, - { - text: gettext('Convert to template'), - disabled: template, - xtype: 'pveMenuItem', - iconCls: 'fa fa-fw fa-file-o', - hidden: caps.vms['VM.Allocate'] ? false : true, - confirmMsg: Proxmox.Utils.format_task_description('vztemplate', vmid), - handler: function() { - Proxmox.Utils.API2Request({ - url: base_url + '/template', - waitMsgTarget: me, - method: 'POST', - failure: function(response, opts) { - Ext.Msg.alert('Error', response.htmlStatus); - } - }); - } - }, - { - iconCls: 'fa fa-heartbeat ', - hidden: !caps.nodes['Sys.Console'], - text: gettext('Manage HA'), - handler: function() { - var ha = vm.hastate; - Ext.create('PVE.ha.VMResourceEdit', { - vmid: vmid, - guestType: 'ct', - isCreate: (!ha || ha === 'unmanaged') - }).show(); - } - }, - { - text: gettext('Remove'), - disabled: !caps.vms['VM.Allocate'], - itemId: 'removeBtn', - handler: function() { - Ext.create('PVE.window.SafeDestroy', { - url: base_url, - item: { type: 'CT', id: vmid } - }).show(); - }, - iconCls: 'fa fa-trash-o' - } - ]} - }); - - var consoleBtn = Ext.create('PVE.button.ConsoleButton', { - disabled: !caps.vms['VM.Console'], - consoleType: 'lxc', - consoleName: vm.name, - hidden: template, - nodename: nodename, - vmid: vmid - }); - - var statusTxt = Ext.create('Ext.toolbar.TextItem', { - data: { - lock: undefined - }, - tpl: [ - '', - ' ({lock})', - '' - ] - }); - - - Ext.apply(me, { - title: Ext.String.format(gettext("Container {0} on node '{1}'"), vm.text, nodename), - hstateid: 'lxctab', - tbarSpacing: false, - tbar: [ statusTxt, '->', startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn ], - defaults: { statusStore: me.statusStore }, - items: [ - { - title: gettext('Summary'), - xtype: 'pveGuestSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ] - }); - - if (caps.vms['VM.Console'] && !template) { - me.items.push( - { - title: gettext('Console'), - itemId: 'consolejs', - iconCls: 'fa fa-terminal', - xtype: 'pveNoVncConsole', - vmid: vmid, - consoleType: 'lxc', - xtermjs: true, - nodename: nodename - } - ); - } - - me.items.push( - { - title: gettext('Resources'), - itemId: 'resources', - expandedOnInit: true, - iconCls: 'fa fa-cube', - xtype: 'pveLxcRessourceView' - }, - { - title: gettext('Network'), - iconCls: 'fa fa-exchange', - itemId: 'network', - xtype: 'pveLxcNetworkView' - }, - { - title: gettext('DNS'), - iconCls: 'fa fa-globe', - itemId: 'dns', - xtype: 'pveLxcDNS' - }, - { - title: gettext('Options'), - itemId: 'options', - iconCls: 'fa fa-gear', - xtype: 'pveLxcOptions' - }, - { - title: gettext('Task History'), - itemId: 'tasks', - iconCls: 'fa fa-list', - xtype: 'proxmoxNodeTasks', - nodename: nodename, - vmidFilter: vmid - } - ); - - if (caps.vms['VM.Backup']) { - me.items.push({ - title: gettext('Backup'), - iconCls: 'fa fa-floppy-o', - xtype: 'pveBackupView', - itemId: 'backup' - }, - { - title: gettext('Replication'), - iconCls: 'fa fa-retweet', - xtype: 'pveReplicaView', - itemId: 'replication' - }); - } - - if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback'] || - caps.vms['VM.Audit']) && !template) { - me.items.push({ - title: gettext('Snapshots'), - iconCls: 'fa fa-history', - xtype: 'pveGuestSnapshotTree', - type: 'lxc', - itemId: 'snapshot' - }); - } - - if (caps.vms['VM.Console']) { - me.items.push( - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - iconCls: 'fa fa-shield', - allow_iface: true, - base_url: base_url + '/firewall/rules', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - groups: ['firewall'], - iconCls: 'fa fa-gear', - onlineHelp: 'pve_firewall_vm_container_configuration', - title: gettext('Options'), - base_url: base_url + '/firewall/options', - fwtype: 'vm', - itemId: 'firewall-options' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: base_url + '/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: gettext('IPSet'), - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: base_url + '/firewall/ipset', - list_refs_url: base_url + '/firewall/refs', - itemId: 'firewall-ipset' - }, - { - title: gettext('Log'), - groups: ['firewall'], - iconCls: 'fa fa-list', - onlineHelp: 'chapter_pve_firewall', - itemId: 'firewall-fwlog', - xtype: 'proxmoxLogView', - url: '/api2/extjs' + base_url + '/firewall/log' - } - ); - } - - if (caps.vms['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - itemId: 'permissions', - iconCls: 'fa fa-unlock', - path: '/vms/' + vmid - }); - } - - me.callParent(); - - var prevStatus = 'unknown'; - me.mon(me.statusStore, 'load', function(s, records, success) { - var status; - var lock; - if (!success) { - status = 'unknown'; - } else { - var rec = s.data.get('status'); - status = rec ? rec.data.value : 'unknown'; - rec = s.data.get('template'); - template = rec.data.value || false; - rec = s.data.get('lock'); - lock = rec ? rec.data.value : undefined; - } - - statusTxt.update({ lock: lock }); - - startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); - shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); - me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); - consoleBtn.setDisabled(template); - - if (prevStatus === 'stopped' && status === 'running') { - let con = me.down('#consolejs'); - if (con) { - con.reload(); - } - } - - prevStatus = status; - }); - - me.on('afterrender', function() { - me.statusStore.startUpdate(); - }); - - me.on('destroy', function() { - me.statusStore.stopUpdate(); - }); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.lxc.CreateWizard', { - extend: 'PVE.window.Wizard', - mixins: ['Proxmox.Mixin.CBind'], - - viewModel: { - data: { - nodename: '', - storage: '', - unprivileged: true - } - }, - - cbindData: { - nodename: undefined - }, - - subject: gettext('LXC Container'), - - items: [ - { - xtype: 'inputpanel', - title: gettext('General'), - onlineHelp: 'pct_general', - column1: [ - { - xtype: 'pveNodeSelector', - name: 'nodename', - cbind: { - selectCurNode: '{!nodename}', - preferredValue: '{nodename}' - }, - bind: { - value: '{nodename}' - }, - fieldLabel: gettext('Node'), - allowBlank: false, - onlineValidator: true - }, - { - xtype: 'pveGuestIDSelector', - name: 'vmid', // backend only knows vmid - guestType: 'lxc', - value: '', - loadNextFreeID: true, - validateExists: false - }, - { - xtype: 'proxmoxtextfield', - name: 'hostname', - vtype: 'DnsName', - value: '', - fieldLabel: gettext('Hostname'), - skipEmptyText: true, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - name: 'unprivileged', - value: true, - bind: { - value: '{unprivileged}' - }, - fieldLabel: gettext('Unprivileged container') - } - ], - column2: [ - { - xtype: 'pvePoolSelector', - fieldLabel: gettext('Resource Pool'), - name: 'pool', - value: '', - allowBlank: true - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'password', - value: '', - fieldLabel: gettext('Password'), - allowBlank: false, - minLength: 5, - change: function(f, value) { - if (f.rendered) { - f.up().down('field[name=confirmpw]').validate(); - } - } - }, - { - xtype: 'textfield', - inputType: 'password', - name: 'confirmpw', - value: '', - fieldLabel: gettext('Confirm password'), - allowBlank: true, - submitValue: false, - validator: function(value) { - var pw = this.up().down('field[name=password]').getValue(); - if (pw !== value) { - return "Passwords do not match!"; - } - return true; - } - }, - { - xtype: 'proxmoxtextfield', - name: 'ssh-public-keys', - value: '', - fieldLabel: gettext('SSH public key'), - allowBlank: true, - validator: function(value) { - var pwfield = this.up().down('field[name=password]'); - if (value.length) { - var key = PVE.Parser.parseSSHKey(value); - if (!key) { - return "Failed to recognize ssh key"; - } - pwfield.allowBlank = true; - } else { - pwfield.allowBlank = false; - } - pwfield.validate(); - return true; - }, - afterRender: function() { - if (!window.FileReader) { - // No FileReader support in this browser - return; - } - var cancel = function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - }; - var field = this; - field.inputEl.on('dragover', cancel); - field.inputEl.on('dragenter', cancel); - field.inputEl.on('drop', function(ev) { - ev = ev.event; - if (ev.preventDefault) { - ev.preventDefault(); - } - var files = ev.dataTransfer.files; - PVE.Utils.loadSSHKeyFromFile(files[0], function(v) { - field.setValue(v); - }); - }); - } - }, - { - xtype: 'filebutton', - name: 'file', - hidden: !window.FileReader, - text: gettext('Load SSH Key File'), - listeners: { - change: function(btn, e, value) { - e = e.event; - var field = this.up().down('proxmoxtextfield[name=ssh-public-keys]'); - PVE.Utils.loadSSHKeyFromFile(e.target.files[0], function(v) { - field.setValue(v); - }); - btn.reset(); - } - } - } - ] - }, - { - xtype: 'inputpanel', - title: gettext('Template'), - onlineHelp: 'pct_container_images', - column1: [ - { - xtype: 'pveStorageSelector', - name: 'tmplstorage', - fieldLabel: gettext('Storage'), - storageContent: 'vztmpl', - autoSelect: true, - allowBlank: false, - bind: { - value: '{storage}', - nodename: '{nodename}' - } - }, - { - xtype: 'pveFileSelector', - name: 'ostemplate', - storageContent: 'vztmpl', - fieldLabel: gettext('Template'), - bind: { - storage: '{storage}', - nodename: '{nodename}' - }, - allowBlank: false - } - ] - }, - { - xtype: 'pveLxcMountPointInputPanel', - title: gettext('Root Disk'), - insideWizard: true, - isCreate: true, - unused: false, - bind: { - nodename: '{nodename}', - unprivileged: '{unprivileged}' - }, - confid: 'rootfs' - }, - { - xtype: 'pveLxcCPUInputPanel', - title: gettext('CPU'), - insideWizard: true - }, - { - xtype: 'pveLxcMemoryInputPanel', - title: gettext('Memory'), - insideWizard: true - }, - { - xtype: 'pveLxcNetworkInputPanel', - title: gettext('Network'), - insideWizard: true, - bind: { - nodename: '{nodename}' - }, - isCreate: true - }, - { - xtype: 'pveLxcDNSInputPanel', - title: gettext('DNS'), - insideWizard: true - }, - { - title: gettext('Confirm'), - layout: 'fit', - items: [ - { - xtype: 'grid', - store: { - model: 'KeyValue', - sorters: [{ - property : 'key', - direction: 'ASC' - }] - }, - columns: [ - {header: 'Key', width: 150, dataIndex: 'key'}, - {header: 'Value', flex: 1, dataIndex: 'value'} - ] - } - ], - dockedItems: [ - { - xtype: 'proxmoxcheckbox', - name: 'start', - dock: 'bottom', - margin: '5 0 0 0', - boxLabel: gettext('Start after created') - } - ], - listeners: { - show: function(panel) { - var wizard = this.up('window'); - var kv = wizard.getValues(); - var data = []; - Ext.Object.each(kv, function(key, value) { - if (key === 'delete' || key === 'tmplstorage') { // ignore - return; - } - if (key === 'password') { // don't show pw - return; - } - var html = Ext.htmlEncode(Ext.JSON.encode(value)); - data.push({ key: key, value: value }); - }); - - var summarystore = panel.down('grid').getStore(); - summarystore.suspendEvents(); - summarystore.removeAll(); - summarystore.add(data); - summarystore.sort(); - summarystore.resumeEvents(); - summarystore.fireEvent('refresh'); - } - }, - onSubmit: function() { - var wizard = this.up('window'); - var kv = wizard.getValues(); - delete kv['delete']; - - var nodename = kv.nodename; - delete kv.nodename; - delete kv.tmplstorage; - - if (!kv.pool.length) { - delete kv.pool; - } - - if (!kv.password.length && kv['ssh-public-keys']) { - delete kv.password; - } - - Proxmox.Utils.API2Request({ - url: '/nodes/' + nodename + '/lxc', - waitMsgTarget: wizard, - method: 'POST', - params: kv, - success: function(response, opts){ - var upid = response.result.data; - - var win = Ext.create('Proxmox.window.TaskViewer', { - upid: upid - }); - win.show(); - wizard.close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - } - ] -}); - - - -/*jslint confusion: true */ -var labelWidth = 120; - -Ext.define('PVE.lxc.MemoryEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('Memory'), - items: Ext.create('PVE.lxc.MemoryInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - - -Ext.define('PVE.lxc.CPUEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - Ext.apply(me, { - subject: gettext('CPU'), - items: Ext.create('PVE.lxc.CPUInputPanel') - }); - - me.callParent(); - - me.load(); - } -}); - -Ext.define('PVE.lxc.CPUInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcCPUInputPanel', - - onlineHelp: 'pct_cpu', - - insideWizard: false, - - onGetValues: function(values) { - var me = this; - - PVE.Utils.delete_if_default(values, 'cores', '', me.insideWizard); - // cpu{limit,unit} aren't in the wizard so create is always false - PVE.Utils.delete_if_default(values, 'cpulimit', '0', 0); - PVE.Utils.delete_if_default(values, 'cpuunits', '1024', 0); - - return values; - }, - - advancedColumn1: [ - { - xtype: 'numberfield', - name: 'cpulimit', - minValue: 0, - value: '', - step: 1, - fieldLabel: gettext('CPU limit'), - allowBlank: true, - emptyText: gettext('unlimited') - } - ], - - advancedColumn2: [ - { - xtype: 'proxmoxintegerfield', - name: 'cpuunits', - fieldLabel: gettext('CPU units'), - value: 1024, - minValue: 8, - maxValue: 500000, - labelWidth: labelWidth, - allowBlank: false - } - ], - - initComponent: function() { - var me = this; - - me.column1 = [ - { - xtype: 'proxmoxintegerfield', - name: 'cores', - minValue: 1, - maxValue: 128, - value: me.insideWizard ? 1 : '', - fieldLabel: gettext('Cores'), - allowBlank: true, - deleteEmpty: true, - emptyText: gettext('unlimited') - } - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.lxc.MemoryInputPanel', { - extend: 'Proxmox.panel.InputPanel', - alias: 'widget.pveLxcMemoryInputPanel', - - onlineHelp: 'pct_memory', - - insideWizard: false, - - initComponent : function() { - var me = this; - - var items = [ - { - xtype: 'proxmoxintegerfield', - name: 'memory', - minValue: 16, - value: '512', - step: 32, - fieldLabel: gettext('Memory') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'swap', - minValue: 0, - value: '512', - step: 32, - fieldLabel: gettext('Swap') + ' (MiB)', - labelWidth: labelWidth, - allowBlank: false - } - ]; - - if (me.insideWizard) { - me.column1 = items; - } else { - me.items = items; - } - - me.callParent(); - } -}); -Ext.define('PVE.window.MPResize', { - extend: 'Ext.window.Window', - - resizable: false, - - resize_disk: function(disk, size) { - var me = this; - var params = { disk: disk, size: '+' + size + 'G' }; - - Proxmox.Utils.API2Request({ - params: params, - url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/resize', - waitMsgTarget: me, - method: 'PUT', - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, opts) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskViewer', { upid: upid }); - win.show(); - me.close(); - } - }); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.vmid) { - throw "no VM ID specified"; - } - - var items = [ - { - xtype: 'displayfield', - name: 'disk', - value: me.disk, - fieldLabel: gettext('Disk'), - vtype: 'StorageId', - allowBlank: false - } - ]; - - me.hdsizesel = Ext.createWidget('numberfield', { - name: 'size', - minValue: 0, - maxValue: 128*1024, - decimalPrecision: 3, - value: '0', - fieldLabel: gettext('Size Increment') + ' (GiB)', - allowBlank: false - }); - - items.push(me.hdsizesel); - - me.formPanel = Ext.create('Ext.form.Panel', { - bodyPadding: 10, - border: false, - fieldDefaults: { - labelWidth: 120, - anchor: '100%' - }, - items: items - }); - - var form = me.formPanel.getForm(); - - var submitBtn; - - me.title = gettext('Resize disk'); - submitBtn = Ext.create('Ext.Button', { - text: gettext('Resize disk'), - handler: function() { - if (form.isValid()) { - var values = form.getValues(); - me.resize_disk(me.disk, values.size); - } - } - }); - - Ext.apply(me, { - modal: true, - border: false, - layout: 'fit', - buttons: [ submitBtn ], - items: [ me.formPanel ] - }); - - - me.callParent(); - - if (!me.disk) { - return; - } - - } -}); -/*jslint confusion: true*/ -/* hidden: boolean and string - * bind: function and object - * disabled: boolean and string - */ -Ext.define('PVE.lxc.MountPointInputPanel', { - extend: 'Proxmox.panel.InputPanel', - xtype: 'pveLxcMountPointInputPanel', - - insideWizard: false, - - onlineHelp: 'pct_container_storage', - - unused: false, // add unused disk imaged - - unprivileged: false, - - vmconfig: {}, // used to select unused disks - - setUnprivileged: function(unprivileged) { - var me = this; - var vm = me.getViewModel(); - me.unprivileged = unprivileged; - vm.set('unpriv', unprivileged); - }, - - onGetValues: function(values) { - var me = this; - - var confid = me.confid || "mp"+values.mpid; - me.mp.file = me.down('field[name=file]').getValue(); - - if (me.unused) { - confid = "mp"+values.mpid; - } else if (me.isCreate) { - me.mp.file = values.hdstorage + ':' + values.disksize; - } - - // delete unnecessary fields - delete values.mpid; - delete values.hdstorage; - delete values.disksize; - delete values.diskformat; - - let mountopts = (values.mountoptions || []).join(';'); - PVE.Utils.propertyStringSet(me.mp, values.mp, 'mp'); - PVE.Utils.propertyStringSet(me.mp, values.mountoptions, 'mountoptions', mountopts); - PVE.Utils.propertyStringSet(me.mp, values.backup, 'backup'); - PVE.Utils.propertyStringSet(me.mp, values.quota, 'quota'); - PVE.Utils.propertyStringSet(me.mp, values.ro, 'ro'); - PVE.Utils.propertyStringSet(me.mp, values.acl, 'acl'); - PVE.Utils.propertyStringSet(me.mp, values.replicate, 'replicate'); - - var res = {}; - res[confid] = PVE.Parser.printLxcMountPoint(me.mp); - return res; - }, - - - setMountPoint: function(mp) { - var me = this; - var vm = this.getViewModel(); - vm.set('mptype', mp.type); - if (mp.mountoptions) { - mp.mountoptions = mp.mountoptions.split(';'); - } - me.mp = mp; - - if (this.confid === 'rootfs') { - var field = me.down('field[name=mountoptions]'); - var forbidden = ['nodev', 'noexec']; - var filtered = field.comboItems.filter(e => !forbidden.includes(e[0])); - field.setComboItems(filtered); - } - - me.setValues(mp); - }, - - setVMConfig: function(vmconfig) { - var me = this; - var vm = me.getViewModel(); - me.vmconfig = vmconfig; - vm.set('unpriv', vmconfig.unprivileged); - - PVE.Utils.forEachMP(function(bus, i) { - var name = "mp" + i.toString(); - if (!Ext.isDefined(vmconfig[name])) { - me.down('field[name=mpid]').setValue(i); - return false; - } - }); - }, - - setNodename: function(nodename) { - var me = this; - var vm = me.getViewModel(); - vm.set('node', nodename); - me.down('#diskstorage').setNodename(nodename); - }, - - controller: { - xclass: 'Ext.app.ViewController', - - control: { - 'field[name=mpid]': { - change: function(field, value) { - field.validate(); - } - }, - '#hdstorage': { - change: function(field, newValue) { - var me = this; - if (!newValue) { - return; - } - - var rec = field.store.getById(newValue); - if (!rec) { - return; - } - - var vm = me.getViewModel(); - vm.set('type', rec.data.type); - } - } - }, - - init: function(view) { - var me = this; - var vm = this.getViewModel(); - view.mp = {}; - vm.set('confid', view.confid); - vm.set('unused', view.unused); - vm.set('node', view.nodename); - vm.set('unpriv', view.unprivileged); - vm.set('hideStorSelector', view.unused || !view.isCreate); - - // can be array if created from unused disk - if (view.isCreate) { - vm.set('isIncludedInBackup', true); - } - } - }, - - viewModel: { - data: { - unpriv: false, - unused: false, - showStorageSelector: false, - mptype: '', - type: '', - confid: '', - node: '' - }, - - formulas: { - quota: function(get) { - return !(get('type') === 'zfs' || - get('type') === 'zfspool' || - get('unpriv') || - get('isBind')); - }, - hasMP: function(get) { - return !!get('confid') && !get('unused'); - }, - isRoot: function(get) { - return get('confid') === 'rootfs'; - }, - isBind: function(get) { - return get('mptype') === 'bind'; - }, - isBindOrRoot: function(get) { - return get('isBind') || get('isRoot'); - } - } - }, - - column1: [ - { - xtype: 'proxmoxintegerfield', - name: 'mpid', - fieldLabel: gettext('Mount Point ID'), - minValue: 0, - maxValue: PVE.Utils.mp_counts.mps - 1, - hidden: true, - allowBlank: false, - disabled: true, - bind: { - hidden: '{hasMP}', - disabled: '{hasMP}' - }, - validator: function(value) { - var me = this.up('inputpanel'); - if (!me.rendered) { - return; - } - if (Ext.isDefined(me.vmconfig["mp"+value])) { - return "Mount point is already in use."; - } - /*jslint confusion: true*/ - /* returns a string above */ - return true; - } - }, - { - xtype: 'pveDiskStorageSelector', - itemId: 'diskstorage', - storageContent: 'rootdir', - hidden: true, - autoSelect: true, - selectformat: false, - defaultSize: 8, - bind: { - hidden: '{hideStorSelector}', - disabled: '{hideStorSelector}', - nodename: '{node}' - } - }, - { - xtype: 'textfield', - disabled: true, - submitValue: false, - fieldLabel: gettext('Disk image'), - name: 'file', - bind: { - hidden: '{!hideStorSelector}' - } - } - ], - - column2: [ - { - xtype: 'textfield', - name: 'mp', - value: '', - emptyText: gettext('/some/path'), - allowBlank: false, - disabled: true, - fieldLabel: gettext('Path'), - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'backup', - fieldLabel: gettext('Backup'), - autoEl: { - tag: 'div', - 'data-qtip': gettext('Include volume in backup job'), - }, - bind: { - hidden: '{isRoot}', - disabled: '{isBindOrRoot}', - value: '{isIncludedInBackup}' - } - } - ], - - advancedColumn1: [ - { - xtype: 'proxmoxcheckbox', - name: 'quota', - defaultValue: 0, - bind: { - disabled: '{!quota}' - }, - fieldLabel: gettext('Enable quota'), - listeners: { - disable: function() { - this.reset(); - } - } - }, - { - xtype: 'proxmoxcheckbox', - name: 'ro', - defaultValue: 0, - bind: { - hidden: '{isRoot}', - disabled: '{isRoot}' - }, - fieldLabel: gettext('Read-only') - }, - { - xtype: 'proxmoxKVComboBox', - name: 'mountoptions', - fieldLabel: gettext('Mount options'), - deleteEmpty: false, - comboItems: [ - ['noatime', 'noatime'], - ['nodev', 'nodev'], - ['noexec', 'noexec'], - ['nosuid', 'nosuid'] - ], - multiSelect: true, - value: [], - allowBlank: true - }, - ], - - advancedColumn2: [ - { - xtype: 'proxmoxKVComboBox', - name: 'acl', - fieldLabel: 'ACLs', - deleteEmpty: false, - comboItems: [ - ['__default__', Proxmox.Utils.defaultText], - ['1', Proxmox.Utils.enabledText], - ['0', Proxmox.Utils.disabledText] - ], - value: '__default__', - bind: { - disabled: '{isBind}' - }, - allowBlank: true - }, - { - xtype: 'proxmoxcheckbox', - inputValue: '0', // reverses the logic - name: 'replicate', - fieldLabel: gettext('Skip replication') - } - ] -}); - -Ext.define('PVE.lxc.MountPointEdit', { - extend: 'Proxmox.window.Edit', - - unprivileged: false, - - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var unused = me.confid && me.confid.match(/^unused\d+$/); - - me.isCreate = me.confid ? unused : true; - - var ipanel = Ext.create('PVE.lxc.MountPointInputPanel', { - confid: me.confid, - nodename: nodename, - unused: unused, - unprivileged: me.unprivileged, - isCreate: me.isCreate - }); - - var subject; - if (unused) { - subject = gettext('Unused Disk'); - } else if (me.isCreate) { - subject = gettext('Mount Point'); - } else { - subject = gettext('Mount Point') + ' (' + me.confid + ')'; - } - - Ext.apply(me, { - subject: subject, - defaultFocus: me.confid !== 'rootfs' ? 'textfield[name=mp]' : 'tool', - items: ipanel - }); - - me.callParent(); - - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - if (me.confid) { - /*jslint confusion: true*/ - /*data is defined as array above*/ - var value = response.result.data[me.confid]; - /*jslint confusion: false*/ - var mp = PVE.Parser.parseLxcMountPoint(value); - - if (!mp) { - Ext.Msg.alert(gettext('Error'), 'Unable to parse mount point options'); - me.close(); - return; - } - - ipanel.setMountPoint(mp); - me.isValid(); // trigger validation - } - } - }); - } -}); -Ext.define('PVE.pool.StatusView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pvePoolStatusView'], - disabled: true, - - title: gettext('Status'), - cwidth1: 150, - interval: 30000, - //height: 195, - initComponent : function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var rows = { - comment: { - header: gettext('Comment'), - renderer: Ext.String.htmlEncode, - required: true - } - }; - - Ext.apply(me, { - url: "/api2/json/pools/" + pool, - rows: rows - }); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pvePoolSummary', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - var statusview = Ext.create('PVE.pool.StatusView', { - pveSelNode: me.pveSelNode, - style: 'padding-top:0px' - }); - - var rstore = statusview.rstore; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - defaults: { - style: 'padding-top:10px', - width: 800 - }, - items: [ statusview ] - }); - - me.on('activate', rstore.startUpdate); - me.on('destroy', rstore.stopUpdate); - - me.callParent(); - } -}); -Ext.define('PVE.pool.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.pvePoolConfig', - - onlineHelp: 'pveum_pools', - - initComponent: function() { - var me = this; - - var pool = me.pveSelNode.data.pool; - if (!pool) { - throw "no pool specified"; - } - - Ext.apply(me, { - title: Ext.String.format(gettext("Resource Pool") + ': ' + pool), - hstateid: 'pooltab', - items: [ - { - title: gettext('Summary'), - iconCls: 'fa fa-book', - xtype: 'pvePoolSummary', - itemId: 'summary' - }, - { - title: gettext('Members'), - xtype: 'pvePoolMembers', - iconCls: 'fa fa-th', - pool: pool, - itemId: 'members' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/pool/' + pool - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.panel.StorageBase', { - extend: 'Proxmox.panel.InputPanel', - controller: 'storageEdit', - - type: '', - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = me.type; - } else { - delete values.storage; - } - - values.disable = values.enable ? 0 : 1; - delete values.enable; - - return values; - }, - - initComponent : function() { - var me = this; - - me.column1.unshift({ - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'storage', - value: me.storageId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }); - - me.column2.unshift( - { - xtype: 'pveNodeSelector', - name: 'nodes', - disabled: me.storageId === 'local', - fieldLabel: gettext('Nodes'), - emptyText: gettext('All') + ' (' + gettext('No restrictions') +')', - multiSelect: true, - autoSelect: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'enable', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Enable') - } - ); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseEdit', { - extend: 'Proxmox.window.Edit', - - initComponent : function() { - var me = this; - - me.isCreate = !me.storageId; - - if (me.isCreate) { - me.url = '/api2/extjs/storage'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/storage/' + me.storageId; - me.method = 'PUT'; - } - - var ipanel = Ext.create(me.paneltype, { - type: me.type, - isCreate: me.isCreate, - storageId: me.storageId - }); - - Ext.apply(me, { - subject: PVE.Utils.format_storage_type(me.type), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - var ctypes = values.content || ''; - - values.content = ctypes.split(','); - - if (values.nodes) { - values.nodes = values.nodes.split(','); - } - values.enable = values.disable ? 0 : 1; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.grid.TemplateSelector', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveTemplateSelector', - - stateful: true, - stateId: 'grid-template-selector', - viewConfig: { - trackOver: false - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/aplinfo"; - var store = new Ext.data.Store({ - model: 'pve-aplinfo', - groupField: 'section', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - groupHeaderTpl: '{[ "Section: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - }); - - var reload = function() { - store.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - '->', - gettext('Search'), - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - var value = field.getValue().toLowerCase(); - store.clearFilter(true); - store.filterBy(function(rec) { - return (rec.data['package'].toLowerCase().indexOf(value) !== -1) - || (rec.data.headline.toLowerCase().indexOf(value) !== -1); - }); - } - } - } - ], - features: [ groupingFeature ], - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Package'), - flex: 1, - dataIndex: 'package' - }, - { - header: gettext('Version'), - width: 80, - dataIndex: 'version' - }, - { - header: gettext('Description'), - flex: 1.5, - renderer: Ext.String.htmlEncode, - dataIndex: 'headline' - } - ], - listeners: { - afterRender: reload - } - }); - - me.callParent(); - } - -}, function() { - - Ext.define('pve-aplinfo', { - extend: 'Ext.data.Model', - fields: [ - 'template', 'type', 'package', 'version', 'headline', 'infopage', - 'description', 'os', 'section' - ], - idProperty: 'template' - }); - -}); - -Ext.define('PVE.storage.TemplateDownload', { - extend: 'Ext.window.Window', - alias: 'widget.pveTemplateDownload', - - modal: true, - title: gettext('Templates'), - layout: 'fit', - width: 900, - height: 600, - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var grid = Ext.create('PVE.grid.TemplateSelector', { - border: false, - scrollable: true, - nodename: me.nodename - }); - - var sm = grid.getSelectionModel(); - - var submitBtn = Ext.create('Proxmox.button.Button', { - text: gettext('Download'), - disabled: true, - selModel: sm, - handler: function(button, event, rec) { - Proxmox.Utils.API2Request({ - url: '/nodes/' + me.nodename + '/aplinfo', - params: { - storage: me.storage, - template: rec.data.template - }, - method: 'POST', - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response, options) { - var upid = response.result.data; - - Ext.create('Proxmox.window.TaskViewer', { - upid: upid, - listeners: { - destroy: me.reloadGrid - } - }).show(); - - me.close(); - } - }); - } - }); - - Ext.apply(me, { - items: grid, - buttons: [ submitBtn ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.Upload', { - extend: 'Ext.window.Window', - alias: 'widget.pveStorageUpload', - - resizable: false, - - modal: true, - - initComponent : function() { - /*jslint confusion: true */ - var me = this; - - var xhr; - - if (!me.nodename) { - throw "no node name specified"; - } - - if (!me.storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + me.nodename + "/storage/" + me.storage + "/upload"; - - var pbar = Ext.create('Ext.ProgressBar', { - text: 'Ready', - hidden: true - }); - - me.formPanel = Ext.create('Ext.form.Panel', { - method: 'POST', - waitMsgTarget: true, - bodyPadding: 10, - border: false, - width: 300, - fieldDefaults: { - labelWidth: 100, - anchor: '100%' - }, - items: [ - { - xtype: 'pveContentTypeSelector', - cts: me.contents, - fieldLabel: gettext('Content'), - name: 'content', - value: me.contents[0] || '', - allowBlank: false - }, - { - xtype: 'filefield', - name: 'filename', - buttonText: gettext('Select File...'), - allowBlank: false, - listeners: { - afterrender: function(cmp) { - cmp.fileInputEl.set({ - accept: '.img, .iso' - }); - } - } - }, - pbar - ] - }); - - var form = me.formPanel.getForm(); - - var doStandardSubmit = function() { - form.submit({ - url: "/api2/htmljs" + baseurl, - waitMsg: gettext('Uploading file...'), - success: function(f, action) { - me.close(); - }, - failure: function(f, action) { - var msg = PVE.Utils.extractFormActionError(action); - Ext.Msg.alert(gettext('Error'), msg); - } - }); - }; - - var updateProgress = function(per, bytes) { - var text = (per * 100).toFixed(2) + '%'; - if (bytes) { - text += " (" + Proxmox.Utils.format_size(bytes) + ')'; - } - pbar.updateProgress(per, text); - }; - - var abortBtn = Ext.create('Ext.Button', { - text: gettext('Abort'), - disabled: true, - handler: function() { - me.close(); - } - }); - - var submitBtn = Ext.create('Ext.Button', { - text: gettext('Upload'), - disabled: true, - handler: function(button) { - var fd; - try { - fd = new FormData(); - } catch (err) { - doStandardSubmit(); - return; - } - - button.setDisabled(true); - abortBtn.setDisabled(false); - - var field = form.findField('content'); - fd.append("content", field.getValue()); - field.setDisabled(true); - - field = form.findField('filename'); - var file = field.fileInputEl.dom; - fd.append("filename", file.files[0]); - field.setDisabled(true); - - pbar.setVisible(true); - updateProgress(0); - - xhr = new XMLHttpRequest(); - - xhr.addEventListener("load", function(e) { - if (xhr.status == 200) { - me.close(); - } else { - var msg = gettext('Error') + " " + xhr.status.toString() + ": " + Ext.htmlEncode(xhr.statusText); - if (xhr.responseText !== "") { - var result = Ext.decode(xhr.responseText); - result.message = msg; - msg = Proxmox.Utils.extractRequestError(result, true); - } - Ext.Msg.alert(gettext('Error'), msg, function(btn) { - me.close(); - }); - } - }, false); - - xhr.addEventListener("error", function(e) { - var msg = "Error " + e.target.status.toString() + " occurred while receiving the document."; - Ext.Msg.alert(gettext('Error'), msg, function(btn) { - me.close(); - }); - }); - - xhr.upload.addEventListener("progress", function(evt) { - if (evt.lengthComputable) { - var percentComplete = evt.loaded / evt.total; - updateProgress(percentComplete, evt.loaded); - } - }, false); - - xhr.open("POST", "/api2/json" + baseurl, true); - xhr.send(fd); - } - }); - - form.on('validitychange', function(f, valid) { - submitBtn.setDisabled(!valid); - }); - - Ext.apply(me, { - title: gettext('Upload'), - items: me.formPanel, - buttons: [ abortBtn, submitBtn ], - listeners: { - close: function() { - if (xhr) { - xhr.abort(); - } - } - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ContentView', { - extend: 'Ext.grid.GridPanel', - - alias: 'widget.pveStorageContentView', - - stateful: true, - stateId: 'grid-storage-content', - viewConfig: { - trackOver: false, - loadMask: false - }, - features: [ - { - ftype: 'grouping', - groupHeaderTpl: '{name} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' - } - ], - initComponent : function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var baseurl = "/nodes/" + nodename + "/storage/" + storage + "/content"; - var store = Ext.create('Ext.data.Store',{ - model: 'pve-storage-content', - groupField: 'content', - proxy: { - type: 'proxmox', - url: '/api2/json' + baseurl - }, - sorters: { - property: 'volid', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - me.statusStore.load(); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var templateButton = Ext.create('Proxmox.button.Button',{ - itemId: 'tmpl-btn', - text: gettext('Templates'), - handler: function() { - var win = Ext.create('PVE.storage.TemplateDownload', { - nodename: nodename, - storage: storage, - reloadGrid: reload - }); - win.show(); - } - }); - - var uploadButton = Ext.create('Proxmox.button.Button', { - contents : ['iso','vztmpl'], - text: gettext('Upload'), - handler: function() { - var me = this; - var win = Ext.create('PVE.storage.Upload', { - nodename: nodename, - storage: storage, - contents: me.contents - }); - win.show(); - win.on('destroy', reload); - } - }); - - var imageRemoveButton; - var removeButton = Ext.create('Proxmox.button.StdRemoveButton',{ - selModel: sm, - delay: 5, - enableFn: function(rec) { - if (rec && rec.data.content !== 'images') { - imageRemoveButton.setVisible(false); - removeButton.setVisible(true); - return true; - } - return false; - }, - callback: function() { - reload(); - }, - baseurl: baseurl + '/' - }); - - imageRemoveButton = Ext.create('Proxmox.button.Button',{ - selModel: sm, - hidden: true, - text: gettext('Remove'), - enableFn: function(rec) { - if (rec && rec.data.content === 'images') { - removeButton.setVisible(false); - imageRemoveButton.setVisible(true); - return true; - } - return false; - }, - handler: function(btn, event, rec) { - me = this; - - var url = baseurl + '/' + rec.data.volid; - var vmid = rec.data.vmid; - - var store = PVE.data.ResourceStore; - - if (vmid && store.findVMID(vmid)) { - var guest_node = store.guestNode(vmid); - var storage_path = 'storage/' + nodename + '/' + storage; - - // allow to delete local backed images if a VMID exists on another node. - if (store.storageIsShared(storage_path) || guest_node == nodename) { - var msg = Ext.String.format( - gettext("Cannot remove image, a guest with VMID '{0}' exists!"), vmid); - msg += '
' + gettext("You can delete the image from the guest's hardware pane"); - - Ext.Msg.show({ - title: gettext('Cannot remove disk image.'), - icon: Ext.Msg.ERROR, - msg: msg - }); - return; - } - } - var win = Ext.create('PVE.window.SafeDestroy', { - title: Ext.String.format(gettext("Destroy '{0}'"), rec.data.volid), - showProgress: true, - url: url, - item: { type: 'Image', id: vmid } - }).show(); - win.on('destroy', function() { - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - reload(); - - }); - } - }); - - me.statusStore = Ext.create('Proxmox.data.ObjectStore', { - url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - xtype: 'proxmoxButton', - text: gettext('Restore'), - selModel: sm, - disabled: true, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b, e, rec) { - var vmtype; - if (PVE.Utils.volume_is_qemu_backup(rec.data.volid, rec.data.format)) { - vmtype = 'qemu'; - } else if (PVE.Utils.volume_is_lxc_backup(rec.data.volid, rec.data.format)) { - vmtype = 'lxc'; - } else { - return; - } - - var win = Ext.create('PVE.window.Restore', { - nodename: nodename, - volid: rec.data.volid, - volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), - vmtype: vmtype - }); - win.show(); - win.on('destroy', reload); - } - }, - removeButton, - imageRemoveButton, - templateButton, - uploadButton, - { - xtype: 'proxmoxButton', - text: gettext('Show Configuration'), - disabled: true, - selModel: sm, - enableFn: function(rec) { - return rec && rec.data.content === 'backup'; - }, - handler: function(b,e,rec) { - var win = Ext.create('PVE.window.BackupConfig', { - volume: rec.data.volid, - pveSelNode: me.pveSelNode - }); - - win.show(); - } - }, - '->', - gettext('Search') + ':', ' ', - { - xtype: 'textfield', - width: 200, - enableKeyEvents: true, - listeners: { - buffer: 500, - keyup: function(field) { - store.clearFilter(true); - store.filter([ - { - property: 'text', - value: field.getValue(), - anyMatch: true, - caseSensitive: false - } - ]); - } - } - } - ], - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - renderer: PVE.Utils.render_storage_content, - dataIndex: 'text' - }, - { - header: gettext('Date'), - width: 150, - dataIndex: 'vdate' - }, - { - header: gettext('Format'), - width: 100, - dataIndex: 'format' - }, - { - header: gettext('Type'), - width: 100, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Size'), - width: 100, - renderer: Proxmox.Utils.format_size, - dataIndex: 'size' - } - ], - listeners: { - activate: reload - } - }); - - me.callParent(); - - // disable the buttons/restrict the upload window - // if templates or uploads are not allowed - me.mon(me.statusStore, 'load', function(s, records, success) { - var availcontent = []; - Ext.Array.each(records, function(item){ - if (item.id === 'content') { - availcontent = item.data.value.split(','); - } - }); - var templ = false; - var upload = false; - var cts = []; - - Ext.Array.each(availcontent, function(content) { - if (content === 'vztmpl') { - templ = true; - cts.push('vztmpl'); - } else if (content === 'iso') { - upload = true; - cts.push('iso'); - } - }); - - if (templ !== upload) { - uploadButton.contents = cts; - } - - templateButton.setDisabled(!templ); - uploadButton.setDisabled(!upload && !templ); - }); - } -}, function() { - - Ext.define('pve-storage-content', { - extend: 'Ext.data.Model', - fields: [ - 'volid', 'content', 'format', 'size', 'used', 'vmid', - 'channel', 'id', 'lun', - { - name: 'text', - convert: function(value, record) { - // check for volid, because if you click on a grouping header, - // it calls convert (but with an empty volid) - if (value || record.data.volid === null) { - return value; - } - return PVE.Utils.render_storage_content(value, {}, record); - } - }, - { - name: 'vdate', - convert: function(value, record) { - // check for volid, because if you click on a grouping header, - // it calls convert (but with an empty volid) - if (value || record.data.volid === null) { - return value; - } - let t = record.data.content; - if (t === "backup") { - let v = record.data.volid; - let match = v.match(/(\d{4}_\d{2}_\d{2})-(\d{2}_\d{2}_\d{2})/); - if (match) { - let date = match[1].replace(/_/g, '-'); - let time = match[2].replace(/_/g, ':'); - return date + " " + time; - } - } - if (record.data.ctime) { - let ctime = new Date(record.data.ctime * 1000); - return Ext.Date.format(ctime,'Y-m-d H:i:s'); - } - return ''; - } - }, - ], - idProperty: 'volid' - }); - -}); -Ext.define('PVE.storage.StatusView', { - extend: 'PVE.panel.StatusView', - alias: 'widget.pveStorageStatusView', - - height: 230, - title: gettext('Status'), - - layout: { - type: 'vbox', - align: 'stretch' - }, - - defaults: { - xtype: 'pveInfoWidget', - padding: '0 30 5 30' - }, - items: [ - { - xtype: 'box', - height: 30 - }, - { - itemId: 'enabled', - title: gettext('Enabled'), - printBar: false, - textField: 'disabled', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - itemId: 'active', - title: gettext('Active'), - printBar: false, - textField: 'active', - renderer: Proxmox.Utils.format_boolean - }, - { - itemId: 'content', - title: gettext('Content'), - printBar: false, - textField: 'content', - renderer: PVE.Utils.format_content_types - }, - { - itemId: 'type', - title: gettext('Type'), - printBar: false, - textField: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - xtype: 'box', - height: 10 - }, - { - itemId: 'usage', - title: gettext('Usage'), - valueField: 'used', - maxField: 'total' - } - ], - - updateTitle: function() { - return; - } -}); -Ext.define('PVE.storage.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveStorageSummary', - scrollable: true, - bodyPadding: 5, - tbar: [ - '->', - { - xtype: 'proxmoxRRDTypeSelector' - } - ], - layout: { - type: 'column' - }, - defaults: { - padding: 5, - columnWidth: 1 - }, - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storage = me.pveSelNode.data.storage; - if (!storage) { - throw "no storage ID specified"; - } - - var rstore = Ext.create('Proxmox.data.ObjectStore', { - url: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/status", - interval: 1000 - }); - - var rrdstore = Ext.create('Proxmox.data.RRDStore', { - rrdurl: "/api2/json/nodes/" + nodename + "/storage/" + storage + "/rrddata", - model: 'pve-rrd-storage' - }); - - Ext.apply(me, { - items: [ - { - xtype: 'pveStorageStatusView', - pveSelNode: me.pveSelNode, - rstore: rstore - }, - { - xtype: 'proxmoxRRDChart', - title: gettext('Usage'), - fields: ['total','used'], - fieldTitles: ['Total Size', 'Used Size'], - store: rrdstore - } - ], - listeners: { - activate: function() { rstore.startUpdate(); rrdstore.startUpdate(); }, - destroy: function() { rstore.stopUpdate(); rrdstore.stopUpdate(); } - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.storage.Browser', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.storage.Browser', - - onlineHelp: 'chapter_storage', - - initComponent: function() { - var me = this; - - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var storeid = me.pveSelNode.data.storage; - if (!storeid) { - throw "no storage ID specified"; - } - - - me.items = [ - { - title: gettext('Summary'), - xtype: 'pveStorageSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - } - ]; - - var caps = Ext.state.Manager.get('GuiCap'); - - Ext.apply(me, { - title: Ext.String.format(gettext("Storage {0} on node {1}"), - "'" + storeid + "'", "'" + nodename + "'"), - hstateid: 'storagetab' - }); - - if (caps.storage['Datastore.Allocate'] || - caps.storage['Datastore.AllocateSpace'] || - caps.storage['Datastore.Audit']) { - me.items.push({ - xtype: 'pveStorageContentView', - title: gettext('Content'), - iconCls: 'fa fa-th', - itemId: 'content' - }); - } - - if (caps.storage['Permissions.Modify']) { - me.items.push({ - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - path: '/storage/' + storeid - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.storage.DirInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_directory', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'path', - value: '', - fieldLabel: gettext('Directory'), - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.NFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveNFSScan', - - queryParam: 'server', - - valueField: 'path', - displayField: 'path', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.nfsServer) { - me.store.removeAll(); - } - - me.allQuery = me.nfsServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.nfsServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'path', 'options' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/nfs' - } - }); - - store.sort('path', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.NFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_nfs', - - options : [], - - onGetValues: function(values) { - var me = this; - - var i; - var res = []; - for (i = 0; i < me.options.length; i++) { - var item = me.options[i]; - if (!item.match(/^vers=(.*)$/)) { - res.push(item); - } - } - if (values.nfsversion && values.nfsversion !== '__default__') { - res.push('vers=' + values.nfsversion); - } - delete values.nfsversion; - values.options = res.join(','); - if (values.options === '') { - delete values.options; - if (!me.isCreate) { - values["delete"] = "options"; - } - } - - return me.callParent([values]); - }, - - setValues: function(values) { - var me = this; - if (values.options) { - var res = values.options; - me.options = values.options.split(','); - me.options.forEach(function(item) { - var match = item.match(/^vers=(.*)$/); - if (match) { - values.nfsversion = match[1]; - } - }); - } - return me.callParent([values]); - }, - - initComponent : function() { - var me = this; - - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=export]'); - exportField.setServer(value); - exportField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'pveNFSScan' : 'displayfield', - name: 'export', - value: '', - fieldLabel: 'Export', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.advancedColumn1 = [ - { - xtype: 'proxmoxKVComboBox', - fieldLabel: gettext('NFS Version'), - name: 'nfsversion', - value: '__default__', - deleteEmpty: false, - comboItems: [ - ['__default__', Proxmox.Utils.defaultText], - ['3', '3'], - ['4', '4'], - ['4.1', '4.1'], - ['4.2', '4.2'] - ] - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.CIFSScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveCIFSScan', - - queryParam: 'server', - - valueField: 'share', - displayField: 'share', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.cifsServer) { - me.store.removeAll(); - } - - var params = {}; - if (me.cifsUsername && me.cifsPassword) { - params.username = me.cifsUsername; - params.password = me.cifsPassword; - } - - if (me.cifsDomain) { - params.domain = me.cifsDomain; - } - - me.store.getProxy().setExtraParams(params); - me.allQuery = me.cifsServer; - - me.callParent(); - }, - - setServer: function(server) { - this.cifsServer = server; - }, - - setUsername: function(username) { - this.cifsUsername = username; - }, - - setPassword: function(password) { - this.cifsPassword = password; - }, - - setDomain: function(domain) { - this.cifsDomain = domain; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: ['description', 'share'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/cifs' - } - }); - store.sort('share', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.CIFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_cifs', - - initComponent : function() { - var me = this; - - var passwordfield = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - inputType: 'password', - name: 'password', - value: me.isCreate ? '' : '********', - fieldLabel: gettext('Password'), - allowBlank: false, - disabled: me.isCreate, - minLength: 1, - listeners: { - change: function(f, value) { - - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setPassword(value); - } - } - } - }); - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=share]'); - exportField.setServer(value); - } - } - } - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: '', - fieldLabel: gettext('Username'), - emptyText: gettext('Guest user'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (!me.isCreate) { - return; - } - var exportField = me.down('field[name=share]'); - exportField.setUsername(value); - - if (value == "") { - passwordfield.disable(); - } else { - passwordfield.enable(); - } - passwordfield.validate(); - } - } - }, - passwordfield, - { - xtype: me.isCreate ? 'pveCIFSScan' : 'displayfield', - name: 'share', - value: '', - fieldLabel: 'Share', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'domain', - value: me.isCreate ? '' : undefined, - fieldLabel: gettext('Domain'), - allowBlank: true, - listeners: { - change: function(f, value) { - if (me.isCreate) { - - var exportField = me.down('field[name=share]'); - exportField.setDomain(value); - } - } - } - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.GlusterFsScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveGlusterFsScan', - - queryParam: 'server', - - valueField: 'volname', - displayField: 'volname', - matchFieldWidth: false, - listConfig: { - loadingText: 'Scanning...', - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.glusterServer) { - me.store.removeAll(); - } - - me.allQuery = me.glusterServer; - - me.callParent(); - }, - - setServer: function(server) { - var me = this; - - me.glusterServer = server; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'volname' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/glusterfs' - } - }); - - store.sort('volname', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.GlusterFsInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_glusterfs', - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'server', - value: '', - fieldLabel: gettext('Server'), - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var volumeField = me.down('field[name=volume]'); - volumeField.setServer(value); - volumeField.setValue(''); - } - } - } - }, - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'server2', - value: '', - fieldLabel: gettext('Second Server'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'pveGlusterFsScan' : 'displayfield', - name: 'volume', - value: '', - fieldLabel: 'Volume name', - allowBlank: false - }, - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'iso', 'backup', 'vztmpl', 'snippets'], - name: 'content', - value: 'images', - multiSelect: true, - fieldLabel: gettext('Content'), - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - disabled: true, - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.IScsiScan', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveIScsiScan', - - queryParam: 'portal', - valueField: 'target', - displayField: 'target', - matchFieldWidth: false, - listConfig: { - loadingText: gettext('Scanning...'), - width: 350 - }, - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.portal) { - me.store.removeAll(); - } - - me.allQuery = me.portal; - - me.callParent(); - }, - - setPortal: function(portal) { - var me = this; - - me.portal = portal; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'target', 'portal' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/iscsi' - } - }); - - store.sort('target', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.IScsiInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_open_iscsi', - - onGetValues: function(values) { - var me = this; - - values.content = values.luns ? 'images' : 'none'; - delete values.luns; - - return me.callParent([values]); - }, - - setValues: function(values) { - values.luns = (values.content.indexOf('images') !== -1) ? true : false; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: 'Portal', - allowBlank: false, - listeners: { - change: function(f, value) { - if (me.isCreate) { - var exportField = me.down('field[name=target]'); - exportField.setPortal(value); - exportField.setValue(''); - } - } - } - }, - { - readOnly: !me.isCreate, - xtype: me.isCreate ? 'pveIScsiScan' : 'displayfield', - name: 'target', - value: '', - fieldLabel: 'Target', - allowBlank: false - } - ]; - - me.column2 = [ - { - xtype: 'checkbox', - name: 'luns', - checked: true, - fieldLabel: gettext('Use LUNs directly') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.VgSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveVgSelector', - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'vg', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - store.sort('vg', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseStorageSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseStorageSelector', - - existingGroupsText: gettext("Existing volume groups"), - queryMode: 'local', - editable: false, - value: '', - valueField: 'storage', - displayField: 'text', - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store', { - autoLoad: { - addRecords: true, - params: { - type: 'iscsi' - } - }, - fields: [ 'storage', 'type', 'content', - { - name: 'text', - convert: function(value, record) { - if (record.data.storage) { - return record.data.storage + " (iSCSI)"; - } else { - return me.existingGroupsText; - } - } - }], - proxy: { - type: 'proxmox', - url: '/api2/json/storage/' - } - }); - - store.loadData([{ storage: '' }], true); - - store.sort('storage', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LVMInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvm', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.VgSelector', { - name: 'vgname', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var baseField = Ext.createWidget('pveFileSelector', { - name: 'base', - hidden: true, - disabled: true, - nodename: 'localhost', - storageContent: 'images', - fieldLabel: gettext('Base volume'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseStorageSelector', - name: 'basesel', - fieldLabel: gettext('Base storage'), - submitValue: false, - listeners: { - change: function(f, value) { - if (value) { - vgnameField.setVisible(true); - vgnameField.setDisabled(false); - vgField.setVisible(false); - vgField.setDisabled(true); - baseField.setVisible(true); - baseField.setDisabled(false); - } else { - vgnameField.setVisible(false); - vgnameField.setDisabled(true); - vgField.setVisible(true); - vgField.setDisabled(false); - baseField.setVisible(false); - baseField.setDisabled(true); - } - baseField.setStorage(value); - } - } - }); - - me.column1.push(baseField); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'shared', - uncheckedValue: 0, - fieldLabel: gettext('Shared') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.TPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveTPSelector', - - queryParam: 'vg', - valueField: 'lv', - displayField: 'lv', - editable: false, - - doRawQuery: function() { - }, - - onTriggerClick: function() { - var me = this; - - if (!me.queryCaching || me.lastQuery !== me.vg) { - me.store.removeAll(); - } - - me.allQuery = me.vg; - - me.callParent(); - }, - - setVG: function(myvg) { - var me = this; - - me.vg = myvg; - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - fields: [ 'lv' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvmthin' - } - }); - - store.sort('lv', 'ASC'); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.BaseVGSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveBaseVGSelector', - - valueField: 'vg', - displayField: 'vg', - queryMode: 'local', - editable: false, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, - fields: [ 'vg', 'size', 'free'], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm' - } - }); - - Ext.apply(me, { - store: store, - listConfig: { - loadingText: gettext('Scanning...') - } - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.LvmThinInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_lvmthin', - - initComponent : function() { - var me = this; - - me.column1 = []; - - var vgnameField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'vgname', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Volume group'), - allowBlank: false - }); - - var thinpoolField = Ext.createWidget(me.isCreate ? 'textfield' : 'displayfield', { - name: 'thinpool', - hidden: !!me.isCreate, - disabled: !!me.isCreate, - value: '', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - if (me.isCreate) { - var vgField = Ext.create('PVE.storage.TPoolSelector', { - name: 'thinpool', - fieldLabel: gettext('Thin Pool'), - allowBlank: false - }); - - me.column1.push({ - xtype: 'pveBaseVGSelector', - name: 'vgname', - fieldLabel: gettext('Volume group'), - listeners: { - change: function(f, value) { - if (me.isCreate) { - vgField.setVG(value); - vgField.setValue(''); - } - } - } - }); - - me.column1.push(vgField); - } - - me.column1.push(vgnameField); - - me.column1.push(thinpoolField); - - // here value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push({ - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - - me.column2 = []; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.CephFSInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'storage_cephfs', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'cephfs'; - - me.column1 = []; - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - value: '', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - value: 'admin', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['backup', 'iso', 'vztmpl', 'snippets'], - fieldLabel: gettext('Content'), - name: 'content', - value: 'backup', - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Max Backups'), - name: 'maxfiles', - reference: 'maxfiles', - minValue: 0, - maxValue: 365, - value: me.isCreate ? '1' : undefined, - allowBlank: false - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged cephFS') - }]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.Ceph.Model', { - extend: 'Ext.app.ViewModel', - alias: 'viewmodel.cephstorage', - - data: { - pveceph: true, - pvecephPossible: true - } -}); - -Ext.define('PVE.storage.Ceph.Controller', { - extend: 'PVE.controller.StorageEdit', - alias: 'controller.cephstorage', - - control: { - '#': { - afterrender: 'queryMonitors' - }, - 'textfield[name=username]': { - disable: 'resetField' - }, - 'displayfield[name=monhost]': { - enable: 'queryMonitors' - }, - 'textfield[name=monhost]': { - disable: 'resetField', - enable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - queryMonitors: function(field, newVal, oldVal) { - // we get called with two signatures, the above one for a field - // change event and the afterrender from the view, this check only - // can be true for the field change one and omit the API request if - // pveceph got unchecked - as it's not needed there. - if (field && !newVal && oldVal) { - return; - } - var view = this.getView(); - var vm = this.getViewModel(); - if (!(view.isCreate || vm.get('pveceph'))) { - return; // only query on create or if editing a pveceph store - } - - var monhostField = this.lookupReference('monhost'); - - Proxmox.Utils.API2Request({ - url: '/api2/json/nodes/localhost/ceph/mon', - method: 'GET', - scope: this, - callback: function(options, success, response) { - var data = response.result.data; - if (response.status === 200) { - if (data.length > 0) { - var monhost = Ext.Array.pluck(data, 'name').sort().join(','); - monhostField.setValue(monhost); - monhostField.resetOriginalValue(); - if (view.isCreate) { - vm.set('pvecephPossible', true); - } - } else { - vm.set('pveceph', false); - } - } else { - vm.set('pveceph', false); - vm.set('pvecephPossible', false); - } - } - }); - } -}); - -Ext.define('PVE.storage.RBDInputPanel', { - extend: 'PVE.panel.StorageBase', - controller: 'cephstorage', - - onlineHelp: 'ceph_rados_block_devices', - - viewModel: { - type: 'cephstorage' - }, - - setValues: function(values) { - if (values.monhost) { - this.viewModel.set('pveceph', false); - this.lookupReference('pvecephRef').setValue(false); - this.lookupReference('pvecephRef').resetOriginalValue(); - } - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - me.type = 'rbd'; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push({ - xtype: 'pveCephPoolSelector', - nodename: me.nodename, - name: 'pool', - bind: { - disabled: '{!pveceph}', - submitValue: '{pveceph}', - hidden: '{!pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - },{ - xtype: 'textfield', - name: 'pool', - value: 'rbd', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } else { - me.column1.push({ - xtype: 'displayfield', - nodename: me.nodename, - name: 'pool', - fieldLabel: gettext('Pool'), - allowBlank: false - }); - } - - me.column1.push( - { - xtype: 'textfield', - name: 'monhost', - vtype: 'HostList', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}', - hidden: '{pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)', - allowBlank: false - }, - { - xtype: 'displayfield', - reference: 'monhost', - bind: { - disabled: '{!pveceph}', - hidden: '{!pveceph}' - }, - value: '', - fieldLabel: 'Monitor(s)' - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'username', - bind: { - disabled: '{pveceph}', - submitValue: '{!pveceph}' - }, - value: 'admin', - fieldLabel: gettext('User name'), - allowBlank: true - } - ); - - me.column2 = [ - { - xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images'], - multiSelect: true, - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'krbd', - uncheckedValue: 0, - fieldLabel: 'KRBD' - } - ]; - - me.columnB = [{ - xtype: 'proxmoxcheckbox', - name: 'pveceph', - reference: 'pvecephRef', - bind : { - disabled: '{!pvecephPossible}', - value: '{pveceph}' - }, - checked: true, - uncheckedValue: 0, - submitValue: false, - hidden: !me.isCreate, - boxLabel: gettext('Use Proxmox VE managed hyper-converged ceph pool') - }]; - - me.callParent(); - } -}); -/*jslint confusion: true*/ -Ext.define('PVE.storage.ZFSInputPanel', { - extend: 'PVE.panel.StorageBase', - - viewModel: { - parent: null, - data: { - isLIO: false, - isComstar: true, - hasWriteCacheOption: true - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[name=iscsiprovider]': { - change: 'changeISCSIProvider' - } - }, - changeISCSIProvider: function(f, newVal, oldVal) { - var vm = this.getViewModel(); - vm.set('isLIO', newVal === 'LIO'); - vm.set('isComstar', newVal === 'comstar'); - vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt'); - } - }, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.content = 'images'; - } - - values.nowritecache = values.writecache ? 0 : 1; - delete values.writecache; - - return me.callParent([values]); - }, - - setValues: function diff(values) { - values.writecache = values.nowritecache ? 0 : 1; - this.callParent([values]); - }, - - initComponent : function() { - var me = this; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'portal', - value: '', - fieldLabel: gettext('Portal'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'pool', - value: '', - fieldLabel: gettext('Pool'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'blocksize', - value: '4k', - fieldLabel: gettext('Block Size'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'target', - value: '', - fieldLabel: gettext('Target'), - allowBlank: false - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_tg', - value: '', - fieldLabel: gettext('Target group'), - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - allowBlank: true - } - ]; - - me.column2 = [ - { - xtype: me.isCreate ? 'pveiScsiProviderSelector' : 'displayfield', - name: 'iscsiprovider', - value: 'comstar', - fieldLabel: gettext('iSCSI Provider'), - allowBlank: false - }, - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'proxmoxcheckbox', - name: 'writecache', - checked: true, - bind: me.isCreate ? { disabled: '{!hasWriteCacheOption}' } : { hidden: '{!hasWriteCacheOption}' }, - uncheckedValue: 0, - fieldLabel: gettext('Write cache') - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'comstar_hg', - value: '', - bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' }, - fieldLabel: gettext('Host group'), - allowBlank: true - }, - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'lio_tpg', - value: '', - bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' }, - allowBlank: false, - fieldLabel: gettext('Target portal group') - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.storage.ZFSPoolSelector', { - extend: 'Ext.form.field.ComboBox', - alias: 'widget.pveZFSPoolSelector', - valueField: 'pool', - displayField: 'pool', - queryMode: 'local', - editable: false, - listConfig: { - loadingText: gettext('Scanning...') - }, - initComponent : function() { - var me = this; - - if (!me.nodename) { - me.nodename = 'localhost'; - } - - var store = Ext.create('Ext.data.Store', { - autoLoad: {}, // true, - fields: [ 'pool', 'size', 'free' ], - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/zfs' - } - }); - - store.sort('pool', 'ASC'); - - Ext.apply(me, { - store: store - }); - - me.callParent(); - } -}); - -Ext.define('PVE.storage.ZFSPoolInputPanel', { - extend: 'PVE.panel.StorageBase', - - onlineHelp: 'storage_zfspool', - - initComponent : function() { - var me = this; - - me.column1 = []; - - if (me.isCreate) { - me.column1.push(Ext.create('PVE.storage.ZFSPoolSelector', { - name: 'pool', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } else { - me.column1.push(Ext.createWidget('displayfield', { - name: 'pool', - value: '', - fieldLabel: gettext('ZFS Pool'), - allowBlank: false - })); - } - - // value is an array, - // while before it was a string - /*jslint confusion: true*/ - me.column1.push( - {xtype: 'pveContentTypeSelector', - cts: ['images', 'rootdir'], - fieldLabel: gettext('Content'), - name: 'content', - value: ['images', 'rootdir'], - multiSelect: true, - allowBlank: false - }); - /*jslint confusion: false*/ - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'sparse', - checked: false, - uncheckedValue: 0, - fieldLabel: gettext('Thin provision') - }, - { - xtype: 'textfield', - name: 'blocksize', - emptyText: '8k', - fieldLabel: gettext('Block Size'), - allowBlank: true - } - ]; - - me.callParent(); - } -}); -Ext.define('PVE.ha.StatusView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAStatusView'], - - onlineHelp: 'chapter_ha_manager', - - sortPriority: { - quorum: 1, - master: 2, - lrm: 3, - service: 4 - }, - - initComponent : function() { - var me = this; - - if (!me.rstore) { - throw "no rstore given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sortAfterUpdate: true, - sorters: [{ - sorterFn: function(rec1, rec2) { - var p1 = me.sortPriority[rec1.data.type]; - var p2 = me.sortPriority[rec2.data.type]; - return (p1 !== p2) ? ((p1 > p2) ? 1 : -1) : 0; - } - }], - filters: { - property: 'type', - value: 'service', - operator: '!=' - } - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Type'), - width: 80, - dataIndex: 'type' - }, - { - header: gettext('Status'), - width: 80, - flex: 1, - dataIndex: 'status' - } - ] - }); - - me.callParent(); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - - } -}, function() { - - Ext.define('pve-ha-status', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'type', 'node', 'status', 'sid', - 'state', 'group', 'comment', - 'max_restart', 'max_relocate', 'type', - 'crm_state', 'request_state', - { - name: 'vname', - convert: function(value, record) { - let sid = record.data.sid; - if (!sid) return ''; - - let res = sid.match(/^(\S+):(\S+)$/); - if (res[1] !== 'vm' && res[1] !== 'ct') { - return '-'; - } - let vmid = res[2]; - return PVE.data.ResourceStore.guestName(vmid); - }, - }, - ], - idProperty: 'id' - }); - -}); -Ext.define('PVE.ha.Status', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveHAStatus', - - onlineHelp: 'chapter_ha_manager', - layout: { - type: 'vbox', - align: 'stretch' - }, - - initComponent: function() { - var me = this; - - me.rstore = Ext.create('Proxmox.data.ObjectStore', { - interval: me.interval, - model: 'pve-ha-status', - storeid: 'pve-store-' + (++Ext.idSeed), - groupField: 'type', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/ha/status/current' - } - }); - - me.items = [{ - xtype: 'pveHAStatusView', - title: gettext('Status'), - rstore: me.rstore, - border: 0, - collapsible: true, - padding: '0 0 20 0' - },{ - xtype: 'pveHAResourcesView', - flex: 1, - collapsible: true, - title: gettext('Resources'), - border: 0, - rstore: me.rstore - }]; - - me.callParent(); - me.on('activate', me.rstore.startUpdate); - } -}); -Ext.define('PVE.ha.GroupSelector', { - extend: 'Proxmox.form.ComboGrid', - alias: ['widget.pveHAGroupSelector'], - - value: [], - autoSelect: false, - valueField: 'group', - displayField: 'group', - listConfig: { - columns: [ - { - header: gettext('Group'), - width: 100, - sortable: true, - dataIndex: 'group' - }, - { - header: gettext('Nodes'), - width: 100, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode - } - ] - }, - store: { - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }, - - initComponent: function() { - var me = this; - me.callParent(); - me.getStore().load(); - } - -}, function() { - - Ext.define('pve-ha-groups', { - extend: 'Ext.data.Model', - fields: [ - 'group', 'type', 'digest', 'nodes', 'comment', - { - name : 'restricted', - type: 'boolean' - }, - { - name : 'nofailback', - type: 'boolean' - } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/ha/groups" - }, - idProperty: 'group' - }); -}); -Ext.define('PVE.ha.VMResourceInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_resource_config', - vmid: undefined, - - onGetValues: function(values) { - var me = this; - - if (values.vmid) { - values.sid = values.vmid; - } - delete values.vmid; - - PVE.Utils.delete_if_default(values, 'group', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_restart', '1', me.isCreate); - PVE.Utils.delete_if_default(values, 'max_relocate', '1', me.isCreate); - - return values; - }, - - initComponent : function() { - var me = this; - var MIN_QUORUM_VOTES = 3; - - var disabledHint = Ext.createWidget({ - xtype: 'displayfield', // won't get submitted by default - userCls: 'pmx-hint', - value: 'Disabling the resource will stop the guest system. ' + - 'See the online help for details.', - hidden: true - }); - - var fewVotesHint = Ext.createWidget({ - itemId: 'fewVotesHint', - xtype: 'displayfield', - userCls: 'pmx-hint', - value: 'At least three quorum votes are recommended for reliable HA.', - hidden: true - }); - - Proxmox.Utils.API2Request({ - url: '/cluster/config/nodes', - method: 'GET', - failure: function(response) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - success: function(response) { - var nodes = response.result.data; - var votes = 0; - Ext.Array.forEach(nodes, function(node) { - var vote = parseInt(node.quorum_votes, 10); // parse as base 10 - votes += vote || 0; // parseInt might return NaN, which is false - }); - - if (votes < MIN_QUORUM_VOTES) { - fewVotesHint.setVisible(true); - } - } - }); - - /*jslint confusion: true */ - var vmidStore = (me.vmid) ? {} : { - model: 'PVEResources', - autoLoad: true, - sorters: 'vmid', - filters: [ - { - property: 'type', - value: /lxc|qemu/ - }, - { - property: 'hastate', - value: /unmanaged/ - } - ] - }; - - // value is a string above, but a number below - me.column1 = [ - { - xtype: me.vmid ? 'displayfield' : 'vmComboSelector', - submitValue: me.isCreate, - name: 'vmid', - fieldLabel: (me.vmid && me.guestType === 'ct') ? 'CT' : 'VM', - value: me.vmid, - store: vmidStore, - validateExists: true - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_restart', - fieldLabel: gettext('Max. Restart'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - }, - { - xtype: 'proxmoxintegerfield', - name: 'max_relocate', - fieldLabel: gettext('Max. Relocate'), - value: 1, - minValue: 0, - maxValue: 10, - allowBlank: false - } - ]; - /*jslint confusion: false */ - - me.column2 = [ - { - xtype: 'pveHAGroupSelector', - name: 'group', - fieldLabel: gettext('Group') - }, - { - xtype: 'proxmoxKVComboBox', - name: 'state', - value: 'started', - fieldLabel: gettext('Request State'), - comboItems: [ - ['started', 'started'], - ['stopped', 'stopped'], - ['ignored', 'ignored'], - ['disabled', 'disabled'] - ], - listeners: { - 'change': function(field, newValue) { - if (newValue === 'disabled') { - disabledHint.setVisible(true); - } - else { - if (disabledHint.isVisible()) { - disabledHint.setVisible(false); - } - } - } - } - }, - disabledHint - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - fewVotesHint - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.VMResourceEdit', { - extend: 'Proxmox.window.Edit', - - vmid: undefined, - guestType: undefined, - isCreate: undefined, - - initComponent : function() { - var me = this; - - if (me.isCreate === undefined) { - me.isCreate = !me.vmid; - } - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/resources'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/resources/' + me.vmid; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.VMResourceInputPanel', { - isCreate: me.isCreate, - vmid: me.vmid, - guestType: me.guestType - }); - - Ext.apply(me, { - subject: gettext('Resource') + ': ' + gettext('Container') + - '/' + gettext('Virtual Machine'), - isAdd: true, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(values.sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - throw "got unexpected resource type"; - } - - values.vmid = res[2]; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.ResourcesView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAResourcesView'], - - onlineHelp: 'ha_manager_resources', - - stateful: true, - stateId: 'grid-ha-resources', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - if (!me.rstore) { - throw "no store given"; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - filters: { - property: 'type', - value: 'service' - } - }); - - var reload = function() { - me.rstore.load(); - }; - - var render_error = function(dataIndex, value, metaData, record) { - var errors = record.data.errors; - if (errors) { - var msg = errors[dataIndex]; - if (msg) { - metaData.tdCls = 'proxmox-invalid-row'; - var html = '

' + Ext.htmlEncode(msg) + '

'; - metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + - html.replace(/\"/g,'"') + '"'; - } - } - return value; - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - var sid = rec.data.sid; - - var regex = /^(\S+):(\S+)$/; - var res = regex.exec(sid); - - if (res[1] !== 'vm' && res[1] !== 'ct') { - return; - } - var guestType = res[1]; - var vmid = res[2]; - - var win = Ext.create('PVE.ha.VMResourceEdit',{ - guestType: guestType, - vmid: vmid - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/resources/', - getUrl: function(rec) { - var me = this; - return me.baseurl + '/' + rec.get('sid'); - }, - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.VMResourceEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - - columns: [ - { - header: 'ID', - width: 100, - sortable: true, - dataIndex: 'sid' - }, - { - header: gettext('State'), - width: 100, - sortable: true, - dataIndex: 'state' - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Request State'), - width: 100, - hidden: true, - sortable: true, - renderer: function(v) { - return v || 'started'; - }, - dataIndex: 'request_state' - }, - { - header: gettext('CRM State'), - width: 100, - hidden: true, - sortable: true, - dataIndex: 'crm_state' - }, - { - header: gettext('Name'), - width: 100, - sortable: true, - dataIndex: 'vname', - }, - { - header: gettext('Max. Restart'), - width: 100, - sortable: true, - renderer: (v) => v === undefined ? '1' : v, - dataIndex: 'max_restart' - }, - { - header: gettext('Max. Relocate'), - width: 100, - sortable: true, - renderer: (v) => v === undefined ? '1' : v, - dataIndex: 'max_relocate' - }, - { - header: gettext('Group'), - width: 200, - sortable: true, - renderer: function(value, metaData, record) { - return render_error('group', value, metaData, record); - }, - dataIndex: 'group' - }, - { - header: gettext('Description'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.ha.GroupInputPanel', { - extend: 'Proxmox.panel.InputPanel', - onlineHelp: 'ha_manager_groups', - - groupId: undefined, - - onGetValues: function(values) { - var me = this; - - if (me.isCreate) { - values.type = 'group'; - } - - return values; - }, - - initComponent : function() { - var me = this; - - var update_nodefield, update_node_selection; - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - update_nodefield(selected); - } - } - }); - - // use already cached data to avoid an API call - var data = PVE.data.ResourceStore.getNodes(); - - var store = Ext.create('Ext.data.Store', { - fields: [ 'node', 'mem', 'cpu', 'priority' ], - data: data, - proxy: { - type: 'memory', - reader: {type: 'json'} - }, - sorters: [ - { - property : 'node', - direction: 'ASC' - } - ] - }); - - var nodegrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - columns: [ - { - header: gettext('Node'), - flex: 1, - dataIndex: 'node' - }, - { - header: gettext('Memory usage') + " %", - renderer: PVE.Utils.render_mem_usage_percent, - sortable: true, - width: 150, - dataIndex: 'mem' - }, - { - header: gettext('CPU usage'), - renderer: PVE.Utils.render_cpu, - sortable: true, - width: 150, - dataIndex: 'cpu' - }, - { - header: 'Priority', - xtype: 'widgetcolumn', - dataIndex: 'priority', - sortable: true, - stopSelection: true, - widget: { - xtype: 'proxmoxintegerfield', - minValue: 0, - maxValue: 1000, - isFormField: false, - listeners: { - change: function(numberfield, value, old_value) { - var record = numberfield.getWidgetRecord(); - record.set('priority', value); - update_nodefield(sm.getSelection()); - } - } - } - } - ] - }); - - var nodefield = Ext.create('Ext.form.field.Hidden', { - name: 'nodes', - value: '', - listeners: { - change: function (nodefield, value) { - update_node_selection(value); - } - }, - isValid: function () { - var value = nodefield.getValue(); - return (value && 0 !== value.length); - } - }); - - update_node_selection = function(string) { - sm.deselectAll(true); - - string.split(',').forEach(function (e, idx, array) { - var res = e.split(':'); - - store.each(function(record) { - var node = record.get('node'); - - if (node == res[0]) { - sm.select(record, true); - record.set('priority', res[1]); - record.commit(); - } - }); - }); - nodegrid.reconfigure(store); - - }; - - update_nodefield = function(selected) { - var nodes = ''; - var first_iteration = true; - Ext.Array.each(selected, function(record) { - if (!first_iteration) { - nodes += ','; - } - first_iteration = false; - - nodes += record.data.node; - if (record.data.priority) { - nodes += ':' + record.data.priority; - } - }); - - // nodefield change listener calls us again, which results in a - // endless recursion, suspend the event temporary to avoid this - nodefield.suspendEvent('change'); - nodefield.setValue(nodes); - nodefield.resumeEvent('change'); - }; - - me.column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'group', - value: me.groupId || '', - fieldLabel: 'ID', - vtype: 'StorageId', - allowBlank: false - }, - nodefield - ]; - - me.column2 = [ - { - xtype: 'proxmoxcheckbox', - name: 'restricted', - uncheckedValue: 0, - fieldLabel: 'restricted' - }, - { - xtype: 'proxmoxcheckbox', - name: 'nofailback', - uncheckedValue: 0, - fieldLabel: 'nofailback' - } - ]; - - me.columnB = [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }, - nodegrid - ]; - - me.callParent(); - } -}); - -Ext.define('PVE.ha.GroupEdit', { - extend: 'Proxmox.window.Edit', - - groupId: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupId; - - if (me.isCreate) { - me.url = '/api2/extjs/cluster/ha/groups'; - me.method = 'POST'; - } else { - me.url = '/api2/extjs/cluster/ha/groups/' + me.groupId; - me.method = 'PUT'; - } - - var ipanel = Ext.create('PVE.ha.GroupInputPanel', { - isCreate: me.isCreate, - groupId: me.groupId - }); - - Ext.apply(me, { - subject: gettext('HA Group'), - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var values = response.result.data; - - ipanel.setValues(values); - } - }); - } - } -}); -Ext.define('PVE.ha.GroupsView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveHAGroupsView'], - - onlineHelp: 'ha_manager_groups', - - stateful: true, - stateId: 'grid-ha-groups', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - model: 'pve-ha-groups', - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - - var win = Ext.create('PVE.ha.GroupEdit',{ - groupId: rec.data.group - }); - win.on('destroy', reload); - win.show(); - }; - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/ha/groups/', - callback: function() { - reload(); - } - }); - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - Ext.apply(me, { - store: store, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Create'), - disabled: !caps.nodes['Sys.Console'], - handler: function() { - var win = Ext.create('PVE.ha.GroupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ], - columns: [ - { - header: gettext('Group'), - width: 150, - sortable: true, - dataIndex: 'group' - }, - { - header: 'restricted', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'restricted' - }, - { - header: 'nofailback', - width: 100, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'nofailback' - }, - { - header: gettext('Nodes'), - flex: 1, - sortable: false, - dataIndex: 'nodes' - }, - { - header: gettext('Comment'), - flex: 1, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment' - } - ], - listeners: { - activate: reload, - beforeselect: function(grid, record, index, eOpts) { - if (!caps.nodes['Sys.Console']) { - return false; - } - }, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.ha.FencingView', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveFencingView'], - - onlineHelp: 'ha_manager_fencing', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-ha-fencing', - data: [] - }); - - Ext.apply(me, { - store: store, - stateful: false, - viewConfig: { - trackOver: false, - deferEmptyText: false, - emptyText: 'Use watchdog based fencing.' - }, - columns: [ - { - header: 'Node', - width: 100, - sortable: true, - dataIndex: 'node' - }, - { - header: gettext('Command'), - flex: 1, - dataIndex: 'command' - } - ] - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-ha-fencing', { - extend: 'Ext.data.Model', - fields: [ - 'node', 'command', 'digest' - ] - }); - -}); -Ext.define('PVE.dc.Summary', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSummary', - - scrollable: true, - - bodyPadding: 5, - - layout: 'column', - - defaults: { - padding: 5, - columnWidth: 1, - }, - - items: [ - { - itemId: 'dcHealth', - xtype: 'pveDcHealth' - }, - { - itemId: 'dcGuests', - xtype: 'pveDcGuests' - }, - { - title: gettext('Resources'), - xtype: 'panel', - minHeight: 250, - bodyPadding: 5, - layout: 'hbox', - defaults: { - xtype: 'proxmoxGauge', - flex: 1 - }, - items:[ - { - title: gettext('CPU'), - itemId: 'cpu' - }, - { - title: gettext('Memory'), - itemId: 'memory' - }, - { - title: gettext('Storage'), - itemId: 'storage' - } - ] - }, - { - itemId: 'nodeview', - xtype: 'pveDcNodeView', - height: 250 - }, - { - title: gettext('Subscriptions'), - height: 220, - items: [ - { - itemId: 'subscriptions', - xtype: 'pveHealthWidget', - userCls: 'pointer', - listeners: { - element: 'el', - click: function() { - if (this.component.userCls === 'pointer') { - window.open('https://www.proxmox.com/en/proxmox-ve/pricing', '_blank'); - } - } - } - } - ] - } - ], - - listeners: { - resize: function(panel) { - PVE.Utils.updateColumns(panel); - }, - }, - - initComponent: function() { - var me = this; - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-status', - model: 'pve-dc-nodes', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/status" - } - }); - - var gridstore = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - filters: { - property: 'type', - value: 'node' - }, - sorters: { - property: 'id', - direction: 'ASC' - } - }); - - me.callParent(); - - me.getComponent('nodeview').setStore(gridstore); - - var gueststatus = me.getComponent('dcGuests'); - - var cpustat = me.down('#cpu'); - var memorystat = me.down('#memory'); - var storagestat = me.down('#storage'); - var sp = Ext.state.Manager.getProvider(); - - me.mon(PVE.data.ResourceStore, 'load', function(curstore, results) { - me.suspendLayout = true; - - var cpu = 0; - var maxcpu = 0; - - var nodes = 0; - - var memory = 0; - var maxmem = 0; - - var countedStorages = {}; - var used = 0; - var total = 0; - var usableStorages = {}; - var storages = sp.get('dash-storages') || ''; - storages.split(',').forEach(function(storage){ - if (storage !== '') { - usableStorages[storage] = true; - } - }); - - var qemu = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var lxc = { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }; - var error = 0; - - var i; - - for (i = 0; i < results.length; i++) { - var item = results[i]; - switch(item.data.type) { - case 'node': - cpu += (item.data.cpu * item.data.maxcpu); - maxcpu += item.data.maxcpu || 0; - memory += item.data.mem || 0; - maxmem += item.data.maxmem || 0; - nodes++; - - // update grid also - var griditem = gridstore.getById(item.data.id); - if (griditem) { - griditem.set('cpuusage', item.data.cpu); - var max = item.data.maxmem || 1; - var val = item.data.mem || 0; - griditem.set('memoryusage', val/max); - griditem.set('uptime', item.data.uptime); - griditem.commit(); //else it marks the fields as dirty - } - break; - case 'storage': - if (!Ext.Object.isEmpty(usableStorages)) { - if (usableStorages[item.data.id] === true) { - used += item.data.disk; - total += item.data.maxdisk; - } - break; - } - if (!countedStorages[item.data.storage] || - (!item.data.shared && !countedStorages[item.data.id])) { - used += item.data.disk; - total += item.data.maxdisk; - - countedStorages[item.data.storage === 'local'?item.data.id:item.data.storage] = true; - } - break; - case 'qemu': - qemu[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - case 'lxc': - lxc[item.data.template ? 'template' : item.data.status]++; - if (item.data.hastate === 'error') { - error++; - } - break; - default: break; - } - } - - var text = Ext.String.format(gettext('of {0} CPU(s)'), maxcpu); - cpustat.updateValue((cpu/maxcpu), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(memory), PVE.Utils.render_size(maxmem)); - memorystat.updateValue((memory/maxmem), text); - - text = Ext.String.format(gettext('{0} of {1}'), PVE.Utils.render_size(used), PVE.Utils.render_size(total)); - storagestat.updateValue((used/total), text); - - gueststatus.updateValues(qemu,lxc,error); - - me.suspendLayout = false; - me.updateLayout(true); - }); - - var dcHealth = me.getComponent('dcHealth'); - me.mon(rstore, 'load', dcHealth.updateStatus, dcHealth); - - var subs = me.down('#subscriptions'); - me.mon(rstore, 'load', function(store, records, success) { - var i; - var level; - var mixed = false; - for (i = 0; i < records.length; i++) { - if (records[i].get('type') !== 'node') { - continue; - } - var node = records[i]; - if (node.get('status') === 'offline') { - continue; - } - - var curlevel = node.get('level'); - - if (curlevel === '') { // no subscription trumps all, set and break - level = ''; - break; - } - - if (level === undefined) { // save level - level = curlevel; - } else if (level !== curlevel) { // detect different levels - mixed = true; - } - } - - var data = { - title: Proxmox.Utils.unknownText, - text: Proxmox.Utils.unknownText, - iconCls: PVE.Utils.get_health_icon(undefined, true) - }; - if (level === '') { - data = { - title: gettext('No Subscription'), - iconCls: PVE.Utils.get_health_icon('critical', true), - text: gettext('You have at least one node without subscription.') - }; - subs.setUserCls('pointer'); - } else if (mixed) { - data = { - title: gettext('Mixed Subscriptions'), - iconCls: PVE.Utils.get_health_icon('warning', true), - text: gettext('Warning: Your subscription levels are not the same.') - }; - subs.setUserCls('pointer'); - } else if (level) { - data = { - title: PVE.Utils.render_support_level(level), - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext('Your subscription status is valid.') - }; - subs.setUserCls(''); - } - - subs.setData(data); - }); - - me.on('destroy', function(){ - rstore.stopUpdate(); - }); - - me.mon(sp, 'statechange', function(provider, key, value) { - if (key !== 'summarycolumns') { - return; - } - PVE.Utils.updateColumns(me); - }); - - rstore.startUpdate(); - } - -}); -Ext.define('PVE.window.ReplicaEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveReplicaEdit', - - subject: gettext('Replication Job'), - - - url: '/cluster/replication', - method: 'POST', - - initComponent: function() { - var me = this; - - var vmid = me.pveSelNode.data.vmid; - var nodename = me.pveSelNode.data.node; - - var items = []; - - items.push({ - xtype: (me.isCreate && !vmid)?'pveGuestIDSelector':'displayfield', - name: 'guest', - fieldLabel: 'CT/VM ID', - value: vmid || '' - }); - - items.push( - { - xtype: me.isCreate ? 'pveNodeSelector':'displayfield', - name: 'target', - disallowedNodes: [nodename], - allowBlank: false, - onlineValidator: true, - fieldLabel: gettext("Target") - }, - { - xtype: 'pveCalendarEvent', - fieldLabel: gettext('Schedule'), - emptyText: '*/15 - ' + Ext.String.format(gettext('Every {0} minutes'), 15), - name: 'schedule' - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Rate limit') + ' (MB/s)', - step: 1, - minValue: 1, - emptyText: gettext('unlimited'), - name: 'rate' - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment' - }, - { - xtype: 'proxmoxcheckbox', - name: 'enabled', - defaultValue: 'on', - checked: true, - fieldLabel: gettext('Enabled') - } - ); - - me.items = [ - { - xtype: 'inputpanel', - itemId: 'ipanel', - onlineHelp: 'pvesr_schedule_time_format', - - onGetValues: function(values) { - var me = this.up('window'); - - values.disable = values.enabled ? 0 : 1; - delete values.enabled; - - PVE.Utils.delete_if_default(values, 'rate', '', me.isCreate); - PVE.Utils.delete_if_default(values, 'disable', 0, me.isCreate); - PVE.Utils.delete_if_default(values, 'schedule', '*/15', me.isCreate); - PVE.Utils.delete_if_default(values, 'comment', '', me.isCreate); - - if (me.isCreate) { - values.type = 'local'; - var vm = vmid || values.guest; - var id = -1; - if (me.highestids[vm] !== undefined) { - id = me.highestids[vm]; - } - id++; - values.id = vm + '-' + id.toString(); - delete values.guest; - } - return values; - }, - items: items - } - ]; - - me.callParent(); - - if (me.isCreate) { - me.load({ - success: function(response) { - var jobs = response.result.data; - var highestids = {}; - Ext.Array.forEach(jobs, function(job) { - var match = /^([0-9]+)\-([0-9]+)$/.exec(job.id); - if (match) { - var vmid = parseInt(match[1],10); - var id = parseInt(match[2],10); - if (highestids[vmid] < id || - highestids[vmid] === undefined) { - highestids[vmid] = id; - } - } - }); - - me.highestids = highestids; - } - }); - - } else { - me.load({ - success: function(response, options) { - response.result.data.enabled = !response.result.data.disable; - me.setValues(response.result.data); - me.digest = response.result.data.digest; - } - }); - } - } -}); - -/*jslint confusion: true */ -/* callback is a function and string */ -Ext.define('PVE.grid.ReplicaView', { - extend: 'Ext.grid.Panel', - xtype: 'pveReplicaView', - - onlineHelp: 'chapter_pvesr', - - stateful: true, - stateId: 'grid-pve-replication-status', - - controller: { - xclass: 'Ext.app.ViewController', - - addJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var win = Ext.create('PVE.window.ReplicaEdit', { - isCreate: true, - method: 'POST', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - editJob: function(button,event,rec) { - var me = this.getView(); - var controller = this; - var data = rec.data; - var win = Ext.create('PVE.window.ReplicaEdit', { - url: '/cluster/replication/' + data.id, - method: 'PUT', - pveSelNode: me.pveSelNode - }); - win.on('destroy', function() { controller.reload(); }); - win.show(); - }, - - scheduleJobNow: function(button,event,rec) { - var me = this.getView(); - var controller = this; - - Proxmox.Utils.API2Request({ - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/schedule_now", - method: 'POST', - waitMsgTarget: me, - callback: function() { controller.reload(); }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - showLog: function(button, event, rec) { - var me = this.getView(); - var controller = this; - var logView = Ext.create('Proxmox.panel.LogView', { - border: false, - url: "/api2/extjs/nodes/" + me.nodename + "/replication/" + rec.data.id + "/log" - }); - var win = Ext.create('Ext.window.Window', { - items: [ logView ], - layout: 'fit', - width: 800, - height: 400, - modal: true, - title: gettext("Replication Log") - }); - var task = { - run: function() { - logView.requestUpdate(); - }, - interval: 1000 - }; - Ext.TaskManager.start(task); - win.on('destroy', function() { - Ext.TaskManager.stop(task); - controller.reload(); - }); - win.show(); - }, - - reload: function() { - var me = this.getView(); - me.rstore.load(); - }, - - dblClick: function(grid, record, item) { - var me = this; - me.editJob(undefined, undefined, record); - }, - - // check for cluster - // currently replication is for cluster only, so we disable the whole - // component - checkPrerequisites: function() { - var me = this.getView(); - if (PVE.data.ResourceStore.getNodes().length < 2) { - me.mask(gettext("Replication needs at least two nodes"), ['pve-static-mask']); - } - }, - - control: { - '#': { - itemdblclick: 'dblClick', - afterlayout: 'checkPrerequisites' - } - } - }, - - tbar: [ - { - text: gettext('Add'), - itemId: 'addButton', - handler: 'addJob' - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - itemId: 'editButton', - handler: 'editJob', - disabled: true - }, - { - xtype: 'proxmoxStdRemoveButton', - itemId: 'removeButton', - baseurl: '/api2/extjs/cluster/replication/', - dangerous: true, - callback: 'reload' - }, - { - xtype: 'proxmoxButton', - text: gettext('Log'), - itemId: 'logButton', - handler: 'showLog', - disabled: true - }, - { - xtype: 'proxmoxButton', - text: gettext('Schedule now'), - itemId: 'scheduleNowButton', - handler: 'scheduleJobNow', - disabled: true - } - ], - - initComponent: function() { - var me = this; - var mode = ''; - var url = '/cluster/replication'; - - me.nodename = me.pveSelNode.data.node; - me.vmid = me.pveSelNode.data.vmid; - - me.columns = [ - { - text: gettext('Enabled'), - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true - }, - { - text: 'ID', - dataIndex: 'id', - width: 60, - hidden: true - }, - { - text: gettext('Guest'), - dataIndex: 'guest', - width: 75 - }, - { - text: gettext('Job'), - dataIndex: 'jobnum', - width: 60 - }, - { - text: gettext('Target'), - dataIndex: 'target' - } - ]; - - if (!me.nodename) { - mode = 'dc'; - me.stateId = 'grid-pve-replication-dc'; - } else if (!me.vmid) { - mode = 'node'; - url = '/nodes/' + me.nodename + '/replication'; - } else { - mode = 'vm'; - url = '/nodes/' + me.nodename + '/replication' + '?guest=' + me.vmid; - } - - if (mode !== 'dc') { - me.columns.push( - { - text: gettext('Status'), - dataIndex: 'state', - minWidth: 160, - flex: 1, - renderer: function(value, metadata, record) { - - if (record.data.pid) { - metadata.tdCls = 'x-grid-row-loading'; - return ''; - } - - var icons = []; - var states = []; - - if (record.data.remove_job) { - icons.push(''); - states.push(gettext("Removal Scheduled")); - } - - if (record.data.error) { - icons.push(''); - states.push(record.data.error); - } - - if (icons.length == 0) { - icons.push(''); - states.push(gettext('OK')); - } - - return icons.join(',') + ' ' + states.join(','); - } - }, - { - text: gettext('Last Sync'), - dataIndex: 'last_sync', - width: 150, - renderer: function(value, metadata, record) { - if (!value) { - return '-'; - } - - if (record.data.pid) { - return gettext('syncing'); - } - - return Proxmox.Utils.render_timestamp(value); - } - }, - { - text: gettext('Duration'), - dataIndex: 'duration', - width: 60, - renderer: PVE.Utils.render_duration - }, - { - text: gettext('Next Sync'), - dataIndex: 'next_sync', - width: 150, - renderer: function(value) { - if (!value) { - return '-'; - } - - var now = new Date(); - var next = new Date(value*1000); - - if (next < now) { - return gettext('pending'); - } - - return Proxmox.Utils.render_timestamp(value); - } - } - ); - } - - me.columns.push( - { - text: gettext('Schedule'), - width: 75, - dataIndex: 'schedule' - }, - { - text: gettext('Rate limit'), - dataIndex: 'rate', - renderer: function(value) { - if (!value) { - return gettext('unlimited'); - } - - return value.toString() + ' MB/s'; - }, - hidden: true - }, - { - text: gettext('Comment'), - dataIndex: 'comment', - renderer: Ext.htmlEncode - } - ); - - me.rstore = Ext.create('Proxmox.data.UpdateStore', { - storeid: 'pve-replica-' + me.nodename + me.vmid, - model: (mode === 'dc')? 'pve-replication' : 'pve-replication-state', - interval: 3000, - proxy: { - type: 'proxmox', - url: "/api2/json" + url - } - }); - - me.store = Ext.create('Proxmox.data.DiffStore', { - rstore: me.rstore, - sorters: [ - { - property: 'guest' - }, - { - property: 'jobnum' - } - ] - }); - - me.callParent(); - - // we cannot access the log and scheduleNow button - // in the datacenter, because - // we do not know where/if the jobs runs - if (mode === 'dc') { - me.down('#logButton').setHidden(true); - me.down('#scheduleNowButton').setHidden(true); - } - - // if we set the warning mask, we do not want to load - // or set the mask on store errors - if (PVE.data.ResourceStore.getNodes().length < 2) { - return; - } - - Proxmox.Utils.monStoreErrors(me, me.rstore); - - me.on('destroy', me.rstore.stopUpdate); - me.rstore.startUpdate(); - } -}, function() { - - Ext.define('pve-replication', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'target', 'comment', 'rate', 'type', - { name: 'guest', type: 'integer' }, - { name: 'jobnum', type: 'integer' }, - { name: 'schedule', defaultValue: '*/15' }, - { name: 'disable', defaultValue: '' }, - { name: 'enabled', calculate: function(data) { return !data.disable; } } - ] - }); - - Ext.define('pve-replication-state', { - extend: 'pve-replication', - fields: [ - 'last_sync', 'next_sync', 'error', 'duration', 'state', - 'fail_count', 'remove_job', 'pid' - ] - }); - -}); -Ext.define('PVE.dc.Health', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcHealth', - - title: gettext('Health'), - - bodyPadding: 10, - height: 220, - layout: { - type: 'hbox', - align: 'stretch' - }, - - defaults: { - flex: 1, - xtype: 'box', - style: { - 'text-align':'center' - } - }, - - nodeList: [], - nodeIndex: 0, - - updateStatus: function(store, records, success) { - var me = this; - if (!success) { - return; - } - - var cluster = { - iconCls: PVE.Utils.get_health_icon('good', true), - text: gettext("Standalone node - no cluster defined") - }; - - var nodes = { - online: 0, - offline: 0 - }; - - // by default we have one node - var numNodes = 1; - var i; - - for (i = 0; i < records.length; i++) { - var item = records[i]; - if (item.data.type === 'node') { - nodes[item.data.online === 1 ? 'online':'offline']++; - } else if(item.data.type === 'cluster') { - cluster.text = gettext("Cluster") + ": "; - cluster.text += item.data.name + ", "; - cluster.text += gettext("Quorate") + ": "; - cluster.text += Proxmox.Utils.format_boolean(item.data.quorate); - if (item.data.quorate != 1) { - cluster.iconCls = PVE.Utils.get_health_icon('critical', true); - } - - numNodes = item.data.nodes; - } - } - - if (numNodes !== (nodes.online + nodes.offline)) { - nodes.offline = numNodes - nodes.online; - } - - me.getComponent('clusterstatus').updateHealth(cluster); - me.getComponent('nodestatus').update(nodes); - }, - - updateCeph: function(store, records, success) { - var me = this; - var cephstatus = me.getComponent('ceph'); - if (!success || records.length < 1) { - - // if ceph status is already visible - // don't stop to update - if (cephstatus.isVisible()) { - return; - } - - // try all nodes until we either get a successful api call, - // or we tried all nodes - if (++me.nodeIndex >= me.nodeList.length) { - me.cephstore.stopUpdate(); - } else { - store.getProxy().setUrl('/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status'); - } - - return; - } - - var state = PVE.Utils.render_ceph_health(records[0].data.health || {}); - cephstatus.updateHealth(state); - cephstatus.setVisible(true); - }, - - listeners: { - destroy: function() { - var me = this; - me.cephstore.stopUpdate(); - } - }, - - items: [ - { - itemId: 'clusterstatus', - xtype: 'pveHealthWidget', - title: gettext('Status') - }, - { - itemId: 'nodestatus', - data: { - online: 0, - offline: 0 - }, - tpl: [ - '

' + gettext('Nodes') + '


', - '
', - '
', - ' ', - gettext('Online'), - '
', - '
{online}
', - '

', - '
', - ' ', - gettext('Offline'), - '
', - '
{offline}
', - '
' - ] - }, - { - itemId: 'ceph', - width: 250, - columnWidth: undefined, - userCls: 'pointer', - title: 'Ceph', - xtype: 'pveHealthWidget', - hidden: true, - listeners: { - element: 'el', - click: function() { - var sp = Ext.state.Manager.getProvider(); - sp.set('dctab', {value:'ceph'}, true); - } - } - } - ], - - initComponent: function() { - var me = this; - - me.nodeList = PVE.data.ResourceStore.getNodes(); - me.nodeIndex = 0; - me.cephstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'pve-cluster-ceph', - proxy: { - type: 'proxmox', - url: '/api2/json/nodes/' + me.nodeList[me.nodeIndex].node + '/ceph/status' - } - }); - me.callParent(); - me.mon(me.cephstore, 'load', me.updateCeph, me); - me.cephstore.startUpdate(); - } -}); -Ext.define('PVE.dc.Guests', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcGuests', - - - title: gettext('Guests'), - height: 220, - layout: { - type: 'table', - columns: 2, - tableAttrs: { - style: { - width: '100%' - } - } - }, - bodyPadding: '0 20 20 20', - - defaults: { - xtype: 'box', - padding: '0 50 0 50', - style: { - 'text-align':'center', - 'line-height':'1.2' - } - }, - items: [{ - itemId: 'qemu', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("Virtual Machines") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'lxc', - data: { - running: 0, - paused: 0, - stopped: 0, - template: 0 - }, - tpl: [ - '

' + gettext("LXC Container") + '

', - '
', - ' ', - gettext('Running'), - '
', - '
{running}
' + '
', - '', - '
', - ' ', - gettext('Paused'), - '
', - '
{paused}
' + '
', - '
', - '
', - ' ', - gettext('Stopped'), - '
', - '
{stopped}
' + '
', - '', - '
', - ' ', - gettext('Templates'), - '
', - '
{template}
', - '
' - ] - },{ - itemId: 'error', - colspan: 2, - data: { - num: 0 - }, - columnWidth: 1, - padding: '10 250 0 250', - tpl: [ - '', - '
', - ' ', - gettext('Error'), - '
', - '
{num}
', - '
' - ] - }], - - updateValues: function(qemu, lxc, error) { - var me = this; - me.getComponent('qemu').update(qemu); - me.getComponent('lxc').update(lxc); - me.getComponent('error').update({num: error}); - } -}); - /*jslint confusion: true*/ -Ext.define('PVE.dc.OptionView', { - extend: 'Proxmox.grid.ObjectGrid', - alias: ['widget.pveDcOptionView'], - - onlineHelp: 'datacenter_configuration_file', - - monStoreErrors: true, - - add_inputpanel_row: function(name, text, opts) { - var me = this; - - opts = opts || {}; - me.rows = me.rows || {}; - - var canEdit = (opts.caps === undefined || opts.caps); - me.rows[name] = { - required: true, - defaultValue: opts.defaultValue, - header: text, - renderer: opts.renderer, - editor: canEdit ? { - xtype: 'proxmoxWindowEdit', - width: opts.width || 350, - subject: text, - onlineHelp: opts.onlineHelp, - fieldDefaults: { - labelWidth: opts.labelWidth || 100 - }, - setValues: function(values) { - var edit_value = values[name]; - - if (opts.parseBeforeSet) { - edit_value = PVE.Parser.parsePropertyString(edit_value); - } - - Ext.Array.each(this.query('inputpanel'), function(panel) { - panel.setValues(edit_value); - }); - }, - url: opts.url, - items: [{ - xtype: 'inputpanel', - onGetValues: function(values) { - if (values === undefined || Object.keys(values).length === 0) { - return { 'delete': name }; - } - var ret_val = {}; - ret_val[name] = PVE.Parser.printPropertyString(values); - return ret_val; - }, - items: opts.items - }] - } : undefined - }; - }, - - render_bwlimits: function(value) { - if (!value) { - return gettext("None"); - } - - let parsed = PVE.Parser.parsePropertyString(value); - return Object.entries(parsed) - .map(([k, v]) => k + ": " + Proxmox.Utils.format_size(v * 1024) + "/s") - .join(','); - }, - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.add_combobox_row('keyboard', gettext('Keyboard Layout'), { - renderer: PVE.Utils.render_kvm_language, - comboItems: PVE.Utils.kvm_keymap_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('http_proxy', gettext('HTTP proxy'), { - defaultValue: Proxmox.Utils.noneText, - vtype: 'HttpProxy', - deleteEmpty: true - }); - me.add_combobox_row('console', gettext('Console Viewer'), { - renderer: PVE.Utils.render_console_viewer, - comboItems: PVE.Utils.console_viewer_array(), - defaultValue: '__default__', - deleteEmpty: true - }); - me.add_text_row('email_from', gettext('Email from address'), { - deleteEmpty: true, - vtype: 'proxmoxMail', - defaultValue: 'root@$hostname' - }); - me.add_text_row('mac_prefix', gettext('MAC address prefix'), { - deleteEmpty: true, - vtype: 'MacPrefix', - defaultValue: Proxmox.Utils.noneText - }); - me.add_inputpanel_row('migration', gettext('Migration Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.vms['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - defaultKey: 'type', - items: [{ - xtype: 'displayfield', - name: 'type', - fieldLabel: gettext('Type'), - value: 'secure', - submitValue: true, - }, { - xtype: 'proxmoxNetworkSelector', - name: 'network', - fieldLabel: gettext('Network'), - value: null, - emptyText: Proxmox.Utils.defaultText, - autoSelect: false, - skipEmptyText: true - }] - }); - me.add_inputpanel_row('ha', gettext('HA Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.dc['Sys.Modify'], - labelWidth: 120, - url: "/api2/extjs/cluster/options", - onlineHelp: 'ha_manager_shutdown_policy', - items: [{ - xtype: 'proxmoxKVComboBox', - name: 'shutdown_policy', - fieldLabel: gettext('Shutdown Policy'), - deleteEmpty: false, - value: '__default__', - comboItems: [ - ['__default__', Proxmox.Utils.defaultText + ' (conditional)' ], - ['freeze', 'freeze'], - ['failover', 'failover'], - ['migrate', 'migrate'], - ['conditional', 'conditional'] - ], - defaultValue: '__default__' - }] - }); - me.add_inputpanel_row('u2f', gettext('U2F Settings'), { - renderer: PVE.Utils.render_dc_ha_opts, - caps: caps.dc['Sys.Modify'], - width: 450, - url: "/api2/extjs/cluster/options", - onlineHelp: 'pveum_configure_u2f', - items: [{ - xtype: 'textfield', - name: 'appid', - fieldLabel: gettext('U2F AppID URL'), - emptyText: gettext('Defaults to origin'), - value: '', - skipEmptyText: true, - deleteEmpty: true, - submitEmptyText: false, - skipEmptyText: true, - }, { - xtype: 'textfield', - name: 'origin', - fieldLabel: gettext('U2F Origin'), - emptyText: gettext('Defaults to requesting host URI'), - value: '', - deleteEmpty: true, - skipEmptyText: true, - submitEmptyText: false, - }, - { - xtype: 'displayfield', - userCls: 'pmx-hint', - value: gettext('NOTE: Changing an AppID breaks existing U2F registrations!'), - }] - }); - me.add_inputpanel_row('bwlimit', gettext('Bandwidth Limits'), { - renderer: me.render_bwlimits, - caps: caps.dc['Sys.Modify'], - width: 450, - url: "/api2/extjs/cluster/options", - parseBeforeSet: true, - labelWidth: 120, - items: [{ - xtype: 'pveBandwidthField', - name: 'default', - fieldLabel: gettext('Default'), - emptyText: gettext('none'), - backendUnit: "KiB", - }, - { - xtype: 'pveBandwidthField', - name: 'restore', - fieldLabel: gettext('Backup Restore'), - emptyText: gettext('default'), - backendUnit: "KiB", - }, - { - xtype: 'pveBandwidthField', - name: 'migration', - fieldLabel: gettext('Migration'), - emptyText: gettext('default'), - backendUnit: "KiB", - }, - { - xtype: 'pveBandwidthField', - name: 'clone', - fieldLabel: gettext('Clone'), - emptyText: gettext('default'), - backendUnit: "KiB", - }, - { - xtype: 'pveBandwidthField', - name: 'move', - fieldLabel: gettext('Disk Move'), - emptyText: gettext('default'), - backendUnit: "KiB", - }] - }); - me.add_integer_row('max_workers', gettext('Maximal Workers/bulk-action'), { - deleteEmpty: true, - defaultValue: 4, - minValue: 1, - maxValue: 64, // arbitrary but generous limit as limits are good - }); - - me.selModel = Ext.create('Ext.selection.RowModel', {}); - - Ext.apply(me, { - tbar: [{ - text: gettext('Edit'), - xtype: 'proxmoxButton', - disabled: true, - handler: function() { me.run_editor(); }, - selModel: me.selModel - }], - url: "/api2/json/cluster/options", - editorConfig: { - url: "/api2/extjs/cluster/options" - }, - interval: 5000, - cwidth1: 200, - listeners: { - itemdblclick: me.run_editor - } - }); - - me.callParent(); - - // set the new value for the default console - me.mon(me.rstore, 'load', function(store, records, success) { - if (!success) { - return; - } - - var rec = store.getById('console'); - PVE.VersionInfo.console = rec.data.value; - if (rec.data.value === '__default__') { - delete PVE.VersionInfo.console; - } - }); - - me.on('activate', me.rstore.startUpdate); - me.on('destroy', me.rstore.stopUpdate); - me.on('deactivate', me.rstore.stopUpdate); - } -}); -Ext.define('PVE.dc.StorageView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveStorageView'], - - onlineHelp: 'chapter_storage', - - stateful: true, - stateId: 'grid-dc-storage', - - createStorageEditWindow: function(type, sid) { - var schema = PVE.Utils.storageSchema[type]; - if (!schema || !schema.ipanel) { - throw "no editor registered for storage type: " + type; - } - - Ext.create('PVE.storage.BaseEdit', { - paneltype: 'PVE.storage.' + schema.ipanel, - type: type, - storageId: sid, - autoShow: true, - listeners: { - destroy: this.reloadStore - } - }); - }, - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-storage', - proxy: { - type: 'proxmox', - url: "/api2/json/storage" - }, - sorters: { - property: 'storage', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var type = rec.data.type, - sid = rec.data.storage; - - me.createStorageEditWindow(type, sid); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/storage/', - callback: reload - }); - - // else we cannot dynamically generate the add menu handlers - var addHandleGenerator = function(type) { - return function() { me.createStorageEditWindow(type); }; - }; - var addMenuItems = [], type; - /*jslint forin: true */ - for (type in PVE.Utils.storageSchema) { - var storage = PVE.Utils.storageSchema[type]; - if (storage.hideAdd) { - continue; - } - addMenuItems.push({ - text: PVE.Utils.format_storage_type(type), - iconCls: 'fa fa-fw fa-' + storage.faIcon, - handler: addHandleGenerator(type) - }); - } - - Ext.apply(me, { - store: store, - reloadStore: reload, - selModel: sm, - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: addMenuItems - }) - }, - remove_btn, - edit_btn - ], - columns: [ - { - header: 'ID', - flex: 2, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Type'), - flex: 1, - sortable: true, - dataIndex: 'type', - renderer: PVE.Utils.format_storage_type - }, - { - header: gettext('Content'), - flex: 3, - sortable: true, - dataIndex: 'content', - renderer: PVE.Utils.format_content_types - }, - { - header: gettext('Path') + '/' + gettext('Target'), - flex: 2, - sortable: true, - dataIndex: 'path', - renderer: function(value, metaData, record) { - if (record.data.target) { - return record.data.target; - } - return value; - } - }, - { - header: gettext('Shared'), - flex: 1, - sortable: true, - dataIndex: 'shared', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Enabled'), - flex: 1, - sortable: true, - dataIndex: 'disable', - renderer: Proxmox.Utils.format_neg_boolean - }, - { - header: gettext('Bandwidth Limit'), - flex: 2, - sortable: true, - dataIndex: 'bwlimit' - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-storage', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'content', 'server', 'portal', 'target', 'export', 'storage', - { name: 'shared', type: 'boolean'}, - { name: 'disable', type: 'boolean'} - ], - idProperty: 'storage' - }); - -}); -/*global u2f,QRCode,Uint8Array*/ -/*jslint confusion: true*/ -Ext.define('PVE.window.TFAEdit', { - extend: 'Ext.window.Window', - mixins: ['Proxmox.Mixin.CBind'], - - onlineHelp: 'pveum_tfa_auth', // fake to ensure this gets a link target - - modal: true, - resizable: false, - title: gettext('Two Factor Authentication'), - subject: 'TFA', - url: '/api2/extjs/access/tfa', - width: 512, - - layout: { - type: 'vbox', - align: 'stretch' - }, - - updateQrCode: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var algorithm = values.algorithm; - if (!algorithm) { - algorithm = 'SHA1'; - } - - me.qrcode.makeCode( - 'otpauth://totp/' + encodeURIComponent(me.userid) + - '?secret=' + values.secret + - '&period=' + values.step + - '&digits=' + values.digits + - '&algorithm=' + algorithm + - '&issuer=' + encodeURIComponent(values.issuer) - ); - - me.lookup('challenge').setVisible(true); - me.down('#qrbox').setVisible(true); - }, - - showError: function(error) { - Ext.Msg.alert( - gettext('Error'), - PVE.Utils.render_u2f_error(error) - ); - }, - - doU2FChallenge: function(response) { - var me = this; - - var data = response.result.data; - me.lookup('password').setDisabled(true); - var msg = Ext.Msg.show({ - title: 'U2F: '+gettext('Setup'), - message: gettext('Please press the button on your U2F Device'), - buttons: [] - }); - Ext.Function.defer(function() { - u2f.register(data.appId, [data], [], function(data) { - msg.close(); - if (data.errorCode) { - me.showError(data.errorCode); - } else { - me.respondToU2FChallenge(data); - } - }); - }, 500, me); - }, - - respondToU2FChallenge: function(data) { - var me = this; - var params = { - userid: me.userid, - action: 'confirm', - response: JSON.stringify(data) - }; - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - success: function() { - me.close(); - Ext.Msg.show({ - title: gettext('Success'), - message: gettext('U2F Device successfully connected.'), - buttons: Ext.Msg.OK - }); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - viewModel: { - data: { - in_totp_tab: true, - tfa_required: false, - tfa_type: null, // dependencies of formulas should not be undefined - valid: false, - u2f_available: true, - secret: "", - }, - formulas: { - showTOTPVerifiction: function(get) { - return get('secret').length > 0 && get('canSetupTOTP'); - }, - canDeleteTFA: function(get) { - return (get('tfa_type') !== null && !get('tfa_required')); - }, - canSetupTOTP: function(get) { - var tfa = get('tfa_type'); - return (tfa === null || tfa === 'totp' || tfa === 1); - }, - canSetupU2F: function(get) { - var tfa = get('tfa_type'); - return (get('u2f_available') && (tfa === null || tfa === 'u2f' || tfa === 1)); - }, - secretEmpty: function(get) { - return get('secret').length === 0; - }, - selectedTab: function(get) { - return (get('tfa_type') || 'totp') + '-panel'; - }, - } - }, - - afterLoading: function(realm_tfa_type, user_tfa_type) { - var me = this; - var viewmodel = me.getViewModel(); - if (user_tfa_type === 'oath') { - user_tfa_type = 'totp'; - viewmodel.set('secret', ''); - } - - // if the user has no tfa, generate a secret for him - if (!user_tfa_type) { - me.getController().randomizeSecret(); - } - - viewmodel.set('tfa_type', user_tfa_type || null); - if (!realm_tfa_type) { - // There's no TFA enforced by the realm, everything works. - viewmodel.set('u2f_available', true); - viewmodel.set('tfa_required', false); - } else if (realm_tfa_type === 'oath') { - // The realm explicitly requires TOTP - if (user_tfa_type !== 'totp' && user_tfa_type !== null) { - // user had a different tfa method, so - // we have to change back to the totp tab and - // generate a secret - viewmodel.set('tfa_type', 'totp'); - me.getController().randomizeSecret(); - } - viewmodel.set('tfa_required', true); - viewmodel.set('u2f_available', false); - } else { - // The realm enforces some other TFA type (yubico) - me.close(); - Ext.Msg.alert( - gettext('Error'), - Ext.String.format( - gettext("Custom 2nd factor configuration is not supported on realms with '{0}' TFA."), - realm_tfa_type - ) - ); - } - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - 'field[qrupdate=true]': { - change: function() { - var me = this.getView(); - me.updateQrCode(); - } - }, - 'field': { - validitychange: function(field, valid) { - var me = this; - var viewModel = me.getViewModel(); - var form = me.lookup('totp_form'); - var challenge = me.lookup('challenge'); - var password = me.lookup('password'); - viewModel.set('valid', form.isValid() && challenge.isValid() && password.isValid()); - } - }, - '#': { - show: function() { - var me = this.getView(); - var viewmodel = this.getViewModel(); - - var loadMaskContainer = me.down('#tfatabs'); - Proxmox.Utils.API2Request({ - url: '/access/users/' + encodeURIComponent(me.userid) + '/tfa', - waitMsgTarget: loadMaskContainer, - method: 'GET', - success: function(response, opts) { - var data = response.result.data; - me.afterLoading(data.realm, data.user); - }, - failure: function(response, opts) { - me.close(); - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - - me.qrdiv = document.createElement('center'); - me.qrcode = new QRCode(me.qrdiv, { - width: 256, - height: 256, - correctLevel: QRCode.CorrectLevel.M - }); - me.down('#qrbox').getEl().appendChild(me.qrdiv); - - if (Proxmox.UserName === 'root@pam') { - me.lookup('password').setVisible(false); - me.lookup('password').setDisabled(true); - } - } - }, - '#tfatabs': { - tabchange: function(panel, newcard) { - var viewmodel = this.getViewModel(); - viewmodel.set('in_totp_tab', newcard.itemId === 'totp-panel'); - } - } - }, - - applySettings: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'new', - key: 'v2-' + values.secret, - config: PVE.Parser.printPropertyString({ - type: 'oath', - digits: values.digits, - step: values.step - }), - // this is used to verify that the client generates the correct codes: - response: me.lookup('challenge').value - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - deleteTFA: function() { - var me = this; - var values = me.lookup('totp_form').getValues(); - var params = { - userid: me.getView().userid, - action: 'delete' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response, opts) { - me.getView().close(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }, - - randomizeSecret: function() { - var me = this; - var rnd = new Uint8Array(32); - window.crypto.getRandomValues(rnd); - var data = ''; - rnd.forEach(function(b) { - // secret must be base32, so just use the first 5 bits - b = b & 0x1f; - if (b < 26) { - // A..Z - data += String.fromCharCode(b + 0x41); - } else { - // 2..7 - data += String.fromCharCode(b-26 + 0x32); - } - }); - me.getViewModel().set('secret', data); - }, - - startU2FRegistration: function() { - var me = this; - - var params = { - userid: me.getView().userid, - action: 'new' - }; - - if (Proxmox.UserName !== 'root@pam') { - params.password = me.lookup('password').value; - } - - Proxmox.Utils.API2Request({ - url: '/api2/extjs/access/tfa', - params: params, - method: 'PUT', - waitMsgTarget: me.getView(), - success: function(response) { - me.getView().doU2FChallenge(response); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }, - - items: [ - { - xtype: 'tabpanel', - itemId: 'tfatabs', - reference: 'tfatabs', - border: false, - bind: { - activeTab: '{selectedTab}', - }, - items: [ - { - xtype: 'panel', - title: 'TOTP', - itemId: 'totp-panel', - reference: 'totp_panel', - tfa_type: 'totp', - border: false, - bind: { - disabled: '{!canSetupTOTP}' - }, - layout: { - type: 'vbox', - align: 'stretch' - }, - items: [ - { - xtype: 'form', - layout: 'anchor', - border: false, - reference: 'totp_form', - fieldDefaults: { - anchor: '100%', - padding: '0 5' - }, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('User name'), - cbind: { - value: '{userid}' - } - }, - { - layout: 'hbox', - border: false, - padding: '0 0 5 0', - items: [{ - xtype: 'textfield', - fieldLabel: gettext('Secret'), - emptyText: gettext('Unchanged'), - name: 'secret', - reference: 'tfa_secret', - regex: /^[A-Z2-7=]+$/, - regexText: 'Must be base32 [A-Z2-7=]', - maskRe: /[A-Z2-7=]/, - qrupdate: true, - bind: { - value: "{secret}", - }, - flex: 4 - }, - { - xtype: 'button', - text: gettext('Randomize'), - reference: 'randomize_button', - handler: 'randomizeSecret', - flex: 1 - }] - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Time period'), - name: 'step', - // Google Authenticator ignores this and generates bogus data - hidden: true, - value: 30, - minValue: 10, - qrupdate: true - }, - { - xtype: 'numberfield', - fieldLabel: gettext('Digits'), - name: 'digits', - value: 6, - // Google Authenticator ignores this and generates bogus data - hidden: true, - minValue: 6, - maxValue: 8, - qrupdate: true - }, - { - xtype: 'textfield', - fieldLabel: gettext('Issuer Name'), - name: 'issuer', - value: 'Proxmox Web UI', - qrupdate: true - } - ] - }, - { - xtype: 'box', - itemId: 'qrbox', - visible: false, // will be enabled when generating a qr code - bind: { - visible: '{!secretEmpty}', - }, - style: { - 'background-color': 'white', - padding: '5px', - width: '266px', - height: '266px' - } - }, - { - xtype: 'textfield', - fieldLabel: gettext('Verification Code'), - allowBlank: false, - reference: 'challenge', - bind: { - disabled: '{!showTOTPVerifiction}', - visible: '{showTOTPVerifiction}', - }, - padding: '0 5', - emptyText: gettext('Scan QR code and enter TOTP auth. code to verify') - } - ] - }, - { - title: 'U2F', - itemId: 'u2f-panel', - reference: 'u2f_panel', - tfa_type: 'u2f', - border: false, - padding: '5 5', - layout: { - type: 'vbox', - align: 'middle' - }, - bind: { - disabled: '{!canSetupU2F}' - }, - items: [ - { - xtype: 'label', - width: 500, - text: gettext('To register a U2F device, connect the device, then click the button and follow the instructions.') - } - ] - } - ] - }, - { - xtype: 'textfield', - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - reference: 'password', - allowBlank: false, - validateBlank: true, - padding: '0 0 5 5', - emptyText: gettext('verify current password') - } - ], - - buttons: [ - { - xtype: 'proxmoxHelpButton' - }, - '->', - { - text: gettext('Apply'), - handler: 'applySettings', - bind: { - hidden: '{!in_totp_tab}', - disabled: '{!valid}' - } - }, - { - xtype: 'button', - text: gettext('Register U2F Device'), - handler: 'startU2FRegistration', - bind: { - hidden: '{in_totp_tab}', - disabled: '{tfa_type}' - } - }, - { - text: gettext('Delete'), - reference: 'delete_button', - disabled: true, - handler: 'deleteTFA', - bind: { - disabled: '{!canDeleteTFA}' - } - } - ], - - initComponent: function() { - var me = this; - - if (!me.userid) { - throw "no userid given"; - } - - me.callParent(); - - Ext.GlobalEvents.fireEvent('proxmoxShowHelp', 'pveum_tfa_auth'); - } -}); -Ext.define('PVE.dc.UserEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcUserEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.userid; - - var url; - var method; - var realm; - - if (me.isCreate) { - url = '/api2/extjs/access/users'; - method = 'POST'; - } else { - url = '/api2/extjs/access/users/' + encodeURIComponent(me.userid); - method = 'PUT'; - } - - var verifypw; - var pwfield; - - var validate_pw = function() { - if (verifypw.getValue() !== pwfield.getValue()) { - return gettext("Passwords do not match"); - } - return true; - }; - - verifypw = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Confirm password'), - name: 'verifypassword', - submitValue: false, - disabled: true, - hidden: true, - validator: validate_pw - }); - - pwfield = Ext.createWidget('textfield', { - inputType: 'password', - fieldLabel: gettext('Password'), - minLength: 5, - name: 'password', - disabled: true, - hidden: true, - validator: validate_pw - }); - - var update_passwd_field = function(realm) { - if (realm === 'pve') { - pwfield.setVisible(true); - pwfield.setDisabled(false); - verifypw.setVisible(true); - verifypw.setDisabled(false); - } else { - pwfield.setVisible(false); - pwfield.setDisabled(true); - verifypw.setVisible(false); - verifypw.setDisabled(true); - } - - }; - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'userid', - fieldLabel: gettext('User name'), - value: me.userid, - allowBlank: false, - submitValue: me.isCreate ? true : false - }, - pwfield, verifypw, - { - xtype: 'pveGroupSelector', - name: 'groups', - multiSelect: true, - allowBlank: true, - fieldLabel: gettext('Group') - }, - { - xtype: 'datefield', - name: 'expire', - emptyText: 'never', - format: 'Y-m-d', - submitFormat: 'U', - fieldLabel: gettext('Expire') - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enabled'), - name: 'enable', - uncheckedValue: 0, - defaultValue: 1, - checked: true - } - ]; - - var column2 = [ - { - xtype: 'textfield', - name: 'firstname', - fieldLabel: gettext('First Name') - }, - { - xtype: 'textfield', - name: 'lastname', - fieldLabel: gettext('Last Name') - }, - { - xtype: 'textfield', - name: 'email', - fieldLabel: gettext('E-Mail'), - vtype: 'proxmoxMail' - } - ]; - - if (me.isCreate) { - column1.splice(1,0,{ - xtype: 'pveRealmComboBox', - name: 'realm', - fieldLabel: gettext('Realm'), - allowBlank: false, - matchFieldWidth: false, - listConfig: { width: 300 }, - listeners: { - change: function(combo, newValue){ - realm = newValue; - update_passwd_field(realm); - } - }, - submitValue: false - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [ - { - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - } - ], - advancedItems: [ - { - xtype: 'textfield', - name: 'keys', - fieldLabel: gettext('Key IDs') - } - ], - onGetValues: function(values) { - // hack: ExtJS datefield does not submit 0, so we need to set that - if (!values.expire) { - values.expire = 0; - } - - if (realm) { - values.userid = values.userid + '@' + realm; - } - - if (!values.password) { - delete values.password; - } - - return values; - } - }); - - Ext.applyIf(me, { - subject: gettext('User'), - url: url, - method: method, - fieldDefaults: { - labelWidth: 110 // for spanish translation - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data; - if (Ext.isDefined(data.expire)) { - if (data.expire) { - data.expire = new Date(data.expire * 1000); - } else { - // display 'never' instead of '1970-01-01' - data.expire = null; - } - } - me.setValues(data); - if (data.keys) { - if ( data.keys === 'x!oath' || data.keys === 'x!u2f' ) { - me.down('[name="keys"]').setDisabled(1); - } - } - } - }); - } - } -}); -/*jslint confusion: true */ -Ext.define('PVE.dc.UserView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveUserView'], - - onlineHelp: 'pveum_users', - - stateful: true, - stateId: 'grid-users', - - initComponent : function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - var store = new Ext.data.Store({ - id: "users", - model: 'pve-users', - sorters: { - property: 'userid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/access/users/', - enableFn: function(rec) { - if (!caps.access['User.Modify']) { - return false; - } - return rec.data.userid !== 'root@pam'; - }, - callback: function() { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec || !caps.access['User.Modify']) { - return; - } - - var win = Ext.create('PVE.dc.UserEdit',{ - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - enableFn: function(rec) { - return !!caps.access['User.Modify']; - }, - selModel: sm, - handler: run_editor - }); - - var pwchange_btn = new Proxmox.button.Button({ - text: gettext('Password'), - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var win = Ext.create('Proxmox.window.PasswordEdit', { - userid: rec.data.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var tfachange_btn = new Proxmox.button.Button({ - text: 'TFA', - disabled: true, - selModel: sm, - handler: function(btn, event, rec) { - var d = rec.data; - var tfa_type = PVE.Parser.parseTfaType(d.keys); - var win = Ext.create('PVE.window.TFAEdit',{ - tfa_type: tfa_type, - userid: d.userid - }); - win.on('destroy', reload); - win.show(); - } - }); - - var perm_btn = new Proxmox.button.Button({ - text: gettext('Permissions'), - disabled: false, - selModel: sm, - handler: function(btn, event, rec) { - var win = Ext.create('PVE.dc.PermissionView', { - userid: rec.data.userid - }); - win.show(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - disabled: !caps.access['User.Modify'], - handler: function() { - var win = Ext.create('PVE.dc.UserEdit',{ - }); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn, pwchange_btn, tfachange_btn, perm_btn - ]; - - var render_username = function(userid) { - return userid.match(/^(.+)(@[^@]+)$/)[1]; - }; - - var render_realm = function(userid) { - return userid.match(/@([^@]+)$/)[1]; - }; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('User name'), - width: 200, - sortable: true, - renderer: render_username, - dataIndex: 'userid' - }, - { - header: gettext('Realm'), - width: 100, - sortable: true, - renderer: render_realm, - dataIndex: 'userid' - }, - { - header: gettext('Enabled'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_boolean, - dataIndex: 'enable' - }, - { - header: gettext('Expire'), - width: 80, - sortable: true, - renderer: Proxmox.Utils.format_expire, - dataIndex: 'expire' - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - renderer: PVE.Utils.render_full_name, - dataIndex: 'firstname' - }, - { - header: 'TFA', - width: 50, - sortable: true, - renderer: function(v) { - var tfa_type = PVE.Parser.parseTfaType(v); - if (tfa_type === undefined) { - return Proxmox.Utils.noText; - } else if (tfa_type === 1) { - return Proxmox.Utils.yesText; - } else { - return tfa_type; - } - }, - dataIndex: 'keys' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - - Proxmox.Utils.monStoreErrors(me, store); - } -}); -Ext.define('PVE.dc.PoolView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pvePoolView'], - - onlineHelp: 'pveum_pools', - - stateful: true, - stateId: 'grid-pools', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-pools', - sorters: { - property: 'poolid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/pools/', - callback: function () { - reload(); - } - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.PoolEdit',{ - poolid: rec.data.poolid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.PoolEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'poolid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.PoolEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcPoolEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.poolid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/pools'; - method = 'POST'; - } else { - url = '/api2/extjs/pools/' + me.poolid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Pool'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'poolid', - value: me.poolid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.GroupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveGroupView'], - - onlineHelp: 'pveum_groups', - - stateful: true, - stateId: 'grid-groups', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-groups', - sorters: { - property: 'groupid', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/groups/' - }); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.GroupEdit',{ - groupid: rec.data.groupid - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var tbar = [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.GroupEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - edit_btn, remove_btn - ]; - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Name'), - width: 200, - sortable: true, - dataIndex: 'groupid' - }, - { - header: gettext('Comment'), - sortable: false, - renderer: Ext.String.htmlEncode, - dataIndex: 'comment', - flex: 1 - }, - { - header: gettext('Users'), - sortable: false, - dataIndex: 'users', - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.GroupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcGroupEdit'], - - initComponent : function() { - var me = this; - - me.isCreate = !me.groupid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/groups'; - method = 'POST'; - } else { - url = '/api2/extjs/access/groups/' + me.groupid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Group'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - fieldLabel: gettext('Name'), - name: 'groupid', - value: me.groupid, - allowBlank: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Comment'), - name: 'comment', - allowBlank: true - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load(); - } - } -}); -Ext.define('PVE.dc.RoleView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveRoleView'], - - onlineHelp: 'pveum_roles', - - stateful: true, - stateId: 'grid-roles', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-roles', - sorters: { - property: 'roleid', - order: 'DESC' - } - }); - - var render_privs = function(value, metaData) { - - if (!value) { - return '-'; - } - - // allow word wrap - metaData.style = 'white-space:normal;'; - - return value.replace(/\,/g, ' '); - }; - - Proxmox.Utils.monStoreErrors(me, store); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - if (!!rec.data.special) { - return; - } - - var win = Ext.create('PVE.dc.RoleEdit',{ - roleid: rec.data.roleid, - privs: rec.data.privs - }); - win.on('destroy', reload); - win.show(); - }; - - Ext.apply(me, { - store: store, - selModel: sm, - - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Built-In'), - width: 65, - sortable: true, - dataIndex: 'special', - renderer: Proxmox.Utils.format_boolean - }, - { - header: gettext('Name'), - width: 150, - sortable: true, - dataIndex: 'roleid' - }, - { - itemid: 'privs', - header: gettext('Privileges'), - sortable: false, - renderer: render_privs, - dataIndex: 'privs', - flex: 1 - } - ], - listeners: { - activate: function() { - store.load(); - }, - itemdblclick: run_editor - }, - tbar: [ - { - text: gettext('Create'), - handler: function() { - var win = Ext.create('PVE.dc.RoleEdit', {}); - win.on('destroy', reload); - win.show(); - } - }, - { - xtype: 'proxmoxButton', - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor, - enableFn: (rec) => !rec.data.special, - }, - { - xtype: 'proxmoxStdRemoveButton', - selModel: sm, - callback: function() { - reload(); - }, - baseurl: '/access/roles/', - enableFn: (rec) => !rec.data.special, - } - ] - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.RoleEdit', { - extend: 'Proxmox.window.Edit', - xtype: 'pveDcRoleEdit', - - width: 400, - - initComponent : function() { - var me = this; - - me.isCreate = !me.roleid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/access/roles'; - method = 'POST'; - } else { - url = '/api2/extjs/access/roles/' + me.roleid; - method = 'PUT'; - } - - Ext.applyIf(me, { - subject: gettext('Role'), - url: url, - method: method, - items: [ - { - xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', - name: 'roleid', - value: me.roleid, - allowBlank: false, - fieldLabel: gettext('Name') - }, - { - xtype: 'pvePrivilegesSelector', - name: 'privs', - value: me.privs, - allowBlank: false, - fieldLabel: gettext('Privileges') - } - ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response) { - var data = response.result.data; - var keys = Ext.Object.getKeys(data); - - me.setValues({ - privs: keys, - roleid: me.roleid - }); - } - }); - } - } -}); -Ext.define('PVE.dc.ACLAdd', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveACLAdd'], - url: '/access/acl', - method: 'PUT', - isAdd: true, - initComponent : function() { - - var me = this; - - me.isCreate = true; - - var items = [ - { - xtype: me.path ? 'hiddenfield' : 'pvePermPathSelector', - name: 'path', - value: me.path, - allowBlank: false, - fieldLabel: gettext('Path') - } - ]; - - if (me.aclType === 'group') { - me.subject = gettext("Group Permission"); - items.push({ - xtype: 'pveGroupSelector', - name: 'groups', - fieldLabel: gettext('Group') - }); - } else if (me.aclType === 'user') { - me.subject = gettext("User Permission"); - items.push({ - xtype: 'pveUserSelector', - name: 'users', - fieldLabel: gettext('User') - }); - } else { - throw "unknown ACL type"; - } - - items.push({ - xtype: 'pveRoleSelector', - name: 'roles', - value: 'NoAccess', - fieldLabel: gettext('Role') - }); - - if (!me.path) { - items.push({ - xtype: 'proxmoxcheckbox', - name: 'propagate', - checked: true, - uncheckedValue: 0, - fieldLabel: gettext('Propagate') - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - items: items, - onlineHelp: 'pveum_permission_management' - }); - - Ext.apply(me, { - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.dc.ACLView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveACLView'], - - onlineHelp: 'chapter_user_management', - - stateful: true, - stateId: 'grid-acls', - - // use fixed path - path: undefined, - - initComponent : function() { - var me = this; - - var store = Ext.create('Ext.data.Store',{ - model: 'pve-acl', - proxy: { - type: 'proxmox', - url: "/api2/json/access/acl" - }, - sorters: { - property: 'path', - order: 'DESC' - } - }); - - if (me.path) { - store.addFilter(Ext.create('Ext.util.Filter',{ - filterFn: function(item) { - if (item.data.path === me.path) { - return true; - } - } - })); - } - - var render_ugid = function(ugid, metaData, record) { - if (record.data.type == 'group') { - return '@' + ugid; - } - - return ugid; - }; - - var columns = [ - { - header: gettext('User') + '/' + gettext('Group'), - flex: 1, - sortable: true, - renderer: render_ugid, - dataIndex: 'ugid' - }, - { - header: gettext('Role'), - flex: 1, - sortable: true, - dataIndex: 'roleid' - } - ]; - - if (!me.path) { - columns.unshift({ - header: gettext('Path'), - flex: 1, - sortable: true, - dataIndex: 'path' - }); - columns.push({ - header: gettext('Propagate'), - width: 80, - sortable: true, - dataIndex: 'propagate' - }); - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - store.load(); - }; - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - disabled: true, - selModel: sm, - confirmMsg: gettext('Are you sure you want to remove this entry'), - handler: function(btn, event, rec) { - var params = { - 'delete': 1, - path: rec.data.path, - roles: rec.data.roleid - }; - if (rec.data.type === 'group') { - params.groups = rec.data.ugid; - } else if (rec.data.type === 'user') { - params.users = rec.data.ugid; - } else { - throw 'unknown data type'; - } - - Proxmox.Utils.API2Request({ - url: '/access/acl', - params: params, - method: 'PUT', - waitMsgTarget: me, - callback: function() { - reload(); - }, - failure: function (response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ - { - text: gettext('Add'), - menu: { - xtype: 'menu', - items: [ - { - text: gettext('Group Permission'), - iconCls: 'fa fa-fw fa-group', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'group', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('User Permission'), - iconCls: 'fa fa-fw fa-user', - handler: function() { - var win = Ext.create('PVE.dc.ACLAdd',{ - aclType: 'user', - path: me.path - }); - win.on('destroy', reload); - win.show(); - } - } - ] - } - }, - remove_btn - ], - viewConfig: { - trackOver: false - }, - columns: columns, - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-acl', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'type', 'ugid', 'roleid', - { - name: 'propagate', - type: 'boolean' - } - ] - }); - -}); -Ext.define('PVE.dc.AuthView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveAuthView'], - - onlineHelp: 'pveum_authentication_realms', - - stateful: true, - stateId: 'grid-authrealms', - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-domains', - sorters: { - property: 'realm', - order: 'DESC' - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.AuthEdit',{ - realm: rec.data.realm, - authType: rec.data.type - }); - win.on('destroy', reload); - win.show(); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - baseurl: '/access/domains/', - selModel: sm, - enableFn: function(rec) { - return !(rec.data.type === 'pve' || rec.data.type === 'pam'); - }, - callback: function() { - reload(); - } - }); - - var tbar = [ - { - text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: [ - { - text: gettext('Active Directory Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit', { - authType: 'ad' - }); - win.on('destroy', reload); - win.show(); - } - }, - { - text: gettext('LDAP Server'), - handler: function() { - var win = Ext.create('PVE.dc.AuthEdit',{ - authType: 'ldap' - }); - win.on('destroy', reload); - win.show(); - } - } - ] - }) - }, - edit_btn, remove_btn - ]; - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: tbar, - viewConfig: { - trackOver: false - }, - columns: [ - { - header: gettext('Realm'), - width: 100, - sortable: true, - dataIndex: 'realm' - }, - { - header: gettext('Type'), - width: 100, - sortable: true, - dataIndex: 'type' - }, - { - header: gettext('TFA'), - width: 100, - sortable: true, - dataIndex: 'tfa' - }, - { - header: gettext('Comment'), - sortable: false, - dataIndex: 'comment', - renderer: Ext.String.htmlEncode, - flex: 1 - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}); -Ext.define('PVE.dc.AuthEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcAuthEdit'], - - isAdd: true, - - initComponent : function() { - var me = this; - - me.isCreate = !me.realm; - - var url; - var method; - var serverlist; - - if (me.isCreate) { - url = '/api2/extjs/access/domains'; - method = 'POST'; - } else { - url = '/api2/extjs/access/domains/' + me.realm; - method = 'PUT'; - } - - var column1 = [ - { - xtype: me.isCreate ? 'textfield' : 'displayfield', - name: 'realm', - fieldLabel: gettext('Realm'), - value: me.realm, - allowBlank: false - } - ]; - - if (me.authType === 'ad') { - - me.subject = gettext('Active Directory Server'); - - column1.push({ - xtype: 'textfield', - name: 'domain', - fieldLabel: gettext('Domain'), - emptyText: 'company.net', - allowBlank: false - }); - - } else if (me.authType === 'ldap') { - - me.subject = gettext('LDAP Server'); - - column1.push({ - xtype: 'textfield', - name: 'base_dn', - fieldLabel: gettext('Base Domain Name'), - emptyText: 'CN=Users,DC=Company,DC=net', - allowBlank: false - }); - - column1.push({ - xtype: 'textfield', - name: 'user_attr', - emptyText: 'uid / sAMAccountName', - fieldLabel: gettext('User Attribute Name'), - allowBlank: false - }); - } else if (me.authType === 'pve') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'Proxmox VE authentication server'; - - } else if (me.authType === 'pam') { - - if (me.isCreate) { - throw 'unknown auth type'; - } - - me.subject = 'linux PAM'; - - } else { - throw 'unknown auth type '; - } - - column1.push({ - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Default'), - name: 'default', - uncheckedValue: 0 - }); - - var column2 = []; - - if (me.authType === 'ldap' || me.authType === 'ad') { - column2.push( - { - xtype: 'textfield', - fieldLabel: gettext('Server'), - name: 'server1', - allowBlank: false - }, - { - xtype: 'proxmoxtextfield', - fieldLabel: gettext('Fallback Server'), - deleteEmpty: !me.isCreate, - name: 'server2' - }, - { - xtype: 'proxmoxintegerfield', - name: 'port', - fieldLabel: gettext('Port'), - minValue: 1, - maxValue: 65535, - emptyText: gettext('Default'), - submitEmptyText: false - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: 'SSL', - name: 'secure', - uncheckedValue: 0 - } - ); - } - - // Two Factor Auth settings - - column2.push({ - xtype: 'proxmoxKVComboBox', - name: 'tfa', - deleteEmpty: !me.isCreate, - value: '', - fieldLabel: gettext('TFA'), - comboItems: [ ['__default__', Proxmox.Utils.noneText], ['oath', 'OATH'], ['yubico', 'Yubico']], - listeners: { - change: function(f, value) { - if (!me.rendered) { - return; - } - me.down('field[name=oath_step]').setVisible(value === 'oath'); - me.down('field[name=oath_digits]').setVisible(value === 'oath'); - me.down('field[name=yubico_api_id]').setVisible(value === 'yubico'); - me.down('field[name=yubico_api_key]').setVisible(value === 'yubico'); - me.down('field[name=yubico_url]').setVisible(value === 'yubico'); - } - } - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_step', - value: '', - minValue: 10, - emptyText: Proxmox.Utils.defaultText + ' (30)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH time step' - }); - - column2.push({ - xtype: 'proxmoxintegerfield', - name: 'oath_digits', - value: '', - minValue: 6, - maxValue: 8, - emptyText: Proxmox.Utils.defaultText + ' (6)', - submitEmptyText: false, - hidden: true, - fieldLabel: 'OATH password length' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_id', - hidden: true, - fieldLabel: 'Yubico API Id' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_api_key', - hidden: true, - fieldLabel: 'Yubico API Key' - }); - - column2.push({ - xtype: 'textfield', - name: 'yubico_url', - hidden: true, - fieldLabel: 'Yubico URL' - }); - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - column1: column1, - column2: column2, - columnB: [{ - xtype: 'textfield', - name: 'comment', - fieldLabel: gettext('Comment') - }], - onGetValues: function(values) { - if (!values.port) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'port' }); - } - delete values.port; - } - - if (me.isCreate) { - values.type = me.authType; - } - - if (values.tfa === 'oath') { - values.tfa = "type=oath"; - if (values.oath_step) { - values.tfa += ",step=" + values.oath_step; - } - if (values.oath_digits) { - values.tfa += ",digits=" + values.oath_digits; - } - } else if (values.tfa === 'yubico') { - values.tfa = "type=yubico"; - values.tfa += ",id=" + values.yubico_api_id; - values.tfa += ",key=" + values.yubico_api_key; - if (values.yubico_url) { - values.tfa += ",url=" + values.yubico_url; - } - } else { - delete values.tfa; - } - - delete values.oath_step; - delete values.oath_digits; - delete values.yubico_api_id; - delete values.yubico_api_key; - delete values.yubico_url; - - return values; - } - }); - - Ext.applyIf(me, { - url: url, - method: method, - fieldDefaults: { - labelWidth: 120 - }, - items: [ ipanel ] - }); - - me.callParent(); - - if (!me.isCreate) { - me.load({ - success: function(response, options) { - var data = response.result.data || {}; - // just to be sure (should not happen) - if (data.type !== me.authType) { - me.close(); - throw "got wrong auth type"; - } - - if (data.tfa) { - var tfacfg = PVE.Parser.parseTfaConfig(data.tfa); - data.tfa = tfacfg.type; - if (tfacfg.type === 'yubico') { - data.yubico_api_key = tfacfg.key; - data.yubico_api_id = tfacfg.id; - data.yubico_url = tfacfg.url; - } else if (tfacfg.type === 'oath') { - // step is a number before - /*jslint confusion: true*/ - data.oath_step = tfacfg.step; - data.oath_digits = tfacfg.digits; - /*jslint confusion: false*/ - } - } - - me.setValues(data); - } - }); - } - } -}); -Ext.define('PVE.dc.BackupEdit', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveDcBackupEdit'], - - defaultFocus: undefined, - - initComponent : function() { - var me = this; - - me.isCreate = !me.jobid; - - var url; - var method; - - if (me.isCreate) { - url = '/api2/extjs/cluster/backup'; - method = 'POST'; - } else { - url = '/api2/extjs/cluster/backup/' + me.jobid; - method = 'PUT'; - } - - var vmidField = Ext.create('Ext.form.field.Hidden', { - name: 'vmid' - }); - - /*jslint confusion: true*/ - // 'value' can be assigned a string or an array - var selModeField = Ext.create('Proxmox.form.KVComboBox', { - xtype: 'proxmoxKVComboBox', - comboItems: [ - ['include', gettext('Include selected VMs')], - ['all', gettext('All')], - ['exclude', gettext('Exclude selected VMs')], - ['pool', gettext('Pool based')] - ], - fieldLabel: gettext('Selection mode'), - name: 'selMode', - value: '' - }); - - var sm = Ext.create('Ext.selection.CheckboxModel', { - mode: 'SIMPLE', - listeners: { - selectionchange: function(model, selected) { - var sel = []; - Ext.Array.each(selected, function(record) { - sel.push(record.data.vmid); - }); - - // to avoid endless recursion suspend the vmidField change - // event temporary as it calls us again - vmidField.suspendEvent('change'); - vmidField.setValue(sel); - vmidField.resumeEvent('change'); - } - } - }); - - var storagesel = Ext.create('PVE.form.StorageSelector', { - fieldLabel: gettext('Storage'), - nodename: 'localhost', - storageContent: 'backup', - allowBlank: false, - name: 'storage' - }); - - var store = new Ext.data.Store({ - model: 'PVEResources', - sorters: { - property: 'vmid', - order: 'ASC' - } - }); - - var vmgrid = Ext.createWidget('grid', { - store: store, - border: true, - height: 300, - selModel: sm, - disabled: true, - columns: [ - { - header: 'ID', - dataIndex: 'vmid', - width: 60 - }, - { - header: gettext('Node'), - dataIndex: 'node' - }, - { - header: gettext('Status'), - dataIndex: 'uptime', - renderer: function(value) { - if (value) { - return Proxmox.Utils.runningText; - } else { - return Proxmox.Utils.stoppedText; - } - } - }, - { - header: gettext('Name'), - dataIndex: 'name', - flex: 1 - }, - { - header: gettext('Type'), - dataIndex: 'type' - } - ] - }); - - var selectPoolMembers = function(poolid) { - if (!poolid) { - return; - } - sm.deselectAll(true); - store.filter([ - { - id: 'poolFilter', - property: 'pool', - value: poolid - } - ]); - sm.selectAll(true); - }; - - var selPool = Ext.create('PVE.form.PoolSelector', { - fieldLabel: gettext('Pool to backup'), - hidden: true, - allowBlank: true, - name: 'pool', - listeners: { - change: function( selpool, newValue, oldValue) { - selectPoolMembers(newValue); - } - } - }); - - var nodesel = Ext.create('PVE.form.NodeSelector', { - name: 'node', - fieldLabel: gettext('Node'), - allowBlank: true, - editable: true, - autoSelect: false, - emptyText: '-- ' + gettext('All') + ' --', - listeners: { - change: function(f, value) { - storagesel.setNodename(value || 'localhost'); - var mode = selModeField.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!value || rec.get('node') === value); - }); - if (mode === 'all') { - sm.selectAll(true); - } - - if (mode === 'pool') { - selectPoolMembers(selPool.value); - } - } - } - }); - - var column1 = [ - nodesel, - storagesel, - { - xtype: 'pveDayOfWeekSelector', - name: 'dow', - fieldLabel: gettext('Day of week'), - multiSelect: true, - value: ['sat'], - allowBlank: false - }, - { - xtype: 'timefield', - fieldLabel: gettext('Start Time'), - name: 'starttime', - format: 'H:i', - formatText: 'HH:MM', - value: '00:00', - allowBlank: false - }, - selModeField, - selPool - ]; - - var column2 = [ - { - xtype: 'textfield', - fieldLabel: gettext('Send email to'), - name: 'mailto' - }, - { - xtype: 'pveEmailNotificationSelector', - fieldLabel: gettext('Email notification'), - name: 'mailnotification', - deleteEmpty: me.isCreate ? false : true, - value: me.isCreate ? 'always' : '' - }, - { - xtype: 'pveCompressionSelector', - fieldLabel: gettext('Compression'), - name: 'compress', - deleteEmpty: me.isCreate ? false : true, - value: 'lzo' - }, - { - xtype: 'pveBackupModeSelector', - fieldLabel: gettext('Mode'), - value: 'snapshot', - name: 'mode' - }, - { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Enable'), - name: 'enabled', - uncheckedValue: 0, - defaultValue: 1, - checked: true - }, - vmidField - ]; - /*jslint confusion: false*/ - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - onlineHelp: 'chapter_vzdump', - column1: column1, - column2: column2, - onGetValues: function(values) { - if (!values.node) { - if (!me.isCreate) { - Proxmox.Utils.assemble_field_data(values, { 'delete': 'node' }); - } - delete values.node; - } - - var selMode = values.selMode; - delete values.selMode; - - if (selMode === 'all') { - values.all = 1; - values.exclude = ''; - delete values.vmid; - } else if (selMode === 'exclude') { - values.all = 1; - values.exclude = values.vmid; - delete values.vmid; - } else if (selMode === 'pool') { - delete values.vmid; - } - - if (selMode !== 'pool') { - delete values.pool; - } - return values; - } - }); - - var update_vmid_selection = function(list, mode) { - if (mode !== 'all' && mode !== 'pool') { - sm.deselectAll(true); - if (list) { - Ext.Array.each(list.split(','), function(vmid) { - var rec = store.findRecord('vmid', vmid); - if (rec) { - sm.select(rec, true); - } - }); - } - } - }; - - vmidField.on('change', function(f, value) { - var mode = selModeField.getValue(); - update_vmid_selection(value, mode); - }); - - selModeField.on('change', function(f, value, oldValue) { - if (oldValue === 'pool') { - store.removeFilter('poolFilter'); - } - - if (oldValue === 'all') { - sm.deselectAll(true); - vmidField.setValue(''); - } - - if (value === 'all') { - sm.selectAll(true); - vmgrid.setDisabled(true); - } else { - vmgrid.setDisabled(false); - } - - if (value === 'pool') { - vmgrid.setDisabled(true); - vmidField.setValue(''); - selPool.setVisible(true); - selPool.allowBlank = false; - selectPoolMembers(selPool.value); - - } else { - selPool.setVisible(false); - selPool.allowBlank = true; - } - var list = vmidField.getValue(); - update_vmid_selection(list, value); - }); - - var reload = function() { - store.load({ - params: { type: 'vm' }, - callback: function() { - var node = nodesel.getValue(); - store.clearFilter(); - store.filterBy(function(rec) { - return (!node || node.length === 0 || rec.get('node') === node); - }); - var list = vmidField.getValue(); - var mode = selModeField.getValue(); - if (mode === 'all') { - sm.selectAll(true); - } else if (mode === 'pool'){ - selectPoolMembers(selPool.value); - } else { - update_vmid_selection(list, mode); - } - } - }); - }; - - Ext.applyIf(me, { - subject: gettext("Backup Job"), - url: url, - method: method, - items: [ ipanel, vmgrid ] - }); - - me.callParent(); - - if (me.isCreate) { - selModeField.setValue('include'); - } else { - me.load({ - success: function(response, options) { - var data = response.result.data; - - data.dow = data.dow.split(','); - - if (data.all || data.exclude) { - if (data.exclude) { - data.vmid = data.exclude; - data.selMode = 'exclude'; - } else { - data.vmid = ''; - data.selMode = 'all'; - } - } else if (data.pool) { - data.selMode = 'pool'; - data.selPool = data.pool; - } else { - data.selMode = 'include'; - } - - me.setValues(data); - } - }); - } - - reload(); - } -}); - - -Ext.define('PVE.dc.BackupView', { - extend: 'Ext.grid.GridPanel', - - alias: ['widget.pveDcBackupView'], - - onlineHelp: 'chapter_vzdump', - - allText: '-- ' + gettext('All') + ' --', - allExceptText: gettext('All except {0}'), - - initComponent : function() { - var me = this; - - var store = new Ext.data.Store({ - model: 'pve-cluster-backup', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/backup" - } - }); - - var reload = function() { - store.load(); - }; - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - var win = Ext.create('PVE.dc.BackupEdit', { - jobid: rec.data.id - }); - win.on('destroy', reload); - win.show(); - }; - - var run_backup_now = function(job) { - job = Ext.clone(job); - - let jobNode = job.node; - // Remove properties related to scheduling - delete job.enabled; - delete job.starttime; - delete job.dow; - delete job.id; - delete job.node; - job.all = job.all === true ? 1 : 0; - - let allNodes = PVE.data.ResourceStore.getNodes(); - let nodes = allNodes.filter(node => node.status === 'online').map(node => node.node); - let errors = []; - - if (jobNode !== undefined) { - if (!nodes.includes(jobNode)) { - Ext.Msg.alert('Error', "Node '"+ jobNode +"' from backup job isn't online!"); - return; - } - nodes = [ jobNode ]; - } else { - let unkownNodes = allNodes.filter(node => node.status !== 'online'); - if (unkownNodes.length > 0) - errors.push(unkownNodes.map(node => node.node + ": " + gettext("Node is offline"))); - } - let jobTotalCount = nodes.length, jobsStarted = 0; - - Ext.Msg.show({ - title: gettext('Please wait...'), - closable: false, - progress: true, - progressText: '0/' + jobTotalCount, - }); - - let postRequest = function () { - jobsStarted++; - Ext.Msg.updateProgress(jobsStarted / jobTotalCount, jobsStarted + '/' + jobTotalCount); - - if (jobsStarted == jobTotalCount) { - Ext.Msg.hide(); - if (errors.length > 0) { - Ext.Msg.alert('Error', 'Some errors have been encountered:
' + errors.join('
')); - } - } - }; - - nodes.forEach(node => Proxmox.Utils.API2Request({ - url: '/nodes/' + node + '/vzdump', - method: 'POST', - params: job, - failure: function (response, opts) { - errors.push(node + ': ' + response.htmlStatus); - postRequest(); - }, - success: postRequest - })); - }; - - var edit_btn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - var run_btn = new Proxmox.button.Button({ - text: gettext('Run now'), - disabled: true, - selModel: sm, - handler: function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - - Ext.Msg.show({ - title: gettext('Confirm'), - icon: Ext.Msg.QUESTION, - msg: gettext('Start the selected backup job now?'), - buttons: Ext.Msg.YESNO, - callback: function(btn) { - if (btn !== 'yes') { - return; - } - run_backup_now(rec.data); - } - }); - } - }); - - var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: '/cluster/backup', - callback: function() { - reload(); - } - }); - - Proxmox.Utils.monStoreErrors(me, store); - - Ext.apply(me, { - store: store, - selModel: sm, - stateful: true, - stateId: 'grid-dc-backup', - viewConfig: { - trackOver: false - }, - tbar: [ - { - text: gettext('Add'), - handler: function() { - var win = Ext.create('PVE.dc.BackupEdit',{}); - win.on('destroy', reload); - win.show(); - } - }, - '-', - remove_btn, - edit_btn, - '-', - run_btn - ], - columns: [ - { - header: gettext('Enabled'), - width: 80, - dataIndex: 'enabled', - xtype: 'checkcolumn', - sortable: true, - disabled: true, - disabledCls: 'x-item-enabled', - stopSelection: false - }, - { - header: gettext('Node'), - width: 100, - sortable: true, - dataIndex: 'node', - renderer: function(value) { - if (value) { - return value; - } - return me.allText; - } - }, - { - header: gettext('Day of week'), - width: 200, - sortable: false, - dataIndex: 'dow', - renderer: function(val) { - var dows = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; - var selected = []; - var cur = -1; - val.split(',').forEach(function(day){ - cur++; - var dow = (dows.indexOf(day)+6)%7; - if (cur === dow) { - if (selected.length === 0 || selected[selected.length-1] === 0) { - selected.push(1); - } else { - selected[selected.length-1]++; - } - } else { - while (cur < dow) { - cur++; - selected.push(0); - } - selected.push(1); - } - }); - - cur = -1; - var days = []; - selected.forEach(function(item) { - cur++; - if (item > 2) { - days.push(Ext.Date.dayNames[(cur+1)] + '-' + Ext.Date.dayNames[(cur+item)%7]); - cur += item-1; - } else if (item == 2) { - days.push(Ext.Date.dayNames[cur+1]); - days.push(Ext.Date.dayNames[(cur+2)%7]); - cur++; - } else if (item == 1) { - days.push(Ext.Date.dayNames[(cur+1)%7]); - } - }); - return days.join(', '); - } - }, - { - header: gettext('Start Time'), - width: 60, - sortable: true, - dataIndex: 'starttime' - }, - { - header: gettext('Storage'), - width: 100, - sortable: true, - dataIndex: 'storage' - }, - { - header: gettext('Selection'), - flex: 1, - sortable: false, - dataIndex: 'vmid', - renderer: function(value, metaData, record) { - /*jslint confusion: true */ - if (record.data.all) { - if (record.data.exclude) { - return Ext.String.format(me.allExceptText, record.data.exclude); - } - return me.allText; - } - if (record.data.vmid) { - return record.data.vmid; - } - - if (record.data.pool) { - return "Pool '"+ record.data.pool + "'"; - } - - return "-"; - } - } - ], - listeners: { - activate: reload, - itemdblclick: run_editor - } - }); - - me.callParent(); - } -}, function() { - - Ext.define('pve-cluster-backup', { - extend: 'Ext.data.Model', - fields: [ - 'id', 'starttime', 'dow', - 'storage', 'node', 'vmid', 'exclude', - 'mailto', 'pool', 'compress', 'mode', - { name: 'enabled', type: 'boolean' }, - { name: 'all', type: 'boolean' } - ] - }); -}); -Ext.define('PVE.dc.Support', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveDcSupport', - pveGuidePath: '/pve-docs/index.html', - onlineHelp: 'getting_help', - - invalidHtml: '

No valid subscription

' + PVE.Utils.noSubKeyHtml, - - communityHtml: 'Please use the public community forum for any questions.', - - activeHtml: 'Please use our support portal for any questions. You can also use the public community forum to get additional information.', - - bugzillaHtml: '

Bug Tracking

Our bug tracking system is available here.', - - docuHtml: function() { - var me = this; - var guideUrl = window.location.origin + me.pveGuidePath; - var text = Ext.String.format('

Documentation

' - + 'The official Proxmox VE Administration Guide' - + ' is included with this installation and can be browsed at ' - + '{0}', guideUrl); - return text; - }, - - updateActive: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.activeHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateCommunity: function(data) { - var me = this; - - var html = '

' + data.productname + '

' + me.communityHtml; - html += '

' + me.docuHtml(); - html += '

' + me.bugzillaHtml; - - me.update(html); - }, - - updateInactive: function(data) { - var me = this; - me.update(me.invalidHtml); - }, - - initComponent: function() { - var me = this; - - var reload = function() { - Proxmox.Utils.API2Request({ - url: '/nodes/localhost/subscription', - method: 'GET', - waitMsgTarget: me, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - me.update('Unable to load subscription status' + ": " + response.htmlStatus); - }, - success: function(response, opts) { - var data = response.result.data; - - if (data.status === 'Active') { - if (data.level === 'c') { - me.updateCommunity(data); - } else { - me.updateActive(data); - } - } else { - me.updateInactive(data); - } - } - }); - }; - - Ext.apply(me, { - autoScroll: true, - bodyStyle: 'padding:10px', - listeners: { - activate: reload - } - }); - - me.callParent(); - } -}); -Ext.define('pve-security-groups', { - extend: 'Ext.data.Model', - - fields: [ 'group', 'comment', 'digest' ], - idProperty: 'group' -}); - -Ext.define('PVE.SecurityGroupEdit', { - extend: 'Proxmox.window.Edit', - - base_url: "/cluster/firewall/groups", - - allow_iface: false, - - initComponent : function() { - var me = this; - - me.isCreate = (me.group_name === undefined); - - var subject; - - me.url = '/api2/extjs' + me.base_url; - me.method = 'POST'; - - var items = [ - { - xtype: 'textfield', - name: 'group', - value: me.group_name || '', - fieldLabel: gettext('Name'), - allowBlank: false - }, - { - xtype: 'textfield', - name: 'comment', - value: me.group_comment || '', - fieldLabel: gettext('Comment') - } - ]; - - if (me.isCreate) { - subject = gettext('Security Group'); - } else { - subject = gettext('Security Group') + " '" + me.group_name + "'"; - items.push({ - xtype: 'hiddenfield', - name: 'rename', - value: me.group_name - }); - } - - var ipanel = Ext.create('Proxmox.panel.InputPanel', { - // InputPanel does not have a 'create' property, does it need a 'isCreate' - isCreate: me.isCreate, - items: items - }); - - - Ext.apply(me, { - subject: subject, - items: [ ipanel ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.SecurityGroupList', { - extend: 'Ext.grid.Panel', - alias: 'widget.pveSecurityGroupList', - - stateful: true, - stateId: 'grid-securitygroups', - - rule_panel: undefined, - - addBtn: undefined, - removeBtn: undefined, - editBtn: undefined, - - base_url: "/cluster/firewall/groups", - - initComponent: function() { - /*jslint confusion: true */ - var me = this; - - if (me.rule_panel == undefined) { - throw "no rule panel specified"; - } - - if (me.base_url == undefined) { - throw "no base_url specified"; - } - - var store = new Ext.data.Store({ - model: 'pve-security-groups', - proxy: { - type: 'proxmox', - url: '/api2/json' + me.base_url - }, - sorters: { - property: 'group', - order: 'DESC' - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var reload = function() { - var oldrec = sm.getSelection()[0]; - store.load(function(records, operation, success) { - if (oldrec) { - var rec = store.findRecord('group', oldrec.data.group); - if (rec) { - sm.select(rec); - } - } - }); - }; - - var run_editor = function() { - var rec = sm.getSelection()[0]; - if (!rec) { - return; - } - var win = Ext.create('PVE.SecurityGroupEdit', { - digest: rec.data.digest, - group_name: rec.data.group, - group_comment: rec.data.comment - }); - win.show(); - win.on('destroy', reload); - }; - - me.editBtn = new Proxmox.button.Button({ - text: gettext('Edit'), - disabled: true, - selModel: sm, - handler: run_editor - }); - - me.addBtn = new Proxmox.button.Button({ - text: gettext('Create'), - handler: function() { - sm.deselectAll(); - var win = Ext.create('PVE.SecurityGroupEdit', {}); - win.show(); - win.on('destroy', reload); - } - }); - - me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', { - selModel: sm, - baseurl: me.base_url + '/', - enableFn: function(rec) { - return (rec && me.base_url); - }, - callback: function() { - reload(); - } - }); - - Ext.apply(me, { - store: store, - tbar: [ '' + gettext('Group') + ':', me.addBtn, me.removeBtn, me.editBtn ], - selModel: sm, - columns: [ - { header: gettext('Group'), dataIndex: 'group', width: '100' }, - { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.String.htmlEncode, flex: 1 } - ], - listeners: { - itemdblclick: run_editor, - select: function(sm, rec) { - var url = '/cluster/firewall/groups/' + rec.data.group; - me.rule_panel.setBaseUrl(url); - }, - deselect: function() { - me.rule_panel.setBaseUrl(undefined); - }, - show: reload - } - }); - - me.callParent(); - - store.load(); - } -}); - -Ext.define('PVE.SecurityGroups', { - extend: 'Ext.panel.Panel', - alias: 'widget.pveSecurityGroups', - - title: 'Security Groups', - - initComponent: function() { - var me = this; - - var rule_panel = Ext.createWidget('pveFirewallRules', { - region: 'center', - allow_groups: false, - list_refs_url: '/cluster/firewall/refs', - tbar_prefix: '' + gettext('Rules') + ':', - border: false - }); - - var sglist = Ext.createWidget('pveSecurityGroupList', { - region: 'west', - rule_panel: rule_panel, - width: '25%', - border: false, - split: true - }); - - - Ext.apply(me, { - layout: 'border', - items: [ sglist, rule_panel ], - listeners: { - show: function() { - sglist.fireEvent('show', sglist); - } - } - }); - - me.callParent(); - } -}); -/* - * Datacenter config panel, located in the center of the ViewPort after the Datacenter view is selected - */ - -Ext.define('PVE.dc.Config', { - extend: 'PVE.panel.Config', - alias: 'widget.PVE.dc.Config', - - onlineHelp: 'pve_admin_guide', - - initComponent: function() { - var me = this; - - var caps = Ext.state.Manager.get('GuiCap'); - - me.items = []; - - Ext.apply(me, { - title: gettext("Datacenter"), - hstateid: 'dctab' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - title: gettext('Summary'), - xtype: 'pveDcSummary', - iconCls: 'fa fa-book', - itemId: 'summary' - }, - { - title: gettext('Cluster'), - xtype: 'pveClusterAdministration', - iconCls: 'fa fa-server', - itemId: 'cluster' - }, - { - title: 'Ceph', - itemId: 'ceph', - iconCls: 'fa fa-ceph', - xtype: 'pveNodeCephStatus' - }, - { - xtype: 'pveDcOptionView', - title: gettext('Options'), - iconCls: 'fa fa-gear', - itemId: 'options' - }); - } - - if (caps.storage['Datastore.Allocate'] || caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveStorageView', - title: gettext('Storage'), - iconCls: 'fa fa-database', - itemId: 'storage' - }); - } - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveDcBackupView', - iconCls: 'fa fa-floppy-o', - title: gettext('Backup'), - itemId: 'backup' - }, - { - xtype: 'pveReplicaView', - iconCls: 'fa fa-retweet', - title: gettext('Replication'), - itemId: 'replication' - }, - { - xtype: 'pveACLView', - title: gettext('Permissions'), - iconCls: 'fa fa-unlock', - itemId: 'permissions', - expandedOnInit: true - }); - } - - me.items.push({ - xtype: 'pveUserView', - groups: ['permissions'], - iconCls: 'fa fa-user', - title: gettext('Users'), - itemId: 'users' - }); - - if (caps.dc['Sys.Audit']) { - me.items.push({ - xtype: 'pveGroupView', - title: gettext('Groups'), - iconCls: 'fa fa-users', - groups: ['permissions'], - itemId: 'groups' - }, - { - xtype: 'pvePoolView', - title: gettext('Pools'), - iconCls: 'fa fa-tags', - groups: ['permissions'], - itemId: 'pools' - }, - { - xtype: 'pveRoleView', - title: gettext('Roles'), - iconCls: 'fa fa-male', - groups: ['permissions'], - itemId: 'roles' - }, - { - xtype: 'pveAuthView', - title: gettext('Authentication'), - groups: ['permissions'], - iconCls: 'fa fa-key', - itemId: 'domains' - }, - { - xtype: 'pveHAStatus', - title: 'HA', - iconCls: 'fa fa-heartbeat', - itemId: 'ha' - }, - { - title: gettext('Groups'), - groups: ['ha'], - xtype: 'pveHAGroupsView', - iconCls: 'fa fa-object-group', - itemId: 'ha-groups' - }, - { - title: gettext('Fencing'), - groups: ['ha'], - iconCls: 'fa fa-bolt', - xtype: 'pveFencingView', - itemId: 'ha-fencing' - }, - { - xtype: 'pveFirewallRules', - title: gettext('Firewall'), - allow_iface: true, - base_url: '/cluster/firewall/rules', - list_refs_url: '/cluster/firewall/refs', - iconCls: 'fa fa-shield', - itemId: 'firewall' - }, - { - xtype: 'pveFirewallOptions', - title: gettext('Options'), - groups: ['firewall'], - iconCls: 'fa fa-gear', - base_url: '/cluster/firewall/options', - onlineHelp: 'pve_firewall_cluster_wide_setup', - fwtype: 'dc', - itemId: 'firewall-options' - }, - { - xtype: 'pveSecurityGroups', - title: gettext('Security Group'), - groups: ['firewall'], - iconCls: 'fa fa-group', - itemId: 'firewall-sg' - }, - { - xtype: 'pveFirewallAliases', - title: gettext('Alias'), - groups: ['firewall'], - iconCls: 'fa fa-external-link', - base_url: '/cluster/firewall/aliases', - itemId: 'firewall-aliases' - }, - { - xtype: 'pveIPSet', - title: 'IPSet', - groups: ['firewall'], - iconCls: 'fa fa-list-ol', - base_url: '/cluster/firewall/ipset', - list_refs_url: '/cluster/firewall/refs', - itemId: 'firewall-ipset' - }, - { - xtype: 'pveDcSupport', - title: gettext('Support'), - itemId: 'support', - iconCls: 'fa fa-comments-o' - }); - } - - me.callParent(); - } -}); -Ext.define('PVE.dc.NodeView', { - extend: 'Ext.grid.GridPanel', - alias: 'widget.pveDcNodeView', - - title: gettext('Nodes'), - disableSelection: true, - scrollable: true, - - columns: [ - { - header: gettext('Name'), - flex: 1, - sortable: true, - dataIndex: 'name' - }, - { - header: 'ID', - width: 40, - sortable: true, - dataIndex: 'nodeid' - }, - { - header: gettext('Online'), - width: 60, - sortable: true, - dataIndex: 'online', - renderer: function(value) { - var cls = (value)?'good':'critical'; - return ''; - } - }, - { - header: gettext('Support'), - width: 100, - sortable: true, - dataIndex: 'level', - renderer: PVE.Utils.render_support_level - }, - { - header: gettext('Server Address'), - width: 115, - sortable: true, - dataIndex: 'ip' - }, - { - header: gettext('CPU usage'), - sortable: true, - width: 110, - dataIndex: 'cpuusage', - tdCls: 'x-progressbar-default-cell', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Memory usage'), - width: 110, - sortable: true, - tdCls: 'x-progressbar-default-cell', - dataIndex: 'memoryusage', - xtype: 'widgetcolumn', - widget: { - xtype: 'pveProgressBar' - } - }, - { - header: gettext('Uptime'), - sortable: true, - dataIndex: 'uptime', - align: 'right', - renderer: Proxmox.Utils.render_uptime - } - ], - - stateful: true, - stateId: 'grid-cluster-nodes', - tools: [ - { - type: 'up', - handler: function(){ - var me = this.up('grid'); - var height = Math.max(me.getHeight()-50, 250); - me.setHeight(height); - } - }, - { - type: 'down', - handler: function(){ - var me = this.up('grid'); - var height = me.getHeight()+50; - me.setHeight(height); - } - } - ] -}, function() { - - Ext.define('pve-dc-nodes', { - extend: 'Ext.data.Model', - fields: [ 'id', 'type', 'name', 'nodeid', 'ip', 'level', 'local', 'online'], - idProperty: 'id' - }); - -}); - -Ext.define('PVE.widget.ProgressBar',{ - extend: 'Ext.Progress', - alias: 'widget.pveProgressBar', - - animate: true, - textTpl: [ - '{percent}%' - ], - - setValue: function(value){ - var me = this; - me.callParent([value]); - - me.removeCls(['warning', 'critical']); - - if (value > 0.89) { - me.addCls('critical'); - } else if (value > 0.59) { - me.addCls('warning'); - } - } -}); -/*jslint confusion: true*/ -Ext.define('pve-cluster-nodes', { - extend: 'Ext.data.Model', - fields: [ - 'node', { type: 'integer', name: 'nodeid' }, 'ring0_addr', 'ring1_addr', - { type: 'integer', name: 'quorum_votes' } - ], - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/nodes" - }, - idProperty: 'nodeid' -}); - -Ext.define('pve-cluster-info', { - extend: 'Ext.data.Model', - proxy: { - type: 'proxmox', - url: "/api2/json/cluster/config/join" - } -}); - -Ext.define('PVE.ClusterAdministration', { - extend: 'Ext.panel.Panel', - xtype: 'pveClusterAdministration', - - title: gettext('Cluster Administration'), - onlineHelp: 'chapter_pvecm', - - border: false, - defaults: { border: false }, - - viewModel: { - parent: null, - data: { - totem: {}, - nodelist: [], - preferred_node: { - name: '', - fp: '', - addr: '' - }, - isInCluster: false, - nodecount: 0 - } - }, - - items: [ - { - xtype: 'panel', - title: gettext('Cluster Information'), - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.store = Ext.create('Proxmox.data.UpdateStore', { - autoStart: true, - interval: 15 * 1000, - storeid: 'pve-cluster-info', - model: 'pve-cluster-info' - }); - view.store.on('load', this.onLoad, this); - view.on('destroy', view.store.stopUpdate); - }, - - onLoad: function(store, records, success) { - var vm = this.getViewModel(); - if (!success || !records || !records[0].data) { - vm.set('totem', {}); - vm.set('isInCluster', false); - vm.set('nodelist', []); - vm.set('preferred_node', { - name: '', - addr: '', - fp: '' - }); - return; - } - var data = records[0].data; - vm.set('totem', data.totem); - vm.set('isInCluster', !!data.totem.cluster_name); - vm.set('nodelist', data.nodelist); - - var nodeinfo = Ext.Array.findBy(data.nodelist, function (el) { - return el.name === data.preferred_node; - }); - - var links = []; - PVE.Utils.forEachCorosyncLink(nodeinfo, - (num, link) => links.push(link)); - - vm.set('preferred_node', { - name: data.preferred_node, - addr: nodeinfo.pve_addr, - ring_addr: links, - fp: nodeinfo.pve_fp - }); - }, - - onCreate: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterCreateWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - }, - - onClusterInfo: function() { - var vm = this.getViewModel(); - var win = Ext.create('PVE.ClusterInfoWindow', { - joinInfo: { - ipAddress: vm.get('preferred_node.addr'), - fingerprint: vm.get('preferred_node.fp'), - ring_addr: vm.get('preferred_node.ring_addr'), - totem: vm.get('totem') - } - }); - win.show(); - }, - - onJoin: function() { - var view = this.getView(); - view.store.stopUpdate(); - var win = Ext.create('PVE.ClusterJoinNodeWindow', { - autoShow: true, - listeners: { - destroy: function() { - view.store.startUpdate(); - } - } - }); - } - }, - tbar: [ - { - text: gettext('Create Cluster'), - reference: 'createButton', - handler: 'onCreate', - bind: { - disabled: '{isInCluster}' - } - }, - { - text: gettext('Join Information'), - reference: 'addButton', - handler: 'onClusterInfo', - bind: { - disabled: '{!isInCluster}' - } - }, - { - text: gettext('Join Cluster'), - reference: 'joinButton', - handler: 'onJoin', - bind: { - disabled: '{isInCluster}' - } - } - ], - layout: 'hbox', - bodyPadding: 5, - items: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Cluster Name'), - bind: { - value: '{totem.cluster_name}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Config Version'), - bind: { - value: '{totem.config_version}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Number of Nodes'), - labelWidth: 120, - bind: { - value: '{nodecount}', - hidden: '{!isInCluster}' - }, - flex: 1 - }, - { - xtype: 'displayfield', - value: gettext('Standalone node - no cluster defined'), - bind: { - hidden: '{isInCluster}' - }, - flex: 1 - } - ] - }, - { - xtype: 'grid', - title: gettext('Cluster Nodes'), - autoScroll: true, - enableColumnHide: false, - controller: { - xclass: 'Ext.app.ViewController', - - init: function(view) { - view.rstore = Ext.create('Proxmox.data.UpdateStore', { - autoLoad: true, - xtype: 'update', - interval: 5 * 1000, - autoStart: true, - storeid: 'pve-cluster-nodes', - model: 'pve-cluster-nodes' - }); - view.setStore(Ext.create('Proxmox.data.DiffStore', { - rstore: view.rstore, - sorters: { - property: 'nodeid', - order: 'DESC' - } - })); - Proxmox.Utils.monStoreErrors(view, view.rstore); - view.rstore.on('load', this.onLoad, this); - view.on('destroy', view.rstore.stopUpdate); - }, - - onLoad: function(store, records, success) { - var view = this.getView(); - var vm = this.getViewModel(); - - if (!success || !records || !records.length) { - vm.set('nodecount', 0); - return; - } - vm.set('nodecount', records.length); - - // show/hide columns according to used links - var linkIndex = view.columns.length; - var columns = Ext.each(view.columns, (col, i) => { - if (col.linkNumber !== undefined) { - col.setHidden(true); - - // save offset at which link columns start, so we - // can address them directly below - if (i < linkIndex) { - linkIndex = i; - } - } - }); - - PVE.Utils.forEachCorosyncLink(records[0].data, - (linknum, val) => { - if (linknum > 7) { - return; - } - view.columns[linkIndex+linknum].setHidden(false); - } - ); - } - }, - columns: { - items: [ - { - header: gettext('Nodename'), - hidden: false, - dataIndex: 'name' - }, - { - header: gettext('ID'), - minWidth: 100, - width: 100, - flex: 0, - hidden: false, - dataIndex: 'nodeid' - }, - { - header: gettext('Votes'), - minWidth: 100, - width: 100, - flex: 0, - hidden: false, - dataIndex: 'quorum_votes' - }, - { - header: Ext.String.format(gettext('Link {0}'), 0), - dataIndex: 'ring0_addr', - linkNumber: 0 - }, - { - header: Ext.String.format(gettext('Link {0}'), 1), - dataIndex: 'ring1_addr', - linkNumber: 1 - }, - { - header: Ext.String.format(gettext('Link {0}'), 2), - dataIndex: 'ring2_addr', - linkNumber: 2 - }, - { - header: Ext.String.format(gettext('Link {0}'), 3), - dataIndex: 'ring3_addr', - linkNumber: 3 - }, - { - header: Ext.String.format(gettext('Link {0}'), 4), - dataIndex: 'ring4_addr', - linkNumber: 4 - }, - { - header: Ext.String.format(gettext('Link {0}'), 5), - dataIndex: 'ring5_addr', - linkNumber: 5 - }, - { - header: Ext.String.format(gettext('Link {0}'), 6), - dataIndex: 'ring6_addr', - linkNumber: 6 - }, - { - header: Ext.String.format(gettext('Link {0}'), 7), - dataIndex: 'ring7_addr', - linkNumber: 7 - } - ], - defaults: { - flex: 1, - hidden: true, - minWidth: 150 - } - } - } - ] -}); -/*jslint confusion: true*/ -Ext.define('PVE.ClusterCreateWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterCreateWindow', - - title: gettext('Create Cluster'), - width: 600, - - method: 'POST', - url: '/cluster/config', - - isCreate: true, - subject: gettext('Cluster'), - showTaskViewer: true, - - onlineHelp: 'pvecm_create_cluster', - - items: { - xtype: 'inputpanel', - items: [{ - xtype: 'textfield', - fieldLabel: gettext('Cluster Name'), - allowBlank: false, - maxLength: 15, - name: 'clustername' - }, - { - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 0), - emptyText: gettext("Optional, defaults to IP resolved by node's hostname"), - name: 'link0', - autoSelect: false, - valueField: 'address', - displayField: 'address', - skipEmptyText: true - }], - advancedItems: [{ - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 1), - emptyText: gettext("Optional second link for redundancy"), - name: 'link1', - autoSelect: false, - valueField: 'address', - displayField: 'address', - skipEmptyText: true - }] - } -}); - -Ext.define('PVE.ClusterInfoWindow', { - extend: 'Ext.window.Window', - xtype: 'pveClusterInfoWindow', - mixins: ['Proxmox.Mixin.CBind'], - - width: 800, - modal: true, - resizable: false, - title: gettext('Cluster Join Information'), - - joinInfo: { - ipAddress: undefined, - fingerprint: undefined, - totem: {} - }, - - items: [ - { - xtype: 'component', - border: false, - padding: '10 10 10 10', - html: gettext("Copy the Join Information here and use it on the node you want to add.") - }, - { - xtype: 'container', - layout: 'form', - border: false, - padding: '0 10 10 10', - items: [ - { - xtype: 'textfield', - fieldLabel: gettext('IP Address'), - cbind: { value: '{joinInfo.ipAddress}' }, - editable: false - }, - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - cbind: { value: '{joinInfo.fingerprint}' }, - editable: false - }, - { - xtype: 'textarea', - inputId: 'pveSerializedClusterInfo', - fieldLabel: gettext('Join Information'), - grow: true, - cbind: { joinInfo: '{joinInfo}' }, - editable: false, - listeners: { - afterrender: function(field) { - if (!field.joinInfo) { - return; - } - var jsons = Ext.JSON.encode(field.joinInfo); - var base64s = Ext.util.Base64.encode(jsons); - field.setValue(base64s); - } - } - } - ] - } - ], - dockedItems: [{ - dock: 'bottom', - xtype: 'toolbar', - items: [{ - xtype: 'button', - handler: function(b) { - var el = document.getElementById('pveSerializedClusterInfo'); - el.select(); - document.execCommand("copy"); - }, - text: gettext('Copy Information') - }] - }] -}); - -Ext.define('PVE.ClusterJoinNodeWindow', { - extend: 'Proxmox.window.Edit', - xtype: 'pveClusterJoinNodeWindow', - - title: gettext('Cluster Join'), - width: 800, - - method: 'POST', - url: '/cluster/config/join', - - defaultFocus: 'textarea[name=serializedinfo]', - isCreate: true, - bind: { - submitText: '{submittxt}', - }, - showTaskViewer: true, - - onlineHelp: 'pvecm_join_node_to_cluster', - - viewModel: { - parent: null, - data: { - info: { - fp: '', - ip: '', - clusterName: '', - ring0Needed: false, - ring1Possible: false, - ring1Needed: false - } - }, - formulas: { - ring0EmptyText: function(get) { - if (get('info.ring0Needed')) { - return gettext("Cannot use default address safely"); - } else { - return gettext("Default: IP resolved by node's hostname"); - } - }, - submittxt: function(get) { - let cn = get('info.clusterName'); - if (cn) { - return `${gettext('Join')} '${cn}'`; - } - return gettext('Join'); - }, - }, - }, - - controller: { - xclass: 'Ext.app.ViewController', - control: { - '#': { - close: function() { - delete PVE.Utils.silenceAuthFailures; - } - }, - 'proxmoxcheckbox[name=assistedEntry]': { - change: 'onInputTypeChange' - }, - 'textarea[name=serializedinfo]': { - change: 'recomputeSerializedInfo', - enable: 'resetField' - }, - 'proxmoxtextfield[name=ring1_addr]': { - enable: 'ring1Needed' - }, - 'textfield': { - disable: 'resetField' - } - }, - resetField: function(field) { - field.reset(); - }, - ring1Needed: function(f) { - var vm = this.getViewModel(); - f.allowBlank = !vm.get('info.ring1Needed'); - }, - onInputTypeChange: function(field, assistedInput) { - var vm = this.getViewModel(); - if (!assistedInput) { - vm.set('info.ring1Possible', true); - } - }, - recomputeSerializedInfo: function(field, value) { - var vm = this.getViewModel(); - var jsons = Ext.util.Base64.decode(value); - var joinInfo = Ext.JSON.decode(jsons, true); - - var info = { - fp: '', - ring1Needed: false, - ring1Possible: false, - ip: '', - clusterName: '' - }; - - var totem = {}; - if (!(joinInfo && joinInfo.totem)) { - field.valid = false; - } else { - var ring0Needed = false; - if (joinInfo.ring_addr !== undefined) { - ring0Needed = joinInfo.ring_addr[0] !== joinInfo.ipAddress; - } - - info = { - ip: joinInfo.ipAddress, - fp: joinInfo.fingerprint, - ring0Needed: ring0Needed, - ring1Possible: !!joinInfo.totem['interface']['1'], - ring1Needed: !!joinInfo.totem['interface']['1'], - clusterName: joinInfo.totem['cluster_name'] - }; - totem = joinInfo.totem; - field.valid = true; - } - - vm.set('info', info); - } - }, - - submit: function() { - // joining may produce temporarily auth failures, ignore as long the task runs - PVE.Utils.silenceAuthFailures = true; - this.callParent(); - }, - - taskDone: function(success) { - delete PVE.Utils.silenceAuthFailures; - if (success) { - var txt = gettext('Cluster join task finished, node certificate may have changed, reload GUI!'); - // ensure user cannot do harm - Ext.getBody().mask(txt, ['pve-static-mask']); - // TaskView may hide above mask, so tell him directly - Ext.Msg.show({ - title: gettext('Join Task Finished'), - icon: Ext.Msg.INFO, - msg: txt - }); - // reload always (if user wasn't faster), but wait a bit for pveproxy - Ext.defer(function() { - window.location.reload(true); - }, 5000); - } - }, - - items: [{ - xtype: 'proxmoxcheckbox', - reference: 'assistedEntry', - name: 'assistedEntry', - submitValue: false, - value: true, - autoEl: { - tag: 'div', - 'data-qtip': gettext('Select if join information should be extracted from pasted cluster information, deselect for manual entering') - }, - boxLabel: gettext('Assisted join: Paste encoded cluster join information and enter password.') - }, - { - xtype: 'textarea', - name: 'serializedinfo', - submitValue: false, - allowBlank: false, - fieldLabel: gettext('Information'), - emptyText: gettext('Paste encoded Cluster Information here'), - validator: function(val) { - return val === '' || this.valid || - gettext('Does not seem like a valid encoded Cluster Information!'); - }, - bind: { - disabled: '{!assistedEntry.checked}', - hidden: '{!assistedEntry.checked}' - }, - value: '' - }, - { - xtype: 'inputpanel', - column1: [ - { - xtype: 'textfield', - fieldLabel: gettext('Peer Address'), - allowBlank: false, - bind: { - value: '{info.ip}', - readOnly: '{assistedEntry.checked}' - }, - name: 'hostname' - }, - { - xtype: 'textfield', - inputType: 'password', - emptyText: gettext("Peer's root password"), - fieldLabel: gettext('Password'), - allowBlank: false, - name: 'password' - } - ], - column2: [ - { - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 0), - bind: { - emptyText: '{ring0EmptyText}', - allowBlank: '{!info.ring0Needed}' - }, - skipEmptyText: true, - autoSelect: false, - valueField: 'address', - displayField: 'address', - name: 'link0' - }, - { - xtype: 'proxmoxNetworkSelector', - fieldLabel: Ext.String.format(gettext('Link {0}'), 1), - skipEmptyText: true, - autoSelect: false, - valueField: 'address', - displayField: 'address', - bind: { - disabled: '{!info.ring1Possible}', - allowBlank: '{!info.ring1Needed}', - }, - name: 'link1' - } - ], - columnB: [ - { - xtype: 'textfield', - fieldLabel: gettext('Fingerprint'), - allowBlank: false, - bind: { - value: '{info.fp}', - readOnly: '{assistedEntry.checked}' - }, - name: 'fingerprint' - } - ] - }] -}); -/*jslint confusion: true */ - -Ext.define('pve-permissions', { - extend: 'Ext.data.TreeModel', - fields: [ - 'text', 'type', - { type: 'boolean', name: 'propagate' } - ] -}); - -Ext.define('PVE.dc.PermissionGridPanel', { - extend: 'Ext.tree.Panel', - onlineHelp: 'chapter_user_management', - - scrollable: true, - - sorterFn: function(rec1, rec2) { - var v1, v2; - - if (rec1.data.type != rec2.data.type) { - v2 = rec1.data.type; - v1 = rec2.data.type; - } else { - v1 = rec1.data.text; - v2 = rec2.data.text; - } - - return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)); - }, - - initComponent: function() { - var me = this; - - Proxmox.Utils.API2Request({ - url: '/access/permissions?userid=' + me.userid, - method: 'GET', - failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); - me.load_task.delay(me.load_delay); - }, - success: function(response, opts) { - Proxmox.Utils.setErrorMask(me, false); - var result = Ext.decode(response.responseText); - var data = result.data || {}; - var records = []; - - var root = { name: '__root', expanded: true, children: [] }; - var idhash = {}; - Ext.Object.each(data, function(path, perms) { - var path_item = {}; - path_item.text = path; - path_item.type = 'path'; - path_item.children = []; - Ext.Object.each(perms, function(perm, propagate) { - var perm_item = {}; - perm_item.text = perm; - perm_item.type = 'perm'; - perm_item.propagate = propagate == 1 ? true : false; - perm_item.iconCls = 'fa fa-fw fa-unlock'; - perm_item.leaf = true; - path_item.children.push(perm_item); - path_item.expandable = true; - }); - idhash[path] = path_item; - }); - - if (!idhash['/']) { - idhash['/'] = { - children: [], - text: '/', - type: 'path', - }; - } - - Ext.Object.each(idhash, function(path, item) { - var parent_item; - if (path == '/') { - parent_item = root; - item.expand = true; - } else { - let split_path = path.split('/'); - while (split_path.pop()) { - let parent_path = split_path.join('/'); - if (parent_item = idhash[parent_path]) { - break; - } - } - } - if (!parent_item) { - parent_item = idhash['/']; - } - parent_item.children.push(item); - }); - - me.setRootNode(root); - } - }); - - var sm = Ext.create('Ext.selection.RowModel', {}); - - Ext.apply(me, { - layout: 'fit', - rootVisible: false, - animate: false, - sortableColumns: false, - selModel: sm, - columns: [ - { - xtype: 'treecolumn', - header: gettext('Path') + '/' + gettext('Permission'), - flex: 1, - sortable: true, - dataIndex: 'text' - }, - { - header: gettext('Propagate'), - width: 80, - sortable: true, - renderer: function(value) { - if (Ext.isDefined(value)) { - return Proxmox.Utils.format_boolean(value); - } else { - return ''; - } - }, - dataIndex: 'propagate' - }, - ], - listeners: { - } - }); - - me.callParent(); - - me.store.sorters.add(new Ext.util.Sorter({ - sorterFn: me.sorterFn - })); - } -}); - -Ext.define('PVE.dc.PermissionView', { - extend: 'Ext.window.Window', - scrollable: true, - width: 800, - height: 600, - layout: 'fit', - - initComponent: function() { - var me = this; - - if (!me.userid) { - throw "no userid specified"; - } - - var grid = Ext.create('PVE.dc.PermissionGridPanel', { - userid: me.userid - }); - - Ext.apply(me, { - title: me.userid + ' - ' + gettext('Permissions'), - items: [ grid ] - }); - - me.callParent(); - } -}); - -/* - * Workspace base class - * - * popup login window when auth fails (call onLogin handler) - * update (re-login) ticket every 15 minutes - * - */ - -Ext.define('PVE.Workspace', { - extend: 'Ext.container.Viewport', - - title: 'Proxmox Virtual Environment', - - loginData: null, // Data from last login call - - onLogin: function(loginData) {}, - - // private - updateLoginData: function(loginData) { - var me = this; - me.loginData = loginData; - Proxmox.Utils.setAuthData(loginData); - - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(loginData.clustername); - - if (loginData.cap) { - Ext.state.Manager.set('GuiCap', loginData.cap); - } - me.response401count = 0; - - me.onLogin(loginData); - }, - - // private - showLogin: function() { - var me = this; - - Proxmox.Utils.authClear(); - Proxmox.UserName = null; - me.loginData = null; - - if (!me.login) { - me.login = Ext.create('PVE.window.LoginWindow', { - handler: function(data) { - me.login = null; - me.updateLoginData(data); - Proxmox.Utils.checked_command(function() {}); // display subscription status - } - }); - } - me.onLogin(null); - me.login.show(); - }, - - initComponent : function() { - var me = this; - - Ext.tip.QuickTipManager.init(); - - // fixme: what about other errors - Ext.Ajax.on('requestexception', function(conn, response, options) { - if (response.status == 401 && !PVE.Utils.silenceAuthFailures) { // auth failure - // don't immediately show as logged out to cope better with some big - // upgrades, which may temporarily produce a false positive 401 err - me.response401count++; - if (me.response401count > 5) { - me.showLogin(); - } - } - }); - - me.callParent(); - - if (!Proxmox.Utils.authOK()) { - me.showLogin(); - } else { - if (me.loginData) { - me.onLogin(me.loginData); - } - } - - Ext.TaskManager.start({ - run: function() { - var ticket = Proxmox.Utils.authOK(); - if (!ticket || !Proxmox.UserName) { - return; - } - - Ext.Ajax.request({ - params: { - username: Proxmox.UserName, - password: ticket - }, - url: '/api2/json/access/ticket', - method: 'POST', - success: function(response, opts) { - var obj = Ext.decode(response.responseText); - me.updateLoginData(obj.data); - } - }); - }, - interval: 15*60*1000 - }); - - } -}); - -Ext.define('PVE.StdWorkspace', { - extend: 'PVE.Workspace', - - alias: ['widget.pveStdWorkspace'], - - // private - setContent: function(comp) { - var me = this; - - var cont = me.child('#content'); - - var lay = cont.getLayout(); - - var cur = lay.getActiveItem(); - - if (comp) { - Proxmox.Utils.setErrorMask(cont, false); - comp.border = false; - cont.add(comp); - if (cur !== null && lay.getNext()) { - lay.next(); - var task = Ext.create('Ext.util.DelayedTask', function(){ - cont.remove(cur); - }); - task.delay(10); - } - } - else { - // helper for cleaning the content when logging out - cont.removeAll(); - } - }, - - selectById: function(nodeid) { - var me = this; - var tree = me.down('pveResourceTree'); - tree.selectById(nodeid); - }, - - onLogin: function(loginData) { - var me = this; - - me.updateUserInfo(); - - if (loginData) { - PVE.data.ResourceStore.startUpdate(); - - Proxmox.Utils.API2Request({ - url: '/version', - method: 'GET', - success: function(response) { - PVE.VersionInfo = response.result.data; - me.updateVersionInfo(); - } - }); - } - }, - - updateUserInfo: function() { - var me = this; - var ui = me.query('#userinfo')[0]; - ui.setText(Proxmox.UserName || ''); - ui.updateLayout(); - }, - - updateVersionInfo: function() { - var me = this; - - var ui = me.query('#versioninfo')[0]; - - if (PVE.VersionInfo) { - var version = PVE.VersionInfo.version; - ui.update('Virtual Environment ' + version); - } else { - ui.update('Virtual Environment'); - } - ui.updateLayout(); - }, - - initComponent : function() { - var me = this; - - Ext.History.init(); - - var sprovider = Ext.create('PVE.StateProvider'); - Ext.state.Manager.setProvider(sprovider); - - var selview = Ext.create('PVE.form.ViewSelector'); - - var rtree = Ext.createWidget('pveResourceTree', { - viewFilter: selview.getViewFilter(), - flex: 1, - selModel: { - selType: 'treemodel', - listeners: { - selectionchange: function(sm, selected) { - if (selected.length > 0) { - var n = selected[0]; - var tlckup = { - root: 'PVE.dc.Config', - node: 'PVE.node.Config', - qemu: 'PVE.qemu.Config', - lxc: 'PVE.lxc.Config', - storage: 'PVE.storage.Browser', - pool: 'pvePoolConfig' - }; - var comp = { - xtype: tlckup[n.data.type || 'root'] || - 'pvePanelConfig', - showSearch: (n.data.id === 'root') || - Ext.isDefined(n.data.groupbyid), - pveSelNode: n, - workspace: me, - viewFilter: selview.getViewFilter() - }; - PVE.curSelectedNode = n; - me.setContent(comp); - } - } - } - } - }); - - selview.on('select', function(combo, records) { - if (records) { - var view = combo.getViewFilter(); - rtree.setViewFilter(view); - } - }); - - var caps = sprovider.get('GuiCap'); - - var createVM = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-desktop', - text: gettext("Create VM"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.qemu.CreateWizard', {}); - wiz.show(); - } - }); - - var createCT = Ext.createWidget('button', { - pack: 'end', - margin: '3 5 0 0', - baseCls: 'x-btn', - iconCls: 'fa fa-cube', - text: gettext("Create CT"), - disabled: !caps.vms['VM.Allocate'], - handler: function() { - var wiz = Ext.create('PVE.lxc.CreateWizard', {}); - wiz.show(); - } - }); - - sprovider.on('statechange', function(sp, key, value) { - if (key === 'GuiCap' && value) { - caps = value; - createVM.setDisabled(!caps.vms['VM.Allocate']); - createCT.setDisabled(!caps.vms['VM.Allocate']); - } - }); - - Ext.apply(me, { - layout: { type: 'border' }, - border: false, - items: [ - { - region: 'north', - layout: { - type: 'hbox', - align: 'middle' - }, - baseCls: 'x-plain', - defaults: { - baseCls: 'x-plain' - }, - border: false, - margin: '2 0 2 5', - items: [ - { - html: '' + - '' - }, - { - minWidth: 150, - id: 'versioninfo', - html: 'Virtual Environment' - }, - { - xtype: 'pveGlobalSearchField', - tree: rtree - }, - { - flex: 1 - }, - { - xtype: 'proxmoxHelpButton', - hidden: false, - baseCls: 'x-btn', - iconCls: 'fa fa-book x-btn-icon-el-default-toolbar-small ', - listenToGlobalEvent: false, - onlineHelp: 'pve_documentation_index', - text: gettext('Documentation'), - margin: '0 5 0 0' - }, - createVM, - createCT, - { - pack: 'end', - margin: '0 5 0 0', - id: 'userinfo', - xtype: 'button', - baseCls: 'x-btn', - style: { - // proxmox dark grey p light grey as border - backgroundColor: '#464d4d', - borderColor: '#ABBABA' - }, - iconCls: 'fa fa-user', - menu: [ - { - iconCls: 'fa fa-gear', - text: gettext('My Settings'), - handler: function() { - var win = Ext.create('PVE.window.Settings'); - win.show(); - } - }, - { - text: gettext('Password'), - iconCls: 'fa fa-fw fa-key', - handler: function() { - var win = Ext.create('Proxmox.window.PasswordEdit', { - userid: Proxmox.UserName - }); - win.show(); - } - }, - { - text: 'TFA', - iconCls: 'fa fa-fw fa-lock', - handler: function(btn, event, rec) { - var win = Ext.create('PVE.window.TFAEdit',{ - userid: Proxmox.UserName - }); - win.show(); - } - }, - '-', - { - iconCls: 'fa fa-fw fa-sign-out', - text: gettext("Logout"), - handler: function() { - PVE.data.ResourceStore.loadData([], false); - me.showLogin(); - me.setContent(null); - var rt = me.down('pveResourceTree'); - rt.setDatacenterText(undefined); - rt.clearTree(); - - // empty the stores of the StatusPanel child items - var statusPanels = Ext.ComponentQuery.query('pveStatusPanel grid'); - Ext.Array.forEach(statusPanels, function(comp) { - if (comp.getStore()) { - comp.getStore().loadData([], false); - } - }); - } - } - ] - } - ] - }, - { - region: 'center', - stateful: true, - stateId: 'pvecenter', - minWidth: 100, - minHeight: 100, - id: 'content', - xtype: 'container', - layout: { type: 'card' }, - border: false, - margin: '0 5 0 0', - items: [] - }, - { - region: 'west', - stateful: true, - stateId: 'pvewest', - itemId: 'west', - xtype: 'container', - border: false, - layout: { type: 'vbox', align: 'stretch' }, - margin: '0 0 0 5', - split: true, - width: 200, - items: [ selview, rtree ], - listeners: { - resize: function(panel, width, height) { - var viewWidth = me.getSize().width; - if (width > viewWidth - 100) { - panel.setWidth(viewWidth - 100); - } - } - } - }, - { - xtype: 'pveStatusPanel', - stateful: true, - stateId: 'pvesouth', - itemId: 'south', - region: 'south', - margin:'0 5 5 5', - title: gettext('Logs'), - collapsible: true, - header: false, - height: 200, - split:true, - listeners: { - resize: function(panel, width, height) { - var viewHeight = me.getSize().height; - if (height > (viewHeight - 150)) { - panel.setHeight(viewHeight - 150); - } - } - } - } - ] - }); - - me.callParent(); - - me.updateUserInfo(); - - // on resize, center all modal windows - Ext.on('resize', function(){ - var wins = Ext.ComponentQuery.query('window[modal]'); - if (wins.length > 0) { - wins.forEach(function(win){ - win.alignTo(me, 'c-c'); - }); - } - }); - } -}); - diff --git a/serverside/jsmod/README.md b/serverside/jsmod/README.md deleted file mode 100644 index e811fc7..0000000 --- a/serverside/jsmod/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# PVEDiscordDark Javascript Modifications - -As you might have noticed, the CSS edits on its own do not completely darken Proxmox's Web UI. -HOWEVER, I have found a way to change that - by modifying it's javascript files. - -These modifications are server-side only and currently it is only working on PVE 5.4-3 as I dont have servers that run an older version. If you're running an older version you could send me the files needed so I could take a look at them but for now this is all I have. - -## Installation -Download the bash file for your version and run it anywhere on your Proxmox **node**. If the changes arent appearing on your browser right away, please clear cache. - -Currently supported versions are 5.4-3, 6.0-4 and 6.1-3. - -## Uninstallation -The bash script backs up the files it replaces. Just remove the modified file and remove the '.bak' extension from the end of the backed up files. - -## Files modified -The files modified are.. -* `/usr/share/pve-manager/js/pvemanager.js` -* `/usr/share/javascript/extjs/charts.js` -* `/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js` - -For more on whats modified, check [here.](https://github.com/Weilbyte/PVEDiscordDark/blob/master/serverside/jsmod/changes.md) diff --git a/serverside/jsmod/changes.md b/serverside/jsmod/changes.md deleted file mode 100644 index db9fc0c..0000000 --- a/serverside/jsmod/changes.md +++ /dev/null @@ -1,31 +0,0 @@ -# Changes -Here you will find exactly what I have modified from the original Proxmox files - -## 5-4.3 -* **pvemanager.js** -Dark color for node summary background to dark (`line 17026`) -Dark color for background of the TFA QR Code (`line 34513`) -Dark color for the S.M.A.R.T disk report (`line 15744`) -Dark color for the system report in subscription tab (`line 17210`) -* **charts.js** -Dark chart background color (`line 8874`) -White chart label color (`+line 8888`) -White chart title color (`+line 8895`) -Light dark color for the chart grid (`line 8901`) -White gauge text (`line 8935`) -* **proxmoxlib.js** -Blurple color for gauge filled meter (`line 4462`) -Dark color for gauge meter background (`line 4463`) - -## 6.0-4 -* **pvemanagerlib.js** -`background-color` to `#23272a` for node summary background (`line 18019`) -`background-color` to `#23272a` for TFA QR code background (`line 35826`) -`background-color` to `#23272a` for S.M.A.R.T disk report (`line 16729`) -`background-color` to `#23272a` for system report (`line 18203`) -* **charts.js** -Remains roughly the same as 5-4.3 -* **proxmoxlib.js** -`defaultColor` to `#7289DA` (`line 5084`) -`backgroundColor` to `#2C2F33` (`line 5085`) - diff --git a/serverside/style.css b/serverside/style.css deleted file mode 100644 index 1355fe1..0000000 --- a/serverside/style.css +++ /dev/null @@ -1,834 +0,0 @@ -/* PVEDiscordDark Theme by Weilbyte - https://github.com/Weilbyte/PVEDiscordDark */ -.x-box-inner { -overflow:hidden; -position:relative; -left:0; -top:0; -background:#23272a; -} - -.x-body { -color:#fff; -font-size:13px; -line-height:17px; -font-weight:300; -font-family:helvetica, arial, verdana, sans-serif; -background:#fff; -} - -.x-form-item-body.x-form-item-body-default.x-form-display-field-body.x-form-display-field-body-default { - border-radius: 6px; - padding: 2px; -} - -.x-field.x-form-item.x-form-item-default.x-form-readonly.x-form-type-text { - border-radius: 6px; -} - -.x-viewport { -background:#23272a; -} - -.x-viewport > .x-body { -background:#23272a; -} - -.x-form-text-default { -color:#818082; -background-color:#4a4d53; -font:300 13px/17px helvetica, arial, verdana, sans-serif; -min-height:22px; -padding:0 6px 2px; -} - -.x-form-trigger-wrap-default { -border-color:#cfcfcf; -border-style:solid; -border-width:0; -} - -.x-form-trigger-default { -width:22px; -background:0 center #72767d url(/pve2/images/dd_trigger.png) no-repeat; -} - -.x-panel-body-default { -background:#2c2f33; -color:#fff; -font-size:13px; -font-weight:300; -font-family:helvetica, arial, verdana, sans-serif; -border-color:#fff; -border-style:solid; -border-width:0; -} - -.x-grid-cell-inner { -background: transparent; -color:#fff; -} - -.x-grid-cell-rowbody { -background:#2c2f33; -color:#fff; -} - -.x-toolbar-default { -background-color:#2c2f33; -border-width:0; -} - -.x-toolbar { -background:#2c2f33; -} - -#toolbar-1069-innerCt { -background:#2c2f33; -} - -.x-autocontainer-innerCt { -background:#2c2f33; -} - -[id^="toolbar"] { -background:#2c2f33; -} - -[id^="legend"] { -background:#23272a; -border-color:red; -} - -.x-legend-item { -background:#2c2f33; -color:#fff; -border-width:0; -} - -.x-legend-container { -background:#2c2f33; -border-radius:0; -box-shadow:rgba(0,0,0,0) 0 0 0; -border-width:0; -} - -.x-legend.x-docked-top .x-legend-item { -border-left:0; -border-bottom:0; -} - -.x-legend.x-docked-bottom .x-legend-item { -border-left:0; -border-bottom:0; -} - -.x-legend-panel.x-docked-top .x-legend-item { -border-left:0; -border-bottom:0; -} - -.x-legend-panel.x-docked-bottom .x-legend-item { -border-left:0; -border-bottom:0; -} - -.x-treelist-item-leaf { -background:#2c2f33; -color:#fff; -} - -.x-treelist-container { -background:#2c2f33; -color:#fff; -} - -.x-treelist-row { -background:#2c2f33; -color:#fff; -} - -.x-treelist-row-with-icon { -background:#2c2f33; -color:#fff; -} - -.x-treelist-item-expandable { -background:#2c2f33; -color:#fff; -} - -.x-treelist-item-wrap { -background:#2c2f33; -color:#fff; -} - -.x-treelist-item-text { -background:#2c2f33; -color:#fff; -} - -.x-treelist-item-icon { -background:#2c2f33; -color:#fff; -} - -.x-treelist-item-selected > .x-treelist-row { -background-color:#23272a; -} - -.x-treelist-row-over { -color:#FF0; -} - -.x-treelist-row-over > * > .x-treelist-item-text { -color:#7289da; -transition:color .5s; -} - -.x-treelist-row-over > * > .x-treelist-item-icon { -color:#7289da; -transition:color .5s; -} - -.fa-ceph:before { -background-image:url(/pve2/images/dd_cephwhite.png); -} - -.x-treelist-row-over > * > .fa-ceph:before { -background-image:url(/pve2/images/dd_cephblurp.png); -background-size:14px 14px; -transition:background-image .5s; -} - -.x-progress { -background:#2c2f33; -} - -.x-progress-bar { -background-color:#7289da!important; -} - -.x-progress-text.x-progress-text-back { -color:#fff!important; -} - -.x-progress-text { -color:#fff!important; -} - -.x-component.x-box-item.x-component-default { -background:#23272a; -} - -.x-panel-header { -background:#23272a; -border:0; -} - -.x-title-text { -color:#7289da; -} - -.x-btn { -background:#7289da; -border-width:0; -} - -.x-btn-inner { -color:#fff; -} - -.x-btn-inner.x-btn-inner-default-small { -color:#fff; -} - -#button-1030 { - background:#23272a; - border: 1px solid #d23d3f; -} - -#userinfo.x-btn { - background-color: transparent !important; - border: 1px solid #d23d3f !important; -} - -#menuitem-1030-iconEl { - color: white; -} - -#menuitem-1031-iconEl { - color: white; -} - -#menuitem-1032-iconEl { - color: white; -} - -#menuitem-1034-iconEl { - color: #c52d2f; -} - -.x-btn.x-unselectable.x-box-item.x-toolbar-item.x-btn-default-toolbar-small.x-btn-over { -background-color:#677bc4; -} - -.x-btn.x-unselectable.x-box-item.x-btn-default-small.x-btn-over { -background-color:#677bc4; -} - -.x-menu-item-focus { -background-color:#677bc4; -} - -.x-btn.x-unselectable.x-box-item.x-toolbar-item.x-btn-default-toolbar-small.x-btn-menu-active { -background-color:#677bc4; -} - -.x-btn-disabled { -background-image:none; -background-color:#737fab!important; -} - -.x-btn-icon-el.x-btn-icon-el-default-small.fa.fa-book.x-btn-icon-el-default-toolbar-small { -color:#fff; -} - -.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-undo { -color:#fff; -} - -.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-terminal { -color:#fff; -} - -.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-fw.fa-ellipsis-v { -color:#fff; -} - -.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.x-btn-icon-el-default-toolbar-small.fa.fa-question-circle { -color:#fff; -} - -.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-send-o { -color:#fff; -} - -.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-play { -color:#43b581; -} - -.x-btn-inner.x-btn-inner-default-toolbar-small { -color:#fff; -} - -.x-btn-icon-el.x-btn-icon-el-default-small.fa.black.fa-gear { -color:#fff; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-send-o { -color:#fff; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-clone { -color:#fff; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-file-o { -color:#fff; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-heartbeat { -color:#fff; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-trash-o { -color:#fff; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-desktop { -color:#fff; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-cube { -color:#fff; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-terminal { -color:#fff; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-play { -color:#43b581; -} - -.x-btn-icon-el.x-btn-icon-el-default-toolbar-small.fa.fa-power-off { -color:#d23d3f; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-stop { -color:#d23d3f; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-stop { -color:#d23d3f; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-power-off { -color:#d23d3f; -} - -.x-menu-item-icon-default.x-menu-item-icon.fa.fa-fw.fa-pause { -color:#faa61a; -} - -.x-menu-item { -background:#7289da; -border-style:none; -border-width:0; -} - -.x-menu-default { -background:#7289da; -border-style:none; -border-width:0; -} - -.x-menu-item-text { -color:#fff; -} - -.x-menu-item-default.x-menu-item-separator { -height:0; -border-top-width:0; -margin:0; -padding:0; -} - -.x-menu-header { -border-radius:1px; -background:#23272a; -border-width:0; -} - -[id^="tooltip"] { -background:#7289da; -color:#fff; -border-width:0; -} - -[id^="ext-quicktips-tip-body"] { -background-color:#7289da; -color:#fff; -border-radius:0; -border-width:0; -} - -[id^="ext-quicktips-tip-innerCt"] { -background-color:#7289da; -color:#fff; -border-radius:0; -border-width:0; -} - -[id^="ext-quicktips-tip-ext-quicktips-tip-outerCt"] { -background-color:#7289da; -color:#fff; -border-radius:0; -border-width:0; -} - -[id^="ext-form-error"] { -background-color:#7289da; -color:#fff; -border-radius:0; -border-width:0; -} - -.x-tip-default { -background-color:#7289da; -color:#fff; -border-radius:0; -border-width:0; -} - -.x-tool-img { -background-image:url(/pve2/images/dd_tool-sprites.png); -} - -.x-window-header.x-header.x-header-draggable.x-docked.x-unselectable.x-window-header-default.x-horizontal.x-window-header-horizontal.x-window-header-default-horizontal.x-top.x-window-header-top.x-window-header-default-top.x-box-layout-ct { -background:#23272a; -border-bottom-width:0; -border-right-width:0; -} - -.x-window-body { -background:#23272a; -border-bottom-width:0; -border-right-width:0; -} - -.x-window-default { -border-radius:0; -background-color:#23272a; -box-shadow:none; -border-color:#23272a; -border-style:none; -border-width:0; -padding:0; -} - -.x-window-default-mc { -background-color:#23272a; -} - -.x-window-body-default { -color: white; -border-width:0!important; -} - -.x-window-header-default-top { -border-top-left-radius:0!important; -border-top-right-radius:0!important; -border-bottom-right-radius:0!important; -border-bottom-left-radius:0!important; -background-color:#23272a; -border-width:0!important; -padding:9px; -} - -.x-form-text { -color:#fff; -} - -.x-form-item-label-inner-default { -color:#fff; -} - -.x-window-text { -color:#fff; -} - -.x-form-display-field { -color: #707C80; -} - -.x-component { -color:#fff; -} - -.x-mask { -background-color:rgba(26,26,29,0.27); -} - -.x-splitter-collapsed .x-layout-split-bottom { -background-image:url(/pve2/images/dd_mini-top.png); -} - -.x-layout-split-bottom { -background-image:url(/pve2/images/dd_mini-bottom.png); -} - -.x-tab { -background:#737fab; -color:#fff; -border-width:0; -} - -.x-tab-inner { -color:#fff; -} - -.x-tab-active { -background:#7289da!important; -border-width:0!important; -} - -.x-tab-over { -background:#7289da!important; -border-width:0!important; -} - -.x-tab-default-focus { -background:#7289da!important; -border-width:0!important; -} - -.x-tab-focus { -background:#7289da!important; -border-width:0!important; -} - -.x-tab-bar-body { -background:#23272a; -} - -.x-tab-disabled { -background:#737fab!important; -} - -.x-column-header-inner { -background:#2c2f33; -border-width:0!important; -} - -.x-column-header-default { -background:#2c2f33; -border-width:0!important; -} - -.x-grid-item { -background:#2c2f33; -border-width:0!important; -} - -.x-grid-item-alt { -background:#23272a; -border-width:0!important; -} - -.x-grid-header-ct { -background:#2c2f33; -border-width:0!important; -} - -.x-column-header-text-inner { -color:#fff; -} - -.x-column-header-trigger { -border-color:#23272a; -} - -.x-toolbar-text.x-box-item.x-toolbar-item.x-toolbar-text-default { -color:#fff; -} - -.x-grid-group-title { -color:#fff; -} - -[id^="pveNodeStatus"] { -background:#23272a; -} - -div[id^="pveNotesView-"][id$="-innerCt"] { -background:#23272a; -} - -img[src^="/pve2/images/proxmox_logo"] { -background:#23272a; -content:url(/pve2/images/dd_logo.png); -} - -[id^="versioninfo"] { -background:#23272a; -} - -[id^="proxmoxGauge"] { -background:#23272a; -} - -div[id^="panel-"][id$="-body"] { -background:#23272a; -} - -.x-surface-canvas { -border-radius:3px; -} - -.x-component.x-fieldset-header-text.x-component-default { -color:#fff; -} - -.x-fieldset-default { -border:1px solid #7289da; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-server { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-server { -color:#7289da!important; -} - -.fa-building.online { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-leaf.fa.fa-cube.running.ha-unmanaged { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-building { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-building { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-leaf.fa.fa-cube { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-leaf.fa.fa-desktop { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-leaf.fa.fa-database { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-cube { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-desktop { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent.fa.fa-database { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-database { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-desktop { -color:#7289da!important; -} - -.x-tree-icon.x-tree-icon-custom.x-tree-icon-parent-expanded.fa.fa-cube { -color:#7289da!important; -} - -.x-grid-group-hd.x-grid-group-hd-collapsible { -background:#23272a; -color:#fff; -border-width:0; -} - -.x-splitter { -background:#23272a; -} - -.x-box-scroller { -background:#2c2f33!important; -} - -.x-box-scroller-body-vertical { -background:#2c2f33!important; -} - -.x-vertical-scroller { -background:#2c2f33!important; -} - -.x-toolbar-vertical-scroller { -background:#2c2f33!important; -} - -.x-toolbar-default-vertical-scroller { -background:#2c2f33!important; -} - -.pve-itype-icon-processor { -background-image:url(/pve2/images/dd_icon-cpu.png); -} - -.pve-itype-icon-memory { -background-image:url(/pve2/images/dd_icon-ram.png); -} - -.pve-itype-icon-storage { -background-image:url(/pve2/images/dd_icon-hdd.png); -} - -.pve-itype-icon-swap { -background-image:url(/pve2/images/dd_icon-swap.png); -} - -.pve-itype-icon-display, .x-grid-row-console { -background-image: url(/pve2/images/dd_icon-display.png); -} - -.pve-itype-icon-cdrom { -background-image: url(/pve2/images/dd_icon-cd.png); -} - -.pve-itype-icon-network { -background-image: url(/pve2/images/dd_icon-network.png); -} - -.pve-itype-icon-pci { -background-image: url(/pve2/images/dd_icon-pci.png); -} - -.pve-itype-icon-usb { -background-image: url(/pve2/images/dd_icon-usb.png); -} - -.pve-itype-icon-serial { -background-image: url(/pve2/images/dd_icon-serial.png); -} - -.pve-itype-icon-cloud { -background-image: url(/pve2/images/dd_icon-cloud.png); -} - -.fa-fw.x-grid-icon-custom.fa.fa-database { -color:#7289da; -} - -.fa-fw.x-grid-icon-custom.fa.fa-desktop { -color:#7289da; -} - -.fa-fw.x-grid-icon-custom.fa.fa-cube { -color:#7289da; -} - -html { -overflow:scroll; -overflow-x:hidden; -} - -::-webkit-scrollbar { -width:0; -background:transparent; -} - -.lxc:after { -background:transparent!important; -color:#7289da; -text-shadow:0 0 0 #2c2f33!important; -} - -.qemu:after { -background:transparent!important; -color:#7289da; -text-shadow:0 0 0 #2c2f33!important; -} - -.x-tree-icon-custom:after { -text-shadow:0 0 0 #2c2f33; -} - -.x-grid-icon-custom:after { -text-shadow:0 0 0 #2c2f33; -} - -* { - font-weight: 350; -} - -.x-grid-row-loading { -background-image: url(/pve2/images/dd_loading.svg); -background-size: 32px; -} - -.proxmox-invalid-row { -background-color: #401314; -}