Improve the new UI:

- Revert `notesList` to standalone notes mode only
- Fix splitter styling on Windows
- Fix `contextPane` performance fixes
- Add a customizable throttle function to `Zotero.Utilities`
- Fix reader tab selection issues
- Improve error handling in the new editor
- Update pdf-reader and zotero-note-editor submodules
- Fix contextPane child notes list
- Properly notify editor about new images
- Fix note title and body snippet extraction
- Persist right-hand pane state when switching tab
- Hopefully fix two panes visible at the same time
This commit is contained in:
Martynas Bagdonas 2020-12-22 15:25:13 +02:00 committed by Dan Stillman
parent c6a196b999
commit 51f760fe1a
12 changed files with 211 additions and 141 deletions

View file

@ -61,7 +61,9 @@
#zotero-collections-splitter:not([state=collapsed]), #zotero-collections-splitter:not([state=collapsed]),
#zotero-items-splitter:not([state=collapsed]), #zotero-items-splitter:not([state=collapsed]),
#zotero-tags-splitter:not([state=collapsed]) { #zotero-tags-splitter:not([state=collapsed]),
#zotero-context-splitter:not([state=collapsed]),
#zotero-context-splitter-stacked:not([state=collapsed]) {
border: 0; border: 0;
background-color: transparent; background-color: transparent;
position: relative; position: relative;
@ -70,9 +72,15 @@
z-index: 1; z-index: 1;
} }
#zotero-context-splitter:not([state=collapsed]),
#zotero-context-splitter-stacked:not([state=collapsed]) {
z-index: 0;
}
#zotero-collections-splitter:not([state=collapsed]), #zotero-collections-splitter:not([state=collapsed]),
#zotero-items-splitter:not([state=collapsed]):not([orient=vertical]), #zotero-items-splitter:not([state=collapsed]):not([orient=vertical]),
#zotero-tags-splitter:not([state=collapsed]) { #zotero-tags-splitter:not([state=collapsed]),
#zotero-context-splitter:not([state=collapsed]) {
border-inline-end: 1px solid var(--theme-border-color); border-inline-end: 1px solid var(--theme-border-color);
min-width: 0; min-width: 0;
width: 3px; width: 3px;
@ -80,7 +88,8 @@
} }
#zotero-tags-splitter:not([state=collapsed]), #zotero-tags-splitter:not([state=collapsed]),
#zotero-items-splitter:not([state=collapsed])[orient=vertical] { #zotero-items-splitter:not([state=collapsed])[orient=vertical],
#zotero-context-splitter-stacked:not([state=collapsed]) {
border-block-end: 1px solid var(--theme-border-color); border-block-end: 1px solid var(--theme-border-color);
min-height: 0; min-height: 0;
height: 3px; height: 3px;
@ -89,13 +98,16 @@
#zotero-collections-splitter > grippy, #zotero-collections-splitter > grippy,
#zotero-items-splitter > grippy, #zotero-items-splitter > grippy,
#zotero-tags-splitter > grippy { #zotero-tags-splitter > grippy,
#zotero-context-splitter > grippy {
border: 0; border: 0;
} }
#zotero-collections-splitter:not([state=collapsed]) > grippy, #zotero-collections-splitter:not([state=collapsed]) > grippy,
#zotero-items-splitter:not([state=collapsed]) > grippy, #zotero-items-splitter:not([state=collapsed]) > grippy,
#zotero-tags-splitter:not([state=collapsed]) > grippy { #zotero-tags-splitter:not([state=collapsed]) > grippy,
#zotero-context-splitter:not([state=collapsed]) > grippy,
#zotero-context-splitter-stacked:not([state=collapsed]) > grippy {
display: none; display: none;
} }

View file

@ -109,18 +109,8 @@
} }
this.notify = async (event, type, ids, extraData) => { this.notify = async (event, type, ids, extraData) => {
// Update citations
let uris = [];
let items = await Zotero.Items.getAsync(ids);
for (let item of items) {
let uri = Zotero.URI.getItemURI(item);
if (uri) {
uris.push(uri)
}
}
if (this._editorInstance) { if (this._editorInstance) {
await this._editorInstance.updateCitationsForURIs(uris); await this._editorInstance.notify(ids);
} }
if (!this.item) return; if (!this.item) return;
@ -252,15 +242,14 @@
<property name="linksOnTop"> <property name="linksOnTop">
<setter> <setter>
<![CDATA[ <![CDATA[
return; // if (val) {
if (val) { // var container = this._id('links-container');
var container = this._id('links-container'); // var parent = container.parentNode;
var parent = container.parentNode; // var sib = container.nextSibling;
var sib = container.nextSibling; // while (parent.firstChild !== container) {
while (parent.firstChild !== container) { // parent.insertBefore(parent.removeChild(parent.firstChild), sib);
parent.insertBefore(parent.removeChild(parent.firstChild), sib); // }
} // }
}
]]> ]]>
</setter> </setter>
</property> </property>

View file

@ -25,18 +25,14 @@
import React, { forwardRef, useImperativeHandle, useState } from 'react'; import React, { forwardRef, useImperativeHandle, useState } from 'react';
const NoteRow = ({ parentTitle, parentImageSrc, title, body, date, onClick }) => { const NoteRow = ({ title, body, date, onClick }) => {
return ( return (
<div className="note-row" onClick={onClick}> <div className="note-row" onClick={onClick}>
<div className="inner"> <div className="inner">
{parentTitle !== null && <div className="first-line"> <div className="first-line">
<div className="icon"><img src={parentImageSrc}/></div>
<div className="title">{parentTitle}</div>
</div>}
<div className="second-line">
<div className="title">{title}</div> <div className="title">{title}</div>
</div> </div>
<div className="third-line"> <div className="second-line">
<div className="date">{date}</div> <div className="date">{date}</div>
<div className="body">{body}</div> <div className="body">{body}</div>
</div> </div>

View file

@ -82,43 +82,66 @@ var ZoteroContextPane = new function () {
_init(); _init();
this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'tab'], 'contextPane'); this._notifierID = Zotero.Notifier.registerObserver(this, ['item', 'tab'], 'contextPane');
window.addEventListener('resize', _update); window.addEventListener('resize', _update);
_itemToggle.addEventListener('click', () => { _itemToggle.addEventListener('click', _toggleItemButton);
_togglePane(0); _notesToggle.addEventListener('click', _toggleNotesButton);
});
_notesToggle.addEventListener('click', () => {
_togglePane(1);
});
Zotero.Reader.onChangeSidebarWidth = _updatePaneWidth; Zotero.Reader.onChangeSidebarWidth = _updatePaneWidth;
Zotero.Reader.onChangeSidebarOpen = _updatePaneWidth; Zotero.Reader.onChangeSidebarOpen = _updatePaneWidth;
const observer = new MutationObserver(() => { this._mutationObserver = new MutationObserver(() => {
_updateToolbarWidth(); _updateToolbarWidth();
// Sometimes XUL is late to reflow // Sometimes XUL is late to reflow
setTimeout(_updateToolbarWidth, 100); setTimeout(_updateToolbarWidth, 100);
}); });
observer.observe(_tabToolbarContainer, { attributes: true, childList: true, subtree: true }); this._mutationObserver.observe(_tabToolbarContainer, { attributes: true, childList: true, subtree: true });
}; };
this.onUnload = function () { this.onUnload = function () {
Zotero.Notifier.unregisterObserver(this._unregisterID); _itemToggle.removeEventListener('click', _toggleItemButton);
_notesToggle.removeEventListener('click', _toggleNotesButton);
window.removeEventListener('resize', _update);
Zotero.Notifier.unregisterObserver(this._notifierID);
this._mutationObserver.disconnect();
Zotero.Reader.onChangeSidebarWidth = () => {};
Zotero.Reader.onChangeSidebarOpen = () => {};
_contextPaneInner.innerHTML = '';
_itemContexts = [];
_notesContexts = [];
}; };
this.notify = Zotero.Promise.coroutine(function* (action, type, ids, extraData) { this.notify = Zotero.Promise.coroutine(function* (action, type, ids, extraData) {
if (type == 'item') { if (type == 'item') {
// TODO: Filter library and seriously thing about the performance of this part // Update, remove or re-create item panes
for (let context of _itemContexts) { for (let context of _itemContexts.slice()) {
let item = Zotero.Items.get(context.itemID); let item = Zotero.Items.get(context.itemID);
if (item && item.parentID != context.parentID) { if (!item) {
_removeItemContext(context.tabID);
}
else if (item.parentID != context.parentID) {
_removeItemContext(context.tabID); _removeItemContext(context.tabID);
_addItemContext(context.tabID, context.itemID); _addItemContext(context.tabID, context.itemID);
} }
context.update(); else {
context.update();
}
} }
// Update notes lists for affected libraries
let libraryIDs = [];
for (let id of ids) {
let item = Zotero.Items.get(id);
if (item && item.isNote()) {
libraryIDs.push(item.libraryID);
}
else if (action == 'delete') {
libraryIDs.push(extraData[id].libraryID);
}
}
for (let context of _notesContexts) { for (let context of _notesContexts) {
context.updateNotesList(); if (libraryIDs.includes(context.libraryID)) {
context.update();
}
} }
} }
else if (type == 'tab') { else if (type == 'tab') {
@ -157,11 +180,19 @@ var ZoteroContextPane = new function () {
if (context) { if (context) {
_selectNotesContext(context.libraryID); _selectNotesContext(context.libraryID);
} }
_selectItemContext(ids[0], extraData[ids[0]].type); _selectItemContext(ids[0]);
_update(); _update();
} }
} }
}); });
function _toggleItemButton() {
_togglePane(0);
}
function _toggleNotesButton() {
_togglePane(1);
}
function _removeNote(id) { function _removeNote(id) {
var ps = Components.classes['@mozilla.org/embedcomp/prompt-service;1'] var ps = Components.classes['@mozilla.org/embedcomp/prompt-service;1']
@ -183,11 +214,13 @@ var ZoteroContextPane = new function () {
if (splitter.getAttribute('state') != 'collapsed') { if (splitter.getAttribute('state') != 'collapsed') {
if (_panesDeck.selectedIndex == 0) { if (_panesDeck.selectedIndex == 0) {
let child = _itemPaneDeck.selectedPanel; let child = _itemPaneDeck.selectedPanel;
var tabPanels = child.querySelector('tabpanels'); if (child) {
if (tabPanels && tabPanels.selectedIndex == 1) { var tabPanels = child.querySelector('tabpanels');
var notesDeck = child.querySelector('.notes-deck'); if (tabPanels && tabPanels.selectedIndex == 1) {
if (notesDeck.selectedIndex == 1) { var notesDeck = child.querySelector('.notes-deck');
return child.querySelector('zoteronoteeditor'); if (notesDeck.selectedIndex == 1) {
return child.querySelector('zoteronoteeditor');
}
} }
} }
} }
@ -286,29 +319,18 @@ var ZoteroContextPane = new function () {
} }
function _togglePane(paneIndex) { function _togglePane(paneIndex) {
var splitter; var splitter = Zotero.Prefs.get('layout') == 'stacked'
var stacked = Zotero.Prefs.get('layout') == 'stacked'; ? _contextPaneSplitterStacked : _contextPaneSplitter;
if (stacked) {
splitter = _contextPaneSplitterStacked;
}
else {
splitter = _contextPaneSplitter;
}
var isOpen = splitter.getAttribute('state') != 'collapsed'; var isOpen = splitter.getAttribute('state') != 'collapsed';
var hide = false; var hide = false;
var currentPane = _panesDeck.selectedIndex; var currentPane = _panesDeck.selectedIndex;
if (isOpen && currentPane == paneIndex) { if (isOpen && currentPane == paneIndex) {
hide = true; hide = true;
} }
else { else {
_panesDeck.setAttribute('selectedIndex', paneIndex); _panesDeck.setAttribute('selectedIndex', paneIndex);
} }
var context = _itemContexts.find(x => x.tabID == Zotero_Tabs.selectedID);
context.selectedIndex = paneIndex;
splitter.setAttribute('state', hide ? 'collapsed' : 'open'); splitter.setAttribute('state', hide ? 'collapsed' : 'open');
_update(); _update();
@ -371,7 +393,7 @@ var ZoteroContextPane = new function () {
var label = document.createElement('label'); var label = document.createElement('label');
var button = document.createElement('button'); var button = document.createElement('button');
button.setAttribute('label', Zotero.Intl.strings['zotero.item.add']); button.setAttribute('label', Zotero.Intl.strings['zotero.toolbar.newNote']);
button.addEventListener('click', () => { button.addEventListener('click', () => {
contextNode.setAttribute('selectedIndex', 1); contextNode.setAttribute('selectedIndex', 1);
var item = new Zotero.Item('note'); var item = new Zotero.Item('note');
@ -394,7 +416,7 @@ var ZoteroContextPane = new function () {
input.setAttribute('type', 'search'); input.setAttribute('type', 'search');
input.setAttribute('timeout', '250'); input.setAttribute('timeout', '250');
input.addEventListener('command', () => { input.addEventListener('command', () => {
updateNotesList(); _updateNotesList();
}); });
vbox2.append(input); vbox2.append(input);
@ -411,20 +433,20 @@ var ZoteroContextPane = new function () {
var notesListRef = React.createRef(); var notesListRef = React.createRef();
var updateNotesList = async (reset) => { async function _updateNotesList(reset) {
if (reset) { if (reset) {
input.value = ''; input.value = '';
contextNode.setAttribute('selectedIndex', 0); contextNode.setAttribute('selectedIndex', 0);
} }
var text = input.value; var query = input.value;
await Zotero.Schema.schemaUpdatePromise; await Zotero.Schema.schemaUpdatePromise;
var s = new Zotero.Search(); var s = new Zotero.Search();
s.addCondition('libraryID', 'is', libraryID); s.addCondition('libraryID', 'is', libraryID);
s.addCondition('itemType', 'is', 'note'); s.addCondition('itemType', 'is', 'note');
s.addCondition('noChildren', 'false'); s.addCondition('noChildren', 'true');
if (text) { if (query) {
s.addCondition('note', 'contains', text, true); s.addCondition('note', 'contains', query, true);
} }
var notes = await s.search(); var notes = await s.search();
notes = Zotero.Items.get(notes); notes = Zotero.Items.get(notes);
@ -435,20 +457,15 @@ var ZoteroContextPane = new function () {
}); });
notesListRef.current.setNotes(notes.map(note => { notesListRef.current.setNotes(notes.map(note => {
var text2 = note.note.slice(0, 500); var text = note.note;
text2 = text2.trim(); text = Zotero.Utilities.unescapeHTML(text);
// TODO: Fix a potential performance issuse text = text.trim();
text2 = Zotero.Utilities.unescapeHTML(text2); text = text.slice(0, 500);
var parts = text2.split('\n').map(x => x.trim()).filter(x => x.length); var parts = text.split('\n').map(x => x.trim()).filter(x => x.length);
var parent = null; var title = parts[0] && parts[0].slice(0, Zotero.Notes.MAX_TITLE_LENGTH);
if (note.parentID) {
parent = Zotero.Items.get(note.parentID);
}
return { return {
id: note.id, id: note.id,
parentTitle: parent && parent.getDisplayTitle(), title: title || Zotero.getString('pane.item.notes.untitled'),
parentImageSrc: parent && parent.getImageSrc(),
title: parts[0] || Zotero.getString('pane.item.notes.untitled'),
body: parts[1] || '', body: parts[1] || '',
date: (new Date(note.dateModified).toLocaleDateString(Zotero.locale)) date: (new Date(note.dateModified).toLocaleDateString(Zotero.locale))
}; };
@ -468,14 +485,14 @@ var ZoteroContextPane = new function () {
/>, />,
listInner, listInner,
() => { () => {
updateNotesList(); _updateNotesList();
} }
); );
var context = { var context = {
libraryID, libraryID,
node: contextNode, node: contextNode,
updateNotesList, update: Zotero.Utilities.throttle(_updateNotesList, 1000, { leading: false }),
editor editor
}; };
@ -503,7 +520,7 @@ var ZoteroContextPane = new function () {
_notesContexts = _notesContexts.filter(x => x.libraryID != libraryID); _notesContexts = _notesContexts.filter(x => x.libraryID != libraryID);
} }
function _libraryEditable(libraryID) { function _isLibraryEditable(libraryID) {
var type = Zotero.Libraries.get(libraryID).libraryType; var type = Zotero.Libraries.get(libraryID).libraryType;
if (type == 'group') { if (type == 'group') {
var groupID = Zotero.Groups.getGroupIDFromLibraryID(libraryID); var groupID = Zotero.Groups.getGroupIDFromLibraryID(libraryID);
@ -514,7 +531,7 @@ var ZoteroContextPane = new function () {
} }
function _setPinnedNote(libraryID, itemID) { function _setPinnedNote(libraryID, itemID) {
var editable = _libraryEditable(libraryID); var editable = _isLibraryEditable(libraryID);
var context = _getNotesContext(libraryID); var context = _getNotesContext(libraryID);
if (context) { if (context) {
let { editor, node } = context; let { editor, node } = context;
@ -530,7 +547,7 @@ var ZoteroContextPane = new function () {
function _appendNoteRows(notes, list, editable, onClick, onDelete) { function _appendNoteRows(notes, list, editable, onClick, onDelete) {
for (var i = 0; i < notes.length; i++) { for (var i = 0; i < notes.length; i++) {
var note = notes[i]; var note = notes[i];
var id = notes[i].id; let id = notes[i].id;
var icon = document.createElement('image'); var icon = document.createElement('image');
icon.className = 'zotero-box-icon'; icon.className = 'zotero-box-icon';
@ -551,7 +568,9 @@ var ZoteroContextPane = new function () {
}); });
box.appendChild(icon); box.appendChild(icon);
box.appendChild(label); box.appendChild(label);
var row = document.createElement('row');
row.appendChild(box);
if (editable) { if (editable) {
var removeButton = document.createElement('label'); var removeButton = document.createElement('label');
removeButton.setAttribute('value', '-'); removeButton.setAttribute('value', '-');
@ -559,11 +578,6 @@ var ZoteroContextPane = new function () {
removeButton.addEventListener('click', function () { removeButton.addEventListener('click', function () {
onDelete(id); onDelete(id);
}); });
}
var row = document.createElement('row');
row.appendChild(box);
if (editable) {
row.appendChild(removeButton); row.appendChild(removeButton);
} }
@ -576,34 +590,32 @@ var ZoteroContextPane = new function () {
_itemContexts = _itemContexts.filter(x => x.tabID != tabID); _itemContexts = _itemContexts.filter(x => x.tabID != tabID);
} }
function _selectItemContext(tabID, type) { function _selectItemContext(tabID) {
let selectedIndex = Array.from(_itemPaneDeck.children).findIndex(x => x.id == tabID + '-context'); let selectedIndex = Array.from(_itemPaneDeck.children).findIndex(x => x.id == tabID + '-context');
if (selectedIndex != -1) { if (selectedIndex != -1) {
_itemPaneDeck.setAttribute('selectedIndex', selectedIndex); _itemPaneDeck.setAttribute('selectedIndex', selectedIndex);
var context = _itemContexts.find(x => x.tabID == tabID);
if (context && Zotero_Tabs.selectedIndex > 0) {
_panesDeck.setAttribute('selectedIndex', context.selectedIndex);
}
} }
} }
function _addItemContext(tabID, itemID) { function _addItemContext(tabID, itemID) {
var item = Zotero.Items.get(itemID);
if (!item) {
return;
}
var libraryID = item.libraryID;
var editable = _isLibraryEditable(libraryID);
var parentID = item.parentID;
var container = document.createElement('vbox'); var container = document.createElement('vbox');
container.id = tabID + '-context'; container.id = tabID + '-context';
container.className = 'zotero-item-pane-content'; container.className = 'zotero-item-pane-content';
_itemPaneDeck.appendChild(container); _itemPaneDeck.appendChild(container);
var item = Zotero.Items.get(itemID);
var libraryID = item.libraryID;
var editable = _libraryEditable(libraryID);
var parentID = item.parentID;
var context = { var context = {
tabID, tabID,
itemID, itemID,
parentID, parentID,
libraryID, libraryID,
selectedIndex: 0,
update: () => {} update: () => {}
}; };
_itemContexts.push(context); _itemContexts.push(context);
@ -619,7 +631,6 @@ var ZoteroContextPane = new function () {
container.append(vbox); container.append(vbox);
return; return;
} }
var parentItem = Zotero.Items.get(item.parentID); var parentItem = Zotero.Items.get(item.parentID);
// tabbox // tabbox
@ -685,7 +696,6 @@ var ZoteroContextPane = new function () {
hbox.setAttribute('align', 'center'); hbox.setAttribute('align', 'center');
var label = document.createElement('label'); var label = document.createElement('label');
var button = document.createElement('button'); var button = document.createElement('button');
// TODO: Should not depend on the current ZoteroPane state
button.hidden = !editable; button.hidden = !editable;
button.setAttribute('label', Zotero.Intl.strings['zotero.item.add']); button.setAttribute('label', Zotero.Intl.strings['zotero.item.add']);
button.addEventListener('click', () => { button.addEventListener('click', () => {
@ -763,6 +773,10 @@ var ZoteroContextPane = new function () {
function _renderNotesPanel() { function _renderNotesPanel() {
rows.innerHTML = ''; rows.innerHTML = '';
var parentItem = Zotero.Items.get(parentID);
if (!parentItem) {
return;
}
var parentNotes = Zotero.Items.get(parentItem.getNotes()); var parentNotes = Zotero.Items.get(parentItem.getNotes());
_appendNoteRows(parentNotes, rows, editable, (id) => { _appendNoteRows(parentNotes, rows, editable, (id) => {
deck.setAttribute('selectedIndex', 1); deck.setAttribute('selectedIndex', 1);
@ -782,7 +796,7 @@ var ZoteroContextPane = new function () {
label.value = Zotero.getString(str, [c]); label.value = Zotero.getString(str, [c]);
} }
context.update = _renderNotesPanel; context.update = Zotero.Utilities.throttle(_renderNotesPanel, 500);
_renderNotesPanel(); _renderNotesPanel();
} }
}; };

View file

@ -113,7 +113,7 @@ var Zotero_Tabs = new function () {
index = index || this._tabs.length; index = index || this._tabs.length;
this._tabs.splice(index, 0, tab); this._tabs.splice(index, 0, tab);
this._update(); this._update();
Zotero.Notifier.trigger('add', 'tab', [id], { [id]: notifierData }); Zotero.Notifier.trigger('add', 'tab', [id], { [id]: notifierData }, true);
if (select) { if (select) {
this.select(id); this.select(id);
} }
@ -157,7 +157,7 @@ var Zotero_Tabs = new function () {
if (tab.onClose) { if (tab.onClose) {
tab.onClose(); tab.onClose();
} }
Zotero.Notifier.trigger('close', 'tab', [tab.id]); Zotero.Notifier.trigger('close', 'tab', [tab.id], true);
this._update(); this._update();
}; };
@ -199,7 +199,7 @@ var Zotero_Tabs = new function () {
this._selectedID = id; this._selectedID = id;
this.deck.selectedIndex = Array.from(this.deck.children).findIndex(x => x.id == id); this.deck.selectedIndex = Array.from(this.deck.children).findIndex(x => x.id == id);
this._update(); this._update();
Zotero.Notifier.trigger('select', 'tab', [tab.id], { [tab.id]: { type: tab.type } }); Zotero.Notifier.trigger('select', 'tab', [tab.id], { [tab.id]: { type: tab.type } }, true);
}; };
/** /**

View file

@ -101,11 +101,28 @@ class EditorInstance {
async updateCitationsForURIs(uris) { async updateCitationsForURIs(uris) {
let subscriptions = this._subscriptions let subscriptions = this._subscriptions
.filter(s => s.data.citation && s.data.citation.citationItems .filter(s => s.data.citation && s.data.citation.citationItems
.some(citationItem => uris.some(uri => citationItem.uris.includes(uri)))); .some(citationItem => citationItem.uris && uris.some(uri => citationItem.uris.includes(uri))));
for (let subscription of subscriptions) { for (let subscription of subscriptions) {
await this._feedSubscription(subscription); await this._feedSubscription(subscription);
} }
} }
async notify(ids) {
let items = await Zotero.Items.getAsync(ids);
// Update attachments
let keys = items.map(item => item.key);
this._subscriptions
.filter(s => keys.includes(s.data.attachmentKey))
.forEach(s => this._feedSubscription(s));
// Update citations
let uris = items.map(x => Zotero.URI.getItemURI(x)).filter(x => x);
this._subscriptions
.filter(s => s.data.citation && s.data.citation.citationItems
.some(citationItem => citationItem.uris && uris.some(uri => citationItem.uris.includes(uri))))
.forEach(s => this._feedSubscription(s));
}
saveSync() { saveSync() {
if (!this._readOnly && !this._disableSaving && this._iframeWindow) { if (!this._readOnly && !this._disableSaving && this._iframeWindow) {
@ -600,6 +617,9 @@ class EditorInstance {
async _getFormattedCitationParts(citation) { async _getFormattedCitationParts(citation) {
let formattedItems = []; let formattedItems = [];
for (let citationItem of citation.citationItems) { for (let citationItem of citation.citationItems) {
if (!Array.isArray(citationItem.uris)) {
continue;
}
let item = await this._getItemFromURIs(citationItem.uris); let item = await this._getItemFromURIs(citationItem.uris);
if (!item && citationItem.itemData) { if (!item && citationItem.itemData) {
item = new Zotero.Item(); item = new Zotero.Item();

View file

@ -755,6 +755,10 @@ class Reader {
} }
if (reader) { if (reader) {
if (reader instanceof ReaderTab) {
reader._window.Zotero_Tabs.select(reader.tabID);
}
if (location) { if (location) {
reader.navigate(location); reader.navigate(location);
} }
@ -806,9 +810,6 @@ class Reader {
if (reader instanceof ReaderWindow) { if (reader instanceof ReaderWindow) {
reader._window.focus(); reader._window.focus();
} }
else {
reader._window.Zotero_Tabs.select(reader.tabID);
}
} }
async triggerAnnotationsImportCheck(itemID) { async triggerAnnotationsImportCheck(itemID) {

View file

@ -49,6 +49,62 @@ Zotero.Utilities = {
}; };
}, },
/**
* Creates and returns a new, throttled version of the
* passed function, that, when invoked repeatedly,
* will only actually call the original function at most
* once per every wait milliseconds
*
* By default, throttle will execute the function as soon
* as you call it for the first time, and, if you call it
* again any number of times during the wait period, as soon
* as that period is over. If you'd like to disable the
* leading-edge call, pass {leading: false}, and if you'd
* like to disable the execution on the trailing-edge,
* pass {trailing: false}. See
* https://underscorejs.org/#throttle
* https://github.com/jashkenas/underscore/blob/master/underscore.js
* (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Underscore may be freely distributed under the MIT license.
*
* @param {Function} func Function to throttle
* @param {Integer} wait Wait period in milliseconds
* @param {Boolean} [options.leading] Call at the beginning of the wait period
* @param {Boolean} [options.trailing] Call at the end of the wait period
*/
throttle: function (func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function () {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function () {
var now = Date.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
}
else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
},
/** /**
* Fixes author name capitalization. * Fixes author name capitalization.
* Currently for all uppercase names only * Currently for all uppercase names only

View file

@ -3,7 +3,7 @@
} }
#zotero-context-pane { #zotero-context-pane {
min-width: 300px; min-width: 360px;
} }
#zotero-context-pane.stacked { #zotero-context-pane.stacked {
@ -49,7 +49,7 @@
right: 0; right: 0;
height: 32px; height: 32px;
-moz-appearance: none; -moz-appearance: none;
background: linear-gradient(to top, #aeaeae 0, #aeaeae 1px, #d3d3d3 1px, #e3e3e3 100%); background: linear-gradient(to top, #a9a9a9 0, #a9a9a9 1px, #f6f6f6 1px, #f6f6f6 100%);
} }
#zotero-tab-toolbar-container #zotero-tb-locate, #zotero-tab-toolbar-container #zotero-tb-locate,
@ -63,7 +63,7 @@
opacity: 0.99; opacity: 0.99;
width: 100%; width: 100%;
margin-inline-start: -5px; margin-inline-start: -5px;
background: linear-gradient(to top, #aeaeae 0, #aeaeae 1px, #d3d3d3 1px, #e3e3e3 100%); background: linear-gradient(to top, #a9a9a9 0, #a9a9a9 1px, #f6f6f6 1px, #f6f6f6 100%);
} }
.zotero-context-notes-list { .zotero-context-notes-list {

@ -1 +1 @@
Subproject commit be1fa9a77fb9090a1aad9a9cd40b8ebb22ee4281 Subproject commit 7595b7d83720941812a531b9a547deac626b24bc

View file

@ -31,24 +31,6 @@
.first-line { .first-line {
display: flex; display: flex;
.icon {
display: flex;
align-items: center;
}
.title {
flex-grow: 1;
width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-left: 8px;
}
}
.second-line {
display: flex;
.title { .title {
flex-grow: 1; flex-grow: 1;
width: 0; width: 0;
@ -59,7 +41,7 @@
} }
} }
.third-line { .second-line {
display: flex; display: flex;
.date { .date {

@ -1 +1 @@
Subproject commit a271598218fa8f52cd6d571d5c1b28e913803409 Subproject commit db2bfc6cbb724dbb1a22bafc324357a1bd61c3b1