@@ -4,6 +4,8 @@ const attrsToCopy = [
4
4
"data-githubContributorsUrl" ,
5
5
"data-githubContributorsFilename" ,
6
6
"data-pathToRoot" ,
7
+ "data-rawLocation" ,
8
+ "data-dynamicSideMenu" ,
7
9
]
8
10
9
11
/**
@@ -25,7 +27,7 @@ function savePageState(doc) {
25
27
}
26
28
return {
27
29
mainDiv : doc . querySelector ( "#main" ) ?. innerHTML ,
28
- leftColumn : doc . querySelector ( "#leftColumn" ) . innerHTML ,
30
+ leftColumn : dynamicSideMenu ? null : doc . querySelector ( "#leftColumn" ) . innerHTML ,
29
31
title : doc . title ,
30
32
attrs,
31
33
} ;
@@ -38,12 +40,15 @@ function savePageState(doc) {
38
40
function loadPageState ( doc , saved ) {
39
41
doc . title = saved . title ;
40
42
doc . querySelector ( "#main" ) . innerHTML = saved . mainDiv ;
41
- doc . querySelector ( "#leftColumn" ) . innerHTML = saved . leftColumn ;
43
+ if ( ! dynamicSideMenu )
44
+ doc . querySelector ( "#leftColumn" ) . innerHTML = saved . leftColumn ;
42
45
for ( const attr of attrsToCopy ) {
43
46
doc . documentElement . setAttribute ( attr , saved . attrs [ attr ] ) ;
44
47
}
45
48
}
46
49
50
+ const attachedElements = new WeakSet ( )
51
+
47
52
function attachAllListeners ( ) {
48
53
if ( observer ) {
49
54
observer . disconnect ( ) ;
@@ -97,19 +102,19 @@ function attachAllListeners() {
97
102
}
98
103
}
99
104
100
- document
101
- . querySelectorAll ( ".documentableElement .signature" )
102
- . forEach ( ( signature ) => {
103
- const short = signature . querySelector ( ".signature-short" ) ;
104
- const long = signature . querySelector ( ".signature-long" ) ;
105
- const extender = document . createElement ( "span" ) ;
106
- const extenderDots = document . createTextNode ( "..." ) ;
107
- extender . appendChild ( extenderDots ) ;
108
- extender . classList . add ( "extender" ) ;
109
- if ( short && long && signature . children [ 1 ] . hasChildNodes ( ) ) {
110
- signature . children [ 0 ] . append ( extender ) ;
111
- }
112
- } ) ;
105
+ document
106
+ . querySelectorAll ( ".documentableElement .signature" )
107
+ . forEach ( ( signature ) => {
108
+ const short = signature . querySelector ( ".signature-short" ) ;
109
+ const long = signature . querySelector ( ".signature-long" ) ;
110
+ const extender = document . createElement ( "span" ) ;
111
+ const extenderDots = document . createTextNode ( "..." ) ;
112
+ extender . appendChild ( extenderDots ) ;
113
+ extender . classList . add ( "extender" ) ;
114
+ if ( short && long && signature . children [ 1 ] . hasChildNodes ( ) ) {
115
+ signature . children [ 0 ] . append ( extender ) ;
116
+ }
117
+ } ) ;
113
118
114
119
const documentableLists = document . getElementsByClassName ( "documentableList" ) ;
115
120
[ ...documentableLists ] . forEach ( ( list ) => {
@@ -151,6 +156,8 @@ document
151
156
return ;
152
157
}
153
158
const url = new URL ( href ) ;
159
+ if ( attachedElements . has ( el ) ) return ;
160
+ attachedElements . add ( el ) ;
154
161
el . addEventListener ( "click" , ( e ) => {
155
162
if (
156
163
url . href . replace ( / # .* / , "" ) === window . location . href . replace ( / # .* / , "" )
@@ -166,6 +173,7 @@ document
166
173
e . preventDefault ( ) ;
167
174
e . stopPropagation ( ) ;
168
175
$ . get ( href , function ( data ) {
176
+ const oldLoc = getRawLoc ( ) ;
169
177
if ( window . history . state === null ) {
170
178
window . history . replaceState ( savePageState ( document ) , "" ) ;
171
179
}
@@ -174,6 +182,11 @@ document
174
182
const state = savePageState ( parsedDocument ) ;
175
183
window . history . pushState ( state , "" , href ) ;
176
184
loadPageState ( document , state ) ;
185
+ const newLoc = getRawLoc ( ) ;
186
+ if ( dynamicSideMenu ) {
187
+ updateMenu ( oldLoc , newLoc ) ;
188
+ }
189
+
177
190
window . dispatchEvent ( new Event ( DYNAMIC_PAGE_LOAD ) ) ;
178
191
document
179
192
. querySelector ( "#main" )
@@ -182,11 +195,15 @@ document
182
195
} ) ;
183
196
} ) ;
184
197
185
- $ ( ".ar" ) . on ( "click" , function ( e ) {
186
- $ ( this ) . parent ( ) . parent ( ) . toggleClass ( "expanded" ) ;
187
- $ ( this ) . toggleClass ( "expanded" ) ;
188
- e . stopPropagation ( ) ;
189
- } ) ;
198
+ document . querySelectorAll ( '.ar' ) . forEach ( ( el ) => {
199
+ if ( attachedElements . has ( el ) ) return ;
200
+ attachedElements . add ( el ) ;
201
+ el . addEventListener ( 'click' , ( e ) => {
202
+ e . stopPropagation ( ) ;
203
+ el . parentElement . parentElement . classList . toggle ( "expanded" ) ;
204
+ el . classList . toggle ( "expanded" ) ;
205
+ } )
206
+ } )
190
207
191
208
document . querySelectorAll ( ".documentableList .ar" ) . forEach ( ( arrow ) => {
192
209
arrow . addEventListener ( "click" , ( ) => {
@@ -195,7 +212,9 @@ document
195
212
} ) ;
196
213
} ) ;
197
214
198
- document . querySelectorAll ( ".nh" ) . forEach ( ( el ) =>
215
+ document . querySelectorAll ( ".nh" ) . forEach ( ( el ) => {
216
+ if ( attachedElements . has ( el ) ) return ;
217
+ attachedElements . add ( el ) ;
199
218
el . addEventListener ( "click" , ( ) => {
200
219
if (
201
220
el . lastChild . href . replace ( "#" , "" ) ===
@@ -206,8 +225,8 @@ document
206
225
} else {
207
226
el . lastChild . click ( ) ;
208
227
}
209
- } ) ,
210
- ) ;
228
+ } ) ;
229
+ } ) ;
211
230
212
231
const toggleShowAllElem = ( element ) => {
213
232
if ( element . textContent == "Show all" ) {
@@ -345,7 +364,7 @@ window.addEventListener(DYNAMIC_PAGE_LOAD, () => {
345
364
attachAllListeners ( ) ;
346
365
} ) ;
347
366
348
- window . addEventListener ( "dynamicPageLoad" , ( ) => {
367
+ window . addEventListener ( DYNAMIC_PAGE_LOAD , ( ) => {
349
368
const sideMenuOpen = sessionStorage . getItem ( "sideMenuOpen" ) ;
350
369
if ( sideMenuOpen ) {
351
370
if ( document . querySelector ( "#leftColumn" ) . classList . contains ( "show" ) ) {
@@ -365,10 +384,136 @@ window.addEventListener("dynamicPageLoad", () => {
365
384
}
366
385
} ) ;
367
386
387
+ let dynamicSideMenu = false ;
388
+ /** @param {Element } elem @param {boolean } hide */
389
+ function updatePath ( elem , hide , first = true ) {
390
+ if ( elem . classList . contains ( "side-menu" ) ) return ;
391
+ const span = elem . firstElementChild
392
+ const btn = span . firstElementChild
393
+ if ( hide ) {
394
+ elem . classList . remove ( "expanded" ) ;
395
+ span . classList . remove ( "h100" , "selected" , "expanded" , "cs" ) ;
396
+ if ( btn ) btn . classList . remove ( "expanded" ) ;
397
+ } else {
398
+ elem . classList . add ( "expanded" ) ;
399
+ span . classList . add ( "h100" , "expanded" , "cs" ) ;
400
+ if ( btn ) btn . classList . add ( "expanded" ) ;
401
+ if ( first ) span . classList . add ( "selected" ) ;
402
+ }
403
+ updatePath ( elem . parentElement , hide , false ) ;
404
+ }
405
+ let updateMenu = null ;
406
+ function getRawLoc ( ) {
407
+ return document . documentElement . getAttribute ( "data-rawLocation" ) ?. split ( "/" ) ?. filter ( c => c !== "" ) ;
408
+ }
409
+
410
+ /**
411
+ * @template {keyof HTMLElementTagNameMap} T
412
+ * @param {T } el type of element to create
413
+ * @param {{ cls?: string | null, id?: string | null, href?: string | null } } attrs element attributes
414
+ * @param {Array<HTMLElement | string | null> } chldr element children
415
+ * @returns {HTMLElementTagNameMap[T] }
416
+ */
417
+ function render ( el , { cls = null , id = null , href = null , loc = null } = { } , chldr = [ ] ) {
418
+ const r = document . createElement ( el ) ;
419
+ if ( cls ) cls . split ( " " ) . filter ( x => x !== "" ) . forEach ( c => r . classList . add ( c ) ) ;
420
+ if ( id ) r . id = id ;
421
+ if ( href ) r . href = href ;
422
+ if ( loc ) r . setAttribute ( "data-loc" , loc ) ;
423
+ chldr . filter ( c => c !== null ) . forEach ( c =>
424
+ r . appendChild ( typeof c === "string" ? document . createTextNode ( c ) : c )
425
+ ) ;
426
+ return r ;
427
+ }
428
+ function renderDynamicSideMenu ( ) {
429
+ const pathToRoot = document . documentElement . getAttribute ( "data-pathToRoot" )
430
+ const path = pathToRoot + "dynamicSideMenu.json" ;
431
+ const rawLocation = getRawLoc ( ) ;
432
+ const baseUrl = window . location . pathname . split ( "/" ) . slice ( 0 ,
433
+ - 1 - pathToRoot . split ( "/" ) . filter ( c => c != "" ) . length
434
+ ) ;
435
+ function linkTo ( loc ) {
436
+ return `${ baseUrl } /${ loc . join ( "/" ) } .html` ;
437
+ }
438
+ fetch ( path ) . then ( r => r . json ( ) ) . then ( menu => {
439
+ function renderNested ( item , nestLevel , prefix , isApi ) {
440
+ const name = item . name ;
441
+ const newName =
442
+ isApi && item . kind === "package" && name . startsWith ( prefix + "." )
443
+ ? name . substring ( prefix . length + 1 )
444
+ : name ;
445
+ const newPrefix =
446
+ prefix == ""
447
+ ? newName
448
+ : prefix + "." + newName ;
449
+ const chldr =
450
+ item . children . map ( x => renderNested ( x , nestLevel + 1 , newPrefix , isApi ) ) ;
451
+ const link = render ( "span" , { cls : `nh ${ isApi ? "" : "de" } ` } , [
452
+ chldr . length ? render ( "button" , { cls : "ar icon-button" } ) : null ,
453
+ render ( "a" , { href : linkTo ( item . location ) } , [
454
+ item . kind && render ( "span" , { cls : `micon ${ item . kind . slice ( 0 , 2 ) } ` } ) ,
455
+ render ( "span" , { } , [ newName ] ) ,
456
+ ] ) ,
457
+ ] ) ;
458
+ const loc = item . location . join ( "/" ) ;
459
+ const ret = render ( "div" , { cls : `ni n${ nestLevel } ` , loc : item . location . join ( "/" ) } , [ link , ...chldr ] ) ;
460
+ return ret ;
461
+ }
462
+ const d = render ( "div" , { cls : "switcher-container" } , [
463
+ menu . docs && render ( "a" , {
464
+ id : "docs-nav-button" ,
465
+ cls : "switcher h100" ,
466
+ href : linkTo ( menu . docs . location )
467
+ } , [ "Docs" ] ) ,
468
+ menu . api && render ( "a" , {
469
+ id : "api-nav-button" ,
470
+ cls : "switcher h100" ,
471
+ href : linkTo ( menu . api . location )
472
+ } , [ "API" ] ) ,
473
+ ] ) ;
474
+ const d1 = menu . docs && render ( "nav" , { cls : "side-menu" , id : "docs-nav" } ,
475
+ menu . docs . children . map ( item => renderNested ( item , 0 , "" , false ) )
476
+ ) ;
477
+ const d2 = menu . api && render ( "nav" , { cls : "side-menu" , id : "api-nav" } ,
478
+ menu . api . children . map ( item => renderNested ( item , 0 , "" , true ) )
479
+ ) ;
480
+
481
+ document . getElementById ( "leftColumn" ) . appendChild ( d ) ;
482
+ d1 && document . getElementById ( "leftColumn" ) . appendChild ( d1 ) ;
483
+ d2 && document . getElementById ( "leftColumn" ) . appendChild ( d2 ) ;
484
+ updateMenu = ( oldLoc , newLoc ) => {
485
+ if ( oldLoc ) {
486
+ const elem = document . querySelector ( `[data-loc="${ oldLoc . join ( "/" ) } "]` ) ;
487
+ if ( elem ) updatePath ( elem , true ) ;
488
+ }
489
+ if ( d1 && d2 ) {
490
+ if ( newLoc [ 0 ] && newLoc [ 0 ] == menu . api . location [ 0 ] ) {
491
+ d1 . hidden = true ;
492
+ d2 . hidden = false ;
493
+ } else {
494
+ d1 . hidden = false ;
495
+ d2 . hidden = true ;
496
+ }
497
+ }
498
+ const elem = document . querySelector ( `[data-loc="${ newLoc . join ( "/" ) } "]` ) ;
499
+ if ( elem ) updatePath ( elem , false )
500
+ }
501
+ updateMenu ( null , rawLocation ) ;
502
+
503
+ window . dispatchEvent ( new Event ( DYNAMIC_PAGE_LOAD ) ) ;
504
+ } )
505
+ }
506
+
368
507
window . addEventListener ( "DOMContentLoaded" , ( ) => {
369
508
hljs . registerLanguage ( "scala" , highlightDotty ) ;
370
509
hljs . registerAliases ( [ "dotty" , "scala3" ] , "scala" ) ;
371
- window . dispatchEvent ( new Event ( DYNAMIC_PAGE_LOAD ) ) ;
510
+
511
+ dynamicSideMenu = document . documentElement . getAttribute ( "data-dynamicSideMenu" ) === "true" ;
512
+ if ( dynamicSideMenu ) {
513
+ renderDynamicSideMenu ( ) ;
514
+ } else {
515
+ window . dispatchEvent ( new Event ( DYNAMIC_PAGE_LOAD ) ) ;
516
+ }
372
517
} ) ;
373
518
374
519
const elements = document . querySelectorAll ( ".documentableElement" ) ;
0 commit comments