release new phonetics!
This commit is contained in:
parent
8dd63ad9c4
commit
54fb2050c1
|
@ -67,6 +67,10 @@ npm install
|
||||||
#### Development
|
#### Development
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
firebase login
|
||||||
|
# get envars locally
|
||||||
|
firebase functions:config:get > .runtimeconfig.json
|
||||||
|
# start functions emulator
|
||||||
npm run serve
|
npm run serve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lingdocs/inflect": "5.10.1",
|
"@lingdocs/inflect": "6.0.0",
|
||||||
"base64url": "^3.0.1",
|
"base64url": "^3.0.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"connect-redis": "^6.0.0",
|
"connect-redis": "^6.0.0",
|
||||||
|
@ -124,9 +124,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lingdocs/inflect": {
|
"node_modules/@lingdocs/inflect": {
|
||||||
"version": "5.10.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://npm.lingdocs.com/@lingdocs%2finflect/-/inflect-5.10.1.tgz",
|
"resolved": "https://npm.lingdocs.com/@lingdocs%2finflect/-/inflect-6.0.0.tgz",
|
||||||
"integrity": "sha512-8MPsfQzeerlyT02dz7D7L+AYFrjGOrQB7nMBUXutnLw3/RKhvW99dLImFZKSnCr8DZsEONEp0IVeqxeIUczxog==",
|
"integrity": "sha512-aPvjqOkeKhu60Inbk7uuLooR/9hvUS4rDHyqR5JJPziZMLJ05U5fBTUvehit7stHSRGivskR00uU3liWbXce6g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fp-ts": "^2.16.0",
|
"fp-ts": "^2.16.0",
|
||||||
|
@ -2747,9 +2747,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@lingdocs/inflect": {
|
"@lingdocs/inflect": {
|
||||||
"version": "5.10.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://npm.lingdocs.com/@lingdocs%2finflect/-/inflect-5.10.1.tgz",
|
"resolved": "https://npm.lingdocs.com/@lingdocs%2finflect/-/inflect-6.0.0.tgz",
|
||||||
"integrity": "sha512-8MPsfQzeerlyT02dz7D7L+AYFrjGOrQB7nMBUXutnLw3/RKhvW99dLImFZKSnCr8DZsEONEp0IVeqxeIUczxog==",
|
"integrity": "sha512-aPvjqOkeKhu60Inbk7uuLooR/9hvUS4rDHyqR5JJPziZMLJ05U5fBTUvehit7stHSRGivskR00uU3liWbXce6g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fp-ts": "^2.16.0",
|
"fp-ts": "^2.16.0",
|
||||||
"pbf": "^3.2.1",
|
"pbf": "^3.2.1",
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lingdocs/inflect": "5.10.1",
|
"@lingdocs/inflect": "6.0.0",
|
||||||
"base64url": "^3.0.1",
|
"base64url": "^3.0.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"connect-redis": "^6.0.0",
|
"connect-redis": "^6.0.0",
|
||||||
|
@ -22,6 +22,7 @@
|
||||||
"express-session": "^1.17.2",
|
"express-session": "^1.17.2",
|
||||||
"lokijs": "^1.5.12",
|
"lokijs": "^1.5.12",
|
||||||
"nano": "^9.0.3",
|
"nano": "^9.0.3",
|
||||||
|
"next": "^13.4.12",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"nodemailer": "^6.6.3",
|
"nodemailer": "^6.6.3",
|
||||||
"passport": "^0.4.1",
|
"passport": "^0.4.1",
|
||||||
|
@ -42,6 +43,7 @@
|
||||||
"@types/cron": "^2.0.0",
|
"@types/cron": "^2.0.0",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/express-session": "^1.17.4",
|
"@types/express-session": "^1.17.4",
|
||||||
|
"@types/lokijs": "^1.5.8",
|
||||||
"@types/node": "^16.6.0",
|
"@types/node": "^16.6.0",
|
||||||
"@types/node-fetch": "^2.5.12",
|
"@types/node-fetch": "^2.5.12",
|
||||||
"@types/nodemailer": "^6.4.4",
|
"@types/nodemailer": "^6.4.4",
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
typePredicates as tp,
|
typePredicates as tp,
|
||||||
entryOfFull,
|
entryOfFull,
|
||||||
standardizePashto,
|
standardizePashto,
|
||||||
} from "@lingdocs/inflect"
|
} from "@lingdocs/inflect";
|
||||||
|
|
||||||
export let collection: Collection<T.DictionaryEntry> | undefined = undefined;
|
export let collection: Collection<T.DictionaryEntry> | undefined = undefined;
|
||||||
export let allWordsCollection: Collection<T.PsString> | undefined = undefined;
|
export let allWordsCollection: Collection<T.PsString> | undefined = undefined;
|
||||||
|
@ -34,7 +34,10 @@ async function fetchDictionary(): Promise<T.Dictionary> {
|
||||||
|
|
||||||
async function fetchAllWords(): Promise<T.AllWordsWithInflections> {
|
async function fetchAllWords(): Promise<T.AllWordsWithInflections> {
|
||||||
// TODO: this is really ugly
|
// TODO: this is really ugly
|
||||||
const res = await fetch(process.env.LINGDOCS_DICTIONARY_URL?.slice(0, -4) + "all-words.json");
|
const res = await fetch(
|
||||||
|
process.env.LINGDOCS_DICTIONARY_URL?.slice(0, -10) +
|
||||||
|
"all-words-dictionary.json"
|
||||||
|
);
|
||||||
return await res.json();
|
return await res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,33 +79,34 @@ export function findInAllWords(p: string | RegExp): T.PsWord[] | undefined {
|
||||||
throw new Error("allWords not initialized");
|
throw new Error("allWords not initialized");
|
||||||
}
|
}
|
||||||
return allWordsCollection.find({
|
return allWordsCollection.find({
|
||||||
p: typeof p === "string"
|
p: typeof p === "string" ? p : { $regex: p },
|
||||||
? p
|
|
||||||
: { $regex: p },
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEntries(ids: (number | string)[]): Promise<{
|
export async function getEntries(ids: (number | string)[]): Promise<{
|
||||||
results: (T.DictionaryEntry | T.VerbEntry)[],
|
results: (T.DictionaryEntry | T.VerbEntry)[];
|
||||||
notFound: (number | string)[],
|
notFound: (number | string)[];
|
||||||
}> {
|
}> {
|
||||||
if (!collection) {
|
if (!collection) {
|
||||||
throw new Error("dictionary not initialized");
|
throw new Error("dictionary not initialized");
|
||||||
}
|
}
|
||||||
const idsP = ids.map(x => typeof x === "number" ? x : standardizePashto(x))
|
const idsP = ids.map((x) =>
|
||||||
const results: (T.DictionaryEntry | T.VerbEntry)[] = collection.find({
|
typeof x === "number" ? x : standardizePashto(x)
|
||||||
"$or": [
|
);
|
||||||
{ "ts": { "$in": idsP }},
|
const results: (T.DictionaryEntry | T.VerbEntry)[] = collection
|
||||||
{ "p": { "$in": idsP }},
|
.find({
|
||||||
],
|
$or: [{ ts: { $in: idsP } }, { p: { $in: idsP } }],
|
||||||
}).map(x => {
|
})
|
||||||
|
.map((x) => {
|
||||||
const { $loki, meta, ...entry } = x;
|
const { $loki, meta, ...entry } = x;
|
||||||
return entry;
|
return entry;
|
||||||
}).map((entry): T.DictionaryEntry | T.VerbEntry => {
|
})
|
||||||
|
.map((entry): T.DictionaryEntry | T.VerbEntry => {
|
||||||
if (tp.isVerbDictionaryEntry(entry)) {
|
if (tp.isVerbDictionaryEntry(entry)) {
|
||||||
if (entry.c?.includes("comp.") && entry.l) {
|
if (entry.c?.includes("comp.") && entry.l) {
|
||||||
const complement = getOneByTs(entry.l);
|
const complement = getOneByTs(entry.l);
|
||||||
if (!complement) throw new Error("Error getting complement "+entry.l);
|
if (!complement)
|
||||||
|
throw new Error("Error getting complement " + entry.l);
|
||||||
return {
|
return {
|
||||||
entry,
|
entry,
|
||||||
complement,
|
complement,
|
||||||
|
@ -115,17 +119,21 @@ export async function getEntries(ids: (number | string)[]): Promise<{
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
results,
|
results,
|
||||||
notFound: ids.filter(id => !results.find(x => {
|
notFound: ids.filter(
|
||||||
|
(id) =>
|
||||||
|
!results.find((x) => {
|
||||||
const entry = entryOfFull(x);
|
const entry = entryOfFull(x);
|
||||||
return entry.p === id || entry.ts === id;
|
return entry.p === id || entry.ts === id;
|
||||||
})),
|
})
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
lokidb.loadDatabase({}, (err: Error) => {
|
lokidb.loadDatabase({}, (err: Error) => {
|
||||||
lokidb.removeCollection(collectionName);
|
lokidb.removeCollection(collectionName);
|
||||||
lokidb.removeCollection(allWordsCollectionName);
|
lokidb.removeCollection(allWordsCollectionName);
|
||||||
fetchDictionary().then((dictionary) => {
|
fetchDictionary()
|
||||||
|
.then((dictionary) => {
|
||||||
collection = lokidb.addCollection(collectionName, {
|
collection = lokidb.addCollection(collectionName, {
|
||||||
indices: ["i", "p"],
|
indices: ["i", "p"],
|
||||||
unique: ["ts"],
|
unique: ["ts"],
|
||||||
|
@ -133,7 +141,8 @@ lokidb.loadDatabase({}, (err: Error) => {
|
||||||
version = dictionary.info.release;
|
version = dictionary.info.release;
|
||||||
collection?.insert(dictionary.entries);
|
collection?.insert(dictionary.entries);
|
||||||
updateJob.start();
|
updateJob.start();
|
||||||
}).catch(console.error);
|
})
|
||||||
|
.catch(console.error);
|
||||||
fetchAllWords().then((allWords) => {
|
fetchAllWords().then((allWords) => {
|
||||||
allWordsCollection = lokidb.addCollection(allWordsCollectionName, {
|
allWordsCollection = lokidb.addCollection(allWordsCollectionName, {
|
||||||
indices: ["p"],
|
indices: ["p"],
|
||||||
|
|
|
@ -2,7 +2,6 @@ import express from "express";
|
||||||
import {
|
import {
|
||||||
allWordsCollection,
|
allWordsCollection,
|
||||||
collection,
|
collection,
|
||||||
findInAllWords,
|
|
||||||
getEntries,
|
getEntries,
|
||||||
updateDictionary,
|
updateDictionary,
|
||||||
} from "../lib/dictionary";
|
} from "../lib/dictionary";
|
||||||
|
@ -26,7 +25,7 @@ dictionaryRouter.post("/script-to-phonetics", async (req, res, next) => {
|
||||||
}
|
}
|
||||||
const results = await scriptToPhonetics(text, accents);
|
const results = await scriptToPhonetics(text, accents);
|
||||||
res.send({ ok: true, results });
|
res.send({ ok: true, results });
|
||||||
})
|
});
|
||||||
|
|
||||||
dictionaryRouter.post("/entries", async (req, res, next) => {
|
dictionaryRouter.post("/entries", async (req, res, next) => {
|
||||||
if (!collection) {
|
if (!collection) {
|
||||||
|
@ -44,7 +43,7 @@ dictionaryRouter.get("/entries/:id", async (req, res, next) => {
|
||||||
if (!collection) {
|
if (!collection) {
|
||||||
return res.send({ ok: false, message: "dictionary not ready" });
|
return res.send({ ok: false, message: "dictionary not ready" });
|
||||||
}
|
}
|
||||||
const ids = req.params.id.split(",").map(x => {
|
const ids = req.params.id.split(",").map((x) => {
|
||||||
const n = parseInt(x);
|
const n = parseInt(x);
|
||||||
return Number.isNaN(n) ? x : n;
|
return Number.isNaN(n) ? x : n;
|
||||||
});
|
});
|
||||||
|
|
2865
account/yarn.lock
2865
account/yarn.lock
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
||||||
"name": "functions",
|
"name": "functions",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google-cloud/storage": "^5.8.1",
|
"@google-cloud/storage": "^5.8.1",
|
||||||
"@lingdocs/inflect": "5.10.1",
|
"@lingdocs/inflect": "6.0.0",
|
||||||
"@types/cors": "^2.8.10",
|
"@types/cors": "^2.8.10",
|
||||||
"@types/google-spreadsheet": "^3.0.2",
|
"@types/google-spreadsheet": "^3.0.2",
|
||||||
"@types/react": "^18.0.21",
|
"@types/react": "^18.0.21",
|
||||||
|
@ -1468,9 +1468,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lingdocs/inflect": {
|
"node_modules/@lingdocs/inflect": {
|
||||||
"version": "5.10.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://npm.lingdocs.com/@lingdocs%2finflect/-/inflect-5.10.1.tgz",
|
"resolved": "https://npm.lingdocs.com/@lingdocs%2finflect/-/inflect-6.0.0.tgz",
|
||||||
"integrity": "sha512-8MPsfQzeerlyT02dz7D7L+AYFrjGOrQB7nMBUXutnLw3/RKhvW99dLImFZKSnCr8DZsEONEp0IVeqxeIUczxog==",
|
"integrity": "sha512-aPvjqOkeKhu60Inbk7uuLooR/9hvUS4rDHyqR5JJPziZMLJ05U5fBTUvehit7stHSRGivskR00uU3liWbXce6g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fp-ts": "^2.16.0",
|
"fp-ts": "^2.16.0",
|
||||||
|
@ -8056,9 +8056,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@lingdocs/inflect": {
|
"@lingdocs/inflect": {
|
||||||
"version": "5.10.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://npm.lingdocs.com/@lingdocs%2finflect/-/inflect-5.10.1.tgz",
|
"resolved": "https://npm.lingdocs.com/@lingdocs%2finflect/-/inflect-6.0.0.tgz",
|
||||||
"integrity": "sha512-8MPsfQzeerlyT02dz7D7L+AYFrjGOrQB7nMBUXutnLw3/RKhvW99dLImFZKSnCr8DZsEONEp0IVeqxeIUczxog==",
|
"integrity": "sha512-aPvjqOkeKhu60Inbk7uuLooR/9hvUS4rDHyqR5JJPziZMLJ05U5fBTUvehit7stHSRGivskR00uU3liWbXce6g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fp-ts": "^2.16.0",
|
"fp-ts": "^2.16.0",
|
||||||
"pbf": "^3.2.1",
|
"pbf": "^3.2.1",
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"main": "lib/functions/src/index.js",
|
"main": "lib/functions/src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google-cloud/storage": "^5.8.1",
|
"@google-cloud/storage": "^5.8.1",
|
||||||
"@lingdocs/inflect": "5.10.1",
|
"@lingdocs/inflect": "6.0.0",
|
||||||
"@types/cors": "^2.8.10",
|
"@types/cors": "^2.8.10",
|
||||||
"@types/google-spreadsheet": "^3.0.2",
|
"@types/google-spreadsheet": "^3.0.2",
|
||||||
"@types/react": "^18.0.21",
|
"@types/react": "^18.0.21",
|
||||||
|
|
|
@ -4,12 +4,17 @@ import { receiveSubmissions } from "./submissions";
|
||||||
import lingdocsAuth from "./middleware/lingdocs-auth";
|
import lingdocsAuth from "./middleware/lingdocs-auth";
|
||||||
import publish from "./publish";
|
import publish from "./publish";
|
||||||
|
|
||||||
export const publishDictionary = functions.runWith({
|
export const publishDictionary = functions
|
||||||
|
.runWith({
|
||||||
timeoutSeconds: 525,
|
timeoutSeconds: 525,
|
||||||
memory: "2GB"
|
memory: "2GB",
|
||||||
}).https.onRequest(
|
})
|
||||||
|
.https.onRequest(
|
||||||
lingdocsAuth(
|
lingdocsAuth(
|
||||||
async (req, res: functions.Response<FT.PublishDictionaryResponse | FT.FunctionError>) => {
|
async (
|
||||||
|
req,
|
||||||
|
res: functions.Response<FT.PublishDictionaryResponse | FT.FunctionError>
|
||||||
|
) => {
|
||||||
if (req.user.level !== "editor") {
|
if (req.user.level !== "editor") {
|
||||||
res.status(403).send({ ok: false, error: "403 forbidden" });
|
res.status(403).send({ ok: false, error: "403 forbidden" });
|
||||||
return;
|
return;
|
||||||
|
@ -25,11 +30,17 @@ export const publishDictionary = functions.runWith({
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const submissions = functions.runWith({
|
export const submissions = functions
|
||||||
|
.runWith({
|
||||||
timeoutSeconds: 60,
|
timeoutSeconds: 60,
|
||||||
memory: "1GB",
|
memory: "1GB",
|
||||||
}).https.onRequest(lingdocsAuth(
|
})
|
||||||
async (req, res: functions.Response<FT.SubmissionsResponse | FT.FunctionError>) => {
|
.https.onRequest(
|
||||||
|
lingdocsAuth(
|
||||||
|
async (
|
||||||
|
req,
|
||||||
|
res: functions.Response<FT.SubmissionsResponse | FT.FunctionError>
|
||||||
|
) => {
|
||||||
if (!Array.isArray(req.body)) {
|
if (!Array.isArray(req.body)) {
|
||||||
res.status(400).send({
|
res.status(400).send({
|
||||||
ok: false,
|
ok: false,
|
||||||
|
@ -45,6 +56,7 @@ export const submissions = functions.runWith({
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
res.status(500).send({ ok: false, error: e.message });
|
res.status(500).send({ ok: false, error: e.message });
|
||||||
};
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,26 +11,23 @@ import {
|
||||||
simplifyPhonetics,
|
simplifyPhonetics,
|
||||||
standardizeEntry,
|
standardizeEntry,
|
||||||
} from "@lingdocs/inflect";
|
} from "@lingdocs/inflect";
|
||||||
import {
|
import { getWordList } from "./word-list-maker";
|
||||||
getWordList,
|
import { PublishDictionaryResponse } from "../../website/src/types/functions-types";
|
||||||
} from "./word-list-maker";
|
|
||||||
import {
|
|
||||||
PublishDictionaryResponse,
|
|
||||||
} from "../../website/src/types/functions-types";
|
|
||||||
import { Storage } from "@google-cloud/storage";
|
import { Storage } from "@google-cloud/storage";
|
||||||
const storage = new Storage({
|
const storage = new Storage({
|
||||||
projectId: "lingdocs",
|
projectId: "lingdocs",
|
||||||
});
|
});
|
||||||
|
|
||||||
const title = "LingDocs Pashto Dictionary"
|
const title = "LingDocs Pashto Dictionary";
|
||||||
const license = "Copyright © 2021 lingdocs.com All Rights Reserved - Licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License - https://creativecommons.org/licenses/by-nc-sa/4.0/";
|
const license =
|
||||||
|
"Copyright © 2021 lingdocs.com All Rights Reserved - Licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License - https://creativecommons.org/licenses/by-nc-sa/4.0/";
|
||||||
const bucketName = "lingdocs";
|
const bucketName = "lingdocs";
|
||||||
const baseUrl = `https://storage.googleapis.com/${bucketName}/`;
|
const baseUrl = `https://storage.googleapis.com/${bucketName}/`;
|
||||||
const dictionaryFilename = "dict";
|
const dictionaryFilename = "dictionary";
|
||||||
const dictionaryInfoFilename = "dict-info";
|
const dictionaryInfoFilename = "dictionary-info";
|
||||||
// const hunspellAffFileFilename = "ps_AFF.aff";
|
// const hunspellAffFileFilename = "ps_AFF.aff";
|
||||||
// const hunspellDicFileFilename = "ps_AFF.dic";
|
// const hunspellDicFileFilename = "ps_AFF.dic";
|
||||||
const allWordsJsonFilename = "all-words.json";
|
const allWordsJsonFilename = "all-words-dictionary.json";
|
||||||
const url = `${baseUrl}${dictionaryFilename}`;
|
const url = `${baseUrl}${dictionaryFilename}`;
|
||||||
const infoUrl = `${baseUrl}${dictionaryInfoFilename}`;
|
const infoUrl = `${baseUrl}${dictionaryInfoFilename}`;
|
||||||
|
|
||||||
|
@ -41,7 +38,7 @@ export default async function publish(): Promise<PublishDictionaryResponse> {
|
||||||
const entries = await getRawEntries();
|
const entries = await getRawEntries();
|
||||||
const errors = checkForErrors(entries);
|
const errors = checkForErrors(entries);
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
return({ ok: false, errors });
|
return { ok: false, errors };
|
||||||
}
|
}
|
||||||
// const duplicates = findDuplicates(entries);
|
// const duplicates = findDuplicates(entries);
|
||||||
// duplicates.forEach((duplicate) => {
|
// duplicates.forEach((duplicate) => {
|
||||||
|
@ -58,24 +55,27 @@ export default async function publish(): Promise<PublishDictionaryResponse> {
|
||||||
numberOfEntries: entries.length,
|
numberOfEntries: entries.length,
|
||||||
},
|
},
|
||||||
entries,
|
entries,
|
||||||
}
|
};
|
||||||
uploadDictionaryToStorage(dictionary).catch(console.error);
|
uploadDictionaryToStorage(dictionary).catch(console.error);
|
||||||
// TODO: make this async and run after publish response
|
// TODO: make this async and run after publish response
|
||||||
doHunspellEtc(dictionary.info, entries).catch(console.error);
|
doHunspellEtc(dictionary.info, entries).catch(console.error);
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
info: dictionary.info
|
info: dictionary.info,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doHunspellEtc(info: T.DictionaryInfo, entries: T.DictionaryEntry[]) {
|
async function doHunspellEtc(
|
||||||
|
info: T.DictionaryInfo,
|
||||||
|
entries: T.DictionaryEntry[]
|
||||||
|
) {
|
||||||
const wordlistResponse = getWordList(entries);
|
const wordlistResponse = getWordList(entries);
|
||||||
if (!wordlistResponse.ok) {
|
if (!wordlistResponse.ok) {
|
||||||
throw new Error(JSON.stringify(wordlistResponse.errors));
|
throw new Error(JSON.stringify(wordlistResponse.errors));
|
||||||
}
|
}
|
||||||
// const hunspell = makeHunspell(wordlistResponse.wordlist);
|
// const hunspell = makeHunspell(wordlistResponse.wordlist);
|
||||||
// await uploadHunspellToStorage(hunspell);
|
// await uploadHunspellToStorage(hunspell);
|
||||||
await uploadAllWordsToStoarage(info, wordlistResponse.wordlist)
|
await uploadAllWordsToStoarage(info, wordlistResponse.wordlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,9 +88,7 @@ async function doHunspellEtc(info: T.DictionaryInfo, entries: T.DictionaryEntry[
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function getRows() {
|
async function getRows() {
|
||||||
const doc = new GoogleSpreadsheet(
|
const doc = new GoogleSpreadsheet(functions.config().sheet.id);
|
||||||
functions.config().sheet.id,
|
|
||||||
);
|
|
||||||
await doc.useServiceAccountAuth({
|
await doc.useServiceAccountAuth({
|
||||||
client_email: functions.config().serviceacct.email,
|
client_email: functions.config().serviceacct.email,
|
||||||
private_key: functions.config().serviceacct.key,
|
private_key: functions.config().serviceacct.key,
|
||||||
|
@ -98,7 +96,7 @@ async function getRows() {
|
||||||
await doc.loadInfo();
|
await doc.loadInfo();
|
||||||
const sheet = doc.sheetsByIndex[0];
|
const sheet = doc.sheetsByIndex[0];
|
||||||
const rows = await sheet.getRows();
|
const rows = await sheet.getRows();
|
||||||
rows.sort((a, b) => a.ts > b.ts ? -1 : a.ts < b.ts ? 1 : 0);
|
rows.sort((a, b) => (a.ts > b.ts ? -1 : a.ts < b.ts ? 1 : 0));
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,15 +135,19 @@ async function getRawEntries(): Promise<T.DictionaryEntry[]> {
|
||||||
g: simplifyPhonetics(row.f),
|
g: simplifyPhonetics(row.f),
|
||||||
e: row.e,
|
e: row.e,
|
||||||
};
|
};
|
||||||
dictionaryEntryNumberFields.forEach((field: T.DictionaryEntryNumberField) => {
|
dictionaryEntryNumberFields.forEach(
|
||||||
|
(field: T.DictionaryEntryNumberField) => {
|
||||||
if (row[field]) e[field] = parseInt(row[field]);
|
if (row[field]) e[field] = parseInt(row[field]);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
dictionaryEntryTextFields.forEach((field: T.DictionaryEntryTextField) => {
|
dictionaryEntryTextFields.forEach((field: T.DictionaryEntryTextField) => {
|
||||||
if (row[field]) e[field] = row[field].trim();
|
if (row[field]) e[field] = row[field].trim();
|
||||||
});
|
});
|
||||||
dictionaryEntryBooleanFields.forEach((field: T.DictionaryEntryBooleanField) => {
|
dictionaryEntryBooleanFields.forEach(
|
||||||
|
(field: T.DictionaryEntryBooleanField) => {
|
||||||
if (row[field]) e[field] = true;
|
if (row[field]) e[field] = true;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
entries.push(standardizeEntry(e));
|
entries.push(standardizeEntry(e));
|
||||||
}
|
}
|
||||||
// add alphabetical index
|
// add alphabetical index
|
||||||
|
@ -158,8 +160,11 @@ async function getRawEntries(): Promise<T.DictionaryEntry[]> {
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkForErrors(entries: T.DictionaryEntry[]): T.DictionaryEntryError[] {
|
function checkForErrors(
|
||||||
return entries.reduce((errors: T.DictionaryEntryError[], entry: T.DictionaryEntry) => {
|
entries: T.DictionaryEntry[]
|
||||||
|
): T.DictionaryEntryError[] {
|
||||||
|
return entries.reduce(
|
||||||
|
(errors: T.DictionaryEntryError[], entry: T.DictionaryEntry) => {
|
||||||
const response = validateEntry(entry);
|
const response = validateEntry(entry);
|
||||||
if ("errors" in response && response.errors.length) {
|
if ("errors" in response && response.errors.length) {
|
||||||
return [...errors, response];
|
return [...errors, response];
|
||||||
|
@ -177,7 +182,11 @@ function checkForErrors(entries: T.DictionaryEntry[]): T.DictionaryEntryError[]
|
||||||
};
|
};
|
||||||
return [...errors, error];
|
return [...errors, error];
|
||||||
}
|
}
|
||||||
if (!complement.c?.includes("n.") && !complement.c?.includes("adj.") && !complement.c?.includes("adv.")) {
|
if (
|
||||||
|
!complement.c?.includes("n.") &&
|
||||||
|
!complement.c?.includes("adj.") &&
|
||||||
|
!complement.c?.includes("adv.")
|
||||||
|
) {
|
||||||
const error: T.DictionaryEntryError = {
|
const error: T.DictionaryEntryError = {
|
||||||
errors: ["complement link to invalid complement"],
|
errors: ["complement link to invalid complement"],
|
||||||
ts: entry.ts,
|
ts: entry.ts,
|
||||||
|
@ -190,7 +199,9 @@ function checkForErrors(entries: T.DictionaryEntry[]): T.DictionaryEntryError[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
}, []);
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function findDuplicates(entries: T.DictionaryEntry[]): T.DictionaryEntry[] {
|
// function findDuplicates(entries: T.DictionaryEntry[]): T.DictionaryEntry[] {
|
||||||
|
@ -234,8 +245,14 @@ async function upload(content: Buffer | string, filename: string) {
|
||||||
// ]);
|
// ]);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
async function uploadAllWordsToStoarage(info: T.DictionaryInfo, words: T.PsString[]) {
|
async function uploadAllWordsToStoarage(
|
||||||
await upload(JSON.stringify({ info, words } as T.AllWordsWithInflections), allWordsJsonFilename);
|
info: T.DictionaryInfo,
|
||||||
|
words: T.PsString[]
|
||||||
|
) {
|
||||||
|
await upload(
|
||||||
|
JSON.stringify({ info, words } as T.AllWordsWithInflections),
|
||||||
|
allWordsJsonFilename
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadDictionaryToStorage(dictionary: T.Dictionary) {
|
async function uploadDictionaryToStorage(dictionary: T.Dictionary) {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.15.2",
|
"@fortawesome/fontawesome-free": "^5.15.2",
|
||||||
"@lingdocs/ps-react": "5.10.1",
|
"@lingdocs/ps-react": "6.0.0",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
|
|
|
@ -99,13 +99,23 @@ hr {
|
||||||
background-color: var(--closer) !important;
|
background-color: var(--closer) !important;
|
||||||
color: var(--high-contrast);
|
color: var(--high-contrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-white {
|
.bg-white {
|
||||||
background-color: var(--theme-shade) !important;
|
background-color: var(--theme-shade) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: better handling of modals across light and dark modes */
|
/* TODO: better handling of modals across light and dark modes */
|
||||||
.modal-body, .modal-title {
|
.modal-body,
|
||||||
color:#1d1f25;
|
.modal-title {
|
||||||
|
color: var(--high-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: var(--theme-shade);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content .table {
|
||||||
|
color: var(--high-contrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
|
@ -310,6 +320,7 @@ input {
|
||||||
.entry-suggestion-button {
|
.entry-suggestion-button {
|
||||||
right: 15px;
|
right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.conjugation-search-button {
|
.conjugation-search-button {
|
||||||
right: 15px;
|
right: 15px;
|
||||||
}
|
}
|
||||||
|
@ -339,6 +350,7 @@ input {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--farther);
|
color: var(--farther);
|
||||||
}
|
}
|
||||||
|
|
||||||
.clickable:hover {
|
.clickable:hover {
|
||||||
color: var(--farther);
|
color: var(--farther);
|
||||||
}
|
}
|
||||||
|
@ -380,6 +392,7 @@ input {
|
||||||
width: 10em;
|
width: 10em;
|
||||||
height: 10em;
|
height: 10em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader {
|
.loader {
|
||||||
margin: 60px auto;
|
margin: 60px auto;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
@ -395,24 +408,29 @@ input {
|
||||||
-webkit-animation: load8 1.1s infinite linear;
|
-webkit-animation: load8 1.1s infinite linear;
|
||||||
animation: load8 1.1s infinite linear;
|
animation: load8 1.1s infinite linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes load8 {
|
@-webkit-keyframes load8 {
|
||||||
0% {
|
0% {
|
||||||
-webkit-transform: rotate(0deg);
|
-webkit-transform: rotate(0deg);
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform: rotate(360deg);
|
-webkit-transform: rotate(360deg);
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes load8 {
|
@keyframes load8 {
|
||||||
0% {
|
0% {
|
||||||
-webkit-transform: rotate(0deg);
|
-webkit-transform: rotate(0deg);
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform: rotate(360deg);
|
-webkit-transform: rotate(360deg);
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End of loading animation from https://projects.lukehaas.me/css-loaders/ */
|
/* End of loading animation from https://projects.lukehaas.me/css-loaders/ */
|
|
@ -72,6 +72,7 @@ import PhraseBuilder from "./screens/PhraseBuilder";
|
||||||
import { searchAllInflections } from "./lib/search-all-inflections";
|
import { searchAllInflections } from "./lib/search-all-inflections";
|
||||||
import { addToWordlist } from "./lib/wordlist-database";
|
import { addToWordlist } from "./lib/wordlist-database";
|
||||||
import ScriptToPhonetics from "./screens/ScriptToPhonetics";
|
import ScriptToPhonetics from "./screens/ScriptToPhonetics";
|
||||||
|
import { Modal, Button } from "react-bootstrap";
|
||||||
|
|
||||||
// to allow Moustrap key combos even when input fields are in focus
|
// to allow Moustrap key combos even when input fields are in focus
|
||||||
Mousetrap.prototype.stopCallback = function () {
|
Mousetrap.prototype.stopCallback = function () {
|
||||||
|
@ -107,6 +108,7 @@ class App extends Component<RouteComponentProps, State> {
|
||||||
this.state = {
|
this.state = {
|
||||||
dictionaryStatus: "loading",
|
dictionaryStatus: "loading",
|
||||||
dictionaryInfo: undefined,
|
dictionaryInfo: undefined,
|
||||||
|
showModal: false,
|
||||||
// TODO: Choose between the saved options and the options in the saved user
|
// TODO: Choose between the saved options and the options in the saved user
|
||||||
options: savedOptions
|
options: savedOptions
|
||||||
? savedOptions
|
? savedOptions
|
||||||
|
@ -146,6 +148,8 @@ class App extends Component<RouteComponentProps, State> {
|
||||||
this.handleRefreshReviewTasks = this.handleRefreshReviewTasks.bind(this);
|
this.handleRefreshReviewTasks = this.handleRefreshReviewTasks.bind(this);
|
||||||
this.handleDictionaryUpdate = this.handleDictionaryUpdate.bind(this);
|
this.handleDictionaryUpdate = this.handleDictionaryUpdate.bind(this);
|
||||||
this.handleInflectionSearch = this.handleInflectionSearch.bind(this);
|
this.handleInflectionSearch = this.handleInflectionSearch.bind(this);
|
||||||
|
this.handleShowModal = this.handleShowModal.bind(this);
|
||||||
|
this.handleCloseModal = this.handleCloseModal.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
|
@ -583,6 +587,14 @@ class App extends Component<RouteComponentProps, State> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleCloseModal() {
|
||||||
|
this.setState({ showModal: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleShowModal() {
|
||||||
|
this.setState({ showModal: true });
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -641,7 +653,7 @@ class App extends Component<RouteComponentProps, State> {
|
||||||
>
|
>
|
||||||
<div className="my-4">New words this month</div>
|
<div className="my-4">New words this month</div>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="mt-4 pt-3">
|
<div className="my-4 pt-3">
|
||||||
<Link
|
<Link
|
||||||
to="/phrase-builder"
|
to="/phrase-builder"
|
||||||
className="plain-link h5 font-weight-light"
|
className="plain-link h5 font-weight-light"
|
||||||
|
@ -656,6 +668,12 @@ class App extends Component<RouteComponentProps, State> {
|
||||||
Grammar
|
Grammar
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={this.handleShowModal}
|
||||||
|
className="mt-2 btn btn-lg btn-secondary"
|
||||||
|
>
|
||||||
|
✨ New Phonetics for ی's!! 👀
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/about">
|
<Route path="/about">
|
||||||
|
@ -816,6 +834,87 @@ class App extends Component<RouteComponentProps, State> {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</footer>
|
</footer>
|
||||||
|
<Modal
|
||||||
|
show={this.state.showModal}
|
||||||
|
onHide={this.handleCloseModal}
|
||||||
|
centered
|
||||||
|
>
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>Phonetics Update! 📰</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<p>
|
||||||
|
The phonetics for{" "}
|
||||||
|
<span style={{ backgroundColor: "rgba(255,255,0,0.4)" }}>
|
||||||
|
two of the five ی's have been updated
|
||||||
|
</span>{" "}
|
||||||
|
to something much more logical and helpful for pronunciation.
|
||||||
|
</p>
|
||||||
|
<h5>Pure Vowels (mouth stays still)</h5>
|
||||||
|
<table className="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Letter</th>
|
||||||
|
<th scope="col">Phonetics</th>
|
||||||
|
<th scope="col">Sound</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>ي</td>
|
||||||
|
<td>ee</td>
|
||||||
|
<td>long "ee" like "bee"</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ې</td>
|
||||||
|
<td>e</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
like "ee" but <em>with a slightly more open mouth</em>
|
||||||
|
</div>
|
||||||
|
<div className="small">
|
||||||
|
This is a special vowel <em>not found in English</em>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h5>Dipthongs (pure vowel + y)</h5>
|
||||||
|
<table className="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Letter</th>
|
||||||
|
<th scope="col">Phonetics</th>
|
||||||
|
<th scope="col">Sound</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr style={{ backgroundColor: "rgba(255,255,0,0.4)" }}>
|
||||||
|
<td>ی</td>
|
||||||
|
<td>ay</td>
|
||||||
|
<td>short 'a' + y</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ۍ</td>
|
||||||
|
<td>uy</td>
|
||||||
|
<td>'u' shwa (ə) + y</td>
|
||||||
|
</tr>
|
||||||
|
<tr style={{ backgroundColor: "rgba(255,255,0,0.4)" }}>
|
||||||
|
<td>ئ</td>
|
||||||
|
<td>ey</td>
|
||||||
|
<td>
|
||||||
|
<div>'e' (ې) + y</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button variant="secondary" onClick={this.handleCloseModal}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,11 @@ import { fuzzifyPashto } from "./fuzzify-pashto/fuzzify-pashto";
|
||||||
import relevancy from "relevancy";
|
import relevancy from "relevancy";
|
||||||
import { makeAWeeBitFuzzy } from "./wee-bit-fuzzy";
|
import { makeAWeeBitFuzzy } from "./wee-bit-fuzzy";
|
||||||
import { getTextOptions } from "./get-text-options";
|
import { getTextOptions } from "./get-text-options";
|
||||||
import {
|
import { DictionaryAPI, State } from "../types/dictionary-types";
|
||||||
DictionaryAPI,
|
|
||||||
State,
|
|
||||||
} from "../types/dictionary-types";
|
|
||||||
|
|
||||||
// const dictionaryBaseUrl = "https://storage.googleapis.com/lingdocs/";
|
// const dictionaryBaseUrl = "https://storage.googleapis.com/lingdocs/";
|
||||||
const dictionaryUrl = `https://storage.googleapis.com/lingdocs/dict`;
|
const dictionaryUrl = `https://storage.googleapis.com/lingdocs/dictionary`;
|
||||||
const dictionaryInfoUrl = `https://storage.googleapis.com/lingdocs/dict-info`;
|
const dictionaryInfoUrl = `https://storage.googleapis.com/lingdocs/dictionary-info`;
|
||||||
|
|
||||||
const dictionaryInfoLocalStorageKey = "dictionaryInfo5";
|
const dictionaryInfoLocalStorageKey = "dictionaryInfo5";
|
||||||
const dictionaryCollectionName = "dictionary3";
|
const dictionaryCollectionName = "dictionary3";
|
||||||
|
@ -37,11 +34,13 @@ export const pageSize = 35;
|
||||||
|
|
||||||
const relevancySorter = new relevancy.Sorter();
|
const relevancySorter = new relevancy.Sorter();
|
||||||
|
|
||||||
const db = indexedDB.open('inPrivate');
|
const db = indexedDB.open("inPrivate");
|
||||||
db.onerror = (e) => {
|
db.onerror = (e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert("Your browser does not have IndexedDB enabled. This might be because you are using private mode. Please use regular mode or enable IndexedDB to use this dictionary");
|
alert(
|
||||||
}
|
"Your browser does not have IndexedDB enabled. This might be because you are using private mode. Please use regular mode or enable IndexedDB to use this dictionary"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const dictDb = new DictionaryDb({
|
const dictDb = new DictionaryDb({
|
||||||
url: dictionaryUrl,
|
url: dictionaryUrl,
|
||||||
|
@ -57,7 +56,8 @@ function makeSearchStringSafe(searchString: string): string {
|
||||||
function fuzzifyEnglish(input: string): string {
|
function fuzzifyEnglish(input: string): string {
|
||||||
const safeInput = input.trim().replace(/[#-.]|[[-^]|[?|{}]/g, "");
|
const safeInput = input.trim().replace(/[#-.]|[[-^]|[?|{}]/g, "");
|
||||||
// TODO: Could do: cover british/american things like offense / offence
|
// TODO: Could do: cover british/american things like offense / offence
|
||||||
return safeInput.replace("to ", "")
|
return safeInput
|
||||||
|
.replace("to ", "")
|
||||||
.replace(/our/g, "ou?r")
|
.replace(/our/g, "ou?r")
|
||||||
.replace(/or/g, "ou?r");
|
.replace(/or/g, "ou?r");
|
||||||
}
|
}
|
||||||
|
@ -100,16 +100,18 @@ function tsOneMonthBack(): number {
|
||||||
return d.getTime();
|
return d.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
function alphabeticalLookup({ searchString, page }: {
|
function alphabeticalLookup({
|
||||||
searchString: string,
|
searchString,
|
||||||
page: number,
|
page,
|
||||||
|
}: {
|
||||||
|
searchString: string;
|
||||||
|
page: number;
|
||||||
}): T.DictionaryEntry[] {
|
}): T.DictionaryEntry[] {
|
||||||
const r = new RegExp("^" + sanitizePashto(makeSearchStringSafe(searchString)));
|
const r = new RegExp(
|
||||||
|
"^" + sanitizePashto(makeSearchStringSafe(searchString))
|
||||||
|
);
|
||||||
const regexResults: T.DictionaryEntry[] = dictDb.collection.find({
|
const regexResults: T.DictionaryEntry[] = dictDb.collection.find({
|
||||||
$or: [
|
$or: [{ p: { $regex: r } }, { g: { $regex: r } }],
|
||||||
{p: { $regex: r }},
|
|
||||||
{g: { $regex: r }},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
const indexNumbers = regexResults.map((mpd: any) => mpd.i);
|
const indexNumbers = regexResults.map((mpd: any) => mpd.i);
|
||||||
// Find the first matching word occuring first in the Pashto Index
|
// Find the first matching word occuring first in the Pashto Index
|
||||||
|
@ -119,7 +121,8 @@ function alphabeticalLookup({ searchString, page }: {
|
||||||
}
|
}
|
||||||
// $gt query from that first occurance
|
// $gt query from that first occurance
|
||||||
if (firstIndexNumber !== null) {
|
if (firstIndexNumber !== null) {
|
||||||
return dictDb.collection.chain()
|
return dictDb.collection
|
||||||
|
.chain()
|
||||||
.find({ i: { $gt: firstIndexNumber - 1 } })
|
.find({ i: { $gt: firstIndexNumber - 1 } })
|
||||||
.simplesort("i")
|
.simplesort("i")
|
||||||
.limit(page * pageSize)
|
.limit(page * pageSize)
|
||||||
|
@ -128,32 +131,40 @@ function alphabeticalLookup({ searchString, page }: {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function fuzzyLookup<S extends T.DictionaryEntry>({ searchString, language, page, tpFilter }: {
|
function fuzzyLookup<S extends T.DictionaryEntry>({
|
||||||
searchString: string,
|
searchString,
|
||||||
language: "Pashto" | "English" | "Both",
|
language,
|
||||||
page: number,
|
page,
|
||||||
tpFilter?: (e: T.DictionaryEntry) => e is S,
|
tpFilter,
|
||||||
|
}: {
|
||||||
|
searchString: string;
|
||||||
|
language: "Pashto" | "English" | "Both";
|
||||||
|
page: number;
|
||||||
|
tpFilter?: (e: T.DictionaryEntry) => e is S;
|
||||||
}): S[] {
|
}): S[] {
|
||||||
// TODO: Implement working with both
|
// TODO: Implement working with both
|
||||||
if (Number(searchString)) {
|
if (Number(searchString)) {
|
||||||
const entry = dictionary.findOneByTs(Number(searchString));
|
const entry = dictionary.findOneByTs(Number(searchString));
|
||||||
// @ts-ignore;
|
// @ts-ignore;
|
||||||
return entry ? [entry] : [] as S[];
|
return entry ? [entry] : ([] as S[]);
|
||||||
}
|
}
|
||||||
return language === "Pashto"
|
return language === "Pashto"
|
||||||
? pashtoFuzzyLookup({ searchString, page, tpFilter })
|
? pashtoFuzzyLookup({ searchString, page, tpFilter })
|
||||||
: englishLookup({ searchString, page, tpFilter })
|
: englishLookup({ searchString, page, tpFilter });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function englishLookup<S extends T.DictionaryEntry>({ searchString, page, tpFilter }: {
|
function englishLookup<S extends T.DictionaryEntry>({
|
||||||
searchString: string,
|
searchString,
|
||||||
page: number,
|
page,
|
||||||
tpFilter?: (e: T.DictionaryEntry) => e is S,
|
tpFilter,
|
||||||
|
}: {
|
||||||
|
searchString: string;
|
||||||
|
page: number;
|
||||||
|
tpFilter?: (e: T.DictionaryEntry) => e is S;
|
||||||
}): S[] {
|
}): S[] {
|
||||||
function sortByR(a: T.DictionaryEntry, b: T.DictionaryEntry) {
|
function sortByR(a: T.DictionaryEntry, b: T.DictionaryEntry) {
|
||||||
return (b.r || 3) - (a.r || 3);
|
return (b.r || 3) - (a.r || 3);
|
||||||
};
|
}
|
||||||
let resultsGiven: number[] = [];
|
let resultsGiven: number[] = [];
|
||||||
// get exact results
|
// get exact results
|
||||||
const exactQuery = {
|
const exactQuery = {
|
||||||
|
@ -162,7 +173,8 @@ function englishLookup<S extends T.DictionaryEntry>({ searchString, page, tpFilt
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const exactResultsLimit = pageSize < 10 ? Math.floor(pageSize / 2) : 10;
|
const exactResultsLimit = pageSize < 10 ? Math.floor(pageSize / 2) : 10;
|
||||||
const exactResults = dictDb.collection.chain()
|
const exactResults = dictDb.collection
|
||||||
|
.chain()
|
||||||
.find(exactQuery)
|
.find(exactQuery)
|
||||||
.limit(exactResultsLimit)
|
.limit(exactResultsLimit)
|
||||||
.simplesort("i")
|
.simplesort("i")
|
||||||
|
@ -176,14 +188,18 @@ function englishLookup<S extends T.DictionaryEntry>({ searchString, page, tpFilt
|
||||||
},
|
},
|
||||||
$loki: { $nin: resultsGiven },
|
$loki: { $nin: resultsGiven },
|
||||||
};
|
};
|
||||||
const startingResultsLimit = (pageSize * page) - resultsGiven.length;
|
const startingResultsLimit = pageSize * page - resultsGiven.length;
|
||||||
const startingResults = dictDb.collection.chain()
|
const startingResults = dictDb.collection
|
||||||
|
.chain()
|
||||||
.find(startingQuery)
|
.find(startingQuery)
|
||||||
.limit(startingResultsLimit)
|
.limit(startingResultsLimit)
|
||||||
.simplesort("i")
|
.simplesort("i")
|
||||||
.data();
|
.data();
|
||||||
startingResults.sort(sortByR);
|
startingResults.sort(sortByR);
|
||||||
resultsGiven = [...resultsGiven, ...startingResults.map((mpd: any) => mpd.$loki)];
|
resultsGiven = [
|
||||||
|
...resultsGiven,
|
||||||
|
...startingResults.map((mpd: any) => mpd.$loki),
|
||||||
|
];
|
||||||
// get results with full word match anywhere
|
// get results with full word match anywhere
|
||||||
const fullWordQuery = {
|
const fullWordQuery = {
|
||||||
e: {
|
e: {
|
||||||
|
@ -191,14 +207,18 @@ function englishLookup<S extends T.DictionaryEntry>({ searchString, page, tpFilt
|
||||||
},
|
},
|
||||||
$loki: { $nin: resultsGiven },
|
$loki: { $nin: resultsGiven },
|
||||||
};
|
};
|
||||||
const fullWordResultsLimit = (pageSize * page) - resultsGiven.length;
|
const fullWordResultsLimit = pageSize * page - resultsGiven.length;
|
||||||
const fullWordResults = dictDb.collection.chain()
|
const fullWordResults = dictDb.collection
|
||||||
|
.chain()
|
||||||
.find(fullWordQuery)
|
.find(fullWordQuery)
|
||||||
.limit(fullWordResultsLimit)
|
.limit(fullWordResultsLimit)
|
||||||
.simplesort("i")
|
.simplesort("i")
|
||||||
.data();
|
.data();
|
||||||
fullWordResults.sort(sortByR);
|
fullWordResults.sort(sortByR);
|
||||||
resultsGiven = [...resultsGiven, ...fullWordResults.map((mpd: any) => mpd.$loki)]
|
resultsGiven = [
|
||||||
|
...resultsGiven,
|
||||||
|
...fullWordResults.map((mpd: any) => mpd.$loki),
|
||||||
|
];
|
||||||
// get results with partial match anywhere
|
// get results with partial match anywhere
|
||||||
const partialMatchQuery = {
|
const partialMatchQuery = {
|
||||||
e: {
|
e: {
|
||||||
|
@ -206,8 +226,9 @@ function englishLookup<S extends T.DictionaryEntry>({ searchString, page, tpFilt
|
||||||
},
|
},
|
||||||
$loki: { $nin: resultsGiven },
|
$loki: { $nin: resultsGiven },
|
||||||
};
|
};
|
||||||
const partialMatchLimit = (pageSize * page) - resultsGiven.length;
|
const partialMatchLimit = pageSize * page - resultsGiven.length;
|
||||||
const partialMatchResults = dictDb.collection.chain()
|
const partialMatchResults = dictDb.collection
|
||||||
|
.chain()
|
||||||
.where(tpFilter ? tpFilter : () => true)
|
.where(tpFilter ? tpFilter : () => true)
|
||||||
.find(partialMatchQuery)
|
.find(partialMatchQuery)
|
||||||
.limit(partialMatchLimit)
|
.limit(partialMatchLimit)
|
||||||
|
@ -234,16 +255,21 @@ function pashtoExactLookup(searchString: string): T.DictionaryEntry[] {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function pashtoFuzzyLookup<S extends T.DictionaryEntry>({ searchString, page, tpFilter }: {
|
function pashtoFuzzyLookup<S extends T.DictionaryEntry>({
|
||||||
searchString: string,
|
searchString,
|
||||||
page: number,
|
page,
|
||||||
tpFilter?: (e: T.DictionaryEntry) => e is S,
|
tpFilter,
|
||||||
|
}: {
|
||||||
|
searchString: string;
|
||||||
|
page: number;
|
||||||
|
tpFilter?: (e: T.DictionaryEntry) => e is S;
|
||||||
}): S[] {
|
}): S[] {
|
||||||
let resultsGiven: number[] = [];
|
let resultsGiven: number[] = [];
|
||||||
// Check if it's in Pashto or Latin script
|
// Check if it's in Pashto or Latin script
|
||||||
const searchStringToUse = sanitizePashto(makeSearchStringSafe(searchString));
|
const searchStringToUse = sanitizePashto(makeSearchStringSafe(searchString));
|
||||||
const index = isPashtoScript(searchStringToUse) ? "p" : "g";
|
const index = isPashtoScript(searchStringToUse) ? "p" : "g";
|
||||||
const search = index === "g" ? simplifyPhonetics(searchStringToUse) : searchStringToUse;
|
const search =
|
||||||
|
index === "g" ? simplifyPhonetics(searchStringToUse) : searchStringToUse;
|
||||||
const infIndex = index === "p" ? "p" : "f";
|
const infIndex = index === "p" ? "p" : "f";
|
||||||
// Get exact matches
|
// Get exact matches
|
||||||
const exactExpression = new RegExp("^" + search);
|
const exactExpression = new RegExp("^" + search);
|
||||||
|
@ -259,11 +285,14 @@ function pashtoFuzzyLookup<S extends T.DictionaryEntry>({ searchString, page, tp
|
||||||
const pashtoExactResultFields = [
|
const pashtoExactResultFields = [
|
||||||
{
|
{
|
||||||
[index]: { $regex: exactExpression },
|
[index]: { $regex: exactExpression },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
[arabicPluralIndex]: { $regex: weeBitFuzzy },
|
[arabicPluralIndex]: { $regex: weeBitFuzzy },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
[pashtoPluralIndex]: { $regex: weeBitFuzzy },
|
[pashtoPluralIndex]: { $regex: weeBitFuzzy },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
[presentStemIndex]: { $regex: weeBitFuzzy },
|
[presentStemIndex]: { $regex: weeBitFuzzy },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -277,7 +306,8 @@ function pashtoFuzzyLookup<S extends T.DictionaryEntry>({ searchString, page, tp
|
||||||
// just special incase using really small limits
|
// just special incase using really small limits
|
||||||
// multiple times scrolling / chunking / sorting might get a bit messed up if using a limit of less than 10
|
// multiple times scrolling / chunking / sorting might get a bit messed up if using a limit of less than 10
|
||||||
const exactResultsLimit = pageSize < 10 ? Math.floor(pageSize / 2) : 10;
|
const exactResultsLimit = pageSize < 10 ? Math.floor(pageSize / 2) : 10;
|
||||||
const exactResults = dictDb.collection.chain()
|
const exactResults = dictDb.collection
|
||||||
|
.chain()
|
||||||
.find(exactQuery)
|
.find(exactQuery)
|
||||||
.limit(exactResultsLimit)
|
.limit(exactResultsLimit)
|
||||||
.simplesort("i")
|
.simplesort("i")
|
||||||
|
@ -289,8 +319,9 @@ function pashtoFuzzyLookup<S extends T.DictionaryEntry>({ searchString, page, tp
|
||||||
[index]: { $regex: slightlyFuzzy },
|
[index]: { $regex: slightlyFuzzy },
|
||||||
$loki: { $nin: resultsGiven },
|
$loki: { $nin: resultsGiven },
|
||||||
};
|
};
|
||||||
const slightlyFuzzyResultsLimit = (pageSize * page) - resultsGiven.length;
|
const slightlyFuzzyResultsLimit = pageSize * page - resultsGiven.length;
|
||||||
const slightlyFuzzyResults = dictDb.collection.chain()
|
const slightlyFuzzyResults = dictDb.collection
|
||||||
|
.chain()
|
||||||
.find(slightlyFuzzyQuery)
|
.find(slightlyFuzzyQuery)
|
||||||
.limit(slightlyFuzzyResultsLimit)
|
.limit(slightlyFuzzyResultsLimit)
|
||||||
.data();
|
.data();
|
||||||
|
@ -306,48 +337,57 @@ function pashtoFuzzyLookup<S extends T.DictionaryEntry>({ searchString, page, tp
|
||||||
const pashtoFuzzyQuery = [
|
const pashtoFuzzyQuery = [
|
||||||
{
|
{
|
||||||
[index]: { $regex: fuzzyPashtoExperssion },
|
[index]: { $regex: fuzzyPashtoExperssion },
|
||||||
}, { // TODO: Issue, this fuzzy doesn't line up well because it's not the simplified phonetics - still has 's etc
|
},
|
||||||
|
{
|
||||||
|
// TODO: Issue, this fuzzy doesn't line up well because it's not the simplified phonetics - still has 's etc
|
||||||
[arabicPluralIndex]: { $regex: fuzzyPashtoExperssion },
|
[arabicPluralIndex]: { $regex: fuzzyPashtoExperssion },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
[presentStemIndex]: { $regex: fuzzyPashtoExperssion },
|
[presentStemIndex]: { $regex: fuzzyPashtoExperssion },
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
// fuzzy results should be allowed to take up the rest of the limit (not used up by exact results)
|
// fuzzy results should be allowed to take up the rest of the limit (not used up by exact results)
|
||||||
const fuzzyResultsLimit = (pageSize * page) - resultsGiven.length;
|
const fuzzyResultsLimit = pageSize * page - resultsGiven.length;
|
||||||
// don't get these fuzzy results if searching in only English
|
// don't get these fuzzy results if searching in only English
|
||||||
const fuzzyQuery = {
|
const fuzzyQuery = {
|
||||||
$or: pashtoFuzzyQuery,
|
$or: pashtoFuzzyQuery,
|
||||||
$loki: { $nin: resultsGiven },
|
$loki: { $nin: resultsGiven },
|
||||||
};
|
};
|
||||||
const fuzzyResults = dictDb.collection.chain()
|
const fuzzyResults = dictDb.collection
|
||||||
|
.chain()
|
||||||
.find(fuzzyQuery)
|
.find(fuzzyQuery)
|
||||||
.limit(fuzzyResultsLimit)
|
.limit(fuzzyResultsLimit)
|
||||||
.data();
|
.data();
|
||||||
const results = tpFilter
|
const results = tpFilter
|
||||||
? [...exactResults, ...slightlyFuzzyResults, ...fuzzyResults].filter(tpFilter)
|
? [...exactResults, ...slightlyFuzzyResults, ...fuzzyResults].filter(
|
||||||
|
tpFilter
|
||||||
|
)
|
||||||
: [...exactResults, ...slightlyFuzzyResults, ...fuzzyResults];
|
: [...exactResults, ...slightlyFuzzyResults, ...fuzzyResults];
|
||||||
// sort out each chunk (based on limit used multiple times by infinite scroll)
|
// sort out each chunk (based on limit used multiple times by infinite scroll)
|
||||||
// so that when infinite scrolling, it doesn't re-sort the previous chunks given
|
// so that when infinite scrolling, it doesn't re-sort the previous chunks given
|
||||||
const closeResultsLength = exactResults.length + slightlyFuzzyResults.length;
|
const closeResultsLength = exactResults.length + slightlyFuzzyResults.length;
|
||||||
const chunksToSort = chunkOutArray(results, pageSize);
|
const chunksToSort = chunkOutArray(results, pageSize);
|
||||||
return chunksToSort
|
return chunksToSort.reduce(
|
||||||
.reduce((acc, cur, i) => ((i === 0)
|
(acc, cur, i) =>
|
||||||
|
i === 0
|
||||||
? [
|
? [
|
||||||
...sortByRelevancy(cur.slice(0, closeResultsLength), search, index),
|
...sortByRelevancy(cur.slice(0, closeResultsLength), search, index),
|
||||||
...sortByRelevancy(cur.slice(closeResultsLength), search, index),
|
...sortByRelevancy(cur.slice(closeResultsLength), search, index),
|
||||||
]
|
]
|
||||||
: [
|
: [...acc, ...sortByRelevancy(cur, search, index)],
|
||||||
...acc,
|
[]
|
||||||
...sortByRelevancy(cur, search, index),
|
);
|
||||||
]), []);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortByRelevancy<T>(arr: T[], searchI: string, index: string): T[] {
|
function sortByRelevancy<T>(arr: T[], searchI: string, index: string): T[] {
|
||||||
return relevancySorter.sort(arr, searchI, (obj: any, calc: any) => calc(obj[index]));
|
return relevancySorter.sort(arr, searchI, (obj: any, calc: any) =>
|
||||||
|
calc(obj[index])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function relatedWordsLookup(word: T.DictionaryEntry): T.DictionaryEntry[] {
|
function relatedWordsLookup(word: T.DictionaryEntry): T.DictionaryEntry[] {
|
||||||
const wordArray = word.e.trim()
|
const wordArray = word.e
|
||||||
|
.trim()
|
||||||
.replace(/\?/g, "")
|
.replace(/\?/g, "")
|
||||||
.replace(/( |,|\.|!|;|\(|\))/g, " ")
|
.replace(/( |,|\.|!|;|\(|\))/g, " ")
|
||||||
.split(/ +/)
|
.split(/ +/)
|
||||||
|
@ -357,7 +397,8 @@ function relatedWordsLookup(word: T.DictionaryEntry): T.DictionaryEntry[] {
|
||||||
let r: RegExp;
|
let r: RegExp;
|
||||||
try {
|
try {
|
||||||
r = new RegExp(`\\b${w}\\b`, "i");
|
r = new RegExp(`\\b${w}\\b`, "i");
|
||||||
const relatedToWord = dictDb.collection.chain()
|
const relatedToWord = dictDb.collection
|
||||||
|
.chain()
|
||||||
.find({
|
.find({
|
||||||
// don't include the original word
|
// don't include the original word
|
||||||
ts: { $ne: word.ts },
|
ts: { $ne: word.ts },
|
||||||
|
@ -377,16 +418,19 @@ function relatedWordsLookup(word: T.DictionaryEntry): T.DictionaryEntry[] {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return !this[a.$loki] && (this[a.$loki] = true);
|
return !this[a.$loki] && (this[a.$loki] = true);
|
||||||
}, Object.create(null));
|
}, Object.create(null));
|
||||||
return(results);
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function allEntries() {
|
export function allEntries() {
|
||||||
return dictDb.collection.find();
|
return dictDb.collection.find();
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeLookupPortal<X extends T.DictionaryEntry>(tpFilter: (x: T.DictionaryEntry) => x is X): T.EntryLookupPortal<X> {
|
function makeLookupPortal<X extends T.DictionaryEntry>(
|
||||||
|
tpFilter: (x: T.DictionaryEntry) => x is X
|
||||||
|
): T.EntryLookupPortal<X> {
|
||||||
return {
|
return {
|
||||||
search: (s: string) => fuzzyLookup({
|
search: (s: string) =>
|
||||||
|
fuzzyLookup({
|
||||||
searchString: s,
|
searchString: s,
|
||||||
language: "Pashto",
|
language: "Pashto",
|
||||||
page: 1,
|
page: 1,
|
||||||
|
@ -397,7 +441,7 @@ function makeLookupPortal<X extends T.DictionaryEntry>(tpFilter: (x: T.Dictionar
|
||||||
if (!res) return undefined;
|
if (!res) return undefined;
|
||||||
return tpFilter(res) ? res : undefined;
|
return tpFilter(res) ? res : undefined;
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeVerbLookupPortal(): T.EntryLookupPortal<T.VerbEntry> {
|
function makeVerbLookupPortal(): T.EntryLookupPortal<T.VerbEntry> {
|
||||||
|
@ -409,12 +453,15 @@ function makeVerbLookupPortal(): T.EntryLookupPortal<T.VerbEntry> {
|
||||||
page: 1,
|
page: 1,
|
||||||
tpFilter: tp.isVerbDictionaryEntry,
|
tpFilter: tp.isVerbDictionaryEntry,
|
||||||
});
|
});
|
||||||
return vEntries.map((entry): T.VerbEntry => ({
|
return vEntries.map(
|
||||||
|
(entry): T.VerbEntry => ({
|
||||||
entry,
|
entry,
|
||||||
complement: (entry.c?.includes("comp.") && entry.l)
|
complement:
|
||||||
|
entry.c?.includes("comp.") && entry.l
|
||||||
? dictionary.findOneByTs(entry.l)
|
? dictionary.findOneByTs(entry.l)
|
||||||
: undefined,
|
: undefined,
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getByTs: (ts: number): T.VerbEntry | undefined => {
|
getByTs: (ts: number): T.VerbEntry | undefined => {
|
||||||
const entry = dictDb.findOneByTs(ts);
|
const entry = dictDb.findOneByTs(ts);
|
||||||
|
@ -436,7 +483,7 @@ function makeVerbLookupPortal(): T.EntryLookupPortal<T.VerbEntry> {
|
||||||
})();
|
})();
|
||||||
return { entry, complement };
|
return { entry, complement };
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const entryFeeder: T.EntryFeeder = {
|
export const entryFeeder: T.EntryFeeder = {
|
||||||
|
@ -445,26 +492,28 @@ export const entryFeeder: T.EntryFeeder = {
|
||||||
adjectives: makeLookupPortal(tp.isAdjectiveEntry),
|
adjectives: makeLookupPortal(tp.isAdjectiveEntry),
|
||||||
locativeAdverbs: makeLookupPortal(tp.isLocativeAdverbEntry),
|
locativeAdverbs: makeLookupPortal(tp.isLocativeAdverbEntry),
|
||||||
adverbs: makeLookupPortal(tp.isAdverbEntry),
|
adverbs: makeLookupPortal(tp.isAdverbEntry),
|
||||||
}
|
};
|
||||||
|
|
||||||
export const dictionary: DictionaryAPI = {
|
export const dictionary: DictionaryAPI = {
|
||||||
// NOTE: For some reason that I do not understand you have to pass the functions from the
|
// NOTE: For some reason that I do not understand you have to pass the functions from the
|
||||||
// dictionary core class in like this... ie. initialize: dictDb.initialize will mess up the this usage
|
// dictionary core class in like this... ie. initialize: dictDb.initialize will mess up the this usage
|
||||||
// in the dictionary core class
|
// in the dictionary core class
|
||||||
initialize: async () => await dictDb.initialize(),
|
initialize: async () => await dictDb.initialize(),
|
||||||
update: async (notifyUpdateComing: () => void) => await dictDb.updateDictionary(notifyUpdateComing),
|
update: async (notifyUpdateComing: () => void) =>
|
||||||
|
await dictDb.updateDictionary(notifyUpdateComing),
|
||||||
search: function (state: State): T.DictionaryEntry[] {
|
search: function (state: State): T.DictionaryEntry[] {
|
||||||
const searchString = revertSpelling(
|
const searchString = revertSpelling(
|
||||||
state.searchValue,
|
state.searchValue,
|
||||||
getTextOptions(state).spelling,
|
getTextOptions(state).spelling
|
||||||
);
|
);
|
||||||
if (state.searchValue === "") {
|
if (state.searchValue === "") {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return (state.options.searchType === "alphabetical" && state.options.language === "Pashto")
|
return state.options.searchType === "alphabetical" &&
|
||||||
|
state.options.language === "Pashto"
|
||||||
? alphabeticalLookup({
|
? alphabeticalLookup({
|
||||||
searchString,
|
searchString,
|
||||||
page: state.page
|
page: state.page,
|
||||||
})
|
})
|
||||||
: fuzzyLookup({
|
: fuzzyLookup({
|
||||||
searchString,
|
searchString,
|
||||||
|
@ -474,7 +523,8 @@ export const dictionary: DictionaryAPI = {
|
||||||
},
|
},
|
||||||
exactPashtoSearch: pashtoExactLookup,
|
exactPashtoSearch: pashtoExactLookup,
|
||||||
getNewWordsThisMonth: function (): T.DictionaryEntry[] {
|
getNewWordsThisMonth: function (): T.DictionaryEntry[] {
|
||||||
return dictDb.collection.chain()
|
return dictDb.collection
|
||||||
|
.chain()
|
||||||
.find({ ts: { $gt: tsOneMonthBack() } })
|
.find({ ts: { $gt: tsOneMonthBack() } })
|
||||||
.simplesort("ts")
|
.simplesort("ts")
|
||||||
.data()
|
.data()
|
||||||
|
@ -484,4 +534,4 @@ export const dictionary: DictionaryAPI = {
|
||||||
findRelatedEntries: function (entry: T.DictionaryEntry): T.DictionaryEntry[] {
|
findRelatedEntries: function (entry: T.DictionaryEntry): T.DictionaryEntry[] {
|
||||||
return relatedWordsLookup(entry);
|
return relatedWordsLookup(entry);
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
|
@ -78,7 +78,7 @@ const defaultLatinInfo: IDefaultInfoBlock = {
|
||||||
// TODO:
|
// TODO:
|
||||||
["anbiya", "ambiya"],
|
["anbiya", "ambiya"],
|
||||||
["lootfun", "lUtfan"],
|
["lootfun", "lUtfan"],
|
||||||
["sarey", "saRey"],
|
["saray", "saRay"],
|
||||||
["senga", "tsanga"],
|
["senga", "tsanga"],
|
||||||
["daktur", "DakTar"],
|
["daktur", "DakTar"],
|
||||||
["iteebar", "itibaar"],
|
["iteebar", "itibaar"],
|
||||||
|
@ -87,9 +87,9 @@ const defaultLatinInfo: IDefaultInfoBlock = {
|
||||||
["bekár", "bekaar"],
|
["bekár", "bekaar"],
|
||||||
["chaai", "cháai"],
|
["chaai", "cháai"],
|
||||||
["day", "daai"],
|
["day", "daai"],
|
||||||
["dai", "dey"],
|
["dai", "day"],
|
||||||
["daktar", "Daktár"],
|
["daktar", "Daktár"],
|
||||||
["sarái", "saRey"],
|
["sarái", "saRay"],
|
||||||
["beter", "bahtár"],
|
["beter", "bahtár"],
|
||||||
["doosti", "dostee"],
|
["doosti", "dostee"],
|
||||||
["dắraghlum", "deraghlum"], // using the ă along with a combining ́
|
["dắraghlum", "deraghlum"], // using the ă along with a combining ́
|
||||||
|
@ -154,7 +154,7 @@ const defaultSimpleLatinInfo: IDefaultInfoBlock = {
|
||||||
// TODO:
|
// TODO:
|
||||||
["anbiya", "ambiya"],
|
["anbiya", "ambiya"],
|
||||||
["lootfun", "lUtfan"],
|
["lootfun", "lUtfan"],
|
||||||
["sarey", "saRey"],
|
["saray", "saRay"],
|
||||||
["senga", "tsanga"],
|
["senga", "tsanga"],
|
||||||
["daktur", "DakTar"],
|
["daktur", "DakTar"],
|
||||||
["iteebar", "itibaar"],
|
["iteebar", "itibaar"],
|
||||||
|
@ -163,9 +163,9 @@ const defaultSimpleLatinInfo: IDefaultInfoBlock = {
|
||||||
["bekar", "bekaar"],
|
["bekar", "bekaar"],
|
||||||
["chaai", "chaai"],
|
["chaai", "chaai"],
|
||||||
["day", "daai"],
|
["day", "daai"],
|
||||||
["dai", "dey"],
|
["dai", "day"],
|
||||||
["daktar", "Daktar"],
|
["daktar", "Daktar"],
|
||||||
["sarai", "saRey"],
|
["sarai", "saRay"],
|
||||||
["beter", "bahtar"],
|
["beter", "bahtar"],
|
||||||
["doosti", "dostee"],
|
["doosti", "dostee"],
|
||||||
["daraghlum", "deraghlum"], // using the ă along with a combining ́
|
["daraghlum", "deraghlum"], // using the ă along with a combining ́
|
||||||
|
@ -221,17 +221,13 @@ const optionsPossibilities: ITestOptions[] = [
|
||||||
viceVersaMatches: true,
|
viceVersaMatches: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
matches: [
|
matches: [...matchesWithSpaces],
|
||||||
...matchesWithSpaces,
|
|
||||||
],
|
|
||||||
nonMatches: [],
|
nonMatches: [],
|
||||||
options: { allowSpacesInWords: true },
|
options: { allowSpacesInWords: true },
|
||||||
viceVersaMatches: true,
|
viceVersaMatches: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
matches: [
|
matches: [...matchesWithSpacesLatin],
|
||||||
...matchesWithSpacesLatin,
|
|
||||||
],
|
|
||||||
nonMatches: [],
|
nonMatches: [],
|
||||||
options: { allowSpacesInWords: true, script: "Latin" },
|
options: { allowSpacesInWords: true, script: "Latin" },
|
||||||
viceVersaMatches: true,
|
viceVersaMatches: true,
|
||||||
|
@ -247,12 +243,8 @@ const optionsPossibilities: ITestOptions[] = [
|
||||||
options: { allowSpacesInWords: false, script: "Latin" },
|
options: { allowSpacesInWords: false, script: "Latin" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
matches: [
|
matches: [["کار", "بېکاري"]],
|
||||||
["کار", "بېکاري"],
|
nonMatches: [["سرک", "بېترک"]],
|
||||||
],
|
|
||||||
nonMatches: [
|
|
||||||
["سرک", "بېترک"],
|
|
||||||
],
|
|
||||||
options: { matchStart: "anywhere" },
|
options: { matchStart: "anywhere" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -292,7 +284,26 @@ const optionsPossibilities: ITestOptions[] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const punctuationToExclude = [
|
const punctuationToExclude = [
|
||||||
"،", "؟", "؛", "۔", "۲", "۹", "۰", "»", "«", "٫", "!", ".", "؋", "٪", "٬", "×", ")", "(", " ", "\t",
|
"،",
|
||||||
|
"؟",
|
||||||
|
"؛",
|
||||||
|
"۔",
|
||||||
|
"۲",
|
||||||
|
"۹",
|
||||||
|
"۰",
|
||||||
|
"»",
|
||||||
|
"«",
|
||||||
|
"٫",
|
||||||
|
"!",
|
||||||
|
".",
|
||||||
|
"؋",
|
||||||
|
"٪",
|
||||||
|
"٬",
|
||||||
|
"×",
|
||||||
|
")",
|
||||||
|
"(",
|
||||||
|
" ",
|
||||||
|
"\t",
|
||||||
];
|
];
|
||||||
|
|
||||||
optionsPossibilities.forEach((o) => {
|
optionsPossibilities.forEach((o) => {
|
||||||
|
@ -362,7 +373,10 @@ test(`وs should be optional if entered in search string`, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`matchWholeWordOnly should override matchStart = "anywhere"`, () => {
|
test(`matchWholeWordOnly should override matchStart = "anywhere"`, () => {
|
||||||
const re = fuzzifyPashto("کار", { matchWholeWordOnly: true, matchStart: "anywhere" });
|
const re = fuzzifyPashto("کار", {
|
||||||
|
matchWholeWordOnly: true,
|
||||||
|
matchStart: "anywhere",
|
||||||
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const result = "کار کوه، بېکاره مه ګرځه".match(new RegExp(re));
|
const result = "کار کوه، بېکاره مه ګرځه".match(new RegExp(re));
|
||||||
expect(result).toHaveLength(1);
|
expect(result).toHaveLength(1);
|
||||||
|
@ -382,14 +396,19 @@ test(`returnWholeWord should return the whole word`, () => {
|
||||||
script: "Latin",
|
script: "Latin",
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const resultLatin = "kaaroona kawa, bekaara ma gurdza.".match(new RegExp(reLatin));
|
const resultLatin = "kaaroona kawa, bekaara ma gurdza.".match(
|
||||||
|
new RegExp(reLatin)
|
||||||
|
);
|
||||||
expect(resultLatin).toHaveLength(1);
|
expect(resultLatin).toHaveLength(1);
|
||||||
expect(resultLatin).toContain("kaaroona");
|
expect(resultLatin).toContain("kaaroona");
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`returnWholeWord should return the whole word even when starting the matching in the middle`, () => {
|
test(`returnWholeWord should return the whole word even when starting the matching in the middle`, () => {
|
||||||
// With Pashto Script
|
// With Pashto Script
|
||||||
const re = fuzzifyPashto("کار", { returnWholeWord: true, matchStart: "anywhere" });
|
const re = fuzzifyPashto("کار", {
|
||||||
|
returnWholeWord: true,
|
||||||
|
matchStart: "anywhere",
|
||||||
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const result = "کارونه کوه، بېکاره مه شه".match(new RegExp(re, "g"));
|
const result = "کارونه کوه، بېکاره مه شه".match(new RegExp(re, "g"));
|
||||||
expect(result).toHaveLength(2);
|
expect(result).toHaveLength(2);
|
||||||
|
@ -402,14 +421,20 @@ test(`returnWholeWord should return the whole word even when starting the matchi
|
||||||
script: "Latin",
|
script: "Latin",
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const resultLatin = "kaaroona kawa bekaara ma gurdza".match(new RegExp(reLatin, "g"));
|
const resultLatin = "kaaroona kawa bekaara ma gurdza".match(
|
||||||
|
new RegExp(reLatin, "g")
|
||||||
|
);
|
||||||
expect(resultLatin).toHaveLength(2);
|
expect(resultLatin).toHaveLength(2);
|
||||||
expect(resultLatin).toContain("bekaara");
|
expect(resultLatin).toContain("bekaara");
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`returnWholeWord should should not return partial matches if matchWholeWordOnly is true`, () => {
|
test(`returnWholeWord should should not return partial matches if matchWholeWordOnly is true`, () => {
|
||||||
// With Pashto Script
|
// With Pashto Script
|
||||||
const re = fuzzifyPashto("کار", { returnWholeWord: true, matchStart: "anywhere", matchWholeWordOnly: true });
|
const re = fuzzifyPashto("کار", {
|
||||||
|
returnWholeWord: true,
|
||||||
|
matchStart: "anywhere",
|
||||||
|
matchWholeWordOnly: true,
|
||||||
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const result = "کارونه کوه، بېکاره مه ګرځه".match(new RegExp(re));
|
const result = "کارونه کوه، بېکاره مه ګرځه".match(new RegExp(re));
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
|
@ -422,13 +447,18 @@ test(`returnWholeWord should should not return partial matches if matchWholeWord
|
||||||
script: "Latin",
|
script: "Latin",
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const resultLatin = "kaaroona kawa bekaara ma gurdza".match(new RegExp(reLatin));
|
const resultLatin = "kaaroona kawa bekaara ma gurdza".match(
|
||||||
|
new RegExp(reLatin)
|
||||||
|
);
|
||||||
expect(resultLatin).toBeNull();
|
expect(resultLatin).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
punctuationToExclude.forEach((m) => {
|
punctuationToExclude.forEach((m) => {
|
||||||
test(`${m} should not be considered part of a Pashto word`, () => {
|
test(`${m} should not be considered part of a Pashto word`, () => {
|
||||||
const re = fuzzifyPashto("کور", { returnWholeWord: true, matchStart: "word" });
|
const re = fuzzifyPashto("کور", {
|
||||||
|
returnWholeWord: true,
|
||||||
|
matchStart: "word",
|
||||||
|
});
|
||||||
// ISSUE: This should also work when the word is PRECEDED by the punctuation
|
// ISSUE: This should also work when the word is PRECEDED by the punctuation
|
||||||
// Need to work with a lookbehind equivalent
|
// Need to work with a lookbehind equivalent
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
@ -446,7 +476,11 @@ punctuationToExclude.forEach((m) => {
|
||||||
let failed = false;
|
let failed = false;
|
||||||
// if environment is not es2018 with lookbehind support (like node 6, 8) this will fail
|
// if environment is not es2018 with lookbehind support (like node 6, 8) this will fail
|
||||||
try {
|
try {
|
||||||
const re = fuzzifyPashto("کور", { returnWholeWord: true, matchStart: "word", es2018: true });
|
const re = fuzzifyPashto("کور", {
|
||||||
|
returnWholeWord: true,
|
||||||
|
matchStart: "word",
|
||||||
|
es2018: true,
|
||||||
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
result = `زمونږ ${m}کورونه${m} دي`.match(new RegExp(re));
|
result = `زمونږ ${m}کورونه${m} دي`.match(new RegExp(re));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ const velarPlosives = "ګغږکقگك";
|
||||||
const rLikeSounds = "رړڑڼ";
|
const rLikeSounds = "رړڑڼ";
|
||||||
const labialPlosivesAndFricatives = "فپب";
|
const labialPlosivesAndFricatives = "فپب";
|
||||||
// Includes Arabic ى \u0649
|
// Includes Arabic ى \u0649
|
||||||
const theFiveYeys = "ېۍیيئےى";
|
const theFiveYays = "ېۍیيئےى";
|
||||||
const guttural = "ښخشخهحغګ";
|
const guttural = "ښخشخهحغګ";
|
||||||
|
|
||||||
interface IReplacerInfoItem {
|
interface IReplacerInfoItem {
|
||||||
|
@ -38,7 +38,6 @@ const ghzCombo = ["غز", "زغ"];
|
||||||
const pxCombo = ["پښ", "ښپ"];
|
const pxCombo = ["پښ", "ښپ"];
|
||||||
const kshCombo = ["کش", "شک", "کښ", "کش"];
|
const kshCombo = ["کش", "شک", "کښ", "کش"];
|
||||||
|
|
||||||
|
|
||||||
export const pashtoReplacerInfo: IPashtoReplacerInfoItem[] = [
|
export const pashtoReplacerInfo: IPashtoReplacerInfoItem[] = [
|
||||||
{ char: "اً", range: "ان" },
|
{ char: "اً", range: "ان" },
|
||||||
{
|
{
|
||||||
|
@ -54,15 +53,25 @@ export const pashtoReplacerInfo: IPashtoReplacerInfoItem[] = [
|
||||||
{ char: "ٳ", range: "اآهأ" },
|
{ char: "ٳ", range: "اآهأ" },
|
||||||
{ char: "یٰ", range: "ای", plus: ["یٰ"] },
|
{ char: "یٰ", range: "ای", plus: ["یٰ"] },
|
||||||
|
|
||||||
{ char: "ی", range: theFiveYeys, plus: ["ئی", "ئي", "یٰ"], ignorableIfInMiddle: true },
|
{
|
||||||
{ char: "ي", range: theFiveYeys, plus: ["ئی", "ئي", "یٰ"], ignorableIfInMiddle: true },
|
char: "ی",
|
||||||
{ char: "ې", range: theFiveYeys, ignorableIfInMiddle: true },
|
range: theFiveYays,
|
||||||
{ char: "ۍ", range: theFiveYeys },
|
plus: ["ئی", "ئي", "یٰ"],
|
||||||
{ char: "ئي", range: theFiveYeys, plus: ["ئی", "ئي"] },
|
ignorableIfInMiddle: true,
|
||||||
{ char: "ئی", range: theFiveYeys, plus: ["ئی", "ئي"] },
|
},
|
||||||
{ char: "ئے", range: theFiveYeys, plus: ["ئی", "ئي", "يې"]},
|
{
|
||||||
{ char: "ئ", range: theFiveYeys, ignorableIfInMiddle: true },
|
char: "ي",
|
||||||
{ char: "ے", range: theFiveYeys },
|
range: theFiveYays,
|
||||||
|
plus: ["ئی", "ئي", "یٰ"],
|
||||||
|
ignorableIfInMiddle: true,
|
||||||
|
},
|
||||||
|
{ char: "ې", range: theFiveYays, ignorableIfInMiddle: true },
|
||||||
|
{ char: "ۍ", range: theFiveYays },
|
||||||
|
{ char: "ئي", range: theFiveYays, plus: ["ئی", "ئي"] },
|
||||||
|
{ char: "ئی", range: theFiveYays, plus: ["ئی", "ئي"] },
|
||||||
|
{ char: "ئے", range: theFiveYays, plus: ["ئی", "ئي", "يې"] },
|
||||||
|
{ char: "ئ", range: theFiveYays, ignorableIfInMiddle: true },
|
||||||
|
{ char: "ے", range: theFiveYays },
|
||||||
|
|
||||||
{ char: "س", range: sSounds },
|
{ char: "س", range: sSounds },
|
||||||
{ char: "ص", range: sSounds },
|
{ char: "ص", range: sSounds },
|
||||||
|
@ -133,11 +142,12 @@ export const pashtoReplacerInfo: IPashtoReplacerInfoItem[] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
export const pashtoReplacerRegex = /اً|أ|ا|آ|ٱ|ٲ|ٳ|ئی|ئي|ئے|یٰ|ی|ي|ې|ۍ|ئ|ے|س|ص|ث|څ|ج|چ|هٔ|ه|ۀ|غز|زغ|کش|شک|ښک|ښک|پښ|ښپ|ہ|ع|و|ؤ|ښ|غ|خ|ح|ش|ز|ض|ذ|ځ|ظ|ژ|ر|ړ|ڑ|ت|ټ|ٹ|ط|د|ډ|ڈ|مب|م|نب|ن|ڼ|ک|ګ|گ|ل|ق|ږ|ب|پ|ف/g;
|
export const pashtoReplacerRegex =
|
||||||
|
/اً|أ|ا|آ|ٱ|ٲ|ٳ|ئی|ئي|ئے|یٰ|ی|ي|ې|ۍ|ئ|ے|س|ص|ث|څ|ج|چ|هٔ|ه|ۀ|غز|زغ|کش|شک|ښک|ښک|پښ|ښپ|ہ|ع|و|ؤ|ښ|غ|خ|ح|ش|ز|ض|ذ|ځ|ظ|ژ|ر|ړ|ڑ|ت|ټ|ٹ|ط|د|ډ|ڈ|مب|م|نب|ن|ڼ|ک|ګ|گ|ل|ق|ږ|ب|پ|ف/g;
|
||||||
|
|
||||||
// TODO: I removed the h? 's at the beginning and ends. was that a good idea?
|
// TODO: I removed the h? 's at the beginning and ends. was that a good idea?
|
||||||
const aaySoundLatin = "(?:[aá]a?i|[eé]y|[aá]a?y|[aá]h?i)";
|
const aaySoundLatin = "(?:[aá]a?i|[eé]y|[aá]a?y|[aá]h?i)";
|
||||||
const aaySoundSimpleLatin = "(?:aa?i|ey|aa?y|ah?i)";
|
const aaySoundSimpleLatin = "(?:aa?i|ay|aa?y|ah?i)";
|
||||||
const longASoundLatin = "(?:[aá]{1,2}'?h?a{0,2}?)h?";
|
const longASoundLatin = "(?:[aá]{1,2}'?h?a{0,2}?)h?";
|
||||||
const longASoundSimpleLatin = "(?:a{1,2}'?h?a{0,2}?)h?";
|
const longASoundSimpleLatin = "(?:a{1,2}'?h?a{0,2}?)h?";
|
||||||
const shortASoundLatin = "(?:[aáă][a|́]?|au|áu|[uú]|[UÚ]|[ií]|[eé])?h?";
|
const shortASoundLatin = "(?:[aáă][a|́]?|au|áu|[uú]|[UÚ]|[ií]|[eé])?h?";
|
||||||
|
@ -146,8 +156,8 @@ const shwaSoundLatin = "(?:[uú]|[oó]o?|w[uú]|[aáă]|[ií]|[UÚ])?";
|
||||||
const shwaSoundSimpleLatin = "(?:u|oo?|wu|a|i|U)?";
|
const shwaSoundSimpleLatin = "(?:u|oo?|wu|a|i|U)?";
|
||||||
const ooSoundLatin = "(?:[oó]o?|[áa]u|w[uú]|[aá]w|[uú]|[UÚ])(?:h|w)?";
|
const ooSoundLatin = "(?:[oó]o?|[áa]u|w[uú]|[aá]w|[uú]|[UÚ])(?:h|w)?";
|
||||||
const ooSoundSimpleLatin = "(?:oo?|au|wu|aw|u|U)(?:h|w)?";
|
const ooSoundSimpleLatin = "(?:oo?|au|wu|aw|u|U)(?:h|w)?";
|
||||||
const eySoundLatin = "(?:[eé]y|[eé]e?|[uú]y|[aá]y|[ií])";
|
const aySoundLatin = "(?:[eé]y|[eé]e?|[uú]y|[aá]y|[ií])";
|
||||||
const eySoundSimpleLatin = "(?:ey|ee?|uy|ay|i)";
|
const aySoundSimpleLatin = "(?:ay|ee?|uy|ay|i)";
|
||||||
const middleESoundLatin = "(?:[eé]e?|[ií]|[aáă]|[eé])[h|y|́]?";
|
const middleESoundLatin = "(?:[eé]e?|[ií]|[aáă]|[eé])[h|y|́]?";
|
||||||
const middleESoundSimpleLatin = "(?:ee?|i|a|e)[h|y]?";
|
const middleESoundSimpleLatin = "(?:ee?|i|a|e)[h|y]?";
|
||||||
const iSoundLatin = "-?(?:[uú]|[aáă]|[ií]|[eé]e?)?h?-?";
|
const iSoundLatin = "-?(?:[uú]|[aáă]|[ií]|[eé]e?)?h?-?";
|
||||||
|
@ -180,12 +190,12 @@ export const latinReplacerInfo: IPhoneticsReplacerInfoItem[] = [
|
||||||
{ char: "óo", repl: ooSoundLatin },
|
{ char: "óo", repl: ooSoundLatin },
|
||||||
{ char: "i", repl: iSoundLatin, replWhenBeginning: iSoundLatinBeginning },
|
{ char: "i", repl: iSoundLatin, replWhenBeginning: iSoundLatinBeginning },
|
||||||
{ char: "í", repl: iSoundLatin, replWhenBeginning: iSoundLatinBeginning },
|
{ char: "í", repl: iSoundLatin, replWhenBeginning: iSoundLatinBeginning },
|
||||||
{ char: "ey", repl: eySoundLatin },
|
{ char: "ay", repl: aySoundLatin },
|
||||||
{ char: "éy", repl: eySoundLatin },
|
{ char: "áy", repl: aySoundLatin },
|
||||||
{ char: "ee", repl: eySoundLatin },
|
{ char: "ee", repl: aySoundLatin },
|
||||||
{ char: "ée", repl: eySoundLatin },
|
{ char: "ée", repl: aySoundLatin },
|
||||||
{ char: "uy", repl: eySoundLatin },
|
{ char: "uy", repl: aySoundLatin },
|
||||||
{ char: "úy", repl: eySoundLatin },
|
{ char: "úy", repl: aySoundLatin },
|
||||||
{ char: "e", repl: middleESoundLatin },
|
{ char: "e", repl: middleESoundLatin },
|
||||||
{ char: "é", repl: middleESoundLatin },
|
{ char: "é", repl: middleESoundLatin },
|
||||||
{ char: "w", repl: "(?:w{1,2}?[UÚ]?|b)" },
|
{ char: "w", repl: "(?:w{1,2}?[UÚ]?|b)" },
|
||||||
|
@ -254,10 +264,14 @@ export const simpleLatinReplacerInfo: IPhoneticsReplacerInfoItem[] = [
|
||||||
{ char: "U", repl: ooSoundSimpleLatin },
|
{ char: "U", repl: ooSoundSimpleLatin },
|
||||||
{ char: "o", repl: ooSoundSimpleLatin },
|
{ char: "o", repl: ooSoundSimpleLatin },
|
||||||
{ char: "oo", repl: ooSoundSimpleLatin },
|
{ char: "oo", repl: ooSoundSimpleLatin },
|
||||||
{ char: "i", repl: iSoundSimpleLatin, replWhenBeginning: iSoundSimpleLatinBeginning },
|
{
|
||||||
{ char: "ey", repl: eySoundSimpleLatin },
|
char: "i",
|
||||||
{ char: "ee", repl: eySoundSimpleLatin },
|
repl: iSoundSimpleLatin,
|
||||||
{ char: "uy", repl: eySoundSimpleLatin },
|
replWhenBeginning: iSoundSimpleLatinBeginning,
|
||||||
|
},
|
||||||
|
{ char: "ay", repl: aySoundSimpleLatin },
|
||||||
|
{ char: "ee", repl: aySoundSimpleLatin },
|
||||||
|
{ char: "uy", repl: aySoundSimpleLatin },
|
||||||
{ char: "e", repl: middleESoundSimpleLatin },
|
{ char: "e", repl: middleESoundSimpleLatin },
|
||||||
{ char: "w", repl: "(?:w{1,2}?[UÚ]?|b)" },
|
{ char: "w", repl: "(?:w{1,2}?[UÚ]?|b)" },
|
||||||
{ char: "y", repl: "[ií]?y?" },
|
{ char: "y", repl: "[ií]?y?" },
|
||||||
|
@ -313,6 +327,8 @@ export const simpleLatinReplacerInfo: IPhoneticsReplacerInfoItem[] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
export const latinReplacerRegex = /yee|a{1,2}[i|y]|á{1,2}[i|y]|aa|áa|a|ắ|ă|á|U|Ú|u|ú|oo|óo|o|ó|e{1,2}|ée|é|ey|éy|uy|úy|i|í|w|y|q|q|ts|sh|ss|s|dz|z|tt|t|T|dd|d|D|r{1,2}|R|nb|mb|n{1,2}|N|f{1,2}|b{1,2}|p{1,2}|x|kh|q|kk|k|gh|g|G|j|ch|c|ll|l|m{1,2}|h|’|'|`/g;
|
export const latinReplacerRegex =
|
||||||
|
/yee|a{1,2}[i|y]|á{1,2}[i|y]|aa|áa|a|ắ|ă|á|U|Ú|u|ú|oo|óo|o|ó|e{1,2}|ée|é|ay|áy|uy|úy|i|í|w|y|q|q|ts|sh|ss|s|dz|z|tt|t|T|dd|d|D|r{1,2}|R|nb|mb|n{1,2}|N|f{1,2}|b{1,2}|p{1,2}|x|kh|q|kk|k|gh|g|G|j|ch|c|ll|l|m{1,2}|h|’|'|`/g;
|
||||||
|
|
||||||
export const simpleLatinReplacerRegex = /yee|a{1,2}[i|y]|aa|a|U|u|oo|o|e{1,2}|ey|uy|i|w|y|q|ts|sh|s|dz|z|tt|t|T|dd|d|D|r{1,2}|R|nb|mb|n{1,2}|N|f{1,2}|b{1,2}|p{1,2}|x|kh|q|k|gh|g|G|j|ch|c|ll|l|m{1,2}|h/g;
|
export const simpleLatinReplacerRegex =
|
||||||
|
/yee|a{1,2}[i|y]|aa|a|U|u|oo|o|e{1,2}|ay|uy|i|w|y|q|ts|sh|s|dz|z|tt|t|T|dd|d|D|r{1,2}|R|nb|mb|n{1,2}|N|f{1,2}|b{1,2}|p{1,2}|x|kh|q|k|gh|g|G|j|ch|c|ll|l|m{1,2}|h/g;
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const userLocalStorageName = "user1";
|
||||||
|
|
||||||
export function saveOptions(options: Options): void {
|
export function saveOptions(options: Options): void {
|
||||||
localStorage.setItem(optionsLocalStorageName, JSON.stringify(options));
|
localStorage.setItem(optionsLocalStorageName, JSON.stringify(options));
|
||||||
};
|
}
|
||||||
|
|
||||||
export const readOptions = (): undefined | Options => {
|
export const readOptions = (): undefined | Options => {
|
||||||
const optionsRaw = localStorage.getItem(optionsLocalStorageName);
|
const optionsRaw = localStorage.getItem(optionsLocalStorageName);
|
||||||
|
@ -23,10 +23,6 @@ export const readOptions = (): undefined | Options => {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const options = JSON.parse(optionsRaw) as Options;
|
const options = JSON.parse(optionsRaw) as Options;
|
||||||
if (!("searchBarStickyFocus" in options)) {
|
|
||||||
// compatibility with legacy options
|
|
||||||
options.searchBarStickyFocus = false;
|
|
||||||
}
|
|
||||||
return options;
|
return options;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("error parsing saved state JSON", e);
|
console.error("error parsing saved state JSON", e);
|
||||||
|
@ -40,7 +36,7 @@ export function saveUser(user: AT.LingdocsUser | undefined): void {
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem(userLocalStorageName);
|
localStorage.removeItem(userLocalStorageName);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const readUser = (): AT.LingdocsUser | undefined => {
|
export const readUser = (): AT.LingdocsUser | undefined => {
|
||||||
const userRaw = localStorage.getItem(userLocalStorageName);
|
const userRaw = localStorage.getItem(userLocalStorageName);
|
||||||
|
|
|
@ -9,7 +9,7 @@ const pMatches = [
|
||||||
|
|
||||||
const fMatches = [
|
const fMatches = [
|
||||||
["tahliya", "takhliya"],
|
["tahliya", "takhliya"],
|
||||||
["sareyy", "saRey"],
|
["sarey", "saRay"],
|
||||||
["peyTey", "peTey"],
|
["peyTey", "peTey"],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
// R: "[r|R]",
|
// R: "[r|R]",
|
||||||
// };
|
// };
|
||||||
|
|
||||||
const fiveYeys = "[ئ|ۍ|ي|ې|ی]";
|
const fiveYays = "[ئ|ۍ|ي|ې|ی]";
|
||||||
const sSounds = "[س|ص|ث|څ]";
|
const sSounds = "[س|ص|ث|څ]";
|
||||||
const zSounds = "[ز|ژ|ض|ظ|ذ|ځ]";
|
const zSounds = "[ز|ژ|ض|ظ|ذ|ځ]";
|
||||||
const tSounds = "[ت|ط|ټ]";
|
const tSounds = "[ت|ط|ټ]";
|
||||||
|
@ -39,106 +39,115 @@ const hKhSounds = "[خ|ح|ښ|ه]";
|
||||||
const alef = "[آ|ا]";
|
const alef = "[آ|ا]";
|
||||||
|
|
||||||
const pReplacer = {
|
const pReplacer = {
|
||||||
"ی": fiveYeys,
|
ی: fiveYays,
|
||||||
"ي": fiveYeys,
|
ي: fiveYays,
|
||||||
"ۍ": fiveYeys,
|
ۍ: fiveYays,
|
||||||
"ئ": fiveYeys,
|
ئ: fiveYays,
|
||||||
"ې": fiveYeys,
|
ې: fiveYays,
|
||||||
|
|
||||||
"س": sSounds,
|
س: sSounds,
|
||||||
"ص": sSounds,
|
ص: sSounds,
|
||||||
"ث": sSounds,
|
ث: sSounds,
|
||||||
"څ": sSounds,
|
څ: sSounds,
|
||||||
|
|
||||||
"ز": zSounds,
|
ز: zSounds,
|
||||||
"ظ": zSounds,
|
ظ: zSounds,
|
||||||
"ذ": zSounds,
|
ذ: zSounds,
|
||||||
"ض": zSounds,
|
ض: zSounds,
|
||||||
"ژ": zSounds,
|
ژ: zSounds,
|
||||||
"ځ": zSounds,
|
ځ: zSounds,
|
||||||
|
|
||||||
"ت": tSounds,
|
ت: tSounds,
|
||||||
"ط": tSounds,
|
ط: tSounds,
|
||||||
"ټ": tSounds,
|
ټ: tSounds,
|
||||||
|
|
||||||
"د": dSounds,
|
د: dSounds,
|
||||||
"ډ": dSounds,
|
ډ: dSounds,
|
||||||
|
|
||||||
"ر": rSounds,
|
ر: rSounds,
|
||||||
"ړ": rSounds,
|
ړ: rSounds,
|
||||||
|
|
||||||
"ن": nSounds,
|
ن: nSounds,
|
||||||
"ڼ": nSounds,
|
ڼ: nSounds,
|
||||||
|
|
||||||
"خ": hKhSounds,
|
خ: hKhSounds,
|
||||||
"ح": hKhSounds,
|
ح: hKhSounds,
|
||||||
"ښ": hKhSounds,
|
ښ: hKhSounds,
|
||||||
"ه": hKhSounds,
|
ه: hKhSounds,
|
||||||
|
|
||||||
"ا": alef,
|
ا: alef,
|
||||||
"آ": alef,
|
آ: alef,
|
||||||
};
|
};
|
||||||
|
|
||||||
const fiveYeysF = "(?:eyy|ey|ee|é|e|uy)";
|
const fiveYaysF = "(?:ey|ay|ee|é|e|uy)";
|
||||||
const hKhF = "(?:kh|h|x)";
|
const hKhF = "(?:kh|h|x)";
|
||||||
const zSoundsF = "(?:z|dz)";
|
const zSoundsF = "(?:z|dz)";
|
||||||
const sSoundsF = "(?:ts|s)";
|
const sSoundsF = "(?:ts|s)";
|
||||||
|
|
||||||
const fReplacer = {
|
const fReplacer = {
|
||||||
"eyy": fiveYeysF,
|
ey: fiveYaysF,
|
||||||
"ey": fiveYeysF,
|
ay: fiveYaysF,
|
||||||
"uy": fiveYeysF,
|
uy: fiveYaysF,
|
||||||
"ee": fiveYeysF,
|
ee: fiveYaysF,
|
||||||
"e": fiveYeysF,
|
e: fiveYaysF,
|
||||||
|
|
||||||
"z": zSoundsF,
|
z: zSoundsF,
|
||||||
"dz": zSoundsF,
|
dz: zSoundsF,
|
||||||
"x": hKhF,
|
x: hKhF,
|
||||||
"h": hKhF,
|
h: hKhF,
|
||||||
"kh": hKhF,
|
kh: hKhF,
|
||||||
"ts": sSoundsF,
|
ts: sSoundsF,
|
||||||
"s": sSoundsF,
|
s: sSoundsF,
|
||||||
// only used if ignoring accents
|
// only used if ignoring accents
|
||||||
"a": "[a|á]",
|
a: "[a|á]",
|
||||||
"á": "[a|á|u|ú]",
|
á: "[a|á|u|ú]",
|
||||||
"u": "[u|ú|a|á]",
|
u: "[u|ú|a|á]",
|
||||||
"ú": "[u|ú]",
|
ú: "[u|ú]",
|
||||||
"o": "[o|ó]",
|
o: "[o|ó]",
|
||||||
"ó": "[o|ó]",
|
ó: "[o|ó]",
|
||||||
"i": "[i|í]",
|
i: "[i|í]",
|
||||||
"í": "[i|í]",
|
í: "[i|í]",
|
||||||
"U": "[U|Ú]",
|
U: "[U|Ú]",
|
||||||
"Ú": "[U|Ú]",
|
Ú: "[U|Ú]",
|
||||||
"éy": fiveYeysF,
|
áy: fiveYaysF,
|
||||||
"éyy": fiveYeysF,
|
éy: fiveYaysF,
|
||||||
"úy": fiveYeysF,
|
úy: fiveYaysF,
|
||||||
"ée": fiveYeysF,
|
ée: fiveYaysF,
|
||||||
"é": fiveYeysF,
|
é: fiveYaysF,
|
||||||
};
|
};
|
||||||
|
|
||||||
const pRepRegex = new RegExp(Object.keys(pReplacer).join("|"), "g");
|
const pRepRegex = new RegExp(Object.keys(pReplacer).join("|"), "g");
|
||||||
|
|
||||||
const fRepRegex = /eyy|ey|uy|ee|e|z|dz|x|kh|h|ts|s/g;
|
const fRepRegex = /ey|ay|uy|ee|e|z|dz|x|kh|h|ts|s/g;
|
||||||
|
|
||||||
const fRepRegexWAccents = /eyy|éyy|ey|éy|uy|úy|ee|ée|e|é|z|dz|x|ts|s|kh|h|a|á|i|í|o|ó|u|ú|U|Ú/g;
|
const fRepRegexWAccents =
|
||||||
|
/ey|éy|ay|áy|uy|úy|ee|ée|e|é|z|dz|x|ts|s|kh|h|a|á|i|í|o|ó|u|ú|U|Ú/g;
|
||||||
|
|
||||||
function makePAWeeBitFuzzy(s: string): string {
|
function makePAWeeBitFuzzy(s: string): string {
|
||||||
// + s.replace(/ /g, "").split("").join(" *");
|
// + s.replace(/ /g, "").split("").join(" *");
|
||||||
return "^" + s.replace(pRepRegex, mtch => {
|
return (
|
||||||
|
"^" +
|
||||||
|
s.replace(pRepRegex, (mtch) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return `${pReplacer[mtch]}`;
|
return `${pReplacer[mtch]}`;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeFAWeeBitFuzzy(s: string, ignoreAccent?: boolean): string {
|
function makeFAWeeBitFuzzy(s: string, ignoreAccent?: boolean): string {
|
||||||
return "^" + s.replace((ignoreAccent ? fRepRegexWAccents : fRepRegex), mtch => {
|
return (
|
||||||
|
"^" +
|
||||||
|
s.replace(ignoreAccent ? fRepRegexWAccents : fRepRegex, (mtch) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return fReplacer[mtch];
|
return fReplacer[mtch];
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeAWeeBitFuzzy(s: string, i: "f" | "p", ignoreAccent?: boolean): string {
|
export function makeAWeeBitFuzzy(
|
||||||
return i === "p"
|
s: string,
|
||||||
? makePAWeeBitFuzzy(s)
|
i: "f" | "p",
|
||||||
: makeFAWeeBitFuzzy(s, ignoreAccent);
|
ignoreAccent?: boolean
|
||||||
|
): string {
|
||||||
|
return i === "p" ? makePAWeeBitFuzzy(s) : makeFAWeeBitFuzzy(s, ignoreAccent);
|
||||||
}
|
}
|
|
@ -1,71 +1,88 @@
|
||||||
export type DictionaryStatus = "loading" | "ready" | "updating" | "error loading";
|
export type DictionaryStatus =
|
||||||
|
| "loading"
|
||||||
|
| "ready"
|
||||||
|
| "updating"
|
||||||
|
| "error loading";
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
dictionaryStatus: DictionaryStatus,
|
dictionaryStatus: DictionaryStatus;
|
||||||
searchValue: string,
|
showModal: boolean;
|
||||||
options: Options,
|
searchValue: string;
|
||||||
page: number,
|
options: Options;
|
||||||
isolatedEntry: import("@lingdocs/ps-react").Types.DictionaryEntry | undefined,
|
page: number;
|
||||||
results: import("@lingdocs/ps-react").Types.DictionaryEntry[],
|
isolatedEntry: import("@lingdocs/ps-react").Types.DictionaryEntry | undefined;
|
||||||
wordlist: WordlistWord[],
|
results: import("@lingdocs/ps-react").Types.DictionaryEntry[];
|
||||||
reviewTasks: import("./functions-types").ReviewTask[],
|
wordlist: WordlistWord[];
|
||||||
dictionaryInfo: import("@lingdocs/ps-react").Types.DictionaryInfo | undefined,
|
reviewTasks: import("./functions-types").ReviewTask[];
|
||||||
user: undefined | import("./account-types").LingdocsUser,
|
dictionaryInfo: import("@lingdocs/ps-react").Types.DictionaryInfo | undefined;
|
||||||
inflectionSearchResults: undefined | "searching" | {
|
user: undefined | import("./account-types").LingdocsUser;
|
||||||
exact: InflectionSearchResult[],
|
inflectionSearchResults:
|
||||||
fuzzy: InflectionSearchResult[],
|
| undefined
|
||||||
},
|
| "searching"
|
||||||
}
|
| {
|
||||||
|
exact: InflectionSearchResult[];
|
||||||
|
fuzzy: InflectionSearchResult[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type DictionaryAPI = {
|
export type DictionaryAPI = {
|
||||||
initialize: () => Promise<{
|
initialize: () => Promise<{
|
||||||
response: "loaded first time" | "loaded from saved",
|
response: "loaded first time" | "loaded from saved";
|
||||||
dictionaryInfo: import("@lingdocs/ps-react").Types.DictionaryInfo,
|
dictionaryInfo: import("@lingdocs/ps-react").Types.DictionaryInfo;
|
||||||
}>,
|
}>;
|
||||||
update: (updateComing: () => void) => Promise<{
|
update: (updateComing: () => void) => Promise<{
|
||||||
response: "no need for update" | "updated" | "unable to check",
|
response: "no need for update" | "updated" | "unable to check";
|
||||||
dictionaryInfo: import("@lingdocs/ps-react").Types.DictionaryInfo,
|
dictionaryInfo: import("@lingdocs/ps-react").Types.DictionaryInfo;
|
||||||
}>,
|
}>;
|
||||||
search: (state: State) => import("@lingdocs/ps-react").Types.DictionaryEntry[],
|
search: (
|
||||||
exactPashtoSearch: (search: string) => import("@lingdocs/ps-react").Types.DictionaryEntry[],
|
state: State
|
||||||
getNewWordsThisMonth: () => import("@lingdocs/ps-react").Types.DictionaryEntry[],
|
) => import("@lingdocs/ps-react").Types.DictionaryEntry[];
|
||||||
findOneByTs: (ts: number) => import("@lingdocs/ps-react").Types.DictionaryEntry | undefined,
|
exactPashtoSearch: (
|
||||||
findRelatedEntries: (entry: import("@lingdocs/ps-react").Types.DictionaryEntry) => import("@lingdocs/ps-react").Types.DictionaryEntry[],
|
search: string
|
||||||
}
|
) => import("@lingdocs/ps-react").Types.DictionaryEntry[];
|
||||||
|
getNewWordsThisMonth: () => import("@lingdocs/ps-react").Types.DictionaryEntry[];
|
||||||
|
findOneByTs: (
|
||||||
|
ts: number
|
||||||
|
) => import("@lingdocs/ps-react").Types.DictionaryEntry | undefined;
|
||||||
|
findRelatedEntries: (
|
||||||
|
entry: import("@lingdocs/ps-react").Types.DictionaryEntry
|
||||||
|
) => import("@lingdocs/ps-react").Types.DictionaryEntry[];
|
||||||
|
};
|
||||||
|
|
||||||
export type WordlistWordBase = {
|
export type WordlistWordBase = {
|
||||||
_id: string,
|
_id: string;
|
||||||
/* a backup copy of the full dictionary entry in case it gets deleted from the dictionary */
|
/* a backup copy of the full dictionary entry in case it gets deleted from the dictionary */
|
||||||
entry: import("@lingdocs/ps-react").Types.DictionaryEntry,
|
entry: import("@lingdocs/ps-react").Types.DictionaryEntry;
|
||||||
/* the notes/context provided by the user for the word in their wordlist */
|
/* the notes/context provided by the user for the word in their wordlist */
|
||||||
notes: string,
|
notes: string;
|
||||||
supermemo: import("supermemo").SuperMemoItem,
|
supermemo: import("supermemo").SuperMemoItem;
|
||||||
/* rep/stage of warmup stage before moving into supermemo mode */
|
/* rep/stage of warmup stage before moving into supermemo mode */
|
||||||
warmup: number | "done",
|
warmup: number | "done";
|
||||||
/* date due for review - ISO string */
|
/* date due for review - ISO string */
|
||||||
dueDate: number,
|
dueDate: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type WordlistAttachmentInfo = {
|
export type WordlistAttachmentInfo = {
|
||||||
imgSize?: { height: number, width: number },
|
imgSize?: { height: number; width: number };
|
||||||
_attachments: Attachments,
|
_attachments: Attachments;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type WordlistWordWAttachments = WordlistWordBase & WordlistAttachmentInfo;
|
export type WordlistWordWAttachments = WordlistWordBase &
|
||||||
|
WordlistAttachmentInfo;
|
||||||
|
|
||||||
export type WordlistWord = WordlistWordBase | WordlistWordWAttachments;
|
export type WordlistWord = WordlistWordBase | WordlistWordWAttachments;
|
||||||
|
|
||||||
export type Options = {
|
export type Options = {
|
||||||
language: Language,
|
language: Language;
|
||||||
searchType: SearchType,
|
searchType: SearchType;
|
||||||
theme: Theme,
|
theme: Theme;
|
||||||
textOptionsRecord: TextOptionsRecord,
|
textOptionsRecord: TextOptionsRecord;
|
||||||
wordlistMode: WordlistMode,
|
wordlistMode: WordlistMode;
|
||||||
wordlistReviewLanguage: Language,
|
wordlistReviewLanguage: Language;
|
||||||
wordlistReviewBadge: boolean,
|
wordlistReviewBadge: boolean;
|
||||||
searchBarPosition: SearchBarPosition,
|
searchBarPosition: SearchBarPosition;
|
||||||
searchBarStickyFocus: boolean,
|
searchBarStickyFocus: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Language = "Pashto" | "English";
|
export type Language = "Pashto" | "English";
|
||||||
export type SearchType = "alphabetical" | "fuzzy";
|
export type SearchType = "alphabetical" | "fuzzy";
|
||||||
|
@ -78,84 +95,102 @@ export type SearchBarPosition = "top" | "bottom";
|
||||||
export type WordlistMode = "browse" | "review";
|
export type WordlistMode = "browse" | "review";
|
||||||
|
|
||||||
export type TextOptionsRecord = {
|
export type TextOptionsRecord = {
|
||||||
lastModified: import("./account-types").TimeStamp,
|
lastModified: import("./account-types").TimeStamp;
|
||||||
textOptions: import("@lingdocs/ps-react").Types.TextOptions,
|
textOptions: import("@lingdocs/ps-react").Types.TextOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserLevel = "basic" | "student" | "editor";
|
export type UserLevel = "basic" | "student" | "editor";
|
||||||
|
|
||||||
export type OptionsAction = {
|
export type OptionsAction =
|
||||||
type: "toggleSearchType",
|
| {
|
||||||
} | {
|
type: "toggleSearchType";
|
||||||
type: "toggleLanguage",
|
}
|
||||||
} | {
|
| {
|
||||||
type: "changeTheme",
|
type: "toggleLanguage";
|
||||||
payload: Theme,
|
}
|
||||||
} | {
|
| {
|
||||||
type: "changeSearchBarPosition",
|
type: "changeTheme";
|
||||||
payload: SearchBarPosition,
|
payload: Theme;
|
||||||
} | {
|
}
|
||||||
type: "changeWordlistMode",
|
| {
|
||||||
payload: WordlistMode,
|
type: "changeSearchBarPosition";
|
||||||
} | {
|
payload: SearchBarPosition;
|
||||||
type: "changeWordlistReviewLanguage",
|
}
|
||||||
payload: Language,
|
| {
|
||||||
} | {
|
type: "changeWordlistMode";
|
||||||
type: "changeWordlistReviewBadge",
|
payload: WordlistMode;
|
||||||
payload: boolean,
|
}
|
||||||
} | {
|
| {
|
||||||
type: "updateTextOptionsRecord",
|
type: "changeWordlistReviewLanguage";
|
||||||
payload: TextOptionsRecord,
|
payload: Language;
|
||||||
} | {
|
}
|
||||||
type: "changeSearchBarStickyFocus",
|
| {
|
||||||
payload: boolean,
|
type: "changeWordlistReviewBadge";
|
||||||
} | {
|
payload: boolean;
|
||||||
type: "setShowPlayStoreButton",
|
}
|
||||||
payload: boolean,
|
| {
|
||||||
|
type: "updateTextOptionsRecord";
|
||||||
|
payload: TextOptionsRecord;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "changeSearchBarStickyFocus";
|
||||||
|
payload: boolean;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "setShowPlayStoreButton";
|
||||||
|
payload: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TextOptionsAction = {
|
export type TextOptionsAction =
|
||||||
type: "changePTextSize",
|
| {
|
||||||
payload: PTextSize,
|
type: "changePTextSize";
|
||||||
} | {
|
payload: PTextSize;
|
||||||
type: "changeSpelling",
|
}
|
||||||
payload: import("@lingdocs/ps-react").Types.Spelling,
|
| {
|
||||||
} | {
|
type: "changeSpelling";
|
||||||
type: "changePhonetics",
|
payload: import("@lingdocs/ps-react").Types.Spelling;
|
||||||
payload: "lingdocs" | "ipa" | "alalc" | "none",
|
}
|
||||||
} | {
|
| {
|
||||||
type: "changeDialect",
|
type: "changePhonetics";
|
||||||
payload: "standard" | "peshawer" | "southern",
|
payload: "lingdocs" | "ipa" | "alalc" | "none";
|
||||||
} | {
|
}
|
||||||
type: "changeDiacritics",
|
| {
|
||||||
payload: boolean,
|
type: "changeDialect";
|
||||||
|
payload: "standard" | "peshawer" | "southern";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "changeDiacritics";
|
||||||
|
payload: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AttachmentToPut = {
|
export type AttachmentToPut = {
|
||||||
content_type: string,
|
content_type: string;
|
||||||
data: string | Blob,
|
data: string | Blob;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type AttachmentWithData = {
|
export type AttachmentWithData = {
|
||||||
content_type: string,
|
content_type: string;
|
||||||
digest: string,
|
digest: string;
|
||||||
data: string | Blob,
|
data: string | Blob;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type AttachmentWOutData = {
|
export type AttachmentWOutData = {
|
||||||
content_type: string,
|
content_type: string;
|
||||||
digest: string,
|
digest: string;
|
||||||
stub: true;
|
stub: true;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Attachment = AttachmentToPut | AttachmentWithData | AttachmentWOutData
|
export type Attachment =
|
||||||
|
| AttachmentToPut
|
||||||
|
| AttachmentWithData
|
||||||
|
| AttachmentWOutData;
|
||||||
export type AttachmentType = "image" | "audio";
|
export type AttachmentType = "image" | "audio";
|
||||||
export type Attachments = {
|
export type Attachments = {
|
||||||
/* only allows one image and one audio attachment - max 2 values */
|
/* only allows one image and one audio attachment - max 2 values */
|
||||||
[filename: string]: Attachment,
|
[filename: string]: Attachment;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WordlistWordDoc = WordlistWord & { _rev: string, _id: string };
|
export type WordlistWordDoc = WordlistWord & { _rev: string; _id: string };
|
||||||
|
|
||||||
export type InflectionName = "plain" | "1st" | "2nd";
|
export type InflectionName = "plain" | "1st" | "2nd";
|
||||||
|
|
||||||
|
@ -167,15 +202,14 @@ export type PluralInflectionName = "plural" | "2nd";
|
||||||
// the possible matches, and their person/inflection number
|
// the possible matches, and their person/inflection number
|
||||||
|
|
||||||
export type InflectionSearchResult = {
|
export type InflectionSearchResult = {
|
||||||
entry: import("@lingdocs/ps-react").Types.DictionaryEntry,
|
entry: import("@lingdocs/ps-react").Types.DictionaryEntry;
|
||||||
forms: InflectionFormMatch[],
|
forms: InflectionFormMatch[];
|
||||||
}
|
|
||||||
|
|
||||||
export type InflectionFormMatch = {
|
|
||||||
path: string[],
|
|
||||||
matches: {
|
|
||||||
ps: import("@lingdocs/ps-react").Types.PsString,
|
|
||||||
pos: InflectionName[] | import("@lingdocs/ps-react").Types.Person[] | null,
|
|
||||||
}[],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type InflectionFormMatch = {
|
||||||
|
path: string[];
|
||||||
|
matches: {
|
||||||
|
ps: import("@lingdocs/ps-react").Types.PsString;
|
||||||
|
pos: InflectionName[] | import("@lingdocs/ps-react").Types.Person[] | null;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
|
@ -2349,10 +2349,10 @@
|
||||||
"@jridgewell/resolve-uri" "^3.0.3"
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
"@lingdocs/ps-react@5.10.1":
|
"@lingdocs/ps-react@6.0.0":
|
||||||
version "5.10.1"
|
version "6.0.0"
|
||||||
resolved "https://npm.lingdocs.com/@lingdocs%2fps-react/-/ps-react-5.10.1.tgz#949850aaa3c9de54d4beed1daa9b546bb0a84df9"
|
resolved "https://npm.lingdocs.com/@lingdocs%2fps-react/-/ps-react-6.0.0.tgz#dbdfd1a5afd19253679169eacbf1da5562db5dc3"
|
||||||
integrity sha512-Ro/6Fq9mEdF4/2wJf8USkIlYe+9vWmez/RhoUF0mTjOhmyTGV6cpajK0Qpo1WyCaL5d/6BTI3qVuk5h8pWRQjA==
|
integrity sha512-+j6F65FtmPbeEjjHtE3JqKHtCcUM+cMAN2RMTd8yyacJ4sTJW/oWC+6rAQGQqc1da3lP7tuxt6p+esmFYI9fgQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@formkit/auto-animate" "^1.0.0-beta.3"
|
"@formkit/auto-animate" "^1.0.0-beta.3"
|
||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
|
|
Loading…
Reference in New Issue