Merge pull request #160 from toasted-nutbread/mobile
Add support for mobile Firefox
This commit is contained in:
commit
61d1168d94
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<script src="/mixed/lib/dexie.min.js"></script>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap-theme.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap-toggle/bootstrap-toggle.min.css">
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Welcome to Yomichan!</title>
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap-theme.min.css">
|
||||
|
@ -28,7 +28,9 @@ class Backend {
|
||||
await this.translator.prepare();
|
||||
await apiOptionsSet(await optionsLoad());
|
||||
|
||||
chrome.commands.onCommand.addListener(this.onCommand.bind(this));
|
||||
if (chrome.commands !== null && typeof chrome.commands === 'object') {
|
||||
chrome.commands.onCommand.addListener(this.onCommand.bind(this));
|
||||
}
|
||||
chrome.runtime.onMessage.addListener(this.onMessage.bind(this));
|
||||
|
||||
if (this.options.general.showGuide) {
|
||||
@ -40,13 +42,13 @@ class Backend {
|
||||
this.options = utilIsolate(options);
|
||||
|
||||
if (!options.general.enable) {
|
||||
chrome.browserAction.setBadgeBackgroundColor({color: '#555555'});
|
||||
chrome.browserAction.setBadgeText({text: 'off'});
|
||||
this.setExtensionBadgeBackgroundColor('#555555');
|
||||
this.setExtensionBadgeText('off');
|
||||
} else if (!dictConfigured(options)) {
|
||||
chrome.browserAction.setBadgeBackgroundColor({color: '#f0ad4e'});
|
||||
chrome.browserAction.setBadgeText({text: '!'});
|
||||
this.setExtensionBadgeBackgroundColor('#f0ad4e');
|
||||
this.setExtensionBadgeText('!');
|
||||
} else {
|
||||
chrome.browserAction.setBadgeText({text: ''});
|
||||
this.setExtensionBadgeText('');
|
||||
}
|
||||
|
||||
if (options.anki.enable) {
|
||||
@ -125,6 +127,18 @@ class Backend {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
setExtensionBadgeBackgroundColor(color) {
|
||||
if (typeof chrome.browserAction.setBadgeBackgroundColor === 'function') {
|
||||
chrome.browserAction.setBadgeBackgroundColor({color});
|
||||
}
|
||||
}
|
||||
|
||||
setExtensionBadgeText(text) {
|
||||
if (typeof chrome.browserAction.setBadgeText === 'function') {
|
||||
chrome.browserAction.setBadgeText({text});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.yomichan_backend = new Backend();
|
||||
|
@ -228,11 +228,47 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
async importDictionary(archive, callback) {
|
||||
async importDictionary(archive, progressCallback, exceptions) {
|
||||
if (!this.db) {
|
||||
throw 'Database not initialized';
|
||||
}
|
||||
|
||||
const maxTransactionLength = 1000;
|
||||
const bulkAdd = async (table, items, total, current) => {
|
||||
if (items.length < maxTransactionLength) {
|
||||
if (progressCallback) {
|
||||
progressCallback(total, current);
|
||||
}
|
||||
|
||||
try {
|
||||
await table.bulkAdd(items);
|
||||
} catch (e) {
|
||||
if (exceptions) {
|
||||
exceptions.push(e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < items.length; i += maxTransactionLength) {
|
||||
if (progressCallback) {
|
||||
progressCallback(total, current + i / items.length);
|
||||
}
|
||||
|
||||
let count = Math.min(maxTransactionLength, items.length - i);
|
||||
try {
|
||||
await table.bulkAdd(items.slice(i, i + count));
|
||||
} catch (e) {
|
||||
if (exceptions) {
|
||||
exceptions.push(e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const indexDataLoaded = async summary => {
|
||||
if (summary.version > 3) {
|
||||
throw 'Unsupported dictionary version';
|
||||
@ -247,10 +283,6 @@ class Database {
|
||||
};
|
||||
|
||||
const termDataLoaded = async (summary, entries, total, current) => {
|
||||
if (callback) {
|
||||
callback(total, current);
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
if (summary.version === 1) {
|
||||
for (const [expression, reading, definitionTags, rules, score, ...glossary] of entries) {
|
||||
@ -280,14 +312,10 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
await this.db.terms.bulkAdd(rows);
|
||||
await bulkAdd(this.db.terms, rows, total, current);
|
||||
};
|
||||
|
||||
const termMetaDataLoaded = async (summary, entries, total, current) => {
|
||||
if (callback) {
|
||||
callback(total, current);
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
for (const [expression, mode, data] of entries) {
|
||||
rows.push({
|
||||
@ -298,14 +326,10 @@ class Database {
|
||||
});
|
||||
}
|
||||
|
||||
await this.db.termMeta.bulkAdd(rows);
|
||||
await bulkAdd(this.db.termMeta, rows, total, current);
|
||||
};
|
||||
|
||||
const kanjiDataLoaded = async (summary, entries, total, current) => {
|
||||
if (callback) {
|
||||
callback(total, current);
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
if (summary.version === 1) {
|
||||
for (const [character, onyomi, kunyomi, tags, ...meanings] of entries) {
|
||||
@ -332,14 +356,10 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
await this.db.kanji.bulkAdd(rows);
|
||||
await bulkAdd(this.db.kanji, rows, total, current);
|
||||
};
|
||||
|
||||
const kanjiMetaDataLoaded = async (summary, entries, total, current) => {
|
||||
if (callback) {
|
||||
callback(total, current);
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
for (const [character, mode, data] of entries) {
|
||||
rows.push({
|
||||
@ -350,14 +370,10 @@ class Database {
|
||||
});
|
||||
}
|
||||
|
||||
await this.db.kanjiMeta.bulkAdd(rows);
|
||||
await bulkAdd(this.db.kanjiMeta, rows, total, current);
|
||||
};
|
||||
|
||||
const tagDataLoaded = async (summary, entries, total, current) => {
|
||||
if (callback) {
|
||||
callback(total, current);
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
for (const [name, category, order, notes, score] of entries) {
|
||||
const row = dictTagSanitize({
|
||||
@ -372,7 +388,7 @@ class Database {
|
||||
rows.push(row);
|
||||
}
|
||||
|
||||
await this.db.tagMeta.bulkAdd(rows);
|
||||
await bulkAdd(this.db.tagMeta, rows, total, current);
|
||||
};
|
||||
|
||||
return await Database.importDictionaryZip(
|
||||
|
@ -193,7 +193,7 @@ async function onReady() {
|
||||
await dictionaryGroupsPopulate(options);
|
||||
await formMainDictionaryOptionsPopulate(options);
|
||||
} catch (e) {
|
||||
dictionaryErrorShow(e);
|
||||
dictionaryErrorsShow([e]);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -203,6 +203,8 @@ async function onReady() {
|
||||
}
|
||||
|
||||
formUpdateVisibility(options);
|
||||
|
||||
storageInfoInitialize();
|
||||
}
|
||||
|
||||
$(document).ready(utilAsync(onReady));
|
||||
@ -212,36 +214,63 @@ $(document).ready(utilAsync(onReady));
|
||||
* Dictionary
|
||||
*/
|
||||
|
||||
function dictionaryErrorShow(error) {
|
||||
function dictionaryErrorToString(error) {
|
||||
if (error.toString) {
|
||||
error = error.toString();
|
||||
} else {
|
||||
error = `${error}`;
|
||||
}
|
||||
|
||||
for (const [match, subst] of dictionaryErrorToString.overrides) {
|
||||
if (error.includes(match)) {
|
||||
error = subst;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
dictionaryErrorToString.overrides = [
|
||||
[
|
||||
'A mutation operation was attempted on a database that did not allow mutations.',
|
||||
'Access to IndexedDB appears to be restricted. Firefox seems to require that the history preference is set to "Remember history" before IndexedDB use of any kind is allowed.'
|
||||
],
|
||||
[
|
||||
'The operation failed for reasons unrelated to the database itself and not covered by any other error code.',
|
||||
'Unable to access IndexedDB due to a possibly corrupt user profile. Try using the "Refresh Firefox" feature to reset your user profile.'
|
||||
],
|
||||
[
|
||||
'BulkError',
|
||||
'Unable to finish importing dictionary data into IndexedDB. This may indicate that you do not have sufficient disk space available to complete this operation.'
|
||||
]
|
||||
];
|
||||
|
||||
function dictionaryErrorsShow(errors) {
|
||||
const dialog = $('#dict-error');
|
||||
if (error) {
|
||||
const overrides = [
|
||||
[
|
||||
'A mutation operation was attempted on a database that did not allow mutations.',
|
||||
'Access to IndexedDB appears to be restricted. Firefox seems to require that the history preference is set to "Remember history" before IndexedDB use of any kind is allowed.'
|
||||
],
|
||||
[
|
||||
'The operation failed for reasons unrelated to the database itself and not covered by any other error code.',
|
||||
'Unable to access IndexedDB due to a possibly corrupt user profile. Try using the "Refresh Firefox" feature to reset your user profile.'
|
||||
],
|
||||
[
|
||||
'BulkError',
|
||||
'Unable to finish importing dictionary data into IndexedDB. This may indicate that you do not have sufficient disk space available to complete this operation.'
|
||||
]
|
||||
];
|
||||
dialog.show().text('');
|
||||
|
||||
if (error.toString) {
|
||||
error = error.toString();
|
||||
if (errors !== null && errors.length > 0) {
|
||||
const uniqueErrors = {};
|
||||
for (let e of errors) {
|
||||
e = dictionaryErrorToString(e);
|
||||
uniqueErrors[e] = uniqueErrors.hasOwnProperty(e) ? uniqueErrors[e] + 1 : 1;
|
||||
}
|
||||
|
||||
for (const [match, subst] of overrides) {
|
||||
if (error.includes(match)) {
|
||||
error = subst;
|
||||
break;
|
||||
for (const e in uniqueErrors) {
|
||||
const count = uniqueErrors[e];
|
||||
const div = document.createElement('p');
|
||||
if (count > 1) {
|
||||
div.textContent = `${e} `;
|
||||
const em = document.createElement('em');
|
||||
em.textContent = `(${count})`;
|
||||
div.appendChild(em);
|
||||
} else {
|
||||
div.textContent = `${e}`;
|
||||
}
|
||||
dialog.append($(div));
|
||||
}
|
||||
|
||||
dialog.show().text(error);
|
||||
dialog.show();
|
||||
} else {
|
||||
dialog.hide();
|
||||
}
|
||||
@ -317,7 +346,7 @@ async function onDictionaryPurge(e) {
|
||||
const dictProgress = $('#dict-purge').show();
|
||||
|
||||
try {
|
||||
dictionaryErrorShow();
|
||||
dictionaryErrorsShow(null);
|
||||
dictionarySpinnerShow(true);
|
||||
|
||||
await utilDatabasePurge();
|
||||
@ -329,12 +358,16 @@ async function onDictionaryPurge(e) {
|
||||
await dictionaryGroupsPopulate(options);
|
||||
await formMainDictionaryOptionsPopulate(options);
|
||||
} catch (e) {
|
||||
dictionaryErrorShow(e);
|
||||
dictionaryErrorsShow([e]);
|
||||
} finally {
|
||||
dictionarySpinnerShow(false);
|
||||
|
||||
dictControls.show();
|
||||
dictProgress.hide();
|
||||
|
||||
if (storageEstimate.mostRecent !== null) {
|
||||
storageUpdateStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,25 +377,37 @@ async function onDictionaryImport(e) {
|
||||
const dictProgress = $('#dict-import-progress').show();
|
||||
|
||||
try {
|
||||
dictionaryErrorShow();
|
||||
dictionaryErrorsShow(null);
|
||||
dictionarySpinnerShow(true);
|
||||
|
||||
const setProgress = percent => dictProgress.find('.progress-bar').css('width', `${percent}%`);
|
||||
const updateProgress = (total, current) => setProgress(current / total * 100.0);
|
||||
const updateProgress = (total, current) => {
|
||||
setProgress(current / total * 100.0);
|
||||
if (storageEstimate.mostRecent !== null && !storageUpdateStats.isUpdating) {
|
||||
storageUpdateStats();
|
||||
}
|
||||
};
|
||||
setProgress(0.0);
|
||||
|
||||
const exceptions = [];
|
||||
const options = await optionsLoad();
|
||||
const summary = await utilDatabaseImport(e.target.files[0], updateProgress);
|
||||
const summary = await utilDatabaseImport(e.target.files[0], updateProgress, exceptions);
|
||||
options.dictionaries[summary.title] = {enabled: true, priority: 0, allowSecondarySearches: false};
|
||||
if (summary.sequenced && options.general.mainDictionary === '') {
|
||||
options.general.mainDictionary = summary.title;
|
||||
}
|
||||
|
||||
if (exceptions.length > 0) {
|
||||
exceptions.push(`Dictionary may not have been imported properly: ${exceptions.length} error${exceptions.length === 1 ? '' : 's'} reported.`);
|
||||
dictionaryErrorsShow(exceptions);
|
||||
}
|
||||
|
||||
await optionsSave(options);
|
||||
|
||||
await dictionaryGroupsPopulate(options);
|
||||
await formMainDictionaryOptionsPopulate(options);
|
||||
} catch (e) {
|
||||
dictionaryErrorShow(e);
|
||||
dictionaryErrorsShow([e]);
|
||||
} finally {
|
||||
dictionarySpinnerShow(false);
|
||||
|
||||
@ -520,3 +565,93 @@ async function onAnkiFieldTemplatesReset(e) {
|
||||
ankiErrorShow(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Storage
|
||||
*/
|
||||
|
||||
async function getBrowser() {
|
||||
if (typeof chrome !== "undefined") {
|
||||
if (typeof browser !== "undefined") {
|
||||
try {
|
||||
const info = await browser.runtime.getBrowserInfo();
|
||||
if (info.name === "Fennec") {
|
||||
return "firefox-mobile";
|
||||
}
|
||||
} catch (e) { }
|
||||
return "firefox";
|
||||
} else {
|
||||
return "chrome";
|
||||
}
|
||||
} else {
|
||||
return "edge";
|
||||
}
|
||||
}
|
||||
|
||||
function storageBytesToLabeledString(size) {
|
||||
const base = 1000;
|
||||
const labels = ["bytes", "KB", "MB", "GB"];
|
||||
let labelIndex = 0;
|
||||
while (size >= base) {
|
||||
size /= base;
|
||||
++labelIndex;
|
||||
}
|
||||
const label = size.toFixed(1);
|
||||
return `${label}${labels[labelIndex]}`;
|
||||
}
|
||||
|
||||
async function storageEstimate() {
|
||||
try {
|
||||
return (storageEstimate.mostRecent = await navigator.storage.estimate());
|
||||
} catch (e) { }
|
||||
return null;
|
||||
}
|
||||
storageEstimate.mostRecent = null;
|
||||
|
||||
async function storageInfoInitialize() {
|
||||
const browser = await getBrowser();
|
||||
const container = document.querySelector("#storage-info");
|
||||
container.setAttribute("data-browser", browser);
|
||||
|
||||
await storageShowInfo();
|
||||
|
||||
container.classList.remove("storage-hidden");
|
||||
|
||||
document.querySelector("#storage-refresh").addEventListener('click', () => storageShowInfo(), false);
|
||||
}
|
||||
|
||||
async function storageUpdateStats() {
|
||||
storageUpdateStats.isUpdating = true;
|
||||
|
||||
const estimate = await storageEstimate();
|
||||
const valid = (estimate !== null);
|
||||
|
||||
if (valid) {
|
||||
document.querySelector("#storage-usage").textContent = storageBytesToLabeledString(estimate.usage);
|
||||
document.querySelector("#storage-quota").textContent = storageBytesToLabeledString(estimate.quota);
|
||||
}
|
||||
|
||||
storageUpdateStats.isUpdating = false;
|
||||
return valid;
|
||||
}
|
||||
storageUpdateStats.isUpdating = false;
|
||||
|
||||
async function storageShowInfo() {
|
||||
storageSpinnerShow(true);
|
||||
|
||||
const valid = await storageUpdateStats();
|
||||
document.querySelector("#storage-use").classList.toggle("storage-hidden", !valid);
|
||||
document.querySelector("#storage-error").classList.toggle("storage-hidden", valid);
|
||||
|
||||
storageSpinnerShow(false);
|
||||
}
|
||||
|
||||
function storageSpinnerShow(show) {
|
||||
const spinner = $('#storage-spinner');
|
||||
if (show) {
|
||||
spinner.show();
|
||||
} else {
|
||||
spinner.hide();
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,6 @@ function utilDatabasePurge() {
|
||||
return utilBackend().translator.database.purge();
|
||||
}
|
||||
|
||||
function utilDatabaseImport(data, progress) {
|
||||
return utilBackend().translator.database.importDictionary(data, progress);
|
||||
function utilDatabaseImport(data, progress, exceptions) {
|
||||
return utilBackend().translator.database.importDictionary(data, progress, exceptions);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Yomichan Legal</title>
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap-theme.min.css">
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Yomichan Search</title>
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap-theme.min.css">
|
||||
|
@ -2,13 +2,14 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Yomichan Options</title>
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/mixed/lib/bootstrap/css/bootstrap-theme.min.css">
|
||||
<style>
|
||||
#anki-spinner, #anki-general, #anki-error,
|
||||
#dict-spinner, #dict-error, #dict-warning, #dict-purge, #dict-import-progress,
|
||||
#debug, .options-advanced {
|
||||
#debug, .options-advanced, .storage-hidden, #storage-spinner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -24,6 +25,21 @@
|
||||
overflow-x: hidden;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.bottom-links {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
[data-show-for-browser] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-browser=edge] [data-show-for-browser~=edge],
|
||||
[data-browser=chrome] [data-show-for-browser~=chrome],
|
||||
[data-browser=firefox] [data-show-for-browser~=firefox],
|
||||
[data-browser=firefox-mobile] [data-show-for-browser~=firefox-mobile] {
|
||||
display: initial;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -192,6 +208,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="storage-info" class="storage-hidden">
|
||||
<div>
|
||||
<img src="/mixed/img/spinner.gif" class="pull-right" id="storage-spinner" />
|
||||
<h3>Storage</h3>
|
||||
</div>
|
||||
|
||||
<div id="storage-use" class="storage-hidden">
|
||||
<p class="help-block">
|
||||
Yomichan is using approximately <strong id="storage-usage"></strong> of <strong id="storage-quota"></strong>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="storage-error" class="storage-hidden">
|
||||
<p class="help-block">
|
||||
Could not detect how much storage Yomichan is using.
|
||||
</p>
|
||||
<div data-show-for-browser="firefox firefox-mobile"><div class="alert alert-danger options-advanced">
|
||||
On Firefox and Firefox for Android, the storage information feature may be hidden behind a browser flag.
|
||||
|
||||
If you would like to enable this flag, open <a href="about:config" target="_blank">about:config</a> and search for the
|
||||
<strong>dom.storageManager.enabled</strong> option. If this option has a value of <strong>false</strong>, toggling it to
|
||||
<strong>true</strong> may allow storage information to be calculated.
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<div data-show-for-browser="firefox-mobile"><div class="alert alert-warning">
|
||||
If you are using Firefox for Android, you will have to make sure you have enough free space on your device to install dictionaries.
|
||||
</div></div>
|
||||
|
||||
<div>
|
||||
<input type="button" value="Refresh" id="storage-refresh" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<img src="/mixed/img/spinner.gif" class="pull-right" id="anki-spinner" alt>
|
||||
@ -310,8 +360,8 @@
|
||||
|
||||
<pre id="debug"></pre>
|
||||
|
||||
<div class="pull-right">
|
||||
<small><a href="https://foosoft.net/projects/yomichan/" target="_blank">Homepage</a> • <a href="legal.html">Legal</a></small>
|
||||
<div class="pull-right bottom-links">
|
||||
<small><a href="search.html">Search</a> • <a href="https://foosoft.net/projects/yomichan/" target="_blank">Homepage</a> • <a href="legal.html">Legal</a></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="/mixed/lib/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/mixed/lib/bootstrap/css/bootstrap-theme.min.css">
|
||||
|
@ -256,7 +256,7 @@ class Frontend {
|
||||
}
|
||||
|
||||
onError(error) {
|
||||
window.alert(`Error: ${error.toString ? error.toString() : error}`);
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
popupTimerSet(callback) {
|
||||
|
@ -26,11 +26,15 @@ function utilAsync(func) {
|
||||
function utilInvoke(action, params={}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
chrome.runtime.sendMessage({action, params}, ({result, error}) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
chrome.runtime.sendMessage({action, params}, (response) => {
|
||||
if (response !== null && typeof response === 'object') {
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
} else {
|
||||
resolve(response.result);
|
||||
}
|
||||
} else {
|
||||
resolve(result);
|
||||
reject(`Unexpected response of type ${typeof response}`);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
|
Loading…
Reference in New Issue
Block a user