From cede3bfbcb1b63f51b60ef7867eecb03dbb524ce Mon Sep 17 00:00:00 2001 From: Y Date: Tue, 1 Jul 2025 01:55:04 -0400 Subject: [PATCH 001/155] replace DOMNodeInserted with MutationObserver approach --- apps/templates/search_results.html | 50 ++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/apps/templates/search_results.html b/apps/templates/search_results.html index d5e8450c..b2e89fc1 100644 --- a/apps/templates/search_results.html +++ b/apps/templates/search_results.html @@ -37,31 +37,55 @@ e.preventDefault(); }); - // Updated initializeSelectize + // initializeSelectize stays the same function initializeSelectize() { - jQuery(".custom-search-selectize").each(function () { - if (!jQuery(this).hasClass("selectized")) { - jQuery(this).selectize({ - plugins: ["clear_button"], - placeholder: "Select one or more..." + jQuery(".custom-search-selectize").each(function () { + if (!jQuery(this).hasClass("selectized")) { + jQuery(this).selectize({ + plugins: ["clear_button"], + placeholder: "Select one or more..." + }); + } }); - } - }); } - // Initial setup jQuery(function () { + // 1) Initial setup: your static selects... jQuery("#id_collection, #id_author, #id_language").selectize({ plugins: ["clear_button"], placeholder: "Select one or more..." }); + // 2) …and any .custom-search-selectize already in the DOM initializeSelectize(); - }); - // Safer re-init if new elements inserted dynamically - jQuery(document).on("DOMNodeInserted", ".custom-search-selectize", function () { - initializeSelectize(); + // 3) Watch for dynamically inserted selects + const observer = new MutationObserver((mutations) => { + mutations.forEach(mutation => { + mutation.addedNodes.forEach(node => { + // only care about element nodes + if (node.nodeType !== Node.ELEMENT_NODE) return; + + // if the added node *is* a custom-search-selectize, or *contains* one… + if (node.matches('.custom-search-selectize') || + node.querySelector('.custom-search-selectize') + ) { + initializeSelectize(); + } + }); + }); + }); + + // Scope this to a tighter container if you know where new selects appear; + // using document.body will catch everything, but you can replace + // document.body with document.querySelector('#your-results-container') + observer.observe(document.body, { + childList: true, + subtree: true + }); + + // Optional: if at some point you no longer need to observe: + // observer.disconnect(); }); {% endblock %} From d70127c51b2387c26c6a48d2e08808113ee26802 Mon Sep 17 00:00:00 2001 From: Jay Varner Date: Wed, 30 Jul 2025 12:08:00 -0400 Subject: [PATCH 002/155] Remove dependabot file --- .github/dependabot.yml | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index b0a037e0..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: 2 -updates: -- package-ecosystem: pip - directory: "/" - schedule: - interval: weekly - time: "10:00" - open-pull-requests-limit: 10 - target-branch: develop - ignore: - - dependency-name: ipdb - versions: - - 0.13.4 - - 0.13.5 - - 0.13.6 - - dependency-name: django-crispy-forms - versions: - - 1.10.0 - - 1.11.0 - - 1.11.1 - - dependency-name: django - versions: - - 2.2.13 - - dependency-name: coverage - versions: - - "5.4" - - dependency-name: pytest-django - versions: - - 4.1.0 - - dependency-name: wagtail - versions: - - 2.9.3 From 170336bf0cb23e13b1b7da848f347d98c4219a5c Mon Sep 17 00:00:00 2001 From: Y Date: Wed, 27 Aug 2025 15:15:10 -0400 Subject: [PATCH 003/155] update external image download link --- apps/static/js/custom.js | 234 +++++++++++++++++++++++++++++ apps/static/js/index.js | 3 +- apps/templates/_page/_exports.html | 6 +- apps/templates/base.html | 1 + 4 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 apps/static/js/custom.js diff --git a/apps/static/js/custom.js b/apps/static/js/custom.js new file mode 100644 index 00000000..0628b218 --- /dev/null +++ b/apps/static/js/custom.js @@ -0,0 +1,234 @@ +console.log('[readux] custom.js loaded'); + +(function () { + var DEBUG = true; // set to false to silence logs + function log(){ if (DEBUG) try { console.log.apply(console, arguments); } catch(_){} } + + // --- helpers ------------------------------------------------------------ + function stripInfoJson(u) { + if (!u) return null; + try { return u.replace(/\/?info\.json$/i, ''); } catch(_) { return u; } + } + function absoluteUrl(u) { + try { return new URL(u, document.baseURI).toString(); } catch(_) { return null; } + } + function buildIiifFullJpg(base) { + var abs = absoluteUrl(stripInfoJson(base)); + if (!abs) return null; + return abs.replace(/\/$/, '') + '/full/full/0/default.jpg'; + } + + function getLink() { return document.getElementById('rx-download-img'); } + function getTemplate(el) { return el && el.getAttribute('data-href-template'); } + + // Try to discover the active IIIF image service from the viewer stack + function svcFromOSD() { + try { + var osd = window.osdViewer || window.viewer || window.osd || window.openSeadragonViewer; + if (!osd || !osd.world || typeof osd.world.getItemAt !== 'function') return null; + var item = osd.world.getItemAt(0); + if (!item) return null; + var ts = item.source || item.tileSource || (item.getTileSource && item.getTileSource()); + var id = ts && (ts['@id'] || ts['id'] || ts['url'] || ts['tileSource']); + return id ? stripInfoJson(id) : null; + } catch(_) { return null; } + } + function svcFromMirador() { + try { + if (window.Mirador && Mirador.viewer && Mirador.viewer.store && Mirador.viewer.store.getState) { + var st = Mirador.viewer.store.getState(); + var winIds = st && st.windows && Object.keys(st.windows); + if (!winIds || !winIds.length) return null; + var win = st.windows[winIds[0]]; + var canvasId = win && win.canvasId; + return canvasId ? stripInfoJson(canvasId) : null; + } + } catch(_) {} + return null; + } + function svcFromNetwork() { + try { + var entries = (performance.getEntriesByType && performance.getEntriesByType('resource')) || []; + for (var i = entries.length - 1; i >= 0; i--) { + var name = entries[i] && entries[i].name; + if (!name || /^chrome-extension:/.test(name)) continue; + if (/\/(full|pct:|\d+,\d+,\d+,\d+)\//.test(name) && /(jpg|jpeg|png|tif|tiff)(\?|$)/i.test(name)) { + try { + var url = new URL(name, document.baseURI); + var parts = url.pathname.split('/').filter(Boolean); + // IIIF image path ends with 4 trailing segments: + // /{region}/{size}/{rotation}/{quality}.{format} + if (parts.length >= 4) { + var baseParts = parts.slice(0, parts.length - 4); // remove 4 trailing segments + var basePath = '/' + baseParts.join('/'); + return url.origin + basePath; + } + } catch(_) {} + } + if (/\/info\.json(\?|$)/.test(name)) { + try { + var url2 = new URL(name, document.baseURI); + return url2.origin + url2.pathname.replace(/\/info\.json(\?|$).*/, ''); + } catch(_) {} + } + } + } catch(_) {} + return null; + } + + function pidFromAnnotator() { + try { + if (window.annotator && annotator.currentPage && annotator.currentPage.pid) return annotator.currentPage.pid; + if (window.readux && readux.state && readux.state.page && readux.state.page.pid) return readux.state.page.pid; + if (window.Rx && Rx.store && Rx.store.state && Rx.store.state.page && Rx.store.state.page.pid) return Rx.store.state.page.pid; + } catch (_) {} + // OpenSeadragon tileSource id often ends with PID; use as a weak fallback + try { + var osd = window.osdViewer || window.viewer || window.osd || window.openSeadragonViewer; + if (osd && osd.world && typeof osd.world.getItemAt === 'function') { + var item = osd.world.getItemAt(0); + var ts = item && (item.source || item.tileSource || (item.getTileSource && item.getTileSource())); + var id = ts && (ts['@id'] || ts['id'] || ts['url']); + if (id) { + var parts = id.split('/').filter(Boolean); + return parts[parts.length - 1]; + } + } + } catch(_) {} + return null; + } + function pidFromDom() { + var el = document.querySelector('[data-page-pid]'); + if (el) return el.getAttribute('data-page-pid'); + var el2 = document.querySelector('.rx-page[data-pid]'); + if (el2) return el2.getAttribute('data-pid'); + return null; + } + + function currentPid() { + return ( + pidFromAnnotator() || + pidFromDom() || + pidFromServiceBase(lastSvc) || + pidFromLinkHref() || + lastPid + ); + } + function pidFromServiceBase(svc) { + if (!svc) return null; + try { + var u = new URL(svc, document.baseURI); + var parts = u.pathname.split('/').filter(Boolean); + return parts.length ? parts[parts.length - 1] : null; + } catch(_) { return null; } + } + + function pidFromLinkHref() { + try { + var el = getLink(); + if (!el || !el.href) return null; + var u = new URL(el.href, document.baseURI); + var parts = u.pathname.split('/').filter(Boolean); + // Remove the 4 trailing IIIF segments if present + if (parts.length >= 4) { + var baseParts = parts.slice(0, parts.length - 4); + if (baseParts.length) return baseParts[baseParts.length - 1] || null; + } + return parts.length ? parts[parts.length - 1] : null; + } catch(_) { return null; } + } + + var lastPid = null; + var lastSvc = null; + + function updateLink(el) { + if (!el) el = getLink(); + if (!el) return; + // Try service-first for accuracy + var svc = svcFromNetwork() || svcFromOSD() || svcFromMirador() || lastSvc; + if (svc) { + var url = buildIiifFullJpg(svc); + if (url && el.href !== url) { + el.href = url; + lastSvc = svc; + lastPid = pidFromServiceBase(svc) || lastPid; + log('[readux] href (svc) ->', url); + return; + } + } + // Fallback: use template + pid + var template = getTemplate(el); + var pid = pidFromAnnotator() || pidFromDom() || lastPid; + if (template && pid) { + var u = template.replace('__PID__', encodeURIComponent(pid)); + if (el.href !== u) { + el.href = u; + lastPid = pid; + log('[readux] href (pid) ->', u); + } + } + } + + function ensureWired() { + var el = getLink(); + if (!el) return; + + // Click-time resolution (always correct at click) + try { + el.addEventListener('click', function (e) { + try { e.preventDefault(); } catch(_) {} + updateLink(el); + try { window.open(el.href, '_blank'); } catch(_) { + try { location.href = el.href; } catch(_) {} + } + }, true); + } catch(_) {} + + // React to common custom events + ['ecds:page-changed', 'readux:page-changed', 'page-changed'].forEach(function (evt) { + window.addEventListener(evt, function () { setTimeout(function(){ updateLink(el); }, 0); }); + }); + + // OSD events + try { + var osd = window.osdViewer || window.viewer || window.osd || window.openSeadragonViewer; + if (osd && typeof osd.addHandler === 'function') { + ['open','page','tile-loaded','add-item','remove-item','item-index-change','animation-finish'] + .forEach(function (evt) { osd.addHandler(evt, function(){ setTimeout(function(){ updateLink(el); }, 0); }); }); + } + } catch(_) {} + + // PerformanceObserver for network tile loads + try { + if (window.PerformanceObserver) { + var po = new PerformanceObserver(function(){ updateLink(el); }); + po.observe({ entryTypes: ['resource'] }); + } + } catch(_) {} + + // DOM & URL changes + try { + var mo = new MutationObserver(function(){ updateLink(el); }); + mo.observe(document.body, { childList:true, subtree:true, attributes:true }); + } catch(_) {} + window.addEventListener('hashchange', function(){ updateLink(el); }); + window.addEventListener('load', function(){ updateLink(el); }); + document.addEventListener('DOMContentLoaded', function(){ updateLink(el); }); + + // Expose manual hook + window.readuxSetCurrentPid = function (pid) { lastPid = pid; updateLink(el); }; + + // initial + updateLink(el); + } + + // Wait until the link exists (in case template renders it late) + if (!getLink()) { + var wait = setInterval(function(){ + if (getLink()) { clearInterval(wait); ensureWired(); } + }, 100); + setTimeout(function(){ try { clearInterval(wait); ensureWired(); } catch(_){} }, 10000); + } else { + ensureWired(); + } +})(); \ No newline at end of file diff --git a/apps/static/js/index.js b/apps/static/js/index.js index deeb23cf..aa5186e8 100644 --- a/apps/static/js/index.js +++ b/apps/static/js/index.js @@ -5,4 +5,5 @@ require('/apps/static/js/vue-readux.js'); window.UIkit = require('uikit'); window.UIkitIcons = require('/apps/static/js/uikit-icons.min.js'); // require('/apps/static/js/project.js'); -window.ECDSAnnotator = require('../../../node_modules/ecds-annotator/dist/ecds-annotator.min.js'); \ No newline at end of file +window.ECDSAnnotator = require('../../../node_modules/ecds-annotator/dist/ecds-annotator.min.js'); +require('/apps/static/js/custom.js'); \ No newline at end of file diff --git a/apps/templates/_page/_exports.html b/apps/templates/_page/_exports.html index ba37e1ec..ea8eb648 100644 --- a/apps/templates/_page/_exports.html +++ b/apps/templates/_page/_exports.html @@ -3,7 +3,11 @@

Download and Expor
From d5314666bc161c174c98279e7c0a98a42441601c Mon Sep 17 00:00:00 2001 From: Y Date: Tue, 16 Sep 2025 02:02:43 -0400 Subject: [PATCH 023/155] remove nav-link class --- apps/static/css/project.scss | 4 ---- apps/templates/_page/_information.html | 2 +- apps/templates/export.html | 2 +- apps/templates/export_download.html | 2 +- apps/templates/snippets/volume_result.html | 4 ++-- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/static/css/project.scss b/apps/static/css/project.scss index b0e7d925..0cfcb140 100644 --- a/apps/static/css/project.scss +++ b/apps/static/css/project.scss @@ -1573,10 +1573,6 @@ fieldset { --contrast: 200%; } -a.nav-link { - text-decoration: none; -} - .uk-button-primary { background-color: var(--link-color); } diff --git a/apps/templates/_page/_information.html b/apps/templates/_page/_information.html index 4b986067..17f91dde 100644 --- a/apps/templates/_page/_information.html +++ b/apps/templates/_page/_information.html @@ -32,7 +32,7 @@
{% for col in volume.collections.all %} - +
{{ col.label }}
{% endfor %} diff --git a/apps/templates/export.html b/apps/templates/export.html index 8daf5018..fedb4296 100644 --- a/apps/templates/export.html +++ b/apps/templates/export.html @@ -16,7 +16,7 @@


Publication Date: {{ volume.published_date }}
{% for col in volume.collections.all %} - Collection: {{ col.label }}{% endfor %} + Collection: {{ col.label }}{% endfor %}
Publisher: {{ volume.published_city }} : {{ volume.publisher }}

diff --git a/apps/templates/export_download.html b/apps/templates/export_download.html index 260e7f03..bfccee04 100644 --- a/apps/templates/export_download.html +++ b/apps/templates/export_download.html @@ -16,7 +16,7 @@


Publication Date: {{ volume.published_date }}
{% for col in volume.collections.all %} - Collection: {{ col.label }}{% endfor %} + Collection: {{ col.label }}{% endfor %}
Publisher: {{ volume.published_city }} : {{ volume.publisher }} diff --git a/apps/templates/snippets/volume_result.html b/apps/templates/snippets/volume_result.html index 80fbf681..d1ea7e97 100644 --- a/apps/templates/snippets/volume_result.html +++ b/apps/templates/snippets/volume_result.html @@ -4,7 +4,7 @@

{% if volume.pid %} @@ -76,7 +76,7 @@

{% endif %} - +
  • diff --git a/apps/templates/_page/_urls.html b/apps/templates/_page/_urls.html index c8e7008c..f732caa9 100644 --- a/apps/templates/_page/_urls.html +++ b/apps/templates/_page/_urls.html @@ -1,42 +1,42 @@ - - + - + {% for col in volume.collections.all %} - - + + {% endfor %} - + - - + {% if "/page/all" in request.get_full_path in request.get_full_path %} - - + + {% else %} - - + + {% endif %} {# External links from IIIF manifest #} {% if volume.external_links.see_also %} - + {% for link in volume.external_links.see_also %} - - + + {% endfor %} - + {% endif %} {% if volume.external_links.related %} - + {% for link in volume.external_links.related %} - - + + {% endfor %} - + {% endif %} diff --git a/apps/templates/page.html b/apps/templates/page.html index 785cd363..bf3d9f48 100644 --- a/apps/templates/page.html +++ b/apps/templates/page.html @@ -20,8 +20,8 @@ diff --git a/package-lock.json b/package-lock.json index a1351c06..77f655f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,9 +13,12 @@ "axios": "^0.23.0", "ecds-annotator": "github:ecds/ecds-annotator#develop", "jquery": "^3.6.0", - "nouislider": "^15.8.1" + "nouislider": "^15.8.1", + "vue": "^2.6.14" }, "devDependencies": { + "@vue/component-compiler-utils": "^3.3.0", + "css-loader": "^7.1.2", "fontawesome": "^5.6.3", "jquery": "^3.6.0", "source-map-loader": "^4.0.0", @@ -23,6 +26,9 @@ "ts-loader": "^9.4.4", "uikit": "^3.17.0", "uikit-icons": "^0.5.0", + "vue-loader": "^15.11.1", + "vue-style-loader": "^4.1.3", + "vue-template-compiler": "^2.6.14", "webpack": "^5.88.2", "webpack-bundle-tracker": "^1.5.0", "webpack-cli": "^4.9.2" @@ -617,6 +623,69 @@ "dev": true, "license": "MIT" }, + "node_modules/@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vue/component-compiler-utils/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true, + "license": "ISC" + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", "dev": true, @@ -921,6 +990,16 @@ "npm": ">=6" } }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -933,6 +1012,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -1171,6 +1257,20 @@ "resolved": "https://registry.npmjs.org/computed-style/-/computed-style-0.1.4.tgz", "integrity": "sha512-WpAmaKbMNmS3OProfHIdJiNleNJdgUrJfbKArXua28QF7+0CoZjlLn0lp6vlc+dl5r2/X9GQiQRQQU4BzSa69w==" }, + "node_modules/consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -1217,10 +1317,66 @@ "node": ">= 8" } }, + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.0", "license": "MIT" }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1345,6 +1501,16 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "dev": true, @@ -1760,6 +1926,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true, + "license": "MIT" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1772,6 +1945,16 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -1792,6 +1975,19 @@ "node": ">=0.10.0" } }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2005,6 +2201,19 @@ "dev": true, "license": "MIT" }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", @@ -2060,6 +2269,21 @@ "node": ">=6.11.5" } }, + "node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/locate-path": { "version": "5.0.0", "dev": true, @@ -2127,6 +2351,16 @@ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", "license": "MIT" }, + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "^0.6.1" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "dev": true, @@ -2173,6 +2407,16 @@ "node": ">= 0.6" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2190,7 +2434,6 @@ } ], "license": "MIT", - "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -2424,7 +2667,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -2492,6 +2734,97 @@ } } }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/postcss-reporter": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.1.0.tgz", @@ -2518,6 +2851,27 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/preact": { "version": "10.27.2", "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.2.tgz", @@ -2528,6 +2882,23 @@ "url": "https://opencollective.com/preact" } }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -2546,6 +2917,13 @@ "react-is": "^16.13.1" } }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true, + "license": "ISC" + }, "node_modules/punycode": { "version": "2.1.1", "dev": true, @@ -3201,6 +3579,13 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -3210,6 +3595,78 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/vue": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", + "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==", + "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", + "license": "MIT" + }, + "node_modules/vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-loader": { + "version": "15.11.1", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.11.1.tgz", + "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "peerDependencies": { + "css-loader": "*", + "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "prettier": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz", + "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "node_modules/vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true, + "license": "MIT" + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", diff --git a/package.json b/package.json index e98080fa..ccdc495a 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,12 @@ "axios": "^0.23.0", "ecds-annotator": "github:ecds/ecds-annotator#develop", "jquery": "^3.6.0", - "nouislider": "^15.8.1" + "nouislider": "^15.8.1", + "vue": "^2.6.14" }, "devDependencies": { + "@vue/component-compiler-utils": "^3.3.0", + "css-loader": "^7.1.2", "fontawesome": "^5.6.3", "jquery": "^3.6.0", "source-map-loader": "^4.0.0", @@ -21,6 +24,9 @@ "ts-loader": "^9.4.4", "uikit": "^3.17.0", "uikit-icons": "^0.5.0", + "vue-loader": "^15.11.1", + "vue-style-loader": "^4.1.3", + "vue-template-compiler": "^2.6.14", "webpack": "^5.88.2", "webpack-bundle-tracker": "^1.5.0", "webpack-cli": "^4.9.2" diff --git a/webpack.config.js b/webpack.config.js index 7f9e086e..57d6114f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,7 @@ const path = require("path"); const webpack = require("webpack"); const BundleTracker = require("webpack-bundle-tracker"); +const { VueLoaderPlugin } = require('vue-loader'); module.exports = { context: __dirname, @@ -11,10 +12,22 @@ module.exports = { path: path.resolve("./apps/static/js/"), filename: "[name].js", }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + // Use compiler+runtime build of Vue 2 + 'vue$': 'vue/dist/vue.esm.js', + '@': path.resolve(__dirname, 'apps/static/js'), + }, + }, devtool: "source-map", - plugins: [new BundleTracker({ filename: "./webpack-stats.json" })], + plugins: [new BundleTracker({ filename: "./webpack-stats.json" }), new VueLoaderPlugin()], module: { rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + }, { test: /\.js$/, enforce: "pre", From fc2cd7bb43cc33c3994653c441af6a29550f5c0f Mon Sep 17 00:00:00 2001 From: Y Date: Fri, 19 Sep 2025 22:23:12 -0400 Subject: [PATCH 070/155] fix a style bug --- apps/static/css/readux.scss | 3 +-- apps/templates/_page/_information.html | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/static/css/readux.scss b/apps/static/css/readux.scss index e4028359..6a29af7a 100644 --- a/apps/static/css/readux.scss +++ b/apps/static/css/readux.scss @@ -376,8 +376,7 @@ ul.listing-thumbs > li > img.thumbnail-image { height: 150px; } .rx-volume-search em { color: $rx-color-mario-red !important; } .rx-volume-search { max-height: 75vh; } .rx-padding-extra-small { padding: 10px; } -.rx-scrollable-area { max-height: 75vh; overflow-y: auto; margin-right: -5px; -ms-overflow-style: none; scrollbar-width: none; &::-webkit-scrollbar { display: none; } } -.padding-right-1rem { padding-right: 1rem; } +.rx-scrollable-area { max-height: 75vh; overflow-y: auto; -ms-overflow-style: none; scrollbar-width: none; &::-webkit-scrollbar { display: none; } } .count { position: absolute; top: 0; right: 0; padding-top: 20%; padding-right: 20%; height: 10px; width: 10px; font-size: 10px; text-align: center; } .rx-flex { display: flex; } .uk-accordion-content { padding: 0.5rem 10px;} diff --git a/apps/templates/_page/_information.html b/apps/templates/_page/_information.html index 17f91dde..12f72cb9 100644 --- a/apps/templates/_page/_information.html +++ b/apps/templates/_page/_information.html @@ -1,7 +1,7 @@ {% load split_to_bullets %}
    -
    +
    • Basic From cf83e407766701ae81f21461cb3ee00713d71aab Mon Sep 17 00:00:00 2001 From: Y Date: Fri, 19 Sep 2025 22:37:13 -0400 Subject: [PATCH 071/155] speed up the js build time (17s to 2s) --- webpack.config.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 57d6114f..e0e62051 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,7 +20,8 @@ module.exports = { '@': path.resolve(__dirname, 'apps/static/js'), }, }, - devtool: "source-map", + devtool: "eval-cheap-module-source-map", // Dev: fastest reasonable source maps + // devtool: "source-map", // Dev: switch to full source maps for debugging if needed plugins: [new BundleTracker({ filename: "./webpack-stats.json" }), new VueLoaderPlugin()], module: { rules: [ @@ -32,6 +33,13 @@ module.exports = { test: /\.js$/, enforce: "pre", use: ["source-map-loader"], + exclude: [ + /node_modules\/ecds-annotator/, + /node_modules\/uikit/, + /node_modules\/jquery/, + /node_modules\/@selectize\/selectize/, + /node_modules\/nouislider/, + ], }, { test: /\.css$/i, From 78162957c1068012733e62807cdbf26c9dd888c4 Mon Sep 17 00:00:00 2001 From: Y Date: Fri, 19 Sep 2025 22:46:32 -0400 Subject: [PATCH 072/155] update copyText and notification --- apps/static/js/components/InfoUrlExternal.vue | 22 ++++++++++-------- .../static/js/components/InfoUrlImageLink.vue | 23 +++++++++++-------- apps/static/js/components/InfoUrlSingle.vue | 21 +++++++++-------- apps/static/js/components/InfoUrlUnit.vue | 22 ++++++++++-------- 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/apps/static/js/components/InfoUrlExternal.vue b/apps/static/js/components/InfoUrlExternal.vue index c43693bb..b7a630a0 100644 --- a/apps/static/js/components/InfoUrlExternal.vue +++ b/apps/static/js/components/InfoUrlExternal.vue @@ -3,7 +3,10 @@
      @@ -24,15 +27,16 @@ export default { return { localUrl: this.url }; }, methods: { - copyToClipboard() { - if (navigator.clipboard) { - navigator.clipboard.writeText(this.localUrl) - .then(() => alert(`You have copied: ${this.localUrl}`)) - .catch(() => alert("Something went wrong with copy.")); - } else { - alert("Clipboard API not supported in this browser."); + async copyText() { + try { + await navigator.clipboard.writeText(this.localUrl); + if (window.UIkit?.notification) { + UIkit.notification({ message: "Copied!", status: "success", timeout: 1200 }); + } + } catch (err) { + console.error("Copy failed:", err); } - } + }, }, mounted() { window.addEventListener("canvasswitch", (event) => { diff --git a/apps/static/js/components/InfoUrlImageLink.vue b/apps/static/js/components/InfoUrlImageLink.vue index 4621e594..0563e7db 100644 --- a/apps/static/js/components/InfoUrlImageLink.vue +++ b/apps/static/js/components/InfoUrlImageLink.vue @@ -5,9 +5,11 @@
      Copy + > + + Copy
      @@ -34,15 +36,16 @@ export default { }; }, methods: { - copyToClipboard() { - if (navigator.clipboard) { - navigator.clipboard.writeText(this.localUrls) - .then(() => alert(`You have copied: ${this.localUrls}`)) - .catch(() => alert("Something went wrong with copy.")); - } else { - alert("Clipboard API not supported in this browser."); + async copyText() { + try { + await navigator.clipboard.writeText(this.localUrls); + if (window.UIkit?.notification) { + UIkit.notification({ message: "Copied!", status: "success", timeout: 1200 }); + } + } catch (err) { + console.error("Copy failed:", err); } - } + }, }, mounted() { window.addEventListener("canvasswitch", (event) => { diff --git a/apps/static/js/components/InfoUrlSingle.vue b/apps/static/js/components/InfoUrlSingle.vue index d26265a6..1bc93177 100644 --- a/apps/static/js/components/InfoUrlSingle.vue +++ b/apps/static/js/components/InfoUrlSingle.vue @@ -3,7 +3,9 @@
      @@ -20,15 +22,16 @@ export default { url: { type: String, required: true } }, methods: { - copyToClipboard() { - if (navigator.clipboard) { - navigator.clipboard.writeText(this.url) - .then(() => alert(`You have copied: ${this.url}`)) - .catch(() => alert("Something went wrong with copy.")); - } else { - alert("Clipboard API not supported in this browser."); + async copyText() { + try { + await navigator.clipboard.writeText(this.url); + if (window.UIkit?.notification) { + UIkit.notification({ message: "Copied!", status: "success", timeout: 1200 }); + } + } catch (err) { + console.error("Copy failed:", err); } - } + }, } } diff --git a/apps/static/js/components/InfoUrlUnit.vue b/apps/static/js/components/InfoUrlUnit.vue index 9da45ca3..8f950bf3 100644 --- a/apps/static/js/components/InfoUrlUnit.vue +++ b/apps/static/js/components/InfoUrlUnit.vue @@ -2,7 +2,10 @@
      {{ url }}
      - Copy + + + Copy +
      @@ -14,15 +17,16 @@ export default { url: { type: String, required: true } }, methods: { - copyToClipboard() { - if (navigator.clipboard) { - navigator.clipboard.writeText(this.url) - .then(() => alert(`You have copied: ${this.url}`)) - .catch(() => alert("Something went wrong with copy.")); - } else { - alert("Clipboard API not supported in this browser."); + async copyText() { + try { + await navigator.clipboard.writeText(this.url); + if (window.UIkit?.notification) { + UIkit.notification({ message: "Copied!", status: "success", timeout: 1200 }); + } + } catch (err) { + console.error("Copy failed:", err); } - } + }, } } From f6d8a2ec02c24e8e3fbebc7baec41aa68cee5cfd Mon Sep 17 00:00:00 2001 From: Y Date: Fri, 19 Sep 2025 22:46:47 -0400 Subject: [PATCH 073/155] fix a filename typo --- apps/static/js/vue-readux.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/static/js/vue-readux.js b/apps/static/js/vue-readux.js index e143987f..6a9ba828 100644 --- a/apps/static/js/vue-readux.js +++ b/apps/static/js/vue-readux.js @@ -7,7 +7,7 @@ import InfoUrlSingle from './components/InfoUrlSingle.vue' import InfoUrlMultiple from './components/InfoUrlMultiple.vue' import InfoUrlImageLink from './components/InfoUrlImageLink.vue' import InfoUrlExternal from './components/InfoUrlExternal.vue' -import VolumeExportAnnotationBtn from './components/VolumeExportAnnotationBtn.vue.vue' +import VolumeExportAnnotationBtn from './components/VolumeExportAnnotationBtn.vue' var readux = new Vue({ el: "#v-readux", From a0582831c452f3355202c669d7edf733063989d5 Mon Sep 17 00:00:00 2001 From: Y Date: Fri, 19 Sep 2025 23:32:19 -0400 Subject: [PATCH 074/155] adopt ocrLoaded --- apps/static/js/components/OcrInspector.vue | 102 ++++++++++++++++----- apps/templates/_page/_exports.html | 1 - 2 files changed, 78 insertions(+), 25 deletions(-) diff --git a/apps/static/js/components/OcrInspector.vue b/apps/static/js/components/OcrInspector.vue index d67d5ff4..762a1dbe 100644 --- a/apps/static/js/components/OcrInspector.vue +++ b/apps/static/js/components/OcrInspector.vue @@ -57,6 +57,8 @@ +}; + \ No newline at end of file diff --git a/apps/static/js/index.js b/apps/static/js/index.js index 3ac4c929..f29d81bb 100644 --- a/apps/static/js/index.js +++ b/apps/static/js/index.js @@ -8,5 +8,4 @@ require('/apps/static/js/vue-readux.js'); window.UIkit = require('uikit'); window.UIkitIcons = require('uikit/dist/js/uikit-icons'); // require('/apps/static/js/project.js'); -window.ECDSAnnotator = require('../../../node_modules/ecds-annotator/dist/ecds-annotator.min.js'); -require('/apps/static/js/page-image-download.js'); \ No newline at end of file +window.ECDSAnnotator = require('../../../node_modules/ecds-annotator/dist/ecds-annotator.min.js'); \ No newline at end of file diff --git a/apps/static/js/page-image-download.js b/apps/static/js/page-image-download.js deleted file mode 100644 index 885842bc..00000000 --- a/apps/static/js/page-image-download.js +++ /dev/null @@ -1,234 +0,0 @@ -console.log('[readux] page-image-download.js loaded'); - -(function () { - var DEBUG = true; // set to false to silence logs - function log(){ if (DEBUG) try { console.log.apply(console, arguments); } catch(_){} } - - // --- helpers ------------------------------------------------------------ - function stripInfoJson(u) { - if (!u) return null; - try { return u.replace(/\/?info\.json$/i, ''); } catch(_) { return u; } - } - function absoluteUrl(u) { - try { return new URL(u, document.baseURI).toString(); } catch(_) { return null; } - } - function buildIiifFullJpg(base) { - var abs = absoluteUrl(stripInfoJson(base)); - if (!abs) return null; - return abs.replace(/\/$/, '') + '/full/full/0/default.jpg'; - } - - function getLink() { return document.getElementById('rx-download-img'); } - function getTemplate(el) { return el && el.getAttribute('data-href-template'); } - - // Try to discover the active IIIF image service from the viewer stack - function svcFromOSD() { - try { - var osd = window.osdViewer || window.viewer || window.osd || window.openSeadragonViewer; - if (!osd || !osd.world || typeof osd.world.getItemAt !== 'function') return null; - var item = osd.world.getItemAt(0); - if (!item) return null; - var ts = item.source || item.tileSource || (item.getTileSource && item.getTileSource()); - var id = ts && (ts['@id'] || ts['id'] || ts['url'] || ts['tileSource']); - return id ? stripInfoJson(id) : null; - } catch(_) { return null; } - } - function svcFromMirador() { - try { - if (window.Mirador && Mirador.viewer && Mirador.viewer.store && Mirador.viewer.store.getState) { - var st = Mirador.viewer.store.getState(); - var winIds = st && st.windows && Object.keys(st.windows); - if (!winIds || !winIds.length) return null; - var win = st.windows[winIds[0]]; - var canvasId = win && win.canvasId; - return canvasId ? stripInfoJson(canvasId) : null; - } - } catch(_) {} - return null; - } - function svcFromNetwork() { - try { - var entries = (performance.getEntriesByType && performance.getEntriesByType('resource')) || []; - for (var i = entries.length - 1; i >= 0; i--) { - var name = entries[i] && entries[i].name; - if (!name || /^chrome-extension:/.test(name)) continue; - if (/\/(full|pct:|\d+,\d+,\d+,\d+)\//.test(name) && /(jpg|jpeg|png|tif|tiff)(\?|$)/i.test(name)) { - try { - var url = new URL(name, document.baseURI); - var parts = url.pathname.split('/').filter(Boolean); - // IIIF image path ends with 4 trailing segments: - // /{region}/{size}/{rotation}/{quality}.{format} - if (parts.length >= 4) { - var baseParts = parts.slice(0, parts.length - 4); // remove 4 trailing segments - var basePath = '/' + baseParts.join('/'); - return url.origin + basePath; - } - } catch(_) {} - } - if (/\/info\.json(\?|$)/.test(name)) { - try { - var url2 = new URL(name, document.baseURI); - return url2.origin + url2.pathname.replace(/\/info\.json(\?|$).*/, ''); - } catch(_) {} - } - } - } catch(_) {} - return null; - } - - function pidFromAnnotator() { - try { - if (window.annotator && annotator.currentPage && annotator.currentPage.pid) return annotator.currentPage.pid; - if (window.readux && readux.state && readux.state.page && readux.state.page.pid) return readux.state.page.pid; - if (window.Rx && Rx.store && Rx.store.state && Rx.store.state.page && Rx.store.state.page.pid) return Rx.store.state.page.pid; - } catch (_) {} - // OpenSeadragon tileSource id often ends with PID; use as a weak fallback - try { - var osd = window.osdViewer || window.viewer || window.osd || window.openSeadragonViewer; - if (osd && osd.world && typeof osd.world.getItemAt === 'function') { - var item = osd.world.getItemAt(0); - var ts = item && (item.source || item.tileSource || (item.getTileSource && item.getTileSource())); - var id = ts && (ts['@id'] || ts['id'] || ts['url']); - if (id) { - var parts = id.split('/').filter(Boolean); - return parts[parts.length - 1]; - } - } - } catch(_) {} - return null; - } - function pidFromDom() { - var el = document.querySelector('[data-page-pid]'); - if (el) return el.getAttribute('data-page-pid'); - var el2 = document.querySelector('.rx-page[data-pid]'); - if (el2) return el2.getAttribute('data-pid'); - return null; - } - - function currentPid() { - return ( - pidFromAnnotator() || - pidFromDom() || - pidFromServiceBase(lastSvc) || - pidFromLinkHref() || - lastPid - ); - } - function pidFromServiceBase(svc) { - if (!svc) return null; - try { - var u = new URL(svc, document.baseURI); - var parts = u.pathname.split('/').filter(Boolean); - return parts.length ? parts[parts.length - 1] : null; - } catch(_) { return null; } - } - - function pidFromLinkHref() { - try { - var el = getLink(); - if (!el || !el.href) return null; - var u = new URL(el.href, document.baseURI); - var parts = u.pathname.split('/').filter(Boolean); - // Remove the 4 trailing IIIF segments if present - if (parts.length >= 4) { - var baseParts = parts.slice(0, parts.length - 4); - if (baseParts.length) return baseParts[baseParts.length - 1] || null; - } - return parts.length ? parts[parts.length - 1] : null; - } catch(_) { return null; } - } - - var lastPid = null; - var lastSvc = null; - - function updateLink(el) { - if (!el) el = getLink(); - if (!el) return; - // Try service-first for accuracy - var svc = svcFromNetwork() || svcFromOSD() || svcFromMirador() || lastSvc; - if (svc) { - var url = buildIiifFullJpg(svc); - if (url && el.href !== url) { - el.href = url; - lastSvc = svc; - lastPid = pidFromServiceBase(svc) || lastPid; - log('[readux] href (svc) ->', url); - return; - } - } - // Fallback: use template + pid - var template = getTemplate(el); - var pid = pidFromAnnotator() || pidFromDom() || lastPid; - if (template && pid) { - var u = template.replace('__PID__', encodeURIComponent(pid)); - if (el.href !== u) { - el.href = u; - lastPid = pid; - log('[readux] href (pid) ->', u); - } - } - } - - function ensureWired() { - var el = getLink(); - if (!el) return; - - // Click-time resolution (always correct at click) - try { - el.addEventListener('click', function (e) { - try { e.preventDefault(); } catch(_) {} - updateLink(el); - try { window.open(el.href, '_blank'); } catch(_) { - try { location.href = el.href; } catch(_) {} - } - }, true); - } catch(_) {} - - // React to common custom events - ['ecds:page-changed', 'readux:page-changed', 'page-changed'].forEach(function (evt) { - window.addEventListener(evt, function () { setTimeout(function(){ updateLink(el); }, 0); }); - }); - - // OSD events - try { - var osd = window.osdViewer || window.viewer || window.osd || window.openSeadragonViewer; - if (osd && typeof osd.addHandler === 'function') { - ['open','page','tile-loaded','add-item','remove-item','item-index-change','animation-finish'] - .forEach(function (evt) { osd.addHandler(evt, function(){ setTimeout(function(){ updateLink(el); }, 0); }); }); - } - } catch(_) {} - - // PerformanceObserver for network tile loads - try { - if (window.PerformanceObserver) { - var po = new PerformanceObserver(function(){ updateLink(el); }); - po.observe({ entryTypes: ['resource'] }); - } - } catch(_) {} - - // DOM & URL changes - try { - var mo = new MutationObserver(function(){ updateLink(el); }); - mo.observe(document.body, { childList:true, subtree:true, attributes:true }); - } catch(_) {} - window.addEventListener('hashchange', function(){ updateLink(el); }); - window.addEventListener('load', function(){ updateLink(el); }); - document.addEventListener('DOMContentLoaded', function(){ updateLink(el); }); - - // Expose manual hook - window.readuxSetCurrentPid = function (pid) { lastPid = pid; updateLink(el); }; - - // initial - updateLink(el); - } - - // Wait until the link exists (in case template renders it late) - if (!getLink()) { - var wait = setInterval(function(){ - if (getLink()) { clearInterval(wait); ensureWired(); } - }, 100); - setTimeout(function(){ try { clearInterval(wait); ensureWired(); } catch(_){} }, 10000); - } else { - ensureWired(); - } -})(); \ No newline at end of file diff --git a/apps/templates/_page/_exports.html b/apps/templates/_page/_exports.html index aef143f0..d566c353 100644 --- a/apps/templates/_page/_exports.html +++ b/apps/templates/_page/_exports.html @@ -1,5 +1,9 @@

      Download and Export

      + + + +