Installing Epwing
- To install epwing, please download and extract
zip file, then run the
- install script for your operating system. It doesn't copy any executables to any directories, so you need to keep the folder where it is.
- If you accidentally move or delete the folder, just get the zip again, extract to the desired location and re-run the install script.
+ To install epwing, please download and extract
zip file, then run the
+ install script for your operating system. It doesn't copy any executables to any directories, so you need to
+ keep the folder where it is.
+ If you accidentally move or delete the folder, just get the zip again, extract to the desired location and
+ re-run the install script.
-
+
Version 1.3.4
+ - Adds the ability to select which dictionary definition you want to import or hear the audio of
- Fixes missing audio from Language Pod
@@ -394,7 +421,9 @@
Version 1.3.1
Version 1.3.0
- - Added a VN Hook page. While the page is open, it'll monitor your clipboard for you and automatically add items copied to the page
+ - Added a VN Hook page. While the page is open, it'll monitor your clipboard for you and automatically
+ add items copied to the page
+
Version 1.2.2
@@ -433,7 +462,9 @@
Version 1.1.4
Version 1.1.3
- Included support for Typescript by including Webpack. Hopefully won't cause issues
- - Fixed annoying bug where the icon wouldn't reset on start, making the icon look like it was enabled when it wasn't
+ - Fixed annoying bug where the icon wouldn't reset on start, making the icon look like it was enabled
+ when it wasn't
+
Version 1.1.2
@@ -443,7 +474,8 @@
Version 1.1.2
Version 1.1.1
- - Fixing Epwing popup showing constantly, because of course I'd forget to fix that before releasing
+ - Fixing Epwing popup showing constantly, because of course I'd forget to fix that before releasing
+
Version 1.1.0
diff --git a/src/AnkiImport.ts b/src/AnkiImport.ts
index 625cc82..00cdbf8 100644
--- a/src/AnkiImport.ts
+++ b/src/AnkiImport.ts
@@ -104,14 +104,16 @@ export default class AnkiImport {
return Promise.all(promises);
}
- async downloadAudio(entry: SearchResults) {
+ async downloadAudio(entry: SearchResults & { selected?: number }) {
if (!isDictionaryResult(entry)) return;
if (!entry?.data) return;
if (await AudioPlayer.isNoAudio(entry)) return;
+ const selected = entry.selected || 0;
+
const audioUrl = AudioPlayer.getAudioUrl(entry);
- let [, dictionaryForm, reading] = entry.data[0][0].match(
+ let [, dictionaryForm, reading] = entry.data[selected][0].match(
/^(.+?)\s+(?:\[(.*?)\])?\s*\/(.+)\//
);
@@ -124,6 +126,7 @@ export default class AnkiImport {
}
// entry = Contains the work lookup info (kana, kanji, def)
+ // selected = The index of the entry to look up
// word = Highlighted Word
// sentence = The sentence containing the highlighted word
// sentenceWBlank = Like sentence except the highlighted word is replaced with blanks
@@ -131,6 +134,7 @@ export default class AnkiImport {
// saveFormat = Token-based save format
static async makeTextOptions(
entry: SearchResults,
+ selected: number,
word: string,
sentence: string,
sentenceWithBlank: string,
@@ -159,7 +163,9 @@ export default class AnkiImport {
// entryData[2] = kana (null if no kanji)
// entryData[3] = definition
- entryData = entry.data[0][0].match(/^(.+?)\s+(?:\[(.*?)\])?\s*\/(.+)\//);
+ entryData = entry.data[selected][0].match(
+ /^(.+?)\s+(?:\[(.*?)\])?\s*\/(.+)\//
+ );
let [_, dictionaryForm, reading, definition] = entryData;
// Does the user want to use the reading in place of kanji for the $d token?
diff --git a/src/AudioPlayer.ts b/src/AudioPlayer.ts
index 4abf893..8ee60eb 100644
--- a/src/AudioPlayer.ts
+++ b/src/AudioPlayer.ts
@@ -2,9 +2,10 @@ import Utils from "./Utils";
import {isKanjiResult, SearchResults} from "./interfaces/SearchResults";
export default class AudioPlayer {
- static getAudioUrl(entry: SearchResults): string {
+ static getAudioUrl(entry: SearchResults & { selected?: number }): string {
let kanaText;
let kanjiText;
+ const selected = entry.selected || 0;
//We have a single kanji selected
if (isKanjiResult(entry)) {
@@ -18,8 +19,8 @@ export default class AudioPlayer {
kanaText = Utils.convertKatakanaToHirigana(kanaText).kana;
if (!kanaText) return;
- } else if (entry.data[0]) {
- let entryData = entry.data[0][0].match(
+ } else if (entry.data[selected]) {
+ let entryData = entry.data[selected][0].match(
/^(.+?)\s+(?:\[(.*?)\])?\s*\/(.+)\//
);
diff --git a/src/background.ts b/src/background.ts
index e835a1e..c586c68 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -117,6 +117,7 @@ export class RikaiController {
async sendToAnki(content) {
const {
entry,
+ selected,
word,
sentence,
sentenceWithBlank,
@@ -125,6 +126,7 @@ export class RikaiController {
} = content;
const entryFormat = await AnkiImport.makeTextOptions(
entry,
+ selected || 0,
word,
sentence,
sentenceWithBlank,
diff --git a/src/content/index.ts b/src/content/index.ts
index ac8c7c4..50541cf 100644
--- a/src/content/index.ts
+++ b/src/content/index.ts
@@ -1,1106 +1,1376 @@
-import {docImposterDestroy, docRangeFromPoint} from './document';
+import {docImposterDestroy, docRangeFromPoint} from "./document";
import {transformNameEntriesToHtml} from "./entryTransformers";
-import autobind from '../../lib/autobind';
-import defaultConfig, {Config} from '../defaultConfig';
-import Utils from '../Utils';
-import '../../styles/popup.css';
+import autobind from "../../lib/autobind";
+import defaultConfig, {Config} from "../defaultConfig";
+import Utils from "../Utils";
+import "../../styles/popup.css";
import {TextSourceRange} from "./source";
-import {isDictionaryResult, isKanjiResult, SearchResults} from "../interfaces/SearchResults";
+import {isDictionaryResult, isKanjiResult, SearchResults,} from "../interfaces/SearchResults";
type position = {
- clientX: number,
- clientY: number,
- pageX: number,
- pageY: number,
-}
+ clientX: number;
+ clientY: number;
+ pageX: number;
+ pageY: number;
+};
type tabData = {
- selText?: string;
- previousRangeOffset?: number;
- pos?: position,
- previousTarget?: HTMLElement,
- previousTextSource?: TextSourceRange,
+ selText?: string;
+ previousRangeOffset?: number;
+ pos?: position;
+ previousTarget?: HTMLElement;
+ previousTextSource?: TextSourceRange;
};
class Rikai {
- private document: Document;
- private tabData: tabData = {};
- private lastFound: SearchResults;
- private enabled: boolean = false;
- private popupId: string = 'rikaichan-window';
- private config: Config;
- private keysDown: any[] = [];
-
- private epwingMode: boolean = false;
- private epwingTotalHits: number = 0;
- private epwingCurrentHit: number = 0;
- private epwingPreviousHit: number = 0;
- private epwingResults: any[] = [];
-
- private sanseidoFallback: number = 0;
- private sanseidoMode: boolean = false;
- private abortController: AbortController = new AbortController();
-
- private word: string;
- private sentence: string;
- private sentenceWithBlank: string;
-
- constructor(document: Document) {
- autobind(this);
-
- this.document = document;
- this.config = defaultConfig;
+ private document: Document;
+ private tabData: tabData = {};
+ private lastFound: SearchResults;
+ private enabled: boolean = false;
+ private popupId: string = "rikaichan-window";
+ private config: Config;
+ private keysDown: any[] = [];
+
+ private epwingMode: boolean = false;
+ private epwingTotalHits: number = 0;
+ private epwingCurrentHit: number = 0;
+ private epwingPreviousHit: number = 0;
+ private epwingResults: any[] = [];
+
+ private sanseidoFallback: number = 0;
+ private sanseidoMode: boolean = false;
+ private abortController: AbortController = new AbortController();
+
+ private selectedEntry: number = 0;
+ private totalEntries: number = 1;
+ private word: string;
+ private sentence: string;
+ private sentenceWithBlank: string;
+
+ private lastPopupPosition: { left: number; top: number };
+
+ constructor(document: Document) {
+ autobind(this);
+
+ this.document = document;
+ this.config = defaultConfig;
+ }
+
+ enable(): void {
+ if (this.enabled) return;
+
+ browser.storage.sync
+ .get("config")
+ .then((config: Storage & { config: Config }) => {
+ this.config = config.config || defaultConfig;
+ this.document.documentElement.removeChild(this.getPopup());
+ });
+
+ this.document.addEventListener("mousemove", this.onMouseMove);
+ this.document.addEventListener("mousedown", this.onMouseDown);
+ this.document.addEventListener("keydown", (event: KeyboardEvent) => {
+ const shouldStop = this.onKeyDown(event);
+ if (shouldStop === true) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ });
+ this.document.addEventListener("keyup", this.onKeyUp);
+
+ browser.storage.onChanged.addListener((changes, areaSet) => {
+ if (areaSet !== "sync") return;
+ if (typeof changes.config === "undefined") return;
+
+ this.config = changes.config.newValue || defaultConfig;
+ this.document.documentElement.removeChild(this.getPopup());
+ });
+
+ this.createPopup();
+
+ this.enabled = true;
+ }
+
+ disable(): void {
+ this.document.removeEventListener("mousemove", this.onMouseMove);
+ this.document.removeEventListener("mousedown", this.onMouseDown);
+ this.document.removeEventListener("keydown", (event: KeyboardEvent) => {
+ const shouldStop = this.onKeyDown(event);
+ if (shouldStop === true) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ });
+
+ this.document.removeEventListener("keyup", this.onKeyUp);
+
+ if (this.hasPopup()) {
+ this.document.documentElement.removeChild(this.getPopup());
}
- enable(): void {
- if (this.enabled) return;
-
- browser.storage.sync.get('config').then((config: Storage & {config: Config}) => {
- this.config = config.config || defaultConfig;
- this.document.documentElement.removeChild(this.getPopup());
- });
-
- this.document.addEventListener('mousemove', this.onMouseMove);
- this.document.addEventListener('mousedown', this.onMouseDown);
- this.document.addEventListener('keydown', (event: KeyboardEvent) => {
- const shouldStop = this.onKeyDown(event);
- if (shouldStop === true) {
- event.stopPropagation();
- event.preventDefault();
- }
- });
- this.document.addEventListener('keyup', this.onKeyUp);
-
- browser.storage.onChanged.addListener((changes, areaSet) => {
- if (areaSet !== 'sync') return;
- if (typeof changes.config === 'undefined') return;
-
- this.config = changes.config.newValue || defaultConfig;
- this.document.documentElement.removeChild(this.getPopup());
+ this.enabled = false;
+ }
+
+ onKeyDown(event: KeyboardEvent): boolean {
+ if (event.altKey || event.metaKey || event.ctrlKey) return;
+ if (event.shiftKey && event.keyCode !== 16) return;
+ if (this.keysDown[event.keyCode]) return;
+ if (!this.isVisible()) return;
+
+ const { pos } = this.tabData;
+
+ this.keysDown[event.keyCode] = 1;
+
+ switch (event.keyCode) {
+ case this.config.keymap.playAudio:
+ return this.playAudio();
+ case this.config.keymap.sendToAnki:
+ return this.sendToAnki();
+ case this.config.keymap.selectNextDictionary:
+ this.sendRequest("selectNextDictionary").then(() => {
+ setTimeout(() => {
+ this.searchAt({ x: pos.clientX, y: pos.clientY }, this.tabData);
+ }, 1);
});
+ return;
+ case this.config.keymap.toggleSanseidoMode:
+ browser.storage.local.set({ sanseidoMode: !this.sanseidoMode });
+ setTimeout(() => {
+ this.searchAt({ x: pos.clientX, y: pos.clientY }, this.tabData);
+ }, 5);
+ return true;
- this.createPopup();
-
- this.enabled = true;
- }
-
- disable(): void {
- this.document.removeEventListener('mousemove', this.onMouseMove);
- this.document.removeEventListener('mousedown', this.onMouseDown);
- this.document.removeEventListener('keydown', (event: KeyboardEvent) => {
- const shouldStop = this.onKeyDown(event);
- if (shouldStop === true) {
- event.stopPropagation();
- event.preventDefault();
- }
- });
+ case this.config.keymap.toggleEpwingMode:
+ const originalEpwing = this.epwingMode;
+ browser.storage.local.set({ epwingMode: !this.epwingMode });
+ setTimeout(() => {
+ if (this.epwingMode !== originalEpwing) {
+ this.searchAt({ x: pos.clientX, y: pos.clientY }, this.tabData);
+ }
+ }, 5);
+ return true;
- this.document.removeEventListener('keyup', this.onKeyUp);
+ case this.config.keymap.epwingNextEntry:
+ return this.showNextEpwingEntry();
+ case this.config.keymap.epwingPreviousEntry:
+ return this.showPreviousEpwingEntry();
- if (this.hasPopup()) {
- this.document.documentElement.removeChild(this.getPopup());
+ case this.config.keymap.nextDefinition:
+ this.selectedEntry++;
+ if (this.selectedEntry + 1 > this.totalEntries) {
+ this.selectedEntry = 0;
}
- this.enabled = false;
- }
+ this.renderPopup();
+ return true;
- onKeyDown(event: KeyboardEvent): boolean {
- if ((event.altKey) || (event.metaKey) || (event.ctrlKey)) return;
- if ((event.shiftKey) && (event.keyCode !== 16)) return;
- if (this.keysDown[event.keyCode]) return;
- if (!this.isVisible()) return;
-
- const { pos } = this.tabData;
-
- this.keysDown[event.keyCode] = 1;
-
- switch (event.keyCode) {
- case this.config.keymap.playAudio:
- return this.playAudio();
- case this.config.keymap.sendToAnki:
- return this.sendToAnki();
- case this.config.keymap.selectNextDictionary:
- this.sendRequest('selectNextDictionary').then(() => {
- setTimeout(() => {
- this.searchAt({x: pos.clientX, y: pos.clientY}, this.tabData);
- }, 1);
- });
- return;
- case this.config.keymap.toggleSanseidoMode:
- browser.storage.local.set({ sanseidoMode: !this.sanseidoMode });
- setTimeout(() => {
- this.searchAt({x: pos.clientX, y: pos.clientY}, this.tabData);
- }, 5);
- return true;
-
- case this.config.keymap.toggleEpwingMode:
- const originalEpwing = this.epwingMode;
- browser.storage.local.set({ epwingMode: !this.epwingMode });
- setTimeout(() => {
- if (this.epwingMode !== originalEpwing) {
- this.searchAt({x: pos.clientX, y: pos.clientY}, this.tabData);
- }
- }, 5);
- return true;
-
- case this.config.keymap.epwingNextEntry:
- return this.showNextEpwingEntry();
- case this.config.keymap.epwingPreviousEntry:
- return this.showPreviousEpwingEntry();
+ case this.config.keymap.previousDefinition:
+ this.selectedEntry--;
+ if (this.selectedEntry < 0) {
+ this.selectedEntry = this.totalEntries - 1;
}
- return false;
- }
-
- onKeyUp(event: KeyboardEvent) {
- if (this.keysDown[event.keyCode]) this.keysDown[event.keyCode] = 0;
- }
-
- onMouseDown(): void {
- this.clear();
+ this.renderPopup();
+ return true;
}
- async onMouseMove(event: MouseEvent) {
- const tabData = this.tabData;
+ return false;
+ }
- if (event.buttons !== 0) return;
+ onKeyUp(event: KeyboardEvent) {
+ if (this.keysDown[event.keyCode]) this.keysDown[event.keyCode] = 0;
+ }
- let distance = null;
- if (tabData.pos) {
- const distanceX = tabData.pos.clientX - event.clientX;
- const distanceY = tabData.pos.clientY - event.clientY;
- distance = Math.sqrt((distanceX * distanceX) + (distanceY * distanceY));
- }
+ onMouseDown(): void {
+ this.clear();
+ }
- if (event.target === tabData.previousTarget && distance && distance <= 4) {
- return;
- }
+ async onMouseMove(event: MouseEvent) {
+ const tabData = this.tabData;
- tabData.previousTarget =
event.target;
- tabData.pos = {clientX: event.clientX, clientY: event.clientY, pageX: event.pageX, pageY: event.pageY};
+ if (event.buttons !== 0) return;
- return setTimeout(() => {
- this.searchAt({x: event.clientX, y: event.clientY}, tabData, event);
- }, 1);
+ let distance = null;
+ if (tabData.pos) {
+ const distanceX = tabData.pos.clientX - event.clientX;
+ const distanceY = tabData.pos.clientY - event.clientY;
+ distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
}
- async searchAt(point: { x: number, y: number }, tabData: tabData, event?: MouseEvent) {
- const textSource = docRangeFromPoint(point);
-
- if (!textSource || !textSource.range || typeof textSource.range.startContainer.textContent === 'undefined') {
- this.clear();
- return;
- }
-
- if (event && event.target === tabData.previousTarget && textSource.equals(tabData.previousTextSource)) {
- return;
- }
-
- let charCode = textSource.range.startContainer.textContent.charCodeAt(textSource.range.startOffset);
- if ((isNaN(charCode)) ||
- ((charCode !== 0x25CB) &&
- ((charCode < 0x3001) || (charCode > 0x30FF)) &&
- ((charCode < 0x3400) || (charCode > 0x9FFF)) &&
- ((charCode < 0xF900) || (charCode > 0xFAFF)) &&
- ((charCode < 0xFF10) || (charCode > 0xFF9D)))) {
- this.clear();
- return -2;
- }
-
- tabData.previousRangeOffset = textSource.range.startOffset;
-
- tabData.previousTextSource = textSource;
-
- const textClone = textSource.clone();
- const sentenceClone = textSource.clone();
+ if (event.target === tabData.previousTarget && distance && distance <= 4) {
+ return;
+ }
- textClone.setEndOffset(20);
- const text = textClone.text();
+ tabData.previousTarget = event.target;
+ tabData.pos = {
+ clientX: event.clientX,
+ clientY: event.clientY,
+ pageX: event.pageX,
+ pageY: event.pageY,
+ };
- sentenceClone.setEndOffsetFromBeginningOfCurrentNode(textSource.range.startContainer.textContent.length + 50);
- const sentence = sentenceClone.text();
+ return setTimeout(() => {
+ this.searchAt({ x: event.clientX, y: event.clientY }, tabData, event);
+ }, 1);
+ }
+
+ async searchAt(
+ point: { x: number; y: number },
+ tabData: tabData,
+ event?: MouseEvent
+ ) {
+ const textSource = docRangeFromPoint(point);
+
+ if (
+ !textSource ||
+ !textSource.range ||
+ typeof textSource.range.startContainer.textContent === "undefined"
+ ) {
+ this.clear();
+ return;
+ }
- this.showFromText(text, sentence, textSource.range.startOffset,() => {
- textClone.setEndOffset(this.word.length);
+ if (
+ event &&
+ event.target === tabData.previousTarget &&
+ textSource.equals(tabData.previousTextSource)
+ ) {
+ return;
+ }
- const currentSelection = document.defaultView.getSelection();
- if (currentSelection.toString() !== '' && currentSelection.toString() !== tabData.selText) {
- return;
- }
- textClone.select();
- tabData.selText = textClone.text();
- }, tabData);
- };
+ let charCode = textSource.range.startContainer.textContent.charCodeAt(
+ textSource.range.startOffset
+ );
+ if (
+ isNaN(charCode) ||
+ (charCode !== 0x25cb &&
+ (charCode < 0x3001 || charCode > 0x30ff) &&
+ (charCode < 0x3400 || charCode > 0x9fff) &&
+ (charCode < 0xf900 || charCode > 0xfaff) &&
+ (charCode < 0xff10 || charCode > 0xff9d))
+ ) {
+ this.clear();
+ return -2;
+ }
- getSentenceStuff(rangeOffset: number, sentence: string): {sentence: string, sentenceStartPos: number, startOffset: number} {
- let i = rangeOffset;
+ tabData.previousRangeOffset = textSource.range.startOffset;
- let sentenceStartPos;
- let sentenceEndPos;
+ tabData.previousTextSource = textSource;
- // Find the last character of the sentence
- while (i < sentence.length) {
- if (sentence[i] === "。" || sentence[i] === "\n" || sentence[i] === "?" || sentence[i] === "!") {
- sentenceEndPos = i;
- break;
- }
- else if (i === (sentence.length - 1)) {
- sentenceEndPos = i;
- }
+ const textClone = textSource.clone();
+ const sentenceClone = textSource.clone();
- i++;
- }
+ textClone.setEndOffset(20);
+ const text = textClone.text();
- i = rangeOffset;
+ sentenceClone.setEndOffsetFromBeginningOfCurrentNode(
+ textSource.range.startContainer.textContent.length + 50
+ );
+ const sentence = sentenceClone.text();
- // Find the first character of the sentence
- while (i >= 0) {
- if (sentence[i] === "。" || sentence[i] === "\n" || sentence[i] === "?" || sentence[i] === "!") {
- sentenceStartPos = i + 1;
- break;
- }
- else if (i === 0) {
- sentenceStartPos = i;
- }
+ this.showFromText(
+ text,
+ sentence,
+ textSource.range.startOffset,
+ () => {
+ textClone.setEndOffset(this.word.length);
- i--;
+ const currentSelection = document.defaultView.getSelection();
+ if (
+ currentSelection.toString() !== "" &&
+ currentSelection.toString() !== tabData.selText
+ ) {
+ return;
}
+ textClone.select();
+ tabData.selText = textClone.text();
+ },
+ tabData
+ );
+ }
+
+ getSentenceStuff(
+ rangeOffset: number,
+ sentence: string
+ ): { sentence: string; sentenceStartPos: number; startOffset: number } {
+ let i = rangeOffset;
+
+ let sentenceStartPos;
+ let sentenceEndPos;
+
+ // Find the last character of the sentence
+ while (i < sentence.length) {
+ if (
+ sentence[i] === "。" ||
+ sentence[i] === "\n" ||
+ sentence[i] === "?" ||
+ sentence[i] === "!"
+ ) {
+ sentenceEndPos = i;
+ break;
+ } else if (i === sentence.length - 1) {
+ sentenceEndPos = i;
+ }
+
+ i++;
+ }
- // Extract the sentence
- sentence = sentence.substring(sentenceStartPos, sentenceEndPos + 1);
-
- let startingWhitespaceMatch = sentence.match(/^\s+/);
+ i = rangeOffset;
+
+ // Find the first character of the sentence
+ while (i >= 0) {
+ if (
+ sentence[i] === "。" ||
+ sentence[i] === "\n" ||
+ sentence[i] === "?" ||
+ sentence[i] === "!"
+ ) {
+ sentenceStartPos = i + 1;
+ break;
+ } else if (i === 0) {
+ sentenceStartPos = i;
+ }
+
+ i--;
+ }
- // Strip out control characters
- sentence = sentence.replace(/[\n\r\t]/g, '');
+ // Extract the sentence
+ sentence = sentence.substring(sentenceStartPos, sentenceEndPos + 1);
- let startOffset = 0;
+ let startingWhitespaceMatch = sentence.match(/^\s+/);
- // Adjust offset of selected word according to the number of
- // whitespace chars at the beginning of the sentence
- if (startingWhitespaceMatch) {
- startOffset -= startingWhitespaceMatch[0].length;
- }
+ // Strip out control characters
+ sentence = sentence.replace(/[\n\r\t]/g, "");
- // Trim
- sentence = Rikai.trim(sentence);
+ let startOffset = 0;
- return {sentence, sentenceStartPos, startOffset};
+ // Adjust offset of selected word according to the number of
+ // whitespace chars at the beginning of the sentence
+ if (startingWhitespaceMatch) {
+ startOffset -= startingWhitespaceMatch[0].length;
}
- async showFromText(text: string, sentence: string, rangeOffset: number, highlightFunction, tabData: tabData): Promise {
- const sentenceStuff = this.getSentenceStuff(rangeOffset, sentence);
- sentence = sentenceStuff.sentence;
+ // Trim
+ sentence = Rikai.trim(sentence);
- this.sentence = sentence;
+ return { sentence, sentenceStartPos, startOffset };
+ }
- if (text.length === 0) {
- this.clear();
- return;
- }
+ async showFromText(
+ text: string,
+ sentence: string,
+ rangeOffset: number,
+ highlightFunction,
+ tabData: tabData
+ ): Promise {
+ const sentenceStuff = this.getSentenceStuff(rangeOffset, sentence);
+ sentence = sentenceStuff.sentence;
- let entry = await this.sendRequest('wordSearch', text);
- if (!entry) {
- this.clear();
- return;
- }
+ this.sentence = sentence;
- if (!entry.matchLen) entry.matchLen = 1;
- this.lastFound = entry;
- this.word = text.substr(0, entry.matchLen);
+ if (text.length === 0) {
+ this.clear();
+ return;
+ }
- const wordPositionInString = rangeOffset - 1;
- this.sentenceWithBlank = sentence.substr(0, wordPositionInString) + "___"
- + sentence.substr(wordPositionInString + entry.matchLen, sentence.length);
+ let entry = (
+ await this.sendRequest("wordSearch", text)
+ );
- // @TODO: Add config check for "shouldHighlight"
- // const shouldHighlight = (!('form' in tabData.previousTarget));
- const shouldHighlight = true;
- if (shouldHighlight) {
- highlightFunction(entry);
- }
+ if (!entry) {
+ this.clear();
+ return;
+ }
- // @TODO: Add audio playing
- // @TODO: Add checks for super sticky
+ if (!entry.matchLen) entry.matchLen = 1;
+ this.lastFound = entry;
+ this.selectedEntry = 0;
+ if (isDictionaryResult(entry)) {
+ this.totalEntries = entry.data.length;
+ }
- //Sanseido mode
- if (this.sanseidoMode) {
- this.sanseidoFallback = 0;
- return this.lookupSanseido();
- }
+ this.word = text.substr(0, entry.matchLen);
- if (this.epwingMode) {
- let epwingFound = await this.lookupEpwing();
- if (epwingFound === true) {
- return;
- }
- }
+ const wordPositionInString = rangeOffset - 1;
+ this.sentenceWithBlank =
+ sentence.substr(0, wordPositionInString) +
+ "___" +
+ sentence.substr(wordPositionInString + entry.matchLen, sentence.length);
- this.showPopup(await this.makeHTML(entry), tabData.previousTarget, tabData.pos);
+ // @TODO: Add config check for "shouldHighlight"
+ // const shouldHighlight = (!('form' in tabData.previousTarget));
+ const shouldHighlight = true;
+ if (shouldHighlight) {
+ highlightFunction(entry);
}
- // Extract the first search term from the highlighted word.
- // Returns search term string or null on error.
- // forceGetReading - true = force this routine to return the reading of the word
- extractSearchTerm (forceGetReading: boolean = false): string {
- if (!this.lastFound) {
- return null;
- }
+ // @TODO: Add audio playing
+ // @TODO: Add checks for super sticky
- // Get the currently hilited entry
- let highlightedEntry = this.lastFound;
+ //Sanseido mode
+ if (this.sanseidoMode) {
+ this.sanseidoFallback = 0;
+ return this.lookupSanseido();
+ }
- let searchTerm = "";
+ if (this.epwingMode) {
+ let epwingFound = await this.lookupEpwing();
+ if (epwingFound === true) {
+ return;
+ }
+ }
- // Get the search term to use
- if (isKanjiResult(highlightedEntry)) {
- // A single kanji was selected
+ this.showPopup(
+ await this.makeHTML(entry),
+ tabData.previousTarget,
+ tabData.pos
+ );
+ }
+
+ // Extract the first search term from the highlighted word.
+ // Returns search term string or null on error.
+ // forceGetReading - true = force this routine to return the reading of the word
+ extractSearchTerm(forceGetReading: boolean = false): string {
+ if (!this.lastFound) {
+ return null;
+ }
- searchTerm = highlightedEntry.kanji;
- } else if (highlightedEntry && highlightedEntry.data[0]) {
- // An entire word was selected
+ // Get the currently hilited entry
+ let highlightedEntry = this.lastFound;
- const entryData = highlightedEntry.data[0][0].match(/^(.+?)\s+(?:\[(.*?)\])?\s*\/(.+)\//);
+ let searchTerm = "";
- // Example of what data[0][0] looks like (linebreak added by me):
- // 乃 [の] /(prt,uk) indicates possessive/verb and adjective nominalizer (nominaliser)/substituting
- // for "ga" in subordinate phrases/indicates a confident conclusion/emotional emphasis (sentence end) (fem)/(P)/
- //
- // Extract needed data from the hilited entry
- // entryData[0] = kanji/kana + kana + definition
- // entryData[1] = kanji (or kana if no kanji)
- // entryData[2] = kana (null if no kanji)
- // entryData[3] = definition
+ // Get the search term to use
+ if (isKanjiResult(highlightedEntry)) {
+ // A single kanji was selected
- if (forceGetReading) {
- if (entryData[2]) {
- searchTerm = entryData[2];
- }
- else {
- searchTerm = entryData[1];
- }
- }
- else {
- // If the highlighted word is kana, don't use the kanji.
- // Example1: if の is highlighted, use の rather than the kanji equivalent (乃)
- // Example2: if された is highlighted, use される rather then 為れる
- if (entryData[2] && !Utils.containsKanji(this.word)) {
- searchTerm = entryData[2];
- }
- else {
- searchTerm = entryData[1];
- }
- }
- }
- else {
- return null;
- }
+ searchTerm = highlightedEntry.kanji;
+ } else if (highlightedEntry && highlightedEntry.data[0]) {
+ // An entire word was selected
- return searchTerm;
+ const entryData = highlightedEntry.data[0][0].match(
+ /^(.+?)\s+(?:\[(.*?)\])?\s*\/(.+)\//
+ );
- }
-
- async lookupSanseido() {
- let searchTerm;
- const {tabData} = this;
-
- // Determine if we should use the kanji form or kana form when looking up the word
- if (this.sanseidoFallback === 0) {
- // Get this kanji form if it exists
- searchTerm = this.extractSearchTerm(false);
- }
- else if (this.sanseidoFallback === 1) {
- // Get the reading
- searchTerm = this.extractSearchTerm(true);
- }
+ // Example of what data[0][0] looks like (linebreak added by me):
+ // 乃 [の] /(prt,uk) indicates possessive/verb and adjective nominalizer (nominaliser)/substituting
+ // for "ga" in subordinate phrases/indicates a confident conclusion/emotional emphasis (sentence end) (fem)/(P)/
+ //
+ // Extract needed data from the hilited entry
+ // entryData[0] = kanji/kana + kana + definition
+ // entryData[1] = kanji (or kana if no kanji)
+ // entryData[2] = kana (null if no kanji)
+ // entryData[3] = definition
- if (!searchTerm) {
- return;
+ if (forceGetReading) {
+ if (entryData[2]) {
+ searchTerm = entryData[2];
+ } else {
+ searchTerm = entryData[1];
}
-
- // If the kanji form was requested but it returned the kana form anyway, then update the state
- if ((this.sanseidoFallback === 0) && !Utils.containsKanji(searchTerm)) {
- this.sanseidoFallback = 1;
+ } else {
+ // If the highlighted word is kana, don't use the kanji.
+ // Example1: if の is highlighted, use の rather than the kanji equivalent (乃)
+ // Example2: if された is highlighted, use される rather then 為れる
+ if (entryData[2] && !Utils.containsKanji(this.word)) {
+ searchTerm = entryData[2];
+ } else {
+ searchTerm = entryData[1];
}
-
- // Show the loading message to the screen while we fetch the entry page
- this.showPopup("Loading...", tabData.previousTarget, tabData.pos);
-
- this.abortController.abort();
- this.abortController = new AbortController();
- const { signal } = this.abortController;
- return fetch(`https://www.sanseido.biz/User/Dic/Index.aspx?TWords=${searchTerm}&st=0&DailyJJ=checkbox`, { signal })
- .then(response => response.text())
- .then(response => this.parseAndDisplaySanseido(response));
+ }
+ } else {
+ return null;
}
- enableEpwing(): void {
- if (!this.config.epwingDictionaries.length) {
- this.showPopup('No Epwing Dictionary Set');
+ return searchTerm;
+ }
- browser.storage.local.set({epwingMode: false});
- return;
- }
+ async lookupSanseido() {
+ let searchTerm;
+ const { tabData } = this;
- this.epwingTotalHits = 0;
- this.epwingCurrentHit = 0;
- this.epwingPreviousHit = 0;
- this.epwingResults = [];
+ // Determine if we should use the kanji form or kana form when looking up the word
+ if (this.sanseidoFallback === 0) {
+ // Get this kanji form if it exists
+ searchTerm = this.extractSearchTerm(false);
+ } else if (this.sanseidoFallback === 1) {
+ // Get the reading
+ searchTerm = this.extractSearchTerm(true);
}
- async disableEpwing(): Promise {
- this.epwingMode = false;
+ if (!searchTerm) {
+ return;
}
- async lookupEpwing(): Promise {
- const searchTerm = this.extractSearchTerm(false);
-
- if (!searchTerm) {
- return false;
- }
-
- let epwingText: string = await this.sendRequest('getEpwingDefinition', searchTerm);
-
- if (epwingText === "No results found") {
- return false;
- }
+ // If the kanji form was requested but it returned the kana form anyway, then update the state
+ if (this.sanseidoFallback === 0 && !Utils.containsKanji(searchTerm)) {
+ this.sanseidoFallback = 1;
+ }
- const entryFields = epwingText.split(/{ENTRY: \d+}\n/);
- let entryList = [];
+ // Show the loading message to the screen while we fetch the entry page
+ this.showPopup("Loading...", tabData.previousTarget, tabData.pos);
+
+ this.abortController.abort();
+ this.abortController = new AbortController();
+ const { signal } = this.abortController;
+ return fetch(
+ `https://www.sanseido.biz/User/Dic/Index.aspx?TWords=${searchTerm}&st=0&DailyJJ=checkbox`,
+ { signal }
+ )
+ .then((response) => response.text())
+ .then((response) => this.parseAndDisplaySanseido(response));
+ }
+
+ enableEpwing(): void {
+ if (!this.config.epwingDictionaries.length) {
+ this.showPopup("No Epwing Dictionary Set");
+
+ browser.storage.local.set({ epwingMode: false });
+ return;
+ }
- for (let i = 0; i < entryFields.length; ++i) {
- const curEntry = entryFields[i];
+ this.epwingTotalHits = 0;
+ this.epwingCurrentHit = 0;
+ this.epwingPreviousHit = 0;
+ this.epwingResults = [];
+ }
- if (curEntry.length > 0) {
- let isDuplicate = false;
+ async disableEpwing(): Promise {
+ this.epwingMode = false;
+ }
- for (let j = 0; j < entryList.length; ++j) {
- if (curEntry === entryList[j]) {
- isDuplicate = true;
- break;
- }
- }
+ async lookupEpwing(): Promise {
+ const searchTerm = this.extractSearchTerm(false);
- if (!isDuplicate) {
- entryList.push(entryFields[i]);
- }
-
- // If user wants to limit number of entries, check to see if we have enough
- // if (rcxConfig.epwingshowallentries && (entryList.length >= rcxConfig.epwingmaxentries)) {
- // break;
- // }
- }
- }
+ if (!searchTerm) {
+ return false;
+ }
- this.epwingCurrentHit = 0;
- this.epwingPreviousHit = 0;
- this.epwingTotalHits = entryList.length;
- this.epwingResults = entryList;
+ let epwingText: string = await this.sendRequest(
+ "getEpwingDefinition",
+ searchTerm
+ );
- this.showEpwingDefinition();
- return true;
+ if (epwingText === "No results found") {
+ return false;
}
- showNextEpwingEntry(): boolean {
- if (!this.epwingMode || this.epwingTotalHits < 2) {
- return false;
- }
+ const entryFields = epwingText.split(/{ENTRY: \d+}\n/);
+ let entryList = [];
- this.epwingCurrentHit++;
- if (this.epwingCurrentHit > this.epwingTotalHits - 1) {
- this.epwingCurrentHit = 0;
- }
+ for (let i = 0; i < entryFields.length; ++i) {
+ const curEntry = entryFields[i];
- this.showEpwingDefinition();
- return true;
- }
+ if (curEntry.length > 0) {
+ let isDuplicate = false;
- showPreviousEpwingEntry(): boolean {
- if (!this.epwingMode || this.epwingTotalHits < 2) {
- return false;
+ for (let j = 0; j < entryList.length; ++j) {
+ if (curEntry === entryList[j]) {
+ isDuplicate = true;
+ break;
+ }
}
- this.epwingCurrentHit--;
- if (this.epwingCurrentHit < 0) {
- this.epwingCurrentHit = this.epwingTotalHits - 1;
+ if (!isDuplicate) {
+ entryList.push(entryFields[i]);
}
- this.showEpwingDefinition();
- return false;
+ // If user wants to limit number of entries, check to see if we have enough
+ // if (rcxConfig.epwingshowallentries && (entryList.length >= rcxConfig.epwingmaxentries)) {
+ // break;
+ // }
+ }
}
- async showEpwingDefinition() {
- if (!isDictionaryResult(this.lastFound)) return;
-
- const {epwingCurrentHit, epwingResults, tabData} = this;
+ this.epwingCurrentHit = 0;
+ this.epwingPreviousHit = 0;
+ this.epwingTotalHits = entryList.length;
+ this.epwingResults = entryList;
- const epwingDefinitionText = epwingResults[epwingCurrentHit];
- const entry = await this.formatEpwingEntry(epwingDefinitionText, true, true);
+ this.showEpwingDefinition();
+ return true;
+ }
- this.lastFound.data[0][0] = this.lastFound.data[0][0]
- .replace(/\/.+\//g, "/" + await this.formatEpwingEntry(epwingDefinitionText) + "/");
-
- this.showPopup(entry, tabData.previousTarget, tabData.pos);
+ showNextEpwingEntry(): boolean {
+ if (!this.epwingMode || this.epwingTotalHits < 2) {
+ return false;
}
- async formatEpwingEntry(entryText, showHeader?: boolean, showEntryNumber?: boolean): Promise {
+ this.epwingCurrentHit++;
+ if (this.epwingCurrentHit > this.epwingTotalHits - 1) {
+ this.epwingCurrentHit = 0;
+ }
- //TODO: Add removing user inputted regex
- //TODO: Add "Header" (Color, pitch and so on)
- if (showHeader) {
- //TODO: Add Frequency Information
+ this.showEpwingDefinition();
+ return true;
+ }
- let entryNumber = "";
- if (showEntryNumber) {
- entryNumber = `(${this.epwingCurrentHit + 1} / ${this.epwingTotalHits})`;
- }
+ showPreviousEpwingEntry(): boolean {
+ if (!this.epwingMode || this.epwingTotalHits < 2) {
+ return false;
+ }
- //TODO: Add known word indicator
- //TODO: Add Showing Conjugation
- //TODO: Add showing dictionary title and number
- entryText = `${entryNumber}
${entryText}`;
- }
+ this.epwingCurrentHit--;
+ if (this.epwingCurrentHit < 0) {
+ this.epwingCurrentHit = this.epwingTotalHits - 1;
+ }
- //TODO: Add Max Lines
+ this.showEpwingDefinition();
+ return false;
+ }
+
+ async showEpwingDefinition() {
+ if (!isDictionaryResult(this.lastFound)) return;
+
+ const { epwingCurrentHit, epwingResults, tabData } = this;
+
+ const epwingDefinitionText = epwingResults[epwingCurrentHit];
+ const entry = await this.formatEpwingEntry(
+ epwingDefinitionText,
+ true,
+ true
+ );
+
+ this.lastFound.data[0][0] = this.lastFound.data[0][0].replace(
+ /\/.+\//g,
+ "/" + (await this.formatEpwingEntry(epwingDefinitionText)) + "/"
+ );
+
+ this.showPopup(entry, tabData.previousTarget, tabData.pos);
+ }
+
+ async formatEpwingEntry(
+ entryText,
+ showHeader?: boolean,
+ showEntryNumber?: boolean
+ ): Promise {
+ //TODO: Add removing user inputted regex
+ //TODO: Add "Header" (Color, pitch and so on)
+ if (showHeader) {
+ //TODO: Add Frequency Information
+
+ let entryNumber = "";
+ if (showEntryNumber) {
+ entryNumber = `(${this.epwingCurrentHit + 1} / ${
+ this.epwingTotalHits
+ })`;
+ }
+
+ //TODO: Add known word indicator
+ //TODO: Add Showing Conjugation
+ //TODO: Add showing dictionary title and number
+ entryText = `${entryNumber}
${entryText}`;
+ }
- //TODO: Add "epwingStripNewLines" config
- if (false) {
- entryText = entryText.replace(/\n/g, " ");
- } else {
- entryText = entryText.replace(/\n/g, "
");
- }
+ //TODO: Add Max Lines
- return entryText;
+ //TODO: Add "epwingStripNewLines" config
+ if (false) {
+ entryText = entryText.replace(/\n/g, " ");
+ } else {
+ entryText = entryText.replace(/\n/g, "
");
}
- async parseAndDisplaySanseido(response) {
- if (!isDictionaryResult(this.lastFound)) return;
-
- // Create DOM tree from entry page text
- // var domPars = rcxMain.htmlParser(entryPageText);
- const {tabData} = this;
- const parser = new DOMParser();
- const document = parser.parseFromString(response, 'text/html');
- let domPars = document.body;
-
- // Get list of div elements
- const divList = domPars.getElementsByTagName("div");
-
- // Will be set if the entry page actually contains a definition
- let entryFound = false;
-
- // Find the div that contains the definition
- for (let divIdx = 0; divIdx < divList.length; divIdx++) {
- // Did we reach the div the contains the definition?
- if (divList[divIdx].className === "NetDicBody") {
- entryFound = true;
-
- // rcxDebug.echo("Raw definition: " + divList[divIdx].innerHTML);
-
- // Will contain the final parsed definition text
- let defText = "";
-
- // A list of all child nodes in the div
- const childList = divList[divIdx].childNodes;
-
- // Set when we need to end the parse
- let defFinished = false;
-
- // Extract the definition from the div's child nodes
- for (let nodeIdx = 0; nodeIdx < childList.length && !defFinished; nodeIdx++) {
- // Is this a b element?
- if (childList[nodeIdx].nodeName.toLowerCase() === "b") {
- // How many child nodes does this b element have?
- if (childList[nodeIdx].childNodes.length === 1) {
- // Check for definition number: [1], [2], ... and add to def
- const defNum = childList[nodeIdx].childNodes[0].nodeValue.match(/[([1234567890]+)]/);
-
- if (defNum) {
- defText += "
" + RegExp.$1;
- }
- else {
- // Check for sub-definition number: (1), (2), ... and add to def
- const subDefNum = childList[nodeIdx].childNodes[0].nodeValue.match(/(([1234567890]+))/);
-
- if (subDefNum) {
- // Convert sub def number to circled number
- defText += Utils.convertIntegerToCircledNumStr(Utils.convertJapNumToInteger(RegExp.$1));
- }
- }
- }
- else // This b element has more than one child node
- {
- // Check the b children for any spans. A span marks the start
- // of non-definition portion, so end the parse.
- for (let bIdx = 0; bIdx < childList[nodeIdx].childNodes.length; bIdx++) {
- if (childList[nodeIdx].childNodes[bIdx].nodeName.toLowerCase() === "span") {
- defFinished = true;
- }
- }
- }
- }
-
- // Have we finished parsing the text?
- if (defFinished) {
- break;
- }
-
- // If the current element is text, add it to the definition
- if ((childList[nodeIdx].nodeName.toLowerCase() === "#text")
- && (Rikai.trim(childList[nodeIdx].nodeValue) !== "")) {
- defText += childList[nodeIdx].nodeValue;
- }
- }
+ return entryText;
+ }
+
+ async parseAndDisplaySanseido(response) {
+ if (!isDictionaryResult(this.lastFound)) return;
+
+ // Create DOM tree from entry page text
+ // var domPars = rcxMain.htmlParser(entryPageText);
+ const { tabData } = this;
+ const parser = new DOMParser();
+ const document = parser.parseFromString(response, "text/html");
+ let domPars = document.body;
+
+ // Get list of div elements
+ const divList = domPars.getElementsByTagName("div");
+
+ // Will be set if the entry page actually contains a definition
+ let entryFound = false;
+
+ // Find the div that contains the definition
+ for (let divIdx = 0; divIdx < divList.length; divIdx++) {
+ // Did we reach the div the contains the definition?
+ if (divList[divIdx].className === "NetDicBody") {
+ entryFound = true;
+
+ // rcxDebug.echo("Raw definition: " + divList[divIdx].innerHTML);
+
+ // Will contain the final parsed definition text
+ let defText = "";
+
+ // A list of all child nodes in the div
+ const childList = divList[divIdx].childNodes;
+
+ // Set when we need to end the parse
+ let defFinished = false;
+
+ // Extract the definition from the div's child nodes
+ for (
+ let nodeIdx = 0;
+ nodeIdx < childList.length && !defFinished;
+ nodeIdx++
+ ) {
+ // Is this a b element?
+ if (childList[nodeIdx].nodeName.toLowerCase() === "b") {
+ // How many child nodes does this b element have?
+ if (childList[nodeIdx].childNodes.length === 1) {
+ // Check for definition number: [1], [2], ... and add to def
+ const defNum = childList[nodeIdx].childNodes[0].nodeValue.match(
+ /[([1234567890]+)]/
+ );
+
+ if (defNum) {
+ defText += "
" + RegExp.$1;
+ } else {
+ // Check for sub-definition number: (1), (2), ... and add to def
+ const subDefNum = childList[
+ nodeIdx
+ ].childNodes[0].nodeValue.match(
+ /(([1234567890]+))/
+ );
- // If the definition is blank (search ばかり for example), fallback
- if (defText.length === 0) {
- // Set to a state that will ensure fallback to default JMDICT popup
- this.sanseidoFallback = 1;
- entryFound = false;
- break;
+ if (subDefNum) {
+ // Convert sub def number to circled number
+ defText += Utils.convertIntegerToCircledNumStr(
+ Utils.convertJapNumToInteger(RegExp.$1)
+ );
}
-
- var jdicCode = "";
-
- // Get the part-of-speech and other JDIC codes
- this.lastFound.data[0][0].match(/\/(\(.+?\) ).+\//);
-
- if (RegExp.$1) {
- jdicCode = RegExp.$1;
+ }
+ } // This b element has more than one child node
+ else {
+ // Check the b children for any spans. A span marks the start
+ // of non-definition portion, so end the parse.
+ for (
+ let bIdx = 0;
+ bIdx < childList[nodeIdx].childNodes.length;
+ bIdx++
+ ) {
+ if (
+ childList[nodeIdx].childNodes[bIdx].nodeName.toLowerCase() ===
+ "span"
+ ) {
+ defFinished = true;
}
-
- // Replace the definition with the one we parsed from sanseido
- this.lastFound.data[0][0] = this.lastFound.data[0][0]
- .replace(/\/.+\//g, "/" + jdicCode + defText + "/");
-
- // Remove all words except for the one we just looked up
- this.lastFound.data = [this.lastFound.data[0]];
-
- // Prevent the "..." from being displayed at the end of the popup text
- this.lastFound.more = false;
-
- // Show the definition
- this.showPopup(await this.makeHTML(this.lastFound),
- tabData.previousTarget, tabData.pos);
-
- // Entry found, stop looking
- break;
+ }
}
-
+ }
+
+ // Have we finished parsing the text?
+ if (defFinished) {
+ break;
+ }
+
+ // If the current element is text, add it to the definition
+ if (
+ childList[nodeIdx].nodeName.toLowerCase() === "#text" &&
+ Rikai.trim(childList[nodeIdx].nodeValue) !== ""
+ ) {
+ defText += childList[nodeIdx].nodeValue;
+ }
}
- // If the entry was not on sanseido, either try to lookup the kana form of the word
- // or display default JMDICT popup
- if (!entryFound) {
- this.sanseidoFallback++;
-
- if (this.sanseidoFallback < 2) {
- // Set a timer to lookup again using the kana form of the word instead
- window.setTimeout
- (
- () => {
- this.lookupSanseido();
- }, 10
- );
- }
- else {
- // Fallback to the default non-sanseido dictionary that comes with rikaichan (JMDICT)
- this.showPopup(await this.makeHTML(this.lastFound), tabData.previousTarget, tabData.pos);
- }
+ // If the definition is blank (search ばかり for example), fallback
+ if (defText.length === 0) {
+ // Set to a state that will ensure fallback to default JMDICT popup
+ this.sanseidoFallback = 1;
+ entryFound = false;
+ break;
}
- }
-
- static trim(text: string): string {
- return text.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
- }
-
- clear(): void {
- this.abortController.abort();
- this.abortController = new AbortController();
- setTimeout(() => {
- docImposterDestroy();
- }, 500);
- this.clearPopup();
- this.clearHighlight();
- }
- clearPopup(): void {
- this.getPopup().style.display = 'none';
- }
+ var jdicCode = "";
- clearHighlight(): void {
- const tabData = this.tabData;
+ // Get the part-of-speech and other JDIC codes
+ this.lastFound.data[0][0].match(/\/(\(.+?\) ).+\//);
- const selection = document.defaultView.getSelection();
- //Changed !selection.isCollapsed to selection.toString() !== '' because of Chrome issue with input text boxes
- if (selection && selection.toString() !== '' && tabData.selText !== selection.toString()) {
- return;
+ if (RegExp.$1) {
+ jdicCode = RegExp.$1;
}
- if (tabData.previousTextSource) {
- tabData.previousTextSource.deselect();
- tabData.previousTextSource = null;
- return;
- }
- }
+ // Replace the definition with the one we parsed from sanseido
+ this.lastFound.data[0][0] = this.lastFound.data[0][0].replace(
+ /\/.+\//g,
+ "/" + jdicCode + defText + "/"
+ );
- async sendRequest(type: string, content: any = ''): Promise {
- return browser.runtime.sendMessage({type, content}).then(response => {
- if (typeof response === 'undefined') {
- this.showPopup('If you have the options page for RikaiRebuilt, please close that. Word search' +
- ' doesn\'t work properly when the options tab is open');
- return -1;
- }
+ // Remove all words except for the one we just looked up
+ this.lastFound.data = [this.lastFound.data[0]];
- return response.response;
- });
- };
+ // Prevent the "..." from being displayed at the end of the popup text
+ this.lastFound.more = false;
- createPopup(): void {
- if (this.hasPopup()) return;
+ // Show the definition
+ this.showPopup(
+ await this.makeHTML(this.lastFound),
+ tabData.previousTarget,
+ tabData.pos
+ );
- const popup = this.document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
- popup.setAttribute('id', this.popupId);
- popup.setAttribute('style', 'display: none;');
- popup.setAttribute('class', `rikai-${this.config.theme}`);
- document.documentElement.appendChild(popup);
+ // Entry found, stop looking
+ break;
+ }
}
- getPopup(): HTMLElement {
- if (!this.hasPopup()) this.createPopup();
-
- return this.document.getElementById(this.popupId);
+ // If the entry was not on sanseido, either try to lookup the kana form of the word
+ // or display default JMDICT popup
+ if (!entryFound) {
+ this.sanseidoFallback++;
+
+ if (this.sanseidoFallback < 2) {
+ // Set a timer to lookup again using the kana form of the word instead
+ window.setTimeout(() => {
+ this.lookupSanseido();
+ }, 10);
+ } else {
+ // Fallback to the default non-sanseido dictionary that comes with rikaichan (JMDICT)
+ this.showPopup(
+ await this.makeHTML(this.lastFound),
+ tabData.previousTarget,
+ tabData.pos
+ );
+ }
}
-
- hasPopup(): HTMLElement {
- return this.document.getElementById(this.popupId);
+ }
+
+ static trim(text: string): string {
+ return text.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
+ }
+
+ clear(): void {
+ this.abortController.abort();
+ this.abortController = new AbortController();
+ setTimeout(() => {
+ docImposterDestroy();
+ }, 500);
+ this.clearPopup();
+ this.clearHighlight();
+ }
+
+ clearPopup(): void {
+ this.getPopup().style.display = "none";
+ }
+
+ clearHighlight(): void {
+ const tabData = this.tabData;
+
+ const selection = document.defaultView.getSelection();
+ //Changed !selection.isCollapsed to selection.toString() !== '' because of Chrome issue with input text boxes
+ if (
+ selection &&
+ selection.toString() !== "" &&
+ tabData.selText !== selection.toString()
+ ) {
+ return;
}
- showPopup(textToShow: string, previousTarget?: HTMLElement, position?: position): void {
- let {pageX, pageY, clientX, clientY} = position || {pageX: 10, pageY: 10, clientX: 10, clientY: 10};
- const popup = this.getPopup();
-
- popup.innerHTML = `\n${textToShow}`;
- popup.style.display = 'block';
- popup.style.maxWidth = '600px';
-
- if (previousTarget && (typeof previousTarget !== 'undefined')
- && previousTarget.parentNode && (typeof previousTarget.parentNode !== 'undefined')) {
-
- let width = popup.offsetWidth;
- let height = popup.offsetHeight;
-
- popup.style.top = '-1000px';
- popup.style.left = '0px';
- popup.style.display = '';
-
- //TODO: Add alt-views here
- //TODO: Stuff for box object and zoom?
- //TODO: Check for Option Element? What?
-
- if (clientX + width > window.innerWidth - 20) {
- pageX = window.innerWidth - width - 20;
- if (pageX < 0) pageX = 0;
- }
-
- let v = 25;
- if (previousTarget.title && previousTarget.title !== '') v += 20;
-
- if (clientY + v + height > window.innerHeight) {
- let t = pageY - height - 30;
- if (t >= 0) pageY = t;
- } else {
- pageY += v;
- }
- }
-
- popup.style.left = pageX + 'px';
- popup.style.top = pageY + 'px';
+ if (tabData.previousTextSource) {
+ tabData.previousTextSource.deselect();
+ tabData.previousTextSource = null;
+ return;
+ }
+ }
+
+ async sendRequest(type: string, content: any = ""): Promise {
+ return browser.runtime.sendMessage({ type, content }).then((response) => {
+ if (typeof response === "undefined") {
+ this.showPopup(
+ "If you have the options page for RikaiRebuilt, please close that. Word search" +
+ " doesn't work properly when the options tab is open"
+ );
+ return -1;
+ }
+
+ return response.response;
+ });
+ }
+
+ createPopup(): void {
+ if (this.hasPopup()) return;
+
+ const popup = this.document.createElementNS(
+ "http://www.w3.org/1999/xhtml",
+ "div"
+ );
+ popup.setAttribute("id", this.popupId);
+ popup.setAttribute("style", "display: none;");
+ popup.setAttribute("class", `rikai-${this.config.theme}`);
+ document.documentElement.appendChild(popup);
+ }
+
+ getPopup(): HTMLElement {
+ if (!this.hasPopup()) this.createPopup();
+
+ return this.document.getElementById(this.popupId);
+ }
+
+ hasPopup(): HTMLElement {
+ return this.document.getElementById(this.popupId);
+ }
+
+ async renderPopup() {
+ return this.showPopup(
+ await this.makeHTML(this.lastFound),
+ this.tabData.previousTarget,
+ this.tabData.pos,
+ true
+ );
+ }
+
+ showPopup(
+ textToShow: string,
+ previousTarget?: HTMLElement,
+ position?: position,
+ rerender = false
+ ): void {
+ let { pageX, pageY, clientX, clientY } = position || {
+ pageX: 10,
+ pageY: 10,
+ clientX: 10,
+ clientY: 10,
+ };
+ const popup = this.getPopup();
+
+ popup.innerHTML = `\n${textToShow}`;
+ popup.style.display = "block";
+ popup.style.maxWidth = "600px";
+
+ if (
+ previousTarget &&
+ typeof previousTarget !== "undefined" &&
+ previousTarget.parentNode &&
+ typeof previousTarget.parentNode !== "undefined"
+ ) {
+ let width = popup.offsetWidth;
+ let height = popup.offsetHeight;
+
+ popup.style.top = "-1000px";
+ popup.style.left = "0px";
+ popup.style.display = "";
+
+ //TODO: Add alt-views here
+ //TODO: Stuff for box object and zoom?
+ //TODO: Check for Option Element? What?
+
+ if (clientX + width > window.innerWidth - 20) {
+ pageX = window.innerWidth - width - 20;
+ if (pageX < 0) pageX = 0;
+ }
+
+ let v = 25;
+ if (previousTarget.title && previousTarget.title !== "") v += 20;
+
+ if (clientY + v + height > window.innerHeight) {
+ let t = pageY - height - 30;
+ if (t >= 0) pageY = t;
+ } else {
+ pageY += v;
+ }
}
- async makeHTML(entries: SearchResults) {
- let k;
- let entry;
- let returnValue = [];
- let c, s;
- let i, j;
-
- if (entries == null) return '';
-
- if (isKanjiResult(entries)) {
- let yomi;
- let box;
- let nums;
-
- yomi = entries.onkun.replace(/\.([^\u3001]+)/g, '$1');
- if (entries.nanori.length) {
- yomi += `
\u540D\u4E57\u308A ${entries.nanori}`;
- }
- if (entries.bushumei.length) {
- yomi += `
\u90E8\u9996\u540D ${entries.bushumei}`;
- }
+ if (rerender) {
+ pageX = this.lastPopupPosition.left;
+ pageY = this.lastPopupPosition.top;
+ }
- let kanjiUse = entries.misc['G'];
- switch (kanjiUse) {
- case 8:
- kanjiUse = 'general
use';
- break;
- case 9:
- kanjiUse = 'name
use';
- break;
- default:
- kanjiUse = isNaN(kanjiUse) ? '-' : ('grade
' + kanjiUse);
- break;
- }
- box = `
- radical ${entries.radical.charAt(0)} ${entries.radicalNumber} |
+ popup.style.left = pageX + "px";
+ popup.style.top = pageY + "px";
+
+ this.lastPopupPosition = { left: pageX, top: pageY };
+ }
+
+ async makeHTML(entries: SearchResults) {
+ let k;
+ let entry;
+ let returnValue = [];
+ let c, s;
+ let i, j;
+
+ if (entries == null) return "";
+
+ if (isKanjiResult(entries)) {
+ let yomi;
+ let box;
+ let nums;
+
+ yomi = entries.onkun.replace(
+ /\.([^\u3001]+)/g,
+ '$1'
+ );
+ if (entries.nanori.length) {
+ yomi += `
\u540D\u4E57\u308A ${entries.nanori}`;
+ }
+ if (entries.bushumei.length) {
+ yomi += `
\u90E8\u9996\u540D ${entries.bushumei}`;
+ }
+
+ let kanjiUse = entries.misc["G"];
+ switch (kanjiUse) {
+ case 8:
+ kanjiUse = "general
use";
+ break;
+ case 9:
+ kanjiUse = "name
use";
+ break;
+ default:
+ kanjiUse = isNaN(kanjiUse) ? "-" : "grade
" + kanjiUse;
+ break;
+ }
+ box = `
+ radical ${entries.radical.charAt(0)} ${
+ entries.radicalNumber
+ } |
${kanjiUse} |
- freq ${entries.misc['F'] ? entries.misc['F'] : '-'} |
- strokes ${entries.misc['S']} |
+ freq ${
+ entries.misc["F"] ? entries.misc["F"] : "-"
+ } |
+ strokes ${entries.misc["S"]} |
`;
- if (this.config.showKanjiComponents) {
- k = entries.radical.split('\t');
- box += '' +
- '' + k[0] + ' | ' +
- '' + k[2] + ' | ' +
- '' + k[3] + ' |
';
- j = 1;
- for (const radical of entries.radicals) {
- k = radical.split('\t');
- c = ' class="k-bbox-' + (j ^= 1);
- box += '' + k[0] + ' | ' +
- '' + k[2] + ' | ' +
- '' + k[3] + ' |
';
- }
- box += '
';
- }
-
- nums = '';
- j = 0;
-
- const numList = {
- 'H': ['showKanjiHalpern', 'Halpern'],
- 'L': ['showKanjiHeisig', 'Heisig'],
- 'E': ['showKanjiHenshall', 'Henshall'],
- 'DK': ['showKanjiLearnersDictionary', 'Kanji Learners Dictionary'],
- 'N': ['showKanjiNelson', 'Nelson'],
- 'V': ['showKanjiNewNelson', 'New Nelson'],
- 'Y': ['showKanjiPinYin', 'PinYin'],
- 'P': ['showKanjiSkipPattern', 'Skip Pattern'],
- 'IN': ['showKanjiTurtleAndKana', 'Turtle Kanji & Kana'],
- 'I': ['showKanjiTurtleDictionary', 'Turtle Kanji Dictionary'],
- 'U': ['showKanjiUnicode', 'Unicode']
- };
-
- for (const i in numList) {
- const configName = numList[i][0];
- const displayName = numList[i][1];
-
- if (this.config[configName]) {
- s = entries.misc[i]; // The number
- c = ' class="k-mix-td' + (j ^= 1) + '"';
-
- if (configName === "showKanjiHeisig") {
- const revTkLink = 'http://kanji.koohii.com/study/kanji/' + entries.kanji;
- nums += '
' + 'Heisig' + ' | ' + ''
- + (s ? s : '-') + '' + ' |
';
- }
- else {
- nums += '' + displayName + ' | ' + (s ? s : '-') + ' |
';
- }
- }
- }
- if (nums.length) nums = '';
-
- returnValue.push('');
- returnValue.push(box);
- returnValue.push('' + entries.kanji + ' ');
- if (!this.config.hideDefinitions) returnValue.push('' + entries.eigo + ' ');
- returnValue.push('' + yomi + ' ');
- returnValue.push(' |
' + nums + ' |
');
- return returnValue.join('');
+ if (this.config.showKanjiComponents) {
+ k = entries.radical.split("\t");
+ box +=
+ '' +
+ '' +
+ k[0] +
+ " | " +
+ '' +
+ k[2] +
+ " | " +
+ '' +
+ k[3] +
+ " |
";
+ j = 1;
+ for (const radical of entries.radicals) {
+ k = radical.split("\t");
+ c = ' class="k-bbox-' + (j ^= 1);
+ box +=
+ "' +
+ k[0] +
+ " | " +
+ "' +
+ k[2] +
+ " | " +
+ "' +
+ k[3] +
+ " |
";
}
-
- let translationText = '';
-
- if (entries.names) {
- return transformNameEntriesToHtml(entries, this.config);
- // return this.transformNamesToHtml(entries);
- }
-
- if (entries.title) {
- returnValue.push(`${entries.title}
`);
+ box += "
";
+ }
+
+ nums = "";
+ j = 0;
+
+ const numList = {
+ H: ["showKanjiHalpern", "Halpern"],
+ L: ["showKanjiHeisig", "Heisig"],
+ E: ["showKanjiHenshall", "Henshall"],
+ DK: ["showKanjiLearnersDictionary", "Kanji Learners Dictionary"],
+ N: ["showKanjiNelson", "Nelson"],
+ V: ["showKanjiNewNelson", "New Nelson"],
+ Y: ["showKanjiPinYin", "PinYin"],
+ P: ["showKanjiSkipPattern", "Skip Pattern"],
+ IN: ["showKanjiTurtleAndKana", "Turtle Kanji & Kana"],
+ I: ["showKanjiTurtleDictionary", "Turtle Kanji Dictionary"],
+ U: ["showKanjiUnicode", "Unicode"],
+ };
+
+ for (const i in numList) {
+ const configName = numList[i][0];
+ const displayName = numList[i][1];
+
+ if (this.config[configName]) {
+ s = entries.misc[i]; // The number
+ c = ' class="k-mix-td' + (j ^= 1) + '"';
+
+ if (configName === "showKanjiHeisig") {
+ const revTkLink =
+ "http://kanji.koohii.com/study/kanji/" + entries.kanji;
+ nums +=
+ "" +
+ "Heisig' +
+ " | " +
+ "' +
+ (s ? s : "-") +
+ "" +
+ " |
";
+ } else {
+ nums +=
+ "" +
+ displayName +
+ " | " +
+ (s ? s : "-") +
+ " |
";
+ }
}
+ }
+ if (nums.length) nums = '";
+
+ returnValue.push('');
+ returnValue.push(box);
+ returnValue.push(
+ '' + entries.kanji + " "
+ );
+ if (!this.config.hideDefinitions)
+ returnValue.push('' + entries.eigo + " ");
+ returnValue.push('' + yomi + " ");
+ returnValue.push(" |
" + nums + " |
");
+ return `${returnValue.join("")}
`;
+ }
- for (i = 0; i < entries.data.length; ++i) {
- let previousKanji = '';
- let kanaText = '';
- let previousDefinition = '';
+ let translationText = "";
- entry = entries.data[i][0].match(/^(.+?)\s+(?:\[(.*?)\])?\s*\/([\S\s]+)\//);
- let [ _, kanji, kana, definition ] = entry;
- if (!entry) continue;
+ if (entries.names) {
+ return transformNameEntriesToHtml(entries, this.config);
+ }
- if (previousDefinition !== definition) {
- returnValue.push(translationText);
- previousKanji = kanaText = '';
- }
- else {
- kanaText = translationText.length ? '
' : '';
- }
+ if (entries.title) {
+ returnValue.push(`${entries.title}
`);
+ }
- if (kana) {
- if (previousKanji === kanji) kanaText = '\u3001 ' + kana + '';
- else kanaText += '' + kanji + ' ' + kana + '';
- previousKanji = kanji;
- } else {
- kanaText += '' + kanji + '';
- previousKanji = '';
- }
- returnValue.push(kanaText);
+ type Definition = {
+ kanji?: string;
+ kana?: string;
+ conjugation?: string;
+ definitions: string[];
+ };
+ const transformed = entries.data.reduce(
+ (acc: Definition[], entry): Definition[] => {
+ const conjugation = entry[1];
+ let [_, kanji, kana, definition] = entry[0].match(
+ /^(.+?)\s+(?:\[(.*?)\])?\s*\/([\S\s]+)\//
+ );
+
+ const existing = acc.find(
+ (def) =>
+ def.kanji === kanji &&
+ def.kana === kana &&
+ def.conjugation === conjugation
+ );
+ if (!existing) {
+ acc.push({
+ kanji,
+ kana,
+ definitions: [definition],
+ conjugation,
+ });
+ } else {
+ existing.definitions.push(definition);
+ }
- //TODO: Add config usage here
- // Add pitch accent right after the reading
- if (this.config.showPitchAccent) {
- const pitchAccent = await this.sendRequest('getPitch', { expression: kanji, reading: kana });
+ return acc;
+ },
+ []
+ );
- if (pitchAccent && (pitchAccent.length > 0)) {
- returnValue.push(' ' + pitchAccent + '');
- }
- }
+ const definitionTransformer = (definition: string): string => {
+ if (this.config.hideDefinitions) return "";
- if (entries.data[i][1]) returnValue.push(' (' + entries.data[i][1] + ')');
+ definition = definition.replace(/\//g, "; ");
- // Add frequency
- if (this.config.showFrequency) {
- const freqExpression = kanji;
- let freqReading = kana;
+ if (!this.config.showWordTypeIndicator)
+ definition = definition.replace(/^\([^)]+\)\s*/, "");
+ if (!this.config.showPopularWordIndicator)
+ definition = definition.replace("; (P)", "");
- if (freqReading === null) {
- freqReading = freqExpression;
- }
+ definition = definition.replace(/\n/g, "
");
- const freq = await this.getFrequency(freqExpression, freqReading, i === 0);
+ if (!definition.length) return "";
- if (freq && freq.length > 0) {
- const frequencyClass = Rikai.getFrequencyStyle(freq);
- returnValue.push(' ' + freq + '');
- }
- }
+ return `${definition}`;
+ };
- //TODO: Add config usage here
- previousDefinition = definition;
- if (this.config.hideDefinitions) {
- translationText = '
';
- } else {
- translationText = previousDefinition.replace(/\//g, '; ');
- //TODO: Add config here
- if (!this.config.showWordTypeIndicator) translationText = translationText.replace(/^\([^)]+\)\s*/, '');
- if (!this.config.showPopularWordIndicator) translationText = translationText.replace('; (P)', '');
- translationText = translationText.replace(/\n/g, '
');
- translationText = '
' + translationText + '
';
+ const transformedEntries = await Promise.all(
+ transformed.map(
+ async ({ kana, kanji, definitions, conjugation }, index) => {
+ let title;
+ let pitch = "";
+ const conjugationString = conjugation
+ ? `(${entries.data[i][1]})`
+ : "";
+ const definitionString = definitions
+ .map(definitionTransformer)
+ .filter((def) => !!def.length)
+ .join("
");
+
+ let frequencyString = "";
+ if (!kana) {
+ title = `${kanji}`;
+ } else {
+ title = `${kanji} ${kana}`;
+ }
+
+ if (this.config.showPitchAccent) {
+ const pitchAccent = await this.sendRequest("getPitch", {
+ expression: kanji,
+ reading: kana,
+ });
+
+ if (pitchAccent && pitchAccent.length > 0) {
+ pitch = `${pitchAccent}`;
}
- }
- returnValue.push(translationText);
- if (entries.more) returnValue.push('...
');
+ }
- return returnValue.join('');
- }
+ if (this.config.showFrequency) {
+ const freq = await this.getFrequency(kanji, kana || kanji, i === 0);
- static getFrequencyStyle(inFreqNum) {
- let freqNum = inFreqNum.replace(/_r/g, "");
-
- let freqStyle = 'w-freq-rare';
+ if (freq?.length) {
+ const frequencyClass = Rikai.getFrequencyStyle(freq);
+ frequencyString = ` ${freq}`;
+ }
+ }
- if (freqNum <= 5000) {
- freqStyle = "w-freq-very-common";
- }
- else if (freqNum <= 10000) {
- freqStyle = "w-freq-common";
- }
- else if (freqNum <= 20000) {
- freqStyle = "w-freq-uncommon";
+ return `${title} ${pitch} ${conjugationString} ${frequencyString}
${definitionString}
`;
}
+ )
+ );
- return freqStyle;
+ if (entries.more) {
+ transformedEntries.push(`...
`);
}
- async getFrequency(inExpression: string, inReading: string, useHighlightedWord: boolean) {
- const highlightedWord = this.word;
- return this.sendRequest('getFrequency', {inExpression, inReading, useHighlightedWord, highlightedWord});
- }
+ return transformedEntries.join("");
+ }
- sendToAnki(): boolean {
- const word = this.word;
- const sentence = this.sentence;
- const sentenceWithBlank = this.sentenceWithBlank;
- const entry = this.lastFound;
- const pageTitle = window.document.title;
- const sourceUrl = window.location.href;
+ static getFrequencyStyle(inFreqNum) {
+ let freqNum = inFreqNum.replace(/_r/g, "");
- this.sendRequest('sendToAnki', {word, sentence, sentenceWithBlank, entry, pageTitle, sourceUrl});
- return true;
- }
+ let freqStyle = "w-freq-rare";
- isVisible(): boolean {
- const popup = this.getPopup();
- return popup && popup.style.display !== 'none';
+ if (freqNum <= 5000) {
+ freqStyle = "w-freq-very-common";
+ } else if (freqNum <= 10000) {
+ freqStyle = "w-freq-common";
+ } else if (freqNum <= 20000) {
+ freqStyle = "w-freq-uncommon";
}
- playAudio(): boolean {
- const {lastFound} = this;
-
- if (!lastFound) return false;
-
- this.sendRequest('playAudio', lastFound);
- return true;
- }
-
- setSanseidoMode(sanseidoMode: boolean): void {
- this.sanseidoMode = sanseidoMode || false;
- }
-
- setEpwingMode(epwingMode: boolean): void {
- if (epwingMode) {
- this.enableEpwing();
- } else {
- this.disableEpwing();
- }
+ return freqStyle;
+ }
+
+ async getFrequency(
+ inExpression: string,
+ inReading: string,
+ useHighlightedWord: boolean
+ ) {
+ const highlightedWord = this.word;
+ return this.sendRequest("getFrequency", {
+ inExpression,
+ inReading,
+ useHighlightedWord,
+ highlightedWord,
+ });
+ }
+
+ sendToAnki(): boolean {
+ const word = this.word;
+ const sentence = this.sentence;
+ const sentenceWithBlank = this.sentenceWithBlank;
+ const entry = this.lastFound;
+ const pageTitle = window.document.title;
+ const sourceUrl = window.location.href;
+
+ this.sendRequest("sendToAnki", {
+ word,
+ sentence,
+ sentenceWithBlank,
+ entry,
+ pageTitle,
+ sourceUrl,
+ selected: this.selectedEntry,
+ });
+ return true;
+ }
+
+ isVisible(): boolean {
+ const popup = this.getPopup();
+ return popup && popup.style.display !== "none";
+ }
+
+ playAudio(): boolean {
+ const { lastFound } = this;
+
+ if (!lastFound) return false;
+
+ this.sendRequest("playAudio", {
+ ...lastFound,
+ selected: this.selectedEntry,
+ });
+ return true;
+ }
+
+ setSanseidoMode(sanseidoMode: boolean): void {
+ this.sanseidoMode = sanseidoMode || false;
+ }
+
+ setEpwingMode(epwingMode: boolean): void {
+ if (epwingMode) {
+ this.enableEpwing();
+ } else {
+ this.disableEpwing();
}
+ }
}
const rikai = new Rikai(document);
-browser.storage.local.get('enabled').then(({enabled}) => {
- if (enabled) {
- rikai.enable();
- }
+browser.storage.local.get("enabled").then(({ enabled }) => {
+ if (enabled) {
+ rikai.enable();
+ }
});
-browser.storage.local.get('sanseidoMode').then(({ sanseidoMode }: Storage & { sanseidoMode: boolean }) => {
+browser.storage.local
+ .get("sanseidoMode")
+ .then(({ sanseidoMode }: Storage & { sanseidoMode: boolean }) => {
rikai.setSanseidoMode(sanseidoMode);
-});
+ });
-browser.storage.local.get('epwingMode').then(({ epwingMode }: Storage & { epwingMode: boolean }) => {
+browser.storage.local
+ .get("epwingMode")
+ .then(({ epwingMode }: Storage & { epwingMode: boolean }) => {
rikai.setEpwingMode(epwingMode);
-});
+ });
browser.storage.onChanged.addListener((change, storageArea) => {
- if (storageArea !== "local") return;
- if (typeof change.enabled !== 'undefined') {
- if (change.enabled.newValue === true) {
- rikai.enable();
- } else {
- rikai.disable();
- }
+ if (storageArea !== "local") return;
+ if (typeof change.enabled !== "undefined") {
+ if (change.enabled.newValue === true) {
+ rikai.enable();
+ } else {
+ rikai.disable();
}
+ }
- if (typeof change.sanseidoMode !== 'undefined') {
- rikai.setSanseidoMode(change.sanseidoMode.newValue);
- }
+ if (typeof change.sanseidoMode !== "undefined") {
+ rikai.setSanseidoMode(change.sanseidoMode.newValue);
+ }
- if (typeof change.epwingMode !== 'undefined') {
- rikai.setEpwingMode(change.epwingMode.newValue);
- }
+ if (typeof change.epwingMode !== "undefined") {
+ rikai.setEpwingMode(change.epwingMode.newValue);
+ }
});
diff --git a/src/defaultConfig.ts b/src/defaultConfig.ts
index afd678b..f997396 100644
--- a/src/defaultConfig.ts
+++ b/src/defaultConfig.ts
@@ -1,160 +1,180 @@
import {DictionaryDefinition} from "./interfaces/DictionaryDefinition";
export interface Config {
- ankiTags: string;
- keymap: { playAudio: number; epwingPreviousEntry: number; epwingNextEntry: number; selectNextDictionary: number; toggleSanseidoMode: number; toggleEpwingMode: number; sendToAnki: number };
- showKanjiNewNelson: boolean;
- showFrequency: boolean;
- showKanjiNelson: boolean;
- showPopularWordIndicator: boolean;
- epwingDictionaries: { name: string, path: string }[];
- epwingMode?: boolean;
- startWithSanseido: boolean;
- importEmptyAudio: boolean;
- ankiFields: { [key: string]: string };
- showKanjiHalpern: boolean;
- maxEntries: number;
- showKanjiHenshall: boolean;
- theme: string;
- hideXRatedEntries: boolean;
- showKanjiLearnersDictionary: boolean;
- audioVolume: number;
- installedDictionaries: DictionaryDefinition[];
- showKanjiTurtleAndKana: boolean;
- showKanjiHeisig: boolean;
- recommendedDictionaries: DictionaryDefinition[];
- showKanjiUnicode: boolean;
- showKanjiPinYin: boolean;
- showKanjiComponents: boolean;
- startWithEpwing: boolean;
- hideDefinitions: boolean;
- showWordTypeIndicator: boolean;
- openChangelogOnUpdate: boolean;
- showKanjiTurtleDictionary: boolean;
- nameMax: number;
- showPitchAccent: boolean;
- showKanjiSkipPattern: boolean;
+ ankiTags: string;
+ keymap: {
+ playAudio: number;
+ epwingPreviousEntry: number;
+ epwingNextEntry: number;
+ selectNextDictionary: number;
+ toggleSanseidoMode: number;
+ toggleEpwingMode: number;
+ sendToAnki: number;
+ nextDefinition: number;
+ previousDefinition: number;
+ };
+ showKanjiNewNelson: boolean;
+ showFrequency: boolean;
+ showKanjiNelson: boolean;
+ showPopularWordIndicator: boolean;
+ epwingDictionaries: { name: string; path: string }[];
+ epwingMode?: boolean;
+ startWithSanseido: boolean;
+ importEmptyAudio: boolean;
+ ankiFields: { [key: string]: string };
+ showKanjiHalpern: boolean;
+ maxEntries: number;
+ showKanjiHenshall: boolean;
+ theme: string;
+ hideXRatedEntries: boolean;
+ showKanjiLearnersDictionary: boolean;
+ audioVolume: number;
+ installedDictionaries: DictionaryDefinition[];
+ showKanjiTurtleAndKana: boolean;
+ showKanjiHeisig: boolean;
+ recommendedDictionaries: DictionaryDefinition[];
+ showKanjiUnicode: boolean;
+ showKanjiPinYin: boolean;
+ showKanjiComponents: boolean;
+ startWithEpwing: boolean;
+ hideDefinitions: boolean;
+ showWordTypeIndicator: boolean;
+ openChangelogOnUpdate: boolean;
+ showKanjiTurtleDictionary: boolean;
+ nameMax: number;
+ showPitchAccent: boolean;
+ showKanjiSkipPattern: boolean;
- vnHookClipboardFrequency: number;
- vnHookAppendToTop: boolean;
- vnAutoScroll: boolean;
+ vnHookClipboardFrequency: number;
+ vnHookAppendToTop: boolean;
+ vnAutoScroll: boolean;
}
const defaultConfig: Config = {
- startWithSanseido: false,
- startWithEpwing: false,
+ startWithSanseido: false,
+ startWithEpwing: false,
- keymap: {
- playAudio: 70,
- sendToAnki: 82,
- selectNextDictionary: 16,
- toggleSanseidoMode: 79,
+ keymap: {
+ playAudio: 70,
+ sendToAnki: 82,
+ selectNextDictionary: 16,
+ toggleSanseidoMode: 79,
- toggleEpwingMode: 80,
- epwingPreviousEntry: 219,
- epwingNextEntry: 221,
- },
- hideDefinitions: false, //hidedef
- hideXRatedEntries: false, //hidex
- nameMax: 20, //namax
- maxEntries: 10,
- ankiFields: {},
- showWordTypeIndicator: true, //wpos
- showPopularWordIndicator: true, //wpop
- ankiTags: '',
- importEmptyAudio: true,
- openChangelogOnUpdate: false,
- showFrequency: false,
- showPitchAccent: false, //showpitchaccent
- theme: 'blue',
- audioVolume: 100,
+ toggleEpwingMode: 80,
+ epwingPreviousEntry: 219,
+ epwingNextEntry: 221,
+
+ nextDefinition: 40,
+ previousDefinition: 38,
+ },
+ hideDefinitions: false, //hidedef
+ hideXRatedEntries: false, //hidex
+ nameMax: 20, //namax
+ maxEntries: 10,
+ ankiFields: {},
+ showWordTypeIndicator: true, //wpos
+ showPopularWordIndicator: true, //wpop
+ ankiTags: "",
+ importEmptyAudio: true,
+ openChangelogOnUpdate: false,
+ showFrequency: false,
+ showPitchAccent: false, //showpitchaccent
+ theme: "blue",
+ audioVolume: 100,
- //Definitions for what we can show in the Kanji Dictionary display
- showKanjiComponents: true, //kindex-COMP
- showKanjiHalpern: true, //kindex-H
- showKanjiHeisig: true, //kindex-L
- showKanjiHenshall: true, //kindex-E
- showKanjiLearnersDictionary: true, //kindex-DK
- showKanjiNelson: true, //kindex-N
- showKanjiNewNelson: true, //kindex-V
- showKanjiPinYin: true, //kindex-Y
- showKanjiSkipPattern: true, //kindex-P
- showKanjiTurtleAndKana: true, //kindex-IN
- showKanjiTurtleDictionary: true, //kindex-I
- showKanjiUnicode: true, //kindex-U
+ //Definitions for what we can show in the Kanji Dictionary display
+ showKanjiComponents: true, //kindex-COMP
+ showKanjiHalpern: true, //kindex-H
+ showKanjiHeisig: true, //kindex-L
+ showKanjiHenshall: true, //kindex-E
+ showKanjiLearnersDictionary: true, //kindex-DK
+ showKanjiNelson: true, //kindex-N
+ showKanjiNewNelson: true, //kindex-V
+ showKanjiPinYin: true, //kindex-Y
+ showKanjiSkipPattern: true, //kindex-P
+ showKanjiTurtleAndKana: true, //kindex-IN
+ showKanjiTurtleDictionary: true, //kindex-I
+ showKanjiUnicode: true, //kindex-U
- //Epwing Options
- epwingDictionaries: [],
+ //Epwing Options
+ epwingDictionaries: [],
- installedDictionaries: [],
- recommendedDictionaries: [
- {
- name: 'Japanese to English Dictionary',
- id: 'e82ffa3b-1cd4-4749-b5bf-9a6f64e6890a',
- hasType: true,
- isNameDictionary: false,
- isKanjiDictionary: false,
- url: 'https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/english.json'
- },
- {
- name: 'Japanese Names',
- id: '359fe507-7235-4040-8f7b-c5af90e9897d',
- hasType: false,
- isNameDictionary: true,
- isKanjiDictionary: false,
- url: 'https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/names.json'
- // url: '../../names.json'
- },
- {
- name: 'Japanese to Dutch Dictionary',
- id: 'a544e3ba-51cc-4574-aed5-54e195557e17',
- hasType: true,
- isNameDictionary: false,
- isKanjiDictionary: false,
- url: 'https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/dutch.json'
- // url: '../../dutch.json'
- },
- {
- name: 'Japanese to French Dictionary',
- id: 'eb8e4ac0-9086-4710-b121-05f2acef5664',
- hasType: true,
- isNameDictionary: false,
- isKanjiDictionary: false,
- url: 'https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/french.json'
- // url: '../../french.json'
- },
- {
- name: 'Japanese to German Dictionary',
- id: '1d7e1b66-8478-4a7d-8c00-60cb85af772e',
- hasType: true,
- isNameDictionary: false,
- isKanjiDictionary: false,
- url: 'https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/german.json'
- // url: '../../german.json'
- },
- {
- name: 'Japanese to Russian Dictionary',
- id: '62be5b14-353b-4a25-92d7-341da40fd380',
- hasType: true,
- isNameDictionary: false,
- isKanjiDictionary: false,
- url: 'https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/russian.json'
- // url: '../../russian.json'
- },
- {
- name: 'Japanese to Thai Dictionary',
- id: 'bef50e55-3d98-438f-801f-70137714be30',
- hasType: true,
- isNameDictionary: false,
- isKanjiDictionary: false,
- url: 'https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/thai.json'
- // url: '../../thai.json'
- },
- ],
+ installedDictionaries: [],
+ recommendedDictionaries: [
+ {
+ name: "Japanese to English Dictionary",
+ id: "e82ffa3b-1cd4-4749-b5bf-9a6f64e6890a",
+ hasType: true,
+ isNameDictionary: false,
+ isKanjiDictionary: false,
+ url:
+ "https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/english.json",
+ },
+ {
+ name: "Japanese Names",
+ id: "359fe507-7235-4040-8f7b-c5af90e9897d",
+ hasType: false,
+ isNameDictionary: true,
+ isKanjiDictionary: false,
+ url:
+ "https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/names.json",
+ // url: '../../names.json'
+ },
+ {
+ name: "Japanese to Dutch Dictionary",
+ id: "a544e3ba-51cc-4574-aed5-54e195557e17",
+ hasType: true,
+ isNameDictionary: false,
+ isKanjiDictionary: false,
+ url:
+ "https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/dutch.json",
+ // url: '../../dutch.json'
+ },
+ {
+ name: "Japanese to French Dictionary",
+ id: "eb8e4ac0-9086-4710-b121-05f2acef5664",
+ hasType: true,
+ isNameDictionary: false,
+ isKanjiDictionary: false,
+ url:
+ "https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/french.json",
+ // url: '../../french.json'
+ },
+ {
+ name: "Japanese to German Dictionary",
+ id: "1d7e1b66-8478-4a7d-8c00-60cb85af772e",
+ hasType: true,
+ isNameDictionary: false,
+ isKanjiDictionary: false,
+ url:
+ "https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/german.json",
+ // url: '../../german.json'
+ },
+ {
+ name: "Japanese to Russian Dictionary",
+ id: "62be5b14-353b-4a25-92d7-341da40fd380",
+ hasType: true,
+ isNameDictionary: false,
+ isKanjiDictionary: false,
+ url:
+ "https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/russian.json",
+ // url: '../../russian.json'
+ },
+ {
+ name: "Japanese to Thai Dictionary",
+ id: "bef50e55-3d98-438f-801f-70137714be30",
+ hasType: true,
+ isNameDictionary: false,
+ isKanjiDictionary: false,
+ url:
+ "https://raw.githubusercontent.com/Garethp/RikaiRebuilt-dictionaries/master/thai.json",
+ // url: '../../thai.json'
+ },
+ ],
- vnHookClipboardFrequency: 200,
- vnHookAppendToTop: false,
- vnAutoScroll: true,
+ vnHookClipboardFrequency: 200,
+ vnHookAppendToTop: false,
+ vnAutoScroll: true,
};
export default defaultConfig;
diff --git a/styles/popup.css b/styles/popup.css
index ec7af16..6ae071d 100644
--- a/styles/popup.css
+++ b/styles/popup.css
@@ -23,11 +23,13 @@
position: absolute;
z-index: 7777;
border: 1px solid #D0D0D0 !important;
- padding: 4px;
top: 5px;
left: 5px;
min-width: 100px;
}
+#rikaichan-window > div {
+ padding: 4px;
+}
/* used for word definitions */
#rikaichan-window .w-kanji {
@@ -224,8 +226,11 @@
#rikaichan-window.rikai-blue {
background: #5C73B8;
}
+#rikaichan-window.rikai-blue > div.selected {
+ background-color: #3E528E;
+}
#rikaichan-window.rikai-blue, #rikaichan-window.rikai-blue * {
- color: #FFFFFF;
+ color: #ffffff;
}
#rikaichan-window.rikai-blue .w-kanji {
color: #B7E7FF;
diff --git a/styles/popup.css.map b/styles/popup.css.map
index 1390989..8739314 100644
--- a/styles/popup.css.map
+++ b/styles/popup.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["popup.scss"],"names":[],"mappings":"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","file":"popup.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["popup.scss"],"names":[],"mappings":"AAAA;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;;AAIR;AAEA;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAIJ;EACI;EACA;EACA;EAGA;EACA;;AAEA;EACI;;;AAKR;AAEA;EACI;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;AAEA;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;AAEA;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;AAEA;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAIJ;AAEA;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAIJ;AAKA;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;;;AAIJ;AAEA;EAEI;EACA;;;AAGJ;EAEI;;;AAGJ;EAEI;EACA;EACA;EACA;EACA;EACA;;;AAGJ;AACA;EAMI;;AALA;EACI;;AAMJ;EAAO;;AAEP;EAAW;;AACX;EAAU;;AACV;EAAU;;AAEV;EAAsB;;AACtB;EAAiB;;AACjB;EAAmB;;AACnB;EAAe;;AAEf;EAAW;EAAqB;;AAEhC;EAAW;;AACX;EAAU;;AACV;EAAa;;AACb;EAAa;;AAEb;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AAEjC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAmB;;AAEnB;EAAK;;;AAGT;AACA;EACI;;AACA;EAAO;;AAEP;EAAW;;AACX;EAAU;;AACV;EAAU;;AAEV;EAAsB;;AACtB;EAAiB;;AACjB;EAAmB;;AACnB;EAAe;;AAEf;EAAW;EAAqB;;AAEhC;EAAW;;AACX;EAAU;;AACV;EAAa;;AACb;EAAa;;AAEb;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AAEjC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAmB;;AAEnB;EAAK;;;AAGT;AACA;EACI;;AACA;EAAO;;AAEP;EAAW;;AACX;EAAU;;AACV;EAAU;;AAEV;EAAsB;;AACtB;EAAiB;;AACjB;EAAmB;;AACnB;EAAe;;AAEf;EAAW;EAAqB;;AAEhC;EAAW;;AACX;EAAU;;AACV;EAAa;;AACb;EAAa;;AAEb;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AAEjC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAmB;;AAEnB;EAAK;;;AAGT;AACA;EACI;;AACA;EAAO;;AAEP;EAAW;;AACX;EAAU;;AACV;EAAU;;AAEV;EAAsB;;AACtB;EAAiB;;AACjB;EAAmB;;AACnB;EAAe;;AAEf;EAAW;EAAqB;;AAEhC;EAAW;;AACX;EAAU;;AACV;EAAa;;AACb;EAAa;;AAEb;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AACjC;EAAY;EAAqB;;AAEjC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAa;EAAqB;;AAClC;EAAa;EAAqB;;AAElC;EAAmB;;AAEnB;EAAK","file":"popup.css"}
\ No newline at end of file
diff --git a/styles/popup.scss b/styles/popup.scss
index 782efe6..f9b7f51 100644
--- a/styles/popup.scss
+++ b/styles/popup.scss
@@ -24,10 +24,13 @@
position: absolute;
z-index: 7777;
border: 1px solid #D0D0D0 !important;
- padding: 4px;
top: 5px;
left: 5px;
min-width: 100px;
+
+ > div {
+ padding: 4px;
+ }
}
/* used for word definitions */
@@ -245,8 +248,14 @@
/** Blue Theme **/
#rikaichan-window.rikai-blue {
+ > div.selected {
+ background-color: #3E528E;
+
+ }
+
background: #5C73B8;
- &, * { color: #FFFFFF; }
+
+ &, * { color: #ffffff; }
.w-kanji { color: #B7E7FF; }
.w-kana { color: #C0FFC0; }