$(document).ready(() => { let selectedLang = null; let schema = null; let langs = null; $.ajax({ url: `/langs?installed`, success: data => { langs = data; const selectedLangCode = localStorage.selectedLangCode; let options = ''; langs.forEach(lang => { if(selectedLangCode && lang.code == selectedLangCode) { options += ``; } else { options += ``; } }); $('#langs').html(options); setLang($('#langs').val()); } }); $('#langs').on('change', e => { setLang(e.target.value); }); function setLang(code) { const lang = langs.find(lang => lang.code == code); localStorage.selectedLangCode = code; selectedLang = lang; $.ajax({ url: `/static/schemas/${lang.name}.json`, success: data => { polishSchemas = data if(window.location.hash) { getWord(); } } }); } const searchBar = $('#search-bar'); const searchForm = $('#search-form'); const ajaxContent = $('#ajax-content'); window.onhashchange = () => { getWord(); }; searchBar.autocomplete({ appendTo: '#search-form', source: (request, response) => { $.ajax({ url: `/langs/${selectedLang.code}/words?like=${request.term.trim()}&limit=20&offset=0`, success: data => response(data) }) }, select: (_, ui) => window.location.hash = ui.item.value }); searchBar.on('focus', e => { setTimeout(() => e.currentTarget.select(), 100); }); searchForm.on('submit', e => { e.preventDefault(); const word = e.target[0].value.trim(); window.location.hash = `#${word}`; }); function getWord() { const word = window.location.hash.replace('#', ''); if (word) { const decodedWord = decodeURIComponent(word); document.title = `Inflective - ${decodedWord}`; $.ajax({ url: `/langs/${selectedLang.code}/words/${word}`, success: (data) => { ajaxContent.html(generateHtml(word, data)); }, error: err => console.error(err) }) window.scrollTo(0, 0); searchBar.val(decodedWord); searchBar.select(); searchBar.autocomplete('close'); // Sometimes autocomplete opens after close was called // A better fix should be made setTimeout(() => searchBar.autocomplete('close'), 1000); } else { ajaxContent.html(''); } } function getCells(forms, tags) { if(tags.length === 0) return undefined; let cells = forms.filter(form => tags.every(value => form.tags.includes(value)) ); cells.forEach(cell => cell.used = true ); if(cells.length === 0) return undefined; return cells; } function generateList(data) { let html = ''; return html; } function generateTable(schemas, pos, forms) { let schema = schemas.find(schema => schema.pos.includes(pos)); // No schema was provided by the server - fallback to a list if(!schema) return generateList(forms); let html = '
'; html += ''; schema.rows.forEach(row => { html += ''; row.forEach(cell => { if('display' in cell) { html += ``; } else { let cells = getCells(forms, cell.tags); let content = cells ? cells.map(cell => cell.form).join(',
') : '-'; html += ``; } }); html += ''; }); html += '
${cell.display}${content}
'; html += '
'; let unusedCells = forms.filter(cell => !cell.used); if(schema.ignoreUnused) { unusedCells = unusedCells.filter(cell => !schema.ignoreUnused.map(tags => tags.every(tag => cell.tags.includes(tag))) ); } if(unusedCells.length > 0) { html += '

Other

'; html += generateList(unusedCells); } return html; } function generateHtml(word, data) { let html = ''; if(data.length === 0) { html += `

Not found: ${decodeURIComponent(word)}

`; } else { data.forEach(entry => { html += `

${entry.word} (${entry.pos})

` if('sounds' in entry) { html += `

${entry.sounds.map(sound => sound.ipa).filter(sound => sound && sound.match(/\/.*\//g)).join(', ')}

`; } if('senses' in entry) { let tags = []; entry.senses.forEach(sense => { if('tags' in sense) { tags.push(...sense.tags); } }); if(tags.length > 0) { tags = [...new Set(tags)]; html += '
Tags: ' html += tags.map(tag => `${tag}`).join(', ') html += '
' } html += '

Senses

'; html += '
    '; entry.senses.forEach(sense => { html += '
  1. ' if('form_of' in sense) { let word = sense.form_of[0].word; html += sense.glosses[0].replace(new RegExp(`of ${word}.?$`), ''); html += ` of ${word}`; } else { let link = ' of $1'; html += sense.glosses[0].replace(/of\s+([\u00BF-\u1FFF\u2C00-\uD7FF\w]+)\s*$/, link); } if('tags' in sense) { html += ' - ' html += sense.tags.map(tag => `${tag}`).join(', ') } html += '
  2. '; }) html += '
'; } if('forms' in entry) { if(entry.pos === 'verb') { let conjugation = entry.forms.filter(form => 'source' in form && form.source === 'conjugation'); if(conjugation.length > 0) { html += '

Conjugation

'; html += generateTable(polishSchemas, entry.pos, conjugation); } } else { let declension = entry.forms.filter(form => 'source' in form && form.source === 'declension'); if(declension.length > 0) { html += '

Declension

'; html += generateTable(polishSchemas, entry.pos, declension); } } } }); } return html; } });