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)); })();