1+ // Modern Interactive Documentation JavaScript
2+ // ===========================================
3+
4+ document . addEventListener ( 'DOMContentLoaded' , function ( ) {
5+ 'use strict' ;
6+
7+ // Initialize all interactive features
8+ initializeInteractiveFeatures ( ) ;
9+ initializeSearchHighlighting ( ) ;
10+ initializeCodeCopyButtons ( ) ;
11+ initializeProgressiveDisclosure ( ) ;
12+ initializeSmoothScrolling ( ) ;
13+ initializeThemeToggle ( ) ;
14+
15+ console . log ( 'Enhanced documentation JavaScript loaded successfully' ) ;
16+ } ) ;
17+
18+ /**
19+ * Initialize all interactive features
20+ */
21+ function initializeInteractiveFeatures ( ) {
22+ // Add loading animations for heavy content
23+ const heavyContent = document . querySelectorAll ( '.architecture-diagram, .call-tree-diagram' ) ;
24+ heavyContent . forEach ( element => {
25+ element . style . opacity = '0' ;
26+ element . style . transition = 'opacity 0.5s ease-in-out' ;
27+
28+ // Simulate loading and fade in
29+ setTimeout ( ( ) => {
30+ element . style . opacity = '1' ;
31+ } , 100 ) ;
32+ } ) ;
33+
34+ // Add hover effects for interactive elements
35+ const interactiveElements = document . querySelectorAll ( '.sd-card, .admonition, details.sd-dropdown' ) ;
36+ interactiveElements . forEach ( element => {
37+ element . addEventListener ( 'mouseenter' , function ( ) {
38+ this . style . transition = 'all 0.3s ease' ;
39+ } ) ;
40+ } ) ;
41+ }
42+
43+ /**
44+ * Enhanced search term highlighting
45+ */
46+ function initializeSearchHighlighting ( ) {
47+ // Get search terms from URL parameters
48+ const urlParams = new URLSearchParams ( window . location . search ) ;
49+ const searchTerm = urlParams . get ( 'highlight' ) ;
50+
51+ if ( searchTerm ) {
52+ highlightSearchTerm ( searchTerm ) ;
53+ }
54+ }
55+
56+ /**
57+ * Highlight search terms in the document
58+ */
59+ function highlightSearchTerm ( term ) {
60+ const walker = document . createTreeWalker (
61+ document . body ,
62+ NodeFilter . SHOW_TEXT ,
63+ {
64+ acceptNode : function ( node ) {
65+ // Skip script and style elements
66+ const parentTag = node . parentElement . tagName . toLowerCase ( ) ;
67+ if ( [ 'script' , 'style' , 'noscript' ] . includes ( parentTag ) ) {
68+ return NodeFilter . FILTER_REJECT ;
69+ }
70+ return NodeFilter . FILTER_ACCEPT ;
71+ }
72+ }
73+ ) ;
74+
75+ const textNodes = [ ] ;
76+ let node ;
77+
78+ while ( node = walker . nextNode ( ) ) {
79+ textNodes . push ( node ) ;
80+ }
81+
82+ const regex = new RegExp ( `(${ term } )` , 'gi' ) ;
83+
84+ textNodes . forEach ( textNode => {
85+ if ( regex . test ( textNode . textContent ) ) {
86+ const parent = textNode . parentNode ;
87+ const wrapper = document . createElement ( 'span' ) ;
88+ wrapper . innerHTML = textNode . textContent . replace ( regex , '<mark class="search-highlight">$1</mark>' ) ;
89+ parent . replaceChild ( wrapper , textNode ) ;
90+ }
91+ } ) ;
92+
93+ // Add CSS for search highlighting
94+ if ( ! document . getElementById ( 'search-highlight-styles' ) ) {
95+ const style = document . createElement ( 'style' ) ;
96+ style . id = 'search-highlight-styles' ;
97+ style . textContent = `
98+ .search-highlight {
99+ background-color: #ffeb3b;
100+ color: #333;
101+ padding: 2px 4px;
102+ border-radius: 3px;
103+ font-weight: 600;
104+ animation: highlight-pulse 2s ease-in-out;
105+ }
106+
107+ @keyframes highlight-pulse {
108+ 0% { background-color: #ffeb3b; }
109+ 50% { background-color: #ff9800; }
110+ 100% { background-color: #ffeb3b; }
111+ }
112+ ` ;
113+ document . head . appendChild ( style ) ;
114+ }
115+ }
116+
117+ /**
118+ * Enhanced copy buttons for code blocks
119+ */
120+ function initializeCodeCopyButtons ( ) {
121+ const codeBlocks = document . querySelectorAll ( '.highlight pre' ) ;
122+
123+ codeBlocks . forEach ( codeBlock => {
124+ // Skip if copy button already exists
125+ if ( codeBlock . parentNode . querySelector ( '.copy-button' ) ) {
126+ return ;
127+ }
128+
129+ const copyButton = document . createElement ( 'button' ) ;
130+ copyButton . className = 'copy-button' ;
131+ copyButton . innerHTML = `
132+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
133+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
134+ <path d="m5 15-4 4c-1-5 0-7 4-11 4-4 8-3 11 0l4 4c3 3 4 7 0 11-4 4-8 3-11 0z"></path>
135+ </svg>
136+ Copy
137+ ` ;
138+ copyButton . title = 'Copy code to clipboard' ;
139+
140+ copyButton . addEventListener ( 'click' , async function ( ) {
141+ const code = codeBlock . textContent ;
142+
143+ try {
144+ await navigator . clipboard . writeText ( code ) ;
145+
146+ // Visual feedback
147+ copyButton . innerHTML = `
148+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
149+ <polyline points="20,6 9,17 4,12"></polyline>
150+ </svg>
151+ Copied!
152+ ` ;
153+ copyButton . style . background = '#28a745' ;
154+ copyButton . style . color = 'white' ;
155+
156+ setTimeout ( ( ) => {
157+ copyButton . innerHTML = `
158+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
159+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
160+ <path d="m5 15-4 4c-1-5 0-7 4-11 4-4 8-3 11 0l4 4c3 3 4 7 0 11-4 4-8 3-11 0z"></path>
161+ </svg>
162+ Copy
163+ ` ;
164+ copyButton . style . background = '' ;
165+ copyButton . style . color = '' ;
166+ } , 2000 ) ;
167+
168+ } catch ( err ) {
169+ console . error ( 'Failed to copy code:' , err ) ;
170+ copyButton . innerHTML = 'Failed to copy' ;
171+ setTimeout ( ( ) => {
172+ copyButton . innerHTML = `
173+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
174+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
175+ <path d="m5 15-4 4c-1-5 0-7 4-11 4-4 8-3 11 0l4 4c3 3 4 7 0 11-4 4-8 3-11 0z"></path>
176+ </svg>
177+ Copy
178+ ` ;
179+ } , 2000 ) ;
180+ }
181+ } ) ;
182+
183+ // Position the button
184+ const container = codeBlock . parentNode ;
185+ container . style . position = 'relative' ;
186+ container . appendChild ( copyButton ) ;
187+ } ) ;
188+
189+ // Add CSS for copy buttons
190+ if ( ! document . getElementById ( 'copy-button-styles' ) ) {
191+ const style = document . createElement ( 'style' ) ;
192+ style . id = 'copy-button-styles' ;
193+ style . textContent = `
194+ .copy-button {
195+ position: absolute;
196+ top: 10px;
197+ right: 10px;
198+ background: rgba(255, 255, 255, 0.1);
199+ border: 1px solid rgba(255, 255, 255, 0.2);
200+ color: rgba(255, 255, 255, 0.8);
201+ padding: 6px 12px;
202+ border-radius: 4px;
203+ font-size: 12px;
204+ cursor: pointer;
205+ display: flex;
206+ align-items: center;
207+ gap: 4px;
208+ transition: all 0.2s ease;
209+ backdrop-filter: blur(10px);
210+ z-index: 10;
211+ }
212+
213+ .copy-button:hover {
214+ background: rgba(255, 255, 255, 0.2);
215+ border-color: rgba(255, 255, 255, 0.3);
216+ color: white;
217+ }
218+
219+ .copy-button svg {
220+ width: 16px;
221+ height: 16px;
222+ }
223+ ` ;
224+ document . head . appendChild ( style ) ;
225+ }
226+ }
227+
228+ /**
229+ * Progressive disclosure for large sections
230+ */
231+ function initializeProgressiveDisclosure ( ) {
232+ // Auto-collapse large sections on mobile
233+ if ( window . innerWidth < 768 ) {
234+ const largeSections = document . querySelectorAll ( 'details.sd-dropdown' ) ;
235+ largeSections . forEach ( ( section , index ) => {
236+ if ( index > 2 ) { // Keep first 3 open, collapse rest
237+ section . removeAttribute ( 'open' ) ;
238+ }
239+ } ) ;
240+ }
241+
242+ // Add expand/collapse all functionality
243+ const navSection = document . querySelector ( '.sidebar-navigation' ) || document . querySelector ( 'nav' ) ;
244+ if ( navSection ) {
245+ const expandAllBtn = document . createElement ( 'button' ) ;
246+ expandAllBtn . textContent = 'Expand All Sections' ;
247+ expandAllBtn . className = 'expand-all-btn' ;
248+ expandAllBtn . style . cssText = `
249+ margin: 10px 0;
250+ padding: 8px 16px;
251+ background: var(--primary-color, #0066cc);
252+ color: white;
253+ border: none;
254+ border-radius: 4px;
255+ cursor: pointer;
256+ font-size: 12px;
257+ width: 100%;
258+ ` ;
259+
260+ let allExpanded = false ;
261+ expandAllBtn . addEventListener ( 'click' , function ( ) {
262+ const dropdowns = document . querySelectorAll ( 'details.sd-dropdown' ) ;
263+
264+ if ( allExpanded ) {
265+ dropdowns . forEach ( dropdown => dropdown . removeAttribute ( 'open' ) ) ;
266+ expandAllBtn . textContent = 'Expand All Sections' ;
267+ allExpanded = false ;
268+ } else {
269+ dropdowns . forEach ( dropdown => dropdown . setAttribute ( 'open' , '' ) ) ;
270+ expandAllBtn . textContent = 'Collapse All Sections' ;
271+ allExpanded = true ;
272+ }
273+ } ) ;
274+
275+ navSection . appendChild ( expandAllBtn ) ;
276+ }
277+ }
278+
279+ /**
280+ * Smooth scrolling for internal links
281+ */
282+ function initializeSmoothScrolling ( ) {
283+ const internalLinks = document . querySelectorAll ( 'a[href^="#"]' ) ;
284+
285+ internalLinks . forEach ( link => {
286+ link . addEventListener ( 'click' , function ( e ) {
287+ const targetId = this . getAttribute ( 'href' ) . substring ( 1 ) ;
288+ const targetElement = document . getElementById ( targetId ) ;
289+
290+ if ( targetElement ) {
291+ e . preventDefault ( ) ;
292+
293+ targetElement . scrollIntoView ( {
294+ behavior : 'smooth' ,
295+ block : 'start'
296+ } ) ;
297+
298+ // Update URL without triggering scroll
299+ history . pushState ( null , null , `#${ targetId } ` ) ;
300+
301+ // Highlight the target element briefly
302+ targetElement . style . transition = 'background-color 0.3s ease' ;
303+ targetElement . style . backgroundColor = 'rgba(0, 102, 204, 0.1)' ;
304+
305+ setTimeout ( ( ) => {
306+ targetElement . style . backgroundColor = '' ;
307+ } , 1500 ) ;
308+ }
309+ } ) ;
310+ } ) ;
311+ }
312+
313+ /**
314+ * Theme toggle functionality (if supported)
315+ */
316+ function initializeThemeToggle ( ) {
317+ // Check if theme toggle is supported by the current theme
318+ const themeToggle = document . querySelector ( '.theme-toggle' ) ;
319+
320+ if ( themeToggle || window . matchMedia ) {
321+ // Add theme preference handling
322+ const prefersDark = window . matchMedia ( '(prefers-color-scheme: dark)' ) ;
323+
324+ if ( prefersDark . matches ) {
325+ document . body . classList . add ( 'dark-theme' ) ;
326+ }
327+
328+ // Listen for theme changes
329+ prefersDark . addListener ( ( e ) => {
330+ if ( e . matches ) {
331+ document . body . classList . add ( 'dark-theme' ) ;
332+ } else {
333+ document . body . classList . remove ( 'dark-theme' ) ;
334+ }
335+ } ) ;
336+ }
337+ }
338+
339+ /**
340+ * Lazy loading for images and heavy content
341+ */
342+ function initializeLazyLoading ( ) {
343+ if ( 'IntersectionObserver' in window ) {
344+ const imageObserver = new IntersectionObserver ( ( entries , observer ) => {
345+ entries . forEach ( entry => {
346+ if ( entry . isIntersecting ) {
347+ const img = entry . target ;
348+ if ( img . dataset . src ) {
349+ img . src = img . dataset . src ;
350+ img . classList . remove ( 'lazy' ) ;
351+ observer . unobserve ( img ) ;
352+ }
353+ }
354+ } ) ;
355+ } ) ;
356+
357+ const lazyImages = document . querySelectorAll ( 'img[data-src]' ) ;
358+ lazyImages . forEach ( img => imageObserver . observe ( img ) ) ;
359+ }
360+ }
361+
362+ /**
363+ * Performance monitoring
364+ */
365+ function initializePerformanceMonitoring ( ) {
366+ if ( 'PerformanceObserver' in window ) {
367+ const perfObserver = new PerformanceObserver ( ( list ) => {
368+ list . getEntries ( ) . forEach ( ( entry ) => {
369+ if ( entry . entryType === 'navigation' ) {
370+ console . log ( `Page load time: ${ entry . loadEventEnd - entry . loadEventStart } ms` ) ;
371+ }
372+ } ) ;
373+ } ) ;
374+
375+ perfObserver . observe ( { entryTypes : [ 'navigation' ] } ) ;
376+ }
377+ }
378+
379+ // Initialize additional features when the page is fully loaded
380+ window . addEventListener ( 'load' , function ( ) {
381+ initializeLazyLoading ( ) ;
382+ initializePerformanceMonitoring ( ) ;
383+ } ) ;
384+
385+ // Export functions for external use
386+ window . DocumentationEnhancements = {
387+ highlightSearchTerm,
388+ initializeCodeCopyButtons,
389+ initializeProgressiveDisclosure,
390+ initializeSmoothScrolling
391+ } ;
0 commit comments