1
+
2
+ const dropdown = document . querySelector ( '.version-picker .dropdown' ) ;
3
+ const dropdownMenu = dropdown . querySelector ( '.dropdown-menu' ) ;
4
+
5
+ fetchVersions ( dropdown , dropdownMenu ) . then ( ( ) => {
6
+ initializeVersionDropdown ( dropdown , dropdownMenu ) ;
7
+ } ) ;
8
+
9
+ /**
10
+ * Initialize the dropdown functionality for version selection.
11
+ *
12
+ * @param {Element } dropdown - The dropdown element.
13
+ * @param {Element } dropdownMenu - The dropdown menu element.
14
+ */
15
+ function initializeVersionDropdown ( dropdown , dropdownMenu ) {
16
+ // Toggle the dropdown menu on click
17
+ dropdown . addEventListener ( 'click' , function ( ) {
18
+ this . setAttribute ( 'tabindex' , 1 ) ;
19
+ this . classList . toggle ( 'active' ) ;
20
+ dropdownMenu . style . display = ( dropdownMenu . style . display === 'block' ) ? 'none' : 'block' ;
21
+ } ) ;
22
+
23
+ // Remove the 'active' class and hide the dropdown menu on focusout
24
+ dropdown . addEventListener ( 'focusout' , function ( ) {
25
+ this . classList . remove ( 'active' ) ;
26
+ dropdownMenu . style . display = 'none' ;
27
+ } ) ;
28
+
29
+ // Handle item selection within the dropdown menu
30
+ const dropdownMenuItems = dropdownMenu . querySelectorAll ( 'li' ) ;
31
+ dropdownMenuItems . forEach ( function ( item ) {
32
+ item . addEventListener ( 'click' , function ( ) {
33
+ dropdownMenuItems . forEach ( function ( item ) {
34
+ item . classList . remove ( 'active' ) ;
35
+ } ) ;
36
+ this . classList . add ( 'active' ) ;
37
+ dropdown . querySelector ( 'span' ) . textContent = this . textContent ;
38
+ dropdown . querySelector ( 'input' ) . value = this . getAttribute ( 'id' ) ;
39
+
40
+ window . location . href = changeVersion ( window . location . href , this . textContent ) ;
41
+ } ) ;
42
+ } ) ;
43
+ } ;
44
+
45
+ /**
46
+ * This function fetches the available versions from a GitHub repository
47
+ * and inserts them into the version picker.
48
+ *
49
+ * @param {Element } dropdown - The dropdown element.
50
+ * @param {Element } dropdownMenu - The dropdown menu element.
51
+ * @returns {Promise<Array<string>> } A promise that resolves with an array of available versions.
52
+ */
53
+ function fetchVersions ( dropdown , dropdownMenu ) {
54
+ return new Promise ( ( resolve , reject ) => {
55
+ window . addEventListener ( "load" , ( ) => {
56
+
57
+ fetch ( "https://api.github.com/repos/matrix-org/synapse/git/trees/gh-pages" , {
58
+ cache : "force-cache" ,
59
+ } ) . then ( res =>
60
+ res . json ( )
61
+ ) . then ( resObject => {
62
+ const excluded = [ 'dev-docs' , 'v1.91.0' , 'v1.80.0' , 'v1.69.0' ] ;
63
+ const tree = resObject . tree . filter ( item => item . type === "tree" && ! excluded . includes ( item . path ) ) ;
64
+ const versions = tree . map ( item => item . path ) . sort ( sortVersions ) ;
65
+
66
+ // Create a list of <li> items for versions
67
+ versions . forEach ( ( version ) => {
68
+ const li = document . createElement ( "li" ) ;
69
+ li . textContent = version ;
70
+ li . id = version ;
71
+
72
+ if ( window . SYNAPSE_VERSION === version ) {
73
+ li . classList . add ( 'active' ) ;
74
+ dropdown . querySelector ( 'span' ) . textContent = version ;
75
+ dropdown . querySelector ( 'input' ) . value = version ;
76
+ }
77
+
78
+ dropdownMenu . appendChild ( li ) ;
79
+ } ) ;
80
+
81
+ resolve ( versions ) ;
82
+
83
+ } ) . catch ( ex => {
84
+ console . error ( "Failed to fetch version data" , ex ) ;
85
+ reject ( ex ) ;
86
+ } )
87
+ } ) ;
88
+ } ) ;
89
+ }
90
+
91
+ /**
92
+ * Custom sorting function to sort an array of version strings.
93
+ *
94
+ * @param {string } a - The first version string to compare.
95
+ * @param {string } b - The second version string to compare.
96
+ * @returns {number } - A negative number if a should come before b, a positive number if b should come before a, or 0 if they are equal.
97
+ */
98
+ function sortVersions ( a , b ) {
99
+ // Put 'develop' and 'latest' at the top
100
+ if ( a === 'develop' || a === 'latest' ) return - 1 ;
101
+ if ( b === 'develop' || b === 'latest' ) return 1 ;
102
+
103
+ const versionA = ( a . match ( / v \d + ( \. \d + ) + / ) || [ ] ) [ 0 ] ;
104
+ const versionB = ( b . match ( / v \d + ( \. \d + ) + / ) || [ ] ) [ 0 ] ;
105
+
106
+ return versionB . localeCompare ( versionA ) ;
107
+ }
108
+
109
+ /**
110
+ * Change the version in a URL path.
111
+ *
112
+ * @param {string } url - The original URL to be modified.
113
+ * @param {string } newVersion - The new version to replace the existing version in the URL.
114
+ * @returns {string } The updated URL with the new version.
115
+ */
116
+ function changeVersion ( url , newVersion ) {
117
+ const parsedURL = new URL ( url ) ;
118
+ const pathSegments = parsedURL . pathname . split ( '/' ) ;
119
+
120
+ // Modify the version
121
+ pathSegments [ 2 ] = newVersion ;
122
+
123
+ // Reconstruct the URL
124
+ parsedURL . pathname = pathSegments . join ( '/' ) ;
125
+
126
+ return parsedURL . href ;
127
+ }
0 commit comments