From feddf6e0235751a326691d7956faf932bb3232e6 Mon Sep 17 00:00:00 2001 From: Jochen Weber Date: Mon, 5 Aug 2024 15:28:16 -0400 Subject: [PATCH 1/6] Merge pull request #105 from ImageMarkup/isic-gui-multilesion Merging branch for Multi-image Lesion window after review from Jochen, Nick, and Veronica. --- index.html | 26 +- package-lock.json | 1308 ++++++----------- package.json | 2 +- sources/constants.js | 21 +- sources/models/lesionWindowImagesUrls.js | 47 + sources/models/lesionsModel.js | 360 +++++ sources/models/state.js | 6 +- sources/services/ajaxActions.js | 23 + sources/services/gallery/gallery.js | 24 +- .../gallery/multiimageLesionWindow.js | 667 +++++++++ sources/services/gallery/searchButtonModel.js | 11 +- sources/styles/common.less | 48 + sources/styles/pages.less | 36 +- sources/styles/popups.less | 249 ++++ sources/views/components/collapser.js | 93 +- sources/views/components/svgIcon.js | 27 + sources/views/subviews/gallery/gallery.js | 22 +- .../subviews/gallery/parts/filterPanel.js | 2 +- .../subviews/gallery/parts/galleryDataview.js | 25 + .../gallery/parts/mobileFilterPanel.js | 2 +- .../gallery/windows/multiImageLesionWindow.js | 738 ++++++++++ webpack.config.js | 5 +- 22 files changed, 2826 insertions(+), 916 deletions(-) create mode 100644 sources/models/lesionWindowImagesUrls.js create mode 100644 sources/models/lesionsModel.js create mode 100644 sources/services/gallery/multiimageLesionWindow.js create mode 100644 sources/views/components/svgIcon.js create mode 100644 sources/views/subviews/gallery/windows/multiImageLesionWindow.js diff --git a/index.html b/index.html index f5eea84..c4ecfdc 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@ - + @@ -52,7 +52,7 @@ c2.769,0.502,5.056,1.75,6.865,3.751c1.807,1.999,2.71,4.539,2.71,7.616C254.326,269.095,253.684,271.288,252.394,273.174z"> - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/package-lock.json b/package-lock.json index 727384e..935fb04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-webpack": "^0.13.1", "eslint-plugin-import": "^2.25.1", - "html-loader": "^0.5.1", + "html-loader": "^5.0.0", "html-webpack-plugin": "^5.3.2", "jest": "^27.2.5", "less": "^3.13.1", @@ -2926,6 +2926,12 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -3163,9 +3169,9 @@ "dev": true }, "node_modules/@types/http-proxy": { - "version": "1.17.8", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", - "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", "dev": true, "dependencies": { "@types/node": "*" @@ -3227,6 +3233,15 @@ "integrity": "sha512-dxcOx8801kMo3KlU+C+/ctWrzREAH7YvoF3aoVpRdqgs+Kf7flp+PJDN/EX5bME3suDUZHsxes9hpvBmzYlWbA==", "dev": true }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/prettier": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.1.tgz", @@ -3298,9 +3313,9 @@ "dev": true }, "node_modules/@types/ws": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", - "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "dependencies": { "@types/node": "*" @@ -3879,19 +3894,6 @@ "node": ">= 6.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4055,12 +4057,6 @@ "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", "dev": true }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, "node_modules/array-includes": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", @@ -4139,15 +4135,6 @@ "get-intrinsic": "^1.1.3" } }, - "node_modules/ast-types": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", - "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -4164,15 +4151,6 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4519,12 +4497,15 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/body-parser": { @@ -4584,18 +4565,14 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "dependencies": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, "node_modules/boolbase": { @@ -4615,12 +4592,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -4689,12 +4666,6 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, - "node_modules/buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -4726,15 +4697,21 @@ } }, "node_modules/camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", "dev": true, "dependencies": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" } }, + "node_modules/camel-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -4788,9 +4765,9 @@ } }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "dependencies": { "anymatch": "~3.1.2", @@ -4804,6 +4781,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -4850,15 +4830,6 @@ "node": ">=0.10.0" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -4927,10 +4898,13 @@ } }, "node_modules/commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } }, "node_modules/commondir": { "version": "1.0.1", @@ -4990,9 +4964,9 @@ "dev": true }, "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, "engines": { "node": ">=0.8" @@ -5388,23 +5362,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "node_modules/deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "dependencies": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -5456,28 +5413,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", - "dev": true, - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -5541,29 +5476,16 @@ "node": ">=8" } }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, "node_modules/dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "dev": true, - "dependencies": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "dependencies": { - "buffer-indexof": "^1.0.0" + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" } }, "node_modules/doctrine": { @@ -5906,16 +5828,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-templates": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", - "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=", - "dev": true, - "dependencies": { - "recast": "~0.11.12", - "through": "~2.3.6" - } - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -6919,12 +6831,6 @@ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", "dev": true }, - "node_modules/fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, "node_modules/fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -6973,9 +6879,9 @@ "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -7486,37 +7392,89 @@ "dev": true }, "node_modules/html-loader": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-0.5.5.tgz", - "integrity": "sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-5.0.0.tgz", + "integrity": "sha512-puaGKdjdVVIFRtgIC2n5dt5bt0N5j6heXlAQZ4Do1MLjHmOT1gCE1Ogg7XZNeJlnOVHHsrZKGs5dfh+XwZ3XPw==", "dev": true, "dependencies": { - "es6-templates": "^0.2.3", - "fastparse": "^1.1.1", - "html-minifier": "^3.5.8", - "loader-utils": "^1.1.0", - "object-assign": "^4.1.1" + "html-minifier-terser": "^7.2.0", + "parse5": "^7.1.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/html-loader/node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/html-loader/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/html-minifier": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", - "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "node_modules/html-loader/node_modules/html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", "dev": true, "dependencies": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.2.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" }, "bin": { - "html-minifier": "cli.js" + "html-minifier-terser": "cli.js" }, "engines": { - "node": ">=4" + "node": "^14.13.1 || >=16.0.0" + } + }, + "node_modules/html-loader/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/html-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/html-minifier-terser": { @@ -7540,16 +7498,6 @@ "node": ">=6" } }, - "node_modules/html-minifier-terser/node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, "node_modules/html-minifier-terser/node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -7559,16 +7507,6 @@ "node": ">= 6" } }, - "node_modules/html-minifier-terser/node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/html-minifier-terser/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7601,12 +7539,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/html-minifier-terser/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, "node_modules/html-webpack-plugin": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.3.2.tgz", @@ -7733,12 +7665,12 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", - "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "dev": true, "dependencies": { - "@types/http-proxy": "^1.17.5", + "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", @@ -7746,6 +7678,14 @@ }, "engines": { "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } } }, "node_modules/https-proxy-agent": { @@ -7871,15 +7811,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -7917,12 +7848,6 @@ "node": ">= 0.10" } }, - "node_modules/ip": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", - "dev": true - }, "node_modules/ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -8133,24 +8058,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", @@ -10290,6 +10197,16 @@ "language-subtag-registry": "^0.3.20" } }, + "node_modules/launch-editor": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.0.tgz", + "integrity": "sha512-vJranOAJrI/llyWGRQqiDM+adrw+k83fvmmx3+nV47g3+36xM15jE+zyZ6Ffel02+xSvuM0b2GDRosXZkbb6wA==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, "node_modules/less": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", @@ -10499,12 +10416,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -10769,18 +10680,6 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/mouse-wheel-zoom": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/mouse-wheel-zoom/-/mouse-wheel-zoom-1.1.5.tgz", @@ -10793,24 +10692,18 @@ "dev": true }, "node_modules/multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "dependencies": { - "dns-packet": "^1.3.1", + "dns-packet": "^5.2.2", "thunky": "^1.0.2" }, "bin": { "multicast-dns": "cli.js" } }, - "node_modules/multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, "node_modules/nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -10864,15 +10757,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "dependencies": { - "lower-case": "^1.1.1" - } - }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -10947,6 +10831,7 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10959,22 +10844,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -11189,21 +11058,6 @@ "node": ">=8" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-retry": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", @@ -11227,14 +11081,21 @@ } }, "node_modules/param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", "dev": true, "dependencies": { - "no-case": "^2.2.0" + "dot-case": "^3.0.4", + "tslib": "^2.0.3" } }, + "node_modules/param-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -11387,29 +11248,6 @@ "node": ">=8" } }, - "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -11562,15 +11400,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -11792,34 +11621,6 @@ "node": ">=8.10.0" } }, - "node_modules/recast": { - "version": "0.11.23", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", - "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", - "dev": true, - "dependencies": { - "ast-types": "0.9.6", - "esprima": "~3.1.0", - "private": "~0.1.5", - "source-map": "~0.5.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/recast/node_modules/esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/rechoir": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", @@ -12160,12 +11961,13 @@ "dev": true }, "node_modules/selfsigned": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", - "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "dependencies": { - "node-forge": "^1.2.0" + "@types/node-forge": "^1.3.0", + "node-forge": "^1" }, "engines": { "node": ">=10" @@ -12366,6 +12168,15 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -12971,12 +12782,6 @@ "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", "dev": true }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -13184,37 +12989,6 @@ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, - "node_modules/uglify-js": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", - "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", - "dev": true, - "dependencies": { - "commander": "~2.19.0", - "source-map": "~0.6.1" - }, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uglify-js/node_modules/commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true - }, - "node_modules/uglify-js/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -13317,12 +13091,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -13729,40 +13497,41 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", - "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dev": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.2.2", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", - "bonjour": "^3.5.0", - "chokidar": "^3.5.2", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", "colorette": "^2.0.10", "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", + "connect-history-api-fallback": "^2.0.0", "default-gateway": "^6.0.3", - "del": "^6.0.0", - "express": "^4.17.1", + "express": "^4.17.3", "graceful-fs": "^4.2.6", "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.0", + "http-proxy-middleware": "^2.0.3", "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", "open": "^8.0.9", "p-retry": "^4.5.0", - "portfinder": "^1.0.28", + "rimraf": "^3.0.2", "schema-utils": "^4.0.0", - "selfsigned": "^2.0.0", + "selfsigned": "^2.1.1", "serve-index": "^1.9.1", - "sockjs": "^0.3.21", + "sockjs": "^0.3.24", "spdy": "^4.0.2", - "strip-ansi": "^7.0.0", - "webpack-dev-middleware": "^5.3.0", - "ws": "^8.1.0" + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" @@ -13770,10 +13539,17 @@ "engines": { "node": ">= 12.13.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, "peerDependencies": { "webpack": "^4.37.0 || ^5.0.0" }, "peerDependenciesMeta": { + "webpack": { + "optional": true + }, "webpack-cli": { "optional": true } @@ -13807,18 +13583,6 @@ "ajv": "^8.8.2" } }, - "node_modules/webpack-dev-server/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/webpack-dev-server/node_modules/colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", @@ -13846,36 +13610,21 @@ "node": ">= 12.13.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", - "integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -14138,9 +13887,9 @@ } }, "node_modules/ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" @@ -16390,6 +16139,12 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, "@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -16614,9 +16369,9 @@ "dev": true }, "@types/http-proxy": { - "version": "1.17.8", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", - "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", "dev": true, "requires": { "@types/node": "*" @@ -16678,6 +16433,15 @@ "integrity": "sha512-dxcOx8801kMo3KlU+C+/ctWrzREAH7YvoF3aoVpRdqgs+Kf7flp+PJDN/EX5bME3suDUZHsxes9hpvBmzYlWbA==", "dev": true }, + "@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/prettier": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.1.tgz", @@ -16749,9 +16513,9 @@ "dev": true }, "@types/ws": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", - "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "requires": { "@types/node": "*" @@ -17183,16 +16947,6 @@ "debug": "4" } }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -17313,12 +17067,6 @@ "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", "dev": true }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, "array-includes": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", @@ -17376,12 +17124,6 @@ "get-intrinsic": "^1.1.3" } }, - "ast-types": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", - "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", - "dev": true - }, "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -17395,15 +17137,6 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -17664,9 +17397,9 @@ "dev": true }, "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true }, "body-parser": { @@ -17718,18 +17451,14 @@ } } }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, "boolbase": { @@ -17749,12 +17478,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-process-hrtime": { @@ -17800,12 +17529,6 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -17828,13 +17551,21 @@ "dev": true }, "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", "dev": true, "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + } } }, "camelcase": { @@ -17867,9 +17598,9 @@ "dev": true }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -17917,12 +17648,6 @@ } } }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -17981,9 +17706,9 @@ } }, "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true }, "commondir": { @@ -18040,9 +17765,9 @@ "dev": true }, "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true }, "content-disposition": { @@ -18325,20 +18050,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -18375,22 +18086,6 @@ "object-keys": "^1.1.1" } }, - "del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", - "dev": true, - "requires": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -18435,29 +18130,13 @@ "path-type": "^4.0.0" } }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, "dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "requires": { - "buffer-indexof": "^1.0.0" + "@leichtgewicht/ip-codec": "^2.0.1" } }, "doctrine": { @@ -18729,16 +18408,6 @@ "is-symbol": "^1.0.2" } }, - "es6-templates": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", - "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=", - "dev": true, - "requires": { - "recast": "~0.11.12", - "through": "~2.3.6" - } - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -19500,12 +19169,6 @@ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", "dev": true }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, "fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -19548,9 +19211,9 @@ "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -19930,31 +19593,60 @@ "dev": true }, "html-loader": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-0.5.5.tgz", - "integrity": "sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog==", - "dev": true, - "requires": { - "es6-templates": "^0.2.3", - "fastparse": "^1.1.1", - "html-minifier": "^3.5.8", - "loader-utils": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "html-minifier": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", - "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-5.0.0.tgz", + "integrity": "sha512-puaGKdjdVVIFRtgIC2n5dt5bt0N5j6heXlAQZ4Do1MLjHmOT1gCE1Ogg7XZNeJlnOVHHsrZKGs5dfh+XwZ3XPw==", "dev": true, "requires": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.2.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" + "html-minifier-terser": "^7.2.0", + "parse5": "^7.1.2" + }, + "dependencies": { + "clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, + "html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "dev": true, + "requires": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + } + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "requires": { + "entities": "^4.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "html-minifier-terser": { @@ -19972,32 +19664,12 @@ "terser": "^4.6.3" }, "dependencies": { - "camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true }, - "param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -20022,12 +19694,6 @@ "dev": true } } - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true } } }, @@ -20126,12 +19792,12 @@ } }, "http-proxy-middleware": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", - "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "dev": true, "requires": { - "@types/http-proxy": "^1.17.5", + "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", @@ -20222,12 +19888,6 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -20259,12 +19919,6 @@ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, - "ip": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", - "dev": true - }, "ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -20394,18 +20048,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, "is-plain-obj": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", @@ -22000,6 +21642,16 @@ "language-subtag-registry": "^0.3.20" } }, + "launch-editor": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.0.tgz", + "integrity": "sha512-vJranOAJrI/llyWGRQqiDM+adrw+k83fvmmx3+nV47g3+36xM15jE+zyZ6Ffel02+xSvuM0b2GDRosXZkbb6wA==", + "dev": true, + "requires": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, "less": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", @@ -22161,12 +21813,6 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -22362,15 +22008,6 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "mouse-wheel-zoom": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/mouse-wheel-zoom/-/mouse-wheel-zoom-1.1.5.tgz", @@ -22383,21 +22020,15 @@ "dev": true }, "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "requires": { - "dns-packet": "^1.3.1", + "dns-packet": "^5.2.2", "thunky": "^1.0.2" } }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, "nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -22436,15 +22067,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "^1.1.1" - } - }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -22503,23 +22125,14 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "peer": true }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -22674,15 +22287,6 @@ "p-limit": "^2.2.0" } }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, "p-retry": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", @@ -22700,12 +22304,21 @@ "dev": true }, "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", "dev": true, "requires": { - "no-case": "^2.2.0" + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + } } }, "parent-module": { @@ -22832,28 +22445,6 @@ "find-up": "^4.0.0" } }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, "postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -22953,12 +22544,6 @@ } } }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -23135,26 +22720,6 @@ "picomatch": "^2.2.1" } }, - "recast": { - "version": "0.11.23", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", - "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", - "dev": true, - "requires": { - "ast-types": "0.9.6", - "esprima": "~3.1.0", - "private": "~0.1.5", - "source-map": "~0.5.0" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - } - } - }, "rechoir": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", @@ -23408,12 +22973,13 @@ "dev": true }, "selfsigned": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", - "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "requires": { - "node-forge": "^1.2.0" + "@types/node-forge": "^1.3.0", + "node-forge": "^1" } }, "semver": { @@ -23587,6 +23153,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -24052,12 +23624,6 @@ "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", "dev": true }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, "thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -24219,30 +23785,6 @@ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, - "uglify-js": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", - "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", - "dev": true, - "requires": { - "commander": "~2.19.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -24304,12 +23846,6 @@ "picocolors": "^1.0.0" } }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -24663,40 +24199,41 @@ } }, "webpack-dev-server": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", - "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dev": true, "requires": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.2.2", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", - "bonjour": "^3.5.0", - "chokidar": "^3.5.2", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", "colorette": "^2.0.10", "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", + "connect-history-api-fallback": "^2.0.0", "default-gateway": "^6.0.3", - "del": "^6.0.0", - "express": "^4.17.1", + "express": "^4.17.3", "graceful-fs": "^4.2.6", "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.0", + "http-proxy-middleware": "^2.0.3", "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", "open": "^8.0.9", "p-retry": "^4.5.0", - "portfinder": "^1.0.28", + "rimraf": "^3.0.2", "schema-utils": "^4.0.0", - "selfsigned": "^2.0.0", + "selfsigned": "^2.1.1", "serve-index": "^1.9.1", - "sockjs": "^0.3.21", + "sockjs": "^0.3.24", "spdy": "^4.0.2", - "strip-ansi": "^7.0.0", - "webpack-dev-middleware": "^5.3.0", - "ws": "^8.1.0" + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" }, "dependencies": { "ajv": { @@ -24720,12 +24257,6 @@ "fast-deep-equal": "^3.1.3" } }, - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, "colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", @@ -24750,19 +24281,10 @@ "ajv-keywords": "^5.0.0" } }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - }, "ws": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", - "integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "requires": {} } @@ -24928,9 +24450,9 @@ } }, "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index 7c27dbd..9425edc 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-webpack": "^0.13.1", "eslint-plugin-import": "^2.25.1", - "html-loader": "^0.5.1", + "html-loader": "^5.0.0", "html-webpack-plugin": "^5.3.2", "jest": "^27.2.5", "less": "^3.13.1", diff --git a/sources/constants.js b/sources/constants.js index 507d4ee..bc6b830 100644 --- a/sources/constants.js +++ b/sources/constants.js @@ -184,11 +184,30 @@ export default { NULL_OPTION_VALUE: "unknown", MISSING_KEY_VALUE: "missing key", + DEFAULT_RIBBON_IMAGE_ICON_WIDTH: 14, + DEFAULT_RIBBON_IMAGE_ICON_HEIGHT: 14, + DEFAULT_RIBBON_ICON_CONTAINER_WIDTH: 18, + DEFAULT_RIBBON_ICON_CONTAINER_HEIGHT: 18, DEFAULT_GALLERY_IMAGE_ICON_WIDTH: 18, DEFAULT_GALLERY_IMAGE_ICON_HEIGHT: 18, DEFAULT_GALLERY_IMAGE_ICON_CONTAINER_WIDTH: 22, DEFAULT_GALLERY_IMAGE_ICON_CONTAINER_HEIGHT: 22, DEFAULT_GALLERY_IMAGE_NAME_FONT_SIZE: 14, DEFAULT_GALLERY_IMAGE_WIDTH: 180, - DEFAULT_GALLERY_IMAGE_HEIGHT: 123 + DEFAULT_GALLERY_IMAGE_HEIGHT: 123, + + // MultiLesion + MULTI_LESION_GROUP_BY: { + TIME: "Time Point", + TYPE: "Modality", + COMBINATION: "Time Point and Modality", + NO_GROUP: "No group" + }, + MULTI_LESION_SIDE: { + LEFT: "left", + RIGHT: "right", + }, + MULTI_LESION_TYPE_PRIORITY: { + FIRST: "dermoscopic" + }, }; diff --git a/sources/models/lesionWindowImagesUrls.js b/sources/models/lesionWindowImagesUrls.js new file mode 100644 index 0000000..cdad7ba --- /dev/null +++ b/sources/models/lesionWindowImagesUrls.js @@ -0,0 +1,47 @@ +let prevImagesUrl = ""; +let nextImagesUrl = ""; +let currImagesUrl = ""; +let offset = 0; + +function setCurrImagesUrl(url) { + currImagesUrl = url; +} + +function getCurrImagesUrl() { + return currImagesUrl; +} + +function setNextImagesUrl(url) { + nextImagesUrl = url; +} + +function getNextImagesUrl() { + return nextImagesUrl; +} + +function setPrevImagesUrl(url) { + prevImagesUrl = url; +} + +function getPrevImagesUrl() { + return prevImagesUrl; +} + +function getOffset() { + return offset; +} + +function setOffset(newOffset) { + offset = newOffset; +} + +export default { + getCurrImagesUrl, + setCurrImagesUrl, + getNextImagesUrl, + setNextImagesUrl, + getPrevImagesUrl, + setPrevImagesUrl, + getOffset, + setOffset, +}; diff --git a/sources/models/lesionsModel.js b/sources/models/lesionsModel.js new file mode 100644 index 0000000..947666f --- /dev/null +++ b/sources/models/lesionsModel.js @@ -0,0 +1,360 @@ +const lesionsMap = new Map(); +let currentItem; +let leftMode; +let rightMode; +let leftImage; +let rightImage; +/** @type {webix.DataCollection} */ +const currentLeftImagesCollection = new webix.DataCollection(); +/** @type {webix.DataCollection} */ +const currentRightImagesCollection = new webix.DataCollection(); + +function getLesionByID(lesionID) { + return lesionsMap.get(lesionID); +} + +function setLesions(lesions) { + if (Array.isArray(lesions)) { + lesions.push(...lesions); + } + lesions.forEach((l) => { + lesionsMap.set(l.id, l); + }); +} + +function getLesionImagesCount(lesionID) { + const lesion = getLesionByID(lesionID); + return lesion?.images_count ?? 0; +} + +function getLesionAnchorImageID(lesionID) { + const lesion = getLesionByID(lesionID); + return lesion?.index_image_id; +} + +function checkIsImageAnchor(image) { + const lesionID = getItemLesionID(image); + if (lesionID) { + const lesionAnchorImageID = getAnchorImageID(lesionID); + return lesionAnchorImageID === getItemID(image); + } + return false; +} + +function getLesionModalitiesCount(lesionID) { + const lesion = getLesionByID(lesionID); + const images = lesion.images; + const lesionModalitiesCount = images.reduce((modalities, img) => { + const imgModality = getImageModality(img); + if (modalities.includes(imgModality)) { + return modalities; + } + modalities.push(imgModality); + return modalities; + }, [])?.length; + return lesionModalitiesCount; +} + +function getImagesWithModalityCount(item) { + const lesionID = getItemLesionID(item); + if (lesionID) { + const lesion = getLesionByID(lesionID); + const modality = getItemModality(item); + const images = lesion.images; + const lesionModalityImagesCount = images.filter(i => getImageModality(i) === modality).length; + return lesionModalityImagesCount; + } + return 0; +} + +function getLesionTimePoints(lesionID) { + const lesion = getLesionByID(lesionID); + const images = lesion?.images; + const lesionTimePoints = images?.reduce((timePoints, img) => { + const imgTimePoint = getImageTimePoint(img); + if (timePoints.includes(imgTimePoint)) { + return timePoints; + } + timePoints.push(imgTimePoint); + return timePoints; + }, []); + return lesionTimePoints; +} + +function getLesionTimePointsCount(lesionID) { + const lesionTimePoints = getLesionTimePoints(lesionID); + return lesionTimePoints.length; +} + +function getImagesWithTimePointsCount(item) { + const lesionID = getItemLesionID(item); + if (lesionID) { + const lesion = getLesionByID(lesionID); + const timePoint = getItemTimePoint(item); + const images = lesion?.images; + const lesionTimePointsImagesCount = images + .filter(i => getImageTimePoint(i) === timePoint).length; + return lesionTimePointsImagesCount; + } + return 0; +} + +function getLesionImages(lesionID) { + const lesion = getLesionByID(lesionID); + return lesion?.images; +} + +function getModalityImages(lesionID, modality) { + const lesionImages = getLesionImages(lesionID); + return lesionImages?.filter(i => getImageModality(i) === modality); +} + +function getTimePointImages(lesionID, timePoint) { + const lesionImages = getLesionImages(lesionID); + return lesionImages?.filter(i => getImageTimePoint(i) === timePoint); +} + +// combine is time point and modality +function getCombineImages(lesionID, timePoint, modality) { + const lesionImages = getLesionImages(lesionID); + return lesionImages?.filter((i) => { + const result = getImageTimePoint(i) === timePoint && getImageModality(i) === modality; + return result; + }); +} + +function getAnchorImageID(lesionID) { + const lesion = getLesionByID(lesionID); + return lesion?.index_image_id; +} + +function getFirstNonAnchorImage(lesionID, anchorImageID) { + const timePoints = getLesionTimePoints(lesionID); + const lesionImages = getLesionImages(lesionID); + const anchorImage = lesionImages.find(i => getItemID(i) === anchorImageID); + const anchorTimePoint = getImageTimePoint(anchorImage); + const sortedTimePoints = timePoints.sort((a, b) => a - b); + const firstTimePoint = sortedTimePoints[0]; + let nonAnchorImage; + if (timePoints.length === 1) { + nonAnchorImage = lesionImages.find(i => anchorImageID !== getItemID(i)) + ?? lesionImages[0]; + } + else if (anchorTimePoint === firstTimePoint) { + const secondTimePoint = sortedTimePoints[1]; + nonAnchorImage = lesionImages.find(i => getImageTimePoint(i) === secondTimePoint); + } + else { + nonAnchorImage = lesionImages.find(i => getImageTimePoint(i) === firstTimePoint); + } + return nonAnchorImage; +} + +function getImageTimePoint(image) { + return image?.metadata?.clinical?.acquisition_day; +} + +function getImageModality(image) { + return image?.metadata?.acquisition?.image_type; +} + +function getItemLesionID(item) { + return item?.metadata?.clinical?.lesion_id; +} + +function checkMultipleModality(lesionID) { + const lesionModalitiesCount = getLesionModalitiesCount(lesionID); + return lesionModalitiesCount > 1; +} + +function getCurrentItem() { + return currentItem; +} + +function setCurrentItem(item) { + currentItem = item; +} + +function getItemID(item) { + return item?.isic_id; +} + +function getItemTimePoint(item) { + return item?.metadata?.clinical?.acquisition_day; +} + +function getItemModality(item) { + return item?.metadata?.acquisition?.image_type; +} + +function getUploadDay(item) { + return item?.contribution_day; +} + +function getLeftMode() { + return leftMode; +} + +function setLeftMode(mode) { + leftMode = mode; +} + +function getRightMode() { + return rightMode; +} + +function setRightMode(mode) { + rightMode = mode; +} + +function getLeftImage() { + return leftImage; +} + +function setLeftImage(image) { + leftImage = image; +} + +function getRightImage() { + return rightImage; +} + +function setRightImage(image) { + rightImage = image; +} + +function groupByTimePoint(images) { + const imagesGroups = Object.groupBy(images, i => getImageTimePoint(i)); + return imagesGroups; +} + +function groupByModality(images) { + const imagesGroups = Object.groupBy(images, i => getImageModality(i)); + return imagesGroups; +} + +function groupByTimePointAndModality(images) { + const timePointGroups = Object.groupBy(images, i => getImageTimePoint(i)); + const timePointKeys = Object.keys(timePointGroups); + const imagesGroups = {}; + timePointKeys.forEach((tpk) => { + const modalityGroups = Object.groupBy(timePointGroups[tpk], i => getImageModality(i)); + const modalityKeys = Object.keys(modalityGroups); + modalityKeys.forEach((mk) => { + imagesGroups[`${tpk} and ${mk}`] = modalityGroups[mk]; + }); + }); + return imagesGroups; +} + +function groupByID(images) { + const imagesGroups = Object.groupBy(images, i => getItemID(i)); + return imagesGroups; +} + +function setCurrentLeftImages(images) { + currentLeftImagesCollection.clearAll(); + currentLeftImagesCollection.parse(images); +} + +function getCurrentLeftImages() { + return currentLeftImagesCollection.serialize(); +} + +function setCurrentRightImages(images) { + currentRightImagesCollection.clearAll(); + currentRightImagesCollection.parse(images); +} + +function getCurrentRightImages() { + return currentRightImagesCollection.serialize(); +} + +function getNextLeftImage(image) { + const currentImage = currentLeftImagesCollection.find( + i => getItemID(i) === getItemID(image), + true + ); + const nextImageId = currentLeftImagesCollection.getNextId(currentImage.id) + ?? currentLeftImagesCollection.getFirstId(); + const nextImage = currentLeftImagesCollection.getItem(nextImageId); + return nextImage; +} + +function getNextRightImage(image) { + const currentImage = currentRightImagesCollection.find( + i => getItemID(i) === getItemID(image), + true + ); + const nextImageId = currentRightImagesCollection.getNextId(currentImage.id) + ?? currentRightImagesCollection.getFirstId(); + const nextImage = currentRightImagesCollection.getItem(nextImageId); + return nextImage; +} + +function getPrevLeftImage(image) { + const currentImage = currentLeftImagesCollection.find( + i => getItemID(i) === getItemID(image), + true + ); + const prevImageId = currentLeftImagesCollection.getPrevId(currentImage.id) + ?? currentLeftImagesCollection.getLastId(); + const prevImage = currentLeftImagesCollection.getItem(prevImageId); + return prevImage; +} + +function getPrevRightImage(image) { + const currentImage = currentRightImagesCollection.find( + i => getItemID(i) === getItemID(image), + true + ); + const prevImageId = currentRightImagesCollection.getPrevId(currentImage.id) + ?? currentRightImagesCollection.getLastId(); + const prevImage = currentRightImagesCollection.getItem(prevImageId); + return prevImage; +} + +export default { + setLesions, + getLesionImagesCount, + getLesionModalitiesCount, + getModalityImages, + getImagesWithModalityCount, + getLesionTimePointsCount, + getLesionAnchorImageID, + getLesionImages, + getTimePointImages, + getCombineImages, + getImagesWithTimePointsCount, + getAnchorImageID, + getItemLesionID, + checkMultipleModality, + getCurrentItem, + setCurrentItem, + getItemID, + getItemTimePoint, + getItemModality, + getUploadDay, + getLeftMode, + setLeftMode, + getRightMode, + setRightMode, + getLeftImage, + setLeftImage, + getRightImage, + setRightImage, + checkIsImageAnchor, + getFirstNonAnchorImage, + groupByModality, + groupByTimePoint, + groupByTimePointAndModality, + groupByID, + setCurrentLeftImages, + getCurrentLeftImages, + setCurrentRightImages, + getCurrentRightImages, + getPrevLeftImage, + getNextLeftImage, + getPrevRightImage, + getNextRightImage, +}; diff --git a/sources/models/state.js b/sources/models/state.js index 7bd851e..2e100f0 100644 --- a/sources/models/state.js +++ b/sources/models/state.js @@ -29,7 +29,11 @@ const state = { toolbarValues: {} }; }, - imagesOffset: 0 + imagesOffset: 0, + filteredImages: { + isImagesFiltered: false, + filteredImagesCount: 0 + } }; export default state; diff --git a/sources/services/ajaxActions.js b/sources/services/ajaxActions.js index ecb2220..316466c 100644 --- a/sources/services/ajaxActions.js +++ b/sources/services/ajaxActions.js @@ -66,6 +66,7 @@ class AjaxActions { async _ajaxGet(url, params) { const headers = await getAuthHeaders(); + headers["Content-Type"] = "application/json"; if (!params) { params = {}; } @@ -248,6 +249,28 @@ class AjaxActions { } return null; } + + async getLesions(/* sourceParams */) { + try { + const result = await this._ajaxGet(`${API_URL}lesions/`); + return this._parseData(result); + } + catch (error) { + parseError(error); + return []; + } + } + + async getLesionByID(lesionID) { + try { + const result = await this._ajaxGet(`${API_URL}lesion/${lesionID}`); + return this._parseData(result); + } + catch (error) { + parseError(error); + return []; + } + } } const instance = new AjaxActions(); diff --git a/sources/services/gallery/gallery.js b/sources/services/gallery/gallery.js index 78b4d6e..209f4af 100644 --- a/sources/services/gallery/gallery.js +++ b/sources/services/gallery/gallery.js @@ -35,6 +35,7 @@ class GalleryService { imageWindowMetadata, metadataWindow, metadataWindowMetadata, + multiImageLesionWindow, filtersForm, appliedFiltersList, unselectLink, @@ -66,6 +67,7 @@ class GalleryService { this._imageWindowMetadata = imageWindowMetadata; this._metadataWindow = metadataWindow; this._metadataWindowMetadata = metadataWindowMetadata; + this._multiImageLesionWindow = multiImageLesionWindow; this._filtersForm = filtersForm; this._appliedFiltersList = appliedFiltersList; this._imagesSelectionTemplate = unselectLink; @@ -440,7 +442,9 @@ class GalleryService { const filtered = state.imagesTotalCounts.passedFilters.filtered; this._updateContentHeaderTemplate({ rangeStart: offset + 1, - rangeFinish: currentCount && offset + limit >= currentCount ? currentCount : offset + limit, + rangeFinish: currentCount && offset + limit >= currentCount + ? currentCount + : offset + limit, totalCount: count, currentCount, filtered @@ -636,6 +640,16 @@ class GalleryService { } }; + this._imagesDataview.on_click["layer-group"] = (e, id) => { + if (this._multiImageLesionWindow) { + const currentItem = this._imagesDataview.getItem(id); + this._view.$scope.setMultiLesionMode( + currentItem, + ); + this._multiImageLesionWindow.show(); + } + }; + // -->add onClick property for template this._imagesSelectionTemplate?.define("onClick", { "unselect-images-link": () => { @@ -1139,6 +1153,12 @@ class GalleryService { } _updateContentHeaderTemplate(ranges) { + if (ranges.filtered) { + state.filteredImages.isImagesFiltered = true; + if (ranges.currentCount) { + state.filteredImages.filteredImagesCount = ranges.currentCount; + } + } const values = webix.copy(ranges); this._contentHeaderTemplate?.setValues(values, true); // true -> unchange existing values this._contentHeaderTemplate?.refresh(); @@ -1462,7 +1482,7 @@ class GalleryService { _searchEventsMethods(eventMethod) { this._searchInput.detachEvent("onEnter"); - this._searchInput.on_click["fa-search"] = eventMethod; + this._searchInput.on_click["gallery-search-filter"] = eventMethod; this._searchInput.attachEvent("onEnter", eventMethod); } diff --git a/sources/services/gallery/multiimageLesionWindow.js b/sources/services/gallery/multiimageLesionWindow.js new file mode 100644 index 0000000..00e586e --- /dev/null +++ b/sources/services/gallery/multiimageLesionWindow.js @@ -0,0 +1,667 @@ +import constants from "../../constants"; +import galleryImagesUrls from "../../models/galleryImagesUrls"; +import lesionWindowImagesUrls from "../../models/lesionWindowImagesUrls"; +import lesionsModel from "../../models/lesionsModel"; +import util from "../../utils/util"; +import metadataPart from "../../views/subviews/gallery/parts/metadata"; +import multiImageLesionWindow from "../../views/subviews/gallery/windows/multiImageLesionWindow"; +import ajax from "../ajaxActions"; +import authService from "../auth"; +import searchButtonModel from "./searchButtonModel"; + +const MOVE = { + next: "next", + prev: "prev", +}; + +export default class MultiLesionWindowService { + constructor( + galleryService, + ) { + /** @type {import("./gallery").default} */ + this._galleryService = galleryService; + this._window = $$(multiImageLesionWindow.getWindowID()); + /** @type {webix.ui.list} */ + this._topSlider = $$(multiImageLesionWindow.getTopSliderID()); + /** @type {webix.ui.list} */ + this._leftSlider = $$(multiImageLesionWindow.getLeftSliderID()); + /** @type {webix.ui.list} */ + this._rightSlider = $$(multiImageLesionWindow.getRightSliderID()); + this._leftFooter = $$(multiImageLesionWindow.getLeftFooterID()); + this._rightFooter = $$(multiImageLesionWindow.getRightFooterID()); + this._leftGroup = $$(multiImageLesionWindow.getLeftDropDownFilterID()); + this._rightGroup = $$(multiImageLesionWindow.getRightDropDownFilterID()); + this._leftImageLabel = $$(multiImageLesionWindow.getLeftImageNameLabelID()); + this._rightImageLabel = $$(multiImageLesionWindow.getRightImageNameLabelID()); + this._rightContainer = $$(multiImageLesionWindow.getRightContainerID()); + /** @type {webix.ui.template} */ + this._leftImage = $$(multiImageLesionWindow.getLeftImageID()); + /** @type {webix.ui.template} */ + this._rightImage = $$(multiImageLesionWindow.getRightImageID()); + this._fullScreenButton = $$(multiImageLesionWindow.getFullScreenButtonID()); + this._windowedButton = $$(multiImageLesionWindow.getWindowedButtonID()); + /** @type {webix.ui.search} */ + this._searchInput = $$(multiImageLesionWindow.getSearchID()); + this._prevPageButton = $$(multiImageLesionWindow.getPrevPageButtonID()); + this._nextPageButton = $$(multiImageLesionWindow.getNextPageButtonID()); + /** @type {webix.ui.layout} */ + this._leftAnchorIcon = $$(multiImageLesionWindow.getLeftAnchorIconID()); + this._rightAnchorIcon = $$(multiImageLesionWindow.getRightAnchorIconID()); + this._topPanel = $$(multiImageLesionWindow.getTopPanelID()); + this._expandButton = $$(multiImageLesionWindow.getExpandButtonID()); + this._collapseButton = $$(multiImageLesionWindow.getCollapseButtonID()); + this._fullscreen = false; + this.init(); + } + + async init() { + this._leftImage.attachEvent("onBeforeRender", this.updateImage); + this._leftImage.attachEvent("onAfterLoad", this.updateImage); + + this._rightImage.attachEvent("onBeforeRender", this.updateImage); + this._rightImage.attachEvent("onAfterLoad", this.updateImage); + + this._fullScreenButton.attachEvent("onItemClick", () => { this.changeWindowMode(); }); + this._windowedButton.attachEvent("onItemClick", () => { this.changeWindowMode(); }); + + this._searchInput.on_click["lesionWindow__filter-search"] = this.searchImagesByQueryHandler.bind(this); + this._searchInput.on_click["lesionWindow__fa-times"] = () => { + if (this._searchInput.getValue() !== "") { + this._searchInput.setValue(""); + this._searchInput.callEvent("onEnter"); + } + }; + this._searchInput.attachEvent("onEnter", this.searchImagesByQueryHandler.bind(this)); + this._searchInput.attachEvent("onViewShow", this.searchImagesByQueryHandler.bind(this)); + this._searchInput.attachEvent("onAfterRender", () => { + const inputNode = this._searchInput.$view.getElementsByClassName("webix_el_box")[0]; + const tooltipText = "Clear search value"; + searchButtonModel.createTimesSearchButton( + this._searchInput, + null, + inputNode, + tooltipText, + true + ); + }); + + const navButtonClickHandler = util.debounce(async (navigate) => { + let url = navigate === MOVE.prev + ? lesionWindowImagesUrls.getPrevImagesUrl() + : lesionWindowImagesUrls.getNextImagesUrl(); + if (url) { + this._topSlider.showProgress(); + lesionWindowImagesUrls.setCurrImagesUrl(url); + const result = await ajax.getImagesByUrl(url); + lesionWindowImagesUrls.setPrevImagesUrl(result.previous); + lesionWindowImagesUrls.setNextImagesUrl(result.next); + this._topSlider.clearAll(); + this._topSlider.parse(result.results); + this._topSlider.hideProgress(); + } + }, 100); + this._prevPageButton.attachEvent("onItemClick", navButtonClickHandler.bind(this, MOVE.prev)); + this._nextPageButton.attachEvent("onItemClick", navButtonClickHandler.bind(this, MOVE.next)); + + const clearWindow = () => { + this._leftSlider.clearAll(); + this._rightSlider.clearAll(); + this._topSlider.clearAll(); + this._searchInput.setValue(""); + if (this._window.config.fullscreen) { + this.changeWindowMode(); + } + }; + this._window.attachEvent("onHide", clearWindow); + this._window.attachEvent("onShow", this.searchImagesByQueryHandler.bind(this)); + this._window.attachEvent("onShow", () => { + this._leftGroup.setValue(constants.MULTI_LESION_GROUP_BY.TIME); + this._rightGroup.setValue(constants.MULTI_LESION_GROUP_BY.TIME); + const leftImage = lesionsModel.getLeftImage(); + const leftLesionId = lesionsModel.getItemLesionID(leftImage); + const LeftLesionImages = lesionsModel.getLesionImages(leftLesionId); + const leftType = this._leftGroup.getValue(); + const leftImagesGroups = this.groupImages(LeftLesionImages, leftType); + if (leftImagesGroups.length === 1) { + this._leftGroup.setValue(constants.MULTI_LESION_GROUP_BY.TYPE); + this._leftGroup.refresh(); + } + + const rightImage = lesionsModel.getRightImage(); + const rightLesionId = lesionsModel.getItemLesionID(rightImage); + const rightLesionImages = lesionsModel.getLesionImages(rightLesionId); + const rightType = this._rightGroup.getValue(); + const rightImagesGroups = this.groupImages(rightLesionImages, rightType); + if (rightImagesGroups.length === 1) { + this._rightGroup.setValue(constants.MULTI_LESION_GROUP_BY.TYPE); + this._rightGroup.refresh(); + } + + if (this._topPanel.isVisible()) { + this._collapseButton.hide(); + this._topPanel.hide(); + this._expandButton.show(); + } + }); + + const leftGroupByHandler = () => { + const leftImage = lesionsModel.getLeftImage(); + this.fillLeftPanel(leftImage); + }; + + this._leftGroup.attachEvent("onChange", leftGroupByHandler.bind(this)); + + const rightGroupByHandler = () => { + const rightImage = lesionsModel.getRightImage(); + this.fillRightPanel(rightImage); + }; + + this._rightGroup.attachEvent("onChange", rightGroupByHandler.bind(this)); + + this._rightSlider.attachEvent("onBeforeDrop", () => false); + this._topSlider.attachEvent("onBeforeDrop", () => false); + this._leftSlider.attachEvent("onBeforeDrop", () => false); + const leftImageView = this._leftImage.$view; + const rightImageView = this._rightImage.getNode(); + webix.DragControl.addDrop(leftImageView, {$drop: (/* source, target, event */) => { + const dnd = webix.DragControl.getContext(); + const item = dnd.from.getItem(dnd.start); + const image = item.firstImage ?? item; + const lesionID = lesionsModel.getItemLesionID(image); + if (lesionID) { + lesionsModel.setLeftImage(image); + this.fillLeftPanel(image); + } + else { + webix.message("There are no lesions attached to this image"); + } + }}); + webix.DragControl.addDrop(rightImageView, {$drop: (/* source, target, event */) => { + const dnd = webix.DragControl.getContext(); + const item = dnd.from.getItem(dnd.start); + const image = item.firstImage ?? item; + const lesionID = lesionsModel.getItemLesionID(image); + if (lesionID) { + lesionsModel.setRightImage(image); + this.fillRightPanel(image); + } + else { + webix.message("There are no lesions attached to this image"); + } + }}); + + const changeImage = (side, move) => { + if (side === constants.MULTI_LESION_SIDE.LEFT) { + const currentImage = lesionsModel.getLeftImage(); + const newImage = move === MOVE.prev + ? lesionsModel.getPrevLeftImage(currentImage) + : lesionsModel.getNextLeftImage(currentImage); + if (newImage) { + this.fillLeftPanel(newImage); + } + } + if (side === constants.MULTI_LESION_SIDE.RIGHT) { + const currentImage = lesionsModel.getRightImage(); + const newImage = move === MOVE.prev + ? lesionsModel.getPrevRightImage(currentImage) + : lesionsModel.getNextRightImage(currentImage); + if (newImage) { + this.fillRightPanel(newImage); + } + } + }; + + this._leftImage.define("onClick", { + prev: () => { + changeImage(constants.MULTI_LESION_SIDE.LEFT, MOVE.prev); + }, + next: () => { + changeImage(constants.MULTI_LESION_SIDE.LEFT, MOVE.next); + } + }); + + this._rightImage.define("onClick", { + prev: () => { + changeImage(constants.MULTI_LESION_SIDE.RIGHT, MOVE.prev); + }, + next: () => { + changeImage(constants.MULTI_LESION_SIDE.RIGHT, MOVE.next); + } + }); + + this._topSlider.on_click["resize-icon"] = (e, id) => { + const currentItem = this._topSlider.getItem(id); + this._galleryService._imageWindowTemplateWithoutControls?.hide(); + this._galleryService._imageWindowTemplate?.show(); + this._galleryService._setImageWindowValues(currentItem); + if (this._galleryService._imageWindow) { + this._galleryService._eventForHideMessages(this._galleryService._imageWindow); + this._galleryService._imageWindow.show(); + } + }; + + this._topSlider.on_click["info-icon"] = async (e, id) => { + try { + const currentItem = this._topSlider.getItem(id); + const image = await ajax.getImageItem(currentItem.isic_id); + if (this._galleryService._metadataWindowMetadata) { + webix.ui([metadataPart.getConfig("metadata-window-metadata", image, currentItem)], this._galleryService._metadataWindowMetadata); // [] - because we rebuild only rows of this._imageWindowMetadata + } + else { + webix.ui([metadataPart.getConfig("metadata-window-metadata", image, currentItem)]); // [] - because we rebuild only rows of this._imageWindowMetadata + } + if (this._galleryService._metadataWindow) { + this._galleryService._eventForHideMessages(this._galleryService._metadataWindow); + this._galleryService._metadataWindow.show(); + } + } + catch (error) { + if (!this._galleryService._view.$destructed) { + webix.message("ShowMetadata: Something went wrong"); + } + } + }; + + this._topSlider.on_click["diagnosis-icon"] = (e, id) => { + const currentItem = this._topSlider.getItem(id); + const url = `${constants.URL_MULTIRATER}?id=${currentItem.isic_id}&sid=${currentItem.studyId}&uid=${authService.getToken()}`; + util.openInNewTab(url); + }; + + this._topSlider.on_click["batch-icon"] = async (e, id) => { + const currentItem = this._topSlider.getItem(id); + const currentItemId = currentItem.isic_id; + const url = await ajax.getDownloadUrl( + constants.DOWNLOAD_ZIP_SINGLE_IMAGE, + `isic_id:${currentItemId}`, + currentItemId + ); + if (url) { + util.downloadByLink(url, `${currentItemId}.zip`); + } + }; + + this._topSlider.on_click["layer-group"] = (e, id) => { + if (this._window) { + const currentItem = this._topSlider.getItem(id); + this.setMultiLesionState( + currentItem, + ); + } + }; + + this._collapseButton.attachEvent("onViewShow", () => { + this._searchInput.show(); + }); + + this._expandButton.attachEvent("onViewShow", () => { + this._searchInput.hide(); + }); + + webix.extend(this._topSlider, webix.ProgressBar); + } + + async ready() { + const data = await ajax.getLesions(); + if (data) { + lesionsModel.setLesions(data.results); + } + } + + async updateImage(obj) { + if (typeof galleryImagesUrls.getNormalImageUrl(obj?.isic_id) === "undefined") { + if (obj?.isic_id) { + const item = await ajax.getImageItem(obj.isic_id); + galleryImagesUrls.setNormalImageUrl(obj.isic_id, item.files.full.url); + this.refresh(); + } + } + return true; + } + + setMultiLesionState(item) { + lesionsModel.setCurrentItem(item); + const lesionID = lesionsModel.getItemLesionID(item); + const lesionImages = webix.copy(lesionsModel.getLesionImages(lesionID)); + const itemID = lesionsModel.getItemID(item); + const anchorImageID = lesionsModel.getAnchorImageID(lesionID); + this._rightContainer.show(); + let rightGroupType = this._rightGroup.getValue(); + let imageForRightPanel; + if (itemID === anchorImageID) { + imageForRightPanel = lesionsModel.getFirstNonAnchorImage(lesionID, anchorImageID); + } + else { + imageForRightPanel = item; + } + lesionsModel.setRightImage(imageForRightPanel); + if (rightGroupType === "") { + this._rightGroup.setValue(constants.MULTI_LESION_GROUP_BY.TIME); + rightGroupType = this._rightGroup.getValue(); + } + this.fillRightPanel(imageForRightPanel); + + let leftGroupType = this._leftGroup.getValue(); + if (leftGroupType === "") { + this._leftGroup.setValue(constants.MULTI_LESION_GROUP_BY.TYPE); + leftGroupType = this._leftGroup.getValue(); + } + const anchorImg = lesionImages.find(i => lesionsModel.getItemID(i) === anchorImageID); + this.fillLeftPanel(anchorImg); + } + + parseImages(item, lesionID, isRightSide) { + const lesionImages = lesionsModel.getLesionImages(lesionID); + if (isRightSide === true) { + let rightGroupType = this._rightGroup.getValue(); + const rightImagesGroups = this.groupImages(lesionImages, rightGroupType); + const sortedImages = this.sortImages(rightImagesGroups, rightGroupType, item); + lesionsModel.setCurrentRightImages(sortedImages); + this._rightSlider.clearAll(); + this._rightSlider.parse(rightImagesGroups); + this._rightSlider.unselectAll(); + const groupToSelect = this.findItemToSelect(rightImagesGroups, item); + this._rightSlider.select(groupToSelect.id); + } + else { + let leftGroupType = this._leftGroup.getValue(); + const leftImagesGroups = this.groupImages(lesionImages, leftGroupType); + const sortedImages = this.sortImages(leftImagesGroups, leftGroupType, item); + lesionsModel.setCurrentLeftImages(sortedImages); + this._leftSlider.clearAll(); + this._leftSlider.parse(leftImagesGroups); + this._leftSlider.unselectAll(); + const groupToSelect = this.findItemToSelect(leftImagesGroups, item); + this._leftSlider.select(groupToSelect.id); + } + } + + changeWindowMode() { + if (this._fullscreen) { + this._fullscreen = false; + this._window.define("width", 1240); + this._window.define("height", 750); + this._window.define("position", "center"); + this._fullScreenButton.show(); + this._windowedButton.hide(); + } + else { + this._fullscreen = true; + this._window.define("width", window.innerWidth); + this._window.define("height", window.innerHeight); + this._window.define("position", "center"); + this._fullScreenButton.hide(); + this._windowedButton.show(); + } + this.searchImagesByQueryHandler(); + } + + async searchImagesByQueryHandler() { + let searchValue = this._searchInput.getValue().trim(); + this._searchInput.setValue(searchValue); + let filteredImages = []; + const filter = searchValue !== "" + ? this.buildCondition(searchValue) + : null; + const limit = Math.ceil(this._topSlider.$width / 174) - 1; + this._topSlider.define("type", {width: this._topSlider.$width / limit, height: 104}); + const sourceParams = { + limit, + filter + }; + this._topSlider.showProgress(); + let foundImages = {}; + try { + foundImages = await ajax.getImages(sourceParams); + } + catch (e) { + this._topSlider.hideProgress(); + return; + } + lesionWindowImagesUrls.setPrevImagesUrl(foundImages?.previous); + lesionWindowImagesUrls.setNextImagesUrl(foundImages?.next); + try { + const allImagesArray = webix.copy(foundImages?.results ?? []); + const foundImagesCount = foundImages.count ?? 0; + allImagesArray.forEach((imageObj) => { + filteredImages.push(imageObj); + }); + if (foundImagesCount > 0) { + this._topSlider.clearAll(); + this._topSlider.parse(filteredImages); + } + else { + webix.alert(`Image with name "${searchValue}" was not found`); + } + this._topSlider.hideProgress(); + } + catch (error) { + if (!this._view.$destructed) { + webix.alert(`Image with name "${searchValue}" was not found`); + webix.message("Search Images: Something went wrong"); + this._topSlider.hideProgress(); + } + } + } + + async getImagesByUrl(url) { + const result = ajax.getImagesByUrl(url); + lesionWindowImagesUrls.setCurrImagesUrl(url); + lesionWindowImagesUrls.setNextImagesUrl(url); + lesionWindowImagesUrls.setPrevImagesUrl(url); + return result.results; + } + + fillRightPanel(image) { + lesionsModel.setRightImage(image); + this._rightFooter.parse(image); + this.setAnchorIcon(image, constants.MULTI_LESION_SIDE.RIGHT); + const imageID = lesionsModel.getItemID(image); + this._rightImageLabel.define("label", imageID.toUpperCase()); + this._rightImageLabel.refresh(); + const lesionID = lesionsModel.getItemLesionID(image); + this.parseImages(image, lesionID, true); + this._rightImage.parse(image); + } + + fillLeftPanel(image) { + lesionsModel.setLeftImage(image); + this._leftFooter.parse(image); + this.setAnchorIcon(image, constants.MULTI_LESION_SIDE.LEFT); + const imageID = lesionsModel.getItemID(image); + this._leftImageLabel.define("label", imageID.toUpperCase()); + this._leftImageLabel.refresh(); + const lesionID = lesionsModel.getItemLesionID(image); + this.parseImages(image, lesionID, false); + this._leftImage.parse(image); + } + + buildCondition(filter) { + const searchValues = filter.split(" OR "); + const conditions = searchValues.map((str) => { + const values = str.split(" AND "); + return values.map((v) => { + if (v.includes(":")) { + return v; + } + return `isic_id:${v}`; + }).join(" AND "); + }); + return conditions.join(" OR "); + } + + groupImages(images, type) { + let imagesGroupsObj; + switch (type) { + case constants.MULTI_LESION_GROUP_BY.TIME: + imagesGroupsObj = lesionsModel.groupByTimePoint(images); + break; + case constants.MULTI_LESION_GROUP_BY.TYPE: + imagesGroupsObj = lesionsModel.groupByModality(images); + break; + case constants.MULTI_LESION_GROUP_BY.COMBINATION: + imagesGroupsObj = lesionsModel.groupByTimePointAndModality(images); + break; + default: + imagesGroupsObj = lesionsModel.groupByID(images); + } + const imagesGroupsObjectKeys = Object.keys(imagesGroupsObj); + const result = imagesGroupsObjectKeys.map((k) => { + const imgsGroup = { + firstImage: imagesGroupsObj[k][0], + groupBy: type, + groupValue: k, + images: imagesGroupsObj[k], + }; + return imgsGroup; + }); + return result; + } + + sortImages(imagesGroups, groupType, item) { + let images; + if (groupType === constants.MULTI_LESION_GROUP_BY.NO_GROUP) { + images = imagesGroups.map(g => g.images[0]); + imagesGroups.sort((a, b) => { + const aValue = lesionsModel.getItemID(a.images[0]); + const bValue = lesionsModel.getItemID(b.images[0]); + const result = aValue.localeCompare(bValue); + return result; + }); + } + else { + switch (groupType) { + case constants.MULTI_LESION_GROUP_BY.TIME: + imagesGroups.sort((a, b) => { + const aValue = a.groupValue; + const bValue = b.groupValue; + return Number(aValue) - Number(bValue); + }); + break; + case constants.MULTI_LESION_GROUP_BY.TYPE: + imagesGroups.sort((a, b) => { + const aValue = a.groupValue; + if (aValue === constants.MULTI_LESION_TYPE_PRIORITY.FIRST) { + return -1; + } + const bValue = b.groupValue; + if (bValue === constants.MULTI_LESION_TYPE_PRIORITY.FIRST) { + return 1; + } + return aValue.localeCompare(bValue); + }); + break; + case constants.MULTI_LESION_GROUP_BY.COMBINATION: + imagesGroups.sort((a, b) => { + const aValue = a.groupValue; + const bValue = b.groupValue; + return aValue.localeCompare(bValue); + }); + break; + default: + imagesGroups.sort((a, b) => { + const aValue = a.value; + const bValue = b.value; + return aValue.localeCompare(bValue); + }); + } + const currentImagesGroup = imagesGroups.find((g) => { + const found = g.images + .find(i => lesionsModel.getItemID(item) === lesionsModel.getItemID(i)); + return found; + }); + images = [...currentImagesGroup.images]; + } + if (images.length === 1) { + return images; + } + const sortedImages = images.sort((a, b) => { + let result; + let aValue; + let bValue; + switch (groupType) { + case constants.MULTI_LESION_GROUP_BY.TIME: + aValue = lesionsModel.getItemModality(a); + if (aValue === constants.MULTI_LESION_TYPE_PRIORITY.FIRST) { + result = -1; + } + else { + bValue = lesionsModel.getItemModality(b); + if (bValue === constants.MULTI_LESION_TYPE_PRIORITY.FIRST) { + result = 1; + } + else { + result = aValue.localeCompare(bValue); + } + if (result === 0) { + aValue = lesionsModel.getItemID(a); + bValue = lesionsModel.getItemID(b); + result = aValue.localeCompare(bValue); + } + } + break; + case constants.MULTI_LESION_GROUP_BY.TYPE: + aValue = lesionsModel.getItemTimePoint(a); + bValue = lesionsModel.getItemTimePoint(b); + result = Number(aValue) - Number(bValue); + if (result === 0) { + aValue = lesionsModel.getItemID(a); + bValue = lesionsModel.getItemID(b); + result = aValue.localeCompare(bValue); + } + break; + case constants.MULTI_LESION_GROUP_BY.COMBINATION: + aValue = lesionsModel.getItemID(a); + bValue = lesionsModel.getItemID(b); + result = aValue.localeCompare(bValue); + break; + default: + aValue = lesionsModel.getItemID(a); + bValue = lesionsModel.getItemID(b); + result = aValue.localeCompare(bValue); + } + return result; + }); + return sortedImages; + } + + setAnchorIcon(image, side) { + if (lesionsModel.checkIsImageAnchor(image)) { + switch (side) { + case constants.MULTI_LESION_SIDE.LEFT: + this._leftAnchorIcon.show(); + break; + case constants.MULTI_LESION_SIDE.RIGHT: + this._rightAnchorIcon.show(); + break; + default: + break; + } + } + else { + switch (side) { + case constants.MULTI_LESION_SIDE.LEFT: + this._leftAnchorIcon.hide(); + break; + case constants.MULTI_LESION_SIDE.RIGHT: + this._rightAnchorIcon.hide(); + break; + default: + break; + } + } + } + + findItemToSelect(groups, image) { + const groupToSelect = groups.find((obj) => { + if (obj.images) { + if (obj.images.find(i => lesionsModel.getItemID(i) === lesionsModel.getItemID(image))) { + return true; + } + return false; + } + return lesionsModel.getItemID(obj) === lesionsModel.getItemID(image); + }); + return groupToSelect; + } +} diff --git a/sources/services/gallery/searchButtonModel.js b/sources/services/gallery/searchButtonModel.js index 09d6fe7..75d699b 100644 --- a/sources/services/gallery/searchButtonModel.js +++ b/sources/services/gallery/searchButtonModel.js @@ -24,7 +24,7 @@ function createHintForSearchTimesButton(elementNodeForTooltip, tooltipClassName, const linkProps = this.getBoundingClientRect(); const tooltipProps = tooltipWrap.getBoundingClientRect(); const topPos = linkProps.top - (tooltipProps.height + padding); - tooltipWrap.setAttribute("style", `top:${topPos}px;left:${linkProps.left}px;`); + tooltipWrap.setAttribute("style", `top:${topPos}px;left:${linkProps.left}px;z-index: 1010`); // z-index: 1010 to show tooltip in modal window background } elementNodeForTooltip.addEventListener("mouseover", mouseOverHandler); elementNodeForTooltip.addEventListener("mouseout", () => { @@ -39,11 +39,12 @@ function createTimesSearchButton( tooltipText, nameFilter ) { + const className = `clear-input-${webix.uid()}`; inputNode.lastChild.style.paddingRight = "26px"; const timesSpan = document.createElement("span"); inputNode.appendChild(timesSpan); const timesButtonNode = inputNode.lastChild; - timesButtonNode.setAttribute("class", "search-times-button webix_input_icon fas fa-times"); + timesButtonNode.setAttribute("class", `search-times-button webix_input_icon fas fa-times ${className}`); timesButtonNode.setAttribute("style", "height:26px; padding-top:6px;"); const tootipTextForTimesButton = `${tooltipText}`; const tooltipClassNameForTimesButton = "tooltip"; @@ -52,10 +53,12 @@ function createTimesSearchButton( tooltipClassNameForTimesButton, tootipTextForTimesButton ); - searchInput.on_click["fa-times"] = () => { + searchInput.on_click[className] = () => { if (searchInput.getValue() !== "") { searchInput.setValue(""); - appliedFilterModel.setFilterValue(""); + if (appliedFilterModel) { + appliedFilterModel?.setFilterValue(""); + } if (nameFilter) { searchInput.callEvent("onEnter"); } diff --git a/sources/styles/common.less b/sources/styles/common.less index 525e157..be49af4 100644 --- a/sources/styles/common.less +++ b/sources/styles/common.less @@ -250,6 +250,54 @@ a, .link { } } +.collapser-vertical { + .box-shadow2(0 1px 4px 0 rgba(0,0,0,0.12), 0 2px 4px 0 rgba(0,0,0,0.04)); + + .collapser-btn { + border: none; + cursor: pointer; + .webix_template { + position: relative; + } + .webix_icon { + font-weight: 600; + text-align: center; + position: absolute; + left: 50%; + } + } +} + +/* gallery-images-badge */ +.gallery-images-badge { + background-color: #3C87CB; + font-size: 8px; + line-height: 10px; + text-align: center; + color: #FFFFFF; + border: 1px; + border-radius: 10px; + height: 13px; + width: 13px; + padding: 2px; + text-align: center; + position: absolute; + right: -5px; + top: -5px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +.gallery-images-badge_1 { + top: -5px; + right: -5px; +} + +.gallery-images-badge_2 { + top: -5px; + right: 13px; +} /* tooltip */ .tooltip-container { diff --git a/sources/styles/pages.less b/sources/styles/pages.less index 8e40465..f303d83 100644 --- a/sources/styles/pages.less +++ b/sources/styles/pages.less @@ -1215,6 +1215,34 @@ border-radius: 5px; } +.gallery-image.framed-image, .gallery-image.framed-image2, .gallery-image.framed-image3 { + position: absolute +} + +.gallery-image.framed-image { + top: -13px; + left: -10px; + background: transparent; + z-index: 2; + border: 1px solid black; + box-shadow: 2px 2px rgb(100, 100, 100); +} + +.gallery-image.framed-image2 { + top: -8px; + left: -5px; + z-index: 1; + border: 1px solid black; + box-shadow: 2px 2px rgb(100, 100, 100); +} + +.gallery-image.framed-image3 { + top: 0px; + left: 0px; + z-index: 0; + filter: blur(2px) +} + /* gallery-images-dataview-mobile */ .gallery-images-dataview-mobile { .webix_dataview_item { @@ -1335,13 +1363,19 @@ width: 100%; } -.gallery-images-button-elem { +.gallery-images-button-elem, .gallery-images-button-elem-disabled { display: inline-block; vertical-align: top; width: 22px; height: 22px; margin: 0 4px; } +.gallery-images-button-elem-disabled { + pointer-events: none; +} +.gallery-images-button-elem-disabled:active { + pointer-events: none; +} .gallery-images-button { display: block; line-height: 0; diff --git a/sources/styles/popups.less b/sources/styles/popups.less index 00cbab1..8f2129a 100644 --- a/sources/styles/popups.less +++ b/sources/styles/popups.less @@ -12,6 +12,17 @@ .box-sizing(border-box); padding-left: 14px; } + + .window-header-toolbar2 { + .window-header-toolbar2__title { + .webix_template { + display: flex; + .window-header-toolbar-text_title { + align-self: center; + } + } + } + } } .window-close-button { @@ -188,6 +199,17 @@ display: flex; align-items: center; justify-content: center; + user-select: none; + height: 100%; + .zoomable-image { + max-height:100%; + max-width:100%; + object-fit: contain; + } +} + +.prev, .next { + user-select: none; } /*========== Metadata popup =============*/ @@ -222,3 +244,230 @@ .webix_modal_box { font-family: @main-font; } + +/*========= Multi image lesion window =========*/ +.multi-image-lesion-search-block { + display: flex; + align-content: center; +} +.multi-image-lesion-window-body { + .lesion-controls { + background-color: #E1E7F0; + } + .left-image-panel-container, .right-image-panel-container { + position: relative; + .absolute-centered-image-template { + position: absolute; + top: 0; + left: 0; + .next, .prev { + border-radius: 50%; + background: rgba(255, 255, 255, 0.3); + padding: 0; + width: 40px; + height: 40px; + padding: 0px; + text-align: center; + line-height: 40px; + } + } + .vertical-slider-left, .vertical-slider-right { + position: absolute; + border: none; + z-index: 10; + top: 0px; + } + // scroll start + --sb-track-color: #ffffff; + --sb-thumb-color: #bbc0cb; + --sb-size: 8px; + .vertical-slider-left::-webkit-scrollbar, .vertical-slider-right::-webkit-scrollbar { + width: var(--sb-size); + } + .vertical-slider-left::-webkit-scrollbar-track, .vertical-slider-right::-webkit-scrollbar-track { + background: var(--sb-track-color); + border-radius: 3px; + } + .vertical-slider-left::-webkit-scrollbar-thumb, .vertical-slider-right::-webkit-scrollbar-thumb { + background: var(--sb-thumb-color); + border-radius: 3px; + border: 1px solid #ffffff; + } + @supports not selector(::-webkit-scrollbar) { + .vertical-slider-left, .vertical-slider-right { + scrollbar-color: var(--sb-thumb-color) + var(--sb-track-color); + opacity: 0.1; + } + } + // scroll end + .vertical-slider-left { + right: 10; + } + + .vertical-slider-right { + left: 10 + } + } + + .left-image-panel-container { + .absolute-centered-image-template { + .next { + right: 90px; + } + .prev { + left: 10px; + } + } + } + + .right-image-panel-container { + .absolute-centered-image-template { + .prev { + left: 90px; + } + .next { + right: 10px; + } + } + } + + .container { + position: relative; + .vertical-slider-left, .vertical-slider-right { + position: absolute; + border: none; + z-index: 10; + top: 0px; + width: 80px !important; + background: transparent; + .webix_list_item.webix_selected { + border-color: #46C03C; + } + .webix_list_item{ + background-color: white; + margin-top: 5px; + margin-bottom: 5px; + padding-left: 0px; + padding-right: 0px; + padding-top: 0px; + border: solid 2px white; + .ribbon-image-elem { + position: absolute; + width: 15px; + height: 15px; + bottom: 0px; + right: 5px; + border-radius: 7.5px; + background: rgba(0, 0, 0, 0.3); + .gallery-images-button { + position: absolute; + top: 7.5px; + left: 2px; + width: 10px; + height: 10px; + background: transparent; + border: none; + font-size: 10px; + padding: 0px; + color: white; + } + } + } + } + // scroll start + --sb-track-color: transparent; + --sb-thumb-color: transparent; + --sb-size: 8px; + .vertical-slider-left::-webkit-scrollbar, .vertical-slider-right::-webkit-scrollbar { + width: var(--sb-size); + } + .vertical-slider-left::-webkit-scrollbar-track, .vertical-slider-right::-webkit-scrollbar-track { + background: var(--sb-track-color); + border-radius: 0px; + } + .vertical-slider-left::-webkit-scrollbar-thumb, .vertical-slider-right::-webkit-scrollbar-thumb { + background: var(--sb-thumb-color); + border-radius: 3px; + border: 1px solid transparent; + } + @supports not selector(::-webkit-scrollbar) { + .vertical-slider-left, .vertical-slider-right { + scrollbar-color: var(--sb-thumb-color) + var(--sb-track-color); + } + } + // scroll end + .vertical-slider-left { + right: 10px; + } + + .vertical-slider-right { + left: 10px; + } + + .multi-image-lesion-window__image-label { + font: Roboto; + font-weight: 500; + font-size: 14px; + line-height: 16px; + } + + .footer-template { + border: 0px; + .webix_template { + display: flex; + align-items: center; + .footer-container-wide { + width: 100%; + display: grid; + grid-template-columns: auto auto auto auto; + .footer-item { + min-width: 240px; + .footer-item__name { + color: #6F747F; + } + .footer-item__value { + color: black; + } + } + } + .footer-container-narrow{ + min-width: 435px; + width: calc(100%); + display: grid; + grid-template-columns: auto auto; + grid-template-rows: auto auto; + overflow: hidden; + .footer-item { + overflow: hidden; + min-width: 200px; + padding-bottom: 5px; + .footer-item__name { + color: #6F747F; + min-width: 210px; + } + .footer-item__value { + color: black; + } + } + } + } + } + + } + .multilesion-filter-dropdown { + .webix_inp_static { + border: none; + padding-left: 0px; + } + .webix_input_icon.wxi-menu-down, .webix_input_icon.wxi-menu-down:focus { + outline: none; + color: @main-color-light; + background-color: transparent; + } + } + .multilesion-top-list { + border: none; + } +} diff --git a/sources/views/components/collapser.js b/sources/views/components/collapser.js index 37cce08..d30b0ea 100644 --- a/sources/views/components/collapser.js +++ b/sources/views/components/collapser.js @@ -19,21 +19,103 @@ function changeDataviewItemDimensions(collapsedView) { function getConfig(collapsedViewId, config) { const BTN_CLOSED_STATE_ID = `collapser-btn-closed-${webix.uid()}`; const BTN_OPENED_STATE_ID = `collapser-btn-opened-${webix.uid()}`; + let openedSpan; + let closedSpan; + switch (config.type) { + case "left": + openedSpan = ""; + closedSpan = ""; + break; + case "right": + openedSpan = ""; + closedSpan = ""; + break; + case "top": + openedSpan = ""; + closedSpan = ""; + break; + case "bottom": + openedSpan = ""; + closedSpan = ""; + break; + default: + openedSpan = ""; + closedSpan = ""; + } + if (config.type === "top" || config.type === "bottom") { + return { + css: "collapser-vertical", + height: 23, + cols: [ + { + view: "template", + template: openedSpan, + css: "collapser-btn", + id: BTN_OPENED_STATE_ID, + state: "wasOpened", + hidden: config && config.closed, + onClick: { + // eslint-disable-next-line func-names + "collapser-btn": function (thisButton) { + const thisCollapsedButton = this?.config ? this : thisButton; + const collapsedView = $$(collapsedViewId); + collapsedView.hide(); + thisCollapsedButton.hide(); + $$(BTN_CLOSED_STATE_ID).show(); + webix.ui.resize(); + if (collapsedViewId === constants.ID_GALLERY_RIGHT_PANEL) { + util.setHiddenGalleryCartList(true); + } + else if (collapsedViewId === constants.ID_GALLERY_LEFT_PANEL) { + util.setHiddenGalleryLeftPanel(true); + } + changeDataviewItemDimensions(collapsedView); + } + } + }, + { + view: "template", + template: closedSpan, + css: "collapser-btn", + id: BTN_CLOSED_STATE_ID, + state: "wasClosed", + hidden: !(config && config.closed), + onClick: { + // eslint-disable-next-line func-names + "collapser-btn": function (thisButton) { + const thisCollapsedButton = this?.config ? this : thisButton; + const collapsedView = $$(collapsedViewId); + collapsedView.show(); + thisCollapsedButton.hide(); + $$(BTN_OPENED_STATE_ID).show(); + webix.ui.resize(); + if (collapsedViewId === constants.ID_GALLERY_RIGHT_PANEL) { + util.setHiddenGalleryCartList(false); + } + else if (collapsedViewId === constants.ID_GALLERY_LEFT_PANEL) { + util.setHiddenGalleryLeftPanel(false); + } + changeDataviewItemDimensions(collapsedView); + } + } + } + ] + }; + } return { css: "collapser", width: 23, rows: [ { view: "template", - template: config && config.type === "left" ? - "" : - "", + template: openedSpan, css: "collapser-btn", id: BTN_OPENED_STATE_ID, state: "wasOpened", hidden: config && config.closed, onClick: { + // eslint-disable-next-line func-names "collapser-btn": function (thisButton) { const thisCollapsedButton = this?.config ? this : thisButton; const collapsedView = $$(collapsedViewId); @@ -53,14 +135,13 @@ function getConfig(collapsedViewId, config) { }, { view: "template", - template: config && config.type === "left" ? - "" : - "", + template: closedSpan, css: "collapser-btn", id: BTN_CLOSED_STATE_ID, state: "wasClosed", hidden: !(config && config.closed), onClick: { + // eslint-disable-next-line func-names "collapser-btn": function (thisButton) { const thisCollapsedButton = this?.config ? this : thisButton; const collapsedView = $$(collapsedViewId); diff --git a/sources/views/components/svgIcon.js b/sources/views/components/svgIcon.js new file mode 100644 index 0000000..7b6396a --- /dev/null +++ b/sources/views/components/svgIcon.js @@ -0,0 +1,27 @@ +webix.protoUI({ + name: "svgIcon", + $cssName: "icon", + defaults: { + template(obj, view) { + const min = Math.min(obj.awidth, obj.aheight); + const top = Math.round((view.$height - obj.aheight) / 2); + const inner = ``; + + const lineHeight = obj.aheight !== min ? obj.aheight : 0; + return `
${inner} + ${obj.tooltip} + ${obj.badge || obj.badge === 0 ? `${obj.badge}` : ""} +
`; + } + } +}, webix.ui.icon); + +function wrapIconWithSpan(elementClass, icon, active) { + return ` + + + + `; +} diff --git a/sources/views/subviews/gallery/gallery.js b/sources/views/subviews/gallery/gallery.js index 6d770c8..41bac56 100644 --- a/sources/views/subviews/gallery/gallery.js +++ b/sources/views/subviews/gallery/gallery.js @@ -4,9 +4,11 @@ import constants from "../../../constants"; import "../../components/activeList"; import galleryImagesUrls from "../../../models/galleryImagesUrls"; import selectedImages from "../../../models/selectedGalleryImages"; +import state from "../../../models/state"; import ajax from "../../../services/ajaxActions"; import authService from "../../../services/auth"; import GalleryService from "../../../services/gallery/gallery"; +import MultiLesionWindowService from "../../../services/gallery/multiimageLesionWindow"; import searchButtonModel from "../../../services/gallery/searchButtonModel"; import util from "../../../utils/util"; import collapser from "../../components/collapser"; @@ -17,6 +19,7 @@ import dataview from "./parts/galleryDataview"; import pager from "./parts/galleryPager"; import imageWindow from "./windows/imageWindow"; import metadataWindow from "./windows/metadataWindow"; +import multiImageLesionWindow from "./windows/multiImageLesionWindow"; const ID_PAGER = "gallery-pager-id"; const ID_DATAVIEW = "gallery-dataview-id"; @@ -143,11 +146,11 @@ export default class GalleryView extends JetView { template(obj) { const rangeHtml = `Shown images: ${obj.rangeStart || ""}-${obj.rangeFinish || ""}.`; const totalAmountHtml = `Total amount of images: ${obj.totalCount || ""}.`; - const filteredAmountHtml = `Filtered images: ${obj.currentCount || 0}`; + const filteredAmountHtml = `Filtered images: ${state.filteredImages.filteredImagesCount || 0}`; let result = ""; if (obj.filtered) { result = ` ${filteredAmountHtml} ${totalAmountHtml}`; - if (obj.rangeFinish - obj.rangeStart < obj.currentCount) { + if (obj.rangeFinish - obj.rangeStart < state.filteredImages.filteredImagesCount) { result = `${rangeHtml} ${result}`; } } @@ -326,6 +329,10 @@ export default class GalleryView extends JetView { null, this.removeParam.bind(this) )); + this.multiImageLesionWindow = this.ui(multiImageLesionWindow.getConfig( + "MULTI-IMAGE LESION CASE STUDY", + () => {} + )); this.metadataWindow = this.ui(metadataWindow.getConfig(ID_METADATA_WINDOW)); const contextMenuConfig = contextMenu.getConfig(ID_GALLERY_CONTEXT_MENU); this.galleryContextMenu = this.ui(contextMenuConfig); @@ -354,6 +361,7 @@ export default class GalleryView extends JetView { $$(imageWindow.getMetadataLayoutId()), this.metadataWindow, $$(metadataWindow.getMetadataLayoutId()), + this.multiImageLesionWindow, filtersForm, this.getAppliedFiltersList(), this.imagesSelectionTemplate, @@ -376,6 +384,12 @@ export default class GalleryView extends JetView { null, // portraitClearAllFiltersTemplate null // landscapeClearAllFiltersTemplate ); + + // multi lesion + this._multiImageLesionService = new MultiLesionWindowService( + this._galleryService + ); + this._multiImageLesionService.ready(); } async ready() { @@ -666,4 +680,8 @@ export default class GalleryView extends JetView { this.setParam("image", "", true); } } + + setMultiLesionMode(item) { + this._multiImageLesionService.setMultiLesionState(item); + } } diff --git a/sources/views/subviews/gallery/parts/filterPanel.js b/sources/views/subviews/gallery/parts/filterPanel.js index 3d8aa01..4384a2e 100644 --- a/sources/views/subviews/gallery/parts/filterPanel.js +++ b/sources/views/subviews/gallery/parts/filterPanel.js @@ -36,7 +36,7 @@ function getConfig(config) { const searchField = { view: "search", - icon: "fas fa-search", + icon: "fas fa-search gallery-search-filter", id: ID_SEARCH_FIELD, name: NAME_SEARCH_FIELD, value: `${appliedFiltersModel.getFilterValue()}`, diff --git a/sources/views/subviews/gallery/parts/galleryDataview.js b/sources/views/subviews/gallery/parts/galleryDataview.js index 05af097..bddc71a 100644 --- a/sources/views/subviews/gallery/parts/galleryDataview.js +++ b/sources/views/subviews/gallery/parts/galleryDataview.js @@ -1,6 +1,7 @@ import "../../../components/activeDataview"; import constants from "../../../../constants"; import galleryImageUrl from "../../../../models/galleryImagesUrls"; +import lesionsModel from "../../../../models/lesionsModel"; import selectedImages from "../../../../models/selectedGalleryImages"; import state from "../../../../models/state"; import util from "../../../../utils/util"; @@ -89,12 +90,20 @@ const dataview = { datathrottle: 500, onContext: {}, template(obj, common) { + const lesionID = lesionsModel.getItemLesionID(obj); + const lesionModalitiesCount = lesionID + ? lesionsModel.getLesionModalitiesCount(lesionID) + : null; + const lesionTimePointsCount = lesionID + ? lesionsModel.getLesionTimePointsCount(lesionID) + : null; const imageIconDimensions = util.getImageIconDimensions(); let flagForStudies = selectedImages.getStudyFlag(); // TODO check this when if study works if (flagForStudies) { // eslint-disable-next-line no-use-before-define let dataviewConfig = $$(getIdFromConfig()); + // TODO: find out why we use find // eslint-disable-next-line array-callback-return dataviewConfig.find((config) => { if (selectedImages.isSelectedInStudies(config.isic_id)) { @@ -112,6 +121,21 @@ const dataview = { Multirater ` : ""; + const lesionIconElementClass = lesionID + ? "gallery-images-button-elem" + : "gallery-images-button-elem-disabled"; + const lesionIcon = ``; const starHtml = obj.hasAnnotations ? "" : ""; if (typeof galleryImageUrl.getPreviewImageUrl(obj.isic_id) === "undefined") { galleryImageUrl.setPreviewImageUrl( @@ -151,6 +175,7 @@ const dataview = { Download ZIP + ${lesionIcon} ${diagnosisIcon} diff --git a/sources/views/subviews/gallery/parts/mobileFilterPanel.js b/sources/views/subviews/gallery/parts/mobileFilterPanel.js index 7391d44..a7c5bb0 100644 --- a/sources/views/subviews/gallery/parts/mobileFilterPanel.js +++ b/sources/views/subviews/gallery/parts/mobileFilterPanel.js @@ -65,7 +65,7 @@ function getConfig(config) { const searchField = { view: "search", - icon: "fas fa-search", + icon: "fas fa-search gallery-search-filter", id: ID_SEARCH_FIELD, name: NAME_SEARCH_FIELD, value: `${appliedFiltersModel.getFilterValue()}`, diff --git a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js new file mode 100644 index 0000000..0e1b75a --- /dev/null +++ b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js @@ -0,0 +1,738 @@ +import constants from "../../../../constants"; +import appliedFiltersModel from "../../../../models/appliedFilters"; +import galleryImageUrl from "../../../../models/galleryImagesUrls"; +import lesionsModel from "../../../../models/lesionsModel"; +import util from "../../../../utils/util"; +import collapser from "../../../components/collapser"; +import "../../../components/svgIcon"; + +const ID_MULTI_IMAGE_LESION_WINDOW = `multi-image-lesion-window-id-${webix.uid()}`; +const ID_LEFT_IMAGE_NAME_LABEL = `image-name-id-${webix.uid()}`; +const ID_RIGHT_IMAGE_NAME_LABEL = `image-name-id-${webix.uid()}`; +const ID_TOP_PANEL = `top-panel-id-${webix.uid()}`; +const ID_TOP_SLIDER = `top-slider-id-${webix.uid()}`; +const ID_PREV_PAGE_BUTTON = `prev-page-button-id-${webix.uid()}`; +const ID_NEXT_PAGE_BUTTON = `next-page-button-id-${webix.uid()}`; +const ID_RIGHT_CONTAINER = `container-id-${webix.uid()}`; +const ID_LEFT_DROP_DOWN_FILTER = `left-drop-down-filter-${webix.uid()}`; +const ID_RIGHT_DROP_DOWN_FILTER = `right-drop-down-filter-${webix.uid()}`; +const ID_LEFT_FOOTER = `footer-id-${webix.uid()}`; +const ID_RIGHT_FOOTER = `footer-id-${webix.uid()}`; +const ID_LEFT_SLIDER = `slider-id-${webix.uid()}`; +const ID_RIGHT_SLIDER = `slider-id-${webix.uid()}`; +const ID_LEFT_IMAGE = `image-id-${webix.uid()}`; +const ID_RIGHT_IMAGE = `image-id-${webix.uid()}`; +const ID_BUTTON_FULL_SCREEN = `button-full-screen-id-${webix.uid()}`; +const ID_BUTTON_WINDOWED = `button-windowed-id-${webix.uid()}`; +const ID_SEARCH = `search-id-${webix.uid()}`; +const ID_LEFT_ANCHOR_ICON = `left-anchor-icon-id-${webix.uid()}`; +const ID_RIGHT_ANCHOR_ICON = `right-anchor-icon-id-${webix.uid()}`; + +let expandButtonID; +let collapseButtonID; + +function getConfig(windowTitle, closeCallback) { + const topSlider = getTopSlider( + ID_TOP_PANEL, + ID_TOP_SLIDER, + ID_PREV_PAGE_BUTTON, + ID_NEXT_PAGE_BUTTON + ); + const topSliderCollapser = collapser.getConfig(ID_TOP_PANEL, {type: "top", closed: true}); + // TODO: find alternatives + collapseButtonID = topSliderCollapser.cols[0].id; + expandButtonID = topSliderCollapser.cols[1].id; + const leftSlider = getVerticalSlider(ID_LEFT_SLIDER, constants.MULTI_LESION_SIDE.LEFT); + const rightSlider = getVerticalSlider(ID_RIGHT_SLIDER, constants.MULTI_LESION_SIDE.RIGHT); + + /** @type {webix.ui.labelConfig} */ + const leftImageLabel = getImageLabel(ID_LEFT_IMAGE_NAME_LABEL); + const rightImageLabel = getImageLabel(ID_RIGHT_IMAGE_NAME_LABEL); + + const leftAnchorIcon = { + view: "icon", + id: ID_LEFT_ANCHOR_ICON, + width: 20, + height: 20, + icon: "fas fa-anchor" + }; + + const rightAnchorIcon = { + view: "icon", + id: ID_RIGHT_ANCHOR_ICON, + width: 20, + height: 20, + icon: "fas fa-anchor" + }; + + /** @type {webix.ui.richselectConfig} */ + const leftGroupDropdown = { + view: "richselect", + id: ID_LEFT_DROP_DOWN_FILTER, + css: "multilesion-filter-dropdown", + label: "Group by:", + labelAlign: "left", + width: 270, + height: 24, + labelWidth: 75, + value: constants.MULTI_LESION_GROUP_BY.TIME, + // TODO: check options + options: [ + constants.MULTI_LESION_GROUP_BY.TIME, + constants.MULTI_LESION_GROUP_BY.TYPE, + constants.MULTI_LESION_GROUP_BY.COMBINATION, + constants.MULTI_LESION_GROUP_BY.NO_GROUP, + ] + }; + + const rightGroupDropdown = { + view: "richselect", + id: ID_RIGHT_DROP_DOWN_FILTER, + css: "multilesion-filter-dropdown", + label: "Group by:", + labelAlign: "left", + width: 270, + height: 30, + labelWidth: 75, + value: constants.MULTI_LESION_GROUP_BY.TIME, + // TODO: check options + options: [ + constants.MULTI_LESION_GROUP_BY.TIME, + constants.MULTI_LESION_GROUP_BY.TYPE, + constants.MULTI_LESION_GROUP_BY.COMBINATION, + constants.MULTI_LESION_GROUP_BY.NO_GROUP, + ] + }; + + const leftTemplateViewer = getTemplateViewer( + ID_LEFT_IMAGE, + true, + constants.MULTI_LESION_SIDE.LEFT + ); + + const rightTemplateViewer = getTemplateViewer( + ID_RIGHT_IMAGE, + true, + constants.MULTI_LESION_SIDE.RIGHT + ); + + /** @type {webix.ui.toolbarConfig} */ + const leftToolbar = { + height: 60, + cols: [ + {width: 20}, + leftImageLabel, + {width: 5}, + leftAnchorIcon, + { + gravity: 1, + minWidth: 10 + }, + { + rows: [ + {gravity: 1}, + leftGroupDropdown, + {gravity: 1}, + ] + }, + {width: 100} + ] + }; + + /** @type {webix.ui.toolbarConfig} */ + const rightToolbar = { + height: 60, + cols: [ + {width: 100}, + { + rows: [ + {gravity: 1}, + rightGroupDropdown, + {gravity: 1}, + ] + }, + {gravity: 1}, + rightImageLabel, + {width: 5}, + rightAnchorIcon, + {gravity: 1}, + ], + }; + + const leftFooter = getFooter(ID_LEFT_FOOTER, constants.MULTI_LESION_SIDE.LEFT); + const rightFooter = getFooter(ID_RIGHT_FOOTER, constants.MULTI_LESION_SIDE.RIGHT); + + const leftImageContainer = { + css: "container", + cols: [ + { + rows: [ + leftToolbar, + { + css: "left-image-panel-container", + cols: [ + leftTemplateViewer, + ] + }, + { + cols: [ + {width: 10}, + leftFooter + ] + } + ] + }, + { + width: 1, + cols: [ + leftSlider + ] + } + ] + }; + + const rightImageContainer = { + id: ID_RIGHT_CONTAINER, + css: "container", + cols: [ + { + width: 1, + cols: [ + rightSlider + ] + }, + { + rows: [ + rightToolbar, + { + css: "right-image-panel-container", + cols: [ + rightTemplateViewer, + ] + }, + rightFooter + ] + } + ] + }; + + return { + view: "window", + id: ID_MULTI_IMAGE_LESION_WINDOW, + width: 1240, + height: 750, + css: "window-with-header", + modal: true, + fullscreen: false, + position: "center", + headHeight: 48, + move: false, + head: { + view: "toolbar", + css: "window-header-toolbar2", + borderless: true, + type: "clean", + height: 48, + cols: [ + { + template: `${windowTitle}` || "", + css: "window-header-toolbar-text main-subtitle4 window-header-toolbar2__title", + borderless: true, + autoheight: true, + }, + {gravity: 0.001}, + { + view: "search", + icon: "fas fa-search lesionWindow__filter-search", + id: ID_SEARCH, + name: "multilesionSearchName", + value: `${appliedFiltersModel.getFilterValue()}`, + css: "multi-image-lesion-search-block", + placeholder: "Search images", + hidden: true, + inputHeight: 38, + width: 634, + }, + {width: 20}, + { + view: "button", + id: ID_BUTTON_FULL_SCREEN, + label: "Full Screen", + css: "window-header-toolbar2__fullscreen-button", + type: "icon", + icon: "fas fa-expand", + width: 120, + height: 32, + }, + { + view: "button", + id: ID_BUTTON_WINDOWED, + hidden: true, + label: "Windowed", + css: "window-header-toolbar2__fullscreen-button", + type: "icon", + icon: "fas fa-compress", + width: 120, + height: 32, + }, + {width: 20}, + { + view: "button", + css: "window-close-button", + label: '', + type: "htmlbutton", + width: 30, + align: "right", + on: { + onItemClick() { + this.getTopParentView().hide(); + if (typeof closeCallback === "function") { + closeCallback(); + } + } + } + }, + {width: 5} + ] + }, + body: { + css: "multi-image-lesion-window-body", + rows: [ + topSlider, + topSliderCollapser, + {height: 10}, + { + cols: [ + leftImageContainer, + rightImageContainer + ] + } + ] + } + }; +} + +/** + * Description placeholder + * + * @returns {webix.ui.listConfig} + */ +function getTopSlider(topPanelID, sliderID, prevButtonID, nextButtonID) { + return { + id: topPanelID, + hidden: true, + cols: [ + {width: 20}, + { + view: "icon", + id: prevButtonID, + width: 24, + icon: "fas fa-chevron-left", + css: "navigation-button" + }, + { + view: "list", + id: sliderID, + layout: "x", + css: "multilesion-top-list", + scroll: false, + select: false, + drag: true, + height: 134, + type: { + height: 104 + }, + template(obj, /* common */) { + const lesionID = lesionsModel.getItemLesionID(obj); + const lesionModalitiesCount = lesionID + ? lesionsModel.getLesionModalitiesCount(lesionID) + : null; + const lesionTimePointsCount = lesionID + ? lesionsModel.getLesionTimePointsCount(lesionID) + : null; + const imageIconDimensions = { + iconContainerDimensions: { + width: constants.DEFAULT_RIBBON_ICON_CONTAINER_WIDTH, + height: constants.DEFAULT_RIBBON_ICON_CONTAINER_HEIGHT + }, + iconDimensions: { + width: constants.DEFAULT_RIBBON_IMAGE_ICON_WIDTH, + height: constants.DEFAULT_RIBBON_IMAGE_ICON_HEIGHT + } + }; + const lesionIconElementClass = lesionID + ? "gallery-images-button-elem" + : "gallery-images-button-elem-disabled"; + const diagnosisIcon = obj.hasAnnotations ? + `` : ""; + const lesionIcon = ``; + const starHtml = obj.hasAnnotations ? "" : ""; + if (typeof galleryImageUrl.getPreviewImageUrl(lesionsModel.getItemID(obj)) === "undefined") { + galleryImageUrl.setPreviewImageUrl( + lesionsModel.getItemID(obj), + obj.files.thumbnail_256.url + ); // to prevent sending query more than 1 time + } + return ``; + }, + }, + { + view: "icon", + id: nextButtonID, + width: 24, + icon: "fas fa-chevron-right", + css: "navigation-button" + }, + {width: 20} + ] + }; +} + +function getVerticalSlider(id, side) { + return { + view: "list", + id, + layout: "y", + drag: true, + select: false, + type: { + width: 62, + height: 42, + }, + css: `vertical-slider-${side}`, + width: 1, + /** + * + * @param {Object} obj + * @param {obj} obj.firstImage + * @param {string} obj.groupBy + * @param {string | number} obj.groupValue + * @param {Array} obj.images + * @returns + */ + template(obj, /* common */) { + let images; + let multipleFlag = false; + if (obj.images) { + images = obj.images; + multipleFlag = obj.images.length > 1; + } + const lesionID = lesionsModel.getItemLesionID(images[0]); + const anchorImageID = lesionsModel.getAnchorImageID(lesionID); + const anchorIcon = lesionsModel.getItemID(images) === anchorImageID + ? `` + : ""; + images.forEach((i) => { + if (typeof galleryImageUrl.getPreviewImageUrl(lesionsModel.getItemID(i)) === "undefined") { + galleryImageUrl.setPreviewImageUrl( + lesionsModel.getItemID(i), + i.files.thumbnail_256.url + ); // to prevent sending query more than 1 time + } + }); + return ``; + } + }; +} + + +/** + * Description placeholder + * + * @param {boolean} showButtons + * @returns {webix.ui.templateConfig} + */ +function getTemplateViewer(id, showButtons, side) { + return { + view: "template", + id, + css: "absolute-centered-image-template", + template(obj) { + const imageUrl = galleryImageUrl.getNormalImageUrl(lesionsModel.getItemID(obj)) || ""; + const lesionsImages = side === constants.MULTI_LESION_SIDE.LEFT + ? lesionsModel.getCurrentLeftImages() + : lesionsModel.getCurrentRightImages(); + return `
+ +
+ ${showButtons && lesionsImages.length > 1 ? '' : ""} + `; + }, + borderless: true + }; +} + +function getImageLabel(imageNameId) { + return { + view: "label", + css: "multi-image-lesion-window__image-label", + width: 100, + id: imageNameId, + label: "" + }; +} + +function footerTemplateFunction(obj, /* common */) { + const lesionID = lesionsModel.getItemLesionID(obj); + const lesionImagesCount = lesionID + ? lesionsModel.getLesionImagesCount(lesionID) + : ""; + const lesionTimePointsCount = lesionID + ? lesionsModel.getLesionTimePointsCount(lesionID) + : ""; + let multipleModalities; + let timePoint; + let modality; + if (lesionID) { + multipleModalities = lesionsModel.checkMultipleModality(lesionID) ? "Yes" : "No"; + timePoint = lesionsModel.getItemTimePoint(obj); + modality = lesionsModel.getItemModality(obj); + } + else { + multipleModalities = ""; + } + const container = $$(ID_RIGHT_CONTAINER).isVisible() + ? "` : ""; - const lesionIconElementClass = lesionID + const lesionIconElementClass = lesion ? "gallery-images-button-elem" : "gallery-images-button-elem-disabled"; + const disabledBadge = lesion + ? "" + : " disabled-badge"; const lesionIcon = ``; const starHtml = obj.hasAnnotations ? "" : ""; diff --git a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js index 0e1b75a..5693dc5 100644 --- a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js +++ b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js @@ -344,6 +344,7 @@ function getTopSlider(topPanelID, sliderID, prevButtonID, nextButtonID) { }, template(obj, /* common */) { const lesionID = lesionsModel.getItemLesionID(obj); + const lesion = lesionsModel.getLesionByID(lesionID); const lesionModalitiesCount = lesionID ? lesionsModel.getLesionModalitiesCount(lesionID) : null; @@ -360,9 +361,12 @@ function getTopSlider(topPanelID, sliderID, prevButtonID, nextButtonID) { height: constants.DEFAULT_RIBBON_IMAGE_ICON_HEIGHT } }; - const lesionIconElementClass = lesionID + const lesionIconElementClass = lesion ? "gallery-images-button-elem" : "gallery-images-button-elem-disabled"; + const disabledBadge = lesion + ? "" + : " disabled-badge"; const diagnosisIcon = obj.hasAnnotations ? ``; const starHtml = obj.hasAnnotations ? "" : ""; From a629f341413f5bb5bb710a842cdfe5a3841741de Mon Sep 17 00:00:00 2001 From: Slava Olishevko Date: Thu, 8 Aug 2024 08:49:51 +0300 Subject: [PATCH 3/6] style: improve badges style --- sources/styles/common.less | 4 ++++ sources/styles/pages.less | 2 ++ 2 files changed, 6 insertions(+) diff --git a/sources/styles/common.less b/sources/styles/common.less index be49af4..56134d3 100644 --- a/sources/styles/common.less +++ b/sources/styles/common.less @@ -299,6 +299,10 @@ a, .link { right: 13px; } +.disabled-badge { + background-color: #cccccc; +} + /* tooltip */ .tooltip-container { display: block; diff --git a/sources/styles/pages.less b/sources/styles/pages.less index f303d83..2530974 100644 --- a/sources/styles/pages.less +++ b/sources/styles/pages.less @@ -1371,9 +1371,11 @@ margin: 0 4px; } .gallery-images-button-elem-disabled { + background-color: #cccccc; pointer-events: none; } .gallery-images-button-elem-disabled:active { + background-color: #cccccc; pointer-events: none; } .gallery-images-button { From faed4a649f603928eb38446bb6dd46f9b6efa14c Mon Sep 17 00:00:00 2001 From: Slava Olishevko Date: Fri, 9 Aug 2024 09:13:33 +0300 Subject: [PATCH 4/6] ref: change the return value --- sources/models/lesionsModel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/models/lesionsModel.js b/sources/models/lesionsModel.js index acb32b0..db9c0b1 100644 --- a/sources/models/lesionsModel.js +++ b/sources/models/lesionsModel.js @@ -115,7 +115,7 @@ function getImagesWithTimePointsCount(item) { function getLesionImages(lesionID) { const lesion = getLesionByID(lesionID); - return lesion?.images ?? null; + return lesion?.images ?? []; } function getModalityImages(lesionID, modality) { From ae2e9c80d2c9ffcca7fadab7605b0ef275e859f6 Mon Sep 17 00:00:00 2001 From: Slava Olishevko Date: Fri, 9 Aug 2024 22:00:25 +0300 Subject: [PATCH 5/6] build: add core-js --- package-lock.json | 34 ++++++++++++++++++++++++++-------- package.json | 1 + 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 935fb04..90f6607 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@isic/client": "^0.6.2", "aws-sdk": "^2.910.0", "axios": "^1.6.1", + "core-js": "^3.38.0", "data-collection": "^1.1.6", "file-saver": "^1.3.8", "hint.css": "^2.6.0", @@ -1917,6 +1918,13 @@ "regenerator-runtime": "^0.13.4" } }, + "node_modules/@babel/polyfill/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, "node_modules/@babel/preset-env": { "version": "7.15.8", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.15.8.tgz", @@ -5117,11 +5125,14 @@ } }, "node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true + "version": "3.38.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.0.tgz", + "integrity": "sha512-XPpwqEodRljce9KswjZShh95qJ1URisBeKCjUdq27YdenkslVe7OO0ZJhlYXAChW7OhXaRLl8AAba7IBfoIHug==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } }, "node_modules/core-js-compat": { "version": "3.31.1", @@ -15347,6 +15358,13 @@ "requires": { "core-js": "^2.6.5", "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + } } }, "@babel/preset-env": { @@ -17870,9 +17888,9 @@ } }, "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + "version": "3.38.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.0.tgz", + "integrity": "sha512-XPpwqEodRljce9KswjZShh95qJ1URisBeKCjUdq27YdenkslVe7OO0ZJhlYXAChW7OhXaRLl8AAba7IBfoIHug==" }, "core-js-compat": { "version": "3.31.1", diff --git a/package.json b/package.json index 9425edc..16b4742 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@isic/client": "^0.6.2", "aws-sdk": "^2.910.0", "axios": "^1.6.1", + "core-js": "^3.38.0", "data-collection": "^1.1.6", "file-saver": "^1.3.8", "hint.css": "^2.6.0", From cd1fb002c03f6c448cf01341108d5b916c114e49 Mon Sep 17 00:00:00 2001 From: Slava Olishevko Date: Fri, 9 Aug 2024 22:03:19 +0300 Subject: [PATCH 6/6] feat: add polyfill of Object.groupBy --- sources/models/lesionsModel.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/sources/models/lesionsModel.js b/sources/models/lesionsModel.js index db9c0b1..aeee28b 100644 --- a/sources/models/lesionsModel.js +++ b/sources/models/lesionsModel.js @@ -1,3 +1,5 @@ +import groupBy from "core-js/actual/object/group-by"; + const lesionsMap = new Map(); let currentItem; let leftMode; @@ -238,21 +240,29 @@ function setRightImage(image) { } function groupByTimePoint(images) { - const imagesGroups = Object.groupBy(images, i => getImageTimePoint(i)); + const imagesGroups = !Object.groupBy + ? Object.groupBy(images, i => getImageTimePoint(i)) + : groupBy(images, i => getImageTimePoint(i)); return imagesGroups; } function groupByModality(images) { - const imagesGroups = Object.groupBy(images, i => getImageModality(i)); + const imagesGroups = !Object.groupBy + ? Object.groupBy(images, i => getImageModality(i)) + : groupBy(images, i => getImageTimePoint(i)); return imagesGroups; } function groupByTimePointAndModality(images) { - const timePointGroups = Object.groupBy(images, i => getImageTimePoint(i)); + const timePointGroups = !Object.groupBy + ? Object.groupBy(images, i => getImageTimePoint(i)) + : groupBy(images, i => getImageTimePoint(i)); const timePointKeys = Object.keys(timePointGroups); const imagesGroups = {}; timePointKeys.forEach((tpk) => { - const modalityGroups = Object.groupBy(timePointGroups[tpk], i => getImageModality(i)); + const modalityGroups = !Object.groupBy + ? Object.groupBy(timePointGroups[tpk], i => getImageModality(i)) + : groupBy(timePointGroups[tpk], i => getImageModality(i)); const modalityKeys = Object.keys(modalityGroups); modalityKeys.forEach((mk) => { imagesGroups[`${tpk} and ${mk}`] = modalityGroups[mk]; @@ -262,7 +272,9 @@ function groupByTimePointAndModality(images) { } function groupByID(images) { - const imagesGroups = Object.groupBy(images, i => getItemID(i)); + const imagesGroups = !Object.groupBy + ? Object.groupBy(images, i => getItemID(i)) + : groupBy(images, i => getItemID(i)); return imagesGroups; }