Improve note content insertion
This commit is contained in:
parent
404eaf13d0
commit
232ffba2ab
5 changed files with 164 additions and 78 deletions
|
@ -26,10 +26,10 @@
|
|||
class EditorInstance {
|
||||
constructor() {
|
||||
this.instanceID = Zotero.Utilities.randomString();
|
||||
Zotero.Notes.registerEditorInstance(this);
|
||||
}
|
||||
|
||||
async init(options) {
|
||||
Zotero.Notes.registerEditorInstance(this);
|
||||
this.onNavigate = options.onNavigate;
|
||||
this._item = options.item;
|
||||
this._readOnly = options.readOnly;
|
||||
|
@ -135,9 +135,9 @@ class EditorInstance {
|
|||
}
|
||||
|
||||
async insertAnnotations(annotations) {
|
||||
let list = await this._annotationsToInsertionList(annotations);
|
||||
if (list.length) {
|
||||
this._postMessage({ action: 'insertAnnotationsAndCitations', list, pos: -1 });
|
||||
let html = await this._digestAnnotations(annotations);
|
||||
if (html) {
|
||||
this._postMessage({ action: 'insertHtml', pos: -1, html });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,63 +158,125 @@ class EditorInstance {
|
|||
_handleFontChange = () => {
|
||||
this._postMessage({ action: 'updateFont', font: this._getFont() });
|
||||
}
|
||||
|
||||
async _digestExternalNote(itemID) {
|
||||
let item = await Zotero.Items.getAsync(itemID);
|
||||
item._loaded.childItems = true;
|
||||
let note = item.note;
|
||||
let attachments = await Zotero.Items.getAsync(item.getAttachments());
|
||||
for (let attachment of attachments) {
|
||||
let path = await attachment.getFilePathAsync();
|
||||
let buf = await OS.File.read(path, {});
|
||||
buf = new Uint8Array(buf).buffer;
|
||||
let blob = new this._iframeWindow.Blob([buf], { type: attachment.attachmentContentType });
|
||||
let clonedAttachment = await Zotero.Attachments.importEmbeddedImage({
|
||||
blob,
|
||||
parentItemID: this._item.id,
|
||||
saveOptions: {
|
||||
notifierData: {
|
||||
noteEditorID: this.instanceID
|
||||
}
|
||||
}
|
||||
});
|
||||
note = note.replace(attachment.key, clonedAttachment.key);
|
||||
}
|
||||
return note;
|
||||
}
|
||||
|
||||
async _annotationsToInsertionList(annotations) {
|
||||
let list = [];
|
||||
async _digestAnnotations(annotations) {
|
||||
let html = '';
|
||||
for (let annotation of annotations) {
|
||||
let attachmentItem = await Zotero.Items.getAsync(annotation.itemId);
|
||||
if (!attachmentItem) {
|
||||
continue;
|
||||
}
|
||||
let item = attachmentItem.parentID && await Zotero.Items.getAsync(attachmentItem.parentID) || attachmentItem;
|
||||
|
||||
let citationHTML = '';
|
||||
let imageHTML = '';
|
||||
let highlightHTML = '';
|
||||
let commentHTML = '';
|
||||
|
||||
annotation.uri = Zotero.URI.getItemURI(attachmentItem);
|
||||
if (item !== attachmentItem) {
|
||||
annotation.parentURI = Zotero.URI.getItemURI(item);
|
||||
|
||||
|
||||
// Citation
|
||||
let parentItem = attachmentItem.parentID && await Zotero.Items.getAsync(attachmentItem.parentID) || attachmentItem;
|
||||
if (parentItem) {
|
||||
annotation.parentURI = Zotero.URI.getItemURI(parentItem);
|
||||
let citationItem = {
|
||||
uris: [Zotero.URI.getItemURI(item)],
|
||||
itemData: Zotero.Cite.System.prototype.retrieveItem(item),
|
||||
uris: [Zotero.URI.getItemURI(parentItem)],
|
||||
// TODO: Find a more elegant way to call this method
|
||||
itemData: Zotero.Cite.System.prototype.retrieveItem(parentItem),
|
||||
locator: annotation.pageLabel
|
||||
};
|
||||
|
||||
annotation.citationItem = citationItem;
|
||||
|
||||
let citation = {
|
||||
citationItems: [citationItem],
|
||||
properties: {}
|
||||
};
|
||||
let formattedCitation = (await this._getFormattedCitationParts(citation)).join(';');
|
||||
list.push({ annotation, citation, formattedCitation });
|
||||
let formatted = (await this._getFormattedCitationParts(citation)).join(';');
|
||||
citationHTML = `<span class="citation" data-citation="${encodeURIComponent(JSON.stringify(citation))}">(${formatted})</span>`;
|
||||
}
|
||||
else {
|
||||
list.push({ annotation });
|
||||
|
||||
// Image
|
||||
if (annotation.image) {
|
||||
let blob = this._dataURLtoBlob(annotation.image);
|
||||
delete annotation.image;
|
||||
let imageAttachment = await Zotero.Attachments.importEmbeddedImage({
|
||||
blob,
|
||||
parentItemID: this._item.id,
|
||||
saveOptions: {
|
||||
notifierData: {
|
||||
noteEditorID: this.instanceID
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Normalize image dimensions to 1.25 of the print size
|
||||
let rect = annotation.position.rects[0];
|
||||
let rectWidth = rect[2] - rect[0];
|
||||
let rectHeight = rect[3] - rect[1];
|
||||
// Constants from pdf.js
|
||||
const CSS_UNITS = 96.0 / 72.0;
|
||||
const PDFJS_DEFAULT_SCALE = 1.25;
|
||||
let width = Math.round(rectWidth * CSS_UNITS * PDFJS_DEFAULT_SCALE);
|
||||
let height = Math.round(rectHeight * width / rectWidth);
|
||||
imageHTML = `<img data-attachment-key="${imageAttachment.key}" width="${width}" height="${height}" data-annotation="${encodeURIComponent(JSON.stringify(annotation))}"/>`;
|
||||
}
|
||||
|
||||
// Text
|
||||
if (annotation.text) {
|
||||
highlightHTML = `<span class="highlight" data-annotation="${encodeURIComponent(JSON.stringify(annotation))}">"${annotation.text}"</span>`;
|
||||
}
|
||||
|
||||
// Note
|
||||
if (annotation.comment) {
|
||||
commentHTML = ' ' + annotation.comment;
|
||||
}
|
||||
|
||||
html += '<p>' + imageHTML + [highlightHTML, citationHTML, commentHTML].filter(x => x).join(' ') + '</p>\n';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
async _digestItems(ids) {
|
||||
let html = '';
|
||||
for (let id of ids) {
|
||||
let item = await Zotero.Items.getAsync(id);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
if (item.isRegularItem()) {
|
||||
let citation = {
|
||||
citationItems: [{
|
||||
uris: [Zotero.URI.getItemURI(item)],
|
||||
itemData: Zotero.Cite.System.prototype.retrieveItem(item)
|
||||
}],
|
||||
properties: {}
|
||||
};
|
||||
let formatted = (await this._getFormattedCitationParts(citation)).join(';');
|
||||
html += `<p><span class="citation" data-citation="${encodeURIComponent(JSON.stringify(citation))}">(${formatted})</span></p>`;
|
||||
}
|
||||
else if (item.isNote()) {
|
||||
// TODO: Remove when fixed
|
||||
item._loaded.childItems = true;
|
||||
let note = item.note;
|
||||
let attachments = await Zotero.Items.getAsync(item.getAttachments());
|
||||
for (let attachment of attachments) {
|
||||
let path = await attachment.getFilePathAsync();
|
||||
let buf = await OS.File.read(path, {});
|
||||
buf = new Uint8Array(buf).buffer;
|
||||
let blob = new (Zotero.getMainWindow()).Blob([buf], { type: attachment.attachmentContentType });
|
||||
let clonedAttachment = await Zotero.Attachments.importEmbeddedImage({
|
||||
blob,
|
||||
parentItemID: this._item.id,
|
||||
saveOptions: {
|
||||
notifierData: {
|
||||
noteEditorID: this.instanceID
|
||||
}
|
||||
}
|
||||
});
|
||||
note = note.replace(attachment.key, clonedAttachment.key);
|
||||
}
|
||||
html += `<p></p>${note}<p></p>`;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
return html;
|
||||
}
|
||||
|
||||
_messageHandler = async (e) => {
|
||||
|
@ -225,40 +287,17 @@ class EditorInstance {
|
|||
switch (message.action) {
|
||||
case 'insertObject': {
|
||||
let { type, data, pos } = message;
|
||||
let list = [];
|
||||
let html = '';
|
||||
if (type === 'zotero/item') {
|
||||
let ids = data.split(',').map(id => parseInt(id));
|
||||
for (let id of ids) {
|
||||
let item = await Zotero.Items.getAsync(id);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.isRegularItem()) {
|
||||
let citation = {
|
||||
citationItems: [{
|
||||
uris: [Zotero.URI.getItemURI(item)],
|
||||
itemData: Zotero.Cite.System.prototype.retrieveItem(item)
|
||||
}],
|
||||
properties: {}
|
||||
};
|
||||
|
||||
let formattedCitation = (await this._getFormattedCitationParts(citation)).join(';');
|
||||
|
||||
list.push({ citation, formattedCitation });
|
||||
}
|
||||
else if (item.isNote()) {
|
||||
let note = await this._digestExternalNote(item.id);
|
||||
list.push({ note });
|
||||
}
|
||||
}
|
||||
html = await this._digestItems(ids);
|
||||
}
|
||||
else if (type === 'zotero/annotation') {
|
||||
let annotations = JSON.parse(data);
|
||||
list = await this._annotationsToInsertionList(annotations);
|
||||
html = await this._digestAnnotations(annotations);
|
||||
}
|
||||
if (list.length) {
|
||||
this._postMessage({ action: 'insertAnnotationsAndCitations', list, pos });
|
||||
if (html) {
|
||||
this._postMessage({ action: 'insertHtml', pos, html });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -329,9 +368,9 @@ class EditorInstance {
|
|||
}
|
||||
case 'generateCitation': {
|
||||
let { citation, pos } = message;
|
||||
let formattedCitation = (await this._getFormattedCitationParts(citation)).join(';');
|
||||
let list = [{ citation, formattedCitation }];
|
||||
this._postMessage({ action: 'insertAnnotationsAndCitations', list, pos });
|
||||
let formatted = (await this._getFormattedCitationParts(citation)).join(';');
|
||||
let html = `<span class="citation" data-citation="${encodeURIComponent(JSON.stringify(citation))}">(${formatted})</span>`;
|
||||
this._postMessage({ action: 'insertHtml', pos, html });
|
||||
return;
|
||||
}
|
||||
case 'subscribeProvider': {
|
||||
|
@ -674,7 +713,7 @@ class EditorInstance {
|
|||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
|
||||
return new this._iframeWindow.Blob([u8arr], { type: mime });
|
||||
return new (Zotero.getMainWindow()).Blob([u8arr], { type: mime });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -835,6 +874,37 @@ class EditorInstance {
|
|||
wrappedJSObject: io
|
||||
});
|
||||
}
|
||||
|
||||
static canCreateNoteFromAnnotations(item) {
|
||||
return item.parentID && item.isAttachment() && item.attachmentContentType === 'application/pdf';
|
||||
}
|
||||
|
||||
static async createNoteFromAnnotations(attachmentItem) {
|
||||
if (!this.canCreateNoteFromAnnotations(attachmentItem)) {
|
||||
return;
|
||||
}
|
||||
let annotations = attachmentItem.getAnnotations();
|
||||
if (!annotations.length) {
|
||||
return;
|
||||
}
|
||||
let note = new Zotero.Item('note');
|
||||
note.libraryID = attachmentItem.libraryID;
|
||||
note.parentID = attachmentItem.parentID;
|
||||
await note.saveTx();
|
||||
let editorInstance = new EditorInstance();
|
||||
editorInstance._item = note;
|
||||
let jsonAnnotations = [];
|
||||
for (let annotation of annotations) {
|
||||
annotation._loaded.childItems = true;
|
||||
let jsonAnnotation = await Zotero.Annotations.toJSON(annotation);
|
||||
jsonAnnotation.itemId = attachmentItem.id;
|
||||
jsonAnnotations.push(jsonAnnotation);
|
||||
}
|
||||
let html = `<p>(${(new Date()).toLocaleString()})</p>\n`;
|
||||
html += await editorInstance._digestAnnotations(jsonAnnotations);
|
||||
note.setNote(html);
|
||||
await note.saveTx();
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.EditorInstance = EditorInstance;
|
||||
|
|
|
@ -2745,7 +2745,8 @@ var ZoteroPane = new function()
|
|||
'createParent',
|
||||
'renameAttachments',
|
||||
'reindexItem',
|
||||
'importAnnotations'
|
||||
'importAnnotations',
|
||||
'createNoteFromAnnotations'
|
||||
];
|
||||
|
||||
var m = {};
|
||||
|
@ -2973,6 +2974,10 @@ var ZoteroPane = new function()
|
|||
if (Zotero.PDFWorker.canImport(item)) {
|
||||
show.push(m.importAnnotations);
|
||||
}
|
||||
|
||||
if (Zotero.EditorInstance.canCreateNoteFromAnnotations(item)) {
|
||||
show.push(m.createNoteFromAnnotations);
|
||||
}
|
||||
}
|
||||
|
||||
// Update attachment submenu
|
||||
|
@ -4666,7 +4671,16 @@ var ZoteroPane = new function()
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.createNoteFromSelected = function () {
|
||||
if (!this.canEdit()) {
|
||||
this.displayCannotEditLibraryMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
let item = this.getSelectedItems()[0];
|
||||
Zotero.EditorInstance.createNoteFromAnnotations(item);
|
||||
};
|
||||
|
||||
this.createEmptyParent = async function (item) {
|
||||
await Zotero.DB.executeTransaction(async function () {
|
||||
|
|
|
@ -312,6 +312,7 @@
|
|||
<menuitem class="menuitem-iconic zotero-menuitem-import-annotations" label="Import annotations" oncommand="ZoteroPane.importAnnotationsForSelected()"/>
|
||||
<!-- <menuitem class="menuitem-iconic zotero-menuitem-export-annotations" label="&zotero.items.menu.exportAnnotations;" oncommand="ZoteroPane.exportAnnotationsForSelected()"/>-->
|
||||
<menuitem class="menuitem-iconic zotero-menuitem-export-annotations" label="Export Annotations" oncommand="ZoteroPane.exportAnnotationsForSelected()"/>
|
||||
<menuitem class="menuitem-iconic zotero-menuitem-create-note-from-annotations" label="&zotero.items.menu.createNoteFromAnnotations;" oncommand="ZoteroPane.createNoteFromSelected()"/>
|
||||
</menupopup>
|
||||
|
||||
<tooltip id="fake-tooltip"/>
|
||||
|
|
|
@ -101,6 +101,7 @@
|
|||
<!ENTITY zotero.items.menu.unrecognize "Undo Retrieve Metadata">
|
||||
<!ENTITY zotero.items.menu.reportMetadata "Report Incorrect Metadata">
|
||||
<!ENTITY zotero.items.menu.importAnnotations "Import Annotations">
|
||||
<!ENTITY zotero.items.menu.createNoteFromAnnotations "Create Note from Annotations">
|
||||
|
||||
<!ENTITY zotero.duplicatesMerge.versionSelect "Choose the version of the item to use as the master item:">
|
||||
<!ENTITY zotero.duplicatesMerge.fieldSelect "Select fields to keep from other versions of the item:">
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit db2bfc6cbb724dbb1a22bafc324357a1bd61c3b1
|
||||
Subproject commit 73808236baa117a0e712e21f3c0ac06af1a553f0
|
Loading…
Reference in a new issue