From 9068d506761ccd891b36076a4d65248658299339 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Mon, 28 Jul 2025 21:34:38 +0300 Subject: [PATCH 01/30] Add ol library --- package-lock.json | 536 +++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 2 files changed, 415 insertions(+), 123 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4ddff46..3c35ab8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,10 +22,10 @@ "markdown-it-bracketed-spans": "^1.0.1", "markdown-it-container": "^3.0.0", "mouse-wheel-zoom": "^1.1.5", + "ol": "^10.6.1", "openseadragon": "^2.4.2", "promise-file-reader": "^1.0.3", "util": "^0.12.4", - "vanilla-js-wheel-zoom": "^8.0.0", "webix": "6.2.0", "webix-jet": "^1.5.3", "wheelzoom": "^4.0.1" @@ -3001,19 +3001,27 @@ } }, "node_modules/@openid/appauth/node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, + "node_modules/@petamoriken/float16": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz", + "integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==", + "license": "MIT" + }, "node_modules/@resonant/oauth-client": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@resonant/oauth-client/-/oauth-client-1.0.2.tgz", @@ -3317,6 +3325,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/rbush": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/rbush/-/rbush-4.0.0.tgz", + "integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==", + "license": "MIT" + }, "node_modules/@types/retry": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", @@ -4300,12 +4314,15 @@ } }, "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -4624,15 +4641,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4777,10 +4785,11 @@ } }, "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5079,17 +5088,18 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, + "license": "MIT", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { @@ -5111,6 +5121,37 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5492,12 +5533,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -5863,6 +5905,12 @@ "node": ">= 0.4" } }, + "node_modules/earcut": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", + "license": "ISC" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -6043,6 +6091,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -6808,10 +6871,11 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -7324,14 +7388,17 @@ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -10610,6 +10677,12 @@ "shell-quote": "^1.8.1" } }, + "node_modules/lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==", + "license": "Apache-2.0" + }, "node_modules/less": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", @@ -11204,10 +11277,11 @@ "integrity": "sha512-YNFyJnO25kemSH30sOQf1MJpXZc6kBdAfEZM+9AO8Q3MgsEXmaO1jaeKUlKsLuMgxNZQWEpz0/wDRBQOd7fDKw==" }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -11461,6 +11535,48 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "node_modules/ol": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/ol/-/ol-10.6.1.tgz", + "integrity": "sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg==", + "license": "BSD-2-Clause", + "dependencies": { + "@types/rbush": "4.0.0", + "earcut": "^3.0.0", + "geotiff": "^2.1.3", + "pbf": "4.0.1", + "rbush": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openlayers" + } + }, + "node_modules/ol/node_modules/geotiff": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.1.3.tgz", + "integrity": "sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==", + "license": "MIT", + "dependencies": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2", + "zstddec": "^0.1.0" + }, + "engines": { + "node": ">=10.19" + } + }, + "node_modules/ol/node_modules/zstddec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", + "integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==", + "license": "MIT AND BSD-3-Clause" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -11474,10 +11590,11 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -11610,6 +11727,12 @@ "node": ">=6" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -11638,6 +11761,12 @@ "node": ">=6" } }, + "node_modules/parse-headers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", + "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", + "license": "MIT" + }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -11736,6 +11865,18 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz", + "integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -11983,6 +12124,12 @@ "dev": true, "peer": true }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -12078,6 +12225,24 @@ } ] }, + "node_modules/quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "license": "ISC" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -12111,13 +12276,13 @@ "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" + "node_modules/rbush": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-4.0.1.tgz", + "integrity": "sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==", + "license": "MIT", + "dependencies": { + "quickselect": "^3.0.0" } }, "node_modules/react-is": { @@ -12370,6 +12535,15 @@ "node": ">=8" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -12576,12 +12750,6 @@ "node": ">= 0.8" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/send/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -13783,11 +13951,6 @@ "node": ">= 8" } }, - "node_modules/vanilla-js-wheel-zoom": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vanilla-js-wheel-zoom/-/vanilla-js-wheel-zoom-8.0.0.tgz", - "integrity": "sha512-8bssI8g3eMACKrkfuEQvaYthoqqFhtbmt45d7m6Xw/8eV0Qr7MJE9gxDLbDwKFziENpkfquyTCdSURChapOiSQ==" - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -13850,6 +14013,12 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/web-worker": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -14538,6 +14707,12 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "node_modules/xml-utils": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.10.2.tgz", + "integrity": "sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==", + "license": "CC0-1.0" + }, "node_modules/xml2js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", @@ -16820,17 +16995,24 @@ }, "dependencies": { "form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" } } } }, + "@petamoriken/float16": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz", + "integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==" + }, "@resonant/oauth-client": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@resonant/oauth-client/-/oauth-client-1.0.2.tgz", @@ -17110,6 +17292,11 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, + "@types/rbush": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/rbush/-/rbush-4.0.0.tgz", + "integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==" + }, "@types/retry": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", @@ -17828,12 +18015,14 @@ }, "dependencies": { "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" } } @@ -18068,12 +18257,6 @@ "unpipe": "1.0.0" }, "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -18185,9 +18368,9 @@ } }, "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true }, "call-bind": { @@ -18409,17 +18592,17 @@ } }, "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "dependencies": { @@ -18437,6 +18620,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true + }, + "negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true } } }, @@ -18712,12 +18907,12 @@ } }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "decimal.js": { @@ -18981,6 +19176,11 @@ "gopd": "^1.2.0" } }, + "earcut": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -19115,6 +19315,17 @@ "es-errors": "^1.3.0" } }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -19682,9 +19893,9 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -20072,14 +20283,16 @@ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" } }, "forwarded": { @@ -22445,6 +22658,11 @@ "shell-quote": "^1.8.1" } }, + "lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==" + }, "less": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", @@ -22863,9 +23081,9 @@ "integrity": "sha512-YNFyJnO25kemSH30sOQf1MJpXZc6kBdAfEZM+9AO8Q3MgsEXmaO1jaeKUlKsLuMgxNZQWEpz0/wDRBQOd7fDKw==" }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "multicast-dns": { @@ -23050,6 +23268,40 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "ol": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/ol/-/ol-10.6.1.tgz", + "integrity": "sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg==", + "requires": { + "@types/rbush": "4.0.0", + "earcut": "^3.0.0", + "geotiff": "^2.1.3", + "pbf": "4.0.1", + "rbush": "^4.0.0" + }, + "dependencies": { + "geotiff": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.1.3.tgz", + "integrity": "sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==", + "requires": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2", + "zstddec": "^0.1.0" + } + }, + "zstddec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", + "integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==" + } + } + }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -23060,9 +23312,9 @@ } }, "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true }, "once": { @@ -23154,6 +23406,11 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -23181,6 +23438,11 @@ "callsites": "^3.0.0" } }, + "parse-headers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", + "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==" + }, "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -23266,6 +23528,14 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pbf": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz", + "integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==", + "requires": { + "resolve-protobuf-schema": "^2.1.0" + } + }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -23443,6 +23713,11 @@ } } }, + "protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -23510,6 +23785,16 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==" + }, + "quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -23535,14 +23820,14 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - } + } + }, + "rbush": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-4.0.1.tgz", + "integrity": "sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==", + "requires": { + "quickselect": "^3.0.0" } }, "react-is": { @@ -23741,6 +24026,14 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, "retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -23889,12 +24182,6 @@ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -24828,11 +25115,6 @@ } } }, - "vanilla-js-wheel-zoom": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vanilla-js-wheel-zoom/-/vanilla-js-wheel-zoom-8.0.0.tgz", - "integrity": "sha512-8bssI8g3eMACKrkfuEQvaYthoqqFhtbmt45d7m6Xw/8eV0Qr7MJE9gxDLbDwKFziENpkfquyTCdSURChapOiSQ==" - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -24885,6 +25167,11 @@ "minimalistic-assert": "^1.0.0" } }, + "web-worker": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==" + }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -25357,6 +25644,11 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xml-utils": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.10.2.tgz", + "integrity": "sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==" + }, "xml2js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", diff --git a/package.json b/package.json index 5c3c3c9..27d5eea 100644 --- a/package.json +++ b/package.json @@ -54,10 +54,10 @@ "markdown-it-bracketed-spans": "^1.0.1", "markdown-it-container": "^3.0.0", "mouse-wheel-zoom": "^1.1.5", + "ol": "^10.6.1", "openseadragon": "^2.4.2", "promise-file-reader": "^1.0.3", "util": "^0.12.4", - "vanilla-js-wheel-zoom": "^8.0.0", "webix": "6.2.0", "webix-jet": "^1.5.3", "wheelzoom": "^4.0.1" From a2d7b0be5df24b11353cc655ac6477b2ded03368 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Mon, 28 Jul 2025 21:39:01 +0300 Subject: [PATCH 02/30] Add div for zooming image to templates, delete unused code --- sources/styles/popups.less | 5 ----- sources/views/subviews/gallery/gallery.js | 15 --------------- .../views/subviews/gallery/windows/imageWindow.js | 8 ++------ .../subviews/gallery/windows/mobileImageWindow.js | 5 +---- .../gallery/windows/multiImageLesionWindow.js | 3 +-- 5 files changed, 4 insertions(+), 32 deletions(-) diff --git a/sources/styles/popups.less b/sources/styles/popups.less index 8f2129a..7793bef 100644 --- a/sources/styles/popups.less +++ b/sources/styles/popups.less @@ -201,11 +201,6 @@ justify-content: center; user-select: none; height: 100%; - .zoomable-image { - max-height:100%; - max-width:100%; - object-fit: contain; - } } .prev, .next { diff --git a/sources/views/subviews/gallery/gallery.js b/sources/views/subviews/gallery/gallery.js index 24aac86..ce29f84 100644 --- a/sources/views/subviews/gallery/gallery.js +++ b/sources/views/subviews/gallery/gallery.js @@ -5,7 +5,6 @@ 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"; @@ -469,19 +468,6 @@ export default class GalleryView extends JetView { const filter = this.getRoot().$scope.getParam("filter"); this.app.show(`${constants.PATH_GALLERY_MOBILE}?image=${isicId ?? ""}&filter=${filter ?? ""}`); } - else if (isicId && isTermsOfUseAccepted) { - if (this.imageWindow) { - this.imageWindowTemplate?.attachEvent("onAfterRender", () => { - if (this._imageInstance) { - this._imageInstance.dispatchEvent(new CustomEvent("wheelzoom.destroy")); - } - if (this._imageWindow) { - this._imageInstance = this._imageWindow.$view.getElementsByClassName("zoomable-image")[0]; - } - window.wheelzoom(this._imageInstance); - }); - } - } const imgTemplateView = this.imageWindow.queryView({id: imageWindow.getViewerId()})?.$view; if (imgTemplateView) { this.enlargeContextMenu.attachTo(imgTemplateView); @@ -491,7 +477,6 @@ export default class GalleryView extends JetView { } ); } - // this.galleryContextMenu.attachTo(galleryDataview.$view); } destroy() { diff --git a/sources/views/subviews/gallery/windows/imageWindow.js b/sources/views/subviews/gallery/windows/imageWindow.js index 99b692f..5e1400e 100644 --- a/sources/views/subviews/gallery/windows/imageWindow.js +++ b/sources/views/subviews/gallery/windows/imageWindow.js @@ -1,15 +1,12 @@ -import galleryImagesUrls from "../../../../models/galleryImagesUrls"; import windowWithHeader from "../../../components/windowWithHeader"; import "../../../components/slideButton"; - const templateViewer = { view: "template", css: "absolute-centered-image-template", template(obj) { - const imageUrl = galleryImagesUrls.getNormalImageUrl(obj.imageId) || ""; return `
- +
@@ -23,9 +20,8 @@ const templateViewerWithoutControls = { css: "absolute-centered-image-template", hidden: true, template(obj) { - const imageUrl = galleryImagesUrls.getNormalImageUrl(obj.imageId) || ""; return `
- +
`; }, diff --git a/sources/views/subviews/gallery/windows/mobileImageWindow.js b/sources/views/subviews/gallery/windows/mobileImageWindow.js index 0be8b9b..2d52433 100644 --- a/sources/views/subviews/gallery/windows/mobileImageWindow.js +++ b/sources/views/subviews/gallery/windows/mobileImageWindow.js @@ -1,6 +1,5 @@ import windowWithHeader from "app-components/mobileWindow"; -import galleryImageUrl from "../../../../models/galleryImagesUrls"; import "../../../components/slideButton"; const closeArea = { @@ -15,10 +14,8 @@ const templateViewer = { autoHeight: true, gravity: 1, template(obj) { - const imageUrl = galleryImageUrl.getNormalImageUrl(obj.imageId) ?? ""; - return `
- +
`; }, borderless: true diff --git a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js index 3de3253..728f58b 100644 --- a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js +++ b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js @@ -517,12 +517,11 @@ function getTemplateViewer(id, showButtons, side) { 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 ? '' : ""} `; From f9e0f7aef71105140977f31d552453c44ede72fd Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Mon, 28 Jul 2025 21:44:42 +0300 Subject: [PATCH 03/30] Implement zoom for tif, jpg, png formats --- sources/services/gallery/gallery.js | 95 +++----------- .../gallery/multiimageLesionWindow.js | 19 ++- sources/services/zoomImages.js | 118 ++++++++++++++++++ 3 files changed, 149 insertions(+), 83 deletions(-) create mode 100644 sources/services/zoomImages.js diff --git a/sources/services/gallery/gallery.js b/sources/services/gallery/gallery.js index 8aee74a..e1340b6 100644 --- a/sources/services/gallery/gallery.js +++ b/sources/services/gallery/gallery.js @@ -1,5 +1,4 @@ -import "wheelzoom"; -import WZoom from "vanilla-js-wheel-zoom"; +import {createZoomableImageView, zoomImage} from "app-services/zoomImages"; import constants from "../../constants"; import appliedFilterModel from "../../models/appliedFilters"; @@ -14,7 +13,6 @@ import logger from "../../utils/logger"; import util from "../../utils/util"; import filtersFormElements from "../../views/subviews/gallery/parts/filtersFormElements"; import metadataPart from "../../views/subviews/gallery/parts/metadata"; -import imageWindow from "../../views/subviews/gallery/windows/imageWindow"; import ajax from "../ajaxActions"; import authService from "../auth"; import filterService from "./filter"; @@ -224,7 +222,6 @@ class GalleryService { this._searchInput.disable(); this._createStudyButton = this._view.$scope.getCreateStudyButton(); this._dataviewYCountSelection = this._view.$scope.getDataviewYCountSelection(); - this._imageTemplate = $$(imageWindow.getViewerId()); if (this._imageWindow) { [this._imageWindowZoomPlusButtons, this._imageZoomMunusButtons] = this._imageWindow?.$view.getElementsByClassName("zoom-btn"); } @@ -497,51 +494,6 @@ class GalleryService { return true; }); - this._imageWindowTemplate?.attachEvent("onAfterRender", () => { - if (this._imageInstance) { - this.wzoom.destroy(); - } - if (this._imageWindow) { - this._imageInstance = this._imageWindow.$view.getElementsByClassName("zoomable-image")[0]; - } - const wzoomOptions = { - type: "image", - maxScale: 5, - zoomOnClick: false, - minScale: 1 - }; - this.wzoom = WZoom.create(this._imageInstance, wzoomOptions); - // TODO: check this - setTimeout(() => { - this.wzoom.transform(0, 0, 1); - }); - }); - - this._imageTemplate?.attachEvent("onBeforeRender", async (obj) => { - if (typeof galleryImagesUrls.getNormalImageUrl(obj.imageId) === "undefined") { - const item = await ajax.getImageItem(obj.imageId); - galleryImagesUrls.setNormalImageUrl(obj.imageId, item.files.full.url); - this._imageTemplate.refresh(); - } - return true; - }); - - this._imageTemplate?.attachEvent("onAfterRender", () => { - if (this._imageInstance) { - this.wzoom.destroy(); - } - if (this._imageWindow) { - this._imageInstance = this._imageWindow.$view.getElementsByClassName("zoomable-image")[0]; - } - const wzoomOptions = { - minScale: 1, - type: "image", - maxScale: 5, - speed: 1.2 - }; - this.wzoom = WZoom.create(this._imageInstance, wzoomOptions); - }); - this._imageWindowTemplateWithoutControls?.attachEvent("onBeforeRender", async (obj) => { if (obj.imageId && typeof galleryImagesUrls.getNormalImageUrl(obj.imageId) === "undefined") { const item = await ajax.getImageItem(obj.imageId); @@ -551,21 +503,15 @@ class GalleryService { return true; }); - this._imageWindowTemplateWithoutControls?.attachEvent("onAfterRender", () => { - if (this._imageInstance) { - this.wzoom.destroy(); - } - if (this._imageWindow) { - this._imageInstance = this._imageWindow.$view.getElementsByClassName("zoomable-image")[0]; - } - const wzoomOptions = { - minScale: 1, - type: "image", - maxScale: 5, - speed: 1.2 - }; - this.wzoom = WZoom.create(this._imageInstance, wzoomOptions); - }); + const initZoomableImageView = async () => { + this._imageView = await createZoomableImageView( + this._imageWindow.$view.getElementsByClassName("zoomable-image")[0] + ); + }; + + this._imageWindowTemplate?.attachEvent("onAfterRender", initZoomableImageView); + this._imageWindowTemplateWithoutControls?.attachEvent("onAfterRender", initZoomableImageView); + this._imagesDataview.on_click["resize-icon"] = (e, id) => { this._imageWindowTemplateWithoutControls?.hide(); @@ -595,28 +541,28 @@ class GalleryService { this._imageWindowZoomButtons?.define("onClick", { "btn-plus": () => { - this._zoomImage("plus"); + zoomImage(this._imageView, true); }, "btn-minus": () => { - this._zoomImage("minus"); + zoomImage(this._imageView, false); } }); this._leftLandImageWindowZoomButton?.define("onClick", { "land-btn-plus": () => { - this._zoomImage("plus"); + zoomImage(this._imageView, true); }, "land-btn-minus": () => { - this._zoomImage("minus"); + zoomImage(this._imageView, false); } }); this._rightLandImageWindowZoomButton?.define("onClick", { "land-btn-plus": () => { - this._zoomImage("plus"); + zoomImage(this._imageView, true); }, "land-btn-minus": () => { - this._zoomImage("minus"); + zoomImage(this._imageView, false); } }); @@ -1631,15 +1577,6 @@ class GalleryService { } } - _zoomImage(buttonIcon) { - if (buttonIcon === "plus") { - this.wzoom.zoomUp(); - } - else if (buttonIcon === "minus") { - this.wzoom.zoomDown(); - } - } - _removeTooltipForDataview(templateView) { const tooltipClassName = constants.TOOLTIP_CLASS_NAME_FOR_DATAVIEW; Array.from(templateView.$view.querySelectorAll("span")).forEach((element) => { diff --git a/sources/services/gallery/multiimageLesionWindow.js b/sources/services/gallery/multiimageLesionWindow.js index 00e586e..df9550a 100644 --- a/sources/services/gallery/multiimageLesionWindow.js +++ b/sources/services/gallery/multiimageLesionWindow.js @@ -1,3 +1,5 @@ +import {createZoomableImageView} from "app-services/zoomImages"; + import constants from "../../constants"; import galleryImagesUrls from "../../models/galleryImagesUrls"; import lesionWindowImagesUrls from "../../models/lesionWindowImagesUrls"; @@ -55,11 +57,20 @@ export default class MultiLesionWindowService { } async init() { - this._leftImage.attachEvent("onBeforeRender", this.updateImage); - this._leftImage.attachEvent("onAfterLoad", this.updateImage); + const initZoomableImageView = async (imageObj) => { + imageObj._imageView = await createZoomableImageView( + imageObj.$view.getElementsByClassName("zoomable-image")[0] + ); + }; - this._rightImage.attachEvent("onBeforeRender", this.updateImage); - this._rightImage.attachEvent("onAfterLoad", this.updateImage); + const images = [this._leftImage, this._rightImage]; + images.forEach((imageObj) => { + imageObj.attachEvent("onBeforeRender", this.updateImage); + imageObj.attachEvent("onAfterRender", async () => { + await initZoomableImageView(imageObj); + }); + imageObj.attachEvent("onAfterLoad", this.updateImage); + }); this._fullScreenButton.attachEvent("onItemClick", () => { this.changeWindowMode(); }); this._windowedButton.attachEvent("onItemClick", () => { this.changeWindowMode(); }); diff --git a/sources/services/zoomImages.js b/sources/services/zoomImages.js new file mode 100644 index 0000000..2578b68 --- /dev/null +++ b/sources/services/zoomImages.js @@ -0,0 +1,118 @@ +import Map from "ol/Map"; +import View from "ol/View"; +import ImageLayer from "ol/layer/Image"; +import TileLayer from "ol/layer/WebGLTile"; +import GeoTIFF from "ol/source/GeoTIFF"; +import ImageStatic from "ol/source/ImageStatic"; + +import galleryImagesUrls from "app-models/galleryImagesUrls"; + +import util from "../utils/util"; + +const MOBILE_PORTRAIT_SIZE = "90vw"; +const MOBILE_LANDSCAPE_SIZE = "90vh"; +const DESKTOP_SIZE = "100%"; + +/** + * @typedef {import("ol/View").default} OlView + */ + +/** + * Creates and initializes an OpenLayers View with zoom functionality for the given HTML element + * Works with jpg/png/tif formats + * @param {HTMLElement} htmlElement with isic_id attribute + * @returns {Promise} + */ +export async function createZoomableImageView(htmlElement) { + const imageUrl = galleryImagesUrls.getNormalImageUrl(htmlElement?.getAttribute("isic_id")); + if (!htmlElement || !imageUrl) return null; + + const imageUrlLowerCase = imageUrl.toLowerCase(); + const isTifImage = imageUrlLowerCase.endsWith(".tif") || imageUrlLowerCase.endsWith(".tiff"); + + let extent; + let layer; + if (isTifImage) { + const source = new GeoTIFF({sources: [{url: imageUrl}]}); + const sourceView = await source.getView(); + extent = sourceView.extent; + layer = new TileLayer({source}); + } + else { + const loadImage = url => new Promise((resolve, reject) => { + const img = new Image(); + img.src = url; + img.onload = () => resolve(img); + img.onerror = reject; + }); + + const img = await loadImage(imageUrl); + extent = [0, 0, img.width, img.height]; + + const source = new ImageStatic({ + url: imageUrl, + imageExtent: extent, + }); + layer = new ImageLayer({source}); + } + + const imageView = new View({extent}); + // eslint-disable-next-line no-new + new Map({ + target: htmlElement, + layers: [layer], + view: imageView, + controls: [], + }); + + imageView.fit(extent); + const [minX, minY, maxX, maxY] = extent; + const width = maxX - minX; + const height = maxY - minY; + // Need to set width or height because the map container's width or height are automatically 0 + setImageContainerSize(htmlElement, width, height); + + return imageView; +} + +/** + * @param {HTMLElement} container + * @param {number} width + * @param {number} height + * @returns {void} + */ +function setImageContainerSize(container, width, height) { + const parent = container.parentElement; + if (!parent) return; + + const parentRect = parent.getBoundingClientRect(); + const imgAspectRatio = height / width; + const parentAspectRatio = parentRect.height / parentRect.width; + + const mobileImageContainerSize = util.isPortrait() ? MOBILE_PORTRAIT_SIZE : MOBILE_LANDSCAPE_SIZE; + const imageContainerSize = util.isMobilePhone() ? mobileImageContainerSize : DESKTOP_SIZE; + + if (parentAspectRatio > imgAspectRatio) { + container.style.width = imageContainerSize; + container.style.height = "auto"; + } + else { + container.style.height = imageContainerSize; + container.style.width = "auto"; + } + container.style.aspectRatio = `${width} / ${height}`; +} + +/** + * @param {OlView} imageView + * @param {boolean} isZoomIn + * @returns {void} + */ +export function zoomImage(imageView, isZoomIn) { + if (!imageView) return; + + const zoom = imageView.getZoom() ?? imageView.getMinZoom(); + const newZoom = isZoomIn ? zoom + 1 : zoom - 1; + + imageView.animate({zoom: newZoom, duration: 250}); +} From 2c134b430647738a9ef32179ad172a57500512f1 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Fri, 1 Aug 2025 19:45:12 +0300 Subject: [PATCH 04/30] Remove unused dependency --- package-lock.json | 194 ---------------------------------------------- package.json | 1 - 2 files changed, 195 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c35ab8..0319bcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,6 @@ "eslint": "^7.32.0", "eslint-config-xbsoftware": "^5.0.0-alpha.3", "eslint-import-resolver-alias": "^1.1.2", - "eslint-import-resolver-webpack": "^0.13.1", "eslint-plugin-import": "^2.25.1", "html-loader": "^5.0.0", "html-webpack-plugin": "^5.3.2", @@ -5960,20 +5959,6 @@ "node": ">= 0.8" } }, - "node_modules/enhanced-resolve": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "integrity": "sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.2.0", - "tapable": "^0.1.8" - }, - "engines": { - "node": ">=0.6" - } - }, "node_modules/enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -6303,70 +6288,6 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-import-resolver-webpack": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.10.tgz", - "integrity": "sha512-ciVTEg7sA56wRMR772PyjcBRmyBMLS46xgzQZqt6cWBEKc7cK65ZSSLCTLVRu2gGtKyXUb5stwf4xxLBfERLFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "enhanced-resolve": "^0.9.1", - "find-root": "^1.1.0", - "hasown": "^2.0.2", - "interpret": "^1.4.0", - "is-core-module": "^2.15.1", - "is-regex": "^1.2.0", - "lodash": "^4.17.21", - "resolve": "^2.0.0-next.5", - "semver": "^5.7.2" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "eslint-plugin-import": ">=1.4.0", - "webpack": ">=1.11.0" - } - }, - "node_modules/eslint-import-resolver-webpack/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-webpack/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-import-resolver-webpack/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/eslint-module-utils": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", @@ -7314,13 +7235,6 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true, - "license": "MIT" - }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -8259,16 +8173,6 @@ "node": ">= 0.4" } }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -11117,13 +11021,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==", - "dev": true, - "license": "MIT" - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -13355,16 +13252,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/tapable": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "integrity": "sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -19217,17 +19104,6 @@ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true }, - "enhanced-resolve": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "integrity": "sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.2.0", - "tapable": "^0.1.8" - } - }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -19610,52 +19486,6 @@ } } }, - "eslint-import-resolver-webpack": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.10.tgz", - "integrity": "sha512-ciVTEg7sA56wRMR772PyjcBRmyBMLS46xgzQZqt6cWBEKc7cK65ZSSLCTLVRu2gGtKyXUb5stwf4xxLBfERLFA==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "enhanced-resolve": "^0.9.1", - "find-root": "^1.1.0", - "hasown": "^2.0.2", - "interpret": "^1.4.0", - "is-core-module": "^2.15.1", - "is-regex": "^1.2.0", - "lodash": "^4.17.21", - "resolve": "^2.0.0-next.5", - "semver": "^5.7.2" - }, - "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" - } - }, - "resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, "eslint-module-utils": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", @@ -20234,12 +20064,6 @@ "pkg-dir": "^4.1.0" } }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -20902,12 +20726,6 @@ "side-channel": "^1.0.4" } }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, "ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -22967,12 +22785,6 @@ } } }, - "memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==", - "dev": true - }, "merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -24671,12 +24483,6 @@ } } }, - "tapable": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "integrity": "sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ==", - "dev": true - }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", diff --git a/package.json b/package.json index 27d5eea..8d0530f 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ "eslint": "^7.32.0", "eslint-config-xbsoftware": "^5.0.0-alpha.3", "eslint-import-resolver-alias": "^1.1.2", - "eslint-import-resolver-webpack": "^0.13.1", "eslint-plugin-import": "^2.25.1", "html-loader": "^5.0.0", "html-webpack-plugin": "^5.3.2", From 5252e6b69a59c41336acd2db0c8876a4475bf371 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Fri, 1 Aug 2025 19:54:39 +0300 Subject: [PATCH 05/30] Fix typos --- sources/models/appliedFilters.js | 8 +++---- sources/styles/pages.less | 6 ++--- .../views/subviews/gallery/parts/filters.js | 22 +++++++++---------- .../gallery/parts/filtersFormElements.js | 22 +++++++++---------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/sources/models/appliedFilters.js b/sources/models/appliedFilters.js index b901331..69c3e3d 100644 --- a/sources/models/appliedFilters.js +++ b/sources/models/appliedFilters.js @@ -4,7 +4,7 @@ import state from "./state"; const appliedFilters = new webix.DataCollection(); const appliedFilterBySearch = new webix.DataCollection(); -const showedFilters = new webix.DataCollection(); +const shownFilters = new webix.DataCollection(); let filterValue = ""; let filterByName; @@ -12,8 +12,8 @@ function getAppliedFilterBySearchCollection() { return appliedFilterBySearch; } -function getShowedFiltersCollection() { - return showedFilters; +function getShownFiltersCollection() { + return shownFilters; } function setAppliedFiltersToLocalStorage(appliedFiltersToStorage) { @@ -617,7 +617,7 @@ export default { getAppliedFilterBySearchCollection, setFilterValue, getFilterValue, - getShowedFiltersCollection, + getShownFiltersCollection, getFiltersFromURL, convertAppliedFiltersToParams, getAppliedCollectionsForApi, diff --git a/sources/styles/pages.less b/sources/styles/pages.less index 444431c..70fd9b2 100644 --- a/sources/styles/pages.less +++ b/sources/styles/pages.less @@ -1124,7 +1124,7 @@ border-color: @lines-color; } -.collapssible-filter, .collapssible-filter-tree { +.collapsible-filter, .collapsible-filter-tree { padding-left: 19px; position: relative; text-transform: uppercase; @@ -1145,7 +1145,7 @@ .transition(all .2s ease-in-out); } - &.showed-filter { + &.shown-filter { &:before { .transform(rotate(90deg)); } @@ -1157,7 +1157,7 @@ } } -.collapssible-filter-tree { +.collapsible-filter-tree { padding-left: 0px; border-width: 0px !important; padding-left: 19px; diff --git a/sources/views/subviews/gallery/parts/filters.js b/sources/views/subviews/gallery/parts/filters.js index 4c60662..691f3f0 100644 --- a/sources/views/subviews/gallery/parts/filters.js +++ b/sources/views/subviews/gallery/parts/filters.js @@ -11,7 +11,7 @@ const NAME_SELECT_ALL_FILTERS_MOBILE = "filter-images-select-all-mobile-name"; const NAME_SELECT_NONE_FILTERS = "filter-images-select-none-name"; const NAME_SELECT_NONE_FILTERS_MOBILE = "filter-images-select-none-mobile-name"; -const showedFiltersCollection = appliedFilters.getShowedFiltersCollection(); +const shownFiltersCollection = appliedFilters.getShownFiltersCollection(); function getLabelUI(label) { const view = { @@ -33,36 +33,36 @@ function _attachCollapseToTreeFilter(filter, dataForCreatingControl, isCollapsed const collapser = children[0]; const controls = children[1]; if (!controls.isVisible()) { - webix.html.addCss(collapser.getNode(), "showed-filter"); + webix.html.addCss(collapser.getNode(), "shown-filter"); webix.html.removeCss(collapser.getNode(), "hidden-filter"); this.config.isRowsVisible = true; controls.show(); - showedFiltersCollection.add({ + shownFiltersCollection.add({ id: dataForCreatingControl.id }); } else { - webix.html.removeCss(collapser.getNode(), "showed-filter"); + webix.html.removeCss(collapser.getNode(), "shown-filter"); webix.html.addCss(collapser.getNode(), "hidden-filter"); this.config.isRowsVisible = false; - if (showedFiltersCollection.exists(dataForCreatingControl.id)) { - showedFiltersCollection.remove(dataForCreatingControl.id); + if (shownFiltersCollection.exists(dataForCreatingControl.id)) { + shownFiltersCollection.remove(dataForCreatingControl.id); } controls.hide(); } }; collapseElement.onClick = { - "collapssible-filter-tree": collapsibleFilterFunction + "collapsible-filter-tree": collapsibleFilterFunction }; if (isCollapsedFilter) { - collapseElement.css = "collapssible-filter-tree hidden-filter"; + collapseElement.css = "collapsible-filter-tree hidden-filter"; collapsibleFilter.rows[1].hidden = true; } else { - collapseElement.css = "collapssible-filter-tree showed-filter"; + collapseElement.css = "collapsible-filter-tree shown-filter"; collapsibleFilter.rows[1].hidden = false; - if (!showedFiltersCollection.exists(dataForCreatingControl.id)) { - showedFiltersCollection.add({ + if (!shownFiltersCollection.exists(dataForCreatingControl.id)) { + shownFiltersCollection.add({ id: dataForCreatingControl.id }); } diff --git a/sources/views/subviews/gallery/parts/filtersFormElements.js b/sources/views/subviews/gallery/parts/filtersFormElements.js index c407989..41766bc 100644 --- a/sources/views/subviews/gallery/parts/filtersFormElements.js +++ b/sources/views/subviews/gallery/parts/filtersFormElements.js @@ -3,7 +3,7 @@ import appliedFilters from "../../../../models/appliedFilters"; import util from "../../../../utils/util"; import filtersViewHelper from "./filters"; -const showedFiltersCollection = appliedFilters.getShowedFiltersCollection(); +const shownFiltersCollection = appliedFilters.getShownFiltersCollection(); const NAME_SELECT_ALL_FILTER = filtersViewHelper.getSelectAllFilersName(); const NAME_SELECT_NONE_FILTER = filtersViewHelper.getSelectNoneFiltersName(); // we assume that the first child of any filter will be a label @@ -33,7 +33,7 @@ function _attachCollapseToFilter(filter, collapsed, dataForCreatingControl) { selectAllFiltersButton.show(); selectNoneFiltersButton.show(); showOrHideAggregateButton(filter, controls, dataForCreatingControl, currentMobile); - webix.html.addCss(labelObject.getNode(), "showed-filter"); + webix.html.addCss(labelObject.getNode(), "shown-filter"); webix.html.removeCss(labelObject.getNode(), "hidden-filter"); this.config.isRowsVisible = true; controls.show(); @@ -41,35 +41,35 @@ function _attachCollapseToFilter(filter, collapsed, dataForCreatingControl) { // TODO: fix mobile view // const filtersNode = controls.getParentView().getNode(); // filtersNode.scrollIntoView(); - showedFiltersCollection.add({ + shownFiltersCollection.add({ id: dataForCreatingControl.id }); } else { selectAllFiltersButton?.hide(); selectNoneFiltersButton?.hide(); - webix.html.removeCss(labelObject.getNode(), "showed-filter"); + webix.html.removeCss(labelObject.getNode(), "shown-filter"); webix.html.addCss(labelObject.getNode(), "hidden-filter"); this.config.isRowsVisible = false; - if (showedFiltersCollection.exists(dataForCreatingControl.id)) { - showedFiltersCollection.remove(dataForCreatingControl.id); + if (shownFiltersCollection.exists(dataForCreatingControl.id)) { + shownFiltersCollection.remove(dataForCreatingControl.id); } controls.hide(); } }; template.onClick = { // eslint-disable-next-line func-names - "collapssible-filter": collapsibleFilterFunction + "collapsible-filter": collapsibleFilterFunction }; if (collapsed) { - template.css += " collapssible-filter hidden-filter"; + template.css += " collapsible-filter hidden-filter"; collapsibleFilter.rows[1].hidden = true; } else { - template.css += " collapssible-filter showed-filter"; + template.css += " collapsible-filter shown-filter"; collapsibleFilter.rows[1].hidden = false; - if (!showedFiltersCollection.exists(dataForCreatingControl.id)) { - showedFiltersCollection.add({ + if (!shownFiltersCollection.exists(dataForCreatingControl.id)) { + shownFiltersCollection.add({ id: dataForCreatingControl.id }); } From 8b7952b9dcb7ec3f88f72bd43bb21084f631b150 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Fri, 1 Aug 2025 20:09:05 +0300 Subject: [PATCH 06/30] Remove dead and duplicate code --- .../views/subviews/gallery/parts/filters.js | 179 +++++++----------- .../gallery/parts/filtersFormElements.js | 5 +- 2 files changed, 66 insertions(+), 118 deletions(-) diff --git a/sources/views/subviews/gallery/parts/filters.js b/sources/views/subviews/gallery/parts/filters.js index 691f3f0..bb354a6 100644 --- a/sources/views/subviews/gallery/parts/filters.js +++ b/sources/views/subviews/gallery/parts/filters.js @@ -1,7 +1,6 @@ import constants from "../../../../constants"; import appliedFilters from "../../../../models/appliedFilters"; import collectionsModel from "../../../../models/collectionsModel"; -import diagnosisModel from "../../../../models/diagnosis"; import globalState from "../../../../models/state"; import filterService from "../../../../services/gallery/filter"; import util from "../../../../utils/util"; @@ -23,8 +22,6 @@ function getLabelUI(label) { return view; } -const expandedParentsFilters = []; - function _attachCollapseToTreeFilter(filter, dataForCreatingControl, isCollapsedFilter) { const collapsibleFilter = webix.copy(filter); const collapseElement = collapsibleFilter.rows[0]; @@ -66,19 +63,12 @@ function _attachCollapseToTreeFilter(filter, dataForCreatingControl, isCollapsed id: dataForCreatingControl.id }); } - if (dataForCreatingControl.parent) { - const parentValue = diagnosisModel.getDiagnosisConcatenateValue( - dataForCreatingControl.parent - ); - const parentId = util.getOptionId("diagnosis", parentValue); - expandedParentsFilters.push(parentId); - } } return collapsibleFilter; } -function getCheckboxUI(data, collapsed, expandedFilters) { +function getCheckboxUI(data, collapsed) { const isMobile = util.isMobilePhone(); const handleAggregateButton = function (controlData, elements, newValue, app) { const filtersInfo = []; @@ -218,113 +208,74 @@ function getCheckboxUI(data, collapsed, expandedFilters) { ] }; - if (data.type === constants.FILTER_ELEMENT_TYPE.TREE_CHECKBOX) { - expandedParentsFilters.length = 0; - data.options?.forEach((currentOption) => { - view.rows[1].rows.push(getTreeCheckboxUI(currentOption, data.id, expandedFilters)); - }); - } - else { - data?.options?.forEach((currentOption) => { - if (data.id === constants.COLLECTION_KEY) { - if (!currentOption.updated) { - const pinnedCollections = collectionsModel - .getPinnedCollections() - .map(collection => ({name: collection.name, id: collection.id})); - const currentCollection = pinnedCollections - .find(collection => collection.id === currentOption.key); - if (currentCollection) { - currentOption.updated = true; - currentOption.optionId = currentCollection.id; - currentOption.key = currentCollection.id; - currentOption.collectionName = currentCollection.name; - } + data?.options?.forEach((currentOption) => { + const isCollection = data.id === constants.COLLECTION_KEY; + if (isCollection) { + if (!currentOption.updated) { + const pinnedCollections = collectionsModel + .getPinnedCollections() + .map(collection => ({name: collection.name, id: collection.id})); + const currentCollection = pinnedCollections + .find(collection => collection.id === currentOption.key); + if (currentCollection) { + currentOption.updated = true; + currentOption.optionId = currentCollection.id; + currentOption.key = currentCollection.id; + currentOption.collectionName = currentCollection.name; } } - const optionName = data.id === constants.COLLECTION_KEY - ? filterService.prepareOptionName(currentOption.name, data.id) - : filterService.prepareOptionName(currentOption.key, data.id); - const id = util.getOptionId(data.id, currentOption.key); - const filtersChangedData = appliedFilters.getFiltersChangedData( - data, - currentOption, - optionName - ); - if (data.id === constants.COLLECTION_KEY) { - view.rows[1].rows.push( - { - cols: [ - { - id, - view: "checkbox", - css: "checkbox-ctrl", - label: "", - labelRight: optionName, - value: 0, - name: id, - height: 28, - gravity: 3, - attributes: { - title: `${optionName}`, - dataOptionId: currentOption.optionId ? `${currentOption.optionId}` : null - }, - labelWidth: 0, - filtersChangedData, - on: { - onChange(status) { - let params = webix.copy(this.config.filtersChangedData); - if (currentOption && data.type === "rangeCheckbox") { - webix.extend(this.config.filtersChangedData, { - to: currentOption.to, - from: currentOption.from - }); - } - params.remove = !status; - params.optionId = currentOption.optionId; - this.getTopParentView().$scope.app.callEvent("filtersChanged", [params]); - } - } - }, - ] - } - ); - } - else { - view.rows[1].rows.push( - { - id, - view: "checkbox", - css: "checkbox-ctrl", - label: "", - labelRight: `${optionName} (0)`, - value: 0, - name: id, - height: 28, - attributes: { - title: `${optionName} (0)`, - dataOptionId: currentOption.optionId ? `${currentOption.optionId}` : null - }, - labelWidth: 0, - filtersChangedData, - on: { - onChange(status) { - let params = webix.copy(this.config.filtersChangedData); - if (currentOption && data.type === "rangeCheckbox") { - webix.extend(this.config.filtersChangedData, { - to: currentOption.to, - from: currentOption.from - }); - } - params.remove = !status; - params.optionId = currentOption.optionId; - this.getTopParentView().$scope.app.callEvent("filtersChanged", [params]); - } - } + } + + const optionName = isCollection + ? filterService.prepareOptionName(currentOption.name, data.id) + : filterService.prepareOptionName(currentOption.key, data.id); + const labelRight = isCollection ? optionName : `${optionName} (0)`; + const id = util.getOptionId(data.id, currentOption.key); + const filtersChangedData = appliedFilters.getFiltersChangedData( + data, + currentOption, + optionName + ); + + const checkboxConfig = { + id, + view: "checkbox", + css: "checkbox-ctrl", + label: "", + labelRight, + value: 0, + name: id, + height: 28, + attributes: { + title: labelRight, + dataOptionId: currentOption.optionId ? `${currentOption.optionId}` : null, + }, + labelWidth: 0, + filtersChangedData, + on: { + onChange(status) { + let params = webix.copy(this.config.filtersChangedData); + if (currentOption && data.type === "rangeCheckbox") { + webix.extend(this.config.filtersChangedData, { + to: currentOption.to, + from: currentOption.from + }); } - ); + params.remove = !status; + params.optionId = currentOption.optionId; + this.getTopParentView().$scope.app.callEvent("filtersChanged", [params]); + } } - }); - } + }; + + if (isCollection) { + view.rows[1].rows.push({ + cols: [checkboxConfig] + }); + } else { + view.rows[1].rows.push(checkboxConfig); + } + }); return view; } diff --git a/sources/views/subviews/gallery/parts/filtersFormElements.js b/sources/views/subviews/gallery/parts/filtersFormElements.js index 41766bc..61662ce 100644 --- a/sources/views/subviews/gallery/parts/filtersFormElements.js +++ b/sources/views/subviews/gallery/parts/filtersFormElements.js @@ -11,7 +11,7 @@ const NAME_SELECT_NONE_FILTER = filtersViewHelper.getSelectNoneFiltersName(); function _attachCollapseToFilter(filter, collapsed, dataForCreatingControl) { const collapsibleFilter = webix.copy(filter); const isMobile = util.isMobilePhone(); - const template = isMobile || filter.type === constants.FILTER_ELEMENT_TYPE.TREE_CHECKBOX + const template = isMobile ? collapsibleFilter.rows[0].cols[0] : collapsibleFilter.rows[0]; const collapsibleFilterFunction = function () { @@ -103,9 +103,6 @@ function transformToFormFormat(data, expandedFilters) { elems.push(_attachCollapseToFilter(filtersConfig, collapsed, dataForCreatingControl)); } break; - /* case "range_slider": - t = filtersViewHelper.getRangeSliderUI(data[key].data[i]); - break; */ case constants.FILTER_ELEMENT_TYPE.TREE_CHECKBOX: { if (util.isMobilePhone()) { break; From 248959c150e59f10616c9f49870630cfb26949c0 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Fri, 1 Aug 2025 20:16:05 +0300 Subject: [PATCH 07/30] Fix tree checkbox collapsing behavior --- sources/services/gallery/gallery.js | 16 ++++--------- .../views/subviews/gallery/parts/filters.js | 3 +-- .../gallery/parts/filtersFormElements.js | 23 +++++++------------ 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/sources/services/gallery/gallery.js b/sources/services/gallery/gallery.js index e1340b6..bdca4ac 100644 --- a/sources/services/gallery/gallery.js +++ b/sources/services/gallery/gallery.js @@ -1258,19 +1258,11 @@ class GalleryService { } } - // expandedFilters - array of filter that should be initially expanded - _createFilters(expandedFilters, forceRebuild) { - let expandedFiltersKeys = []; - if (expandedFilters && expandedFilters.length) { - expandedFiltersKeys = expandedFilters.map((item) => { - if (item.view === constants.FILTER_ELEMENT_TYPE.TREE_CHECKBOX) { - return item.id; - } - return item.key; - }); - } + // appliedFilters - array of filter that should be initially expanded + _createFilters(appliedFilters, forceRebuild) { + const appliedFiltersKeys = (appliedFilters || []).map(item => item.key); return filtersData.getFiltersData(forceRebuild).then((data) => { - const elements = filtersFormElements.transformToFormFormat(data, expandedFiltersKeys); + const elements = filtersFormElements.transformToFormFormat(data, appliedFiltersKeys); this.clearFilterForm(); this._filtersForm = this._view.$scope.getFiltersForm(); webix.ui(elements, this._filtersForm); diff --git a/sources/views/subviews/gallery/parts/filters.js b/sources/views/subviews/gallery/parts/filters.js index bb354a6..2a7a5a4 100644 --- a/sources/views/subviews/gallery/parts/filters.js +++ b/sources/views/subviews/gallery/parts/filters.js @@ -279,8 +279,7 @@ function getCheckboxUI(data, collapsed) { return view; } -function getTreeCheckboxUI(data, collapsed, expandedFilters) { - const elementsToOpen = [...expandedFilters]; +function getTreeCheckboxUI(data, collapsed, elementsToOpen) { const labelId = data.id; const treeTableId = `treeTable-${data.id}`; const treeTableDataForFilters = { diff --git a/sources/views/subviews/gallery/parts/filtersFormElements.js b/sources/views/subviews/gallery/parts/filtersFormElements.js index 61662ce..e9d69ab 100644 --- a/sources/views/subviews/gallery/parts/filtersFormElements.js +++ b/sources/views/subviews/gallery/parts/filtersFormElements.js @@ -78,7 +78,7 @@ function _attachCollapseToFilter(filter, collapsed, dataForCreatingControl) { return collapsibleFilter; } -function transformToFormFormat(data, expandedFilters) { +function transformToFormFormat(data, appliedFiltersKeys) { const elems = []; const dataKeys = Object.keys(data); dataKeys.forEach((key) => { @@ -87,14 +87,11 @@ function transformToFormFormat(data, expandedFilters) { for (let i = 0; i < data[key].data.length; i++) { let filtersConfig = null; const dataForCreatingControl = data[key].data[i]; - let collapsed = true; - const indexOfDataForCreatingControl = expandedFilters.indexOf(dataForCreatingControl.id); - const foundFilterCollection = showedFiltersCollection.find( - showedFilter => dataForCreatingControl.id === showedFilter.id - ); - if (indexOfDataForCreatingControl !== -1 || foundFilterCollection.length !== 0) { - collapsed = false; - } + + const isInAppliedFilters = appliedFiltersKeys.includes(dataForCreatingControl.id); + const isInShownFilters = shownFiltersCollection.exists(dataForCreatingControl.id); + const collapsed = !isInAppliedFilters && !isInShownFilters; + switch (data[key].data[i].type) { case "checkbox": case "rangeCheckbox": @@ -107,15 +104,11 @@ function transformToFormFormat(data, expandedFilters) { if (util.isMobilePhone()) { break; } - const openedItems = getOpenElementFromTreeTable(`treeTable-${dataForCreatingControl.id}`); - expandedFilters.push(...openedItems); - const diagnosisRegex = /^diagnosis\|.*/; - const diagnosisFilter = expandedFilters.find(f => diagnosisRegex.test(f)); - collapsed = !diagnosisFilter; + const openedElements = getOpenElementFromTreeTable(`treeTable-${dataForCreatingControl.id}`); filtersConfig = filtersViewHelper.getTreeCheckboxUI( dataForCreatingControl, collapsed, - expandedFilters + openedElements ); if (filtersConfig) { elems.push(filtersConfig); From eb67209403f7a74478bd706f74b843529c303b79 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Fri, 1 Aug 2025 20:26:29 +0300 Subject: [PATCH 08/30] Add viewport meta tag to HTML pages --- error.html | 2 ++ index.html | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/error.html b/error.html index fc8d855..9fd6da1 100644 --- a/error.html +++ b/error.html @@ -8,6 +8,8 @@ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-W2JQSNLL'); + + diff --git a/index.html b/index.html index 384a2a0..cfb615f 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@ })(window,document,'script','dataLayer','GTM-W2JQSNLL'); - + ISIC Archive From 3d8f3669320320ee8af8456b72fc0c701cb90536 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Mon, 4 Aug 2025 12:21:29 +0300 Subject: [PATCH 09/30] Add image zoom state synchronization for metadata toggle --- sources/views/subviews/gallery/windows/imageWindow.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sources/views/subviews/gallery/windows/imageWindow.js b/sources/views/subviews/gallery/windows/imageWindow.js index 5e1400e..a04c9c0 100644 --- a/sources/views/subviews/gallery/windows/imageWindow.js +++ b/sources/views/subviews/gallery/windows/imageWindow.js @@ -60,13 +60,9 @@ const slideButton = { onChange(newv) { if (newv) { $$(metadataContainer.id).show(); - // eslint-disable-next-line no-use-before-define - refreshTemplate(); } else { $$(metadataContainer.id).hide(); - // eslint-disable-next-line no-use-before-define - refreshTemplate(); } } } @@ -101,12 +97,6 @@ const windowBody = { ] }; -function refreshTemplate() { - // eslint-disable-next-line no-use-before-define - let imageTemplate = $$(getViewerId()); - imageTemplate.refresh(); -} - function getConfig(id, studyImage, closeCallback) { let windowTitle; templateViewer.id = `viewer-${webix.uid()}`; From beb3012870628d43f780468bc685f83cae087396 Mon Sep 17 00:00:00 2001 From: EkaterinaPoddubskaya Date: Tue, 5 Aug 2025 12:17:02 +0300 Subject: [PATCH 10/30] Reset image view extent on container resize --- sources/services/gallery/gallery.js | 42 +++++++++++++------ .../gallery/multiimageLesionWindow.js | 21 +++++++--- sources/services/zoomImages.js | 35 +++++++++++++--- sources/views/subviews/gallery/gallery.js | 2 + .../views/subviews/gallery/galleryMobile.js | 2 + .../subviews/gallery/windows/imageWindow.js | 15 +++---- 6 files changed, 84 insertions(+), 33 deletions(-) diff --git a/sources/services/gallery/gallery.js b/sources/services/gallery/gallery.js index bdca4ac..eace2fa 100644 --- a/sources/services/gallery/gallery.js +++ b/sources/services/gallery/gallery.js @@ -1,4 +1,4 @@ -import {createZoomableImageView, zoomImage} from "app-services/zoomImages"; +import {createZoomableImage, restoreImageViewExtent, zoomImage} from "app-services/zoomImages"; import constants from "../../constants"; import appliedFilterModel from "../../models/appliedFilters"; @@ -36,6 +36,8 @@ class GalleryService { contentHeaderTemplate, imageWindowInstance, imageWindowViewer, + imageWindowSlideButton, + imageWindowMetadataContainer, imageWindowMetadata, metadataWindow, metadataWindowMetadata, @@ -70,6 +72,8 @@ class GalleryService { this._contentHeaderTemplate = contentHeaderTemplate; this._imageWindow = imageWindowInstance; this._imageWindowViewer = imageWindowViewer; + this._imageWindowSlideButton = imageWindowSlideButton; + this._imageWindowMetadataContainer = imageWindowMetadataContainer; this._imageWindowMetadata = imageWindowMetadata; this._metadataWindow = metadataWindow; this._metadataWindowMetadata = metadataWindowMetadata; @@ -503,15 +507,27 @@ class GalleryService { return true; }); - const initZoomableImageView = async () => { - this._imageView = await createZoomableImageView( - this._imageWindow.$view.getElementsByClassName("zoomable-image")[0] - ); + const getZoomableImageNode = () => { + return this._imageWindow.$view.getElementsByClassName("zoomable-image")[0]; + } + + const initZoomableImage = async () => { + this._zoomableImageProperties = await createZoomableImage(getZoomableImageNode()); }; - this._imageWindowTemplate?.attachEvent("onAfterRender", initZoomableImageView); - this._imageWindowTemplateWithoutControls?.attachEvent("onAfterRender", initZoomableImageView); + this._imageWindowTemplate?.attachEvent("onAfterRender", initZoomableImage); + this._imageWindowTemplateWithoutControls?.attachEvent("onAfterRender", initZoomableImage); + + this._imageWindowSlideButton?.attachEvent("onChange", (shouldShowMetadata) => { + if (shouldShowMetadata) { + this._imageWindowMetadataContainer.show(); + } + else { + this._imageWindowMetadataContainer.hide(); + } + restoreImageViewExtent(this._zoomableImageProperties, getZoomableImageNode()); + }); this._imagesDataview.on_click["resize-icon"] = (e, id) => { this._imageWindowTemplateWithoutControls?.hide(); @@ -541,28 +557,28 @@ class GalleryService { this._imageWindowZoomButtons?.define("onClick", { "btn-plus": () => { - zoomImage(this._imageView, true); + zoomImage(this._zoomableImageProperties.view, true); }, "btn-minus": () => { - zoomImage(this._imageView, false); + zoomImage(this._zoomableImageProperties.view, false); } }); this._leftLandImageWindowZoomButton?.define("onClick", { "land-btn-plus": () => { - zoomImage(this._imageView, true); + zoomImage(this._zoomableImageProperties.view, true); }, "land-btn-minus": () => { - zoomImage(this._imageView, false); + zoomImage(this._zoomableImageProperties.view, false); } }); this._rightLandImageWindowZoomButton?.define("onClick", { "land-btn-plus": () => { - zoomImage(this._imageView, true); + zoomImage(this._zoomableImageProperties.view, true); }, "land-btn-minus": () => { - zoomImage(this._imageView, false); + zoomImage(this._zoomableImageProperties.view, false); } }); diff --git a/sources/services/gallery/multiimageLesionWindow.js b/sources/services/gallery/multiimageLesionWindow.js index df9550a..8859eaf 100644 --- a/sources/services/gallery/multiimageLesionWindow.js +++ b/sources/services/gallery/multiimageLesionWindow.js @@ -1,4 +1,4 @@ -import {createZoomableImageView} from "app-services/zoomImages"; +import {createZoomableImage, restoreImageViewExtent} from "app-services/zoomImages"; import constants from "../../constants"; import galleryImagesUrls from "../../models/galleryImagesUrls"; @@ -57,9 +57,9 @@ export default class MultiLesionWindowService { } async init() { - const initZoomableImageView = async (imageObj) => { - imageObj._imageView = await createZoomableImageView( - imageObj.$view.getElementsByClassName("zoomable-image")[0] + const initZoomableImage = async (imageObj) => { + imageObj._zoomableImageProperties = await createZoomableImage( + this.getZoomableImageNode(imageObj.$view) ); }; @@ -67,7 +67,7 @@ export default class MultiLesionWindowService { images.forEach((imageObj) => { imageObj.attachEvent("onBeforeRender", this.updateImage); imageObj.attachEvent("onAfterRender", async () => { - await initZoomableImageView(imageObj); + await initZoomableImage(imageObj); }); imageObj.attachEvent("onAfterLoad", this.updateImage); }); @@ -387,6 +387,10 @@ export default class MultiLesionWindowService { } } + getZoomableImageNode(containerNode) { + return containerNode.getElementsByClassName("zoomable-image")[0]; + } + changeWindowMode() { if (this._fullscreen) { this._fullscreen = false; @@ -404,6 +408,13 @@ export default class MultiLesionWindowService { this._fullScreenButton.hide(); this._windowedButton.show(); } + + const images = [this._leftImage, this._rightImage]; + images.forEach((imageObj) => { + restoreImageViewExtent(imageObj._zoomableImageProperties, + this.getZoomableImageNode(imageObj.$view)); + }); + this.searchImagesByQueryHandler(); } diff --git a/sources/services/zoomImages.js b/sources/services/zoomImages.js index 2578b68..73cce89 100644 --- a/sources/services/zoomImages.js +++ b/sources/services/zoomImages.js @@ -15,15 +15,24 @@ const DESKTOP_SIZE = "100%"; /** * @typedef {import("ol/View").default} OlView + * @typedef {import("ol/Map").default} OlMap + */ + +/** + * @typedef {Object} ZoomableImageProperties + * @property {OlView} view + * @property {OlMap} map + * @property {number} width + * @property {number} height */ /** * Creates and initializes an OpenLayers View with zoom functionality for the given HTML element * Works with jpg/png/tif formats * @param {HTMLElement} htmlElement with isic_id attribute - * @returns {Promise} + * @returns {Promise} */ -export async function createZoomableImageView(htmlElement) { +export async function createZoomableImage(htmlElement) { const imageUrl = galleryImagesUrls.getNormalImageUrl(htmlElement?.getAttribute("isic_id")); if (!htmlElement || !imageUrl) return null; @@ -57,8 +66,7 @@ export async function createZoomableImageView(htmlElement) { } const imageView = new View({extent}); - // eslint-disable-next-line no-new - new Map({ + const imageMap = new Map({ target: htmlElement, layers: [layer], view: imageView, @@ -72,7 +80,12 @@ export async function createZoomableImageView(htmlElement) { // Need to set width or height because the map container's width or height are automatically 0 setImageContainerSize(htmlElement, width, height); - return imageView; + return { + view: imageView, + map: imageMap, + width, + height + }; } /** @@ -116,3 +129,15 @@ export function zoomImage(imageView, isZoomIn) { imageView.animate({zoom: newZoom, duration: 250}); } + +/** + * @param {ZoomableImageProperties} properties + * @param {htmlElement} zoomableImageNode + * @returns {void} + */ +export function restoreImageViewExtent(properties, zoomableImageNode) { + const extentBeforeResize = properties.view.calculateExtent(properties.map.getSize()); + setImageContainerSize(zoomableImageNode, properties.width, properties.height); + properties.map.updateSize(); + properties.view.fit(extentBeforeResize, {size: properties.map.getSize()}); +} diff --git a/sources/views/subviews/gallery/gallery.js b/sources/views/subviews/gallery/gallery.js index ce29f84..de66a02 100644 --- a/sources/views/subviews/gallery/gallery.js +++ b/sources/views/subviews/gallery/gallery.js @@ -372,6 +372,8 @@ export default class GalleryView extends JetView { $$(ID_CONTENT_HEADER), this.imageWindow, $$(imageWindow.getViewerId()), + $$(imageWindow.getSliderButtonId()), + $$(imageWindow.getMetadataContainerId()), $$(imageWindow.getMetadataLayoutId()), this.metadataWindow, $$(metadataWindow.getMetadataLayoutId()), diff --git a/sources/views/subviews/gallery/galleryMobile.js b/sources/views/subviews/gallery/galleryMobile.js index 6bd631f..44c620b 100644 --- a/sources/views/subviews/gallery/galleryMobile.js +++ b/sources/views/subviews/gallery/galleryMobile.js @@ -554,6 +554,8 @@ export default class GalleryMobileView extends JetView { $$(ID_CONTENT_HEADER), // contentHeaderTemplate this.imageWindow, // imageWindowInstance $$(mobileImageWindow.getViewerId()), // imageWindowViewer + null, // imageWindowSlideButton + null, // imageWindowMetadataContainer null, // imageWindowMetadata null, // metadataWindow null, // metadataWindowMetadata diff --git a/sources/views/subviews/gallery/windows/imageWindow.js b/sources/views/subviews/gallery/windows/imageWindow.js index a04c9c0..423cf43 100644 --- a/sources/views/subviews/gallery/windows/imageWindow.js +++ b/sources/views/subviews/gallery/windows/imageWindow.js @@ -56,16 +56,6 @@ const slideButton = { labelLeft: "off", labelRight: "on", value: false, - on: { - onChange(newv) { - if (newv) { - $$(metadataContainer.id).show(); - } - else { - $$(metadataContainer.id).hide(); - } - } - } }; const windowBody = { @@ -134,6 +124,10 @@ function getMetadataLayoutId() { return layoutForMetadata.id; } +function getMetadataContainerId() { + return metadataContainer.id; +} + function getZoomButtonTemplateId() { return zoomButtonsTemplate.id; } @@ -145,5 +139,6 @@ export default { getViewerWithoutControlsId, getSliderButtonId, getMetadataLayoutId, + getMetadataContainerId, getZoomButtonTemplateId }; From 8489fc1ad71a922f6f92ab6f1b9537050d6cdfd9 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Thu, 7 Aug 2025 13:33:10 +0300 Subject: [PATCH 11/30] Fix 404 error on redirect with special character filters (/^:<>) --- sources/views/subviews/gallery/gallery.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sources/views/subviews/gallery/gallery.js b/sources/views/subviews/gallery/gallery.js index de66a02..1ae07dd 100644 --- a/sources/views/subviews/gallery/gallery.js +++ b/sources/views/subviews/gallery/gallery.js @@ -465,10 +465,11 @@ export default class GalleryView extends JetView { const galleryDataview = this.getGalleryDataview(); this.galleryContextMenu.attachTo(galleryDataview); - const isicId = this.getRoot().$scope.getParam("image"); if (util.isMobilePhone()) { - const filter = this.getRoot().$scope.getParam("filter"); - this.app.show(`${constants.PATH_GALLERY_MOBILE}?image=${isicId ?? ""}&filter=${filter ?? ""}`); + const hash = window.location.hash; + const questionMarkIndex = hash.indexOf("?"); + const queryString = hash.substring(questionMarkIndex) || ""; + this.app.show(`${constants.PATH_GALLERY_MOBILE}${queryString}`); } const imgTemplateView = this.imageWindow.queryView({id: imageWindow.getViewerId()})?.$view; if (imgTemplateView) { From 1d8c5d5d267587973842ec30ca004280b8cedeca Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Thu, 7 Aug 2025 13:37:46 +0300 Subject: [PATCH 12/30] Rename image enlargement window --- sources/views/subviews/gallery/windows/imageWindow.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sources/views/subviews/gallery/windows/imageWindow.js b/sources/views/subviews/gallery/windows/imageWindow.js index 423cf43..4050845 100644 --- a/sources/views/subviews/gallery/windows/imageWindow.js +++ b/sources/views/subviews/gallery/windows/imageWindow.js @@ -88,19 +88,13 @@ const windowBody = { }; function getConfig(id, studyImage, closeCallback) { - let windowTitle; templateViewer.id = `viewer-${webix.uid()}`; templateViewerWithoutControls.id = `viewer-without-controls-${webix.uid()}`; slideButton.id = `slidebutton-${webix.uid()}`; layoutForMetadata.id = `layout-for-metadata-${webix.uid()}`; metadataContainer.id = `metadata-container-${webix.uid()}`; zoomButtonsTemplate.id = `zoombuttons-template-${webix.uid()}`; - if (!studyImage) { - windowTitle = "Metadata"; - } - else { - windowTitle = studyImage; - } + const windowTitle = studyImage ?? ""; return windowWithHeader.getConfig(id, windowBody, windowTitle, closeCallback); } From d5062de0f96a50a228a034ee42e0762a560ee43d Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Mon, 11 Aug 2025 11:08:37 +0300 Subject: [PATCH 13/30] Adapt window sizes to screen resolution, delete unused variable --- .../services/gallery/multiimageLesionWindow.js | 4 ++-- .../subviews/gallery/windows/imageWindow.js | 4 ++-- .../subviews/gallery/windows/metadataWindow.js | 2 +- .../gallery/windows/multiImageLesionWindow.js | 16 +++++++++------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sources/services/gallery/multiimageLesionWindow.js b/sources/services/gallery/multiimageLesionWindow.js index 8859eaf..9bc10fd 100644 --- a/sources/services/gallery/multiimageLesionWindow.js +++ b/sources/services/gallery/multiimageLesionWindow.js @@ -394,8 +394,8 @@ export default class MultiLesionWindowService { changeWindowMode() { if (this._fullscreen) { this._fullscreen = false; - this._window.define("width", 1240); - this._window.define("height", 750); + this._window.define("width", this._window.config.initialWidth); + this._window.define("height", this._window.config.initialHeight); this._window.define("position", "center"); this._fullScreenButton.show(); this._windowedButton.hide(); diff --git a/sources/views/subviews/gallery/windows/imageWindow.js b/sources/views/subviews/gallery/windows/imageWindow.js index 4050845..d718e67 100644 --- a/sources/views/subviews/gallery/windows/imageWindow.js +++ b/sources/views/subviews/gallery/windows/imageWindow.js @@ -61,8 +61,8 @@ const slideButton = { const windowBody = { css: "metadata-window-body", paddingX: 35, - width: 1100, - height: 610, + width: Math.floor(window.innerWidth * 0.7), + height: Math.floor(window.innerHeight * 0.7), type: "clean", rows: [ { diff --git a/sources/views/subviews/gallery/windows/metadataWindow.js b/sources/views/subviews/gallery/windows/metadataWindow.js index 9efa645..ac79fc3 100644 --- a/sources/views/subviews/gallery/windows/metadataWindow.js +++ b/sources/views/subviews/gallery/windows/metadataWindow.js @@ -10,7 +10,7 @@ const windowBody = { css: "metadata-window-body", paddingX: 35, width: 523, - height: 610, + height: Math.floor(window.innerHeight * 0.7), type: "clean", rows: [ { // this container is needed to draw external borders diff --git a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js index 728f58b..58ae4d4 100644 --- a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js +++ b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js @@ -106,13 +106,11 @@ function getConfig(windowTitle, closeCallback) { const leftTemplateViewer = getTemplateViewer( ID_LEFT_IMAGE, - true, constants.MULTI_LESION_SIDE.LEFT ); const rightTemplateViewer = getTemplateViewer( ID_RIGHT_IMAGE, - true, constants.MULTI_LESION_SIDE.RIGHT ); @@ -216,11 +214,16 @@ function getConfig(windowTitle, closeCallback) { ] }; + const initialWidth = Math.floor(window.innerWidth * 0.85); + const initialHeight = Math.floor(window.innerHeight * 0.85); + return { view: "window", id: ID_MULTI_IMAGE_LESION_WINDOW, - width: 1240, - height: 750, + width: initialWidth, + height: initialHeight, + initialWidth, + initialHeight, css: "window-with-header", modal: true, fullscreen: false, @@ -508,10 +511,9 @@ function getVerticalSlider(id, side) { /** * Description placeholder * - * @param {boolean} showButtons * @returns {webix.ui.templateConfig} */ -function getTemplateViewer(id, showButtons, side) { +function getTemplateViewer(id, side) { return { view: "template", id, @@ -523,7 +525,7 @@ function getTemplateViewer(id, showButtons, side) { return `
- ${showButtons && lesionsImages.length > 1 ? '' : ""} + ${lesionsImages.length > 1 ? '' : ""} `; }, borderless: true From c6ea3c5193b320c8251e017ee05c152fcf7c9d6b Mon Sep 17 00:00:00 2001 From: EkaterinaPoddubskaya Date: Thu, 14 Aug 2025 12:52:52 +0300 Subject: [PATCH 14/30] Change mobile header layout to show filtered images count --- sources/styles/pages.less | 10 -------- .../views/subviews/gallery/galleryMobile.js | 24 ++++++------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/sources/styles/pages.less b/sources/styles/pages.less index 70fd9b2..3a6c20e 100644 --- a/sources/styles/pages.less +++ b/sources/styles/pages.less @@ -1497,10 +1497,6 @@ } } -.gallery-images-footer { - -} - .mobile-gallery-header-logo { cursor: pointer; } @@ -1511,12 +1507,6 @@ font-weight: bold; } -.gallery-header-mobile-context { - .webix_template { - position: relative; - } -} - .webix_view.webix_control.webix_el_button.filtered .webix_img_btn .fas.fa-filter{ color: @select-color; } diff --git a/sources/views/subviews/gallery/galleryMobile.js b/sources/views/subviews/gallery/galleryMobile.js index 44c620b..6b9a275 100644 --- a/sources/views/subviews/gallery/galleryMobile.js +++ b/sources/views/subviews/gallery/galleryMobile.js @@ -58,8 +58,8 @@ export default class GalleryMobileView extends JetView { const logo = { template: "ISIC", - gravity: 1, css: "mobile-gallery-header-logo", + width: 45, borderless: true, onClick: { "mobile-gallery-header-logo": () => menuHandlerService.clickHome() @@ -95,7 +95,6 @@ export default class GalleryMobileView extends JetView { const userPanel = { view: "multiview", css: "userbar-mobile userbar", - gravity: 1, cells: [ loginPanel, logoutPanel @@ -135,8 +134,7 @@ export default class GalleryMobileView extends JetView { const filteredImagesCountTemplate = { id: ID_CONTENT_HEADER, - css: "gallery-header-mobile-context", - width: 1, + maxWidth: 60, template(obj) { const result = obj?.filtered ? `(${state.filteredImages.filteredImagesCount})` : ""; return result; @@ -185,19 +183,11 @@ export default class GalleryMobileView extends JetView { id: ID_MOBILE_GALLERY_HEADER, cols: [ {width: 20}, - { - cols: [ - logo, - {gravity: 1} - ] - }, - { - cols: [ - openFilterButton, - filteredImagesCountTemplate, - userPanel - ] - } + logo, + {}, + openFilterButton, + filteredImagesCountTemplate, + userPanel ] }; From f894f3149e03fe7fe8ebbee206c11b6becf76cb0 Mon Sep 17 00:00:00 2001 From: kpoddubskaya Date: Mon, 11 Aug 2025 15:55:55 +0300 Subject: [PATCH 15/30] Increase anchor icon width --- .../views/subviews/gallery/windows/multiImageLesionWindow.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js index 58ae4d4..1ae8f3e 100644 --- a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js +++ b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js @@ -52,7 +52,7 @@ function getConfig(windowTitle, closeCallback) { const leftAnchorIcon = { view: "icon", id: ID_LEFT_ANCHOR_ICON, - width: 20, + width: 30, height: 20, icon: "fas fa-anchor" }; @@ -60,7 +60,7 @@ function getConfig(windowTitle, closeCallback) { const rightAnchorIcon = { view: "icon", id: ID_RIGHT_ANCHOR_ICON, - width: 20, + width: 30, height: 20, icon: "fas fa-anchor" }; From ea4276852cad2f5f5ebe6cb68a7b88440f724df1 Mon Sep 17 00:00:00 2001 From: EkaterinaPoddubskaya Date: Thu, 14 Aug 2025 13:34:39 +0300 Subject: [PATCH 16/30] Refactor anchor icon and group dropdown creation to avoid duplication --- .../gallery/windows/multiImageLesionWindow.js | 101 ++++++++---------- 1 file changed, 43 insertions(+), 58 deletions(-) diff --git a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js index 1ae8f3e..a5e5c87 100644 --- a/sources/views/subviews/gallery/windows/multiImageLesionWindow.js +++ b/sources/views/subviews/gallery/windows/multiImageLesionWindow.js @@ -45,64 +45,14 @@ function getConfig(windowTitle, closeCallback) { 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: 30, - height: 20, - icon: "fas fa-anchor" - }; - - const rightAnchorIcon = { - view: "icon", - id: ID_RIGHT_ANCHOR_ICON, - width: 30, - 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 leftAnchorIcon = getAnchorIcon(ID_LEFT_ANCHOR_ICON); + const rightAnchorIcon = getAnchorIcon(ID_RIGHT_ANCHOR_ICON); - 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 leftGroupDropdown = getGroupDropdown(ID_LEFT_DROP_DOWN_FILTER); + const rightGroupDropdown = getGroupDropdown(ID_RIGHT_DROP_DOWN_FILTER); const leftTemplateViewer = getTemplateViewer( ID_LEFT_IMAGE, @@ -316,8 +266,6 @@ function getConfig(windowTitle, closeCallback) { } /** - * Description placeholder - * * @returns {webix.ui.listConfig} */ function getTopSlider(topPanelID, sliderID, prevButtonID, nextButtonID) { @@ -509,8 +457,6 @@ function getVerticalSlider(id, side) { /** - * Description placeholder - * * @returns {webix.ui.templateConfig} */ function getTemplateViewer(id, side) { @@ -532,6 +478,9 @@ function getTemplateViewer(id, side) { }; } +/** + * @returns {webix.ui.labelConfig} + */ function getImageLabel(imageNameId) { return { view: "label", @@ -542,6 +491,42 @@ function getImageLabel(imageNameId) { }; } +/** + * @returns {webix.ui.iconConfig} + */ +function getAnchorIcon(id) { + return { + view: "icon", + id: id, + width: 30, + height: 20, + icon: "fas fa-anchor" + }; +} + +/** + * @returns {webix.ui.richselectConfig} + */ +function getGroupDropdown(id) { + return { + view: "richselect", + id: id, + css: "multilesion-filter-dropdown", + label: "Group by:", + labelAlign: "left", + width: 270, + height: 30, + labelWidth: 75, + value: constants.MULTI_LESION_GROUP_BY.TIME, + 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, + ] + }; +} + function footerTemplateFunction(obj, /* common */) { const lesionID = lesionsModel.getItemLesionID(obj); const lesionImagesCount = lesionID From 3477121ce514d5af2970f8b59f02b72ae3066f84 Mon Sep 17 00:00:00 2001 From: EkaterinaPoddubskaya Date: Mon, 18 Aug 2025 18:57:46 +0300 Subject: [PATCH 17/30] Make adaptive design for image name, checkbox, icons, and icon badges --- sources/constants.js | 8 - sources/services/gallery/gallery.js | 2 +- sources/styles/common.less | 26 +--- sources/styles/pages.less | 80 +++++----- sources/utils/util.js | 139 ++++++++---------- .../windows/imagesSelectionWindow.js | 2 +- .../views/subviews/gallery/galleryMobile.js | 50 ++----- .../subviews/gallery/parts/galleryDataview.js | 124 +++++----------- .../gallery/windows/multiImageLesionWindow.js | 85 +++-------- 9 files changed, 177 insertions(+), 339 deletions(-) diff --git a/sources/constants.js b/sources/constants.js index 839edb3..f7829a7 100644 --- a/sources/constants.js +++ b/sources/constants.js @@ -185,14 +185,6 @@ export default { MISSING_KEY_VALUE: "missing key", COLLECTION_KEY: "collections", - 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, diff --git a/sources/services/gallery/gallery.js b/sources/services/gallery/gallery.js index eace2fa..44483a4 100644 --- a/sources/services/gallery/gallery.js +++ b/sources/services/gallery/gallery.js @@ -411,7 +411,7 @@ class GalleryService { const newImageHeight = Math.round(multiplier * newImageWidth); newInnerImageNameSize = Math.round(newImageHeight * fontSizeMultiplier); - util.setNewThumnailsNameFontSize(newInnerImageNameSize); + util.setImageNameFontSize(newInnerImageNameSize); util.setDataviewSelectionId(id); this._setDataviewColumns(newItemWidth, previousItemHeight, newImageWidth, newImageHeight); if (callUpdatePager) { diff --git a/sources/styles/common.less b/sources/styles/common.less index 56134d3..b535581 100644 --- a/sources/styles/common.less +++ b/sources/styles/common.less @@ -271,34 +271,22 @@ a, .link { /* 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; + display: flex; + justify-content: center; + align-items: center; + width: 60%; + height: 60%; + top: -30%; + border-radius: 50%; 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; -} - .disabled-badge { background-color: #cccccc; } diff --git a/sources/styles/pages.less b/sources/styles/pages.less index 3a6c20e..5838ae4 100644 --- a/sources/styles/pages.less +++ b/sources/styles/pages.less @@ -1258,6 +1258,7 @@ position: absolute; top: 0; left: 0; + right: 0; z-index: 10; width: 100%; height: 100%; @@ -1402,86 +1403,83 @@ /* mobile-gallery-images-info */ .mobile-gallery-images-info { - font-size: 20px; display: flex; justify-content: space-between; - padding: 5px 30px; - border-width: 0px; + align-items: center; + padding: 0px 30px; + + .thumbnails-name { + font-size: 20px; + line-height: 30px; + } } /* gallery-images-container */ .gallery-images-header { - padding-bottom: 5px; - background: rgba(255,255,255,.60); + background: rgba(255, 255, 255, 0.6); + display: flex; + justify-content: space-around; + align-items: center; + height: ~"min(35%, 27px)"; + width: 100%; .thumbnails-name { - color: rgba(0,0,0,0.8); + color: rgba(0, 0, 0, 0.8); font-size: 14px; font-weight: 500; - line-height: 20px; - margin-right: 35px; - padding: 5px 0 0 15px; text-align: left; } + .gallery-images-checkbox { - float: right; - padding-right: 12px; + height: 80%; + aspect-ratio: 1 / 1; + margin: 0; } } -.gallery-images-checkbox { - overflow: hidden; - .checkbox-ctrl { - .webix_el_group { - padding-bottom: 2px; - .box-sizing(border-box); - } - } +.gallery-images-buttons { + display: flex; + flex-wrap: nowrap; + justify-content: center; } -.gallery-images-buttons { - text-align: center; +.gallery-images-info .gallery-images-buttons { + column-gap: ~"min(5%, 8px)"; position: absolute; left: 0; - bottom: 0px; + bottom: ~"min(5%, 10px)"; width: 100%; + height: ~"min(30%, 22px)"; } -.gallery-images-button-elem, .gallery-images-button-elem-disabled { - display: inline-block; - vertical-align: top; - width: 22px; +.mobile-gallery-images-info .gallery-images-buttons { + column-gap: 16px; height: 22px; - 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 { - display: block; - line-height: 0; + height: 100%; + aspect-ratio: 1 / 1; + display: flex; + align-items: center; + justify-content: center; .box-sizing(border-box); - padding: 1px; border: 1px solid @lines-color; .border-radius(2px); background-color: #FFFFFF; - text-align: center; .transition(all .2s ease-in-out); .gallery-icon-svg { - display: inline-block; - vertical-align: middle; + width: 85%; + height: 85%; } - .gallery-icon-svg, .gallery-icon-use { - width: 18px; - height: 18px; + width: 100%; + height: 100%; } .gallery-icon-use { diff --git a/sources/utils/util.js b/sources/utils/util.js index fb1b31a..7ad4e06 100644 --- a/sources/utils/util.js +++ b/sources/utils/util.js @@ -250,83 +250,69 @@ function getFilterLabelId(filterId) { return `filter-label-${filterId || ""}` } -function getImageIconDimensions() { - const initialIconWidth = constants.DEFAULT_GALLERY_IMAGE_ICON_WIDTH; - const initialIconHeight = constants.DEFAULT_GALLERY_IMAGE_ICON_HEIGHT; - const initialIconContainerWidth = constants.DEFAULT_GALLERY_IMAGE_ICON_CONTAINER_WIDTH; - const initialIconContainerHeight = constants.DEFAULT_GALLERY_IMAGE_ICON_CONTAINER_HEIGHT; - const defaultIconWidthDifference = initialIconContainerWidth - initialIconWidth; - const deafultIconHeightDifference = initialIconContainerHeight - initialIconHeight; - - let bottomOffsetPercentage; - let newIconWidth; - let newIconHeight; - let newIconContainerWidth; - let newIconContainerHeight; - let iconContainerDimensions; - let iconDimensions; - let dataviewItemWidth = getDataviewItemWidth(); - - if (dataviewItemWidth <= 110) { - if (dataviewItemWidth > 100) { - newIconContainerWidth = initialIconWidth - 1; - newIconContainerHeight = initialIconHeight - 1; - newIconWidth = newIconContainerWidth - defaultIconWidthDifference; - newIconHeight = newIconContainerHeight - deafultIconHeightDifference; - bottomOffsetPercentage = -8; - } - else if (dataviewItemWidth <= 100 && dataviewItemWidth > 95) { - newIconContainerWidth = initialIconWidth - 2; - newIconContainerHeight = initialIconHeight - 2; - newIconWidth = newIconContainerWidth - defaultIconWidthDifference; - newIconHeight = newIconContainerHeight - deafultIconHeightDifference; - bottomOffsetPercentage = -10; - } - else if (dataviewItemWidth <= 95) { - newIconContainerWidth = initialIconWidth - 3; - newIconContainerHeight = initialIconHeight - 3; - newIconWidth = newIconContainerWidth - defaultIconWidthDifference; - newIconHeight = newIconContainerHeight - deafultIconHeightDifference; - bottomOffsetPercentage = -12; - } - iconContainerDimensions = { - width: newIconContainerWidth, - height: newIconContainerHeight - }; - - iconDimensions = { - width: newIconWidth, - height: newIconHeight - }; - } - else { - iconContainerDimensions = { - width: initialIconContainerWidth, - height: initialIconContainerHeight - }; - - iconDimensions = { - width: initialIconWidth, - height: initialIconHeight - }; - bottomOffsetPercentage = 0; - } - return [iconContainerDimensions, iconDimensions, bottomOffsetPercentage]; +/** + * @param {string} iconId + * @param {boolean} isIconEnabled + * @param {string} tooltipText + * @param {string} gtmClass + * @param {string} iconBadges + * @returns {string} + */ +function getIconButton(iconId, isIconEnabled, tooltipText, gtmClass, iconBadges) { + const buttonClass = `${isIconEnabled ? '' : 'gallery-images-button-elem-disabled'} tooltip-container tooltip-gallery-images`; + return ` +
+ + + + + + ${tooltipText} + ${iconBadges} +
+ `; +}; + +/** + * @param {string} badgeText + * @param {string} tooltipText + * @param {boolean} isEnabledBadge + * @param {boolean} isLeftBadge + * @returns {string} + */ +function getIconBadge(badgeText, tooltipText, isEnabledBadge, isLeftBadge) { + const badgeAbilityClass = isEnabledBadge ? "" : "disabled-badge"; + const maxIconBadgeFontSize = 10; + + const iconBadgeStyle = ` + font-size: ${Math.min(getImageNameFontSize(), maxIconBadgeFontSize)}px; + ${isLeftBadge ? `transform: translateX(-50%); left: 0;` : `transform: translateX(50%); right: 0;`} + `; + + return ` + + ${badgeText} + + ${tooltipText} + `; } -function setNewThumnailsNameFontSize(nameFontSize) { - if (nameFontSize > 28) { - nameFontSize = 28; - } - else if (nameFontSize < 7.5) { - nameFontSize = 7.5; - } - webix.storage.local.put(`thumbnailsNameFontSize-${getUserId()}`, nameFontSize); +/** + * @param {number} imageNameFontSize + */ +function setImageNameFontSize(imageNameFontSize) { + const maxImageNameFontSize = 28; + const minImageNameFontSize = 5; + const fontSize = Math.min(maxImageNameFontSize, Math.max(minImageNameFontSize, imageNameFontSize)); + webix.storage.local.put(`thumbnailsNameFontSize-${getUserId()}`, fontSize); } -function getNewThumnailsNameFontSize() { - let fontSize = webix.storage.local.get(`thumbnailsNameFontSize-${getUserId()}`); - return fontSize || 14; +/** + * @returns {number} + */ +function getImageNameFontSize() { + const storageImageNameFontSize = webix.storage.local.get(`thumbnailsNameFontSize-${getUserId()}`); + return storageImageNameFontSize || constants.DEFAULT_GALLERY_IMAGE_NAME_FONT_SIZE; } function separateThousandsInNumber(number) { @@ -570,9 +556,10 @@ export default { getDataviewItemHeight, setDataviewSelectionId, getDataviewSelectionId, - getImageIconDimensions, - setNewThumnailsNameFontSize, - getNewThumnailsNameFontSize, + getIconButton, + getIconBadge, + setImageNameFontSize, + getImageNameFontSize, separateThousandsInNumber, isObjectEmpty, angleIconChange, diff --git a/sources/views/subviews/createStudy/windows/imagesSelectionWindow.js b/sources/views/subviews/createStudy/windows/imagesSelectionWindow.js index 8998279..4b11f04 100644 --- a/sources/views/subviews/createStudy/windows/imagesSelectionWindow.js +++ b/sources/views/subviews/createStudy/windows/imagesSelectionWindow.js @@ -89,7 +89,7 @@ export default class ImagesSelectionWindow extends JetView {