mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-11 02:44:57 +08:00
🍓 Sync 2026-08-11 20:54:16
This commit is contained in:
@@ -381,33 +381,6 @@ function seed() {
|
||||
adoptEntry();
|
||||
}
|
||||
|
||||
/* Does this document already carry the stylesheet a menu.d node names?
|
||||
*
|
||||
* The value is a path under /luci-static/resources (`view/foo/foo.css`), and head.ut prints it with
|
||||
* a `?v=` cache key, so compare the PATH only and by suffix — the theme does not get to assume the
|
||||
* resource base, and a re-hosted sheet keeps its element and its href (fs-sheets.js disables the
|
||||
* <link>, it never removes it), which is exactly the "already here" this asks about. */
|
||||
function documentHasSheet(css) {
|
||||
const want = '/' + css.replace(/^\/+/, '');
|
||||
for (const link of document.querySelectorAll('link[rel~="stylesheet"][href]')) {
|
||||
/* A <link> INSIDE the view tree dies with the swap — it is the one shape fs-sheets.js needs
|
||||
* no handling for, and every scan there skips it for the same reason (VIEW_SHEETS, and the
|
||||
* `closest('#view')` guards in documentPoisoned/scopeToCurrentPage/dedupeViewSheets).
|
||||
* Counting it here would answer the wrong question: `luci-app-nlbwmon` returns
|
||||
* `E('link', { rel: 'stylesheet', href: L.resource('view/nlbw.css') })` from render(), so
|
||||
* standing on that page its sheet IS in the document — and it is exactly the sheet the next
|
||||
* dom.content() throws away. An app in that shape moving to a menu.d `css` is the migration
|
||||
* #8920 was written for, so the guard has to survive it: leaving from that page must be a
|
||||
* full load, not a swap into an unstyled document. */
|
||||
if (link.closest('#view'))
|
||||
continue;
|
||||
const href = (link.getAttribute('href') || '').split('?')[0];
|
||||
if (href.endsWith(want))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Attempt an in-place navigation to `pathname`. Returns true if handled as a
|
||||
* SPA nav (caller should preventDefault), false to let the browser do a normal
|
||||
* full navigation. `push` adds a history entry (false when replaying popstate).
|
||||
@@ -432,25 +405,29 @@ function navigate(pathname, push, kbd) {
|
||||
|
||||
/* A PAGE WHOSE STYLESHEET ONLY THE SERVER CAN EMIT IS NOT OURS TO SWAP INTO.
|
||||
*
|
||||
* A menu.d node may name its own sheet (`"css": "view/foo/foo.css"`), and the server links it
|
||||
* from <head> on a full load (partials/head.ut). Nothing here can: a swap replaces #view's
|
||||
* children, it does not re-render a document, so reaching such a page by CLICK would show it
|
||||
* with the app's CSS missing — while reaching the same page by URL or F5 showed it styled. One
|
||||
* page, two appearances, decided by how the user got there.
|
||||
* A menu.d node may name its own sheet (`"css": "view/foo/foo.css"`), and the server links it from
|
||||
* <head> on a full load (partials/head.ut). Nothing here can: a swap replaces #view's children, it
|
||||
* does not re-render a document, so reaching such a page by CLICK would show it with the app's CSS
|
||||
* missing — while reaching the same page by URL or F5 showed it styled. One page, two appearances,
|
||||
* decided by how the user got there.
|
||||
*
|
||||
* So decline, exactly as the poisoned-document bail above does: speed is traded for
|
||||
* correctness, never the other way. It costs ONE full load per such page — after it the <link>
|
||||
* is in the document, this test passes, and every later visit is a swap again (fs-sheets.js
|
||||
* owns the sheet from then on and re-lights it per page).
|
||||
* So decline, exactly as the poisoned-document bail above does: speed is traded for correctness,
|
||||
* never the other way. It costs ONE full load per such page — after it the <link> is in the
|
||||
* document, this test passes, and every later visit is a swap again (fs-sheets.js owns the sheet
|
||||
* from then on and re-lights it per page).
|
||||
*
|
||||
* Injecting the <link> here instead would work and is deliberately not done: it would put the
|
||||
* theme in charge of fetching and ordering a foreign stylesheet, which is the job fs-sheets.js
|
||||
* exists to keep out of the theme. The server already does it correctly.
|
||||
* Injecting the <link> here instead would work and is deliberately not done: it would put the theme
|
||||
* in charge of fetching and ordering a foreign stylesheet, which is the job fs-sheets.js exists to
|
||||
* keep out of the theme. The server already does it correctly.
|
||||
*
|
||||
* `node.css` reaches the client because /admin/menu serves the dispatcher's own tree and
|
||||
* ui.js's scrubMenu() only rewrites `satisfied`. On a luci-base that predates the `css` schema
|
||||
* entry the property is dropped server-side, so this is simply never true there. */
|
||||
if (typeof node.css === 'string' && node.css !== '' && !documentHasSheet(node.css))
|
||||
* The QUESTION is fs-sheets.js's, not this module's: which sheets a document carries, and which of
|
||||
* them a swap is about to delete, is the one thing that file knows — see documentCarries(). What is
|
||||
* decided here is only what to do about the answer.
|
||||
*
|
||||
* `node.css` reaches the client because /admin/menu serves the dispatcher's own tree and ui.js's
|
||||
* scrubMenu() only rewrites `satisfied`. On a luci-base that predates the `css` schema entry the
|
||||
* property is dropped server-side, so this is simply never true there. */
|
||||
if (typeof node.css === 'string' && node.css !== '' && !sheets.documentCarries(node.css))
|
||||
return false;
|
||||
|
||||
const rsegs = res.segs;
|
||||
|
||||
@@ -307,6 +307,37 @@ function judgeSheet(el, universe) {
|
||||
* <link> INSIDE the view tree (`luci-app-nlbwmon`) needs no handling: it dies with the swap. */
|
||||
const VIEW_SHEETS = 'style:not([data-fs-shell]), link[rel~="stylesheet"]:not([data-fs-shell])';
|
||||
|
||||
/* DOES THIS SHEET OUTLIVE THE PAGE IT ARRIVED WITH? Everything this module decides hangs off that
|
||||
* one question, and the answer is where the element sits: a <style>/<link> inside the view tree
|
||||
* dies with the swap (dom.content() replaces #view's children), so it can neither poison the next
|
||||
* page, nor need scoping to this one, nor be a duplicate worth removing. Only sheets outside it
|
||||
* are this module's business. Named because it is asked in four places and read wrong in none of
|
||||
* them only by luck: `!el.closest('#view')` states where an element is, not what follows from it. */
|
||||
function outlivesPage(el) {
|
||||
return !el.closest('#view');
|
||||
}
|
||||
|
||||
/* Is `path` — a menu.d node's `css`, i.e. a path under /luci-static/resources — already carried by
|
||||
* this document in a form that SURVIVES a swap?
|
||||
*
|
||||
* The router asks before committing a client navigation: only a server render emits that <link>, so
|
||||
* a page whose stylesheet is missing must arrive by full load (see fs-router.js). The question is
|
||||
* this module's because the answer is: a link inside #view is about to be deleted with the rest of
|
||||
* the view, and counting it would hand the router a sheet the next dom.content() throws away —
|
||||
* `luci-app-nlbwmon` returns E('link', …, L.resource('view/nlbw.css')) from render(), so that shape
|
||||
* is real. Compared by PATH and by suffix: head.ut prints `{{ resource }}/{{ dispatched.css }}?v=…`,
|
||||
* so neither the resource base nor the cache key is anyone's to assume. */
|
||||
function documentCarries(path) {
|
||||
const want = '/' + String(path).replace(/^\/+/, '');
|
||||
for (const link of document.querySelectorAll('link[rel~="stylesheet"][href]')) {
|
||||
if (!outlivesPage(link))
|
||||
continue;
|
||||
if ((link.getAttribute('href') || '').split('?')[0].endsWith(want))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* An invasive sheet we OWN is contained — scopeToCurrentPage() darkens it the moment the router
|
||||
* stamps the new page, so it cannot reach the next page and the document is not spent. One we could
|
||||
* not attribute (not re-hostable, so never owned: an @import at the top, a sheet built with
|
||||
@@ -331,7 +362,7 @@ function documentPoisoned() {
|
||||
const names = themeNames();
|
||||
return Array.prototype.some.call(
|
||||
document.querySelectorAll(VIEW_SHEETS),
|
||||
(el) => !el.closest('#view')
|
||||
(el) => outlivesPage(el)
|
||||
&& (!names || (invasiveSheet(el, names) && !_owner.has(el) && !_silenced.has(el))));
|
||||
}
|
||||
|
||||
@@ -623,7 +654,7 @@ function scopeToCurrentPage(segs) {
|
||||
if (segs) _curKey = appKey(segs);
|
||||
const key = currentKey();
|
||||
document.querySelectorAll(VIEW_SHEETS).forEach((el) => {
|
||||
if (el.closest('#view') || !_owner.has(el)) return;
|
||||
if (!outlivesPage(el) || !_owner.has(el)) return;
|
||||
setEnabled(el, _owner.get(el) === key);
|
||||
});
|
||||
}
|
||||
@@ -731,7 +762,7 @@ function sheetKey(el) {
|
||||
function dedupeViewSheets() {
|
||||
const seen = new Set();
|
||||
document.querySelectorAll(VIEW_SHEETS).forEach((el) => {
|
||||
if (el.closest('#view')) return;
|
||||
if (!outlivesPage(el)) return;
|
||||
const key = sheetKey(el);
|
||||
if (key === null) return;
|
||||
if (seen.has(key)) el.remove();
|
||||
@@ -841,6 +872,7 @@ function watchViewSheets() {
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
documentCarries,
|
||||
documentPoisoned,
|
||||
scopeToCurrentPage,
|
||||
watchViewSheets
|
||||
|
||||
Reference in New Issue
Block a user