jekyll-theme-zer0 0.16.0 → 0.17.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -10
- data/README.md +7 -7
- data/_data/authors.yml +2 -2
- data/_data/features.yml +676 -0
- data/_data/navigation/README.md +54 -0
- data/_data/navigation/about.yml +7 -5
- data/_data/navigation/docs.yml +77 -31
- data/_data/navigation/home.yml +4 -3
- data/_data/navigation/main.yml +16 -7
- data/_data/navigation/quickstart.yml +4 -2
- data/_includes/components/js-cdn.html +2 -2
- data/_includes/landing/landing-install-cards.html +8 -8
- data/_includes/landing/landing-quick-links.html +4 -4
- data/_includes/navigation/breadcrumbs.html +29 -6
- data/_includes/navigation/nav-tree.html +181 -0
- data/_includes/navigation/navbar.html +262 -9
- data/_includes/navigation/sidebar-left.html +22 -23
- data/_layouts/default.html +1 -1
- data/_layouts/landing.html +1 -1
- data/_layouts/notebook.html +4 -4
- data/_layouts/root.html +2 -2
- data/_sass/core/_nav-tree.scss +145 -0
- data/_sass/custom.scss +3 -0
- data/assets/js/modules/navigation/config.js +149 -0
- data/assets/js/modules/navigation/focus.js +189 -0
- data/assets/js/modules/navigation/gestures.js +179 -0
- data/assets/js/modules/navigation/index.js +227 -0
- data/assets/js/modules/navigation/keyboard.js +237 -0
- data/assets/js/modules/navigation/scroll-spy.js +219 -0
- data/assets/js/modules/navigation/sidebar-state.js +267 -0
- data/assets/js/modules/navigation/smooth-scroll.js +153 -0
- data/scripts/migrate-nav-modes.sh +146 -0
- metadata +15 -3
- data/assets/js/sidebar.js +0 -511
data/assets/js/sidebar.js
DELETED
|
@@ -1,511 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ===================================================================
|
|
3
|
-
* SIDEBAR NAVIGATION - Enhanced JavaScript Functionality
|
|
4
|
-
* ===================================================================
|
|
5
|
-
*
|
|
6
|
-
* File: sidebar.js
|
|
7
|
-
* Path: assets/js/sidebar.js
|
|
8
|
-
* Purpose: Enhanced sidebar navigation with scroll spy, smooth scrolling,
|
|
9
|
-
* keyboard shortcuts, and mobile gesture support
|
|
10
|
-
*
|
|
11
|
-
* Features:
|
|
12
|
-
* - Intersection Observer-based scroll spy for better performance
|
|
13
|
-
* - Smooth scroll to TOC anchors with offset for fixed headers
|
|
14
|
-
* - Keyboard shortcuts for navigation ([ and ] for prev/next)
|
|
15
|
-
* - Swipe gesture support for mobile offcanvas
|
|
16
|
-
* - Active section highlighting in TOC
|
|
17
|
-
* - Focus management for accessibility
|
|
18
|
-
* - Error handling for missing elements
|
|
19
|
-
*
|
|
20
|
-
* Dependencies:
|
|
21
|
-
* - Bootstrap 5 (for offcanvas functionality)
|
|
22
|
-
* - Modern browser with Intersection Observer support
|
|
23
|
-
*
|
|
24
|
-
* Browser Support:
|
|
25
|
-
* - Chrome/Edge 58+
|
|
26
|
-
* - Firefox 55+
|
|
27
|
-
* - Safari 12.1+
|
|
28
|
-
*
|
|
29
|
-
* ===================================================================
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
(function() {
|
|
33
|
-
'use strict';
|
|
34
|
-
|
|
35
|
-
// ===================================================================
|
|
36
|
-
// CONFIGURATION
|
|
37
|
-
// ===================================================================
|
|
38
|
-
|
|
39
|
-
const config = {
|
|
40
|
-
tocSelector: '#TableOfContents',
|
|
41
|
-
tocLinkSelector: '#TableOfContents a',
|
|
42
|
-
mainContentSelector: '.bd-content',
|
|
43
|
-
observerRootMargin: '-80px 0px -80px 0px', // Account for fixed headers
|
|
44
|
-
smoothScrollOffset: 80, // Offset for fixed headers
|
|
45
|
-
debounceDelay: 100,
|
|
46
|
-
enableKeyboardShortcuts: true,
|
|
47
|
-
enableSwipeGestures: true,
|
|
48
|
-
swipeThreshold: 50 // Minimum distance for swipe gesture
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// ===================================================================
|
|
52
|
-
// UTILITY FUNCTIONS
|
|
53
|
-
// ===================================================================
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Debounce function to limit function calls
|
|
57
|
-
*/
|
|
58
|
-
function debounce(func, wait) {
|
|
59
|
-
let timeout;
|
|
60
|
-
return function executedFunction(...args) {
|
|
61
|
-
const later = () => {
|
|
62
|
-
clearTimeout(timeout);
|
|
63
|
-
func(...args);
|
|
64
|
-
};
|
|
65
|
-
clearTimeout(timeout);
|
|
66
|
-
timeout = setTimeout(later, wait);
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Get element safely with error handling
|
|
72
|
-
*/
|
|
73
|
-
function getElement(selector) {
|
|
74
|
-
try {
|
|
75
|
-
return document.querySelector(selector);
|
|
76
|
-
} catch (error) {
|
|
77
|
-
console.warn(`Sidebar.js: Element not found - ${selector}`);
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Get all elements safely with error handling
|
|
84
|
-
*/
|
|
85
|
-
function getElements(selector) {
|
|
86
|
-
try {
|
|
87
|
-
return document.querySelectorAll(selector);
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.warn(`Sidebar.js: Elements not found - ${selector}`);
|
|
90
|
-
return [];
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ===================================================================
|
|
95
|
-
// SCROLL SPY - Intersection Observer Implementation
|
|
96
|
-
// ===================================================================
|
|
97
|
-
|
|
98
|
-
class ScrollSpy {
|
|
99
|
-
constructor() {
|
|
100
|
-
this.tocLinks = getElements(config.tocLinkSelector);
|
|
101
|
-
this.headings = this.getHeadings();
|
|
102
|
-
this.currentActive = null;
|
|
103
|
-
|
|
104
|
-
if (this.headings.length === 0 || this.tocLinks.length === 0) {
|
|
105
|
-
console.log('Sidebar.js: No TOC or headings found, skipping scroll spy');
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
this.init();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Get all headings that have corresponding TOC links
|
|
114
|
-
*/
|
|
115
|
-
getHeadings() {
|
|
116
|
-
const headings = [];
|
|
117
|
-
this.tocLinks.forEach(link => {
|
|
118
|
-
const href = link.getAttribute('href');
|
|
119
|
-
if (href && href.startsWith('#')) {
|
|
120
|
-
const id = href.substring(1);
|
|
121
|
-
const heading = document.getElementById(id);
|
|
122
|
-
if (heading) {
|
|
123
|
-
headings.push({
|
|
124
|
-
element: heading,
|
|
125
|
-
link: link,
|
|
126
|
-
id: id
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
return headings;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Initialize Intersection Observer
|
|
136
|
-
*/
|
|
137
|
-
init() {
|
|
138
|
-
const observerOptions = {
|
|
139
|
-
root: null,
|
|
140
|
-
rootMargin: config.observerRootMargin,
|
|
141
|
-
threshold: [0, 0.25, 0.5, 0.75, 1]
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
this.observer = new IntersectionObserver(
|
|
145
|
-
entries => this.handleIntersection(entries),
|
|
146
|
-
observerOptions
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
// Observe all headings
|
|
150
|
-
this.headings.forEach(heading => {
|
|
151
|
-
this.observer.observe(heading.element);
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Handle intersection events
|
|
157
|
-
*/
|
|
158
|
-
handleIntersection(entries) {
|
|
159
|
-
// Find the most visible heading
|
|
160
|
-
let mostVisible = null;
|
|
161
|
-
let maxRatio = 0;
|
|
162
|
-
|
|
163
|
-
entries.forEach(entry => {
|
|
164
|
-
if (entry.isIntersecting && entry.intersectionRatio > maxRatio) {
|
|
165
|
-
maxRatio = entry.intersectionRatio;
|
|
166
|
-
mostVisible = this.headings.find(h => h.element === entry.target);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// If we found a visible heading, activate it
|
|
171
|
-
if (mostVisible) {
|
|
172
|
-
this.setActiveLink(mostVisible.link);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Set active link with visual feedback
|
|
178
|
-
*/
|
|
179
|
-
setActiveLink(link) {
|
|
180
|
-
if (this.currentActive === link) return;
|
|
181
|
-
|
|
182
|
-
// Remove previous active state
|
|
183
|
-
this.tocLinks.forEach(l => l.classList.remove('active'));
|
|
184
|
-
|
|
185
|
-
// Add active state
|
|
186
|
-
if (link) {
|
|
187
|
-
link.classList.add('active');
|
|
188
|
-
this.currentActive = link;
|
|
189
|
-
|
|
190
|
-
// Scroll TOC to show active link (if needed)
|
|
191
|
-
this.scrollTocToActiveLink(link);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Scroll TOC container to show active link
|
|
197
|
-
*/
|
|
198
|
-
scrollTocToActiveLink(link) {
|
|
199
|
-
const tocContainer = getElement('.bd-toc .offcanvas-body');
|
|
200
|
-
if (!tocContainer) return;
|
|
201
|
-
|
|
202
|
-
const linkRect = link.getBoundingClientRect();
|
|
203
|
-
const containerRect = tocContainer.getBoundingClientRect();
|
|
204
|
-
|
|
205
|
-
// Check if link is out of view
|
|
206
|
-
if (linkRect.top < containerRect.top || linkRect.bottom > containerRect.bottom) {
|
|
207
|
-
link.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Cleanup observer
|
|
213
|
-
*/
|
|
214
|
-
destroy() {
|
|
215
|
-
if (this.observer) {
|
|
216
|
-
this.observer.disconnect();
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// ===================================================================
|
|
222
|
-
// SMOOTH SCROLL - Enhanced anchor navigation
|
|
223
|
-
// ===================================================================
|
|
224
|
-
|
|
225
|
-
class SmoothScroll {
|
|
226
|
-
constructor() {
|
|
227
|
-
this.tocLinks = getElements(config.tocLinkSelector);
|
|
228
|
-
this.init();
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
init() {
|
|
232
|
-
this.tocLinks.forEach(link => {
|
|
233
|
-
link.addEventListener('click', e => this.handleClick(e));
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
handleClick(event) {
|
|
238
|
-
const href = event.currentTarget.getAttribute('href');
|
|
239
|
-
|
|
240
|
-
if (!href || !href.startsWith('#')) return;
|
|
241
|
-
|
|
242
|
-
event.preventDefault();
|
|
243
|
-
|
|
244
|
-
const targetId = href.substring(1);
|
|
245
|
-
const targetElement = document.getElementById(targetId);
|
|
246
|
-
|
|
247
|
-
if (!targetElement) return;
|
|
248
|
-
|
|
249
|
-
// Calculate scroll position with offset
|
|
250
|
-
const elementPosition = targetElement.getBoundingClientRect().top;
|
|
251
|
-
const offsetPosition = elementPosition + window.pageYOffset - config.smoothScrollOffset;
|
|
252
|
-
|
|
253
|
-
// Smooth scroll to target
|
|
254
|
-
window.scrollTo({
|
|
255
|
-
top: offsetPosition,
|
|
256
|
-
behavior: 'smooth'
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// Update URL without jumping
|
|
260
|
-
if (history.pushState) {
|
|
261
|
-
history.pushState(null, null, href);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Close mobile offcanvas if open
|
|
265
|
-
this.closeMobileOffcanvas();
|
|
266
|
-
|
|
267
|
-
// Update focus for accessibility
|
|
268
|
-
targetElement.setAttribute('tabindex', '-1');
|
|
269
|
-
targetElement.focus();
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
closeMobileOffcanvas() {
|
|
273
|
-
const tocOffcanvas = document.getElementById('tocContents');
|
|
274
|
-
if (tocOffcanvas && window.innerWidth < 992) {
|
|
275
|
-
const bsOffcanvas = bootstrap.Offcanvas.getInstance(tocOffcanvas);
|
|
276
|
-
if (bsOffcanvas) {
|
|
277
|
-
bsOffcanvas.hide();
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// ===================================================================
|
|
284
|
-
// KEYBOARD SHORTCUTS - Navigation enhancements
|
|
285
|
-
// ===================================================================
|
|
286
|
-
|
|
287
|
-
class KeyboardShortcuts {
|
|
288
|
-
constructor() {
|
|
289
|
-
if (!config.enableKeyboardShortcuts) return;
|
|
290
|
-
|
|
291
|
-
this.tocLinks = Array.from(getElements(config.tocLinkSelector));
|
|
292
|
-
this.currentIndex = -1;
|
|
293
|
-
this.init();
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
init() {
|
|
297
|
-
document.addEventListener('keydown', e => this.handleKeydown(e));
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
handleKeydown(event) {
|
|
301
|
-
// Ignore if user is typing in an input
|
|
302
|
-
if (event.target.matches('input, textarea, select')) return;
|
|
303
|
-
|
|
304
|
-
switch(event.key) {
|
|
305
|
-
case '[':
|
|
306
|
-
event.preventDefault();
|
|
307
|
-
this.navigatePrevious();
|
|
308
|
-
break;
|
|
309
|
-
case ']':
|
|
310
|
-
event.preventDefault();
|
|
311
|
-
this.navigateNext();
|
|
312
|
-
break;
|
|
313
|
-
case '/':
|
|
314
|
-
event.preventDefault();
|
|
315
|
-
this.focusSearch();
|
|
316
|
-
break;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
navigatePrevious() {
|
|
321
|
-
const activeLink = document.querySelector(`${config.tocLinkSelector}.active`);
|
|
322
|
-
if (activeLink) {
|
|
323
|
-
this.currentIndex = this.tocLinks.indexOf(activeLink);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (this.currentIndex > 0) {
|
|
327
|
-
this.currentIndex--;
|
|
328
|
-
this.tocLinks[this.currentIndex].click();
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
navigateNext() {
|
|
333
|
-
const activeLink = document.querySelector(`${config.tocLinkSelector}.active`);
|
|
334
|
-
if (activeLink) {
|
|
335
|
-
this.currentIndex = this.tocLinks.indexOf(activeLink);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
if (this.currentIndex < this.tocLinks.length - 1) {
|
|
339
|
-
this.currentIndex++;
|
|
340
|
-
this.tocLinks[this.currentIndex].click();
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
focusSearch() {
|
|
345
|
-
// Future: focus sidebar search if implemented
|
|
346
|
-
console.log('Sidebar.js: Search functionality not yet implemented');
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// ===================================================================
|
|
351
|
-
// SWIPE GESTURES - Mobile offcanvas control
|
|
352
|
-
// ===================================================================
|
|
353
|
-
|
|
354
|
-
class SwipeGestures {
|
|
355
|
-
constructor() {
|
|
356
|
-
if (!config.enableSwipeGestures) return;
|
|
357
|
-
|
|
358
|
-
this.startX = 0;
|
|
359
|
-
this.startY = 0;
|
|
360
|
-
this.distX = 0;
|
|
361
|
-
this.distY = 0;
|
|
362
|
-
this.init();
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
init() {
|
|
366
|
-
document.addEventListener('touchstart', e => this.handleTouchStart(e), { passive: true });
|
|
367
|
-
document.addEventListener('touchmove', e => this.handleTouchMove(e), { passive: true });
|
|
368
|
-
document.addEventListener('touchend', e => this.handleTouchEnd(e));
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
handleTouchStart(event) {
|
|
372
|
-
const touch = event.touches[0];
|
|
373
|
-
this.startX = touch.clientX;
|
|
374
|
-
this.startY = touch.clientY;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
handleTouchMove(event) {
|
|
378
|
-
if (!this.startX || !this.startY) return;
|
|
379
|
-
|
|
380
|
-
const touch = event.touches[0];
|
|
381
|
-
this.distX = touch.clientX - this.startX;
|
|
382
|
-
this.distY = touch.clientY - this.startY;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
handleTouchEnd(event) {
|
|
386
|
-
if (Math.abs(this.distX) < config.swipeThreshold) {
|
|
387
|
-
this.reset();
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Only handle horizontal swipes
|
|
392
|
-
if (Math.abs(this.distX) > Math.abs(this.distY)) {
|
|
393
|
-
if (this.distX > 0) {
|
|
394
|
-
this.handleSwipeRight();
|
|
395
|
-
} else {
|
|
396
|
-
this.handleSwipeLeft();
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
this.reset();
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
handleSwipeRight() {
|
|
404
|
-
// Open left sidebar
|
|
405
|
-
const leftSidebar = document.getElementById('bdSidebar');
|
|
406
|
-
if (leftSidebar && this.startX < 50) { // Only if started from left edge
|
|
407
|
-
const bsOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(leftSidebar);
|
|
408
|
-
bsOffcanvas.show();
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
handleSwipeLeft() {
|
|
413
|
-
// Open right TOC sidebar
|
|
414
|
-
const rightSidebar = document.getElementById('tocContents');
|
|
415
|
-
const windowWidth = window.innerWidth;
|
|
416
|
-
|
|
417
|
-
if (rightSidebar && this.startX > windowWidth - 50) { // Only if started from right edge
|
|
418
|
-
const bsOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(rightSidebar);
|
|
419
|
-
bsOffcanvas.show();
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
reset() {
|
|
424
|
-
this.startX = 0;
|
|
425
|
-
this.startY = 0;
|
|
426
|
-
this.distX = 0;
|
|
427
|
-
this.distY = 0;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// ===================================================================
|
|
432
|
-
// FOCUS MANAGEMENT - Accessibility enhancements
|
|
433
|
-
// ===================================================================
|
|
434
|
-
|
|
435
|
-
class FocusManager {
|
|
436
|
-
constructor() {
|
|
437
|
-
this.init();
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
init() {
|
|
441
|
-
// Handle focus return when offcanvas closes
|
|
442
|
-
const offcanvasElements = getElements('.offcanvas');
|
|
443
|
-
|
|
444
|
-
offcanvasElements.forEach(offcanvas => {
|
|
445
|
-
offcanvas.addEventListener('hidden.bs.offcanvas', () => {
|
|
446
|
-
this.returnFocus(offcanvas);
|
|
447
|
-
});
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
returnFocus(offcanvas) {
|
|
452
|
-
// Find the trigger button that opened this offcanvas
|
|
453
|
-
const triggerId = offcanvas.id;
|
|
454
|
-
const trigger = document.querySelector(`[data-bs-target="#${triggerId}"]`);
|
|
455
|
-
|
|
456
|
-
if (trigger) {
|
|
457
|
-
trigger.focus();
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// ===================================================================
|
|
463
|
-
// INITIALIZATION
|
|
464
|
-
// ===================================================================
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Initialize all sidebar enhancements when DOM is ready
|
|
468
|
-
*/
|
|
469
|
-
function init() {
|
|
470
|
-
// Check if we're on a page that needs sidebar enhancements
|
|
471
|
-
const toc = getElement(config.tocSelector);
|
|
472
|
-
if (!toc) {
|
|
473
|
-
console.log('Sidebar.js: No table of contents found, skipping initialization');
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
try {
|
|
478
|
-
// Initialize all modules
|
|
479
|
-
const scrollSpy = new ScrollSpy();
|
|
480
|
-
const smoothScroll = new SmoothScroll();
|
|
481
|
-
const keyboardShortcuts = new KeyboardShortcuts();
|
|
482
|
-
const swipeGestures = new SwipeGestures();
|
|
483
|
-
const focusManager = new FocusManager();
|
|
484
|
-
|
|
485
|
-
console.log('Sidebar.js: Successfully initialized all modules');
|
|
486
|
-
|
|
487
|
-
// Cleanup on page unload
|
|
488
|
-
window.addEventListener('beforeunload', () => {
|
|
489
|
-
if (scrollSpy && scrollSpy.destroy) {
|
|
490
|
-
scrollSpy.destroy();
|
|
491
|
-
}
|
|
492
|
-
});
|
|
493
|
-
} catch (error) {
|
|
494
|
-
console.error('Sidebar.js: Initialization error', error);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Wait for DOM and Bootstrap to be ready
|
|
499
|
-
if (document.readyState === 'loading') {
|
|
500
|
-
document.addEventListener('DOMContentLoaded', init);
|
|
501
|
-
} else {
|
|
502
|
-
// DOM already loaded
|
|
503
|
-
if (typeof bootstrap !== 'undefined') {
|
|
504
|
-
init();
|
|
505
|
-
} else {
|
|
506
|
-
// Wait for Bootstrap to load
|
|
507
|
-
window.addEventListener('load', init);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
})();
|