Improve the new UI:

- Rollback all redundant changes made in the last few months
- Introduce `contextPane`
- Show child notes in the notes pane
- Fix splitter styling
- Various bug fixes
- Fix contextPane switching and states persistence
- Persist reader sidebar open/close state
- Fix bottom pane placeholder updating concurrency issues
- Fix toolbar placeholder width updating
- Display titles for split button
- Fix toolbar position when switching tabs
- Add PDF tab loading cover
- Improve notes and citations insertion
- Clean up and refactor code
- Fixes and cleanups to PDF reader
This commit is contained in:
Martynas Bagdonas 2020-11-10 10:59:34 +02:00 committed by Dan Stillman
parent ab04468342
commit 45a3e96e68
43 changed files with 2313 additions and 1419 deletions

6
.gitmodules vendored
View file

@ -31,13 +31,13 @@
url = https://github.com/gildas-lormeau/SingleFile.git
[submodule "pdf-reader"]
path = pdf-reader
url = https://github.com/zotero/pdf-reader.git
url = ssh://git@github.com/zotero/pdf-reader.git
branch = master
[submodule "pdf-worker"]
path = pdf-worker
url = https://github.com/zotero/pdf-worker.git
url = ssh://git@github.com/zotero/pdf-worker.git
branch = master
[submodule "zotero-note-editor"]
path = zotero-note-editor
url = https://github.com/zotero/zotero-note-editor.git
url = ssh://git@github.com/zotero/zotero-note-editor.git
branch = master

View file

@ -129,7 +129,7 @@ input {
.zotero-editpane-tabs {
-moz-appearance: none;
/*background: -moz-linear-gradient(top, #ededed, #cccccc);*/
background: -moz-linear-gradient(top, #ededed, #cccccc);
border-style: solid;
border-width: 0 0 1px 0;
border-color: #bdbdbd;
@ -217,7 +217,8 @@ input {
}
#zotero-collections-splitter:not([state=collapsed]),
#zotero-items-splitter:not([state=collapsed])[orient=horizontal]
#zotero-items-splitter:not([state=collapsed])[orient=horizontal],
#zotero-context-splitter:not([state=collapsed])[orient=horizontal]
{
-moz-appearance: none;
border-inline-start: 1px solid #bdbdbd;
@ -228,7 +229,8 @@ input {
background-image: none;
}
#zotero-items-splitter[orient=vertical]
#zotero-items-splitter[orient=vertical],
#zotero-context-splitter-stacked
{
-moz-border-start: none !important;
-moz-border-end: none !important;
@ -239,17 +241,25 @@ input {
}
#zotero-collections-splitter:not([state=collapsed]) > grippy,
#zotero-items-splitter:not([state=collapsed]) > grippy
#zotero-items-splitter:not([state=collapsed]) > grippy,
#zotero-context-splitter:not([state=collapsed]) > grippy,
#zotero-context-splitter-stacked:not([state=collapsed]) > grippy
{
display: none;
}
#zotero-collections-splitter[state=collapsed], #zotero-items-splitter[state=collapsed] {
#zotero-collections-splitter[state=collapsed],
#zotero-items-splitter[state=collapsed],
#zotero-context-splitter[state=collapsed],
#zotero-context-splitter-stacked[state=collapsed]
{
border: 0 solid #d6d6d6 !important;
padding: 0;
}
#zotero-collections-splitter[state=collapsed], #zotero-items-splitter[state=collapsed][orient=horizontal]
#zotero-collections-splitter[state=collapsed],
#zotero-items-splitter[state=collapsed][orient=horizontal],
#zotero-context-splitter[state=collapsed][orient=horizontal]
{
background-image: url("chrome://zotero/skin/mac/vsplitter.png");
background-repeat: repeat-y;
@ -258,7 +268,8 @@ input {
width: 8px !important;
}
#zotero-items-splitter[state=collapsed][orient=vertical]
#zotero-items-splitter[state=collapsed][orient=vertical],
#zotero-context-splitter-stacked[state=collapsed][orient=vertical]
{
background-image: url("chrome://zotero/skin/mac/hsplitter.png");
background-repeat: repeat-x;
@ -271,11 +282,17 @@ input {
border-right-width: 1px !important;
}
#zotero-items-splitter[state=collapsed] {
#zotero-items-splitter[state=collapsed],
#zotero-context-splitter[state=collapsed],
#zotero-context-splitter-stacked[state=collapsed]
{
border-left-width: 1px !important;
}
#zotero-collections-splitter[state=collapsed] > grippy, #zotero-items-splitter[state=collapsed] > grippy
#zotero-collections-splitter[state=collapsed] > grippy,
#zotero-items-splitter[state=collapsed] > grippy,
#zotero-context-splitter[state=collapsed] > grippy,
#zotero-context-splitter-stacked[state=collapsed] > grippy
{
-moz-appearance: none;
background: url(chrome://zotero/skin/mac/vgrippy.png) center/auto 8px no-repeat;
@ -301,11 +318,20 @@ input {
height: 8px;
}
#zotero-tags-splitter > grippy:hover, #zotero-collections-splitter > grippy:hover, #zotero-items-splitter > grippy:hover
#zotero-tags-splitter > grippy:hover,
#zotero-collections-splitter > grippy:hover,
#zotero-items-splitter > grippy:hover,
#zotero-context-splitter > grippy:hover,
#zotero-context-splitter-stacked > grippy:hover
{
background-color:transparent;
}
#zotero-context-toolbar-extension {
/* To cover #zotero-context-splitter 1px border */
margin-inline-start: -1px;
}
#zotero-items-tree
{
-moz-appearance: none;

View file

@ -76,3 +76,9 @@ tab {
background-color: transparent;
background-image: none;
}
#zotero-context-splitter-stacked {
-moz-appearance: none;
background-color: #ececec;
border-top: 1px solid hsla(0, 0%, 0%, 0.2);
}

View file

@ -42,6 +42,7 @@
<field name="displayTags">false</field>
<field name="displayRelated">false</field>
<field name="displayButton">false</field>
<field name="hideLinksContainer"/>
<field name="buttonCaption"/>
<field name="parentClickHandler"/>
@ -50,7 +51,10 @@
<field name="clickHandler"/>
<field name="navigateHandler"/>
<field name="returnHandler"/>
<constructor><![CDATA[
this._destroyed = false;
this._noteEditorID = Zotero.Utilities.randomString();
this._iframe = document.getAnonymousElementByAttribute(this, 'anonid', 'editor-view');
this._iframe.addEventListener('DOMContentLoaded', (e) => {
@ -99,6 +103,7 @@
popup: document.getAnonymousElementByAttribute(this, 'anonid', 'editor-menu'),
onNavigate: this._navigateHandler,
readOnly: !this.editable,
onReturn: this._returnHandler,
placeholder: this.placeholder
});
}
@ -137,7 +142,7 @@
this._lastHtmlValue = this.item.note;
}
this._id('links-container').hidden = !(this.displayTags && this.displayRelated);
this._id('links-container').hidden = !(this.displayTags && this.displayRelated) || this._hideLinksContainer;
this._id('links-box').refresh();
}
@ -179,12 +184,21 @@
this._mode = val;
document.getAnonymousNodes(this)[0].setAttribute('mode', val);
this._id('links-box').mode = val;
this._id('links-container').hidden = !(this.displayTags && this.displayRelated);
this._id('links-container').hidden = !(this.displayTags && this.displayRelated) || this._hideLinksContainer;
this._id('links-box').refresh();
]]>
</setter>
</property>
<field name="returnHandler"/>
<property name="returnHandler" onget="return this._returnHandler;">
<setter>
<![CDATA[
this._returnHandler = val;
]]>
</setter>
</property>
<field name="_parentItem"/>
<property name="parentItem" onget="return this._parentItem;">
<setter>
@ -262,6 +276,15 @@
</setter>
</property>
<property name="hideLinksContainer">
<setter>
<![CDATA[
this._hideLinksContainer = val;
this._id('links-container').hidden = val;
]]>
</setter>
</property>
<field name="collection"/>
<destructor>
@ -271,6 +294,8 @@
this._editorInstance.uninit();
}
this._destroyed = true;
this._initialized = false;
this._editorInstance = null;
]]>
</destructor>
@ -307,8 +332,8 @@
</implementation>
<content>
<xul:vbox xbl:inherits="flex">
<xul:iframe tooltip="editor-tooltip" anonid="editor-view" flex="1" overflow="auto" style="width: 100%;margin-right: 5px;border: 0"
<xul:vbox xbl:inherits="flex" style="display: flex;flex-direction: column;flex-grow: 1;">
<xul:iframe tooltip="editor-tooltip" anonid="editor-view" flex="1" overflow="auto" style="width: 100%;margin-right: 5px;border: 0;width: 100%;margin-right: 5px;border: 0;flex-grow: 1;"
frameBorder="0" src="resource://zotero/zotero-note-editor/editor.html" type="content"/>
<xul:hbox id="links-container" hidden="true">
<xul:linksbox id="links-box" flex="1" xbl:inherits="notitle"/>
@ -512,7 +537,7 @@
<xul:menupopup id="tagsPopup" ignorekeys="true"
onpopupshown="if (!document.commandDispatcher.focusedElement || document.commandDispatcher.focusedElement.tagName=='xul:label'){ /* DEBUG: it would be nice to make this work -- if (this.firstChild.count==0){ this.firstChild.newTag(); } */ this.setAttribute('showing', 'true'); }"
onpopuphidden="if (!document.commandDispatcher.focusedElement || document.commandDispatcher.focusedElement.tagName=='xul:label'){ this.setAttribute('showing', 'false'); }">
<xul:tagsbox id="tags" flex="1" mode="edit"/>
<xul:tagsbox id="tags" flex="1" mode="edit" style="display: flex"/>
</xul:menupopup>
</xul:popupset>
</xul:vbox>

View file

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

View file

@ -0,0 +1,791 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2020 Corporation for Digital Scholarship
Vienna, Virginia, USA
https://digitalscholar.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
// TODO: Fix import/require related isues that might be
// related with `require` not reusing the context
var React = require('react');
var ReactDOM = require('react-dom');
var TagsBoxContainer = require('containers/tagsBoxContainer').default;
var NotesList = require('components/itemPane/notesList').default;
var ZoteroContextPane = new function () {
const HTML_NS = 'http://www.w3.org/1999/xhtml';
var _tabCover;
var _contextPane;
var _contextPaneInner;
var _contextPaneSplitter;
var _contextPaneSplitterStacked;
var _itemToggle;
var _notesToggle;
var _panesDeck;
var _itemPaneDeck;
var _notesPaneDeck;
var _itemToolbar;
var _splitButton;
var _itemPaneToggle;
var _notesPaneToggle;
var _toolbar;
var _tabToolbarContainer;
var _itemContexts = [];
var _notesContexts = [];
// Using attribute instead of propery to set 'selectedIndex'
// is more reliable
this.update = _update;
this.getActiveEditor = _getActiveEditor;
this.onLoad = function () {
if (!Zotero) {
return;
}
_tabCover = document.getElementById('zotero-tab-cover');
_itemToggle = document.getElementById('zotero-tb-toggle-item-pane');
_notesToggle = document.getElementById('zotero-tb-toggle-notes-pane');
_contextPane = document.getElementById('zotero-context-pane');
_contextPaneInner = document.getElementById('zotero-context-pane-inner');
_contextPaneSplitter = document.getElementById('zotero-context-splitter');
_contextPaneSplitterStacked = document.getElementById('zotero-context-splitter-stacked');
_itemToolbar = document.getElementById('zotero-item-toolbar');
_splitButton = document.getElementById('zotero-tb-split');
_itemPaneToggle = document.getElementById('zotero-tb-toggle-item-pane');
_notesPaneToggle = document.getElementById('zotero-tb-toggle-notes-pane');
_toolbar = document.getElementById('zotero-toolbar');
_tabToolbarContainer = document.getElementById('zotero-tab-toolbar-container');
_init();
this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'tab'], 'contextPane');
window.addEventListener('resize', _update);
_itemToggle.addEventListener('click', () => {
_togglePane(0);
});
_notesToggle.addEventListener('click', () => {
_togglePane(1);
});
Zotero.Reader.onChangeSidebarWidth = _updatePaneWidth;
Zotero.Reader.onChangeSidebarOpen = _updatePaneWidth;
const observer = new MutationObserver(() => {
_updateToolbarWidth();
// Sometimes XUL is late to reflow
setTimeout(_updateToolbarWidth, 100);
});
observer.observe(_tabToolbarContainer, { attributes: true, childList: true, subtree: true });
};
this.onUnload = function () {
Zotero.Notifier.unregisterObserver(this._unregisterID);
};
this.notify = Zotero.Promise.coroutine(function* (action, type, ids, extraData) {
if (type == 'item') {
// TODO: Filter library and seriously thing about the performance of this part
for (let context of _itemContexts) {
let item = Zotero.Items.get(context.itemID);
if (item && item.parentID != context.parentID) {
_removeItemContext(context.tabID);
_addItemContext(context.tabID, context.itemID);
}
context.update();
}
for (let context of _notesContexts) {
context.updateNotesList();
}
}
else if (type == 'tab') {
if (action == 'add') {
_addItemContext(ids[0], extraData[ids[0]].itemID);
}
else if (action == 'close') {
_removeItemContext(ids[0]);
}
else if (action == 'select') {
if (Zotero_Tabs.selectedIndex == 0) {
_contextPaneSplitter.setAttribute('hidden', true);
_contextPane.setAttribute('collapsed', true);
_toolbar.append(_itemToolbar);
_itemToolbar.classList.remove('tab-mode');
_splitButton.classList.add('hidden');
}
else {
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
_tabCover.hidden = false;
(async () => {
await reader._initPromise;
_tabCover.hidden = true;
})();
}
_contextPaneSplitter.setAttribute('hidden', false);
_contextPane.setAttribute('collapsed', !(_contextPaneSplitter.getAttribute('state') != 'collapsed'));
_tabToolbarContainer.append(_itemToolbar);
_itemToolbar.classList.add('tab-mode');
_splitButton.classList.remove('hidden');
}
var context = _itemContexts.find(x => x.tabID == ids[0]);
if (context) {
_selectNotesContext(context.libraryID);
}
_selectItemContext(ids[0], extraData[ids[0]].type);
_update();
}
}
});
function _removeNote(id) {
var ps = Components.classes['@mozilla.org/embedcomp/prompt-service;1']
.getService(Components.interfaces.nsIPromptService);
if (ps.confirm(null, '', Zotero.getString('pane.item.notes.delete.confirm'))) {
Zotero.Items.trashTx(id);
}
}
function _getActiveEditor() {
var splitter;
if (Zotero.Prefs.get('layout') == 'stacked') {
splitter = _contextPaneSplitterStacked;
}
else {
splitter = _contextPaneSplitter;
}
if (splitter.getAttribute('state') != 'collapsed') {
if (_panesDeck.selectedIndex == 0) {
let child = _itemPaneDeck.selectedPanel;
var tabPanels = child.querySelector('tabpanels');
if (tabPanels && tabPanels.selectedIndex == 1) {
var notesDeck = child.querySelector('.notes-deck');
if (notesDeck.selectedIndex == 1) {
return child.querySelector('zoteronoteeditor');
}
}
}
else {
var node = _notesPaneDeck.selectedPanel;
if (node.selectedIndex == 1) {
return node.querySelector('zoteronoteeditor');
}
}
}
}
function _updateAddToNote() {
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
var editor = _getActiveEditor();
reader.enableAddToNote(!!editor);
}
}
function _updatePaneWidth() {
var stacked = Zotero.Prefs.get('layout') == 'stacked';
var width = Zotero.Reader.getSidebarWidth() + 'px';
if (!Zotero.Reader.getSidebarOpen()) {
width = 0;
}
_contextPane.style.left = stacked ? width : 'unset';
}
function _updateToolbarWidth() {
var stacked = Zotero.Prefs.get('layout') == 'stacked';
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
if ((stacked || _contextPaneSplitter.getAttribute('state') == 'collapsed')) {
reader.setToolbarPlaceholderWidth(_tabToolbarContainer.boxObject.width);
}
else {
reader.setToolbarPlaceholderWidth(0);
}
}
}
function _update() {
if (Zotero_Tabs.selectedIndex == 0) {
return;
}
var splitter;
var stacked = Zotero.Prefs.get('layout') == 'stacked';
if (stacked) {
_contextPaneSplitterStacked.setAttribute('hidden', false);
_contextPaneSplitter.setAttribute('state', 'open');
_contextPaneSplitter.setAttribute('hidden', true);
_contextPane.classList.add('stacked');
_contextPane.classList.remove('standard');
splitter = _contextPaneSplitterStacked;
}
else {
_contextPaneSplitter.setAttribute('hidden', false);
_contextPaneSplitterStacked.setAttribute('hidden', true);
_contextPaneSplitterStacked.setAttribute('state', 'open');
_contextPane.classList.add('standard');
_contextPane.classList.remove('stacked');
splitter = _contextPaneSplitter;
}
var collapsed = splitter.getAttribute('state') == 'collapsed';
var selectedIndex = _panesDeck.selectedIndex;
if (!collapsed && selectedIndex == 0) {
_itemPaneToggle.classList.add('toggled');
}
else {
_itemPaneToggle.classList.remove('toggled');
}
if (!collapsed && selectedIndex == 1) {
_notesPaneToggle.classList.add('toggled');
}
else {
_notesPaneToggle.classList.remove('toggled');
}
if (Zotero_Tabs.selectedIndex > 0) {
var height = 0;
if (Zotero.Prefs.get('layout') == 'stacked'
&& _contextPane.getAttribute('collapsed') != 'true') {
height = _contextPaneInner.boxObject.height;
}
Zotero.Reader.setBottomPlaceholderHeight(height);
}
_updatePaneWidth();
_updateToolbarWidth();
_updateAddToNote();
}
function _togglePane(paneIndex) {
var splitter;
var stacked = Zotero.Prefs.get('layout') == 'stacked';
if (stacked) {
splitter = _contextPaneSplitterStacked;
}
else {
splitter = _contextPaneSplitter;
}
var isOpen = splitter.getAttribute('state') != 'collapsed';
var hide = false;
var currentPane = _panesDeck.selectedIndex;
if (isOpen && currentPane == paneIndex) {
hide = true;
}
else {
_panesDeck.setAttribute('selectedIndex', paneIndex);
}
var context = _itemContexts.find(x => x.tabID == Zotero_Tabs.selectedID);
context.selectedIndex = paneIndex;
splitter.setAttribute('state', hide ? 'collapsed' : 'open');
_update();
}
function _init() {
// vbox
var vbox = document.createElement('vbox');
vbox.setAttribute('flex', '1');
_contextPaneInner.append(vbox);
// Toolbar extension
var toolbarExtension = document.createElement('box');
toolbarExtension.style.height = '32px';
toolbarExtension.id = 'zotero-context-toolbar-extension';
_panesDeck = document.createElement('deck');
_panesDeck.setAttribute('flex', 1);
_panesDeck.setAttribute('selectedIndex', 0);
vbox.append(toolbarExtension, _panesDeck);
// Item pane deck
_itemPaneDeck = document.createElement('deck');
// Notes pane deck
_notesPaneDeck = document.createElement('deck');
_notesPaneDeck.style.backgroundColor = 'white';
_notesPaneDeck.setAttribute('flex', 1);
_notesPaneDeck.className = 'notes-pane-deck';
_panesDeck.append(_itemPaneDeck, _notesPaneDeck);
}
function _addNotesContext(libraryID) {
var list = document.createElement('vbox');
list.setAttribute('flex', 1);
list.className = 'zotero-context-notes-list';
var noteContainer = document.createElement('vbox');
var editor = document.createElement('zoteronoteeditor');
editor.className = 'zotero-context-pane-pinned-note';
editor.setAttribute('flex', 1);
noteContainer.appendChild(editor);
let contextNode = document.createElement('deck');
contextNode.append(list, noteContainer);
_notesPaneDeck.append(contextNode);
contextNode.className = 'context-node';
contextNode.setAttribute('selectedIndex', 0);
editor.returnHandler = () => {
contextNode.setAttribute('selectedIndex', 0);
_updateAddToNote();
};
var head = document.createElement('hbox');
head.style.display = 'flex';
var label = document.createElement('label');
var button = document.createElement('button');
button.setAttribute('label', Zotero.Intl.strings['zotero.item.add']);
button.addEventListener('click', () => {
contextNode.setAttribute('selectedIndex', 1);
var item = new Zotero.Item('note');
item.libraryID = libraryID;
// item.parentKey = parentItem.key;
editor.mode = 'edit';
editor.item = item;
editor.parentItem = null;
editor.focus();
_updateAddToNote();
});
var vbox1 = document.createElement('vbox');
vbox1.append(label, button);
var vbox2 = document.createElement('vbox');
vbox2.style.flex = '1';
var input = document.createElement('textbox');
input.setAttribute('type', 'search');
input.setAttribute('timeout', '250');
input.addEventListener('command', () => {
updateNotesList();
});
vbox2.append(input);
head.append(vbox2, vbox1);
var listBox = document.createElement('vbox');
listBox.style.display = 'flex';
listBox.setAttribute('flex', '1');
var listInner = document.createElementNS(HTML_NS, 'div');
listInner.className = 'notes-list-container';
listBox.append(listInner);
list.append(head, listBox);
var notesListRef = React.createRef();
var updateNotesList = async (reset) => {
if (reset) {
input.value = '';
contextNode.setAttribute('selectedIndex', 0);
}
var text = input.value;
await Zotero.Schema.schemaUpdatePromise;
var s = new Zotero.Search();
s.addCondition('libraryID', 'is', libraryID);
s.addCondition('itemType', 'is', 'note');
s.addCondition('noChildren', 'false');
if (text) {
s.addCondition('note', 'contains', text, true);
}
var notes = await s.search();
notes = Zotero.Items.get(notes);
notes.sort((a, b) => {
a = a.getField('dateModified');
b = b.getField('dateModified');
return b.localeCompare(a);
});
notesListRef.current.setNotes(notes.map(note => {
var text2 = note.note.slice(0, 500);
text2 = text2.trim();
// TODO: Fix a potential performance issuse
text2 = Zotero.Utilities.unescapeHTML(text2);
var parts = text2.split('\n').map(x => x.trim()).filter(x => x.length);
var parent = null;
if (note.parentID) {
parent = Zotero.Items.get(note.parentID);
}
return {
id: note.id,
parentTitle: parent && parent.getDisplayTitle(),
parentImageSrc: parent && parent.getImageSrc(),
title: parts[0] || Zotero.getString('pane.item.notes.untitled'),
body: parts[1] || '',
date: (new Date(note.dateModified).toLocaleDateString(Zotero.locale))
};
}));
var c = notes.length;
var str = 'pane.item.notes.count.' + (c == 0 && 'zero' || c == 1 && 'singular' || 'plural');
label.value = Zotero.getString(str, [c]);
}
ReactDOM.render(
<NotesList
ref={notesListRef}
onClick={(id) => {
_setPinnedNote(libraryID, id);
}}
/>,
listInner,
() => {
updateNotesList();
}
);
var context = {
libraryID,
node: contextNode,
updateNotesList,
editor
};
_notesContexts.push(context);
return context;
}
function _getNotesContext(libraryID) {
var context = _notesContexts.find(x => x.libraryID == libraryID);
if (!context) {
context = _addNotesContext(libraryID);
}
return context;
}
function _selectNotesContext(libraryID) {
let context = _getNotesContext(libraryID);
_notesPaneDeck.setAttribute('selectedIndex', Array.from(_notesPaneDeck.children).findIndex(x => x == context.node));
}
function _removeNotesContext(libraryID) {
var context = _notesContexts.find(x => x.libraryID == libraryID);
context.node.remove();
_notesContexts = _notesContexts.filter(x => x.libraryID != libraryID);
}
function _libraryEditable(libraryID) {
var type = Zotero.Libraries.get(libraryID).libraryType;
if (type == 'group') {
var groupID = Zotero.Groups.getGroupIDFromLibraryID(libraryID);
var group = Zotero.Groups.get(groupID);
return group.editable;
}
return true;
}
function _setPinnedNote(libraryID, itemID) {
var editable = _libraryEditable(libraryID);
var context = _getNotesContext(libraryID);
if (context) {
let { editor, node } = context;
node.setAttribute('selectedIndex', 1);
editor.mode = editable ? 'edit' : 'view';
editor.item = Zotero.Items.get(itemID);
editor.parentItem = null;
editor.hideLinksContainer = true;
_updateAddToNote();
}
}
function _appendNoteRows(notes, list, editable, onClick, onDelete) {
for (var i = 0; i < notes.length; i++) {
var note = notes[i];
var id = notes[i].id;
var icon = document.createElement('image');
icon.className = 'zotero-box-icon';
icon.setAttribute('src', `chrome://zotero/skin/treeitem-note${Zotero.hiDPISuffix}.png`);
var label = document.createElement('label');
label.className = 'zotero-box-label';
var title = note.getNoteTitle();
title = title ? title : Zotero.getString('pane.item.notes.untitled');
label.setAttribute('value', title);
label.setAttribute('flex', '1'); //so that the long names will flex smaller
label.setAttribute('crop', 'end');
var box = document.createElement('box');
box.setAttribute('class', 'zotero-clicky');
box.addEventListener('click', () => {
onClick(id);
});
box.appendChild(icon);
box.appendChild(label);
if (editable) {
var removeButton = document.createElement('label');
removeButton.setAttribute('value', '-');
removeButton.setAttribute('class', 'zotero-clicky zotero-clicky-minus');
removeButton.addEventListener('click', function () {
onDelete(id);
});
}
var row = document.createElement('row');
row.appendChild(box);
if (editable) {
row.appendChild(removeButton);
}
list.appendChild(row);
}
}
function _removeItemContext(tabID) {
document.getElementById(tabID + '-context').remove();
_itemContexts = _itemContexts.filter(x => x.tabID != tabID);
}
function _selectItemContext(tabID, type) {
let selectedIndex = Array.from(_itemPaneDeck.children).findIndex(x => x.id == tabID + '-context');
if (selectedIndex != -1) {
_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) {
var container = document.createElement('vbox');
container.id = tabID + '-context';
container.className = 'zotero-item-pane-content';
_itemPaneDeck.appendChild(container);
var item = Zotero.Items.get(itemID);
var libraryID = item.libraryID;
var editable = _libraryEditable(libraryID);
var parentID = item.parentID;
var context = {
tabID,
itemID,
parentID,
libraryID,
selectedIndex: 0,
update: () => {}
};
_itemContexts.push(context);
if (!parentID) {
var vbox = document.createElement('vbox');
vbox.setAttribute('flex', '1');
vbox.setAttribute('align', 'center');
vbox.setAttribute('pack', 'center');
var description = document.createElement('description');
vbox.append(description);
description.append(Zotero.getString('pane.context.noParent'));
container.append(vbox);
return;
}
var parentItem = Zotero.Items.get(item.parentID);
// tabbox
var tabbox = document.createElement('tabbox');
tabbox.setAttribute('flex', '1');
tabbox.className = 'zotero-view-tabbox';
container.append(tabbox);
// tabs
var tabs = document.createElement('tabs');
tabs.className = 'zotero-editpane-tabs';
// tabpanels
var tabpanels = document.createElement('tabpanels');
tabpanels.setAttribute('flex', '1');
tabpanels.className = 'zotero-view-item';
tabpanels.addEventListener('select', () => {
_updateAddToNote();
});
tabbox.append(tabs, tabpanels);
// Info tab
var tabInfo = document.createElement('tab');
tabInfo.setAttribute('label', Zotero.Intl.strings['zotero.tabs.info.label']);
// Notes tab
var tabNotes = document.createElement('tab');
tabNotes.setAttribute('label', Zotero.Intl.strings['zotero.tabs.notes.label']);
// Tags tab
var tabTags = document.createElement('tab');
tabTags.setAttribute('label', Zotero.Intl.strings['zotero.tabs.tags.label']);
// Related tab
var tabRelated = document.createElement('tab');
tabRelated.setAttribute('label', Zotero.Intl.strings['zotero.tabs.related.label']);
tabs.append(tabInfo, tabNotes, tabTags, tabRelated);
// Info panel
var panelInfo = document.createElement('tabpanel');
panelInfo.setAttribute('flex', '1');
panelInfo.className = 'zotero-editpane-item-box';
var itemBox = document.createElement('zoteroitembox');
itemBox.setAttribute('flex', '1');
panelInfo.append(itemBox);
// Notes panel
var panelNotes = document.createElement('tabpanel');
panelNotes.setAttribute('flex', '1');
panelNotes.setAttribute('orient', 'vertical');
var deck = document.createElement('deck');
deck.className = 'notes-deck';
deck.setAttribute('flex', '1');
panelNotes.append(deck);
var vbox2 = document.createElement('vbox');
var note = document.createElement('zoteronoteeditor');
note.setAttribute('flex', 1);
vbox2.append(note);
var vbox = document.createElement('vbox');
vbox.setAttribute('flex', '1');
vbox.setAttribute('class', 'zotero-box');
vbox.style.overflowY = 'auto';
panelNotes.append(vbox);
var hbox = document.createElement('hbox');
hbox.setAttribute('align', 'center');
var label = document.createElement('label');
var button = document.createElement('button');
// TODO: Should not depend on the current ZoteroPane state
button.hidden = !editable;
button.setAttribute('label', Zotero.Intl.strings['zotero.item.add']);
button.addEventListener('click', () => {
deck.setAttribute('selectedIndex', 1);
var item = new Zotero.Item('note');
item.libraryID = parentItem.libraryID;
item.parentID = parentItem.id;
note.returnHandler = () => {
deck.setAttribute('selectedIndex', 0);
_updateAddToNote();
};
note.mode = editable ? 'edit' : 'view';
note.item = item;
note.focus();
_updateAddToNote();
});
hbox.append(label, button);
var grid = document.createElement('grid');
grid.setAttribute('flex', 1);
var columns = document.createElement('columns');
var column = document.createElement('column');
column.setAttribute('flex', 1);
columns.append(column);
var column = document.createElement('column');
columns.append(column);
grid.append(columns);
var rows = document.createElement('rows');
rows.setAttribute('flex', 1);
grid.append(rows);
vbox.append(hbox, grid);
deck.append(vbox, vbox2);
deck.setAttribute('selectedIndex', 0);
// Tags panel
var panelTags = document.createElement('tabpanel');
panelTags.setAttribute('orient', 'vertical');
panelTags.setAttribute('context', 'tags-context-menu');
panelTags.className = 'tags-pane';
panelTags.style.display = 'flex';
var div = document.createElementNS(HTML_NS, 'div');
div.className = 'tags-box-container';
div.style.display = 'flex';
div.style.flexGrow = '1';
panelTags.append(div);
var tagsBoxRef = React.createRef();
ReactDOM.render(
<TagsBoxContainer
key={'tagsBox-' + parentItem.id}
item={parentItem}
editable={editable}
ref={tagsBoxRef}
/>,
div
);
// Related panel
var panelRelated = document.createElement('tabpanel');
var relatedBox = document.createElement('relatedbox');
relatedBox.setAttribute('flex', '1');
relatedBox.className = 'zotero-editpane-related';
panelRelated.addEventListener('click', (event) => {
if (event.originalTarget.closest('.zotero-clicky')) {
Zotero_Tabs.select('zotero-pane');
}
});
panelRelated.append(relatedBox);
tabpanels.append(panelInfo, panelNotes, panelTags, panelRelated);
tabbox.selectedIndex = 0;
itemBox.mode = editable ? 'edit' : 'view';
itemBox.item = parentItem;
relatedBox.mode = editable ? 'edit' : 'view';
relatedBox.item = parentItem;
function _renderNotesPanel() {
rows.innerHTML = '';
var parentNotes = Zotero.Items.get(parentItem.getNotes());
_appendNoteRows(parentNotes, rows, editable, (id) => {
deck.setAttribute('selectedIndex', 1);
note.returnHandler = () => {
deck.setAttribute('selectedIndex', 0);
_updateAddToNote();
};
note.mode = editable ? 'edit' : 'view';
note.item = Zotero.Items.get(id);
note.parentItem = null;
_updateAddToNote();
}, (id) => {
_removeNote(id);
});
var c = parentNotes.length;
var str = 'pane.item.notes.count.' + (c == 0 && 'zero' || c == 1 && 'singular' || 'plural');
label.value = Zotero.getString(str, [c]);
}
context.update = _renderNotesPanel;
_renderNotesPanel();
}
};
addEventListener('load', function (e) { ZoteroContextPane.onLoad(e); }, false);
addEventListener('unload', function (e) { ZoteroContextPane.onUnload(e); }, false);

View file

@ -26,17 +26,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
import TagsBoxContainer from 'containers/tagsBoxContainer';
import NotesList from 'components/itemPane/notesList';
var ZoteroItemPane = new function() {
const HTML_NS = 'http://www.w3.org/1999/xhtml';
var _lastItem, _itemBox, _notesLabel, _notesButton, _notesList, _tagsBox, _relatedBox;
var _selectedNoteID;
var _translationTarget;
var _noteIDs;
var _contextNoteUpdaters = [];
let _pdfTabHidden = false;
this.onLoad = function () {
if (!Zotero) {
@ -60,15 +55,7 @@ var ZoteroItemPane = new function() {
};
_relatedBox = document.getElementById('zotero-editpane-related');
this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'tab'], 'itemPane');
document.getElementById('temp-toggle-1').addEventListener('click', () => {
this.togglePane();
});
document.getElementById('temp-toggle-2').addEventListener('click', () => {
this.togglePane();
});
this.initStandaloneNotesView();
this._unregisterID = Zotero.Notifier.registerObserver(this, ['item'], 'itemPane');
}
@ -115,8 +102,6 @@ var ZoteroItemPane = new function() {
_lastItem = item;
_updateTitle();
var viewBox = document.getElementById('zotero-view-item');
viewBox.classList.remove('no-tabs');
@ -228,7 +213,6 @@ var ZoteroItemPane = new function() {
this.notify = Zotero.Promise.coroutine(function* (action, type, ids, extraData) {
if(type == 'item') {
var viewBox = document.getElementById('zotero-view-item');
// If notes pane is selected, refresh it if any of the notes change or are deleted
if (viewBox.selectedIndex == 1 && (action == 'modify' || action == 'delete')) {
@ -240,22 +224,6 @@ var ZoteroItemPane = new function() {
yield this.viewItem(_lastItem, null, 1);
}
}
this.updateStandaloneNotesList();
for (let updater of _contextNoteUpdaters) {
updater.callback();
}
}
else if (type == 'tab') {
if (action == 'add') {
this.addPDFTabContext(ids[0], extraData.itemID);
}
else if (action == 'close') {
this.removeTabContext(ids[0]);
}
else if (action == 'select') {
this.selectTabContext(ids[0], extraData.type);
}
}
});
@ -311,7 +279,6 @@ var ZoteroItemPane = new function() {
// noteEditor.clearUndo();
// }
document.getElementById('zotero-view-note-sidebar-button').hidden = !!item.parentID;
document.getElementById('zotero-view-note-button').hidden = !editable;
document.getElementById('zotero-item-pane-content').selectedIndex = 2;
};
@ -321,17 +288,6 @@ var ZoteroItemPane = new function() {
// ZoteroPane.setItemPaneMessage(Zotero.getString('pane.item.notes.editingInWindow'));
};
this.openNoteSidebar = async function () {
var selectedNote = Zotero.Items.get(_selectedNoteID);
if (!selectedNote.parentID) {
let editor = document.getElementById('zotero-item-pane-pinned-note');
editor.mode = 'edit';
editor.item = selectedNote;
document.getElementById('zotero-item-pane-pin-deck2').setAttribute('selectedIndex', 1);
this.togglePane(false);
}
}
/**
* Select the parent item and open the note editor
@ -480,11 +436,6 @@ var ZoteroItemPane = new function() {
};
function _updateTitle() {
document.getElementById('item-pane-title').textContent = _lastItem.getDisplayTitle();
}
function _updateNoteCount() {
var c = _notesList.childNodes.length;
@ -503,508 +454,6 @@ var ZoteroItemPane = new function() {
_notesLabel.value = Zotero.getString(str, [c]);
}
this.getActiveNote = function () {
let mainDeck = document.getElementById('zotero-item-pane-main-deck');
if (mainDeck.selectedIndex == 0) {
let contextualDeck = document.getElementById('zotero-item-pane-contextual-deck');
if (contextualDeck.selectedIndex > 0) {
let child = contextualDeck.children[contextualDeck.selectedIndex];
if (child.querySelector('deck').selectedIndex == 1) {
return child.querySelector('zoteronoteeditor');
}
}
}
else {
let pinnedDeck = document.getElementById('zotero-item-pane-pin-deck2');
if (pinnedDeck.selectedIndex == 1) {
return pinnedDeck.querySelector('zoteronoteeditor');
}
}
return null;
}
this.togglePane = function (forceItem) {
var pane = document.getElementById('zotero-item-pane');
let mainDeck = document.getElementById('zotero-item-pane-main-deck');
let value;
if (forceItem !== undefined) {
value = forceItem ? 0 : 1;
}
else {
value = mainDeck.selectedIndex == 0 ? 1 : 0;
}
if (value == 0) {
pane.setAttribute('data-mode', 'item');
document.getElementById('temp-toggle-1').innerHTML = '[&#128196;] &#128210;';
document.getElementById('temp-toggle-2').innerHTML = '[&#128196;] &#128210;';
}
else {
pane.setAttribute('data-mode', 'notes');
document.getElementById('temp-toggle-1').innerHTML = '&#128196; [&#128210;]';
document.getElementById('temp-toggle-2').innerHTML = '&#128196; [&#128210;]';
}
mainDeck.selectedIndex = value;
let contextualDeck = document.getElementById('zotero-item-pane-contextual-deck');
contextualDeck.children[contextualDeck.selectedIndex].setAttribute('state', value);
}
this.initStandaloneNotesView = async function () {
let container = document.getElementById('zotero-item-pane-pin-deck');
let bar = document.createElement('hbox');
container.appendChild(bar);
let inner = document.createElement('deck');
inner.id = 'zotero-item-pane-pin-deck2';
inner.style.backgroundColor = 'white';
inner.setAttribute('flex', 1);
container.appendChild(inner);
var listLabel = document.createElementNS(HTML_NS, 'div');
listLabel.className = 'notes-list-label';
listLabel.textContent = Zotero.getString('pane.item.notes.allNotes');
let list = document.createElement('vbox');
list.setAttribute('flex', 1);
list.className = 'zotero-box';
list.appendChild(listLabel);
var noteContainer = document.createElement('vbox');
var backButton = document.createElementNS(HTML_NS, 'div');
backButton.className = 'item-pane-back-button';
backButton.textContent = (Zotero.dir == 'ltr' ? '←' : '→') + ' '
+ Zotero.getString('general.back');
backButton.addEventListener('click', () => {
inner.setAttribute('selectedIndex', 0);
});
let note = document.createElement('zoteronoteeditor');
note.id = 'zotero-item-pane-pinned-note';
note.setAttribute('flex', 1);
noteContainer.appendChild(backButton);
noteContainer.appendChild(note);
inner.append(list, noteContainer);
inner.setAttribute('selectedIndex', 0);
let head = document.createElement('hbox');
let label = document.createElement('label');
let button = document.createElement('button');
button.setAttribute('label', Zotero.Intl.strings['zotero.item.add']);
button.addEventListener('click', async () => {
inner.setAttribute('selectedIndex', 1);
let item = new Zotero.Item('note');
item.libraryID = ZoteroPane_Local.getSelectedLibraryID();
// item.parentKey = parentItem.key;
note.mode = 'edit';
note.item = item;
note.parentItem = null;
note.focus();
});
head.style.paddingRight = '10px';
let input = document.createElement('textbox');
input.setAttribute('type', 'search');
input.setAttribute('timeout', '250');
input.addEventListener('command', (event) => {
_updateStandaloneNotesList();
})
let vbox1 = document.createElement('vbox');
vbox1.append(label, button);
let vbox2 = document.createElement('vbox');
vbox2.append(input);
head.append(vbox2, vbox1);
head.style.display = 'flex';
vbox2.style.flex = '1';
let listBox = document.createElement('vbox');
listBox.style.display = 'flex';
listBox.setAttribute('flex', '1')
var listInner = document.createElementNS(HTML_NS, 'div');
listInner.className = 'notes-list-container';
list.append(head, listBox);
listBox.append(listInner);
let noteListRef = React.createRef();
let _updateStandaloneNotesList = async (reset) => {
if (reset) {
input.value = '';
inner.setAttribute('selectedIndex', 0);
}
let text = input.value;
await Zotero.Schema.schemaUpdatePromise;
var s = new Zotero.Search();
s.addCondition('libraryID', 'is', ZoteroPane_Local.getSelectedLibraryID());
s.addCondition('itemType', 'is', 'note');
s.addCondition('noChildren', 'true');
if (text) {
s.addCondition('note', 'contains', text, true);
}
let notes = await s.search();
notes = Zotero.Items.get(notes);
notes.sort((a, b) => {
a = a.getField('dateModified');
b = b.getField('dateModified');
return b.localeCompare(a);
});
noteListRef.current.setNotes(notes.map(note => {
let text2 = note.note;
text2 = text2.trim();
// TODO: Fix a potential performance issuse
text2 = Zotero.Utilities.unescapeHTML(text2);
let parts = text2.split('\n').map(x => x.trim()).filter(x => x.length);
return {
id: note.id,
title: parts[0] || Zotero.getString('pane.item.notes.untitled'),
body: parts[1] || '',
date: (new Date(note.dateModified).toLocaleDateString(Zotero.locale))
};
}));
var c = notes.length;
var str = 'pane.item.notes.count.' + (c == 0 && 'zero' || c == 1 && 'singular' || 'plural');
label.value = Zotero.getString(str, [c]);
}
ReactDOM.render(
<NotesList
ref={noteListRef}
onClick={(id) => {
this._setPinnedNote(id);
}}
/>,
listInner,
() => {
_updateStandaloneNotesList();
}
);
this.updateStandaloneNotesList = _updateStandaloneNotesList;
}
this._setPinnedNote = function (itemID) {
let pinnedDeck = document.getElementById('zotero-item-pane-pin-deck2');
pinnedDeck.setAttribute('selectedIndex', 1);
let pinnedNote = document.getElementById('zotero-item-pane-pinned-note');
pinnedNote.mode = 'edit';
pinnedNote.item = Zotero.Items.get(itemID);
pinnedNote.parentItem = null;
this.togglePane(false);
}
this._appendNoteRows = function (notes, list, editable, onClick, onDelete) {
for (var i = 0; i < notes.length; i++) {
let note = notes[i];
let id = notes[i].id;
var icon = document.createElement('image');
icon.className = 'zotero-box-icon';
icon.setAttribute('src', `chrome://zotero/skin/treeitem-note${Zotero.hiDPISuffix}.png`);
var label = document.createElement('label');
label.className = 'zotero-box-label';
var title = note.getNoteTitle();
title = title ? title : Zotero.getString('pane.item.notes.untitled');
label.setAttribute('value', title);
label.setAttribute('flex', '1'); //so that the long names will flex smaller
label.setAttribute('crop', 'end');
var box = document.createElement('box');
box.setAttribute('class', 'zotero-clicky');
box.addEventListener('click', () => {
onClick(id);
});
box.appendChild(icon);
box.appendChild(label);
if (editable) {
var removeButton = document.createElement('label');
removeButton.setAttribute('value', '-');
removeButton.setAttribute('class', 'zotero-clicky zotero-clicky-minus');
removeButton.addEventListener('click', function () {
onDelete(id)
});
}
var row = document.createElement('row');
row.appendChild(box);
if (editable) {
row.appendChild(removeButton);
}
list.appendChild(row);
}
}
this.removeTabContext = function (tabID) {
document.getElementById(tabID + '-context').remove();
_contextNoteUpdaters = _contextNoteUpdaters.filter(x => x.tabID != tabID);
};
this.selectTabContext = function (tabID, type) {
var pane = document.getElementById('zotero-item-pane');
pane.setAttribute('data-type', type);
let contextualDeck = document.getElementById('zotero-item-pane-contextual-deck');
let prevIndex = contextualDeck.selectedIndex;
contextualDeck.selectedIndex = Array.from(contextualDeck.children).findIndex(x => x.id == tabID + '-context');
let toolbar = document.getElementById('zotero-pane-horizontal-space');
let extendedToolbar = document.getElementById('zotero-item-pane-padding-top');
let itemPane = document.getElementById('zotero-item-pane');
if (prevIndex != 0) {
_pdfTabHidden = itemPane.hidden;
}
if (type == 'library') {
toolbar.hidden = false;
extendedToolbar.hidden = true;
itemPane.hidden = false;
}
else {
toolbar.hidden = true;
extendedToolbar.hidden = false;
itemPane.hidden = _pdfTabHidden;
}
let state = contextualDeck.children[contextualDeck.selectedIndex].getAttribute('state');
let mainDeck = document.getElementById('zotero-item-pane-main-deck');
if (state == 0) {
pane.setAttribute('data-mode', 'item');
document.getElementById('temp-toggle-1').innerHTML = '[&#128196;] &#128210;';
document.getElementById('temp-toggle-2').innerHTML = '[&#128196;] &#128210;';
mainDeck.selectedIndex = state;
}
else if (state == 1) {
pane.setAttribute('data-mode', 'notes');
document.getElementById('temp-toggle-1').innerHTML = '&#128196; [&#128210;]';
document.getElementById('temp-toggle-2').innerHTML = '&#128196; [&#128210;]';
mainDeck.selectedIndex = state;
}
};
this.addPDFTabContext = function (tabID, itemID) {
let contextualDeck = document.getElementById('zotero-item-pane-contextual-deck');
let container = document.createElement('vbox');
container.id = tabID + '-context';
container.className = 'zotero-item-pane-content';
contextualDeck.appendChild(container);
var item = Zotero.Items.get(itemID);
if (!item.parentID) {
container.append('The PDF doesn\'t have a parent');
return;
}
let parentID = item.parentID;
let mainDeck = document.getElementById('zotero-item-pane-main-deck');
let pinDeck = document.getElementById('zotero-item-pane-pin-deck2');
container.setAttribute('state', (mainDeck.selectedIndex == 1 && pinDeck.selectedIndex == 1) ? 1 : 0)
let parentItem = Zotero.Items.get(parentID);
let tabbox = document.createElement('tabbox');
tabbox.setAttribute('flex', '1');
tabbox.className = 'zotero-view-tabbox';
let tabs = document.createElement('tabs');
tabs.className = 'zotero-editpane-tabs';
container.append(tabbox);
let tabInfo = document.createElement('tab');
tabInfo.setAttribute('label', Zotero.Intl.strings['zotero.tabs.info.label']);
let tabNotes = document.createElement('tab');
tabNotes.setAttribute('label', Zotero.Intl.strings['zotero.tabs.notes.label']);
let tabTags = document.createElement('tab');
tabTags.setAttribute('label', Zotero.Intl.strings['zotero.tabs.tags.label']);
let tabRelated = document.createElement('tab');
tabRelated.setAttribute('label', Zotero.Intl.strings['zotero.tabs.related.label']);
tabs.append(tabInfo, tabNotes, tabTags, tabRelated);
let tabpanels = document.createElement('tabpanels');
tabpanels.setAttribute('flex', '1');
tabpanels.className = 'zotero-view-item';
tabbox.append(tabs, tabpanels);
let panelInfo = document.createElement('tabpanel');
panelInfo.setAttribute('flex', '1')
panelInfo.className = 'zotero-editpane-item-box';
let itemBox = document.createElement('zoteroitembox');
itemBox.setAttribute('flex', '1');
panelInfo.append(itemBox);
let panelNotes = document.createElement('tabpanel');
panelNotes.setAttribute('flex', '1');
panelNotes.setAttribute('orient', 'vertical');
var deck = document.createElement('deck');
deck.setAttribute('flex', '1');
panelNotes.append(deck);
var vbox2 = document.createElement('vbox');
var backButton = document.createElementNS(HTML_NS, 'div');
backButton.className = 'item-pane-back-button';
backButton.textContent = (Zotero.dir == 'ltr' ? '←' : '→') + ' '
+ Zotero.getString('general.back');
backButton.addEventListener('click', () => {
deck.setAttribute('selectedIndex', 0);
});
let note = document.createElement('zoteronoteeditor');
note.setAttribute('flex', 1);
vbox2.append(backButton, note);
var vbox = document.createElement('vbox');
vbox.setAttribute('flex', '1');
vbox.setAttribute('class', 'zotero-box');
panelNotes.append(vbox);
var hbox = document.createElement('hbox');
hbox.setAttribute('align', 'center');
var label = document.createElement('label');
var button = document.createElement('button');
button.setAttribute('label', Zotero.Intl.strings['zotero.item.add']);
button.addEventListener('click', () => {
deck.setAttribute('selectedIndex', 1);
let item = new Zotero.Item('note');
item.libraryID = parentItem.libraryID;
item.parentItemID = parentItem.id;
note.mode = 'edit';
note.item = item;
note.focus();
});
hbox.append(label, button);
var grid = document.createElement('grid');
grid.setAttribute('flex', 1);
var columns = document.createElement('columns');
var column = document.createElement('column');
column.setAttribute('flex', 1);
columns.append(column);
var column = document.createElement('column');
columns.append(column);
grid.append(columns);
var rows = document.createElement('rows');
rows.setAttribute('flex', 1);
grid.append(rows);
vbox.append(hbox, grid);
deck.append(vbox, vbox2);
deck.setAttribute('selectedIndex', 0);
deck.className = 'zotero-item-pane-content';
let panelTags = document.createElement('tabpanel');
panelTags.setAttribute('orient', 'vertical');
panelTags.setAttribute('context', 'tags-context-menu');
panelTags.className = 'tags-pane';
panelTags.style.display = 'flex';
var div = document.createElementNS(HTML_NS, 'div');
div.className = 'tags-box-container';
div.style.display = 'flex';
div.style.flexGrow = '1';
panelTags.append(div);
let panelRelated = document.createElement('tabpanel');
let relatedBox = document.createElement('relatedbox');
relatedBox.setAttribute('flex', '1');
relatedBox.className = 'zotero-editpane-related';
panelRelated.append(relatedBox);
tabpanels.append(panelInfo, panelNotes, panelTags, panelRelated);
itemBox.mode = 'edit';
itemBox.item = Zotero.Items.get(parentID);
relatedBox.mode = 'edit';
relatedBox.item = parentItem;
panelRelated.addEventListener('click', (event) => {
if (event.originalTarget.closest('.zotero-clicky')) {
Zotero_Tabs.select('zotero-pane');
}
});
let _renderNotesPanel = () => {
while (rows.firstChild) {
rows.firstChild.remove();
}
let parentNotes = Zotero.Items.get(parentItem.getNotes());
this._appendNoteRows(parentNotes, rows, true, (id) => {
deck.setAttribute('selectedIndex', 1);
note.mode = 'edit';
note.item = Zotero.Items.get(id);
note.parentItem = null;
}, (id) => {
ZoteroItemPane.removeNote(id);
});
var c = parentNotes.length;
var str = 'pane.item.notes.count.' + (c == 0 && 'zero' || c == 1 && 'singular' || 'plural');
label.value = Zotero.getString(str, [c]);
}
_contextNoteUpdaters.push({
tabID,
callback: _renderNotesPanel
});
_renderNotesPanel();
let mode = 'edit';
let _tagsBox = { current: null };
let focusItemsList = false;
let _renderTagsPanel = () => {
ReactDOM.render(
<TagsBoxContainer
key={'tagsBox-' + parentItem.id}
item={parentItem}
editable={mode != 'view'}
ref={_tagsBox}
onResetSelection={focusItemsList}
/>,
div
);
}
_renderTagsPanel();
}
}
addEventListener("load", function(e) { ZoteroItemPane.onLoad(e); }, false);

View file

@ -34,20 +34,7 @@
<script src="include.js"/>
<script src="itemPane.js"></script>
<vbox id="zotero-item-pane"
zotero-persist="width height"
data-type="library"
data-mode="item"
>
<hbox id="zotero-item-pane-padding-top" height="32" hidden="true" align="right">
<button id="temp-toggle-2" style="min-width: 37px; padding: 2px; font-size: 13px;">[&#128196;] &#128210;</button>
</hbox>
<html:div id="item-pane-title"/>
<deck id="zotero-item-pane-main-deck" flex="1" selectedIndex="0">
<deck id="zotero-item-pane-contextual-deck" selectedIndex="0">
<vbox id="zotero-pane-context">
<vbox id="zotero-item-pane" zotero-persist="width height">
<!-- My Publications -->
<hbox id="zotero-item-pane-top-buttons-my-publications" class="zotero-item-pane-top-buttons" hidden="true">
<button id="zotero-item-collection-show-hide"/>
@ -128,11 +115,7 @@
-->
<zoteronoteeditor id="zotero-note-editor" flex="1" notitle="1"
previousfocus="zotero-items-tree"
onerror="return;ZoteroPane.displayErrorMessage(); /*this.mode = 'view'*/"/>
<!-- TODO: Localize -->
<button id="zotero-view-note-sidebar-button"
label="Edit in notes sidebar"
oncommand="ZoteroItemPane.openNoteSidebar()"/>
onerror="ZoteroPane.displayErrorMessage(); this.mode = 'view'"/>
<button id="zotero-view-note-button"
label="&zotero.notes.separate;"
oncommand="ZoteroItemPane.openNoteWindow()"/>
@ -163,9 +146,4 @@
</vbox>
</deck>
</vbox>
</deck>
<vbox id="zotero-item-pane-pin-deck"></vbox>
</deck>
</vbox>
</overlay>

View file

@ -27,6 +27,7 @@
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
<script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
<script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
<script src="include.js"/>
<commandset id="mainCommandSet">
<!--FILE-->
<command id="cmd_quitApplication" oncommand="goQuitApplication();"/>
@ -115,27 +116,5 @@
<popupset id="zotero-reader-popupset">
</popupset>
</vbox>
<splitter id="zotero-reader-splitter"
hidden="true"
resizebefore="closest"
resizeafter="closest"
collapse="after"
orient="horizontal"
zotero-persist="state orient" />
<vbox flex="0" id="zotero-reader-note-sidebar" width="350" hidden="true">
<vbox id="zotero-reader-sidebar-cover" flex="1">
<label>Drag a note here…</label>
</vbox>
<vbox id="zotero-reader-sidebar-container" flex="1" style="overflow:auto;" hidden="true">
<zoteronoteeditor id="zotero-reader-editor" flex="1" notitle="1"
previousfocus="zotero-items-tree"
onerror="/*this.mode = 'view'*/"
/>
<button id="zotero-view-note-button" label="Close"
oncommand="document.getElementById('zotero-reader-sidebar-container').hidden = true;document.getElementById('zotero-reader-sidebar-cover').hidden = false;"/>
</vbox>
</vbox>
</hbox>
<script src="include.js"/>
</window>

View file

@ -54,7 +54,8 @@ const ZoteroStandalone = new function() {
notify: async (action, type, ids, extraData) => {
if (action == 'select') {
// "library" or "reader"
this.switchMenuType(extraData.type);
this.switchMenuType(extraData[ids[0]].type);
setTimeout(() => ZoteroPane.updateToolbarPosition(), 0);
}
}
},

View file

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

View file

@ -33,6 +33,7 @@ class EditorInstance {
this.onNavigate = options.onNavigate;
this._item = options.item;
this._readOnly = options.readOnly;
this._onReturn = options.onReturn;
this._iframeWindow = options.iframeWindow;
this._popup = options.popup;
this._state = options.state;
@ -63,6 +64,7 @@ class EditorInstance {
action: 'init',
value: this._state || this._item.note,
readOnly: this._readOnly,
enableReturnButton: !!this._onReturn,
placeholder: options.placeholder,
dir: Zotero.dir,
font: this._getFont(),
@ -140,6 +142,30 @@ class EditorInstance {
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 = [];
for (let annotation of annotations) {
@ -148,10 +174,9 @@ class EditorInstance {
continue;
}
let item = attachmentItem.parentID && await Zotero.Items.getAsync(attachmentItem.parentID) || attachmentItem;
annotation.uri = Zotero.URI.getItemURI(attachmentItem);
if (item !== attachmentItem) {
annotation.parentURI = Zotero.URI.getItemURI(item);
}
annotation.uri = Zotero.URI.getItemURI(attachmentItem);
let citationItem = {
uris: [Zotero.URI.getItemURI(item)],
@ -168,6 +193,10 @@ class EditorInstance {
let formattedCitation = (await this._getFormattedCitationParts(citation)).join(';');
list.push({ annotation, citation, formattedCitation });
}
else {
list.push({ annotation });
}
}
return list;
}
@ -188,6 +217,7 @@ class EditorInstance {
continue;
}
if (item.isRegularItem()) {
let citation = {
citationItems: [{
uris: [Zotero.URI.getItemURI(item)],
@ -200,6 +230,11 @@ class EditorInstance {
list.push({ citation, formattedCitation });
}
else if (item.isNote()) {
let note = await this._digestExternalNote(item.id);
list.push({ note });
}
}
}
else if (type === 'zotero/annotation') {
let annotations = JSON.parse(data);
@ -343,6 +378,10 @@ class EditorInstance {
this._openPopup(x, y, pos, itemGroups);
return;
}
case 'return': {
this._onReturn();
return;
}
}
}

View file

@ -8,17 +8,21 @@ class ReaderInstance {
constructor() {
this.pdfStateFileName = '.zotero-pdf-state';
this.annotationItemIDs = [];
this.onChangeSidebarWidth = null;
this._instanceID = Zotero.Utilities.randomString();
this._window = null;
this._iframeWindow = null;
this._itemID = null;
this._state = null;
this._prevHistory = [];
this._nextHistory = [];
this._isReaderInitialized = false;
this._showItemPaneToggle = false;
this._initPromise = new Promise((resolve, reject) => {
this._resolveInitPromise = resolve;
this._rejectInitPromise = reject;
});
}
async open({ itemID, state, location, skipHistory }) {
async open({ itemID, state, location }) {
if (itemID === this._itemID) {
return false;
}
@ -26,13 +30,6 @@ class ReaderInstance {
if (!item) {
return false;
}
if (this._itemID && !skipHistory) {
this._prevHistory.push({
itemID: this._itemID,
state: this._state
});
this._nextHistory = [];
}
this._itemID = item.id;
let path = await item.getFilePathAsync();
let buf = await OS.File.read(path, {});
@ -51,9 +48,11 @@ class ReaderInstance {
annotations,
state,
location,
enablePrev: !!this._prevHistory.length,
enableNext: !!this._nextHistory.length,
promptImport: !!Zotero.PDF.hasUnmachedAnnotations[this._itemID]
promptImport: !!Zotero.PDF.hasUnmachedAnnotations[this._itemID],
showItemPaneToggle: this._showItemPaneToggle,
sidebarWidth: this._sidebarWidth,
sidebarOpen: this._sidebarOpen,
bottomPlaceholderHeight: this._bottomPlaceholderHeight
}, [buf]);
return true;
}
@ -98,6 +97,28 @@ class ReaderInstance {
this._postMessage({ action: 'toggleImportPrompt', enable });
}
enableAddToNote(enable) {
this._postMessage({ action: 'enableAddToNote', enable });
}
setSidebarWidth(width) {
this._postMessage({ action: 'setSidebarWidth', width });
}
setSidebarOpen(open) {
this._postMessage({ action: 'setSidebarOpen', open });
}
async setBottomPlaceholderHeight(height) {
await this._initPromise;
this._postMessage({ action: 'setBottomPlaceholderHeight', height });
}
async setToolbarPlaceholderWidth(width) {
await this._initPromise;
this._postMessage({ action: 'setToolbarPlaceholderWidth', width });
}
async _saveState(state) {
let item = Zotero.Items.get(this._itemID);
let file = Zotero.Attachments.getStorageDirectory(item);
@ -137,20 +158,6 @@ class ReaderInstance {
}
}
// TODO: Pass sidebar state to the responsible pdf-reader button
_toggleNoteSidebar(isToggled) {
let splitter = this._window.document.getElementById('zotero-reader-splitter');
let sidebar = this._window.document.getElementById('zotero-reader-note-sidebar');
if (isToggled) {
splitter.hidden = false;
sidebar.hidden = false;
}
else {
splitter.hidden = true;
sidebar.hidden = true;
}
}
_getColorIcon(color, selected) {
let stroke = selected ? 'lightgray' : 'transparent';
let fill = '%23' + color.slice(1);
@ -176,8 +183,10 @@ class ReaderInstance {
popup.addEventListener('popuphidden', function () {
popup.remove();
});
let menuitem;
// Add to note
let menuitem = this._window.document.createElement('menuitem');
if (this._window.ZoteroContextPane.getActiveEditor()) {
menuitem = this._window.document.createElement('menuitem');
menuitem.setAttribute('label', 'Add to Note');
menuitem.addEventListener('command', () => {
let data = {
@ -190,6 +199,7 @@ class ReaderInstance {
popup.appendChild(menuitem);
// Separator
popup.appendChild(this._window.document.createElement('menuseparator'));
}
// Colors
for (let color of colors) {
menuitem = this._window.document.createElement('menuitem');
@ -270,26 +280,8 @@ class ReaderInstance {
Zotero.debug('Received message from pdf-reader iframe: ' + JSON.stringify(data));
message = data.message;
switch (message.action) {
case 'navigatePrev': {
let prev = this._prevHistory.pop();
if (prev) {
this._nextHistory.push({
itemID: this._itemID,
state: this._state
});
this.open({ itemID: prev.itemID, state: prev.state, skipHistory: true });
}
return;
}
case 'navigateNext': {
let next = this._nextHistory.pop();
if (next) {
this._prevHistory.push({
itemID: this._itemID,
state: this._state
});
this.open({ itemID: next.itemID, state: next.state, skipHistory: true });
}
case 'initialized': {
this._resolveInitPromise();
return;
}
case 'setAnnotation': {
@ -395,6 +387,20 @@ class ReaderInstance {
this._toggleNoteSidebar(isToggled);
return;
}
case 'changeSidebarWidth': {
let { width } = message;
if (this.onChangeSidebarWidth) {
this.onChangeSidebarWidth(width);
}
return;
}
case 'changeSidebarOpen': {
let { open } = message;
if (this.onChangeSidebarOpen) {
this.onChangeSidebarOpen(open);
}
return;
}
}
}
catch (e) {
@ -459,8 +465,12 @@ class ReaderInstance {
}
class ReaderTab extends ReaderInstance {
constructor(itemID) {
constructor({ itemID, sidebarWidth, sidebarOpen, bottomPlaceholderHeight }) {
super();
this._sidebarWidth = sidebarWidth;
this._sidebarOpen = sidebarOpen;
this._bottomPlaceholderHeight = bottomPlaceholderHeight;
this._showItemPaneToggle = true;
this._window = Services.wm.getMostRecentWindow('navigator:browser');
let { id, container } = this._window.Zotero_Tabs.add({
type: 'reader',
@ -493,7 +503,7 @@ class ReaderTab extends ReaderInstance {
}
});
this._iframe.setAttribute('tooltip', 'iframeTooltip');
this._iframe.setAttribute('tooltip', 'html-tooltip');
}
close() {
@ -534,7 +544,7 @@ class ReaderTab extends ReaderInstance {
}
_addToNote(annotations) {
let noteEditor = this._window.ZoteroItemPane.getActiveNote();
let noteEditor = this._window.ZoteroContextPane.getActiveEditor();
if (!noteEditor) {
return;
}
@ -548,9 +558,11 @@ class ReaderTab extends ReaderInstance {
class ReaderWindow extends ReaderInstance {
constructor() {
constructor({ sidebarWidth, sidebarOpen, bottomPlaceholderHeight }) {
super();
this._sidebarWidth = sidebarWidth;
this._sidebarOpen = sidebarOpen;
this._bottomPlaceholderHeight = bottomPlaceholderHeight;
this.init();
}
@ -564,8 +576,6 @@ class ReaderWindow extends ReaderInstance {
this._window.addEventListener('DOMContentLoaded', (event) => {
if (event.target === this._window.document) {
this._window.addEventListener('dragover', this._handleDragOver, true);
this._window.addEventListener('drop', this._handleDrop, true);
this._window.addEventListener('keypress', this._handleKeyPress);
this._popupset = this._window.document.getElementById('zotero-reader-popupset');
@ -583,23 +593,6 @@ class ReaderWindow extends ReaderInstance {
this._postMessage(data);
};
let editor = this._window.document.getElementById('zotero-reader-editor');
editor.navigateHandler = async (uri, location) => {
let item = await Zotero.URI.getURIItem(uri);
if (!item) {
return;
}
if (item.id === this._itemID) {
this.navigate(location);
}
else {
await this.open({
itemID: item.id,
location
});
}
};
this._iframe = this._window.document.getElementById('reader');
}
@ -618,76 +611,80 @@ class ReaderWindow extends ReaderInstance {
this._window.document.title = title;
}
_handleDragOver = (event) => {
if (event.dataTransfer.getData('zotero/item')) {
event.preventDefault();
event.stopPropagation();
}
}
_handleDrop = (event) => {
let data;
if (!(data = event.dataTransfer.getData('zotero/item'))) {
return;
}
let ids = data.split(',').map(id => parseInt(id));
let item = Zotero.Items.get(ids[0]);
if (!item) {
return;
}
if (item.isNote()) {
event.preventDefault();
event.stopPropagation();
let cover = this._window.document.getElementById('zotero-reader-sidebar-cover');
let container = this._window.document.getElementById('zotero-reader-sidebar-container');
let splitter = this._window.document.getElementById('zotero-reader-splitter');
cover.hidden = true;
container.hidden = false;
splitter.hidden = false;
let editor = this._window.document.getElementById('zotero-reader-editor');
let notebox = this._window.document.getElementById('zotero-reader-note-sidebar');
editor.mode = 'edit';
notebox.hidden = false;
editor.item = item;
}
else if (item.isAttachment() && item.attachmentContentType === 'application/pdf') {
event.preventDefault();
event.stopPropagation();
this.open({ itemID: item.id });
}
else if (item.isRegularItem()) {
let attachments = item.getAttachments();
if (attachments.length === 1) {
let id = attachments[0];
let attachment = Zotero.Items.get(id);
if (attachment.attachmentContentType === 'application/pdf') {
event.preventDefault();
event.stopPropagation();
this.open({ itemID: attachment.id });
}
}
}
}
_handleKeyPress = (event) => {
if ((Zotero.isMac && event.metaKey || event.ctrlKey)
&& !event.shiftKey && !event.altKey && event.key === 'w') {
this._window.close();
}
}
}
class Reader {
constructor() {
this._sidebarWidth = 200;
this._sidebarOpen = false;
this._bottomPlaceholderHeight = 800;
this._readers = [];
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'reader');
this.onChangeSidebarWidth = null;
this.onChangeSidebarOpen = null;
this._debounceSidebarWidthUpdate = Zotero.Utilities.debounce(() => {
let readers = this._readers.filter(r => r instanceof ReaderTab);
for (let reader of readers) {
reader.setSidebarWidth(this._sidebarWidth);
}
}, 500);
}
getSidebarWidth() {
return this._sidebarWidth;
}
_loadSidebarOpenState() {
let win = Zotero.getMainWindow();
if (win) {
let pane = win.document.getElementById('zotero-reader-sidebar-pane');
this._sidebarOpen = pane.getAttribute('collapsed') == 'false';
}
}
_setSidebarOpenState() {
let win = Zotero.getMainWindow();
if (win) {
let pane = win.document.getElementById('zotero-reader-sidebar-pane');
pane.setAttribute('collapsed', this._sidebarOpen ? 'false' : 'true');
}
}
getSidebarOpen() {
return this._sidebarOpen;
}
setSidebarWidth(width) {
this._sidebarWidth = width;
let readers = this._readers.filter(r => r instanceof ReaderTab);
for (let reader of readers) {
reader.setSidebarWidth(width);
}
}
setSidebarOpen(open) {
this._sidebarOpen = open;
let readers = this._readers.filter(r => r instanceof ReaderTab);
for (let reader of readers) {
reader.setSidebarOpen(open);
}
this._setSidebarOpenState();
}
setBottomPlaceholderHeight(height) {
this._bottomPlaceholderHeight = height;
let readers = this._readers.filter(r => r instanceof ReaderTab);
for (let reader of readers) {
reader.setBottomPlaceholderHeight(height);
}
}
notify(event, type, ids, extraData) {
@ -746,6 +743,7 @@ class Reader {
}
async open(itemID, location, openWindow) {
this._loadSidebarOpenState();
this.triggerAnnotationsImportCheck(itemID);
let reader;
@ -762,21 +760,44 @@ class Reader {
}
}
else if (openWindow) {
reader = new ReaderWindow();
reader = new ReaderWindow({
sidebarWidth: this._sidebarWidth,
sidebarOpen: this._sidebarOpen,
bottomPlaceholderHeight: this._bottomPlaceholderHeight
});
this._readers.push(reader);
if (!(await reader.open({ itemID, location }))) {
return;
}
this._readers.push(reader);
reader._window.addEventListener('unload', () => {
this._readers.splice(this._readers.indexOf(reader), 1);
});
}
else {
reader = new ReaderTab(itemID);
reader = new ReaderTab({
itemID,
sidebarWidth: this._sidebarWidth,
sidebarOpen: this._sidebarOpen,
bottomPlaceholderHeight: this._bottomPlaceholderHeight
});
this._readers.push(reader);
if (!(await reader.open({ itemID, location }))) {
return;
}
this._readers.push(reader);
reader.onChangeSidebarWidth = (width) => {
this._sidebarWidth = width;
this._debounceSidebarWidthUpdate();
if (this.onChangeSidebarWidth) {
this.onChangeSidebarWidth(width);
}
};
reader.onChangeSidebarOpen = (open) => {
this._sidebarOpen = open;
this.setSidebarOpen(open);
if (this.onChangeSidebarOpen) {
this.onChangeSidebarOpen(open);
}
};
reader.onClose = () => {
this._readers.splice(this._readers.indexOf(reader), 1);
};

View file

@ -1199,8 +1199,6 @@ var ZoteroPane = new function()
// Rename tab
Zotero_Tabs.rename('zotero-pane', collectionTreeRow.getName());
ZoteroItemPane.updateStandaloneNotesList(true);
// Clear quick search and tag selector when switching views
document.getElementById('zotero-tb-search').value = "";
if (ZoteroPane.tagSelector) {
@ -5060,6 +5058,7 @@ var ZoteroPane = new function()
this.updateToolbarPosition();
this.updateTagsBoxSize();
ZoteroContextPane.update();
}
@ -5195,8 +5194,6 @@ var ZoteroPane = new function()
// Allow item pane to shrink to available height in stacked mode, but don't expand to be too
// wide when there's no persisted width in non-stacked mode
// TODO: Fix this together with stacked view
itemPane.setAttribute("flex", stackedLayout ? 1 : 0);
this.handleTagSelectorResize();

View file

@ -26,6 +26,7 @@
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/contextPane.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/overlay.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero-platform-version/content/style.css"?>
<?xul-overlay href="chrome://zotero/content/containers/containers.xul"?>
@ -43,6 +44,7 @@
<script src="include.js"/>
<script src="tabs.js"/>
<script src="zoteroPane.js" type="application/javascript"/>
<script src="contextPane.js"/>
<script src="fileInterface.js"/>
<script src="reportInterface.js"/>
<script src="timelineInterface.js"/>
@ -74,8 +76,20 @@
</commandset>
<stack id="zotero-pane-stack">
<vbox>
<vbox id="zotero-pane-horizontal-space">
<!-- A placeholder to persist pdf-reader sidebar collapse state to avoid introducing another pref -->
<box id="zotero-reader-sidebar-pane" hidden="true" collapsed="true" zotero-persist="collapsed"/>
<hbox>
<box id="zotero-tab-cover" hidden="true">
<label pack="center">&zotero.general.loading;</label>
</box>
<box id="zotero-tab-toolbar-container"/>
<deck id="tabs-deck" flex="1">
<vbox id="zotero-pane"
onkeydown="ZoteroPane_Local.handleKeyDown(event, this.id)"
onkeyup="ZoteroPane_Local.handleKeyUp(event, this.id)"
onkeypress="ZoteroPane_Local.handleKeyPress(event)"
chromedir="&locale.dir;">
<toolbar id="zotero-toolbar" class="toolbar toolbar-primary">
<hbox id="zotero-collections-toolbar" align="center">
<toolbarbutton id="zotero-tb-collection-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newCollection.label;" command="cmd_zotero_newCollection"/>
@ -161,7 +175,7 @@
oncommand="ZoteroPane_Local.search()"/>
</hbox>
<hbox id="zotero-item-toolbar" flex="1" align="center">
<hbox id="zotero-item-toolbar" flex="1" align="center" tooltip="html-tooltip">
<hbox align="center" pack="start" flex="1">
<toolbarbutton id="zotero-tb-locate" class="zotero-tb-button" tooltiptext="&zotero.toolbar.openURL.label;" type="menu">
<menupopup id="zotero-tb-locate-menu" onpopupshowing="Zotero_LocateMenu.buildLocateMenu()"/>
@ -214,7 +228,11 @@
<div xmlns="http://www.w3.org/1999/xhtml" class="sync-button-tooltip-messages"/>
</tooltip>
</toolbarbutton>
<button id="temp-toggle-1" style="min-width: 37px; padding: 2px; font-size: 13px;">[&#128196;] &#128210;</button>
<div id="zotero-tb-split" xmlns="http://www.w3.org/1999/xhtml" class="split-button hidden">
<div id="zotero-tb-toggle-item-pane" class="toolbarButton item" title="&zotero.toolbar.context.item;"><span></span></div>
<div id="zotero-tb-toggle-notes-pane" class="toolbarButton notes" title="&zotero.toolbar.context.notes;"><span></span></div>
</div>
</hbox>
</toolbar>
@ -225,18 +243,10 @@
<div id="retracted-items-close">×</div>
</div>
</vbox>
</vbox>
<box id="zotero-layout-switcher" orient="horizontal" zotero-persist="orient" flex="1">
<deck flex="1" id="tabs-deck">
<vbox id="zotero-pane"
onkeydown="ZoteroPane_Local.handleKeyDown(event, this.id)"
onkeyup="ZoteroPane_Local.handleKeyUp(event, this.id)"
onkeypress="ZoteroPane_Local.handleKeyPress(event)"
chromedir="&locale.dir;">
<popupset>
<!-- Allows iframes to show a tooltip popup for nodes with titles. `tooltip="iframeTooltip"` attribute has to be set for the iframe -->
<tooltip id="iframeTooltip" onpopupshowing="if (tooltipTitleNode = document.tooltipNode.closest('*[title]')) {this.setAttribute('label', tooltipTitleNode.getAttribute('title')); return true; } return false"/>
<!-- Allows to display a tooltip for 'title' attrbute for HTML nodes. Must be on an XUL node i.e. iframe or toolbar: "tooltip"="html-tooltip" -->
<tooltip id="html-tooltip" onpopupshowing="if (document.tooltipNode &amp;&amp; (tooltipTitleNode = document.tooltipNode.closest('*[title]'))) {this.setAttribute('label', tooltipTitleNode.getAttribute('title')); return true; } return false"/>
<menupopup id="zotero-collectionmenu"
oncommand="ZoteroPane.onCollectionContextMenuSelect(event)">
<!-- Keep order in sync with buildCollectionContextMenu, which adds additional attributes -->
@ -300,6 +310,8 @@
<menuitem class="menuitem-iconic zotero-menuitem-reindex" oncommand="ZoteroPane_Local.reindexItem();"/>
<!-- <menuitem class="menuitem-iconic zotero-menuitem-import-annotations" label="&zotero.items.menu.importAnnotations;" oncommand="ZoteroPane.importAnnotationsForSelected()"/>-->
<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()"/>
</menupopup>
<tooltip id="fake-tooltip"/>
@ -358,6 +370,7 @@
<grippy id="zotero-collections-grippy"/>
</splitter>
<box id="zotero-layout-switcher" orient="horizontal" zotero-persist="orient" flex="1">
<vbox id="zotero-items-pane" zotero-persist="width height" flex="1">
<deck id="zotero-items-pane-content" selectedIndex="0" flex="1">
<!-- Key navigation is handled by listener in itemTreeView.js -->
@ -375,202 +388,171 @@
zotero-persist="current-view-group">
<treecols id="zotero-items-columns-header">
<treecol
id="zotero-items-column-title" primary="true"
default-in="default feed"
id="zotero-items-column-title" primary="true" default-in="default feed"
label="&zotero.items.title_column;" ignoreincolumnpicker="true"
flex="4"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="4" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-firstCreator" default-in="default feed"
label="&zotero.items.creator_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-itemType"
label="&zotero.items.type_column;"
width="40"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
width="40" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-date" default-in="feed"
label="&zotero.items.date_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-year" disabled-in="feed"
label="&zotero.items.year_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-publisher"
label="&zotero.items.publisher_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-publicationTitle" disabled-in="feed"
label="&zotero.items.publication_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-journalAbbreviation" disabled-in="feed"
submenu="true"
label="&zotero.items.journalAbbr_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-language"
submenu="true"
label="&zotero.items.language_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-accessDate" disabled-in="feed"
submenu="true"
label="&zotero.items.accessDate_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-libraryCatalog" disabled-in="feed"
submenu="true"
label="&zotero.items.libraryCatalog_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-callNumber" disabled-in="feed"
submenu="true"
label="&zotero.items.callNumber_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-rights"
submenu="true"
label="&zotero.items.rights_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-dateAdded" disabled-in="feed"
label="&zotero.items.dateAdded_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-dateModified" disabled-in="feed"
label="&zotero.items.dateModified_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-archive" disabled-in="feed"
submenu="true"
label="&zotero.items.archive_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-archiveLocation" disabled-in="feed"
submenu="true"
label="&zotero.items.archiveLocation_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-place" disabled-in="feed"
submenu="true"
label="&zotero.items.place_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-volume" disabled-in="feed"
submenu="true"
label="&zotero.items.volume_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-edition" disabled-in="feed"
submenu="true"
label="&zotero.items.edition_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-pages" disabled-in="feed"
submenu="true"
label="&zotero.items.pages_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-issue" disabled-in="feed"
submenu="true"
label="&zotero.items.issue_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-series" disabled-in="feed"
submenu="true"
label="&zotero.items.series_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-seriesTitle" disabled-in="feed"
submenu="true"
label="&zotero.items.seriesTitle_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-court" disabled-in="feed"
submenu="true"
label="&zotero.items.court_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-medium" disabled-in="feed"
submenu="true"
label="&zotero.items.medium_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-genre" disabled-in="feed"
submenu="true"
label="&zotero.items.genre_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-system" disabled-in="feed"
submenu="true"
label="&zotero.items.system_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-extra" disabled-in="feed"
label="&zotero.items.extra_column;"
flex="1"
zotero-persist="width ordinal hidden sortActive sortDirection"/>
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol
id="zotero-items-column-hasAttachment" default-in="default"
disabled-in="feed"
id="zotero-items-column-hasAttachment" default-in="default" disabled-in="feed"
class="treecol-image"
label="&zotero.tabs.attachments.label;"
fixed="true"
@ -598,10 +580,6 @@
</deck>
</vbox>
</hbox>
</vbox>
</deck>
<splitter id="zotero-items-splitter" resizebefore="closest" resizeafter="closest" collapse="after" orient="horizontal" zotero-persist="state orient"
onmousemove="ZoteroPane.updateToolbarPosition(); ZoteroPane.updateTagsBoxSize()"
oncommand="ZoteroPane.updateToolbarPosition(); ZoteroPane.updateTagsBoxSize()">
@ -611,8 +589,45 @@
<!-- itemPane.xul -->
<vbox id="zotero-item-pane"/>
</box>
</hbox>
</vbox>
</deck>
<splitter id="zotero-context-splitter"
hidden="true"
state="collapsed"
resizebefore="closest"
resizeafter="closest"
collapse="after"
orient="horizontal"
zotero-persist="state"
onmousemove="ZoteroContextPane.update()"
oncommand="ZoteroContextPane.update()"
>
<grippy/>
</splitter>
<!-- contextPane -->
<box id="zotero-context-pane" flex="0" collapsed="true" zotero-persist="width">
<vbox flex="1" style="width: 100%;height: 100%;">
<box class="stacked-context-placeholder" flex="1"/>
<splitter id="zotero-context-splitter-stacked"
state="collapsed"
class="stacked-splitter"
orient="vertical"
collapse="after"
zotero-persist="state"
resizebefore="clsest"
resizeafter="closest"
onmousemove="ZoteroContextPane.update()"
oncommand="ZoteroContextPane.update()"
>
<grippy/>
</splitter>
<hbox id="zotero-context-pane-inner" flex="1" zotero-persist="height"/>
</vbox>
</box>
</hbox>
<!-- Barrier to prevent tabbing into Zotero pane when busy -->
<box id="zotero-pane-tab-catcher-bottom" hidden="true" align="center" pack="center" style="opacity: 0">

View file

@ -156,6 +156,8 @@
<!ENTITY zotero.toolbar.attachment.add "Store Copy of File…">
<!ENTITY zotero.toolbar.attachment.weblink "Save Link to Current Page">
<!ENTITY zotero.toolbar.attachment.snapshot "Take Snapshot of Current Page">
<!ENTITY zotero.toolbar.context.item "Item">
<!ENTITY zotero.toolbar.context.notes "Notes">
<!ENTITY zotero.tagSelector.noTagsToDisplay "No tags to display">
<!ENTITY zotero.tagSelector.loadingTags "Loading tags…">

View file

@ -401,6 +401,8 @@ pane.item.related.count.singular = %S related:
pane.item.related.count.plural = %S related:
pane.item.parentItem = Parent Item:
pane.context.noParent = No parent item
noteEditor.editNote = Edit Note
itemTypes.note = Note

View file

@ -0,0 +1,71 @@
#tabs-deck {
min-width: 600px;
}
#zotero-context-pane {
min-width: 300px;
}
#zotero-context-pane.stacked {
position: fixed;
right: 0;
top: 0;
bottom: 0;
font-size: 0;
pointer-events: none;
}
#zotero-context-pane.stacked #zotero-context-toolbar-extension {
display: none;
}
#zotero-context-pane .stacked-context-placeholder {
min-height: 300px;
}
#zotero-context-pane.standard .stacked-context-placeholder {
display: none;
}
#zotero-context-splitter-stacked {
pointer-events: auto;
}
#zotero-context-pane-inner {
width: 0;
font: message-box;
min-height: 100px;
height: 100%;
pointer-events: auto;
}
/*#zotero-context-pane.standard .stacked-splitter {*/
/* display: none;*/
/*}*/
#zotero-tab-toolbar-container {
position: fixed;
z-index: 1;
right: 0;
height: 32px;
-moz-appearance: none;
background: linear-gradient(to top, #aeaeae 0, #aeaeae 1px, #d3d3d3 1px, #e3e3e3 100%);
}
#zotero-tab-toolbar-container #zotero-tb-locate,
#zotero-tab-toolbar-container #zotero-pq-buttons {
display: none;
}
#zotero-context-toolbar-extension {
height: 32px;
/* To cover the splitter that has a higher stacking order than our parent */
opacity: 0.99;
width: 100%;
margin-inline-start: -5px;
background: linear-gradient(to top, #aeaeae 0, #aeaeae 1px, #d3d3d3 1px, #e3e3e3 100%);
}
.zotero-context-notes-list {
padding-top: 5px;
}

View file

@ -45,51 +45,13 @@
margin-left: 5px;
}
#zotero-item-pane[data-type='library'] #item-pane-title,
#zotero-item-pane[data-mode='notes'] #item-pane-title {
display: none;
}
#item-pane-title {
font-size: 16px;
padding: 0 10px 6px;
}
#zotero-item-pane-contextual-deck .item-pane-back-button {
font-size: 12px;
margin: 4px 5px 5px;
}
#zotero-item-pane-pin-deck .item-pane-back-button {
font-size: 12px;
margin: 7px 6px;
}
#zotero-item-pane-padding-top {
/*border-bottom: 1px solid lightgray;*/
}
#zotero-item-pane-pinned-note {
.zotero-context-pane-pinned-note {
border-top: 1px solid #d9d9d9;
}
.zotero-editpane-tabs {
background: #ececec;
}
#temp-toggle-1.mode-item, #temp-toggle-2.mode-item {
list-style-image: url(chrome://zotero/skin/treeitem.png);
}
#temp-toggle-1.mode-notes, #temp-toggle-2.mode-notes {
list-style-image: url(chrome://zotero/skin/treeitem-note.png);
}
.notes-list-label {
font-size: 16px;
font-weight: bold;
padding: 8px 10px 5px;
}
/*.zotero-editpane-tabs {*/
/* background: #ececec;*/
/*}*/
#retraction-box {
cursor: default;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -708,9 +708,23 @@
display: inherit !important;
}
#zotero-tab-cover {
z-index: 2;
left: 0;
right: 0;
background-color: #ececec;
position: fixed;
height: 100%;
}
#zotero-tab-cover label {
padding-top: 30px;
display: flex;
text-align: center;
}
.zotero-box {
/*margin-left: 5px;*/
margin-left: 5px;
}
.zotero-box-icon {

@ -1 +1 @@
Subproject commit 1da855a9e2447ff0a1409401bc2ba87f9b307038
Subproject commit be1fa9a77fb9090a1aad9a9cd40b8ebb22ee4281

@ -1 +1 @@
Subproject commit 886e714d3344534b66370010e855ea857f460627
Subproject commit e3121e75eb82df8bea6381d390983917bd44395b

View file

@ -11,6 +11,7 @@
@import "abstracts/mixins";
@import "abstracts/placeholders";
@import "abstracts/utilities";
@import "abstracts/split-button";
// Theme
// --------------------------------------------------

View file

@ -0,0 +1,489 @@
// The split button was originally created by flachware for pdf-reader:
// https://github.com/zotero/pdf-reader/blob/master/src/stylesheets/abstracts/mixins/_split-button.scss
$accent-color: #0a6cf5; // 215° 96 96
$accent-color-darken-6: darken($accent-color, 6%);
$blue-btn: #90c8f6;
$toolbar-btn-height: 22px;
$toolbar-btn-bg: linear-gradient(to bottom, #fafafa, #f2f2f2);
$toolbar-btn-padding: 3px 11px;
$toolbar-btn-border: 0;
$toolbar-btn-border-radius: 3.75px;
$toolbar-btn-margin-x: 4px;
$toolbar-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.33), 0 0 0 1px rgba(0, 0, 0, 0.08), 0 1px 0 rgba(0, 0, 0, 0.1);
$toolbar-btn-box-shadow-2x: inset 0 0.5px 0.5px rgba(255, 255, 255, 1), 0 0 0 0.5px rgba(0, 0, 0, 0.1), 0 0.75px 0 rgba(0, 0, 0, 0.125);
$toolbar-btn-focus-box-shadow: 0 0 0 4px rgba($accent-color, 0.5), $toolbar-btn-box-shadow;
$toolbar-btn-focus-box-shadow-2x: 0 0 0 4px rgba($accent-color, 0.5), $toolbar-btn-box-shadow-2x;
$toolbar-btn-hover-bg: null;
$toolbar-btn-border-hover-color: null;
$toolbar-btn-active-bg: linear-gradient(to bottom, #e4e4e4, #ddd);
$toolbar-btn-blurred-bg: transparent;
$toolbar-btn-blurred-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.11);
$toolbar-btn-border-active-color: $blue-btn;
$toolbar-btn-icon-active-offset: 0;
//
// String functions
//
@function str-replace($string, $token, $replace: "") {
$i: str-index($string, $token);
@while (str-index($string, $token) != null) {
$first-part: str-slice($string, 1, ($i - 1));
$last-part: str-slice($string, ($i + str-length($token)));
$string: str-slice($string, 1, ($i - 1)) + $replace + $last-part;
$i: str-index($string, $token);
}
@return $string;
}
@function str-parse($string, $token) {
$i: str-index($string, $token);
@if $i {
$first-part: str-slice($string, 1, ($i - 1));
$string: str-parse(str-slice($string, ($i + str-length($token))), $token);
@return join(simple-selectors($first-part), $string);
}
@return simple-selectors($string);
}
// State mixin
@mixin state($states...) {
@each $state in $states {
$string: $state;
@each $token in ">", "+", "~" {
$string: str-replace($string, $token, " ");
}
$string: str-replace($string, " ", " ");
$selectors: str-parse($string, " ");
@each $sel in $selectors {
@if str-index("#{&}", $sel) != null {
@at-root #{selector-replace(&, $sel, $state)} {
@content;
}
}
}
}
}
// Variant mixin (alias)
@mixin variant($args...) {
@include state($args...) {
@content;
}
}
//
// Media queries
//
@mixin retina {
@media screen and (min-resolution: 1.25dppx) {
@content;
}
}
//
// Asset URLs
//
@function asset-url($type, $path) {
@return url(unquote("#{$type}/#{$path}"));
}
@function icon-url($path) {
@return asset-url("chrome://zotero/skin", $path);
}
@function image-url($path) {
@return asset-url("chrome://zotero/skin", $path);
}
//
// Icon
//
@mixin icon($file-name, $size: 16px) {
&::before {
content: "";
display: inline-block;
vertical-align: top;
width: $size;
height: $size;
background-image: icon-url("#{$file-name}.png");
background-size: 100%;
@include retina {
background-image: icon-url("#{$file-name}@2x.png");
}
}
}
.toolbarButton::before {
position: relative;
z-index: 2;
}
.toolbarButton > span:first-child {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.toolbarButton {
height: $toolbar-btn-height;
background: $toolbar-btn-bg;
padding: $toolbar-btn-padding;
border: $toolbar-btn-border;
border-radius: $toolbar-btn-border-radius;
margin: 0 $toolbar-btn-margin-x;
position: relative;
box-shadow: $toolbar-btn-box-shadow;
@include retina {
box-shadow: $toolbar-btn-box-shadow-2x;
}
&:focus-visible {
box-shadow: $toolbar-btn-focus-box-shadow;
z-index: 1;
@include retina {
box-shadow: $toolbar-btn-focus-box-shadow-2x;
}
}
&:-moz-focusring {
box-shadow: $toolbar-btn-focus-box-shadow;
z-index: 1;
@include retina {
box-shadow: $toolbar-btn-focus-box-shadow-2x;
}
}
&:hover {
background: $toolbar-btn-hover-bg;
border-color: $toolbar-btn-border-hover-color;
}
&:active,
&.active {
background: $toolbar-btn-active-bg;
border-color: $toolbar-btn-border-active-color;
}
&[disabled] {
pointer-events: none;
&::before {
opacity: 0.5;
}
}
&::before {
position: relative;
z-index: 2;
@include state(".toolbarButton:active", ".toolbarButton.active") {
top: $toolbar-btn-icon-active-offset;
}
}
// Label for screen readers
> span:first-child {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
}
.split-button {
display: flex;
}
//
// Split button
//
@mixin split-button($height, $button, $padding-x, $padding-y) {
height: $height;
padding: $padding-y $padding-x $padding-y ($padding-x + 1px);
background: none;
border-radius: 0;
box-shadow: none;
position: relative;
&:focus-visible {
z-index: 2; // Chrome
&::after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: 0;
box-shadow: inset 0 0 0 1px rgba($accent-color, 0.5), 0 0 0 3px rgba($accent-color, 0.5);
@include retina {
box-shadow: inset 0 0 0 1px rgba($accent-color, 0.5), 0 0 0 2.5px rgba($accent-color, 0.5);
}
}
}
&:-moz-focusring {
&::after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: 0;
box-shadow: inset 0 0 0 1px rgba($accent-color, 0.5), 0 0 0 3px rgba($accent-color, 0.5);
z-index: 2; // Firefox
@include retina {
box-shadow: inset 0 0 0 1px rgba($accent-color, 0.5), 0 0 0 2.5px rgba($accent-color, 0.5);
}
}
}
&:first-child {
margin-right: 0;
background: none;
&:focus-visible {
&::after {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
}
&:-moz-focusring {
&::after {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
}
}
&:last-child {
padding: $padding-y ($padding-x + 1px);
margin-left: 0;
background: image-url("mac/#{$button}-end.png") no-repeat right top;
@include retina {
background: image-url("mac/#{$button}-end@2x.png") no-repeat right top / auto $height;
}
&:active {
background: image-url("mac/#{$button}-end-pressed.png") no-repeat right top;
@include retina {
background: image-url("mac/#{$button}-end-pressed@2x.png") no-repeat right top / auto $height;
}
}
@include state(".toolbarButton.toggled") {
background: image-url("mac/#{$button}-end-active.png") no-repeat right top;
@include retina {
background: image-url("mac/#{$button}-end-active@2x.png") no-repeat right top / auto $height;
}
@include state(".toolbarButton:active") {
background: image-url("mac/#{$button}-end-active-pressed.png") no-repeat right top;
@include retina {
background: image-url("mac/#{$button}-end-active-pressed@2x.png") no-repeat right top / auto $height;
}
}
}
&:focus-visible {
&::after {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
}
&:-moz-focusring {
&::after {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
}
}
&:not(:first-child) {
margin-left: 0;
}
&:not(:last-child) {
margin-right: 0;
}
> span:first-child {
clip: initial;
margin: initial;
left: 0;
top: 0;
width: 100%;
height: 100%;
text-indent: -99em;
background:
linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)) no-repeat left center / 1px ($height - 2px),
image-url("mac/#{$button}-start.png") no-repeat center top;
@include retina {
background:
linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)) no-repeat left center / 1px ($height - 2px),
image-url("mac/#{$button}-start@2x.png") no-repeat center top / auto $height;
}
@include state(".toolbarButton:active") {
background: image-url("mac/#{$button}-start-pressed.png") no-repeat center top;
@include retina {
background: image-url("mac/#{$button}-start-pressed@2x.png") no-repeat center top / auto $height;
}
}
@include state(".toolbarButton.toggled") {
background: image-url("mac/#{$button}-start-active.png") no-repeat center top;
@include retina {
background: image-url("mac/#{$button}-start-active@2x.png") no-repeat center top / auto $height;
}
@include state(".toolbarButton:active") {
background: image-url("mac/#{$button}-start-active-pressed.png") no-repeat center top;
@include retina {
background: image-url("mac/#{$button}-start-active-pressed@2x.png") no-repeat center top / auto $height;
}
}
}
@include state(".toolbarButton.toggled + .toolbarButton:not(:active)") {
background: image-url("mac/#{$button}-start.png") no-repeat center top;
@include retina {
background: image-url("mac/#{$button}-start@2x.png") no-repeat center top / auto $height;
}
}
@include variant(".toolbarButton:first-child") {
background: image-url("mac/#{$button}-start.png") no-repeat left top;
@include retina {
background: image-url("mac/#{$button}-start@2x.png") no-repeat left top / auto $height;
}
@include state(".toolbarButton:active") {
background: image-url("mac/#{$button}-start-pressed.png") no-repeat left top;
@include retina {
background: image-url("mac/#{$button}-start-pressed@2x.png") no-repeat left top / auto $height;
}
}
@include state(".toolbarButton.toggled") {
background: image-url("mac/#{$button}-start-active.png") no-repeat left top;
@include retina {
background: image-url("mac/#{$button}-start-active@2x.png") no-repeat left top / auto $height;
}
@include state(".toolbarButton:active") {
background: image-url("mac/#{$button}-start-active-pressed.png") no-repeat left top;
@include retina {
background: image-url("mac/#{$button}-start-active-pressed@2x.png") no-repeat left top / auto $height;
}
}
}
}
@include variant(".toolbarButton:last-child") {
width: calc(100% - 17px);
}
}
}
// Move this out from mixins
#zotero-tb-split {
$item-tool-icon: "mac/item";
$item-tool-icon-active: "mac/item-white";
$notes-tool-icon: "mac/notes";
$notes-tool-icon-active: "mac/notes-white";
&.hidden {
display: none;
}
.item {
@include icon($item-tool-icon);
&.toggled {
@include icon($item-tool-icon-active);
}
}
.notes {
@include icon($notes-tool-icon);
&.toggled {
@include icon($notes-tool-icon-active);
}
}
.toolbarButton {
box-sizing: border-box;
@include split-button(
$height: 24px,
$button: "menubutton",
$padding-x: 11px,
$padding-y: 4px
);
}
}

View file

@ -24,9 +24,31 @@
}
.inner {
> *:not(:first-child) {
margin-top: 3px;
}
.first-line {
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 {
flex-grow: 1;
width: 0;
@ -37,10 +59,11 @@
}
}
.second-line {
.third-line {
display: flex;
.date {
color: $shade-6;
}
.body {

View file

@ -44,7 +44,6 @@
list-style: none;
margin: 0;
padding: 2px 0 0; // Leave space for textbox border on top tag
overflow-y: auto;
}
ul.tags-box-list > li {

@ -1 +1 @@
Subproject commit f4374cdb6386ba5cdd818ff6e3ece74aae83b394
Subproject commit a271598218fa8f52cd6d571d5c1b28e913803409