dynamicbible/app/db/src/app/services/app.service.ts

1115 lines
29 KiB
TypeScript

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import {
AppState,
SavedPage,
HashTable,
Paragraph,
BiblePassage,
BibleVerse,
Error,
BibleParagraph,
BibleParagraphPassage,
CardItem,
DictionaryType,
StrongsDefinition,
StrongsCrossReference,
RMACCrossReference,
RMACDefinition,
WordLookupResult,
WordToStem,
IndexResult,
} from '../models/app-state';
import { Section, BibleReference } from '../common/bible-reference';
import { PageTitles } from '../constants';
import { createStateService } from '../common/state-service';
import * as math from 'mathjs';
const initialState: AppState = {
cards: [],
autocomplete: [],
savedPages: [],
mainPages: [
{ title: PageTitles.Search, icon: 'search' },
{ title: PageTitles.Settings, icon: 'settings' },
{ title: PageTitles.Help, icon: 'help' },
],
error: null,
paragraphs: null,
displaySettings: {
showStrongsAsModal: false,
appendCardToBottom: true,
insertCardNextToItem: true,
fontSize: 12,
fontFamily: 'PT Serif',
showVersesOnNewLine: false,
showVerseNumbers: false,
showParagraphs: true,
showParagraphHeadings: true,
},
};
type AppAction =
| {
type: 'UPDATE_PAGES';
pages: SavedPage[];
}
| {
type: 'ADD_CARD';
card: CardItem;
nextToItem: CardItem;
}
| {
type: 'UPDATE_CARD';
newCard: CardItem;
oldCard: CardItem;
}
| {
type: 'REMOVE_CARD';
card: CardItem;
}
| {
type: 'UPDATE_ERROR';
error: Error;
}
| {
type: 'UPDATE_PARAGRAPHS';
paragraphs: HashTable<Paragraph>;
}
| {
type: 'UPDATE_FONT_SIZE';
size: number;
}
| {
type: 'UPDATE_FONT_FAMILY';
family: string;
}
| {
type: 'UPDATE_AUTOCOMPLETE';
words: string[];
};
function reducer(state: AppState, action: AppAction): AppState {
// somtimes the state is null. lets not complain if that happens.
if (state === undefined) {
state = initialState;
}
switch (action.type) {
case 'UPDATE_AUTOCOMPLETE': {
return {
...state,
autocomplete: [...action.words],
};
}
case 'UPDATE_PAGES': {
return {
...state,
savedPages: [...action.pages],
};
}
case 'ADD_CARD': {
let cards = [];
if (action.nextToItem && state.displaySettings.insertCardNextToItem) {
const idx = state.cards.indexOf(action.nextToItem);
if (state.displaySettings.appendCardToBottom) {
const before = state.cards.slice(0, idx + 1);
const after = state.cards.slice(idx + 1);
cards = [...before, action.card, ...after];
} else {
const before = state.cards.slice(0, idx);
const after = state.cards.slice(idx);
cards = [...before, action.card, ...after];
}
} else {
if (state.displaySettings.appendCardToBottom) {
cards = [...state.cards, action.card];
} else {
cards = [action.card, ...state.cards];
}
}
return {
...state,
cards,
};
}
case 'UPDATE_CARD': {
return {
...state,
cards: state.cards.map((c) => {
if (c === action.oldCard) {
return action.newCard;
}
return c;
}),
};
}
case 'REMOVE_CARD': {
return {
...state,
cards: [...state.cards.filter((c) => c !== action.card)],
};
}
case 'UPDATE_PARAGRAPHS': {
return {
...state,
paragraphs: action.paragraphs,
};
}
case 'UPDATE_FONT_SIZE': {
return {
...state,
displaySettings: {
...state.displaySettings,
fontSize: action.size,
},
};
}
case 'UPDATE_FONT_FAMILY': {
return {
...state,
displaySettings: {
...state.displaySettings,
fontFamily: action.family,
},
};
}
}
}
@Injectable({
providedIn: 'root',
})
export class AppService extends createStateService(reducer, initialState) {
private wordToStem: Map<string, string>;
private searchIndexArray: string[];
private autocomplete: string[];
constructor(private http: HttpClient) {
super();
this.searchIndexArray = this.buildIndexArray().sort();
}
async getSavedPages() {
this.dispatch({
type: 'UPDATE_PAGES',
pages: [
{
queries: [],
title: 'My Page',
},
],
});
}
removeCard(card: CardItem) {
this.dispatch({
type: 'REMOVE_CARD',
card,
});
}
private dispatchError(msg: string) {
this.dispatch({
type: 'UPDATE_ERROR',
error: {
msg,
},
});
console.log(msg);
}
//#region Strongs
async getStrongs(
strongsNumber: string,
dict: DictionaryType,
nextToItem: CardItem = null
) {
const result = await this.getStrongsFromApi(strongsNumber, dict);
const d = dict === 'grk' ? 'G' : 'H';
const card = {
qry: `${d}${strongsNumber}`,
dict: d,
type: 'Strongs',
data: result,
};
this.dispatch({
type: 'ADD_CARD',
card,
nextToItem,
});
}
private async getStrongsFromApi(strongsNumber: string, dict: string) {
const sn = parseInt(strongsNumber, 10);
const result = {
prefix: '',
sn,
def: null,
rmac: null,
crossrefs: null,
rmaccode: '',
};
if (dict === 'grk') {
result.prefix = 'G';
if (sn > 5624 || sn < 1) {
this.dispatchError(
`Strong's Number G${sn} is out of range. Strong's numbers range from 1 - 5624 in the New Testament.`
);
return;
}
} else {
result.prefix = 'H';
if (sn > 8674 || sn < 1) {
this.dispatchError(
`Strong's Number H${sn} is out of range. Strong's numbers range from 1 - 8674 in the Old Testament.`
);
return;
}
}
let url = `${dict}${Math.ceil(sn / 100)}.json`;
try {
const d = await this.http
.get<StrongsDefinition[]>('assets/data/strongs/' + url)
.toPromise();
result.def = d.find((el) => el.i === result.prefix + result.sn);
} catch (err) {
this.dispatchError(
`Unable to retrieve Strong's Data for ${result.prefix}${result.sn}`
);
return;
}
try {
const d = await this.http
.get<StrongsCrossReference[]>(`assets/data/strongscr/cr${url}`)
.toPromise();
for (const cr of d) {
if (cr.id.toUpperCase() === result.prefix + result.sn) {
result.crossrefs = {
id: cr.id, // strongs id H1|G1
t: cr.t, // strongs testament grk|heb
d: cr.d, // strongs word/data definition Aaron {ah-ar-ohn'}
ss: cr.ss.map((ss) => {
return {
w: ss.w, // translated word
rs: ss.rs.map((rs) => {
return {
r: rs.r.split(';').join(':'), // fix the reference...
};
}),
};
}),
};
break;
}
}
} catch (err) {
this.dispatchError(
`Unable to retrieve Strong\'s Cross References for ${result.prefix}${result.sn}`
);
return;
}
if (dict === 'grk') {
url = `assets/data/rmac/rs${Math.ceil(sn / 1000)}.json`;
let rmacCrossReferences: RMACCrossReference[];
// rmac is a two get process.
try {
const d = await this.http.get<RMACCrossReference[]>(url).toPromise();
rmacCrossReferences = d;
} catch (err) {
this.dispatchError('Unable to retrieve RMAC');
return;
}
// deal with RMAC
const tmp = rmacCrossReferences.filter((el, i) => {
return el.i === sn + '';
});
if (tmp.length === 0) {
return result;
}
result.rmaccode = tmp[0].r;
if (result.rmaccode !== undefined) {
url = `assets/data/rmac/r-${result.rmaccode.substring(0, 1)}.json`;
try {
const d = await this.http.get<RMACDefinition[]>(url).toPromise();
for (const rmac of d) {
if (rmac.id.toLowerCase() === result.rmaccode) {
result.rmac = rmac;
break;
}
}
} catch (err) {
this.dispatchError('Unable to retrieve RMAC');
return;
}
}
}
return result;
}
//#endregion
//#region Bible Passages
async getPassage(ref: BibleReference, nextToItem: CardItem = null) {
const card = await this.composeBiblePassageCardItem(ref);
this.dispatch({
type: 'ADD_CARD',
card,
nextToItem,
});
}
async updatePassage(oldCard: CardItem, ref: BibleReference) {
const newCard = await this.composeBiblePassageCardItem(ref);
this.dispatch({
type: 'UPDATE_CARD',
oldCard,
newCard,
});
}
private async composeBiblePassageCardItem(ref: BibleReference) {
const result = await this.getPassageFromApi(ref.Section);
return {
qry: ref.toString(),
dict: ref.Section.start.book.book_number > 39 ? 'G' : 'H',
type: 'Passage',
data: result,
};
}
private async getPassageFromApi(section: Section) {
try {
const chapters = []; // the verses from the chapter.
const result = {
cs: [],
testament: '',
ref: BibleReference.toString(section),
};
if (Number(section.start.chapter) > section.start.book.last_chapter) {
this.dispatchError(
`The requested chapter ${section.start.book.name} is out of range. Please pick a chapter between 1 and ${section.end.book.last_chapter}.`
);
return;
}
if (Number(section.end.chapter) > section.end.book.last_chapter) {
this.dispatchError(
`The requested chapter ${section.end.book.name} is out of range. Please pick a chapter between 1 and ${section.end.book.last_chapter}.`
);
return;
}
for (
let i = Number(section.start.chapter);
i <= Number(section.end.chapter);
i++
) {
try {
const d = await this.http
.get<BiblePassage>(
`assets/data/bibles/kjv_strongs/${section.start.book.book_number}-${i}.json`
)
.toPromise();
chapters.push(d);
} catch (err) {
this.dispatchError(`Unable to retrieve bible passage ${result.ref}.`);
return;
}
}
const passages: BiblePassage[] = [];
// prep the passages
for (let j = 0; j < chapters.length; j++) {
const vss: BibleVerse[] = [];
let start: number;
let end: string | number;
// figure out the start verse.
if (j === 0) {
if (section.start.verse.indexOf('*') !== -1) {
// you sometimes use this as a shortcut to the last verse
// replace the * with the last verse, then eval the expression.
section.start.verse = section.start.verse.replace(
'*',
chapters[j].vss.length.toString()
);
start = math.evaluate(section.start.verse);
// update the section and the ref.
section.start.verse = start.toString();
result.ref = BibleReference.toString(section);
} else {
start = parseInt(section.start.verse, 10);
}
} else {
start = 1;
}
// figure out the end verse
if (j + 1 === chapters.length) {
end = section.end.verse;
} else {
end = '*';
}
// get the verses requested.
const tvs = chapters[j].vss.length;
if (end === '*' || parseInt(end, 10) > tvs) {
end = tvs;
}
// we're using c based indexes here, so the index is 1 less than the verse #.
for (let i = start; i <= end; i++) {
vss.push(chapters[j].vss[i - 1]);
}
passages.push({
ch: chapters[j].ch,
vss,
});
}
// convert into paragraphs.
result.cs = await this.convertToParagraphPassages(passages, section);
if (section.start.book.book_number >= 40) {
result.testament = 'new';
} else {
result.testament = 'old';
}
return result;
} catch (error) {
this.dispatchError(`An unknown error occurred: ${error}.`);
}
}
private async convertToParagraphPassages(
chapters: BiblePassage[],
section: Section
) {
let paragraphMarkers: HashTable<Paragraph> = null;
if (this.getState().paragraphs === null) {
paragraphMarkers = await this.getParagraphMarkers();
} else {
paragraphMarkers = this.getState().paragraphs;
}
const passages: BibleParagraphPassage[] = [];
for (const ch of chapters) {
const passage = {
ch: ch.ch,
paras: this.convertToParagraphs(ch, section, paragraphMarkers),
};
passages.push(passage);
}
return passages;
}
private async getParagraphMarkers(): Promise<HashTable<Paragraph>> {
const paras = await this.http
.get<HashTable<Paragraph>>('assets/data/bibles/paras.json')
.toPromise();
this.dispatch({
type: 'UPDATE_PARAGRAPHS',
paragraphs: paras,
});
return paras;
}
private convertToParagraphs(
ch: BiblePassage,
section: Section,
paragraphMarkers: HashTable<Paragraph>
): BibleParagraph[] {
// group the verses into paragraphs.
// create an initial paragraph to hold verses that might come before a paragraph.
let para = { p: { h: '', p: 0 }, vss: [] };
const paras = [];
const vss: BibleVerse[] = [];
// for each verse in the chapter, break them into paragraphs.
for (const v of ch.vss) {
if (this.getRefKey(v, section) in paragraphMarkers) {
paras.push(para);
para = {
p: paragraphMarkers[this.getRefKey(v, section)],
vss: [v],
};
if (para.p === undefined) {
para.p = { h: '', p: 0 };
} // just in case you can't find a paragraph.
} else {
para.vss.push(v);
}
}
// add the final paragraph if it has verses.
if (para.vss.length > 0) {
paras.push(para);
}
return paras;
}
private getRefKey(vs: BibleVerse, section: Section) {
return BibleReference.formatReferenceKey(
section.start.book.book_number,
section.start.chapter,
vs.v
);
}
//#endregion
//#region Word Search
async getWords(qry: string, nextToItem: CardItem = null) {
const result = await this.getWordsFromApi(qry);
const card = {
qry,
dict: 'n/a',
type: 'Words',
data: result,
};
this.dispatch({
type: 'ADD_CARD',
card,
nextToItem,
});
}
private async getWordsFromApi(qry: string): Promise<WordLookupResult> {
// its possible that this might get called before the word to stem map is build. first time, check and populate...
if (!this.wordToStem) {
await this.getStemWordIndex();
}
// now carry on...
const qs = this.normalizeQueryString(qry);
const results: (readonly string[])[] = [];
// Loop through each query term.
for (const q of qs) {
if (!this.wordToStem.has(q)) {
this.dispatch({
type: 'UPDATE_ERROR',
error: {
msg: `Unable to find the word "${q}" in any passages.`,
},
});
return;
}
const stem = this.wordToStem.get(q);
// handle the first case.
if (stem <= this.searchIndexArray[0]) {
results.unshift(
await this.getSearchReferences(
'assets/data/index/' + this.searchIndexArray[0] + 'idx.json',
stem
)
);
break;
}
// For each query term, figure out which xml file it is in, and get it.
// getSearchRefs returns an array of references.
for (let w = 1; w < this.searchIndexArray.length; w++) {
// If we are at the end of the array, we want to use a different test.
if (
stem <= this.searchIndexArray[w] &&
stem > this.searchIndexArray[w - 1]
) {
results.unshift(
await this.getSearchReferences(
'assets/data/index/' + this.searchIndexArray[w] + 'idx.json',
stem
)
);
}
}
} // End of loop through query terms
// Now we need to test results. If there is more than one item in the array, we need to find the set
// that is shared by all of them. IF not, we can just return those refs.
if (results.length === 0) {
this.dispatch({
type: 'UPDATE_ERROR',
error: {
msg: `No passages found for query: ${qry}.`,
},
});
return;
}
if (results.length === 1) {
// we go down this path if only one word was searched for
return {
refs: results[0],
word: qry,
};
} else {
// we go down this path if more than one word was searched for
return {
refs: this.findSharedSet(results),
word: qry,
};
}
}
/**
* Gets the references a given word is found in.
* Returns a string[].
* @param url - The url of the word index
* @param query - The word to lookup.
*/
private async getStemWordIndex() {
this.wordToStem = new Map<string, string>();
try {
const r = await this.http
.get<WordToStem[]>('assets/data/index/word_to_stem_idx.json')
.toPromise();
// find the right word
for (const i of r) {
this.wordToStem.set(i.w, i.s);
}
} catch (err) {
this.dispatch({
type: 'UPDATE_ERROR',
error: {
msg: err,
},
});
return;
}
}
/**
* Gets the references a given word is found in.
* Returns a string[].
* @param url - The url of the word index
* @param query - The word to lookup.
*/
private async getSearchReferences(url: string, query: string) {
// getSearchRefs takes a url and uses ajax to retrieve the references and returns an array of references.
let r: IndexResult[];
try {
r = await this.http.get<IndexResult[]>(url).toPromise();
} catch (err) {
this.dispatch({
type: 'UPDATE_ERROR',
error: {
msg: err,
},
});
return;
}
// find the right word
const refs = r.filter((o) => o.w === query);
if (refs.length > 0) {
return refs[0].r;
} else {
return [];
}
}
private buildIndexArray() {
const words: string[] = [];
words.unshift('abishur');
words.unshift('achor');
words.unshift('adoni');
words.unshift('afterward');
words.unshift('ahishahar');
words.unshift('alleg');
words.unshift('ambush');
words.unshift('ancestor');
words.unshift('aphik');
words.unshift('arbah');
words.unshift('arodi');
words.unshift('ashkenaz');
words.unshift('ate');
words.unshift('azaniah');
words.unshift('backbiteth');
words.unshift('barbarian');
words.unshift('beard');
words.unshift('begettest');
words.unshift('benefactor');
words.unshift('bethel');
words.unshift('bilshan');
words.unshift('blindeth');
words.unshift('booti');
words.unshift('breaketh');
words.unshift('bucket');
words.unshift('cabbon');
words.unshift('caphtor');
words.unshift('causeless');
words.unshift('chapmen');
words.unshift('chese');
words.unshift('chrysoprasus');
words.unshift('cloth');
words.unshift('common');
words.unshift('confess');
words.unshift('contendeth');
words.unshift('coucheth');
words.unshift('crept');
words.unshift('curseth');
words.unshift('darius');
words.unshift('decketh');
words.unshift('dema');
words.unshift('devil');
words.unshift('directeth');
words.unshift('disposit');
words.unshift('doth');
words.unshift('drowsi');
words.unshift('ebe');
words.unshift('elead');
words.unshift('elkoshit');
words.unshift('encourag');
words.unshift('entreat');
words.unshift('eschew');
words.unshift('ever');
words.unshift('expert');
words.unshift('fallest');
words.unshift('feedeth');
words.unshift('filthi');
words.unshift('fleeth');
words.unshift('forborn');
words.unshift('forsookest');
words.unshift('fretteth');
words.unshift('gahar');
words.unshift('gazzam');
words.unshift('gibea');
words.unshift('glister');
words.unshift('got');
words.unshift('grope');
words.unshift('hadlai');
words.unshift('hammon');
words.unshift('harbona');
words.unshift('hasrah');
words.unshift('hazezon');
words.unshift('heinous');
words.unshift('herebi');
words.unshift('highest');
words.unshift('holdeth');
words.unshift('hosanna');
words.unshift('huri');
words.unshift('ill');
words.unshift('inexcus');
words.unshift('intend');
words.unshift('ishui');
words.unshift('jaazaniah');
words.unshift('jaminit');
words.unshift('jecoliah');
words.unshift('jeopard');
words.unshift('jethro');
words.unshift('joiarib');
words.unshift('juda');
words.unshift('kelaiah');
words.unshift('kishion');
words.unshift('laden');
words.unshift('laughter');
words.unshift('lehi');
words.unshift('lift');
words.unshift('loatheth');
words.unshift('lucius');
words.unshift('madmen');
words.unshift('malachi');
words.unshift('march');
words.unshift('maul');
words.unshift('melchizedek');
words.unshift('merrili');
words.unshift('midianit');
words.unshift('miri');
words.unshift('modest');
words.unshift('move');
words.unshift('naashon');
words.unshift('nazareth');
words.unshift('nephishesim');
words.unshift('nisan');
words.unshift('obadiah');
words.unshift('oliveyard');
words.unshift('oren');
words.unshift('overrun');
words.unshift('pallu');
words.unshift('pas');
words.unshift('peel');
words.unshift('pernici');
words.unshift('philip');
words.unshift('pison');
words.unshift('plucketh');
words.unshift('pour');
words.unshift('price');
words.unshift('proport');
words.unshift('purg');
words.unshift('rabboni');
words.unshift('ravish');
words.unshift('redeemedst');
words.unshift('remainest');
words.unshift('reput');
words.unshift('revers');
words.unshift('rissah');
words.unshift('ruddi');
words.unshift('said');
words.unshift('sapphir');
words.unshift('scepter');
words.unshift('secundus');
words.unshift('separ');
words.unshift('shachia');
words.unshift('sharar');
words.unshift('sheepshear');
words.unshift('sheva');
words.unshift('shishak');
words.unshift('shroud');
words.unshift('signifi');
words.unshift('sittest');
words.unshift('slow');
words.unshift('soft');
words.unshift('sowedst');
words.unshift('spoil');
words.unshift('station');
words.unshift('stoop');
words.unshift('strongest');
words.unshift('sum');
words.unshift('sweep');
words.unshift('tahapan');
words.unshift('tast');
words.unshift('ten');
words.unshift('thereat');
words.unshift('threaten');
words.unshift('timbrel');
words.unshift('tongu');
words.unshift('travailest');
words.unshift('trust');
words.unshift('uncircumcis');
words.unshift('unprepar');
words.unshift('urg');
words.unshift('vat');
words.unshift('visiteth');
words.unshift('wash');
words.unshift('wed');
words.unshift('wherewith');
words.unshift('winepress');
words.unshift('won');
words.unshift('written');
words.unshift('zalmonah');
words.unshift('zenan');
words.unshift('ziphim');
words.unshift('zuzim');
return words;
}
/*
* Returns a list of references in string form as a string[] that are shared
* given a list of lists of references in string form.
*/
private findSharedSet(referenceSet: (readonly string[])[]) {
const results = [];
// FindSharedSet takes an array of reference arrays, and figures out
// which references are shared by all arrays/sets, then returns a single
// array of references.
// tslint:disable-next-line: prefer-for-of
for (let j = 0; j < referenceSet.length; j++) {
const refs = referenceSet[j];
if (refs != null) {
for (let i = 0; i < refs.length; i++) {
const r = refs[i].split(':');
// convert references to single integers.
// Book * 100000000, Chapter * 10000, Verse remains same, add all together.
results[j][i] = this.toReferenceNumber(r);
}
} else {
return null;
}
}
// get the first result
let first = results[0];
// for each additional result, get the shared set
for (let i = 1; i < results.length; i++) {
first = this.returnSharedSet(results[i], first);
}
// convert the references back into book, chapter and verse.
for (let i = 0; i < first.length; i++) {
const ref = first[i];
first[i] = this.toReferenceString(ref);
}
return first;
}
private toReferenceString(ref: number) {
return BibleReference.formatReferenceKey(
Math.floor(ref / 100000000),
Math.floor((ref % 100000000) / 10000),
Math.floor((ref % 100000000) % 10000)
);
}
private toReferenceNumber(r: string[]) {
let ref = parseInt(r[0], 10) * 100000000;
ref = ref + parseInt(r[1], 10) * 10000;
ref = ref + parseInt(r[2], 10) * 1;
return ref;
}
private returnSharedSet(x, y) {
/// <summary>
/// Takes two javascript arrays and returns an array
/// containing a set of values shared by arrays.
/// </summary>
// declare iterator
let i = 0;
// declare terminator
let t = x.length < y.length ? x.length : y.length;
// sort the arrays
x.sort((a, b) => a - b);
y.sort((a, b) => a - b);
// in this loop, we remove from the arrays, the
// values that aren't shared between them.
while (i < t) {
if (x[i] === y[i]) {
i++;
}
if (x[i] < y[i]) {
x.splice(i, 1);
}
if (x[i] > y[i]) {
y.splice(i, 1);
}
t = x.length < y.length ? x.length : y.length;
// we have to make sure to remove any extra values
// at the end of an array when we reach the end of
// the other.
if (t === i && t < x.length) {
x.splice(i, x.length - i);
}
if (t === i && t < y.length) {
y.splice(i, x.length - i);
}
}
// we could return y, because at this time, both arrays
// are identical.
return x;
}
private normalizeQueryString(qry: string): string[] {
qry = qry.toLowerCase();
return qry.replace(/'/g, '').replace(/\s+/g, ' ').split(' ');
}
//#endregion
//#region Auto Complete
async getAutoComplete(keyword: string) {
if (!this.autocomplete) {
// if you have't populated the word list yet, do so...
const data = await this.http
.get<WordToStem[]>('assets/data/index/word_to_stem_idx.json')
.toPromise();
this.autocomplete = data.map((o) => o.w);
}
let qry = keyword;
let prefix = '';
const idx = qry.lastIndexOf(';');
const words: string[] = [];
if (idx > -1) {
qry = keyword.substr(idx + 1).trim();
prefix = keyword.substr(0, idx).trim() + '; ';
}
if (qry.search(/[0-9]/i) === -1) {
// its a word
for (const item of BibleReference.Books) {
if (
item.name !== 'Unknown' &&
(item.name.toLowerCase().indexOf(qry.toLowerCase()) > -1 ||
item.short_name.toLowerCase().indexOf(qry.toLowerCase()) > -1)
) {
words.push(prefix + item.name);
if (words.length > 2) {
break;
}
}
}
for (const item of this.autocomplete) {
if (item.toLowerCase().indexOf(qry.toLowerCase()) > -1) {
words.push(prefix + item);
if (words.length > 6) {
break;
}
}
}
this.dispatch({
type: 'UPDATE_AUTOCOMPLETE',
words,
});
} else if (qry.search(/(H|G)[0-9]/i) !== -1) {
// its a strongs lookup
if (qry.substr(0, 1).toUpperCase() === 'H') {
const num = parseInt(qry.substr(1), 10);
for (let x = num; x < num + 10 && x < 8675; x++) {
words.push('H' + x);
}
} else if (qry.substr(0, 1).toUpperCase() === 'G') {
const num = parseInt(qry.substr(1), 10);
for (let x = num; x < num + 10 && x < 5625; x++) {
words.push('G' + x);
}
}
this.dispatch({
type: 'UPDATE_AUTOCOMPLETE',
words,
});
}
}
//#endregion
}