aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--ext/bg/css/settings.css37
-rw-r--r--ext/bg/data/default-anki-field-templates.handlebars4
-rw-r--r--ext/bg/data/dictionary-index-schema.json16
-rw-r--r--ext/bg/js/anki-note-builder.js76
-rw-r--r--ext/bg/js/backend.js99
-rw-r--r--ext/bg/js/dictionary-importer.js27
-rw-r--r--ext/bg/js/options.js9
-rw-r--r--ext/bg/js/settings/anki-templates.js7
-rw-r--r--ext/bg/js/settings/anki.js2
-rw-r--r--ext/bg/js/settings/dictionaries.js50
-rw-r--r--ext/bg/settings.html4
-rw-r--r--ext/fg/js/float.js27
-rw-r--r--ext/fg/js/frontend.js11
-rw-r--r--ext/mixed/js/api.js8
-rw-r--r--ext/mixed/js/display.js23
16 files changed, 295 insertions, 107 deletions
diff --git a/README.md b/README.md
index 631f5a8b..bf217679 100644
--- a/README.md
+++ b/README.md
@@ -156,6 +156,7 @@ Flashcard fields can be configured with the following steps:
`{cloze-prefix}` | Text for the containing `{sentence}` from the start up to the value of `{cloze-body}`.
`{cloze-suffix}` | Text for the containing `{sentence}` from the value of `{cloze-body}` to the end.
`{dictionary}` | Name of the dictionary from which the card is being created (unavailable in *grouped* mode).
+ `{document-title}` | Title of the web page that the term appeared in.
`{expression}` | Term expressed as Kanji (will be displayed in Kana if Kanji is not available).
`{furigana}` | Term expressed as Kanji with Furigana displayed above it (e.g. <ruby>日本語<rt>にほんご</rt></ruby>).
`{furigana-plain}` | Term expressed as Kanji with Furigana displayed next to it in brackets (e.g. 日本語[にほんご]).
@@ -175,6 +176,7 @@ Flashcard fields can be configured with the following steps:
`{cloze-prefix}` | Text for the containing `{sentence}` from the start up to the value of `{cloze-body}`.
`{cloze-suffix}` | Text for the containing `{sentence}` from the value of `{cloze-body}` to the end.
`{dictionary}` | Name of the dictionary from which the card is being created.
+ `{document-title}` | Title of the web page that the Kanji appeared in.
`{glossary}` | List of definitions for the Kanji.
`{kunyomi}` | Kunyomi (Japanese reading) for the Kanji expressed as Katakana.
`{onyomi}` | Onyomi (Chinese reading) for the Kanji expressed as Hiragana.
diff --git a/ext/bg/css/settings.css b/ext/bg/css/settings.css
index d686e8f8..6344bd38 100644
--- a/ext/bg/css/settings.css
+++ b/ext/bg/css/settings.css
@@ -235,6 +235,43 @@ html:root[data-operating-system=openbsd] [data-hide-for-operating-system~=openbs
display: none;
}
+.dict-details-container {
+ margin: 0.5em 0;
+}
+
+.dict-details-toggle-link {
+ cursor: pointer;
+}
+
+.dict-details {
+ margin-left: 1em;
+}
+
+.dict-details-table {
+ display: table;
+ width: 100%
+}
+
+.dict-details-entry {
+ display: table-row;
+}
+
+.dict-details-entry+.dict-details-entry>* {
+ padding-top: 0.25em;
+}
+
+.dict-details-entry-label {
+ display: table-cell;
+ font-weight: bold;
+ white-space: nowrap;
+ padding-right: 0.5em;
+}
+
+.dict-details-entry-info {
+ display: table-cell;
+ white-space: pre-line;
+}
+
@media screen and (max-width: 740px) {
.col-xs-6 {
diff --git a/ext/bg/data/default-anki-field-templates.handlebars b/ext/bg/data/default-anki-field-templates.handlebars
index 0442f7c5..6061851f 100644
--- a/ext/bg/data/default-anki-field-templates.handlebars
+++ b/ext/bg/data/default-anki-field-templates.handlebars
@@ -158,4 +158,8 @@
<img src="{{definition.screenshotFileName}}" />
{{/inline}}
+{{#*inline "document-title"}}
+ {{~context.document.title~}}
+{{/inline}}
+
{{~> (lookup . "marker") ~}} \ No newline at end of file
diff --git a/ext/bg/data/dictionary-index-schema.json b/ext/bg/data/dictionary-index-schema.json
index 9311f14c..09cff711 100644
--- a/ext/bg/data/dictionary-index-schema.json
+++ b/ext/bg/data/dictionary-index-schema.json
@@ -30,6 +30,22 @@
"description": "Alias for format.",
"enum": [1, 2, 3]
},
+ "author": {
+ "type": "string",
+ "description": "Creator of the dictionary."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL for the source of the dictionary."
+ },
+ "description": {
+ "type": "string",
+ "description": "Description of the dictionary data."
+ },
+ "attribution": {
+ "type": "string",
+ "description": "Attribution information for the dictionary data."
+ },
"tagMeta": {
"type": "object",
"description": "Tag information for terms and kanji. This object is obsolete and individual tag files should be used instead.",
diff --git a/ext/bg/js/anki-note-builder.js b/ext/bg/js/anki-note-builder.js
index d0ff8205..244aaab8 100644
--- a/ext/bg/js/anki-note-builder.js
+++ b/ext/bg/js/anki-note-builder.js
@@ -17,11 +17,12 @@
*/
class AnkiNoteBuilder {
- constructor({renderTemplate}) {
+ constructor({audioSystem, renderTemplate}) {
+ this._audioSystem = audioSystem;
this._renderTemplate = renderTemplate;
}
- async createNote(definition, mode, options, templates) {
+ async createNote(definition, mode, context, options, templates) {
const isKanji = (mode === 'kanji');
const tags = options.anki.tags;
const modeOptions = isKanji ? options.anki.kanji : options.anki.terms;
@@ -35,7 +36,7 @@ class AnkiNoteBuilder {
};
for (const [fieldName, fieldValue] of modeOptionsFieldEntries) {
- note.fields[fieldName] = await this.formatField(fieldValue, definition, mode, options, templates, null);
+ note.fields[fieldName] = await this.formatField(fieldValue, definition, mode, context, options, templates, null);
}
if (!isKanji && definition.audio) {
@@ -60,7 +61,7 @@ class AnkiNoteBuilder {
return note;
}
- async formatField(field, definition, mode, options, templates, errors=null) {
+ async formatField(field, definition, mode, context, options, templates, errors=null) {
const data = {
marker: null,
definition,
@@ -69,7 +70,8 @@ class AnkiNoteBuilder {
modeTermKanji: mode === 'term-kanji',
modeTermKana: mode === 'term-kana',
modeKanji: mode === 'kanji',
- compactGlossaries: options.general.compactGlossaries
+ compactGlossaries: options.general.compactGlossaries,
+ context
};
const pattern = /\{([\w-]+)\}/g;
return await AnkiNoteBuilder.stringReplaceAsync(field, pattern, async (g0, marker) => {
@@ -83,6 +85,70 @@ class AnkiNoteBuilder {
});
}
+ async injectAudio(definition, fields, sources, optionsContext) {
+ if (!this._containsMarker(fields, 'audio')) { return; }
+
+ try {
+ const expressions = definition.expressions;
+ const audioSourceDefinition = Array.isArray(expressions) ? expressions[0] : definition;
+
+ const {uri} = await this._audioSystem.getDefinitionAudio(audioSourceDefinition, sources, {tts: false, optionsContext});
+ const filename = this._createInjectedAudioFileName(audioSourceDefinition);
+ if (filename !== null) {
+ definition.audio = {url: uri, filename};
+ }
+ } catch (e) {
+ // NOP
+ }
+ }
+
+ async injectScreenshot(definition, fields, screenshot, anki) {
+ if (!this._containsMarker(fields, 'screenshot')) { return; }
+
+ const now = new Date(Date.now());
+ const filename = `yomichan_browser_screenshot_${definition.reading}_${this._dateToString(now)}.${screenshot.format}`;
+ const data = screenshot.dataUrl.replace(/^data:[\w\W]*?,/, '');
+
+ try {
+ await anki.storeMediaFile(filename, data);
+ } catch (e) {
+ return;
+ }
+
+ definition.screenshotFileName = filename;
+ }
+
+ _createInjectedAudioFileName(definition) {
+ const {reading, expression} = definition;
+ if (!reading && !expression) { return null; }
+
+ let filename = 'yomichan';
+ if (reading) { filename += `_${reading}`; }
+ if (expression) { filename += `_${expression}`; }
+ filename += '.mp3';
+ return filename;
+ }
+
+ _dateToString(date) {
+ const year = date.getUTCFullYear();
+ const month = date.getUTCMonth().toString().padStart(2, '0');
+ const day = date.getUTCDate().toString().padStart(2, '0');
+ const hours = date.getUTCHours().toString().padStart(2, '0');
+ const minutes = date.getUTCMinutes().toString().padStart(2, '0');
+ const seconds = date.getUTCSeconds().toString().padStart(2, '0');
+ return `${year}-${month}-${day}-${hours}-${minutes}-${seconds}`;
+ }
+
+ _containsMarker(fields, marker) {
+ marker = `{${marker}}`;
+ for (const fieldValue of Object.values(fields)) {
+ if (fieldValue.includes(marker)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
static stringReplaceAsync(str, regex, replacer) {
let match;
let index = 0;
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js
index 1e8c979f..1fa7ede1 100644
--- a/ext/bg/js/backend.js
+++ b/ext/bg/js/backend.js
@@ -51,12 +51,16 @@ class Backend {
this.anki = new AnkiNull();
this.mecab = new Mecab();
this.clipboardMonitor = new ClipboardMonitor({getClipboard: this._onApiClipboardGet.bind(this)});
- this.ankiNoteBuilder = new AnkiNoteBuilder({renderTemplate: this._renderTemplate.bind(this)});
this.options = null;
this.optionsSchema = null;
this.defaultAnkiFieldTemplates = null;
this.audioSystem = new AudioSystem({getAudioUri: this._getAudioUri.bind(this)});
this.audioUriBuilder = new AudioUriBuilder();
+ this.ankiNoteBuilder = new AnkiNoteBuilder({
+ audioSystem: this.audioSystem,
+ renderTemplate: this._renderTemplate.bind(this)
+ });
+
this.optionsContext = {
depth: 0,
url: window.location.href
@@ -455,12 +459,12 @@ class Backend {
return results;
}
- async _onApiDefinitionAdd({definition, mode, context, optionsContext}) {
+ async _onApiDefinitionAdd({definition, mode, context, details, optionsContext}) {
const options = this.getOptions(optionsContext);
const templates = this.defaultAnkiFieldTemplates;
if (mode !== 'kanji') {
- await this._audioInject(
+ await this.ankiNoteBuilder.injectAudio(
definition,
options.anki.terms.fields,
options.audio.sources,
@@ -468,19 +472,20 @@ class Backend {
);
}
- if (context && context.screenshot) {
- await this._injectScreenshot(
+ if (details && details.screenshot) {
+ await this.ankiNoteBuilder.injectScreenshot(
definition,
options.anki.terms.fields,
- context.screenshot
+ details.screenshot,
+ this.anki
);
}
- const note = await this.ankiNoteBuilder.createNote(definition, mode, options, templates);
+ const note = await this.ankiNoteBuilder.createNote(definition, mode, context, options, templates);
return this.anki.addNote(note);
}
- async _onApiDefinitionsAddable({definitions, modes, optionsContext}) {
+ async _onApiDefinitionsAddable({definitions, modes, context, optionsContext}) {
const options = this.getOptions(optionsContext);
const templates = this.defaultAnkiFieldTemplates;
const states = [];
@@ -489,7 +494,7 @@ class Backend {
const notes = [];
for (const definition of definitions) {
for (const mode of modes) {
- const note = await this.ankiNoteBuilder.createNote(definition, mode, options, templates);
+ const note = await this.ankiNoteBuilder.createNote(definition, mode, context, options, templates);
notes.push(note);
}
}
@@ -800,86 +805,10 @@ class Backend {
return await this.audioUriBuilder.getUri(definition, source, options);
}
- async _audioInject(definition, fields, sources, optionsContext) {
- let usesAudio = false;
- for (const fieldValue of Object.values(fields)) {
- if (fieldValue.includes('{audio}')) {
- usesAudio = true;
- break;
- }
- }
-
- if (!usesAudio) {
- return true;
- }
-
- try {
- const expressions = definition.expressions;
- const audioSourceDefinition = Array.isArray(expressions) ? expressions[0] : definition;
-
- const {uri} = await this.audioSystem.getDefinitionAudio(audioSourceDefinition, sources, {tts: false, optionsContext});
- const filename = this._createInjectedAudioFileName(audioSourceDefinition);
- if (filename !== null) {
- definition.audio = {url: uri, filename};
- }
-
- return true;
- } catch (e) {
- return false;
- }
- }
-
- async _injectScreenshot(definition, fields, screenshot) {
- let usesScreenshot = false;
- for (const fieldValue of Object.values(fields)) {
- if (fieldValue.includes('{screenshot}')) {
- usesScreenshot = true;
- break;
- }
- }
-
- if (!usesScreenshot) {
- return;
- }
-
- const dateToString = (date) => {
- const year = date.getUTCFullYear();
- const month = date.getUTCMonth().toString().padStart(2, '0');
- const day = date.getUTCDate().toString().padStart(2, '0');
- const hours = date.getUTCHours().toString().padStart(2, '0');
- const minutes = date.getUTCMinutes().toString().padStart(2, '0');
- const seconds = date.getUTCSeconds().toString().padStart(2, '0');
- return `${year}-${month}-${day}-${hours}-${minutes}-${seconds}`;
- };
-
- const now = new Date(Date.now());
- const filename = `yomichan_browser_screenshot_${definition.reading}_${dateToString(now)}.${screenshot.format}`;
- const data = screenshot.dataUrl.replace(/^data:[\w\W]*?,/, '');
-
- try {
- await this.anki.storeMediaFile(filename, data);
- } catch (e) {
- return;
- }
-
- definition.screenshotFileName = filename;
- }
-
async _renderTemplate(template, data) {
return handlebarsRenderDynamic(template, data);
}
- _createInjectedAudioFileName(definition) {
- const {reading, expression} = definition;
- if (!reading && !expression) { return null; }
-
- let filename = 'yomichan';
- if (reading) { filename += `_${reading}`; }
- if (expression) { filename += `_${expression}`; }
- filename += '.mp3';
- return filename;
- }
-
static _getTabUrl(tab) {
return new Promise((resolve) => {
chrome.tabs.sendMessage(tab.id, {action: 'getUrl'}, {frameId: 0}, (response) => {
diff --git a/ext/bg/js/dictionary-importer.js b/ext/bg/js/dictionary-importer.js
index 607a8b5e..254fde4f 100644
--- a/ext/bg/js/dictionary-importer.js
+++ b/ext/bg/js/dictionary-importer.js
@@ -150,13 +150,7 @@ class DictionaryImporter {
}
// Add dictionary
- const summary = {
- title: dictionaryTitle,
- revision: index.revision,
- sequenced: index.sequenced,
- version,
- prefixWildcardsSupported
- };
+ const summary = this._createSummary(dictionaryTitle, version, index, {prefixWildcardsSupported});
database.bulkAdd('dictionaries', [summary], 0, 1);
@@ -199,6 +193,25 @@ class DictionaryImporter {
return {result: summary, errors};
}
+ _createSummary(dictionaryTitle, version, index, details) {
+ const summary = {
+ title: dictionaryTitle,
+ revision: index.revision,
+ sequenced: index.sequenced,
+ version
+ };
+
+ const {author, url, description, attribution} = index;
+ if (typeof author === 'string') { summary.author = author; }
+ if (typeof url === 'string') { summary.url = url; }
+ if (typeof description === 'string') { summary.description = description; }
+ if (typeof attribution === 'string') { summary.attribution = attribution; }
+
+ Object.assign(summary, details);
+
+ return summary;
+ }
+
async _getSchema(fileName) {
let schemaPromise = this._schemas.get(fileName);
if (typeof schemaPromise !== 'undefined') {
diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js
index 5c68c403..abb054d4 100644
--- a/ext/bg/js/options.js
+++ b/ext/bg/js/options.js
@@ -91,6 +91,15 @@ const profileOptionsVersionUpdates = [
if (utilStringHashCode(options.anki.fieldTemplates) === 1444379824) {
options.anki.fieldTemplates = null;
}
+ },
+ (options) => {
+ // Version 13 changes:
+ // Default anki field tempaltes updated to include {document-title}.
+ let fieldTemplates = options.anki.fieldTemplates;
+ if (typeof fieldTemplates === 'string') {
+ fieldTemplates += '\n\n{{#*inline "document-title"}}\n {{~context.document.title~}}\n{{/inline}}';
+ options.anki.fieldTemplates = fieldTemplates;
+ }
}
];
diff --git a/ext/bg/js/settings/anki-templates.js b/ext/bg/js/settings/anki-templates.js
index c5222d30..e3852eb4 100644
--- a/ext/bg/js/settings/anki-templates.js
+++ b/ext/bg/js/settings/anki-templates.js
@@ -99,10 +99,15 @@ async function ankiTemplatesValidate(infoNode, field, mode, showSuccessResult, i
const definition = await ankiTemplatesValidateGetDefinition(text, optionsContext);
if (definition !== null) {
const options = await apiOptionsGet(optionsContext);
+ const context = {
+ document: {
+ title: document.title
+ }
+ };
let templates = options.anki.fieldTemplates;
if (typeof templates !== 'string') { templates = await apiGetDefaultAnkiFieldTemplates(); }
const ankiNoteBuilder = new AnkiNoteBuilder({renderTemplate: apiTemplateRender});
- result = await ankiNoteBuilder.formatField(field, definition, mode, options, templates, exceptions);
+ result = await ankiNoteBuilder.formatField(field, definition, mode, context, options, templates, exceptions);
}
} catch (e) {
exceptions.push(e);
diff --git a/ext/bg/js/settings/anki.js b/ext/bg/js/settings/anki.js
index b706cd1b..f2e1ca76 100644
--- a/ext/bg/js/settings/anki.js
+++ b/ext/bg/js/settings/anki.js
@@ -243,6 +243,7 @@ function ankiGetFieldMarkers(type) {
'cloze-prefix',
'cloze-suffix',
'dictionary',
+ 'document-title',
'expression',
'furigana',
'furigana-plain',
@@ -258,6 +259,7 @@ function ankiGetFieldMarkers(type) {
return [
'character',
'dictionary',
+ 'document-title',
'glossary',
'kunyomi',
'onyomi',
diff --git a/ext/bg/js/settings/dictionaries.js b/ext/bg/js/settings/dictionaries.js
index 00056e2e..33ced3b9 100644
--- a/ext/bg/js/settings/dictionaries.js
+++ b/ext/bg/js/settings/dictionaries.js
@@ -199,11 +199,16 @@ class SettingsDictionaryEntryUI {
this.allowSecondarySearchesCheckbox = this.content.querySelector('.dict-allow-secondary-searches');
this.priorityInput = this.content.querySelector('.dict-priority');
this.deleteButton = this.content.querySelector('.dict-delete-button');
+ this.detailsToggleLink = this.content.querySelector('.dict-details-toggle-link');
+ this.detailsContainer = this.content.querySelector('.dict-details');
+ this.detailsTable = this.content.querySelector('.dict-details-table');
if (this.dictionaryInfo.version < 3) {
this.content.querySelector('.dict-outdated').hidden = false;
}
+ this.setupDetails(dictionaryInfo);
+
this.content.querySelector('.dict-title').textContent = this.dictionaryInfo.title;
this.content.querySelector('.dict-revision').textContent = `rev.${this.dictionaryInfo.revision}`;
this.content.querySelector('.dict-prefix-wildcard-searches-supported').checked = !!this.dictionaryInfo.prefixWildcardsSupported;
@@ -214,6 +219,45 @@ class SettingsDictionaryEntryUI {
this.eventListeners.addEventListener(this.allowSecondarySearchesCheckbox, 'change', this.onAllowSecondarySearchesChanged.bind(this), false);
this.eventListeners.addEventListener(this.priorityInput, 'change', this.onPriorityChanged.bind(this), false);
this.eventListeners.addEventListener(this.deleteButton, 'click', this.onDeleteButtonClicked.bind(this), false);
+ this.eventListeners.addEventListener(this.detailsToggleLink, 'click', this.onDetailsToggleLinkClicked.bind(this), false);
+ }
+
+ setupDetails(dictionaryInfo) {
+ const targets = [
+ ['Author', 'author'],
+ ['URL', 'url'],
+ ['Description', 'description'],
+ ['Attribution', 'attribution']
+ ];
+
+ let count = 0;
+ for (const [label, key] of targets) {
+ const info = dictionaryInfo[key];
+ if (typeof info !== 'string') { continue; }
+
+ const n1 = document.createElement('div');
+ n1.className = 'dict-details-entry';
+ n1.dataset.type = key;
+
+ const n2 = document.createElement('span');
+ n2.className = 'dict-details-entry-label';
+ n2.textContent = `${label}:`;
+ n1.appendChild(n2);
+
+ const n3 = document.createElement('span');
+ n3.className = 'dict-details-entry-info';
+ n3.textContent = info;
+ n1.appendChild(n3);
+
+ this.detailsTable.appendChild(n1);
+
+ ++count;
+ }
+
+ if (count === 0) {
+ this.detailsContainer.hidden = true;
+ this.detailsToggleLink.hidden = true;
+ }
}
cleanup() {
@@ -318,6 +362,12 @@ class SettingsDictionaryEntryUI {
document.querySelector('#dict-remove-modal-dict-name').textContent = title;
$(n).modal('show');
}
+
+ onDetailsToggleLinkClicked(e) {
+ e.preventDefault();
+
+ this.detailsContainer.hidden = !this.detailsContainer.hidden;
+ }
}
class SettingsDictionaryExtraUI {
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index 237162c7..1297a9cc 100644
--- a/ext/bg/settings.html
+++ b/ext/bg/settings.html
@@ -674,6 +674,10 @@
<label class="dict-result-priority-label">Result priority</label>
<input type="number" class="form-control dict-priority">
</div>
+ <div class="dict-details-container">
+ <a class="dict-details-toggle-link">Details...</a>
+ <div class="dict-details" hidden><div class="dict-details-table"></div></div>
+ </div>
<div class="dict-delete-table">
<div>
<button class="btn btn-default dict-delete-button">Delete Dictionary</button>
diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js
index 9b720ebe..01055ca6 100644
--- a/ext/fg/js/float.js
+++ b/ext/fg/js/float.js
@@ -162,6 +162,33 @@ class DisplayFloat extends Display {
setContentScale(scale) {
document.body.style.fontSize = `${scale}em`;
}
+
+ async getDocumentTitle() {
+ try {
+ const uniqueId = yomichan.generateId(16);
+
+ const promise = yomichan.getTemporaryListenerResult(
+ chrome.runtime.onMessage,
+ ({action, params}, {resolve}) => {
+ if (
+ action === 'documentInformationBroadcast' &&
+ isObject(params) &&
+ params.uniqueId === uniqueId &&
+ params.frameId === 0
+ ) {
+ resolve(params);
+ }
+ },
+ 2000
+ );
+ apiForward('requestDocumentInformationBroadcast', {uniqueId});
+
+ const {title} = await promise;
+ return title;
+ } catch (e) {
+ return '';
+ }
+ }
}
DisplayFloat.instance = new DisplayFloat();
diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js
index 4e9d474c..31843212 100644
--- a/ext/fg/js/frontend.js
+++ b/ext/fg/js/frontend.js
@@ -54,7 +54,8 @@ class Frontend extends TextScanner {
this._runtimeMessageHandlers = new Map([
['popupSetVisibleOverride', ({visible}) => { this.popup.setVisibleOverride(visible); }],
- ['rootPopupRequestInformationBroadcast', () => { this._broadcastRootPopupInformation(); }]
+ ['rootPopupRequestInformationBroadcast', () => { this._broadcastRootPopupInformation(); }],
+ ['requestDocumentInformationBroadcast', ({uniqueId}) => { this._broadcastDocumentInformation(uniqueId); }]
]);
}
@@ -264,6 +265,14 @@ class Frontend extends TextScanner {
}
}
+ _broadcastDocumentInformation(uniqueId) {
+ apiForward('documentInformationBroadcast', {
+ uniqueId,
+ frameId: this.popup.frameId,
+ title: document.title
+ });
+ }
+
async _updatePopupPosition() {
const textSource = this.getCurrentTextSource();
if (textSource !== null && await this.popup.isVisible()) {
diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js
index 0ab07039..feec94df 100644
--- a/ext/mixed/js/api.js
+++ b/ext/mixed/js/api.js
@@ -53,12 +53,12 @@ function apiKanjiFind(text, optionsContext) {
return _apiInvoke('kanjiFind', {text, optionsContext});
}
-function apiDefinitionAdd(definition, mode, context, optionsContext) {
- return _apiInvoke('definitionAdd', {definition, mode, context, optionsContext});
+function apiDefinitionAdd(definition, mode, context, details, optionsContext) {
+ return _apiInvoke('definitionAdd', {definition, mode, context, details, optionsContext});
}
-function apiDefinitionsAddable(definitions, modes, optionsContext) {
- return _apiInvoke('definitionsAddable', {definitions, modes, optionsContext});
+function apiDefinitionsAddable(definitions, modes, context, optionsContext) {
+ return _apiInvoke('definitionsAddable', {definitions, modes, context, optionsContext});
}
function apiNoteView(noteId) {
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index 4a71efe0..2f456c3e 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -752,15 +752,16 @@ class Display {
try {
this.setSpinnerVisible(true);
- const context = {};
+ const details = {};
if (this.noteUsesScreenshot(mode)) {
const screenshot = await this.getScreenshot();
if (screenshot) {
- context.screenshot = screenshot;
+ details.screenshot = screenshot;
}
}
- const noteId = await apiDefinitionAdd(definition, mode, context, this.getOptionsContext());
+ const context = await this._getNoteContext();
+ const noteId = await apiDefinitionAdd(definition, mode, context, details, this.getOptionsContext());
if (noteId) {
const index = this.definitions.indexOf(definition);
const adderButton = this.adderButtonFind(index, mode);
@@ -908,12 +909,17 @@ class Display {
async getDefinitionsAddable(definitions, modes) {
try {
- return await apiDefinitionsAddable(definitions, modes, this.getOptionsContext());
+ const context = await this._getNoteContext();
+ return await apiDefinitionsAddable(definitions, modes, context, this.getOptionsContext());
} catch (e) {
return [];
}
}
+ async getDocumentTitle() {
+ return document.title;
+ }
+
static indexOf(nodeList, node) {
for (let i = 0, ii = nodeList.length; i < ii; ++i) {
if (nodeList[i] === node) {
@@ -934,6 +940,15 @@ class Display {
return (typeof key === 'string' ? (key.length === 1 ? key.toUpperCase() : key) : '');
}
+ async _getNoteContext() {
+ const documentTitle = await this.getDocumentTitle();
+ return {
+ document: {
+ title: documentTitle
+ }
+ };
+ }
+
async _getAudioUri(definition, source) {
const optionsContext = this.getOptionsContext();
return await apiAudioGetUri(definition, source, optionsContext);