zotero/chrome/content/zotero/contextPane.js
Abe Jellinek f81b4b071f fx-compat: Correct create[XUL]Element[NS]() calls
This fixes the dictionary manager and various other places where a XUL
element was being created with createElement(), and also simplifies a lot
of createElementNS(HTML_NS, ...) calls.

This might cause some minor regressions but everything tested has
worked. Not touching preferences (#2659) or bindings.
2022-06-29 11:14:40 -04:00

882 lines
26 KiB
JavaScript

/*
***** 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 issues that might be
// related with `require` not reusing the context
var React = require('react');
var ReactDOM = require('react-dom');
var NotesList = require('components/itemPane/notesList').default;
var ZoteroContextPane = new function () {
var _tabCover;
var _contextPane;
var _contextPaneInner;
var _contextPaneSplitter;
var _contextPaneSplitterStacked;
var _itemToggle;
var _notesToggle;
var _panesDeck;
var _itemPaneDeck;
var _notesPaneDeck;
var _splitButton;
var _itemPaneToggle;
var _notesPaneToggle;
var _tabToolbar;
var _itemContexts = [];
var _notesContexts = [];
// Using attribute instead of property to set 'selectedIndex'
// is more reliable
this.update = _update;
this.getActiveEditor = _getActiveEditor;
this.focus = _focus;
this.init = 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');
_splitButton = document.getElementById('zotero-tb-split');
_itemPaneToggle = document.getElementById('zotero-tb-toggle-item-pane');
_notesPaneToggle = document.getElementById('zotero-tb-toggle-notes-pane');
_tabToolbar = document.getElementById('zotero-tab-toolbar');
if (Zotero.rtl) {
_tabToolbar.style.left = 0;
_splitButton.style.transform = 'scaleX(-1)';
}
else {
_tabToolbar.style.right = 0;
}
// vbox
var vbox = document.createXULElement('vbox');
vbox.setAttribute('flex', '1');
_contextPaneInner.append(vbox);
// Toolbar extension
var toolbarExtension = document.createXULElement('box');
toolbarExtension.style.height = '32px';
toolbarExtension.id = 'zotero-context-toolbar-extension';
_panesDeck = document.createXULElement('deck');
_panesDeck.setAttribute('flex', 1);
_panesDeck.setAttribute('selectedIndex', 0);
vbox.append(toolbarExtension, _panesDeck);
// Item pane deck
_itemPaneDeck = document.createXULElement('deck');
// Notes pane deck
_notesPaneDeck = document.createXULElement('deck');
_notesPaneDeck.style.backgroundColor = 'white';
_notesPaneDeck.setAttribute('flex', 1);
_notesPaneDeck.className = 'notes-pane-deck';
_panesDeck.append(_itemPaneDeck, _notesPaneDeck);
this._notifierID = Zotero.Notifier.registerObserver(this, ['item', 'tab'], 'contextPane');
window.addEventListener('resize', _update);
_itemToggle.addEventListener('click', _toggleItemButton);
_notesToggle.addEventListener('click', _toggleNotesButton);
Zotero.Reader.onChangeSidebarWidth = _updatePaneWidth;
Zotero.Reader.onChangeSidebarOpen = _updatePaneWidth;
};
this.destroy = function () {
_itemToggle.removeEventListener('click', _toggleItemButton);
_notesToggle.removeEventListener('click', _toggleNotesButton);
window.removeEventListener('resize', _update);
Zotero.Notifier.unregisterObserver(this._notifierID);
Zotero.Reader.onChangeSidebarWidth = () => {};
Zotero.Reader.onChangeSidebarOpen = () => {};
_contextPaneInner.innerHTML = '';
_itemContexts = [];
_notesContexts = [];
};
this.notify = function (action, type, ids, extraData) {
if (type == 'item') {
// Update, remove or re-create item panes
for (let context of _itemContexts.slice()) {
let item = Zotero.Items.get(context.itemID);
if (!item) {
_removeItemContext(context.tabID);
}
else if (item.parentID != context.parentID) {
_removeItemContext(context.tabID);
_addItemContext(context.tabID, context.itemID);
}
else {
context.update();
}
}
// Update notes lists for affected libraries
let libraryIDs = [];
for (let id of ids) {
let item = Zotero.Items.get(id);
if (item && (item.isNote() || item.isRegularItem())) {
libraryIDs.push(item.libraryID);
}
else if (action == 'delete') {
libraryIDs.push(extraData[id].libraryID);
}
}
for (let context of _notesContexts) {
if (libraryIDs.includes(context.libraryID)) {
context.affectedIDs = new Set([...context.affectedIDs, ...ids]);
context.update();
}
}
}
else if (type == 'tab') {
if (action == 'add') {
_addItemContext(ids[0], extraData[ids[0]].itemID);
}
else if (action == 'close') {
_removeItemContext(ids[0]);
if (Zotero_Tabs.deck.children.length == 1) {
_notesContexts.forEach(x => x.notesListRef.current.setExpanded(false));
}
}
else if (action == 'select') {
// It seems that changing `hidden` or `collapsed` values might
// be related with significant slow down when there are too many
// DOM nodes (i.e. 10k notes)
if (Zotero_Tabs.selectedType == 'library') {
_contextPaneSplitter.setAttribute('hidden', true);
_contextPane.setAttribute('collapsed', true);
_tabToolbar.hidden = true;
_tabCover.classList.add('hidden');
}
else if (Zotero_Tabs.selectedType == 'reader') {
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
_tabCover.classList.remove('hidden');
(async () => {
await reader._initPromise;
_tabCover.classList.add('hidden');
// Focus reader pages view if context pane note editor is not selected
if (Zotero_Tabs.selectedID == reader.tabID
&& (!document.activeElement
|| !document.activeElement.closest('.context-node iframe[anonid="editor-view"]'))) {
reader.focus();
}
var attachment = await Zotero.Items.getAsync(reader.itemID);
if (attachment) {
_selectNotesContext(attachment.libraryID);
var notesContext = _getNotesContext(attachment.libraryID);
notesContext.updateFromCache();
}
})();
}
_contextPaneSplitter.setAttribute('hidden', false);
// It seems that on heavy load (i.e. syncing) the line below doesn't set the correct value
setTimeout(() => {
_contextPane.setAttribute('collapsed', !(_contextPaneSplitter.getAttribute('state') != 'collapsed'));
});
_tabToolbar.hidden = false;
}
_selectItemContext(ids[0]);
_update();
}
}
};
function _toggleItemButton() {
_togglePane(0);
}
function _toggleNotesButton() {
_togglePane(1);
}
function _getActiveEditor() {
var splitter;
if (Zotero.Prefs.get('layout') == 'stacked') {
splitter = _contextPaneSplitterStacked;
}
else {
splitter = _contextPaneSplitter;
}
if (splitter.getAttribute('state') != 'collapsed') {
if (_panesDeck.selectedIndex == 1) {
var node = _notesPaneDeck.selectedPanel;
if (node.selectedIndex == 1) {
return node.querySelector('note-editor');
}
}
}
}
function _focus() {
var splitter;
if (Zotero.Prefs.get('layout') == 'stacked') {
splitter = _contextPaneSplitterStacked;
}
else {
splitter = _contextPaneSplitter;
}
if (splitter.getAttribute('state') != 'collapsed') {
if (_panesDeck.selectedIndex == 0) {
var node = _itemPaneDeck.selectedPanel;
node.querySelector('tab[selected]').focus();
return true;
}
else {
var node = _notesPaneDeck.selectedPanel;
if (node.selectedIndex == 0) {
node.querySelector('search-textbox').focus();
return true;
}
else {
node.querySelector('note-editor').focusFirst();
return true;
}
}
}
return false;
}
function _updateAddToNote() {
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
var editor = _getActiveEditor();
var libraryReadOnly = editor && editor.item && _isLibraryReadOnly(editor.item.libraryID);
var noteReadOnly = editor && editor.item
&& (editor.item.deleted || editor.item.parentItem && editor.item.parentItem.deleted);
reader.enableAddToNote(!!editor && !libraryReadOnly && !noteReadOnly);
}
}
function _updatePaneWidth() {
var stacked = Zotero.Prefs.get('layout') == 'stacked';
var width = Zotero.Reader.getSidebarWidth() + 'px';
if (!Zotero.Reader.getSidebarOpen()) {
width = 0;
}
if (Zotero.rtl) {
_contextPane.style.left = 0;
_contextPane.style.right = stacked ? width : 'unset';
}
else {
_contextPane.style.left = stacked ? width : 'unset';
_contextPane.style.right = 0;
}
}
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(_tabToolbar.offsetWidth);
}
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.getBoundingClientRect().height;
}
Zotero.Reader.setBottomPlaceholderHeight(height);
}
_updatePaneWidth();
_updateToolbarWidth();
_updateAddToNote();
}
function _togglePane(paneIndex) {
var splitter = Zotero.Prefs.get('layout') == 'stacked'
? _contextPaneSplitterStacked : _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);
}
splitter.setAttribute('state', hide ? 'collapsed' : 'open');
_update();
if (!hide) {
ZoteroContextPane.focus();
}
}
function _getCurrentAttachment() {
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
return Zotero.Items.get(reader.itemID);
}
}
function _addNotesContext(libraryID) {
let readOnly = _isLibraryReadOnly(libraryID);
var list = document.createXULElement('vbox');
list.setAttribute('flex', 1);
list.className = 'zotero-context-notes-list';
var noteContainer = document.createXULElement('vbox');
var title = document.createXULElement('vbox');
title.className = 'zotero-context-pane-editor-parent-line';
var editor = new (customElements.get('note-editor'));
editor.className = 'zotero-context-pane-pinned-note';
editor.setAttribute('flex', 1);
noteContainer.append(title, editor);
let contextNode = document.createXULElement('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.createXULElement('hbox');
head.style.display = 'flex';
async function _createNoteFromAnnotations(child) {
var attachment = _getCurrentAttachment();
if (!attachment) {
return;
}
var note = await Zotero.EditorInstance.createNoteFromAnnotations(
attachment.getAnnotations().filter(x => x.annotationType != 'ink'), child && attachment.parentID
);
_updateAddToNote();
input.value = '';
_updateNotesList();
_setPinnedNote(note.id);
}
function _createNote(child) {
contextNode.setAttribute('selectedIndex', 1);
var item = new Zotero.Item('note');
item.libraryID = libraryID;
if (child) {
var attachment = _getCurrentAttachment();
if (!attachment) {
return;
}
item.parentID = attachment.parentID;
}
editor.mode = 'edit';
editor.item = item;
editor.parentItem = null;
editor.focus();
_updateAddToNote();
input.value = '';
_updateNotesList();
}
var vbox = document.createXULElement('vbox');
vbox.style.flex = '1';
var input = document.createXULElement('search-textbox');
input.style.margin = '4px 7px';
input.setAttribute('type', 'search');
input.setAttribute('timeout', '250');
input.addEventListener('command', () => {
notesListRef.current.setExpanded(false);
_updateNotesList();
});
vbox.append(input);
head.append(vbox);
var listBox = document.createXULElement('vbox');
listBox.style.display = 'flex';
listBox.setAttribute('flex', '1');
var listInner = document.createElement('div');
listInner.className = 'notes-list-container';
// Otherwise it can be focused with tab
listInner.tabIndex = -1;
listBox.append(listInner);
list.append(head, listBox);
var notesListRef = React.createRef();
function _isVisible() {
let splitter = Zotero.Prefs.get('layout') == 'stacked'
? _contextPaneSplitterStacked : _contextPaneSplitter;
return Zotero_Tabs.selectedID != 'zotero-pane'
&& _panesDeck.selectedIndex == 1
&& context.node.selectedIndex == 0
&& splitter.getAttribute('state') != 'collapsed';
}
async function _updateNotesList(useCached) {
var query = input.value;
var notes;
// Calls itself and debounces until notes list becomes
// visible, and then updates
if (!useCached && !_isVisible()) {
context.update();
return;
}
if (useCached && context.cachedNotes.length) {
notes = context.cachedNotes;
}
else {
await Zotero.Schema.schemaUpdatePromise;
var s = new Zotero.Search();
s.addCondition('libraryID', 'is', libraryID);
s.addCondition('itemType', 'is', 'note');
if (query) {
let parts = Zotero.SearchConditions.parseSearchString(query);
for (let part of parts) {
s.addCondition('note', 'contains', part.text);
}
}
notes = await s.search();
notes = Zotero.Items.get(notes);
if (Zotero.Prefs.get('sortNotesChronologically.reader')) {
notes.sort((a, b) => {
a = a.dateModified;
b = b.dateModified;
return (a > b ? -1 : (a < b ? 1 : 0));
});
}
else {
let collation = Zotero.getLocaleCollation();
notes.sort((a, b) => {
let aTitle = Zotero.Items.getSortTitle(a.getNoteTitle());
let bTitle = Zotero.Items.getSortTitle(b.getNoteTitle());
return collation.compareString(1, aTitle, bTitle);
});
}
let cachedNotesIndex = new Map();
for (let cachedNote of context.cachedNotes) {
cachedNotesIndex.set(cachedNote.id, cachedNote);
}
notes = notes.map(note => {
var parentItem = note.parentItem;
// If neither note nor parent item is affected try to return the cached note
if (!context.affectedIDs.has(note.id)
&& (!parentItem || !context.affectedIDs.has(parentItem.id))) {
let cachedNote = cachedNotesIndex.get(note.id);
if (cachedNote) {
return cachedNote;
}
}
var text = note.note;
text = Zotero.Utilities.unescapeHTML(text);
text = text.trim();
text = text.slice(0, 500);
var parts = text.split('\n').map(x => x.trim()).filter(x => x.length);
var title = parts[0] && parts[0].slice(0, Zotero.Notes.MAX_TITLE_LENGTH);
var date = Zotero.Date.sqlToDate(note.dateModified, true);
date = Zotero.Date.toFriendlyDate(date);
return {
id: note.id,
title: title || Zotero.getString('pane.item.notes.untitled'),
body: parts[1] || '',
date,
parentID: note.parentID,
parentItemType: parentItem && parentItem.itemType,
parentTitle: parentItem && parentItem.getDisplayTitle()
};
});
context.cachedNotes = notes;
context.affectedIDs = new Set();
}
var attachment = _getCurrentAttachment();
var parentID = attachment && attachment.parentID;
notesListRef.current.setHasParent(!!parentID);
notesListRef.current.setNotes(notes.map(note => ({
...note,
isCurrentChild: parentID && note.parentID == parentID
})));
}
var context = {
libraryID,
node: contextNode,
editor,
notesListRef,
cachedNotes: [],
affectedIDs: new Set(),
update: Zotero.Utilities.throttle(_updateNotesList, 1000, { leading: false }),
updateFromCache: () => _updateNotesList(true)
};
function _handleListPopupClick(id, event) {
switch (event.originalTarget.id) {
case 'context-pane-list-show-in-library':
ZoteroPane_Local.selectItem(id);
Zotero_Tabs.select('zotero-pane');
break;
case 'context-pane-list-edit-in-window':
ZoteroPane_Local.openNoteWindow(id);
break;
case 'context-pane-list-move-to-trash':
if (!readOnly) {
Zotero.Items.trashTx(id);
context.cachedNotes = context.cachedNotes.filter(x => x.id != id);
_updateNotesList(true);
}
break;
default:
}
}
function _handleAddChildNotePopupClick(event) {
if (readOnly) {
return;
}
switch (event.originalTarget.id) {
case 'context-pane-add-child-note':
_createNote(true);
break;
case 'context-pane-add-child-note-from-annotations':
_createNoteFromAnnotations(true);
break;
default:
}
}
function _handleAddStandaloneNotePopupClick(event) {
if (readOnly) {
return;
}
switch (event.originalTarget.id) {
case 'context-pane-add-standalone-note':
_createNote();
break;
case 'context-pane-add-standalone-note-from-annotations':
_createNoteFromAnnotations();
break;
default:
}
}
ReactDOM.render(
<NotesList
ref={notesListRef}
onClick={(id) => {
_setPinnedNote(id);
}}
onContextMenu={(id, event) => {
document.getElementById('context-pane-list-move-to-trash').setAttribute('disabled', readOnly);
var popup = document.getElementById('context-pane-list-popup');
popup.onclick = (event) => _handleListPopupClick(id, event);
popup.openPopupAtScreen(event.screenX, event.screenY);
}}
onAddChildButtonDown={(event) => {
document.getElementById('context-pane-add-child-note').setAttribute('disabled', readOnly);
document.getElementById('context-pane-add-child-note-from-annotations').setAttribute('disabled', readOnly);
var popup = document.getElementById('context-pane-add-child-note-button-popup');
popup.onclick = _handleAddChildNotePopupClick;
popup.openPopup(event.target, 'after_end');
}}
onAddStandaloneButtonDown={(event) => {
document.getElementById('context-pane-add-standalone-note').setAttribute('disabled', readOnly);
document.getElementById('context-pane-add-standalone-note-from-annotations').setAttribute('disabled', readOnly);
var popup = document.getElementById('context-pane-add-standalone-note-button-popup');
popup.onclick = _handleAddStandaloneNotePopupClick;
popup.openPopup(event.target, 'after_end');
}}
/>,
listInner,
() => {
_updateNotesList();
}
);
_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 _isLibraryReadOnly(libraryID) {
return !Zotero.Libraries.get(libraryID).editable;
}
function _setPinnedNote(itemID) {
var item = Zotero.Items.get(itemID);
if (!item) {
return;
}
var readOnly = _isLibraryReadOnly(item.libraryID);
var context = _getNotesContext(item.libraryID);
if (context) {
var { editor, node } = context;
node.setAttribute('selectedIndex', 1);
editor.mode = readOnly ? 'view' : 'edit';
editor.item = item;
editor.parentItem = null;
node.querySelector('.zotero-context-pane-editor-parent-line').innerHTML = '';
var parentItem = item.parentItem;
if (parentItem) {
var container = document.createElement('div');
var img = document.createElement('img');
img.src = Zotero.ItemTypes.getImageSrc(parentItem.itemType);
img.className = 'parent-item-type';
var title = document.createElement('div');
title.append(parentItem.getDisplayTitle());
title.className = 'parent-title';
container.append(img, title);
node.querySelector('.zotero-context-pane-editor-parent-line').append(container);
}
_updateAddToNote();
}
}
function _removeItemContext(tabID) {
document.getElementById(tabID + '-context').remove();
_itemContexts = _itemContexts.filter(x => x.tabID != tabID);
}
function _selectItemContext(tabID) {
let selectedIndex = Array.from(_itemPaneDeck.children).findIndex(x => x.id == tabID + '-context');
if (selectedIndex != -1) {
_itemPaneDeck.setAttribute('selectedIndex', selectedIndex);
}
}
async function _addItemContext(tabID, itemID) {
var container = document.createXULElement('vbox');
container.id = tabID + '-context';
container.className = 'zotero-item-pane-content';
_itemPaneDeck.appendChild(container);
var { libraryID } = Zotero.Items.getLibraryAndKeyFromID(itemID);
var library = Zotero.Libraries.get(libraryID);
await library.waitForDataLoad('item');
var item = Zotero.Items.get(itemID);
if (!item) {
return;
}
var libraryID = item.libraryID;
var readOnly = _isLibraryReadOnly(libraryID);
var parentID = item.parentID;
var context = {
tabID,
itemID,
parentID,
libraryID,
update: () => {}
};
_itemContexts.push(context);
if (!parentID) {
var vbox = document.createXULElement('vbox');
vbox.setAttribute('flex', '1');
vbox.setAttribute('align', 'center');
vbox.setAttribute('pack', 'center');
var description = document.createXULElement('description');
vbox.append(description);
description.append(Zotero.getString('pane.context.noParent'));
container.append(vbox);
return;
}
var parentItem = Zotero.Items.get(item.parentID);
// Dynamically create item pane tabs and panels as in itemPane.xul.
// Keep the code below in sync with itemPane.xul
// tabbox
var tabbox = document.createXULElement('tabbox');
tabbox.setAttribute('flex', '1');
tabbox.className = 'zotero-view-tabbox';
container.append(tabbox);
// tabs
var tabs = document.createXULElement('tabs');
tabs.className = 'zotero-editpane-tabs';
// tabpanels
var tabpanels = document.createXULElement('tabpanels');
tabpanels.setAttribute('flex', '1');
tabpanels.className = 'zotero-view-item';
tabpanels.addEventListener('select', () => {
_updateAddToNote();
});
// Info tab
var tabInfo = document.createXULElement('tab');
tabInfo.setAttribute('label', Zotero.Intl.strings['zotero.tabs.info.label']);
// Tags tab
var tabTags = document.createXULElement('tab');
tabTags.setAttribute('label', Zotero.Intl.strings['zotero.tabs.tags.label']);
// Related tab
var tabRelated = document.createXULElement('tab');
tabRelated.setAttribute('label', Zotero.Intl.strings['zotero.tabs.related.label']);
tabs.append(tabInfo, tabTags, tabRelated);
tabbox.append(tabs, tabpanels);
// Info panel
var panelInfo = document.createXULElement('tabpanel');
panelInfo.setAttribute('flex', '1');
panelInfo.className = 'zotero-editpane-item-box';
var itemBox = new (customElements.get('item-box'));
itemBox.setAttribute('flex', '1');
panelInfo.append(itemBox);
// Tags panel
var panelTags = document.createXULElement('tabpanel');
var tagsBox = new (customElements.get('tags-box'));
tagsBox.setAttribute('flex', '1');
tagsBox.className = 'zotero-editpane-tags';
panelTags.append(tagsBox);
// Related panel
var panelRelated = document.createXULElement('tabpanel');
var relatedBox = new (customElements.get('related-box'));
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, panelTags, panelRelated);
tabbox.selectedIndex = 0;
itemBox.mode = readOnly ? 'view' : 'edit';
itemBox.item = parentItem;
tagsBox.mode = readOnly ? 'view' : 'edit';
tagsBox.item = parentItem;
relatedBox.mode = readOnly ? 'view' : 'edit';
relatedBox.item = parentItem;
}
};