diff options
Diffstat (limited to 'static/script.js')
-rw-r--r-- | static/script.js | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000..35aa43e --- /dev/null +++ b/static/script.js @@ -0,0 +1,110 @@ +const tbody = document.getElementById('tbody'); +const statusSpan = document.getElementById('status'); + +function newEl(tagname, content) { + const cell = document.createElement(tagname); + if (content instanceof Node) + cell.appendChild(content); + else + cell.textContent = content; + return cell; +} + +const updateUrl = debounce(params => { + if (Object.keys(params).length == 0) + window.history.pushState({}, '', '/'); + else + window.history.pushState({}, '', '?' + new URLSearchParams(params).toString()); +}, 500); + +function normalize(str) { + return str.toLowerCase().replaceAll('_', ' ') +} + +function escapeForRegex(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +} + +function display(proposals) { + const params = {}; + if (nameInput.value) { + const regex = new RegExp('\\b' + escapeForRegex(normalize(nameInput.value))); + proposals = proposals.filter(p => regex.test(normalize(p.name || p.page_title))); + params.q = nameInput.value; + } + + if (authorsInput.value) { + // The first letter of MediaWiki usernames is case-insensitive. + const firstChar = authorsInput.value.charAt(0); + const rest = authorsInput.value.slice(1); + const lowercase = firstChar.toLowerCase() + rest; + const uppercase = firstChar.toUpperCase() + rest; + proposals = proposals.filter(p => p.authors && (p.authors.includes(lowercase) || p.authors.includes(uppercase))); + params.author = authorsInput.value; + } + + const nf = Intl.NumberFormat(); + statusSpan.textContent = `Found ${nf.format(proposals.length)} proposals.`; + + updateUrl(params); + + tbody.innerHTML = ''; + proposals.forEach(proposal => { + const row = document.createElement('tr'); + + const statusCell = newEl('td', proposal.status); + statusCell.className = 'status-' + proposal.status; + row.appendChild(statusCell); + + let date = '???'; + if (proposal.status == 'voting' || proposal.status == 'approved' || proposal.status == 'rejected') { + date = proposal.vote_start; + } else if (proposal.status == 'proposed') { + date = proposal.rfc_start; + } else { + date = proposal.draft_start; + } + row.appendChild(newEl('td', date)); + + const link = newEl('a', proposal.name || proposal.page_title); + link.href = 'https://wiki.openstreetmap.org/wiki/' + proposal.page_title.replaceAll(' ', '_'); + + const nameCell = newEl('td', link); + if (proposal.lang) + nameCell.appendChild(document.createTextNode(` (${proposal.lang})`)); + row.appendChild(nameCell); + row.appendChild(newEl('td', proposal.authors)); + + tbody.appendChild(row); + }); +} + +const nameInput = document.getElementById('name'); +const authorsInput = document.getElementById('authors'); + +function debounce(callback, wait) { + let timeoutId = null; + return (...args) => { + window.clearTimeout(timeoutId); + timeoutId = window.setTimeout(() => { + callback.apply(null, args); + }, wait); + }; +} + +(async function() { + const proposals = await (await fetch('proposals.json')).json(); + + const params = new URLSearchParams(location.search); + nameInput.value = params.get('q'); + authorsInput.value = params.get('author'); + + display(proposals); + + nameInput.addEventListener('input', debounce(e => { + display(proposals); + }, 100)); + authorsInput.addEventListener('input', debounce(e => { + display(proposals); + }, 100)); +})(); |