@udx/md2html 1.0.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/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@udx/md2html",
3
+ "version": "1.0.0",
4
+ "description": "Magazine-quality Markdown to HTML converter with professional styling",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "md2html": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js",
12
+ "test": "echo \"Error: no test specified\" && exit 1"
13
+ },
14
+ "keywords": [
15
+ "markdown",
16
+ "html",
17
+ "documentation",
18
+ "converter",
19
+ "generator",
20
+ "stylish",
21
+ "publishing"
22
+ ],
23
+ "author": "UDX <info@udx.io>",
24
+ "license": "MIT",
25
+ "homepage": "https://github.com/andypotanin/udx.dev#readme",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/andypotanin/udx.dev.git",
29
+ "directory": "tools/md2html"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/andypotanin/udx.dev/issues"
33
+ },
34
+ "dependencies": {
35
+ "chokidar": "^3.5.3",
36
+ "commander": "^11.0.0",
37
+ "handlebars": "^4.7.8",
38
+ "marked": "^9.1.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=14.16"
42
+ },
43
+ "files": [
44
+ "index.js",
45
+ "static/",
46
+ "README.md"
47
+ ]
48
+ }
@@ -0,0 +1,213 @@
1
+ /* Chapter Navigation Styles */
2
+ :root {
3
+ --chapter-nav-width: 180px;
4
+ --chapter-nav-text-color: #333;
5
+ --chapter-nav-text-faded: rgba(51, 51, 51, 0.4);
6
+ --chapter-text-normal-size: 0.9rem;
7
+ --chapter-text-active-size: calc(var(--chapter-text-normal-size) * 1.5);
8
+ --chapter-text-inactive-size: calc(var(--chapter-text-normal-size) * 0.8);
9
+ }
10
+
11
+ /* Container for the floating navigation */
12
+ .chapter-nav-container {
13
+ position: relative;
14
+ width: 100%;
15
+ }
16
+
17
+ /* Floating navigation styles - clean and minimal */
18
+ .chapter-navigation {
19
+ position: fixed;
20
+ top: 50%;
21
+ transform: translateY(-50%);
22
+ left: 20px;
23
+ width: var(--chapter-nav-width);
24
+ max-height: 80vh;
25
+ overflow-y: auto;
26
+ background-color: transparent;
27
+ padding: 10px 0;
28
+ z-index: 100;
29
+ scrollbar-width: none; /* Hide scrollbar for Firefox */
30
+ transition: transform 0.3s ease;
31
+ border: 0 none transparent !important;
32
+ box-shadow: none !important;
33
+ outline: none !important;
34
+ -webkit-box-shadow: none !important;
35
+ -moz-box-shadow: none !important;
36
+ }
37
+
38
+ /* Hide scrollbar for Chrome, Safari and Opera */
39
+ .chapter-navigation::-webkit-scrollbar {
40
+ display: none;
41
+ }
42
+
43
+ .chapter-nav-list {
44
+ list-style: none;
45
+ padding: 0;
46
+ margin: 0;
47
+ }
48
+
49
+ .chapter-nav-item {
50
+ margin-bottom: 10px;
51
+ transition: all 0.3s ease;
52
+ }
53
+
54
+ .chapter-nav-link {
55
+ display: block;
56
+ padding: 4px 0;
57
+ font-size: var(--chapter-text-inactive-size);
58
+ color: var(--chapter-nav-text-faded);
59
+ text-decoration: none;
60
+ transition: all 0.3s ease;
61
+ background-color: transparent;
62
+ border-radius: 0;
63
+ line-height: 1.3;
64
+ }
65
+
66
+ .chapter-nav-link:hover {
67
+ color: var(--chapter-nav-text-color);
68
+ }
69
+
70
+ .chapter-nav-item.active .chapter-nav-link {
71
+ font-size: var(--chapter-text-active-size);
72
+ color: var(--chapter-nav-text-color);
73
+ font-weight: 500;
74
+ }
75
+
76
+ /* Mobile dropdown styles - native-looking and minimal */
77
+ .mobile-chapter-nav {
78
+ display: none;
79
+ position: sticky;
80
+ top: 0;
81
+ z-index: 100;
82
+ width: 100%;
83
+ background-color: #fff;
84
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
85
+ padding: 8px 12px;
86
+ }
87
+
88
+ .mobile-chapter-dropdown {
89
+ position: relative;
90
+ }
91
+
92
+ .chapter-dropdown-btn {
93
+ display: flex;
94
+ justify-content: space-between;
95
+ align-items: center;
96
+ width: 100%;
97
+ padding: 10px 12px;
98
+ background-color: #fff;
99
+ border: 1px solid rgba(0, 0, 0, 0.1);
100
+ border-radius: 4px;
101
+ font-size: 1rem;
102
+ cursor: pointer;
103
+ transition: all 0.2s ease;
104
+ -webkit-tap-highlight-color: transparent;
105
+ }
106
+
107
+ .chapter-dropdown-btn:hover,
108
+ .chapter-dropdown-btn:focus {
109
+ border-color: rgba(0, 0, 0, 0.2);
110
+ outline: none;
111
+ }
112
+
113
+ .current-chapter {
114
+ font-weight: 500;
115
+ }
116
+
117
+ .dropdown-icon {
118
+ transition: transform 0.3s ease;
119
+ opacity: 0.6;
120
+ }
121
+
122
+ .chapter-dropdown-btn.active .dropdown-icon {
123
+ transform: rotate(180deg);
124
+ opacity: 0.9;
125
+ }
126
+
127
+ .chapter-dropdown-menu {
128
+ position: absolute;
129
+ top: 100%;
130
+ left: 0;
131
+ right: 0;
132
+ background-color: #fff;
133
+ border-radius: 4px;
134
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
135
+ margin-top: 4px;
136
+ z-index: 101;
137
+ max-height: 300px;
138
+ overflow-y: auto;
139
+ border: 1px solid rgba(0, 0, 0, 0.1);
140
+ display: none; /* Initially hidden */
141
+ }
142
+
143
+ .chapter-dropdown-menu ul {
144
+ list-style: none;
145
+ padding: 8px 0;
146
+ margin: 0;
147
+ }
148
+
149
+ .chapter-dropdown-menu li {
150
+ padding: 0;
151
+ }
152
+
153
+ .chapter-dropdown-menu a {
154
+ display: block;
155
+ padding: 10px 12px;
156
+ color: var(--chapter-nav-text-faded);
157
+ text-decoration: none;
158
+ font-size: 0.95rem;
159
+ transition: all 0.2s ease;
160
+ -webkit-tap-highlight-color: transparent;
161
+ }
162
+
163
+ .chapter-dropdown-menu a:hover,
164
+ .chapter-dropdown-menu a:focus {
165
+ background-color: rgba(0, 0, 0, 0.05);
166
+ color: var(--chapter-nav-text-color);
167
+ outline: none;
168
+ }
169
+
170
+ .chapter-dropdown-menu li.active a {
171
+ font-weight: 500;
172
+ color: var(--chapter-nav-text-color);
173
+ font-size: calc(0.95rem * 1.2);
174
+ }
175
+
176
+ /* Add padding to the content to prevent floating nav from overlapping */
177
+ .chapter-content {
178
+ padding-left: calc(var(--chapter-nav-width) + 40px);
179
+ }
180
+
181
+ /* Responsive adjustments */
182
+ @media (max-width: 1024px) {
183
+ .chapter-content {
184
+ padding-left: calc(var(--chapter-nav-width) + 30px);
185
+ }
186
+ }
187
+
188
+ @media (max-width: 768px) {
189
+ .chapter-navigation {
190
+ display: none;
191
+ }
192
+
193
+ .mobile-chapter-nav {
194
+ display: block;
195
+ }
196
+
197
+ .chapter-content {
198
+ padding-left: 20px;
199
+ padding-right: 20px;
200
+ }
201
+ }
202
+
203
+ /* Hide nav when printing */
204
+ @media print {
205
+ .chapter-navigation,
206
+ .mobile-chapter-nav {
207
+ display: none;
208
+ }
209
+
210
+ .chapter-content {
211
+ padding-left: 0;
212
+ }
213
+ }
@@ -0,0 +1,410 @@
1
+ // Apply syntax highlighting
2
+ document.addEventListener('DOMContentLoaded', (event) => {
3
+ // Apply syntax highlighting
4
+ document.querySelectorAll('pre code').forEach((block) => {
5
+ hljs.highlightElement(block);
6
+ });
7
+
8
+ // Add copy buttons to code blocks
9
+ document.querySelectorAll('pre').forEach((pre) => {
10
+ if (!pre.querySelector('.copy-button')) {
11
+ const button = document.createElement('button');
12
+ button.className = 'copy-button';
13
+ button.textContent = 'Copy';
14
+ button.addEventListener('click', () => {
15
+ const code = pre.querySelector('code').textContent;
16
+ navigator.clipboard.writeText(code);
17
+ button.textContent = 'Copied!';
18
+ setTimeout(() => {
19
+ button.textContent = 'Copy';
20
+ }, 2000);
21
+ });
22
+ pre.appendChild(button);
23
+ }
24
+ });
25
+
26
+ // Add heading IDs for anchor links if they don't exist
27
+ document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
28
+ if (!heading.id) {
29
+ const id = heading.textContent
30
+ .toLowerCase()
31
+ .replace(/[^\w\s-]/g, '')
32
+ .replace(/[\s_-]+/g, '-')
33
+ .trim();
34
+ heading.id = id;
35
+ }
36
+ });
37
+
38
+ // Make all external links open in new tabs
39
+ document.querySelectorAll('a').forEach(link => {
40
+ const href = link.getAttribute('href');
41
+ if (href && href.startsWith('http') && !href.startsWith('#')) {
42
+ link.setAttribute('target', '_blank');
43
+ link.setAttribute('rel', 'noopener noreferrer');
44
+ }
45
+ });
46
+
47
+ // Ensure all tables are 100% width with stronger enforcement
48
+ document.querySelectorAll('table').forEach(table => {
49
+ // Force full width with inline style AND classes
50
+ table.style.width = '100%';
51
+ table.style.tableLayout = 'fixed';
52
+ table.setAttribute('width', '100%');
53
+ table.classList.add('w-full');
54
+ table.classList.add('table-fixed');
55
+
56
+ // Add responsive wrapper if not already wrapped
57
+ if (table.parentElement.tagName !== 'DIV' || !table.parentElement.classList.contains('table-responsive')) {
58
+ const wrapper = document.createElement('div');
59
+ wrapper.className = 'table-responsive w-full overflow-x-auto my-8';
60
+ wrapper.style.width = '100%';
61
+ wrapper.style.maxWidth = '100%';
62
+ table.parentNode.insertBefore(wrapper, table);
63
+ wrapper.appendChild(table);
64
+ }
65
+
66
+ // Add proper classes and styles to table headers
67
+ table.querySelectorAll('th').forEach(th => {
68
+ th.classList.add('bg-gray-50');
69
+ th.classList.add('font-semibold');
70
+ th.classList.add('p-3');
71
+ th.classList.add('text-left');
72
+ th.classList.add('border');
73
+ th.classList.add('border-gray-200');
74
+ th.style.padding = '0.75rem 1rem';
75
+ });
76
+
77
+ // Add proper classes and styles to table cells
78
+ table.querySelectorAll('td').forEach(td => {
79
+ td.classList.add('p-3');
80
+ td.classList.add('text-left');
81
+ td.classList.add('border');
82
+ td.classList.add('border-gray-200');
83
+ td.style.padding = '0.75rem 1rem';
84
+ td.style.borderColor = 'rgba(0, 0, 0, 0.1)';
85
+ });
86
+
87
+ // Force table to use entire container width
88
+ const parentWidth = table.parentElement.offsetWidth;
89
+ if (parentWidth > 0) {
90
+ table.style.minWidth = parentWidth + 'px';
91
+ }
92
+ });
93
+
94
+ // Remove any blue highlights
95
+ document.querySelectorAll('.highlight, [style*="background-color: rgb(173, 214, 255)"]').forEach(el => {
96
+ el.classList.remove('highlight');
97
+ el.style.backgroundColor = 'transparent';
98
+ el.style.boxShadow = 'none';
99
+ });
100
+
101
+ // Format tabular data with scaling operations
102
+ document.querySelectorAll('p').forEach(p => {
103
+ const text = p.textContent.trim();
104
+ if (text.includes('IT/SRE for every') || text.includes('DevOps Engineer for every')) {
105
+ // Find the paragraph containing the scaling information
106
+ const container = document.createElement('div');
107
+ container.className = 'scaling-table';
108
+
109
+ // Parse the content and create a grid layout
110
+ const content = p.textContent.trim();
111
+ const parts = content.split(',').map(part => part.trim());
112
+
113
+ parts.forEach(part => {
114
+ const itemDiv = document.createElement('div');
115
+ itemDiv.textContent = part;
116
+ itemDiv.className = 'scaling-item';
117
+ container.appendChild(itemDiv);
118
+ });
119
+
120
+ // Replace the paragraph with the grid container
121
+ p.parentNode.replaceChild(container, p);
122
+ }
123
+ });
124
+
125
+ // Improve table layout and ensure proper alignment
126
+ document.querySelectorAll('table').forEach(table => {
127
+ // Add a container for horizontal scrolling if needed
128
+ const wrapper = document.createElement('div');
129
+ wrapper.style.width = '100%';
130
+ wrapper.style.overflowX = 'auto';
131
+ table.parentNode.insertBefore(wrapper, table);
132
+ wrapper.appendChild(table);
133
+
134
+ // Ensure consistent styling
135
+ table.querySelectorAll('th, td').forEach(cell => {
136
+ cell.style.padding = '0.75em 1em';
137
+ cell.style.verticalAlign = 'top';
138
+ cell.style.textAlign = 'left';
139
+ cell.style.fontSize = '18px';
140
+ });
141
+ });
142
+
143
+ // Process quote attributions with em dash
144
+ document.querySelectorAll('blockquote + p').forEach(p => {
145
+ const text = p.textContent || '';
146
+ if (text.startsWith('—') || text.startsWith('-') || text.startsWith('–')) {
147
+ p.style.textAlign = 'right';
148
+ p.style.fontStyle = 'italic';
149
+ p.style.marginTop = '-1.5em';
150
+ p.style.marginBottom = '2em';
151
+ }
152
+ });
153
+
154
+ // Style numbered items that are not in a list (like "1. Title")
155
+ document.querySelectorAll('p').forEach(p => {
156
+ const text = p.textContent.trim();
157
+ const match = text.match(/^(\d+)\.\s+(.+)$/);
158
+ if (match && match[1] && match[2]) {
159
+ const number = match[1];
160
+ const content = match[2];
161
+
162
+ // Don't apply this to short numbers followed by short content (likely actual content, not a title)
163
+ if (content.length > 5) {
164
+ p.innerHTML = `<span style="display: inline-block; min-width: 1.5em; margin-right: 0.5em; text-align: right; font-weight: bold;">${number}.</span>${content}`;
165
+ p.style.marginTop = '1.5em';
166
+ p.style.marginBottom = '0.75em';
167
+ p.style.fontWeight = '600';
168
+ p.style.fontSize = '22px';
169
+ }
170
+ }
171
+ });
172
+
173
+ // Convert imgix images to use w=1600 parameter
174
+ document.querySelectorAll('img[src*="imgix.net"]').forEach(img => {
175
+ const src = img.getAttribute('src');
176
+ if (src) {
177
+ const separator = src.includes('?') ? '&' : '?';
178
+ if (!src.includes('w=')) {
179
+ img.setAttribute('src', `${src}${separator}w=1600`);
180
+ }
181
+ }
182
+ });
183
+
184
+ window.initChapterNavigation = () => {
185
+ const chapterNavItems = document.querySelectorAll('.chapter-nav-item');
186
+ const chapterDropdownBtn = document.getElementById('chapter-dropdown-btn');
187
+ const chapterDropdownMenu = document.getElementById('chapter-dropdown-menu');
188
+ const currentChapterText = document.querySelector('.current-chapter');
189
+
190
+ if (chapterDropdownMenu) {
191
+ chapterDropdownMenu.style.display = 'none';
192
+ }
193
+
194
+ const throttle = (func, limit) => {
195
+ let inThrottle;
196
+ return function() {
197
+ const args = arguments;
198
+ const context = this;
199
+ if (!inThrottle) {
200
+ func.apply(context, args);
201
+ inThrottle = true;
202
+ setTimeout(() => inThrottle = false, limit);
203
+ }
204
+ };
205
+ };
206
+
207
+ window.updateActiveChapter = () => {
208
+ try {
209
+ // Only target H2 headings
210
+ const headings = Array.from(document.querySelectorAll('h2'));
211
+
212
+ if (!headings.length || !chapterNavItems.length) return;
213
+
214
+ // Calculate which heading is most visible in the viewport
215
+ const viewportHeight = window.innerHeight;
216
+ const viewportMiddle = viewportHeight / 2;
217
+
218
+ let bestVisibleHeading = null;
219
+ let bestVisibleScore = -1;
220
+
221
+ headings.forEach(heading => {
222
+ if (!heading) return;
223
+
224
+ const rect = heading.getBoundingClientRect();
225
+
226
+ if (rect.bottom < 0 || rect.top > viewportHeight) return;
227
+
228
+ const visibleTop = Math.max(0, rect.top);
229
+ const visibleBottom = Math.min(viewportHeight, rect.bottom);
230
+ const visibleHeight = visibleBottom - visibleTop;
231
+
232
+ const distanceFromMiddle = Math.abs(rect.top + rect.height / 2 - viewportMiddle);
233
+ const normalizedDistance = 1 - (distanceFromMiddle / viewportHeight);
234
+
235
+ const score = visibleHeight * normalizedDistance * 2;
236
+
237
+ if (score > bestVisibleScore) {
238
+ bestVisibleScore = score;
239
+ bestVisibleHeading = heading;
240
+ }
241
+ });
242
+
243
+ if (bestVisibleHeading) {
244
+ const headingId = bestVisibleHeading.getAttribute('id');
245
+ const headingText = bestVisibleHeading.textContent.trim();
246
+
247
+ chapterNavItems.forEach(item => {
248
+ if (!item) return;
249
+
250
+ const navLink = item.querySelector('.chapter-nav-link');
251
+ if (!navLink) return;
252
+
253
+ const itemText = navLink.textContent.trim();
254
+ const isActive = itemText === headingText || item.getAttribute('data-chapter-id') === headingId;
255
+
256
+ if (isActive) {
257
+ item.classList.add('active');
258
+
259
+ const navContainer = document.querySelector('.chapter-navigation');
260
+ if (navContainer) {
261
+ const itemOffset = item.offsetTop;
262
+ const navHeight = navContainer.offsetHeight;
263
+ const itemHeight = item.offsetHeight;
264
+
265
+ navContainer.scrollTop = itemOffset - (navHeight / 2) + (itemHeight / 2);
266
+ }
267
+
268
+ if (currentChapterText) {
269
+ currentChapterText.textContent = itemText;
270
+ }
271
+ } else {
272
+ item.classList.remove('active');
273
+ }
274
+ });
275
+
276
+ const mobileDropdownItems = document.querySelectorAll('#chapter-dropdown-menu li');
277
+ mobileDropdownItems.forEach(item => {
278
+ if (!item) return;
279
+
280
+ const link = item.querySelector('a');
281
+ if (!link) return;
282
+
283
+ const itemText = link.textContent.trim();
284
+ const isActive = itemText === headingText || item.getAttribute('data-chapter-id') === headingId;
285
+
286
+ if (isActive) {
287
+ item.classList.add('active');
288
+ } else {
289
+ item.classList.remove('active');
290
+ }
291
+ });
292
+ } else if (headings.length > 0) {
293
+ const firstHeading = headings[0];
294
+ const firstHeadingText = firstHeading.textContent.trim();
295
+
296
+ chapterNavItems.forEach(item => {
297
+ if (!item) return;
298
+
299
+ const navLink = item.querySelector('.chapter-nav-link');
300
+ if (!navLink) return;
301
+
302
+ const itemText = navLink.textContent.trim();
303
+ const isActive = itemText === firstHeadingText;
304
+
305
+ if (isActive) {
306
+ item.classList.add('active');
307
+ if (currentChapterText) {
308
+ currentChapterText.textContent = itemText;
309
+ }
310
+ } else {
311
+ item.classList.remove('active');
312
+ }
313
+ });
314
+
315
+ const mobileDropdownItems = document.querySelectorAll('#chapter-dropdown-menu li');
316
+ mobileDropdownItems.forEach(item => {
317
+ if (!item) return;
318
+
319
+ const link = item.querySelector('a');
320
+ if (!link) return;
321
+
322
+ const itemText = link.textContent.trim();
323
+ const isActive = itemText === firstHeadingText;
324
+
325
+ if (isActive) {
326
+ item.classList.add('active');
327
+ } else {
328
+ item.classList.remove('active');
329
+ }
330
+ });
331
+ }
332
+ } catch (error) {
333
+ console.error('Error updating active chapter:', error);
334
+ }
335
+ };
336
+
337
+ if (chapterDropdownBtn && chapterDropdownMenu) {
338
+ chapterDropdownBtn.addEventListener('click', (e) => {
339
+ e.preventDefault();
340
+ e.stopPropagation();
341
+
342
+ chapterDropdownBtn.classList.toggle('active');
343
+
344
+ const isCurrentlyHidden = chapterDropdownMenu.style.display === 'none' || chapterDropdownMenu.style.display === '';
345
+ chapterDropdownMenu.style.display = isCurrentlyHidden ? 'block' : 'none';
346
+
347
+ chapterDropdownBtn.setAttribute('aria-expanded', isCurrentlyHidden ? 'true' : 'false');
348
+
349
+ console.log('Dropdown toggled, now visible:', isCurrentlyHidden);
350
+ });
351
+
352
+ chapterDropdownBtn.addEventListener('touchend', (e) => {
353
+ e.preventDefault();
354
+ e.stopPropagation();
355
+
356
+ chapterDropdownBtn.classList.toggle('active');
357
+
358
+ const isCurrentlyHidden = chapterDropdownMenu.style.display === 'none' || chapterDropdownMenu.style.display === '';
359
+ chapterDropdownMenu.style.display = isCurrentlyHidden ? 'block' : 'none';
360
+
361
+ chapterDropdownBtn.setAttribute('aria-expanded', isCurrentlyHidden ? 'true' : 'false');
362
+
363
+ console.log('Dropdown touched, now visible:', isCurrentlyHidden);
364
+ }, { passive: false });
365
+ }
366
+
367
+ document.addEventListener('click', (e) => {
368
+ if (chapterDropdownMenu && chapterDropdownBtn && !e.target.closest('.mobile-chapter-dropdown')) {
369
+ chapterDropdownBtn.classList.remove('active');
370
+ chapterDropdownMenu.style.display = 'none';
371
+ chapterDropdownBtn.setAttribute('aria-expanded', 'false');
372
+ }
373
+ });
374
+
375
+ document.querySelectorAll('.chapter-nav-link, #chapter-dropdown-menu a').forEach(link => {
376
+ if (!link) return;
377
+
378
+ link.addEventListener('click', (e) => {
379
+ e.preventDefault();
380
+ const targetId = link.getAttribute('href');
381
+ if (!targetId) return;
382
+
383
+ const targetElement = document.querySelector(targetId);
384
+
385
+ if (targetElement) {
386
+ window.scrollTo({
387
+ top: targetElement.offsetTop - 80,
388
+ behavior: 'smooth'
389
+ });
390
+
391
+ if (chapterDropdownMenu && chapterDropdownBtn) {
392
+ chapterDropdownMenu.style.display = 'none';
393
+ chapterDropdownBtn.classList.remove('active');
394
+ chapterDropdownBtn.setAttribute('aria-expanded', 'false');
395
+ }
396
+ }
397
+ });
398
+ });
399
+
400
+ window.addEventListener('scroll', throttle(updateActiveChapter, 100));
401
+
402
+ updateActiveChapter();
403
+ };
404
+
405
+ if (document.readyState === 'loading') {
406
+ document.addEventListener('DOMContentLoaded', window.initChapterNavigation);
407
+ } else {
408
+ window.initChapterNavigation();
409
+ }
410
+ });