hyperbook 0.84.4 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/bootstrap.js +248 -0
- package/dist/assets/cloud.js +28 -17
- package/dist/assets/directive-abc-music/client.js +11 -3
- package/dist/assets/directive-archive/client.js +17 -4
- package/dist/assets/directive-audio/client.js +67 -28
- package/dist/assets/directive-bookmarks/client.js +9 -1
- package/dist/assets/directive-download/client.js +10 -2
- package/dist/assets/directive-embed/client.js +112 -0
- package/dist/assets/directive-embed/style.css +70 -1
- package/dist/assets/directive-excalidraw/client.js +9 -0
- package/dist/assets/directive-excalidraw/hyperbook-excalidraw.umd.js +1 -1
- package/dist/assets/directive-geogebra/client.js +16 -3
- package/dist/assets/directive-h5p/client.js +32 -3
- package/dist/assets/directive-learningmap/client.js +11 -3
- package/dist/assets/directive-mermaid/client.js +11 -1
- package/dist/assets/directive-multievent/multievent.js +2 -2
- package/dist/assets/directive-onlineide/client.js +7 -0
- package/dist/assets/directive-onlineide/include/online-ide-embedded.js +43 -43
- package/dist/assets/directive-p5/client.js +39 -7
- package/dist/assets/directive-protect/client.js +11 -3
- package/dist/assets/directive-pyide/client.js +20 -9
- package/dist/assets/directive-scratchblock/client.js +9 -0
- package/dist/assets/directive-slideshow/client.js +12 -4
- package/dist/assets/directive-sqlide/client.js +7 -0
- package/dist/assets/directive-sqlide/include/sql-ide-embedded.js +3 -3
- package/dist/assets/directive-tabs/client.js +14 -3
- package/dist/assets/directive-textinput/client.js +14 -3
- package/dist/assets/directive-webide/client.js +45 -10
- package/dist/assets/directive-youtube/client.js +99 -0
- package/dist/assets/directive-youtube/style.css +63 -0
- package/dist/assets/hyperbook.types.js +209 -0
- package/dist/assets/i18n.js +15 -1
- package/dist/assets/store.js +174 -139
- package/dist/assets/ui.js +279 -0
- package/dist/index.js +632 -413
- package/dist/locales/de.json +9 -1
- package/dist/locales/en.json +9 -1
- package/package.json +4 -4
- package/dist/assets/client.js +0 -506
package/dist/locales/de.json
CHANGED
|
@@ -95,5 +95,13 @@
|
|
|
95
95
|
"user-logout-confirm": "Möchten Sie sich wirklich abmelden?",
|
|
96
96
|
"user-readonly": "Nur-Lesen-Modus",
|
|
97
97
|
"user-impersonating": "Angemeldet als",
|
|
98
|
-
"user-exit-impersonation": "Identitätswechsel beenden"
|
|
98
|
+
"user-exit-impersonation": "Identitätswechsel beenden",
|
|
99
|
+
"consent-youtube-text": "Bei einem Klick auf den Play-Button wird ein YouTube-Video über YouTube-NoCookie geladen. Dennoch findet der Verbindungsaufbau statt und die dafür notwendigen Daten werden übertragen.",
|
|
100
|
+
"consent-youtube-nocookie": "Wir verwenden YouTube-NoCookie, um Ihre Privatsphäre zu schützen.",
|
|
101
|
+
"consent-youtube-always": "YouTube-Videos immer zulassen",
|
|
102
|
+
"consent-youtube-accept": "Akzeptieren und abspielen",
|
|
103
|
+
"consent-embed-text": "Hier sollen Inhalte einer externen Internetseite geladen werden.",
|
|
104
|
+
"consent-embed-personal-data": "Die externe Internetseite kann möglicherweise personenbezogene Daten erheben.",
|
|
105
|
+
"consent-embed-always": "Inhalte von {{domain}} immer zulassen",
|
|
106
|
+
"consent-embed-accept": "Inhalte laden"
|
|
99
107
|
}
|
package/dist/locales/en.json
CHANGED
|
@@ -95,5 +95,13 @@
|
|
|
95
95
|
"user-logout-confirm": "Are you sure you want to logout?",
|
|
96
96
|
"user-readonly": "Read-Only Mode",
|
|
97
97
|
"user-impersonating": "Impersonating",
|
|
98
|
-
"user-exit-impersonation": "Exit Impersonation"
|
|
98
|
+
"user-exit-impersonation": "Exit Impersonation",
|
|
99
|
+
"consent-youtube-text": "Clicking the play button will load a YouTube video via YouTube-NoCookie. However, a connection will still be established and the necessary data will be transmitted.",
|
|
100
|
+
"consent-youtube-nocookie": "We use YouTube-NoCookie to protect your privacy.",
|
|
101
|
+
"consent-youtube-always": "Always allow YouTube videos",
|
|
102
|
+
"consent-youtube-accept": "Accept and play",
|
|
103
|
+
"consent-embed-text": "Content from an external website is to be loaded here.",
|
|
104
|
+
"consent-embed-personal-data": "The external website may collect personal data.",
|
|
105
|
+
"consent-embed-always": "Always allow content from {{domain}}",
|
|
106
|
+
"consent-embed-accept": "Load content"
|
|
99
107
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyperbook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.85.0",
|
|
4
4
|
"author": "Mike Barkmin",
|
|
5
5
|
"homepage": "https://github.com/openpatch/hyperbook#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"tar": "7.4.3",
|
|
57
57
|
"update-check": "1.5.4",
|
|
58
58
|
"ws": "^8.18.0",
|
|
59
|
+
"@hyperbook/types": "0.22.1",
|
|
60
|
+
"@hyperbook/markdown": "0.57.0",
|
|
59
61
|
"@hyperbook/fs": "0.24.2",
|
|
60
|
-
"create-hyperbook": "0.3.5"
|
|
61
|
-
"@hyperbook/markdown": "0.55.4",
|
|
62
|
-
"@hyperbook/types": "0.22.1"
|
|
62
|
+
"create-hyperbook": "0.3.5"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
65
|
"version": "pnpm build",
|
package/dist/assets/client.js
DELETED
|
@@ -1,506 +0,0 @@
|
|
|
1
|
-
var hyperbook = Object.assign(window.hyperbook || {}, (function () {
|
|
2
|
-
/**
|
|
3
|
-
* Initialize elements within the given root element.
|
|
4
|
-
* @param {HTMLElement} root - The root element to initialize.
|
|
5
|
-
*/
|
|
6
|
-
const initCollapsibles = (root) => {
|
|
7
|
-
// Handle both navigation sections and directive-collapsible elements
|
|
8
|
-
const detailsEls = root.querySelectorAll("details.section, details.directive-collapsible");
|
|
9
|
-
for (let details of detailsEls) {
|
|
10
|
-
const id = details.getAttribute("data-id");
|
|
11
|
-
|
|
12
|
-
// Prevent link clicks from toggling the details element in navigation
|
|
13
|
-
if (id && id.startsWith("_nav:") && !details.classList.contains("empty")) {
|
|
14
|
-
const link = details.querySelector("summary a");
|
|
15
|
-
link?.addEventListener("click", (event) => {
|
|
16
|
-
event.stopPropagation();
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Listen for toggle events to persist state and sync with other elements
|
|
21
|
-
details.addEventListener("toggle", () => {
|
|
22
|
-
if (id) {
|
|
23
|
-
if (details.open) {
|
|
24
|
-
store.collapsibles.put({ id });
|
|
25
|
-
} else {
|
|
26
|
-
store.collapsibles.delete(id);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Sync all elements with the same ID
|
|
30
|
-
const allWithSameId = document.querySelectorAll(`[data-id="${id}"]`);
|
|
31
|
-
for (let el of allWithSameId) {
|
|
32
|
-
if (el !== details && el.tagName === "DETAILS") {
|
|
33
|
-
el.open = details.open;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
setTimeout(() => {
|
|
39
|
-
window.dispatchEvent(new Event("resize")); // geogebra needs this to resize the applet
|
|
40
|
-
}, 100);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
updateCollapsibles(root);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @param {HTMLElement} root
|
|
48
|
-
*/
|
|
49
|
-
const updateCollapsibles = (root) => {
|
|
50
|
-
store.collapsibles.toArray().then((collapsibles) => {
|
|
51
|
-
const detailsEls = root.querySelectorAll("details.section, details.directive-collapsible");
|
|
52
|
-
for (let details of detailsEls) {
|
|
53
|
-
const id = details.getAttribute("data-id");
|
|
54
|
-
if (id) {
|
|
55
|
-
const shouldBeOpen = collapsibles.some((c) => c.id === id);
|
|
56
|
-
// Only update if state differs and it's not a navigation section
|
|
57
|
-
// (navigation sections are auto-expanded based on current page)
|
|
58
|
-
if (!id.startsWith("_nav:") && details.open !== shouldBeOpen) {
|
|
59
|
-
details.open = shouldBeOpen;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const initSearch = (root) => {
|
|
67
|
-
const searchInputEl = root.querySelector("#search-input");
|
|
68
|
-
if (searchInputEl) {
|
|
69
|
-
searchInputEl.addEventListener("keypress", (event) => {
|
|
70
|
-
if (event.key === "Enter") {
|
|
71
|
-
event.preventDefault();
|
|
72
|
-
search();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Toggle the table of contents drawer.
|
|
80
|
-
*/
|
|
81
|
-
function tocToggle() {
|
|
82
|
-
const tocDrawerEl = document.getElementById("toc-drawer");
|
|
83
|
-
tocDrawerEl.open = !tocDrawerEl.open;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Toggle the search drawer.
|
|
88
|
-
*/
|
|
89
|
-
function searchToggle() {
|
|
90
|
-
const searchDrawerEl = document.getElementById("search-drawer");
|
|
91
|
-
searchDrawerEl.open = !searchDrawerEl.open;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Perform a search and display the results.
|
|
96
|
-
*/
|
|
97
|
-
function search() {
|
|
98
|
-
const resultsEl = document.getElementById("search-results");
|
|
99
|
-
resultsEl.innerHTML = "";
|
|
100
|
-
const searchInputEl = document.querySelector("#search-input");
|
|
101
|
-
const query = searchInputEl.value;
|
|
102
|
-
const idx = window.lunr.Index.load(LUNR_INDEX);
|
|
103
|
-
const documents = SEARCH_DOCUMENTS;
|
|
104
|
-
const results = idx.search(query);
|
|
105
|
-
for (let result of results) {
|
|
106
|
-
const doc = documents[result.ref];
|
|
107
|
-
|
|
108
|
-
const container = document.createElement("a");
|
|
109
|
-
container.href = doc.href;
|
|
110
|
-
container.classList.add("search-result");
|
|
111
|
-
const heading = document.createElement("div");
|
|
112
|
-
heading.textContent = doc.heading;
|
|
113
|
-
heading.classList.add("search-result-heading");
|
|
114
|
-
const content = document.createElement("div");
|
|
115
|
-
content.classList.add("search-result-content");
|
|
116
|
-
const href = document.createElement("div");
|
|
117
|
-
href.classList.add("search-result-href");
|
|
118
|
-
href.textContent = doc.href;
|
|
119
|
-
|
|
120
|
-
let contentHTML = "";
|
|
121
|
-
const terms = Object.keys(result.matchData.metadata);
|
|
122
|
-
const term = terms[0];
|
|
123
|
-
if (result?.matchData?.metadata?.[term]?.content?.position?.length > 0) {
|
|
124
|
-
const pos = result.matchData.metadata[term].content.position[0];
|
|
125
|
-
const start = pos[0];
|
|
126
|
-
const len = pos[1];
|
|
127
|
-
let cutoffBefore = start - 50;
|
|
128
|
-
if (cutoffBefore < 0) {
|
|
129
|
-
cutoffBefore = 0;
|
|
130
|
-
} else {
|
|
131
|
-
contentHTML += "...";
|
|
132
|
-
}
|
|
133
|
-
contentHTML += doc.content.slice(cutoffBefore, start);
|
|
134
|
-
|
|
135
|
-
contentHTML += `<mark>${doc.content.slice(start, start + len)}</mark>`;
|
|
136
|
-
let cutoffAfter = start + len + 50;
|
|
137
|
-
|
|
138
|
-
contentHTML += doc.content.slice(start + len, cutoffAfter);
|
|
139
|
-
if (cutoffAfter < doc.content.length) {
|
|
140
|
-
contentHTML += "...";
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
content.innerHTML = contentHTML;
|
|
145
|
-
|
|
146
|
-
container.appendChild(heading);
|
|
147
|
-
container.appendChild(content);
|
|
148
|
-
container.appendChild(href);
|
|
149
|
-
resultsEl.appendChild(container);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Open the QR code dialog.
|
|
155
|
-
*/
|
|
156
|
-
function qrcodeOpen() {
|
|
157
|
-
const qrCodeDialog = document.getElementById("qrcode-dialog");
|
|
158
|
-
const qrcodeEls = qrCodeDialog.getElementsByClassName("make-qrcode");
|
|
159
|
-
const urlEls = qrCodeDialog.getElementsByClassName("url");
|
|
160
|
-
const qrcodeEl = qrcodeEls[0];
|
|
161
|
-
const qrcode = new window.QRCode({
|
|
162
|
-
content: window.location.href,
|
|
163
|
-
padding: 0,
|
|
164
|
-
join: true,
|
|
165
|
-
color: "var(--color-text)",
|
|
166
|
-
container: "svg-viewbox",
|
|
167
|
-
background: "var(--color-background)",
|
|
168
|
-
ecl: "M",
|
|
169
|
-
});
|
|
170
|
-
qrcodeEl.innerHTML = qrcode.svg();
|
|
171
|
-
for (let urlEl of urlEls[0].children) {
|
|
172
|
-
const href = urlEl.getAttribute("data-href");
|
|
173
|
-
urlEl.innerHTML = `${window.location.origin}${href}`;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
qrCodeDialog.showModal();
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Close the QR code dialog.
|
|
181
|
-
*/
|
|
182
|
-
function qrcodeClose() {
|
|
183
|
-
const qrCodeDialog = document.getElementById("qrcode-dialog");
|
|
184
|
-
qrCodeDialog.close();
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Open the share dialog.
|
|
189
|
-
*/
|
|
190
|
-
function shareOpen() {
|
|
191
|
-
const shareDialog = document.getElementById("share-dialog");
|
|
192
|
-
shareUpdatePreview();
|
|
193
|
-
shareDialog.showModal();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Close the share dialog.
|
|
198
|
-
*/
|
|
199
|
-
function shareClose() {
|
|
200
|
-
const shareDialog = document.getElementById("share-dialog");
|
|
201
|
-
shareDialog.close();
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Update the URL preview in the share dialog.
|
|
206
|
-
*/
|
|
207
|
-
function shareUpdatePreview() {
|
|
208
|
-
const standaloneCheckbox = document.getElementById("share-standalone-checkbox");
|
|
209
|
-
const sectionCheckboxes = document.querySelectorAll("#share-dialog input[data-anchor]");
|
|
210
|
-
const previewEl = document.getElementById("share-url-preview");
|
|
211
|
-
|
|
212
|
-
const baseUrl = window.location.origin + window.location.pathname;
|
|
213
|
-
const params = new URLSearchParams();
|
|
214
|
-
|
|
215
|
-
if (standaloneCheckbox && standaloneCheckbox.checked) {
|
|
216
|
-
params.append("standalone", "true");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const selectedSections = Array.from(sectionCheckboxes)
|
|
220
|
-
.filter(cb => cb.checked)
|
|
221
|
-
.map(cb => cb.getAttribute("data-anchor"));
|
|
222
|
-
|
|
223
|
-
if (selectedSections.length > 0) {
|
|
224
|
-
params.append("sections", selectedSections.join(","));
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const finalUrl = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
|
|
228
|
-
previewEl.textContent = finalUrl;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Copy the shareable URL to clipboard.
|
|
233
|
-
*/
|
|
234
|
-
function shareCopyUrl() {
|
|
235
|
-
const previewEl = document.getElementById("share-url-preview");
|
|
236
|
-
const url = previewEl.textContent;
|
|
237
|
-
|
|
238
|
-
navigator.clipboard.writeText(url).then(() => {
|
|
239
|
-
const button = document.querySelector("#share-dialog .copy-button");
|
|
240
|
-
const originalText = button.textContent;
|
|
241
|
-
button.textContent = window.i18n.get("share-dialog-url-copied");
|
|
242
|
-
|
|
243
|
-
setTimeout(() => {
|
|
244
|
-
button.textContent = originalText;
|
|
245
|
-
}, 2000);
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Toggle the navigation drawer.
|
|
251
|
-
*/
|
|
252
|
-
function navToggle() {
|
|
253
|
-
const navDrawerEl = document.getElementById("nav-drawer");
|
|
254
|
-
navDrawerEl.open = !navDrawerEl.open;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Toggle a bookmark.
|
|
259
|
-
* @param {string} key - The key of the bookmark.
|
|
260
|
-
* @param {string} label - The label of the bookmark.
|
|
261
|
-
*/
|
|
262
|
-
function toggleBookmark(key, label) {
|
|
263
|
-
const el = document.querySelectorAll(`.bookmark[data-key="${key}"]`);
|
|
264
|
-
store.bookmarks.get(key).then((bookmark) => {
|
|
265
|
-
if (!bookmark) {
|
|
266
|
-
store.bookmarks.add({ path: key, label }).then(() => {
|
|
267
|
-
el.forEach((e) => e.classList.add("active"));
|
|
268
|
-
hyperbook.bookmarks.update();
|
|
269
|
-
});
|
|
270
|
-
} else {
|
|
271
|
-
store.bookmarks.delete(key).then(() => {
|
|
272
|
-
el.forEach((e) => e.classList.remove("active"));
|
|
273
|
-
hyperbook.bookmarks.update();
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Initialize bookmarks within the given root element.
|
|
281
|
-
* @param {HTMLElement} [root=document] - The root element to initialize.
|
|
282
|
-
*/
|
|
283
|
-
function initBookmarks(root = document) {
|
|
284
|
-
const bookmarkEls = root.getElementsByClassName("bookmark");
|
|
285
|
-
for (let bookmarkEl of bookmarkEls) {
|
|
286
|
-
const key = bookmarkEl.getAttribute("data-key");
|
|
287
|
-
store.bookmarks.get(key).then((bookmark) => {
|
|
288
|
-
if (bookmark) {
|
|
289
|
-
bookmarkEl.classList.add("active");
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Toggle the lightbox view of an element.
|
|
297
|
-
* @param {HTMLElement} el - The element to toggle.
|
|
298
|
-
*/
|
|
299
|
-
function toggleLightbox(el) {
|
|
300
|
-
const overlay = document.createElement("div");
|
|
301
|
-
overlay.classList.add("lightbox-overlay");
|
|
302
|
-
|
|
303
|
-
const captionText =
|
|
304
|
-
el.parentElement.querySelector("figcaption")?.textContent || "";
|
|
305
|
-
|
|
306
|
-
// container for image + caption
|
|
307
|
-
const content = document.createElement("div");
|
|
308
|
-
content.classList.add("lightbox-content");
|
|
309
|
-
|
|
310
|
-
// image container fills remaining space
|
|
311
|
-
const imgContainer = document.createElement("div");
|
|
312
|
-
imgContainer.classList.add("lightbox-image-container");
|
|
313
|
-
|
|
314
|
-
const lightboxImg = document.createElement("img");
|
|
315
|
-
lightboxImg.src = el.src;
|
|
316
|
-
imgContainer.appendChild(lightboxImg);
|
|
317
|
-
|
|
318
|
-
content.appendChild(imgContainer);
|
|
319
|
-
|
|
320
|
-
// add caption if exists
|
|
321
|
-
if (captionText) {
|
|
322
|
-
const caption = document.createElement("div");
|
|
323
|
-
caption.classList.add("caption");
|
|
324
|
-
caption.textContent = captionText;
|
|
325
|
-
content.appendChild(caption);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
overlay.appendChild(content);
|
|
329
|
-
|
|
330
|
-
overlay.addEventListener("click", () => {
|
|
331
|
-
document.body.removeChild(overlay);
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
document.body.appendChild(overlay);
|
|
335
|
-
overlay.style.display = "flex";
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function init(root) {
|
|
339
|
-
initCollapsibles(root);
|
|
340
|
-
initSearch(root);
|
|
341
|
-
initBookmarks(root);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Hide TOC toggle button when sections are filtered
|
|
346
|
-
*/
|
|
347
|
-
function hideTocWhenFiltered() {
|
|
348
|
-
const tocToggle = document.getElementById('toc-toggle');
|
|
349
|
-
|
|
350
|
-
if (tocToggle) {
|
|
351
|
-
tocToggle.style.display = 'none';
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Filter sections based on query parameter
|
|
356
|
-
function filterSections() {
|
|
357
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
358
|
-
const sectionsParam = urlParams.get('sections');
|
|
359
|
-
|
|
360
|
-
if (!sectionsParam) {
|
|
361
|
-
return; // No filtering needed
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Parse sections parameter (comma-separated list of IDs)
|
|
365
|
-
const sectionIds = sectionsParam.split(',').map(id => id.trim()).filter(id => id);
|
|
366
|
-
|
|
367
|
-
if (sectionIds.length === 0) {
|
|
368
|
-
return; // No valid IDs provided
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// Get all headings in the document
|
|
372
|
-
const allHeadings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
|
373
|
-
const headingsArray = Array.from(allHeadings);
|
|
374
|
-
|
|
375
|
-
// Build a map of which elements should be visible
|
|
376
|
-
const visibleElements = new Set();
|
|
377
|
-
|
|
378
|
-
sectionIds.forEach(sectionId => {
|
|
379
|
-
// Find the heading with this ID
|
|
380
|
-
const heading = document.getElementById(sectionId);
|
|
381
|
-
if (!heading) {
|
|
382
|
-
return; // ID not found
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Get the heading level (h1 = 1, h2 = 2, etc.)
|
|
386
|
-
const headingLevel = parseInt(heading.tagName.substring(1));
|
|
387
|
-
|
|
388
|
-
// Mark this heading as visible
|
|
389
|
-
visibleElements.add(heading);
|
|
390
|
-
|
|
391
|
-
// Find the index of this heading
|
|
392
|
-
const headingIndex = headingsArray.indexOf(heading);
|
|
393
|
-
if (headingIndex === -1) {
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Collect all elements until the next heading of the same or higher level
|
|
398
|
-
let currentElement = heading.nextElementSibling;
|
|
399
|
-
|
|
400
|
-
while (currentElement) {
|
|
401
|
-
// Check if this is a heading
|
|
402
|
-
const isHeading = /^H[1-6]$/.test(currentElement.tagName);
|
|
403
|
-
|
|
404
|
-
if (isHeading) {
|
|
405
|
-
const currentLevel = parseInt(currentElement.tagName.substring(1));
|
|
406
|
-
|
|
407
|
-
// Stop if we hit a heading of the same or higher level (lower number)
|
|
408
|
-
if (currentLevel <= headingLevel) {
|
|
409
|
-
break;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Include lower-level headings (subsections)
|
|
413
|
-
visibleElements.add(currentElement);
|
|
414
|
-
} else {
|
|
415
|
-
// Include non-heading elements
|
|
416
|
-
visibleElements.add(currentElement);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
currentElement = currentElement.nextElementSibling;
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// Hide all elements that are not in visibleElements
|
|
424
|
-
const markdownDiv = document.querySelector('main article .hyperbook-markdown');
|
|
425
|
-
if (markdownDiv) {
|
|
426
|
-
Array.from(markdownDiv.children).forEach(element => {
|
|
427
|
-
// Don't hide UI elements (floating buttons container, side drawers)
|
|
428
|
-
const isUIElement = element.id === 'floating-buttons-container' ||
|
|
429
|
-
element.tagName === 'SIDE-DRAWER';
|
|
430
|
-
|
|
431
|
-
if (!visibleElements.has(element) && !isUIElement) {
|
|
432
|
-
element.style.display = 'none';
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
// Hide TOC toggle when sections are filtered
|
|
437
|
-
hideTocWhenFiltered();
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Check for standalone layout URL parameter or iframe context
|
|
442
|
-
function checkStandaloneMode() {
|
|
443
|
-
// Check if explicitly requested via URL parameter
|
|
444
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
445
|
-
const standaloneParam = urlParams.get('standalone') === 'true';
|
|
446
|
-
|
|
447
|
-
// Check if page is inside an iframe (but not VSCode webview)
|
|
448
|
-
const isVSCodeWebview = typeof acquireVsCodeApi !== 'undefined';
|
|
449
|
-
const isInIframe = window.self !== window.top && !isVSCodeWebview;
|
|
450
|
-
|
|
451
|
-
if (standaloneParam || isInIframe) {
|
|
452
|
-
const mainGrid = document.querySelector('.main-grid');
|
|
453
|
-
if (mainGrid && !mainGrid.classList.contains('layout-standalone')) {
|
|
454
|
-
mainGrid.classList.add('layout-standalone');
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// Hide TOC and QR code buttons when in standalone mode
|
|
458
|
-
const tocToggle = document.getElementById('toc-toggle');
|
|
459
|
-
if (tocToggle) {
|
|
460
|
-
tocToggle.style.display = 'none';
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
const qrcodeOpen = document.getElementById('qrcode-open');
|
|
464
|
-
if (qrcodeOpen) {
|
|
465
|
-
qrcodeOpen.style.display = 'none';
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Initialize existing elements on document load
|
|
471
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
472
|
-
init(document);
|
|
473
|
-
checkStandaloneMode();
|
|
474
|
-
filterSections();
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
// Observe for new elements added to the DOM
|
|
478
|
-
const observer = new MutationObserver((mutations) => {
|
|
479
|
-
mutations.forEach((mutation) => {
|
|
480
|
-
mutation.addedNodes.forEach((node) => {
|
|
481
|
-
if (node.nodeType === 1) {
|
|
482
|
-
// Element node
|
|
483
|
-
init(node);
|
|
484
|
-
}
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
490
|
-
|
|
491
|
-
return {
|
|
492
|
-
toggleLightbox,
|
|
493
|
-
toggleBookmark,
|
|
494
|
-
navToggle,
|
|
495
|
-
tocToggle,
|
|
496
|
-
searchToggle,
|
|
497
|
-
search,
|
|
498
|
-
qrcodeOpen,
|
|
499
|
-
qrcodeClose,
|
|
500
|
-
shareOpen,
|
|
501
|
-
shareClose,
|
|
502
|
-
shareUpdatePreview,
|
|
503
|
-
shareCopyUrl,
|
|
504
|
-
init,
|
|
505
|
-
};
|
|
506
|
-
})());
|