diff --git a/index.html b/index.html index c819519..f765bb3 100644 --- a/index.html +++ b/index.html @@ -35,6 +35,9 @@ + diff --git a/src/helpers.js b/src/helpers.js index 13c5889..e1a13be 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -32,3 +32,21 @@ export function removeTags(html) { export function slugify(string) { return removeDiacritics(string).replace(/[!a-zA-Z0-9-_]/g, '-'); } + +export function getIndicesOf(searchStr, findIn, caseSensitive) { + // https://stackoverflow.com/a/3410557 + const searchStrLen = searchStr.length; + if (searchStrLen == 0) { + return []; + } + let startIndex = 0, index, indices = []; + if (!caseSensitive) { + findIn = findIn.toLowerCase(); + searchStr = searchStr.toLowerCase(); + } + while ((index = findIn.indexOf(searchStr, startIndex)) > -1) { + indices.push(index); + startIndex = index + searchStrLen; + } + return indices; +} diff --git a/src/js/search.js b/src/js/search.js index 9e024a7..261623c 100644 --- a/src/js/search.js +++ b/src/js/search.js @@ -1,4 +1,5 @@ -import { cloneObject } from "../helpers"; +import { cloneObject, getIndicesOf } from "../helpers"; +import removeDiacritics from "./StackOverflow/removeDiacritics"; export function getSearchTerm() { return document.getElementById('searchBox').value; @@ -7,6 +8,7 @@ export function getSearchTerm() { export function getSearchFilters() { const filters = { caseSensitive: document.getElementById('searchCaseSensitive').checked, + ignoreDiacritics: document.getElementById('searchIgnoreDiacritics').checked, exact: document.getElementById('searchExactWords').checked, name: document.getElementById('searchIncludeName').checked, definition: document.getElementById('searchIncludeDefinition').checked, @@ -39,17 +41,19 @@ export function getMatchingSearchWords() { } return true; }).filter(word => { + searchTerm = filters.ignoreDiacritics ? removeDiacritics(searchTerm) : searchTerm; searchTerm = filters.caseSensitive ? searchTerm : searchTerm.toLowerCase(); - const name = filters.caseSensitive ? word.name : word.name.toLowerCase(); - const simpleDefinition = filters.caseSensitive ? word.simpleDefinition : word.simpleDefinition.toLowerCase(); - const longDefinition = filters.caseSensitive ? word.longDefinition : word.longDefinition.toLowerCase(); + let name = filters.ignoreDiacritics ? removeDiacritics(word.name) : word.name; + name = filters.caseSensitive ? name : name.toLowerCase(); + let simpleDefinition = filters.ignoreDiacritics ? removeDiacritics(word.simpleDefinition) : word.simpleDefinition; + simpleDefinition = filters.caseSensitive ? simpleDefinition : simpleDefinition.toLowerCase(); + let longDefinition = filters.ignoreDiacritics ? removeDiacritics(word.longDefinition) : word.longDefinition; + longDefinition = filters.caseSensitive ? longDefinition : longDefinition.toLowerCase(); - const isInName = filters.name - && (filters.exact + const isInName = filters.name && (filters.exact ? searchTerm == name : new RegExp(searchTerm, 'g').test(name)); - const isInDefinition = filters.definition - && (filters.exact + const isInDefinition = filters.definition && (filters.exact ? searchTerm == simpleDefinition : new RegExp(searchTerm, 'g').test(simpleDefinition)); const isInDetails = filters.details && new RegExp(searchTerm, 'g').test(longDefinition); @@ -62,14 +66,52 @@ export function getMatchingSearchWords() { } export function highlightSearchTerm(word) { - const searchTerm = getSearchTerm(); + let searchTerm = getSearchTerm(); if (searchTerm) { const filters = getSearchFilters(); - const regexMethod = 'g' + (filters.caseSensitive ? '' : 'i'); const markedUpWord = cloneObject(word); - markedUpWord.name = markedUpWord.name.replace(new RegExp(`(${searchTerm})`, regexMethod), `$1`); - markedUpWord.simpleDefinition = markedUpWord.simpleDefinition.replace(new RegExp(`(${searchTerm})`, regexMethod), `$1`); - markedUpWord.longDefinition = markedUpWord.longDefinition.replace(new RegExp(`(${searchTerm})`, regexMethod), `$1`); + if (filters.ignoreDiacritics) { + const searchTermLength = searchTerm.length; + searchTerm = removeDiacritics(searchTerm); + if (filters.name) { + const nameMatches = getIndicesOf(searchTerm, removeDiacritics(markedUpWord.name), filters.caseSensitive); + nameMatches.forEach((wordIndex, i) => { + wordIndex += ''.length * i; + markedUpWord.name = markedUpWord.name.substring(0, wordIndex) + + '' + markedUpWord.name.substr(wordIndex, searchTermLength) + '' + + markedUpWord.name.substr(wordIndex + searchTermLength); + }); + } + if (filters.definition) { + const simpleDefinitionMatches = getIndicesOf(searchTerm, removeDiacritics(markedUpWord.simpleDefinition), filters.caseSensitive); + simpleDefinitionMatches.forEach((wordIndex, i) => { + wordIndex += ''.length * i; + markedUpWord.simpleDefinition = markedUpWord.simpleDefinition.substring(0, wordIndex) + + '' + markedUpWord.simpleDefinition.substr(wordIndex, searchTermLength) + '' + + markedUpWord.simpleDefinition.substr(wordIndex + searchTermLength); + }); + } + if (filters.details) { + const longDefinitionMatches = getIndicesOf(searchTerm, removeDiacritics(markedUpWord.longDefinition), filters.caseSensitive); + longDefinitionMatches.forEach((wordIndex, i) => { + wordIndex += ''.length * i; + markedUpWord.longDefinition = markedUpWord.longDefinition.substring(0, wordIndex) + + '' + markedUpWord.longDefinition.substr(wordIndex, searchTermLength) + '' + + markedUpWord.longDefinition.substr(wordIndex + searchTermLength); + }); + } + } else { + const regexMethod = 'g' + (filters.caseSensitive ? '' : 'i'); + if (filters.name) { + markedUpWord.name = markedUpWord.name.replace(new RegExp(`(${searchTerm})`, regexMethod), `$1`); + } + if (filters.definition) { + markedUpWord.simpleDefinition = markedUpWord.simpleDefinition.replace(new RegExp(`(${searchTerm})`, regexMethod), `$1`); + } + if (filters.details) { + markedUpWord.longDefinition = markedUpWord.longDefinition.replace(new RegExp(`(${searchTerm})`, regexMethod), `$1`); + } + } return markedUpWord; } return word; diff --git a/src/js/setupListeners.js b/src/js/setupListeners.js index fcdf5c4..c976f79 100644 --- a/src/js/setupListeners.js +++ b/src/js/setupListeners.js @@ -84,6 +84,7 @@ function setupSearchBar() { const searchBox = document.getElementById('searchBox'), clearSearchButton = document.getElementById('clearSearchButton'), openSearchModal = document.getElementById('openSearchModal'), + searchIgnoreDiacritics = document.getElementById('searchIgnoreDiacritics'), searchExactWords = document.getElementById('searchExactWords'), searchIncludeDetails = document.getElementById('searchIncludeDetails'); searchBox.addEventListener('change', () => { @@ -102,7 +103,7 @@ function setupSearchBar() { searchBox.focus(); }); - searchExactWords.addEventListener('change', () => { + const toggleDetailsCheck = function() { if (searchExactWords.checked) { searchIncludeDetails.checked = false; searchIncludeDetails.disabled = true; @@ -110,7 +111,19 @@ function setupSearchBar() { searchIncludeDetails.disabled = false; searchIncludeDetails.checked = true; } + } + + searchIgnoreDiacritics.addEventListener('change', () => { + if (searchIgnoreDiacritics.checked) { + searchExactWords.checked = false; + searchExactWords.disabled = true; + } else { + searchExactWords.disabled = false; + } + toggleDetailsCheck(); }); + + searchExactWords.addEventListener('change', () => toggleDetailsCheck()); } export function setupSearchFilters() {