diff --git a/data/Database.js b/data/Database.js index 49dcfcc1..5664562c 100644 --- a/data/Database.js +++ b/data/Database.js @@ -17,15 +17,19 @@ export default class Database { this.components = Array.from(this.index.values()) } - search(query) { + search(query, langQuery) { // NB: Be careful not to alter the original array here. return Array.from(this.components).filter(function({ definition, form, + language, PA, tokens, UR, }) { + + if (langQuery && langQuery !== `all` && langQuery !== language) return false + return definition?.toLowerCase().includes(query) || form?.toLowerCase().includes(query) || PA?.toLowerCase().includes(query) @@ -41,6 +45,7 @@ export default class Database { || PA?.toLowerCase().includes(query) || UR?.toLowerCase().includes(query) }) + }) } diff --git a/data/Languages.js b/data/Languages.js index c07462b8..a427a03f 100644 --- a/data/Languages.js +++ b/data/Languages.js @@ -39,8 +39,9 @@ export default class Languages extends Map { } - convertRecord({ key, name }) { - return { key, name } + convertRecord({ autonyms = ``, key, name }) { + autonyms = autonyms.split(/,\s*/gv).filter(Boolean) + return { autonyms, key, name } } async load() { diff --git a/data/json/languages.ndjson b/data/json/languages.ndjson index b41e1118..d71822af 100644 --- a/data/json/languages.ndjson +++ b/data/json/languages.ndjson @@ -1,41 +1,41 @@ -{"key":"Abenaki","name":"Abenaki"} -{"key":"Abenaki_Eastern","name":"Eastern Abenaki"} -{"key":"Abenaki_Western","name":"Western Abenaki"} -{"key":"Arapaho","name":"Arapaho"} -{"key":"Blackfoot","name":"Blackfoot"} -{"key":"Cheyenne","name":"Cheyenne"} -{"key":"Cree_Atikamekw","name":"Atikamekw"} -{"key":"Cree_East","name":"East Cree"} -{"key":"Cree_Innu","name":"Innu"} -{"key":"Cree_Moose_Swampy","name":"Moose-Swampy Cree"} -{"key":"Cree_Naskapi_Western","name":"Western Naskapi"} -{"key":"Cree_Northern_East","name":"Northern East Cree"} -{"key":"Cree_Plains","name":"Plains Cree"} -{"key":"Cree_Southern_East","name":"Southern East Cree"} -{"key":"Cree_Swampy","name":"Swampy Cree"} -{"key":"Delaware_Common","name":"Common Delaware"} -{"key":"Delaware_Munsee","name":"Munsee"} -{"key":"Delaware_Unami","name":"Unami"} -{"key":"Kickapoo","name":"Kickapoo"} -{"key":"Menominee","name":"Menominee"} -{"key":"Meskwaki","name":"Meskwaki"} -{"key":"Miami_Illinois","name":"Miami-Illinois"} -{"key":"Mikmaq","name":"Mi’kmaq"} -{"key":"Narragansett","name":"Narragansett"} -{"key":"Ojibwe_Algonquin","name":"Algonquin"} -{"key":"Ojibwe_Common","name":"Common Ojibwe"} -{"key":"Ojibwe_Eastern","name":"Eastern Ojibwe"} -{"key":"Ojibwe_Nipissing","name":"Nipissing"} -{"key":"Ojibwe_NW","name":"Northwestern Ojibwe"} -{"key":"Ojibwe_Odawa","name":"Odawa"} -{"key":"Ojibwe_Severn","name":"Severn Ojibwe"} -{"key":"Ojibwe_SW","name":"Southwestern Ojibwe"} -{"key":"Passamaquoddy_Maliseet","name":"Passamaquoddy-Maliseet"} -{"key":"Potawatomi","name":"Potawatomi"} -{"key":"Powhatan","name":"Powhatan"} -{"key":"Proto_Algonquian","name":"Proto-Algonquian"} -{"key":"Proto_Central_Algonquian","name":"Proto-Central Algonquian"} -{"key":"Proto_Central_Eastern_Algonquian","name":"Proto-Central Eastern Algonquian"} -{"key":"Proto_Eastern_Algonquian","name":"Proto-Eastern Algonquian"} -{"key":"Shawnee","name":"Shawnee"} -{"key":"Wampanoag","name":"Wampanoag"} +{"autonyms":[],"key":"Abenaki","name":"Abenaki"} +{"autonyms":["Alənαpαtəwéwαkan"],"key":"Abenaki_Eastern","name":"Eastern Abenaki"} +{"autonyms":["Alnôbaôdwawôgan"],"key":"Abenaki_Western","name":"Western Abenaki"} +{"autonyms":["Hinónoʼeitíít"],"key":"Arapaho","name":"Arapaho"} +{"autonyms":["Niitsi'powahsin"],"key":"Blackfoot","name":"Blackfoot"} +{"autonyms":["Tsėhesenėstsestotse"],"key":"Cheyenne","name":"Cheyenne"} +{"autonyms":["nehirâmowin"],"key":"Cree_Atikamekw","name":"Atikamekw"} +{"autonyms":[],"key":"Cree_East","name":"East Cree"} +{"autonyms":["Innu-Aimun"],"key":"Cree_Innu","name":"Innu"} +{"autonyms":[],"key":"Cree_Moose_Swampy","name":"Moose-Swampy Cree"} +{"autonyms":[],"key":"Cree_Naskapi_Western","name":"Western Naskapi"} +{"autonyms":["iyiyiu-Ayamiwin","ᐄᔨᔫ ᐊᔨᒨᓐ "],"key":"Cree_Northern_East","name":"Northern East Cree"} +{"autonyms":["nêhiyawêwin","ᓀᐦᐃᔭᐍᐏᐣ"],"key":"Cree_Plains","name":"Plains Cree"} +{"autonyms":["iyiniu-Ayamiwin ᐄᓅ ᐊᔨᒨᓐ"],"key":"Cree_Southern_East","name":"Southern East Cree"} +{"autonyms":["Nêhinawêwin","ᓀᐦᐃᓇᐍᐏᐣ "],"key":"Cree_Swampy","name":"Swampy Cree"} +{"autonyms":[],"key":"Delaware_Common","name":"Common Delaware"} +{"autonyms":["monsii"],"key":"Delaware_Munsee","name":"Munsee"} +{"autonyms":["ləná·p·e"],"key":"Delaware_Unami","name":"Unami"} +{"autonyms":[],"key":"Kickapoo","name":"Kickapoo"} +{"autonyms":["Oma͞eqnomenēweqnaesen","Mamāceqtaweqnaesen"],"key":"Menominee","name":"Menominee"} +{"autonyms":["Meshkwahkihaki"],"key":"Meskwaki","name":"Meskwaki"} +{"autonyms":["myaamia"],"key":"Miami_Illinois","name":"Miami-Illinois"} +{"autonyms":["Miꞌkmawiꞌsimk"],"key":"Mikmaq","name":"Mi’kmaq"} +{"autonyms":[],"key":"Narragansett","name":"Narragansett"} +{"autonyms":["Omàmìwininìmowin"],"key":"Ojibwe_Algonquin","name":"Algonquin"} +{"autonyms":["Anishinaabemowin","Ojibwemowin"],"key":"Ojibwe_Common","name":"Common Ojibwe"} +{"autonyms":["Nishnaabemwin"],"key":"Ojibwe_Eastern","name":"Eastern Ojibwe"} +{"autonyms":[],"key":"Ojibwe_Nipissing","name":"Nipissing"} +{"autonyms":[],"key":"Ojibwe_NW","name":"Northwestern Ojibwe"} +{"autonyms":["Nishnaabemwin"],"key":"Ojibwe_Odawa","name":"Odawa"} +{"autonyms":["Anishininiimowin"],"key":"Ojibwe_Severn","name":"Severn Ojibwe"} +{"autonyms":["Anishinaabemowin"],"key":"Ojibwe_SW","name":"Southwestern Ojibwe"} +{"autonyms":["skicinuwatuwewakon"],"key":"Passamaquoddy_Maliseet","name":"Passamaquoddy-Maliseet"} +{"autonyms":["Bodwéwadmimwen"],"key":"Potawatomi","name":"Potawatomi"} +{"autonyms":[],"key":"Powhatan","name":"Powhatan"} +{"autonyms":[],"key":"Proto_Algonquian","name":"Proto-Algonquian"} +{"autonyms":[],"key":"Proto_Central_Algonquian","name":"Proto-Central Algonquian"} +{"autonyms":[],"key":"Proto_Central_Eastern_Algonquian","name":"Proto-Central Eastern Algonquian"} +{"autonyms":[],"key":"Proto_Eastern_Algonquian","name":"Proto-Eastern Algonquian"} +{"autonyms":["Sawanwa","Savannah","Sewanee","Shawano"],"key":"Shawnee","name":"Shawnee"} +{"autonyms":["Wôpanâôt8âôk"],"key":"Wampanoag","name":"Wampanoag"} diff --git a/pages/Search/Search.css b/pages/Search/Search.css index 656c4b41..7470b3ac 100644 --- a/pages/Search/Search.css +++ b/pages/Search/Search.css @@ -12,6 +12,7 @@ @import './components/pagination/pagination.css'; /* Utility Classes */ + :root { --cell-border: 1px solid #CCC; } @@ -25,6 +26,7 @@ } /* Cell-Specific Styles */ + /* bottom border */ .bb { border-block-end: var(--cell-border); @@ -36,11 +38,13 @@ } /* Multi-Element Styles */ + :is(.form, .PA, .source-forms, .source-PA, .source-URs, .UR) { white-space: nowrap; } /* Element-Specific Styles */ + body { inline-size: fit-content !important; max-inline-size: none; @@ -84,6 +88,14 @@ h2 { position: sticky; } +#language-select { + all: revert; /* Use default browser styles. */ + font-family: 'Times New Roman', serif; /* This is necessary because the adjustments to ligatures don't apply within the + Searches forms (in any orthography) and definitions for a match anywhere within the string. Search is not case sensitive. Leave blank to display all components. + +
-
- Searches forms (in any orthography) and definitions for a match anywhere within the string. Search is not case sensitive. Leave blank and press "Search" to display all components. diff --git a/pages/Search/Search.js b/pages/Search/Search.js index a94cdda4..96f7b8d5 100644 --- a/pages/Search/Search.js +++ b/pages/Search/Search.js @@ -17,6 +17,7 @@ export function Search(req, res) { const { db } = req.app const context = { + languages: db.languages.toJSON().sort((a, b) => a.name.localeCompare(b.name)), numComponents: db.index.size.toLocaleString(), numLanguages: db.languages.size.toLocaleString(), pageCSS: res.app.locals.styles.Search, @@ -25,7 +26,7 @@ export function Search(req, res) { url: req.originalUrl, } - if (!(`q` in req.query)) { + if (!(`q` in req.query || `language` in req.query)) { return res.render(`Search/Search`, context) } @@ -42,14 +43,15 @@ export function Search(req, res) { q = q.trim().toLowerCase() - const allResults = req.app.db.search(q) + let results = req.app.db.search(q, req.query.language) + const numTotalResults = results.length // Sort sort = new SortDirectives(sort) if (sort.size) { - allResults.sort((a, b) => { + results.sort((a, b) => { const comparisons = Array.from(sort.entries()) .map(([field, { direction }]) => { @@ -64,14 +66,13 @@ export function Search(req, res) { // Pagination - limit = Number(limit) - offset = Number(offset) - - const results = allResults.slice(offset, offset + limit) + limit = Number(limit) + offset = Number(offset) + results = results.slice(offset, offset + limit) const numAdjacentPages = 5 - const lastPageOffset = Math.floor(allResults.length / limit) * limit - const nextPageOffset = Math.min(offset + limit, allResults.length) + const lastPageOffset = Math.floor(numTotalResults / limit) * limit + const nextPageOffset = Math.min(offset + limit, numTotalResults) const prevPageOffset = Math.max(offset - limit, 0) const url = new URL(req.originalUrl, `${ req.protocol }://${ req.host }`) @@ -99,7 +100,7 @@ export function Search(req, res) { let nextOffset = offset + limit - while (nextOffset <= allResults.length && nextPages.length <= numAdjacentPages) { + while (nextOffset <= numTotalResults && nextPages.length <= numAdjacentPages) { nextPages.push({ link: changeParam(url, `offset`, nextOffset), @@ -124,7 +125,7 @@ export function Search(req, res) { numResults: results.length.toLocaleString(), pagination: { currentPage: Math.floor(offset / limit) + 1, - endIndex: Math.min(offset + limit, allResults.length).toLocaleString(), + endIndex: Math.min(offset + limit, numTotalResults).toLocaleString(), links: { firstPage: changeParam(url, `offset`, 0), lastPage: changeParam(url, `offset`, lastPageOffset), @@ -133,12 +134,12 @@ export function Search(req, res) { }, nextPages, prevPages, - show: allResults.length > limit, + show: numTotalResults > limit, startIndex: (offset + 1).toLocaleString(), }, results, sort: Object.fromEntries(sort), - totalResults: allResults.length.toLocaleString(), + totalResults: numTotalResults.toLocaleString(), }) res.render(`Search/Search`, context) diff --git a/pages/Search/Search.test.js b/pages/Search/Search.test.js index 29155e85..9842715d 100644 --- a/pages/Search/Search.test.js +++ b/pages/Search/Search.test.js @@ -100,6 +100,25 @@ describe(`Search`, function() { }) + describe(`Language Filter`, function() { + + it(`language filter only`, function() { + cy.visit(`/search`) + cy.get(`#language-select`).select(`Cree_East`) + cy.get(`form`).submit() + cy.get(`#results tbody tr`).should(`have.length`, 6) + }) + + it(`language filter + search query`, function() { + cy.visit(`/search`) + cy.get(`#search-box`).type(`yi`) + cy.get(`#language-select`).select(`Cree_East`) + cy.get(`form`).submit() + cy.get(`#results tbody tr`).should(`have.length`, 1) + }) + + }) + describe(`Pagination`, function() { it(`defaults`, function() { diff --git a/pages/Search/client.js b/pages/Search/client.js index a000f96d..8ca33236 100644 --- a/pages/Search/client.js +++ b/pages/Search/client.js @@ -1,8 +1,8 @@ /* global document */ -import Copier from '../../scripts/Copier.js' -import SearchBox from './scripts/SearchBox.js' -import Table from './scripts/Table.js' +import Copier from '../../scripts/Copier.js' +import SearchForm from './scripts/SearchForm.js' +import Table from './scripts/Table.js' // Initialize button to copy citation information @@ -16,7 +16,7 @@ if (button && el) { // Initialize search box -const search = new SearchBox +const search = new SearchForm search.initialize() diff --git a/pages/Search/scripts/SearchBox.js b/pages/Search/scripts/SearchBox.js deleted file mode 100644 index e945c5db..00000000 --- a/pages/Search/scripts/SearchBox.js +++ /dev/null @@ -1,19 +0,0 @@ -/* global document, window */ - -export default class SearchBox { - - initialize() { - - this.el = document.getElementById(`search-box`) - - const query = new URL(window.location.href).searchParams.get(`q`) - - if (query) { - this.el.value = query - } - - this.el.focus() - - } - -} diff --git a/pages/Search/scripts/SearchForm.js b/pages/Search/scripts/SearchForm.js new file mode 100644 index 00000000..e7968875 --- /dev/null +++ b/pages/Search/scripts/SearchForm.js @@ -0,0 +1,22 @@ +/* global document, window */ + +export default class SearchForm { + + initialize() { + + this.search = document.getElementById(`search-box`) + this.language = document.getElementById(`language-select`) + + const url = new URL(window.location.href) + + const query = url.searchParams.get(`q`) + const language = url.searchParams.get(`language`) + + if (query) this.search.value = query + if (language) this.language.value = language + + this.search.focus() + + } + +}