1180 lines
34 KiB
JavaScript
1180 lines
34 KiB
JavaScript
// Copyright 2017; Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0
|
||
|
||
if (typeof globalThis.browser === "undefined" && typeof chrome !== "undefined") {
|
||
globalThis.browser = {
|
||
...chrome,
|
||
|
||
runtime: {
|
||
...chrome.runtime,
|
||
|
||
sendMessage: function(message) {
|
||
return new Promise((resolve, reject) => {
|
||
chrome.runtime.sendMessage(message, response => {
|
||
const err = chrome.runtime.lastError;
|
||
|
||
// Chrome sets lastError when a message intentionally has no direct response.
|
||
// Firefox-style Nazeka code often uses async side-channel replies instead.
|
||
if (err) {
|
||
console.warn("Nazeka sendMessage warning:", err.message);
|
||
resolve(undefined);
|
||
} else {
|
||
resolve(response);
|
||
}
|
||
});
|
||
});
|
||
},
|
||
|
||
getPlatformInfo: function() {
|
||
return new Promise((resolve, reject) => {
|
||
chrome.runtime.getPlatformInfo(info => {
|
||
const err = chrome.runtime.lastError;
|
||
if (err) reject(err);
|
||
else resolve(info);
|
||
});
|
||
});
|
||
},
|
||
|
||
getURL: chrome.runtime.getURL.bind(chrome.runtime),
|
||
|
||
onMessage: {
|
||
addListener: function(listener) {
|
||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||
try {
|
||
const result = listener(message, sender);
|
||
|
||
if (result && typeof result.then === "function") {
|
||
result.then(sendResponse).catch(error => {
|
||
console.error("Nazeka message listener error:", error);
|
||
sendResponse(undefined);
|
||
});
|
||
return true;
|
||
}
|
||
|
||
if (result !== undefined) {
|
||
sendResponse(result);
|
||
}
|
||
|
||
return false;
|
||
} catch (error) {
|
||
console.error("Nazeka message listener exception:", error);
|
||
sendResponse(undefined);
|
||
return false;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
},
|
||
|
||
storage: {
|
||
...chrome.storage,
|
||
|
||
local: {
|
||
...chrome.storage.local,
|
||
|
||
get: function(keys) {
|
||
return new Promise((resolve, reject) => {
|
||
chrome.storage.local.get(keys, result => {
|
||
const err = chrome.runtime.lastError;
|
||
if (err) reject(err);
|
||
else resolve(result);
|
||
});
|
||
});
|
||
},
|
||
|
||
set: function(items) {
|
||
return new Promise((resolve, reject) => {
|
||
chrome.storage.local.set(items, () => {
|
||
const err = chrome.runtime.lastError;
|
||
if (err) reject(err);
|
||
else resolve();
|
||
});
|
||
});
|
||
},
|
||
|
||
remove: function(keys) {
|
||
return new Promise((resolve, reject) => {
|
||
chrome.storage.local.remove(keys, () => {
|
||
const err = chrome.runtime.lastError;
|
||
if (err) reject(err);
|
||
else resolve();
|
||
});
|
||
});
|
||
}
|
||
},
|
||
|
||
onChanged: chrome.storage.onChanged
|
||
},
|
||
|
||
tabs: chrome.tabs ? {
|
||
...chrome.tabs,
|
||
|
||
getCurrent: function() {
|
||
return new Promise((resolve, reject) => {
|
||
chrome.tabs.getCurrent(tab => {
|
||
const err = chrome.runtime.lastError;
|
||
if (err) reject(err);
|
||
else resolve(tab);
|
||
});
|
||
});
|
||
},
|
||
|
||
sendMessage: function(tabId, message) {
|
||
return new Promise((resolve, reject) => {
|
||
chrome.tabs.sendMessage(tabId, message, response => {
|
||
const err = chrome.runtime.lastError;
|
||
if (err) {
|
||
console.warn("Nazeka tabs.sendMessage warning:", err.message);
|
||
resolve(undefined);
|
||
} else {
|
||
resolve(response);
|
||
}
|
||
});
|
||
});
|
||
},
|
||
|
||
create: chrome.tabs.create.bind(chrome.tabs)
|
||
} : undefined,
|
||
|
||
windows: chrome.windows ? {
|
||
...chrome.windows,
|
||
create: chrome.windows.create.bind(chrome.windows)
|
||
} : undefined,
|
||
|
||
extension: {
|
||
...chrome.extension,
|
||
getURL: chrome.runtime.getURL.bind(chrome.runtime)
|
||
},
|
||
|
||
browserAction: chrome.browserAction || undefined,
|
||
contextMenus: chrome.contextMenus || undefined
|
||
};
|
||
}
|
||
|
||
|
||
function storageGet(key) {
|
||
return new Promise((resolve, reject) => {
|
||
browser.storage.local.get(key, result => {
|
||
if (browser.runtime.lastError) {
|
||
reject(browser.runtime.lastError);
|
||
} else {
|
||
resolve(result);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
function storageSet(value) {
|
||
return new Promise((resolve, reject) => {
|
||
browser.storage.local.set(value, () => {
|
||
if (browser.runtime.lastError) {
|
||
reject(browser.runtime.lastError);
|
||
} else {
|
||
resolve();
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
|
||
'use strict';
|
||
|
||
|
||
|
||
let platform = "win";
|
||
|
||
async function get_real_platform()
|
||
{
|
||
|
||
let my_platform = undefined;
|
||
try {
|
||
my_platform = await browser.runtime.sendMessage({type:"platform"});
|
||
if(my_platform)
|
||
my_platform = my_platform["response"];
|
||
}catch(err){}
|
||
while(my_platform == "")
|
||
{
|
||
try {
|
||
my_platform = await browser.runtime.sendMessage({type:"platform"});
|
||
if(my_platform)
|
||
my_platform = my_platform["response"];
|
||
}catch(err){}
|
||
}
|
||
platform = my_platform;
|
||
}
|
||
|
||
let settings = [];
|
||
|
||
function defaults()
|
||
{
|
||
// behavior
|
||
settings.push({
|
||
kind: "dummy",
|
||
label: "Behavior"
|
||
});
|
||
|
||
settings.push({
|
||
id: "enabled",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Enabled"
|
||
});
|
||
settings.push({
|
||
id: "kanji_mode",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Individual kanji information mode"
|
||
});
|
||
settings.push({
|
||
id: "compact",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Compact"
|
||
});
|
||
settings.push({
|
||
id: "showoriginal",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Show original text"
|
||
});
|
||
settings.push({
|
||
id: "space_saver",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Space saver (removes extra formatting from readings and other spellings)"
|
||
});
|
||
settings.push({
|
||
id: "hide_deconj",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Hide deconjugations"
|
||
});
|
||
settings.push({
|
||
id: "popup_follows_mouse",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Popup follows mouse (even if it doesn't update)"
|
||
});
|
||
settings.push({
|
||
id: "length",
|
||
kind: "number",
|
||
min: 6,
|
||
max: 100,
|
||
step: 1,
|
||
default: 25,
|
||
label: "Search length (characters) (default 25, sane values are from 25 to 50)"
|
||
});
|
||
settings.push({
|
||
id: "lookuprate",
|
||
kind: "number",
|
||
min: 1,
|
||
max: 1000,
|
||
step: 1,
|
||
default: 8,
|
||
label: "Lookup throttle (milliseconds)"
|
||
});
|
||
settings.push({
|
||
id: "alternatives_mode",
|
||
kind: "combobox",
|
||
options: ["Longest match only", "Longest and second longest match", "Longest and shortest match", "All matches"],
|
||
default: 3,
|
||
label: "Matching mode"
|
||
});
|
||
settings.push({
|
||
id: "strict_alternatives",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Strict matching for alternative matches"
|
||
});
|
||
settings.push({
|
||
id: "definitions_mode",
|
||
kind: "combobox",
|
||
options: ["From jmdict then json", "From json then jmdict", "From json; otherwise jmdict", "From json dictionary only", "None"],
|
||
default: 0,
|
||
label: "Where to look for definitions (does not affect jmdict readings)"
|
||
});
|
||
settings.push({
|
||
id: "normal_definitions_in_mining",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Force \"From jmdict then json\" definitions mode when mining UI is open"
|
||
});
|
||
settings.push({
|
||
id: "strict_epwing",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Strict matching for json dictionary matches"
|
||
});
|
||
settings.push({
|
||
id: "json_newlines_replace",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Replace json dictionary linebreaks with this character (blank for newlines)"
|
||
});
|
||
settings.push({
|
||
id: "json_newlines_character",
|
||
kind: "text",
|
||
default: " ⬥ ",
|
||
label: "Character to replace json dictionary linebreaks with"
|
||
});
|
||
settings.push({
|
||
id: "json_ignore_entries",
|
||
kind: "text",
|
||
default: "一,二,三,四,五,六,七,八,九,〇",
|
||
label: "Ignore json dictionary entries with these spellings"
|
||
});
|
||
settings.push({
|
||
id: "popup_requires_key",
|
||
kind: "combobox",
|
||
options: ["None", "Ctrl", "Shift"],
|
||
default: 0,
|
||
label: "Popup requires key"
|
||
});
|
||
settings.push({
|
||
id: "strip_spaces",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Strip spaces (for when spaces find their way into Japanese text and it breaks word searching)"
|
||
});
|
||
settings.push({
|
||
id: "ignore_linebreaks",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Ignore hard line breaks (disable for text with preformatted linebreaks)"
|
||
});
|
||
settings.push({
|
||
id: "ignore_divs",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Search out from within block-like elements like divs (enable only for very poorly-constructed web pages; usually causes performance problems)"
|
||
});
|
||
settings.push({
|
||
id: "live_mining",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Enable live mining (configured via the button below under \"Other\")"
|
||
});
|
||
settings.push({
|
||
id: "ankiconnect_force_sync",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Force Anki to sync to AnkiWeb after each mined card"
|
||
});
|
||
settings.push({
|
||
id: "use_selection",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Use text selection to limit lookup length and force context"
|
||
});
|
||
settings.push({
|
||
id: "only_selection",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Only look up text when mousing over selection (only when something is selected, setting ignored otherwise)"
|
||
});
|
||
settings.push({
|
||
id: "freqlist_mode",
|
||
kind: "combobox",
|
||
options: ["None", "Visual Novels", "Narou", "Novels"],
|
||
default: 1,
|
||
label: "Frequency data source"
|
||
});
|
||
|
||
// display
|
||
settings.push({
|
||
kind: "dummy",
|
||
label: "Display"
|
||
});
|
||
settings.push({
|
||
id: "scale",
|
||
kind: "number",
|
||
min: 0.1,
|
||
max: 64,
|
||
step: 0.1,
|
||
default: 1,
|
||
label: "Lookup popup scale"
|
||
});
|
||
settings.push({
|
||
id: "xoffset",
|
||
kind: "number",
|
||
min: 1,
|
||
max: 200,
|
||
step: 1,
|
||
default: 5,
|
||
label: "Popup offset, horizontal (pixels, default is 5)"
|
||
});
|
||
settings.push({
|
||
id: "yoffset",
|
||
kind: "number",
|
||
min: 1,
|
||
max: 200,
|
||
step: 1,
|
||
default: 22,
|
||
label: "Popup offset, vertical (pixels, default is 22)"
|
||
});
|
||
settings.push({
|
||
id: "width",
|
||
kind: "number",
|
||
min: 100,
|
||
max: 10000,
|
||
step: 1,
|
||
default: 600,
|
||
label: "Lookup popup width (before scaling) (limited to half the browser window width)"
|
||
});
|
||
settings.push({
|
||
id: "sticky_maxheight",
|
||
kind: "number",
|
||
min: 0,
|
||
max: 10000,
|
||
default: 0,
|
||
label: "Maximum height of Sticky and Mining UI modes - 0 is full screen"
|
||
});
|
||
settings.push({
|
||
id: "fixedwidth",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Fixed width"
|
||
});
|
||
settings.push({
|
||
id: "novalign",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Disable use of `vertical-align: middle;` for reading/etc info"
|
||
});
|
||
settings.push({
|
||
id: "inserthr",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Insert horizontal bar between entries"
|
||
});
|
||
/*
|
||
// currently broken
|
||
settings.push({
|
||
id: "fixedwidthpositioning",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Position as though fixed width (currently nonfunctional; will fix later)"
|
||
});
|
||
*/
|
||
settings.push({
|
||
id: "corner",
|
||
kind: "combobox",
|
||
options: ["Top left", "Top right", "Bottom left", "Bottom right"],
|
||
default: 0,
|
||
label: "Which corner of the popup follows the mouse"
|
||
});
|
||
settings.push({
|
||
id: "x_dodge",
|
||
kind: "combobox",
|
||
options: ["Flip", "Push"],
|
||
default: 1,
|
||
label: "How to keep the popup on the screen horizontally"
|
||
});
|
||
settings.push({
|
||
id: "y_dodge",
|
||
kind: "combobox",
|
||
options: ["Flip", "Push"],
|
||
default: 0,
|
||
label: "How to keep the popup on the screen vertically (at least one of these two options should be \"Flip\")"
|
||
});
|
||
|
||
// theme
|
||
settings.push({
|
||
kind: "dummy",
|
||
label: "Theme"
|
||
});
|
||
settings.push({
|
||
id: "superborder",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Super border"
|
||
});
|
||
settings.push({
|
||
id: "disableborder",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Disable border"
|
||
});
|
||
settings.push({
|
||
id: "dropshadow",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Enable dropshadow"
|
||
});
|
||
settings.push({
|
||
id: "bgcolor",
|
||
kind: "color",
|
||
default: "#111111",
|
||
label: "Background color"
|
||
});
|
||
settings.push({
|
||
id: "fgcolor",
|
||
kind: "color",
|
||
default: "#CCCCCC",
|
||
label: "Foreground color"
|
||
});
|
||
settings.push({
|
||
id: "hlcolor",
|
||
kind: "color",
|
||
default: "#99DDFF",
|
||
label: "Main highlight color"
|
||
});
|
||
settings.push({
|
||
id: "hlcolor2",
|
||
kind: "color",
|
||
default: "#99FF99",
|
||
label: "Reading highlight color"
|
||
});
|
||
settings.push({
|
||
id: "titlecolor",
|
||
kind: "color",
|
||
default: "#D8A888",
|
||
label: "Dictionary title color (for when using json dictionaries)"
|
||
});
|
||
settings.push({
|
||
id: "highlight_dict_headers",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Highlight dictionary headers"
|
||
});
|
||
settings.push({
|
||
id: "dropshadowcolor",
|
||
kind: "color",
|
||
default: "#000000",
|
||
label: "Dropshadow color"
|
||
});
|
||
settings.push({
|
||
id: "dropshadowradius",
|
||
kind: "number",
|
||
min: 1,
|
||
max: 50,
|
||
default: 10,
|
||
label: "Dropshadow radius"
|
||
});
|
||
settings.push({
|
||
id: "font",
|
||
kind: "text",
|
||
default: "",
|
||
label: "Font override (without trailing comma)"
|
||
});
|
||
settings.push({
|
||
id: "definition_fontsize",
|
||
kind: "number",
|
||
min: 4,
|
||
max: 48,
|
||
default: 13,
|
||
label: "Definition font size"
|
||
});
|
||
settings.push({
|
||
id: "dict_item_fontsize",
|
||
kind: "number",
|
||
min: 4,
|
||
max: 48,
|
||
default: 18,
|
||
label: "Dictionary item font size"
|
||
});
|
||
settings.push({
|
||
id: "reading_fontsize",
|
||
kind: "number",
|
||
min: 4,
|
||
max: 48,
|
||
default: 15,
|
||
label: "Reading font size"
|
||
});
|
||
settings.push({
|
||
id: "hlfont",
|
||
kind: "text",
|
||
default: "",
|
||
label: "Font override (highlighted text only) (without trailing comma)"
|
||
});
|
||
settings.push({
|
||
id: "sticky",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Sticky mode"
|
||
});
|
||
settings.push({
|
||
id: "padding",
|
||
kind: "number",
|
||
min: 0,
|
||
max: 10,
|
||
default: 2,
|
||
label: "Padding"
|
||
});
|
||
settings.push({
|
||
id: "info_styling",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Yomichan-like definition info tag styling"
|
||
});
|
||
settings.push({
|
||
id: "info_fgcolor",
|
||
kind: "color",
|
||
default: "#CCCCCC",
|
||
label: "Yomichan-like info tag text color"
|
||
});
|
||
settings.push({
|
||
id: "info_bgcolor_pos",
|
||
kind: "color",
|
||
default: "#284880",
|
||
label: "Yomichan-like info tag background for part-of-speech tags"
|
||
});
|
||
settings.push({
|
||
id: "info_bgcolor_num",
|
||
kind: "color",
|
||
default: "#484848",
|
||
label: "Yomichan-like info tag background color for sense numbers"
|
||
});
|
||
settings.push({
|
||
id: "info_bgcolor_etc",
|
||
kind: "color",
|
||
default: "#305028",
|
||
label: "Yomichan-like info tag background color for etc tags"
|
||
});
|
||
|
||
// reader
|
||
settings.push({
|
||
kind: "dummy",
|
||
label: "Reader"
|
||
});
|
||
settings.push({
|
||
id: "reader_width",
|
||
kind: "number",
|
||
min: 100,
|
||
max: 10000,
|
||
step: 1,
|
||
default: 806,
|
||
label: "Reader window default width"
|
||
});
|
||
settings.push({
|
||
id: "reader_height",
|
||
kind: "number",
|
||
min: 50,
|
||
max: 10000,
|
||
step: 1,
|
||
default: 300,
|
||
label: "Reader window default height"
|
||
});
|
||
settings.push({
|
||
id: "reader_leeway",
|
||
kind: "number",
|
||
min: 0,
|
||
max: 10000,
|
||
step: 1,
|
||
default: 200,
|
||
label: "Reader autoscroll leeway (when \"insert at bottom\" is enabled) (0 disables)"
|
||
});
|
||
settings.push({
|
||
id: "reader_sticky",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Force 'sticky' mode in reader"
|
||
});
|
||
settings.push({
|
||
id: "reader_auto",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Automatically look up the beginning of new text in the reader"
|
||
});
|
||
settings.push({
|
||
id: "reader_reverse",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Reader insert at bottom"
|
||
});
|
||
settings.push({
|
||
id: "reader_convert_newlines",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Reader force newlines into linebreaks"
|
||
});
|
||
|
||
settings.push({
|
||
id: "reader_bg",
|
||
kind: "color",
|
||
default: "#111111",
|
||
label: "Reader background color"
|
||
});
|
||
settings.push({
|
||
id: "reader_fg",
|
||
kind: "color",
|
||
default: "#CCCCCC",
|
||
label: "Reader new text color"
|
||
});
|
||
settings.push({
|
||
id: "reader_fg_old",
|
||
kind: "color",
|
||
default: "#B0B0B0",
|
||
label: "Reader old text color"
|
||
});
|
||
settings.push({
|
||
id: "reader_font_size",
|
||
kind: "text",
|
||
default: "1.4em",
|
||
label: "Reader text size"
|
||
});
|
||
settings.push({
|
||
id: "reader_right_padding",
|
||
kind: "text",
|
||
default: "0px",
|
||
label: "Reader right padding (useful if you use 'sticky' mode)"
|
||
});
|
||
settings.push({
|
||
id: "reader_max_width",
|
||
kind: "text",
|
||
default: "1000px",
|
||
label: "Reader max width"
|
||
});
|
||
settings.push({
|
||
id: "reader_margin",
|
||
kind: "text",
|
||
default: "8px",
|
||
label: "Reader left/right margins"
|
||
});
|
||
settings.push({
|
||
id: "reader_font",
|
||
kind: "text",
|
||
default: "",
|
||
label: "Reader font override (without trailing comma)"
|
||
});
|
||
settings.push({
|
||
id: "reader_line_height",
|
||
kind: "text",
|
||
default: "normal",
|
||
label: "Reader line height size (default: 'normal' example: '140%')"
|
||
});
|
||
settings.push({
|
||
id: "reader_throttle",
|
||
kind: "number",
|
||
min: 5,
|
||
max: 1000,
|
||
step: 5,
|
||
default: 200,
|
||
label: "Reader clipboard access interval"
|
||
});
|
||
|
||
// hotkeys
|
||
settings.push({
|
||
kind: "dummy",
|
||
label: "Kanji mode"
|
||
});
|
||
settings.push({
|
||
id: "kanji_show_stroke_count",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Show stroke count"
|
||
});
|
||
settings.push({
|
||
id: "kanji_show_readings",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Show jouyou readings"
|
||
});
|
||
settings.push({
|
||
id: "kanji_show_composition",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Show composition"
|
||
});
|
||
settings.push({
|
||
id: "kanji_show_quality_warning",
|
||
kind: "checkbox",
|
||
default: true,
|
||
label: "Show data quality warning"
|
||
});
|
||
|
||
// hotkeys
|
||
settings.push({
|
||
kind: "dummy",
|
||
label: "Hotkeys (KeyboardEvent.key values)"
|
||
});
|
||
settings.push({
|
||
id: "hotkey_mine",
|
||
kind: "text",
|
||
default: "m",
|
||
label: "Mining UI hotkey"
|
||
});
|
||
settings.push({
|
||
id: "hotkey_nazeka_toggle",
|
||
kind: "text",
|
||
default: "j",
|
||
label: "Toggle nazeka on/off"
|
||
});
|
||
settings.push({
|
||
id: "hotkey_close",
|
||
kind: "text",
|
||
default: "n",
|
||
label: "Close popup (does not disable nazeka)"
|
||
});
|
||
settings.push({
|
||
id: "hotkey_sticky",
|
||
kind: "text",
|
||
default: "b",
|
||
label: "Toggle sticky mode (useful for very long definition lists)"
|
||
});
|
||
settings.push({
|
||
id: "hotkey_audio",
|
||
kind: "text",
|
||
default: "p",
|
||
label: "Play audio from jpod101 if available"
|
||
});
|
||
settings.push({
|
||
id: "hotkey_kanji_mode",
|
||
kind: "text",
|
||
default: "k",
|
||
label: "Toggle individual kanji information mode on/off"
|
||
});
|
||
settings.push({
|
||
id: "hotkey_nudge_left",
|
||
kind: "text",
|
||
default: "ArrowLeft",
|
||
label: "Nudge lookup text left"
|
||
});
|
||
settings.push({
|
||
id: "hotkey_nudge_right",
|
||
kind: "text",
|
||
default: "ArrowRight",
|
||
label: "Nudge lookup text right"
|
||
});
|
||
settings.push({
|
||
id: "volume",
|
||
kind: "number",
|
||
min: 0.0,
|
||
max: 1.0,
|
||
step: 0.05,
|
||
default: 0.2,
|
||
label: "Audio playback volume (press \"p\" while popup is open)"
|
||
});
|
||
settings.push({
|
||
id: "audio_force_https",
|
||
kind: "checkbox",
|
||
default: false,
|
||
label: "Grab audio files over HTTPS instead of HTTP"
|
||
});
|
||
|
||
// other (added imperatively)
|
||
settings.push({
|
||
kind: "dummy",
|
||
label: "Other"
|
||
});
|
||
}
|
||
defaults();
|
||
|
||
function restoreListeners()
|
||
{
|
||
for(let option of settings)
|
||
{
|
||
if(option.kind == "dummy") continue;
|
||
document.querySelector("#"+option.id).addEventListener("change", setOptions);
|
||
}
|
||
}
|
||
function removeListeners()
|
||
{
|
||
for(let option of settings)
|
||
{
|
||
if(option.kind == "dummy") continue;
|
||
document.querySelector("#"+option.id).removeEventListener("change", setOptions);
|
||
}
|
||
}
|
||
|
||
async function buildpage()
|
||
{
|
||
let optionsection = document.querySelector("#optionsection");
|
||
for(let option of settings)
|
||
{
|
||
if(option.kind == "dummy")
|
||
{
|
||
let header = document.createElement("h2");
|
||
header.textContent = option.label;
|
||
optionsection.appendChild(header);
|
||
continue;
|
||
}
|
||
let container = document.createElement("div");
|
||
let configger = undefined;
|
||
if(option.kind == "checkbox")
|
||
{
|
||
let input = document.createElement("input");
|
||
input.type = "checkbox";
|
||
input.id = option.id;
|
||
input.name = option.id;
|
||
input.checked = option.default;
|
||
input.style.zIndex = -100000;
|
||
configger = input;
|
||
}
|
||
if(option.kind == "number")
|
||
{
|
||
let input = document.createElement("input");
|
||
input.type = "number";
|
||
input.id = option.id;
|
||
input.name = option.id;
|
||
input.min = option.min;
|
||
input.max = option.max;
|
||
input.value = option.default;
|
||
input.step = option.step;
|
||
configger = input;
|
||
}
|
||
if(option.kind == "combobox")
|
||
{
|
||
let select = document.createElement("select");
|
||
select.id = option.id;
|
||
select.name = option.id;
|
||
let i = 0;
|
||
for(let setting of option.options)
|
||
{
|
||
let opt = document.createElement("option");
|
||
opt.value = i;
|
||
opt.text = setting;
|
||
select.add(opt);
|
||
i += 1;
|
||
}
|
||
select.value = option.default;
|
||
configger = select;
|
||
}
|
||
if(option.kind == "color")
|
||
{
|
||
let input = document.createElement("input");
|
||
input.type = "color";
|
||
input.id = option.id;
|
||
input.name = option.id;
|
||
input.value = option.default;
|
||
configger = input;
|
||
}
|
||
if(option.kind == "text")
|
||
{
|
||
let input = document.createElement("input");
|
||
input.type = "text";
|
||
input.id = option.id;
|
||
input.name = option.id;
|
||
input.value = option.default;
|
||
configger = input;
|
||
}
|
||
|
||
let label = document.createElement("label");
|
||
label.for = option.id;
|
||
let labelText = document.createTextNode(option.label);
|
||
|
||
if(option.kind == "checkbox")
|
||
{
|
||
label.appendChild(configger);
|
||
label.appendChild(labelText);
|
||
container.appendChild(label);
|
||
}
|
||
else
|
||
{
|
||
label.appendChild(labelText);
|
||
let cont1 = document.createElement("div");
|
||
let cont2 = document.createElement("div");
|
||
cont1.appendChild(configger);
|
||
cont2.appendChild(label);
|
||
container.appendChild(cont1);
|
||
container.appendChild(cont2);
|
||
}
|
||
|
||
container.style.marginBottom = "8px";
|
||
optionsection.appendChild(container);
|
||
}
|
||
|
||
await get_real_platform();
|
||
|
||
if(platform != "android")
|
||
{
|
||
// mining
|
||
|
||
let liveminingbutton = document.createElement("button");
|
||
liveminingbutton.type = "button";
|
||
liveminingbutton.innerText = "Configure live mining";
|
||
liveminingbutton.onclick = function(e){browser.windows.create({url:browser.extension.getURL('livemining.html'),type:'popup'}); e.preventDefault();};
|
||
optionsection.appendChild(liveminingbutton);
|
||
|
||
optionsection.appendChild(document.createElement("hr"));
|
||
|
||
// import
|
||
|
||
let jsonmanagebutton = document.createElement("button");
|
||
jsonmanagebutton.type = "button";
|
||
jsonmanagebutton.innerText = "Manage JSON Dictionaries";
|
||
jsonmanagebutton.onclick = function(e){browser.windows.create({url:browser.extension.getURL('json_config.html'),type:'popup'}); e.preventDefault();};
|
||
optionsection.appendChild(jsonmanagebutton);
|
||
|
||
optionsection.appendChild(document.createElement("hr"));
|
||
}
|
||
else
|
||
{
|
||
// import
|
||
|
||
let jsonmanagebutton = document.createElement("button");
|
||
jsonmanagebutton.type = "button";
|
||
jsonmanagebutton.innerText = "Manage JSON Dictionaries";
|
||
jsonmanagebutton.onclick = function(e){browser.tabs.create({url:browser.extension.getURL('json_config.html')}); e.preventDefault();};
|
||
optionsection.appendChild(jsonmanagebutton);
|
||
|
||
optionsection.appendChild(document.createElement("hr"));
|
||
}
|
||
|
||
// deconjugation rules
|
||
|
||
let decon_file = document.createElement("input");
|
||
decon_file.type = "file";
|
||
decon_file.id = "decon_file";
|
||
decon_file.addEventListener("change", () =>
|
||
{
|
||
let fname = document.querySelector("#decon_file").files[0];
|
||
let reader = new FileReader();
|
||
reader.onload = async(e) =>
|
||
{
|
||
try
|
||
{
|
||
let text = e.target.result;
|
||
if(text == "" || text == "\n" || text == "\r\n")
|
||
storageSet({"deconjugator_rules_json":""});
|
||
else
|
||
storageSet({"deconjugator_rules_json":JSON.stringify(JSON.parse())});
|
||
document.querySelector("#decon_label").textContent = "Imported. Might take a few seconds to apply.";
|
||
}
|
||
catch(except)
|
||
{
|
||
console.log(except.stack);
|
||
}
|
||
};
|
||
reader.readAsText(fname);
|
||
});
|
||
let decon_label = document.createElement("label");
|
||
decon_label.for = decon_file.id;
|
||
decon_label.id = "decon_label";
|
||
decon_label.textContent = "Import deconjugation ruleset. Overrides default deconjugation ruleset. Importing a blank file will restore the default ruleset.";
|
||
decon_label.style.display = "block";
|
||
|
||
optionsection.appendChild(decon_file);
|
||
optionsection.appendChild(decon_label);
|
||
|
||
optionsection.appendChild(document.createElement("hr"));
|
||
|
||
// backup
|
||
|
||
let backup_save = document.createElement("button");
|
||
backup_save.type = "button";
|
||
backup_save.innerText = "Save Backup";
|
||
backup_save.addEventListener("click", (async function ()
|
||
{
|
||
let blob = new Blob([JSON.stringify((await browser.storage.local.get()))], {type: 'text/plain'});
|
||
let url = window.URL.createObjectURL(blob);
|
||
|
||
let a = document.createElement("a");
|
||
a.href = url;
|
||
a.download = "nazeka_data_backup.json";
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
|
||
window.URL.revokeObjectURL(url);
|
||
}));
|
||
optionsection.appendChild(backup_save);
|
||
optionsection.appendChild(document.createElement("br"));
|
||
|
||
let backup_load = document.createElement("input");
|
||
backup_load.type = "file";
|
||
backup_load.id = "backup_file";
|
||
backup_load.addEventListener("change", () =>
|
||
{
|
||
let fname = document.querySelector("#backup_file").files[0];
|
||
let reader = new FileReader();
|
||
reader.onload = async(e) =>
|
||
{
|
||
try
|
||
{
|
||
storageSet(JSON.parse(e.target.result));
|
||
browser.runtime.sendMessage({type:"refreshjson"});
|
||
document.querySelector("#backup_load_label").textContent = "Loaded backup. Might take a few seconds to apply.";
|
||
}
|
||
catch(except)
|
||
{
|
||
console.log(except.stack);
|
||
}
|
||
};
|
||
reader.readAsText(fname);
|
||
});
|
||
// FIXME: add asking for the unlimited storage permission
|
||
let backup_load_label = document.createElement("label");
|
||
backup_load_label.for = backup_save.id;
|
||
backup_load_label.id = "backup_load_label";
|
||
backup_load_label.textContent = "Import Backup. WARNING: Overwrites ALMOST EVERYTHING, INCLUDING MINED CARDS.";
|
||
backup_load_label.style.display = "block";
|
||
optionsection.appendChild(backup_load);
|
||
optionsection.appendChild(backup_load_label);
|
||
|
||
optionsection.appendChild(document.createElement("hr"));
|
||
|
||
let abbrlink = document.getElementById("abbrlink");
|
||
abbrlink.onclick = function(e){browser.windows.create({url:browser.extension.getURL('jmdictabbreviations.html'),type:'popup'}); e.preventDefault();};
|
||
}
|
||
|
||
async function restoreOptions()
|
||
{
|
||
try
|
||
{
|
||
async function getvar(name, defval)
|
||
{
|
||
let temp = (await storageGet(name))[name]; // FIXME do this for all options instead of once per option
|
||
if(temp == undefined)
|
||
temp = defval;
|
||
return temp;
|
||
}
|
||
let alternatives_mode = await getvar("alternatives_mode", 0);
|
||
let strict_alternatives = await getvar("strict_alternatives", true);
|
||
removeListeners();
|
||
for(let option of settings)
|
||
{
|
||
if(option.kind == "dummy") continue;
|
||
let value = await getvar(option.id, option.default);
|
||
if(option.kind == "checkbox")
|
||
document.querySelector("#"+option.id).checked = value?true:false;
|
||
if(option.kind == "number" || option.kind == "text" || option.kind == "color" || option.kind == "combobox")
|
||
document.querySelector("#"+option.id).value = value?value:option.default;
|
||
}
|
||
} catch (error) {}
|
||
removeListeners();
|
||
restoreListeners();
|
||
}
|
||
|
||
function fixicon()
|
||
{
|
||
browser.runtime.sendMessage({type:"fixicon"});
|
||
}
|
||
|
||
function setOptions()
|
||
{
|
||
let setstuff = {};
|
||
for(let option of settings)
|
||
{
|
||
if(option.kind == "dummy") continue;
|
||
if(option.kind == "checkbox")
|
||
setstuff[option.id] = document.querySelector("#"+option.id).checked;
|
||
if(option.kind == "number")
|
||
{
|
||
let num = Number(document.querySelector("#"+option.id).value);
|
||
if(!num) // NaN is falsy
|
||
num = option.default;
|
||
if(option.step % 1 == 0)
|
||
num = Math.round(num);
|
||
setstuff[option.id] = num;
|
||
}
|
||
if(option.kind == "text")
|
||
setstuff[option.id] = document.querySelector("#"+option.id).value;
|
||
if(option.kind == "color")
|
||
setstuff[option.id] = document.querySelector("#"+option.id).value;
|
||
if(option.kind == "combobox")
|
||
setstuff[option.id] = document.querySelector("#"+option.id).value;
|
||
}
|
||
storageSet(setstuff).then(()=>{fixicon();},()=>{});
|
||
}
|
||
|
||
if (document.readyState == "complete")
|
||
{
|
||
buildpage();
|
||
restoreOptions();
|
||
}
|
||
else
|
||
{
|
||
document.addEventListener("DOMContentLoaded", buildpage);
|
||
document.addEventListener("DOMContentLoaded", restoreOptions);
|
||
}
|