summary refs log tree commit diff
path: root/develop/docs/website_files/version-picker.js
blob: 3174b5d0bca841ad954184ed6a13ce24ca8f60f1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
const dropdown = document.querySelector('.version-picker .dropdown');
const dropdownMenu = dropdown.querySelector('.dropdown-menu');

fetchVersions(dropdown, dropdownMenu).then(() => {
    initializeVersionDropdown(dropdown, dropdownMenu);
});

/**
 * Initialize the dropdown functionality for version selection.
 * 
 * @param {Element} dropdown - The dropdown element.
 * @param {Element} dropdownMenu - The dropdown menu element.
 */
function initializeVersionDropdown(dropdown, dropdownMenu) {
    // Toggle the dropdown menu on click
    dropdown.addEventListener('click', function () {
        this.setAttribute('tabindex', 1);
        this.classList.toggle('active');
        dropdownMenu.style.display = (dropdownMenu.style.display === 'block') ? 'none' : 'block';
    });
  
    // Remove the 'active' class and hide the dropdown menu on focusout
    dropdown.addEventListener('focusout', function () {
        this.classList.remove('active');
        dropdownMenu.style.display = 'none';
    });
  
    // Handle item selection within the dropdown menu
    const dropdownMenuItems = dropdownMenu.querySelectorAll('li');    
    dropdownMenuItems.forEach(function (item) {
        item.addEventListener('click', function () {
            dropdownMenuItems.forEach(function (item) {
                item.classList.remove('active');
            });
            this.classList.add('active');
            dropdown.querySelector('span').textContent = this.textContent;
            dropdown.querySelector('input').value = this.getAttribute('id');

            window.location.href = changeVersion(window.location.href, this.textContent);
        });
    });
};

/**
 * This function fetches the available versions from a GitHub repository
 * and inserts them into the version picker.
 * 
 * @param {Element} dropdown - The dropdown element.
 * @param {Element} dropdownMenu - The dropdown menu element.
 * @returns {Promise<Array<string>>} A promise that resolves with an array of available versions.
 */
function fetchVersions(dropdown, dropdownMenu) {
    return new Promise((resolve, reject) => {
        window.addEventListener("load", () => {

            fetch("https://api.github.com/repos/element-hq/synapse/git/trees/gh-pages", {
                cache: "force-cache",
            }).then(res => 
                res.json()
            ).then(resObject => {
                const excluded = ['dev-docs', 'v1.91.0', 'v1.80.0', 'v1.69.0'];
                const tree = resObject.tree.filter(item => item.type === "tree" && !excluded.includes(item.path));
                const versions = tree.map(item => item.path).sort(sortVersions);

                // Create a list of <li> items for versions
                versions.forEach((version) => {
                    const li = document.createElement("li");
                    li.textContent = version;
                    li.id = version;
    
                    if (window.SYNAPSE_VERSION === version) {
                        li.classList.add('active');
                        dropdown.querySelector('span').textContent = version;
                        dropdown.querySelector('input').value = version;
                    }
    
                    dropdownMenu.appendChild(li);
                });

                resolve(versions);

            }).catch(ex => {
                console.error("Failed to fetch version data", ex);
                reject(ex);
            })
        });
    });
}

/**
 * Custom sorting function to sort an array of version strings.
 *
 * @param {string} a - The first version string to compare.
 * @param {string} b - The second version string to compare.
 * @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.
 */
function sortVersions(a, b) {
    // Put 'develop' and 'latest' at the top
    if (a === 'develop' || a === 'latest') return -1;
    if (b === 'develop' || b === 'latest') return 1;

    // If any of the versions do not confrom to a semantic version string, they
    // will be sorted behind a valid version.
    const versionA = (a.match(/v(\d+(\.\d+)+)/) || [])[1]?.split('.') ?? '';
    const versionB = (b.match(/v(\d+(\.\d+)+)/) || [])[1]?.split('.') ?? '';

    for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) {
        if (versionB[i] === undefined) {
            return -1;
        }
        if (versionA[i] === undefined) {
            return 1;
        }

        const partA = parseInt(versionA[i], 10);
        const partB = parseInt(versionB[i], 10);

        if (partA > partB) {
            return -1;
        } else if (partB > partA) {
            return 1;
        }
    }

    return 0;
}

/**
 * Change the version in a URL path.
 *
 * @param {string} url - The original URL to be modified.
 * @param {string} newVersion - The new version to replace the existing version in the URL.
 * @returns {string} The updated URL with the new version.
 */
function changeVersion(url, newVersion) {
    const parsedURL = new URL(url);
    const pathSegments = parsedURL.pathname.split('/');
  
    // Modify the version
    pathSegments[2] = newVersion;

    // Reconstruct the URL
    parsedURL.pathname = pathSegments.join('/');
  
    return parsedURL.href;
}