Merge pull request #2029 from zotero/html-trees
This commit is contained in:
commit
0c01d68425
108 changed files with 11306 additions and 9905 deletions
|
@ -333,37 +333,6 @@ input {
|
|||
margin-inline-start: -1px;
|
||||
}
|
||||
|
||||
#zotero-items-tree
|
||||
{
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#zotero-items-tree treechildren::-moz-tree-cell,
|
||||
#zotero-items-tree treechildren::-moz-tree-column {
|
||||
border-right: 1px solid #d7dad7;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-twisty {
|
||||
-moz-appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
list-style-image: url("chrome://zotero/skin/mac/twisty.svg");
|
||||
-moz-padding-start: 5px;
|
||||
-moz-padding-end: 6px;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-twisty(open) {
|
||||
-moz-appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
list-style-image: url("chrome://zotero/skin/mac/twisty-open.svg");
|
||||
-moz-padding-start: 4px;
|
||||
-moz-padding-end: 7px;
|
||||
}
|
||||
|
||||
/* How to get active twisty?
|
||||
treechildren::-moz-tree-twisty(active) {
|
||||
-moz-appearance: none;
|
||||
|
|
|
@ -1,22 +1,3 @@
|
|||
/*
|
||||
Override selected, unfocused tree row highlight color, which is too similar to the alternating
|
||||
row color by default
|
||||
*/
|
||||
#zotero-collections-tree treechildren::-moz-tree-row(selected),
|
||||
#zotero-items-tree treechildren::-moz-tree-row(selected) {
|
||||
background-color: #D4D4D4;
|
||||
}
|
||||
|
||||
#zotero-collections-tree treechildren::-moz-tree-row(selected, focus),
|
||||
#zotero-items-tree treechildren::-moz-tree-row(selected, focus) {
|
||||
background-color: Highlight;
|
||||
}
|
||||
|
||||
#zotero-collections-tree treechildren::-moz-tree-row {
|
||||
height: 1.3em;
|
||||
}
|
||||
|
||||
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
#zotero-pane-stack .toolbarbutton-icon {
|
||||
width: 16px;
|
||||
|
|
|
@ -111,34 +111,10 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
#zotero-collections-tree, #zotero-items-tree, .zotero-view-item {
|
||||
-moz-appearance: none;
|
||||
border-style: solid;
|
||||
border-color: #818790;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-twisty {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
/* Undo tree row spacing change in Fx25 on Windows */
|
||||
#zotero-collections-tree treechildren::-moz-tree-row,
|
||||
#zotero-items-tree treechildren::-moz-tree-row,
|
||||
#zotero-prefs treechildren::-moz-tree-row {
|
||||
height: 1.6em;
|
||||
}
|
||||
|
||||
tree {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
/* Restore row highlighting on drag over, though I'm not sure how we're losing it to begin with. */
|
||||
#zotero-collections-tree treechildren::-moz-tree-row(dropOn) {
|
||||
background-color: Highlight;
|
||||
}
|
||||
|
||||
#zotero-tag-selector groupbox {
|
||||
-moz-appearance: none;
|
||||
padding: 0;
|
||||
|
|
|
@ -25,20 +25,22 @@
|
|||
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
import ItemTree from 'zotero/itemTree';
|
||||
import { getDefaultColumnsByDataKeys } from 'zotero/itemTreeColumns';
|
||||
|
||||
|
||||
var ZoteroAdvancedSearch = new function() {
|
||||
this.onLoad = onLoad;
|
||||
this.search = search;
|
||||
this.clear = clear;
|
||||
this.onDblClick = onDblClick;
|
||||
this.onUnload = onUnload;
|
||||
this.onItemActivate = onItemActivate;
|
||||
|
||||
this.itemsView = false;
|
||||
|
||||
|
||||
var _searchBox;
|
||||
var _libraryID;
|
||||
|
||||
function onLoad() {
|
||||
async function onLoad() {
|
||||
_searchBox = document.getElementById('zotero-search-box');
|
||||
|
||||
// Set font size from pref
|
||||
|
@ -52,8 +54,37 @@ var ZoteroAdvancedSearch = new function() {
|
|||
.then(function () {
|
||||
_searchBox.search = io.dataIn.search;
|
||||
});
|
||||
|
||||
var elem = document.getElementById('zotero-items-tree');
|
||||
this.itemsView = await ItemTree.init(elem, {
|
||||
id: "advanced-search",
|
||||
dragAndDrop: true,
|
||||
onActivate: this.onItemActivate.bind(this),
|
||||
columns: getDefaultColumnsByDataKeys(['title', 'firstCreator']),
|
||||
});
|
||||
|
||||
// A minimal implementation of Zotero.CollectionTreeRow
|
||||
var collectionTreeRow = {
|
||||
view: {},
|
||||
ref: _searchBox.search,
|
||||
isSearchMode: () => true,
|
||||
getItems: async () => [],
|
||||
isLibrary: () => false,
|
||||
isCollection: () => false,
|
||||
isSearch: () => true,
|
||||
isPublications: () => false,
|
||||
isDuplicates: () => false,
|
||||
isFeed: () => false,
|
||||
isShare: () => false,
|
||||
isTrash: () => false
|
||||
};
|
||||
|
||||
this.itemsView.changeCollectionTreeRow(collectionTreeRow);
|
||||
}
|
||||
|
||||
this.onUnload = function () {
|
||||
this.itemsView.unregister();
|
||||
}
|
||||
|
||||
function search() {
|
||||
_searchBox.updateSearch();
|
||||
|
@ -63,41 +94,34 @@ var ZoteroAdvancedSearch = new function() {
|
|||
var collectionTreeRow = {
|
||||
view: {},
|
||||
ref: _searchBox.search,
|
||||
isSearchMode: function() { return true; },
|
||||
getItems: Zotero.Promise.coroutine(function* () {
|
||||
isSearchMode: () => true,
|
||||
getItems: async function () {
|
||||
var search = _searchBox.search.clone();
|
||||
search.libraryID = _libraryID;
|
||||
var ids = yield search.search();
|
||||
var ids = await search.search();
|
||||
return Zotero.Items.get(ids);
|
||||
}),
|
||||
isLibrary: function () { return false; },
|
||||
isCollection: function () { return false; },
|
||||
isSearch: function () { return true; },
|
||||
},
|
||||
isLibrary: () => false,
|
||||
isCollection: () => false,
|
||||
isSearch: () => true,
|
||||
isPublications: () => false,
|
||||
isDuplicates: () => false,
|
||||
isFeed: () => false,
|
||||
isShare: function () { return false; },
|
||||
isTrash: function () { return false; }
|
||||
}
|
||||
isShare: () => false,
|
||||
isTrash: () => false
|
||||
};
|
||||
|
||||
if (this.itemsView) {
|
||||
this.itemsView.unregister();
|
||||
}
|
||||
|
||||
this.itemsView = new Zotero.ItemTreeView(collectionTreeRow, false);
|
||||
document.getElementById('zotero-items-tree').view = this.itemsView;
|
||||
this.itemsView.changeCollectionTreeRow(collectionTreeRow);
|
||||
}
|
||||
|
||||
|
||||
function clear() {
|
||||
if (this.itemsView) {
|
||||
this.itemsView.unregister();
|
||||
}
|
||||
document.getElementById('zotero-items-tree').view = null;
|
||||
this.itemsView.changeCollectionTreeRow(null);
|
||||
|
||||
var s = new Zotero.Search();
|
||||
// Don't clear the selected library
|
||||
s.libraryID = _searchBox.search.libraryID;
|
||||
s.addCondition('title', 'contains', '')
|
||||
s.addCondition('title', 'contains', '');
|
||||
_searchBox.search = s;
|
||||
_searchBox.active = false;
|
||||
}
|
||||
|
@ -118,26 +142,24 @@ var ZoteroAdvancedSearch = new function() {
|
|||
searches.map(s => s.name).filter(n => n.startsWith(prefix))
|
||||
);
|
||||
|
||||
var name = { value: name };
|
||||
name = { value: name };
|
||||
var result = promptService.prompt(window,
|
||||
Zotero.getString('pane.collections.newSavedSeach'),
|
||||
Zotero.getString('pane.collections.savedSearchName'), name, "", {});
|
||||
|
||||
if (!result)
|
||||
{
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!name.value)
|
||||
{
|
||||
name.value = untitled;
|
||||
if (!name.value) {
|
||||
name.value = 'untitled';
|
||||
}
|
||||
|
||||
var s = _searchBox.search.clone();
|
||||
s.name = name.value;
|
||||
yield s.save();
|
||||
|
||||
window.close()
|
||||
window.close();
|
||||
});
|
||||
|
||||
|
||||
|
@ -149,42 +171,18 @@ var ZoteroAdvancedSearch = new function() {
|
|||
}
|
||||
|
||||
|
||||
// Adapted from: http://www.xulplanet.com/references/elemref/ref_tree.html#cmnote-9
|
||||
function onDblClick(event, tree)
|
||||
function onItemActivate(event, items)
|
||||
{
|
||||
if (event && tree && event.type == "dblclick")
|
||||
{
|
||||
var row = {}, col = {}, obj = {};
|
||||
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
||||
// obj.value == cell/text/image
|
||||
// TODO: handle collection double-click
|
||||
if (obj.value && this.itemsView && this.itemsView.selection.currentIndex > -1)
|
||||
{
|
||||
var item = this.itemsView.getSelectedItems()[0];
|
||||
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
|
||||
var lastWin = wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
if (!lastWin) {
|
||||
window.open();
|
||||
var newWindow = wm.getMostRecentWindow("navigator:browser");
|
||||
var b = newWindow.getBrowser();
|
||||
return;
|
||||
}
|
||||
|
||||
lastWin.ZoteroPane.selectItem(item.getID(), false, true);
|
||||
lastWin.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onUnload() {
|
||||
// Unregister search from Notifier
|
||||
if (this.itemsView) {
|
||||
this.itemsView.unregister();
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
|
||||
var lastWin = wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
if (!lastWin) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastWin.ZoteroPane.selectItems(items.map(item => item.id), false);
|
||||
lastWin.focus();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/overlay.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
|
@ -17,8 +18,9 @@
|
|||
orient="vertical"
|
||||
persist="screenX screenY width height"
|
||||
onload="ZoteroAdvancedSearch.onLoad()"
|
||||
onunload="ZoteroAdvancedSearch.onUnload()"
|
||||
onunload="ZoteroAdvancedSearch.onUnload();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
windowtype="zotero:search">
|
||||
|
||||
<script src="include.js"/>
|
||||
|
@ -34,24 +36,9 @@
|
|||
<button id="zotero-search-save" label="&zotero.search.saveSearch;" oncommand="ZoteroAdvancedSearch.save()"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<tree id="zotero-items-tree" flex="1" hidecolumnpicker="true" seltype="multiple"
|
||||
ondblclick="ZoteroAdvancedSearch.onDblClick(event, this)"
|
||||
ondragstart="if (event.target.localName == 'treechildren') { ZoteroAdvancedSearch.itemsView.onDragStart(event); }">
|
||||
<treecols>
|
||||
<treecol
|
||||
id="zotero-items-column-title" primary="true"
|
||||
label="&zotero.items.title_column;"
|
||||
flex="4" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-firstCreator"
|
||||
label="&zotero.items.creator_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
</treecols>
|
||||
<treechildren alternatingbackground="true"/>
|
||||
</tree>
|
||||
<hbox class="virtualized-table-container" flex="1">
|
||||
<html:div id="zotero-items-tree"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<keyset>
|
||||
|
|
|
@ -190,11 +190,13 @@
|
|||
<method name="add">
|
||||
<body><![CDATA[
|
||||
return Zotero.spawn(function* () {
|
||||
var io = {dataIn: null, dataOut: null};
|
||||
var io = {dataIn: null, dataOut: null, deferred: Zotero.Promise.defer()};
|
||||
|
||||
window.openDialog('chrome://zotero/content/selectItemsDialog.xul', '',
|
||||
'chrome,dialog=no,modal,centerscreen,resizable=yes', io);
|
||||
|
||||
'chrome,dialog=no,centerscreen,resizable=yes', io);
|
||||
|
||||
yield io.deferred.promise;
|
||||
|
||||
if (!io.dataOut || !io.dataOut.length) {
|
||||
return;
|
||||
}
|
||||
|
|
2458
chrome/content/zotero/collectionTree.jsx
Normal file
2458
chrome/content/zotero/collectionTree.jsx
Normal file
File diff suppressed because it is too large
Load diff
121
chrome/content/zotero/components/draggable.jsx
Normal file
121
chrome/content/zotero/components/draggable.jsx
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2019 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const { PureComponent } = React;
|
||||
const { createDragHandler } = require('./utils');
|
||||
const { func, string, number, node } = require('prop-types');
|
||||
const cx = require('classnames');
|
||||
|
||||
const DRAG = { START: 1, ACTIVE: 2, NONE: 3 };
|
||||
|
||||
/**
|
||||
* Creates a synthetic draggable element which does not use the standard
|
||||
* dragstart, dragover, dragend events. Useful for custom interactions
|
||||
* like element resize or column dragging
|
||||
*/
|
||||
class Draggable extends PureComponent {
|
||||
componentWillUnmount() {
|
||||
this.drag.stop();
|
||||
}
|
||||
|
||||
handleMouseDown = (event) => {
|
||||
if (this.dragstate > DRAG.NONE) this.drag.stop();
|
||||
if (event.button !== 0) return;
|
||||
|
||||
if (this.props.onDragStart) {
|
||||
if (this.props.onDragStart(event) === false) return;
|
||||
}
|
||||
|
||||
this.dragstart = Date.now();
|
||||
this.dragstate = DRAG.START;
|
||||
this.drag.start();
|
||||
|
||||
const { pageX, pageY, clientX } = event;
|
||||
|
||||
if (this.props.delay > 0) {
|
||||
this.delay = setTimeout(() => this.handleDrag({ pageX, pageY, clientX }),
|
||||
this.props.delay);
|
||||
}
|
||||
}
|
||||
|
||||
handleDrag = (event) => {
|
||||
if (this.props.delay && (Date.now() - this.dragstart) <= this.props.delay) return;
|
||||
this.dragstate = DRAG.ACTIVE;
|
||||
this.clear();
|
||||
this.props.onDrag(event, this.dragstate);
|
||||
}
|
||||
|
||||
handleDragStop = (event, hasBeenCancelled) => {
|
||||
try {
|
||||
switch (this.dragstate) {
|
||||
case DRAG.START:
|
||||
this.props.onDragStop(event, true);
|
||||
break;
|
||||
case DRAG.ACTIVE:
|
||||
this.props.onDragStop(event, hasBeenCancelled);
|
||||
break;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.clear();
|
||||
this.dragstate = DRAG.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
drag = createDragHandler({
|
||||
handleDrag: this.handleDrag,
|
||||
handleDragStop: this.handleDragStop
|
||||
})
|
||||
|
||||
clear() {
|
||||
if (this.delay) clearTimeout(this.delay);
|
||||
this.delay = null;
|
||||
}
|
||||
|
||||
render() {
|
||||
return React.cloneElement(React.Children.only(this.props.children), {
|
||||
className: cx('draggable', this.props.className),
|
||||
onMouseDown: this.handleMouseDown,
|
||||
});
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
children: node,
|
||||
className: string,
|
||||
delay: number,
|
||||
onDrag: func.isRequired,
|
||||
onDragStart: func.isRequired,
|
||||
onDragStop: func.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
delay: 0,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Draggable;
|
|
@ -27,7 +27,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import cx from 'classnames';
|
||||
import { noop } from '../utils';
|
||||
import { pickKeys } from '@zotero/immutable';
|
||||
import { pickKeys } from 'zotero/modules/immutable';
|
||||
//import AutoResizer from './auto-resizer';
|
||||
import Autosuggest from 'react-autosuggest';
|
||||
|
||||
|
|
|
@ -1,42 +1,60 @@
|
|||
'use strict';
|
||||
|
||||
const React = require('react')
|
||||
const { PureComponent } = React
|
||||
const { element, string } = require('prop-types')
|
||||
const cx = require('classnames')
|
||||
const React = require('react');
|
||||
const { renderToStaticMarkup } = require('react-dom-server');
|
||||
const { PureComponent } = React;
|
||||
const { element, string, object } = require('prop-types');
|
||||
|
||||
const Icon = ({ children, className, name }) => (
|
||||
<span className={cx('icon', `icon-${name}`, className)}>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
const Icon = (props) => {
|
||||
props = Object.assign({}, props);
|
||||
props.className = `icon icon-${props.name} ${props.className || ""}`;
|
||||
delete props.name;
|
||||
// Pass the props forward
|
||||
return <span {...props}></span>;
|
||||
};
|
||||
|
||||
Icon.propTypes = {
|
||||
children: element.isRequired,
|
||||
children: element,
|
||||
className: string,
|
||||
name: string.isRequired
|
||||
name: string.isRequired,
|
||||
style: object
|
||||
}
|
||||
|
||||
module.exports = { Icon }
|
||||
|
||||
|
||||
function i(name, svgOrSrc, hasDPI=true) {
|
||||
function i(name, svgOrSrc, hasHiDPI=true) {
|
||||
const icon = class extends PureComponent {
|
||||
render() {
|
||||
const { className } = this.props
|
||||
let props = Object.assign({}, this.props);
|
||||
props.name = name.toLowerCase();
|
||||
|
||||
if (typeof svgOrSrc == 'string') {
|
||||
let finalSrc = svgOrSrc;
|
||||
if (hasDPI && window.devicePixelRatio >= 1.25) {
|
||||
// N.B. In Electron we can use css-image-set
|
||||
// Also N.B. this modifies svgOrSrc and hasHiDPI for all future invocations
|
||||
// of this function
|
||||
if (hasHiDPI && window.devicePixelRatio >= 1.25) {
|
||||
let parts = svgOrSrc.split('.');
|
||||
parts[parts.length-2] = parts[parts.length-2] + '@2x';
|
||||
finalSrc = parts.join('.')
|
||||
parts[parts.length - 2] = parts[parts.length - 2] + '@2x';
|
||||
finalSrc = parts.join('.');
|
||||
hasHiDPI = false;
|
||||
}
|
||||
return <Icon className={className} name={name.toLowerCase()}><img src={finalSrc}/></Icon>
|
||||
if (!("style" in props)) props.style = {};
|
||||
props.style.backgroundImage = `url(${finalSrc})`;
|
||||
props.className = props.className || "";
|
||||
props.className += " icon-bg";
|
||||
// We use css background-image.
|
||||
// This is a performance optimization for fast-scrolling trees.
|
||||
// If we use img elements they are slow to render
|
||||
// and produce pop-in when fast-scrolling.
|
||||
return (
|
||||
<Icon {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Icon className={className} name={name.toLowerCase()}>{svgOrImg}</Icon>
|
||||
<Icon {...props}>{svgOrSrc}</Icon>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +71,133 @@ function i(name, svgOrSrc, hasDPI=true) {
|
|||
/* eslint-disable max-len */
|
||||
|
||||
|
||||
i('TagSelectorMenu', "chrome://zotero/skin/tag-selector-menu.png")
|
||||
i('DownChevron', "chrome://zotero/skin/searchbar-dropmarker.png")
|
||||
i('TagSelectorMenu', "chrome://zotero/skin/tag-selector-menu.png");
|
||||
i('SortMarker', "chrome://zotero/skin/tag-selector-menu.png");
|
||||
i('DownChevron', "chrome://zotero/skin/searchbar-dropmarker.png");
|
||||
i('Xmark', "chrome://zotero/skin/xmark.png")
|
||||
i('Twisty', (
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z"/>
|
||||
</svg>
|
||||
));
|
||||
i('Cross', "chrome://zotero/skin/cross.png");
|
||||
i('Tick', "chrome://zotero/skin/tick.png");
|
||||
i('ArrowRefresh', "chrome://zotero/skin/arrow_refresh.png");
|
||||
|
||||
i('RTFScanAccept', "chrome://zotero/skin/rtfscan-accept.png");
|
||||
i('RTFScanLink', "chrome://zotero/skin/rtfscan-link.png");
|
||||
|
||||
i('Attach', "chrome://zotero/skin/attach.png");
|
||||
i('AttachSmall', "chrome://zotero/skin/attach-small.png");
|
||||
i('BulletBlue', "chrome://zotero/skin/bullet_blue.png");
|
||||
i('BulletBlueEmpty', "chrome://zotero/skin/bullet_blue_empty.png");
|
||||
|
||||
// TreeItems
|
||||
i('TreeitemArtwork', 'chrome://zotero/skin/treeitem-artwork.png');
|
||||
i('TreeitemAttachmentLink', 'chrome://zotero/skin/treeitem-attachment-link.png');
|
||||
i('TreeitemAttachmentPdf', 'chrome://zotero/skin/treeitem-attachment-pdf.png');
|
||||
i('TreeitemAttachmentPdfLink', 'chrome://zotero/skin/treeitem-attachment-pdf-link.png');
|
||||
i('TreeitemAttachmentSnapshot', 'chrome://zotero/skin/treeitem-attachment-snapshot.png');
|
||||
i('TreeitemAttachmentWebLink', 'chrome://zotero/skin/treeitem-attachment-web-link.png');
|
||||
i('TreeitemAudioRecording', 'chrome://zotero/skin/treeitem-audioRecording.png');
|
||||
i('TreeitemBill', 'chrome://zotero/skin/treeitem-bill.png');
|
||||
i('TreeitemBlogPost', 'chrome://zotero/skin/treeitem-blogPost.png');
|
||||
i('TreeitemBook', 'chrome://zotero/skin/treeitem-book.png');
|
||||
i('TreeitemBookSection', 'chrome://zotero/skin/treeitem-bookSection.png');
|
||||
i('TreeitemCase', 'chrome://zotero/skin/treeitem-case.png');
|
||||
i('TreeitemComputerProgram', 'chrome://zotero/skin/treeitem-computerProgram.png');
|
||||
i('TreeitemConferencePaper', 'chrome://zotero/skin/treeitem-conferencePaper.png');
|
||||
i('TreeitemDictionaryEntry', 'chrome://zotero/skin/treeitem-dictionaryEntry.png');
|
||||
i('TreeitemEmail', 'chrome://zotero/skin/treeitem-email.png');
|
||||
i('TreeitemEncyclopediaArticle', 'chrome://zotero/skin/treeitem-encyclopediaArticle.png');
|
||||
i('TreeitemFilm', 'chrome://zotero/skin/treeitem-film.png');
|
||||
i('TreeitemForumPost', 'chrome://zotero/skin/treeitem-forumPost.png');
|
||||
i('TreeitemHearing', 'chrome://zotero/skin/treeitem-hearing.png');
|
||||
i('TreeitemInstantMessage', 'chrome://zotero/skin/treeitem-instantMessage.png');
|
||||
i('TreeitemInterview', 'chrome://zotero/skin/treeitem-interview.png');
|
||||
i('TreeitemJournalArticle', 'chrome://zotero/skin/treeitem-journalArticle.png');
|
||||
i('TreeitemLetter', 'chrome://zotero/skin/treeitem-letter.png');
|
||||
i('TreeitemMagazineArticle', 'chrome://zotero/skin/treeitem-magazineArticle.png');
|
||||
i('TreeitemManuscript', 'chrome://zotero/skin/treeitem-manuscript.png');
|
||||
i('TreeitemMap', 'chrome://zotero/skin/treeitem-map.png', false);
|
||||
i('TreeitemNewspaperArticle', 'chrome://zotero/skin/treeitem-newspaperArticle.png');
|
||||
i('TreeitemNote', 'chrome://zotero/skin/treeitem-note.png');
|
||||
i('TreeitemNoteSmall', 'chrome://zotero/skin/treeitem-note-small.png');
|
||||
i('TreeitemPatent', 'chrome://zotero/skin/treeitem-patent.png');
|
||||
i('Treeitem', 'chrome://zotero/skin/treeitem.png');
|
||||
i('TreeitemPodcast', 'chrome://zotero/skin/treeitem-podcast.png', false);
|
||||
i('TreeitemPresentation', 'chrome://zotero/skin/treeitem-presentation.png');
|
||||
i('TreeitemRadioBroadcast', 'chrome://zotero/skin/treeitem-radioBroadcast.png', false);
|
||||
i('TreeitemReport', 'chrome://zotero/skin/treeitem-report.png');
|
||||
i('TreeitemStatute', 'chrome://zotero/skin/treeitem-statute.png');
|
||||
i('TreeitemThesis', 'chrome://zotero/skin/treeitem-thesis.png');
|
||||
i('TreeitemTvBroadcast', 'chrome://zotero/skin/treeitem-tvBroadcast.png', false);
|
||||
i('TreeitemVideoRecording', 'chrome://zotero/skin/treeitem-videoRecording.png', false);
|
||||
i('TreeitemWebpageGray', 'chrome://zotero/skin/treeitem-webpage-gray.png');
|
||||
i('TreeitemWebpage', 'chrome://zotero/skin/treeitem-webpage.png', false);
|
||||
|
||||
// Treesource
|
||||
i('TreesourceBucket', 'chrome://zotero/skin/treesource-bucket.png', false);
|
||||
i('TreesourceCollection', 'chrome://zotero/skin/treesource-collection.png');
|
||||
i('TreesourceCommons', 'chrome://zotero/skin/treesource-commons.png', false);
|
||||
i('TreesourceDuplicates', 'chrome://zotero/skin/treesource-duplicates.png');
|
||||
i('TreesourceFeedError', 'chrome://zotero/skin/treesource-feed-error.png');
|
||||
i('TreesourceFeedLibrary', 'chrome://zotero/skin/treesource-feedLibrary.png');
|
||||
i('TreesourceFeed', 'chrome://zotero/skin/treesource-feed.png');
|
||||
i('TreesourceFeedUpdating', 'chrome://zotero/skin/treesource-feed-updating.png', false);
|
||||
i('TreesourceGroups', 'chrome://zotero/skin/treesource-groups.png');
|
||||
i('TreesourceLibrary', 'chrome://zotero/skin/treesource-library.png');
|
||||
i('TreesourceSearch', 'chrome://zotero/skin/treesource-search.png');
|
||||
i('TreesourceShare', 'chrome://zotero/skin/treesource-share.png', false);
|
||||
i('TreesourceTrashFull', 'chrome://zotero/skin/treesource-trash-full.png');
|
||||
i('TreesourceTrash', 'chrome://zotero/skin/treesource-trash.png');
|
||||
i('TreesourceUnfiled', 'chrome://zotero/skin/treesource-unfiled.png');
|
||||
|
||||
if (Zotero.isMac) {
|
||||
i('TreesourceCollection', 'chrome://zotero-platform/content/treesource-collection.png', true);
|
||||
i('TreesourceSearch', 'chrome://zotero-platform/content/treesource-search.png', true);
|
||||
i('Twisty', (
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="3 4 12 4 7.5 12"/>
|
||||
</svg>
|
||||
));
|
||||
}
|
||||
|
||||
if (Zotero.isWin) {
|
||||
i('Twisty', (
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1792 1792">
|
||||
<path d="M1395 736q0 13-10 23l-466 466q-10 10-23 10t-23-10l-466-466q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l393 393 393-393q10-10 23-10t23 10l50 50q10 10 10 23z"/>
|
||||
</svg>
|
||||
));
|
||||
}
|
||||
|
||||
let domElementCache = {};
|
||||
|
||||
/**
|
||||
* Returns a DOM element for the icon class
|
||||
*
|
||||
* To be used in itemTree where rendering is done without react
|
||||
* for performance reasons
|
||||
* @param {String} icon
|
||||
* @returns {Element}
|
||||
*/
|
||||
module.exports.getDOMElement = function (icon) {
|
||||
if (domElementCache[icon]) return domElementCache[icon].cloneNode(true);
|
||||
if (!module.exports[icon]) {
|
||||
Zotero.debug(`Attempting to get non-existant icon ${icon}`);
|
||||
return "";
|
||||
}
|
||||
let div = document.createElementNS("http://www.w3.org/1999/xhtml", 'div');
|
||||
div.innerHTML = renderToStaticMarkup(React.createElement(module.exports[icon]));
|
||||
domElementCache[icon] = div.firstChild;
|
||||
return domElementCache[icon].cloneNode(true);
|
||||
}
|
|
@ -1,5 +1,80 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2019 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
function getDragTargetOrient(event, target) {
|
||||
const elem = target || event.target;
|
||||
const {y, height} = elem.getBoundingClientRect();
|
||||
const ratio = (event.clientY - y) / height;
|
||||
// first 1/6 of the elem ([x-----])
|
||||
if (ratio <= 0.166) return -1;
|
||||
// 2/6 to 5/6 of the elem ([-xxxx-])
|
||||
else if (ratio <= 0.833) return 0;
|
||||
// last 5/6 of the elem ([-----x])
|
||||
else return 1;
|
||||
}
|
||||
|
||||
function createDragHandler({ handleDrag, handleDragStop }) {
|
||||
function onKeyDown(event) {
|
||||
if (event.key == 'Escape') {
|
||||
event.stopPropagation();
|
||||
onDragStop(event);
|
||||
}
|
||||
}
|
||||
|
||||
function onDragStart() {
|
||||
document.addEventListener('mousemove', handleDrag);
|
||||
document.addEventListener('mouseup', onDragStop, { capture: true });
|
||||
document.addEventListener('mouseleave', onDragStop);
|
||||
window.addEventListener('blur', onDragStop);
|
||||
|
||||
// Register on first child because global bindings are bound
|
||||
// on document and we need to stop the propagation in
|
||||
// case we handle it here!
|
||||
document.children[0].addEventListener('keydown', onKeyDown);
|
||||
}
|
||||
|
||||
function onDragStop(event) {
|
||||
document.removeEventListener('mousemove', handleDrag);
|
||||
document.removeEventListener('mouseup', onDragStop, { capture: true });
|
||||
document.removeEventListener('mouseleave', onDragStop);
|
||||
window.removeEventListener('blur', onDragStop);
|
||||
document.children[0].removeEventListener('keydown', onKeyDown);
|
||||
|
||||
handleDragStop(event, !event || event.type !== 'mouseup');
|
||||
}
|
||||
|
||||
return {
|
||||
start: onDragStart,
|
||||
stop: onDragStop
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
noop
|
||||
noop, getDragTargetOrient, createDragHandler
|
||||
};
|
||||
|
|
1421
chrome/content/zotero/components/virtualized-table.jsx
Normal file
1421
chrome/content/zotero/components/virtualized-table.jsx
Normal file
File diff suppressed because it is too large
Load diff
289
chrome/content/zotero/components/windowed-list.js
Normal file
289
chrome/content/zotero/components/windowed-list.js
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2019 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
const requiredOptions = ['getItemCount', 'itemHeight', 'renderItem', 'targetElement'];
|
||||
|
||||
/**
|
||||
* A windowed list for performant display of an essentially infinite number of items
|
||||
* Inspired by https://github.com/bvaughn/react-window
|
||||
*
|
||||
* The main principle here is to display a div with a height set to itemHeight * getItemCount()
|
||||
* and only render rows visible in the scrollbox area, unloading them and rendering new ones
|
||||
* as needed.
|
||||
*
|
||||
* This was created after the measured performance of react-window was not satisfactory
|
||||
* for a 100% fluid experience, especially once rows with multiple cells that needed
|
||||
* responsive resizing were introduced
|
||||
*
|
||||
* The class requires careful handholding to achieve good performance. Read method documentation!
|
||||
*/
|
||||
module.exports = class {
|
||||
/**
|
||||
* @param options (required):
|
||||
* - getItemCount {Function} a function that returns the number of items currently on display
|
||||
* - renderItem {Function} a function that returns a DOM element for an individual row to display
|
||||
* - itemHeight {Integer}
|
||||
* - targetElement {DOMElement} a container DOM element for the windowed-list
|
||||
*/
|
||||
constructor(options) {
|
||||
for (let option of requiredOptions) {
|
||||
if (!options.hasOwnProperty(option)) {
|
||||
throw new Error('Attempted to initialize windowed-list without a required option: ' + option);
|
||||
}
|
||||
}
|
||||
|
||||
this.scrollDirection = 0;
|
||||
this.scrollOffset = 0;
|
||||
this.overscanCount = 2;
|
||||
this._lastItemCount = null;
|
||||
|
||||
Object.assign(this, options);
|
||||
this._renderedRows = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call once to add the windowed-list DOM element to the container
|
||||
*/
|
||||
initialize() {
|
||||
const { targetElement } = this;
|
||||
this.innerElem = document.createElementNS("http://www.w3.org/1999/xhtml", 'div');
|
||||
this.innerElem.className = "windowed-list";
|
||||
|
||||
targetElement.appendChild(this.innerElem);
|
||||
targetElement.addEventListener('scroll', this._handleScroll);
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to remove the windowed-list from the container
|
||||
*/
|
||||
destroy() {
|
||||
if (this.innerElem) {
|
||||
this.targetElement.removeEventListener('scroll', this._handleScroll);
|
||||
this.targetElement.removeChild(this.innerElem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rerender an individual item. A no-op if the item is not in view
|
||||
* @param index {Integer}
|
||||
*/
|
||||
rerenderItem(index) {
|
||||
if (!this._renderedRows.has(index)) return;
|
||||
let oldElem = this._renderedRows.get(index);
|
||||
let elem = this.renderItem(index, oldElem);
|
||||
elem.style.top = this._getItemPosition(index) + "px";
|
||||
elem.style.position = "absolute";
|
||||
if (elem == oldElem) return;
|
||||
this.innerElem.replaceChild(elem, this._renderedRows.get(index));
|
||||
this._renderedRows.set(index, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rerender items within the scrollbox. Call sparingly
|
||||
*/
|
||||
invalidate() {
|
||||
// Removes any items out of view and adds the ones not in view
|
||||
this.render();
|
||||
// Rerender the rest
|
||||
for (let index of Array.from(this._renderedRows.keys())) {
|
||||
this.rerenderItem(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render all items within the scrollbox and remove those no longer visible
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
renderItem,
|
||||
innerElem,
|
||||
} = this;
|
||||
|
||||
const [startIndex, stopIndex] = this._getRangeToRender();
|
||||
|
||||
if (stopIndex - startIndex > 0) {
|
||||
for (let index = startIndex; index < stopIndex; index++) {
|
||||
if (this._renderedRows.has(index)) continue;
|
||||
let elem = renderItem(index);
|
||||
elem.style.top = this._getItemPosition(index) + "px";
|
||||
elem.style.position = "absolute";
|
||||
innerElem.appendChild(elem);
|
||||
this._renderedRows.set(index, elem);
|
||||
}
|
||||
}
|
||||
for (let [index, elem] of this._renderedRows.entries()) {
|
||||
if (index < startIndex || index >= stopIndex) {
|
||||
elem.remove();
|
||||
this._renderedRows.delete(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to update constructor params
|
||||
* @param options (see constructor())
|
||||
*/
|
||||
update(options = {}) {
|
||||
Object.assign(this, options);
|
||||
const { itemHeight, targetElement, innerElem } = this;
|
||||
const itemCount = this._getItemCount();
|
||||
innerElem.style.position = 'relative';
|
||||
innerElem.style.height = `${itemHeight * itemCount}px`;
|
||||
|
||||
this.scrollDirection = 0;
|
||||
this.scrollOffset = targetElement.scrollTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the top of the scrollbox to a specified location
|
||||
* @param scrollOffset {Integer} offset for the top of the tree
|
||||
*/
|
||||
scrollTo(scrollOffset) {
|
||||
const maxOffset = this.itemHeight * this._getItemCount() - this.getWindowHeight();
|
||||
scrollOffset = Math.min(Math.max(0, scrollOffset), maxOffset);
|
||||
this.scrollOffset = scrollOffset;
|
||||
this.targetElement.scrollTop = scrollOffset;
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the scrollbox to a specified item. No-op if already in view
|
||||
* @param index
|
||||
*/
|
||||
scrollToRow(index) {
|
||||
const { itemHeight, scrollOffset } = this;
|
||||
const itemCount = this._getItemCount();
|
||||
const height = this.getWindowHeight();
|
||||
|
||||
index = Math.max(0, Math.min(index, itemCount - 1));
|
||||
let startPosition = this._getItemPosition(index);
|
||||
let endPosition = startPosition + itemHeight;
|
||||
if (startPosition < scrollOffset) {
|
||||
this.scrollTo(startPosition);
|
||||
}
|
||||
else if (endPosition > scrollOffset + height) {
|
||||
this.scrollTo(Math.min(endPosition - height, (itemCount * itemHeight) - height));
|
||||
}
|
||||
}
|
||||
|
||||
getFirstVisibleRow() {
|
||||
return Math.ceil(this.scrollOffset / this.itemHeight);
|
||||
}
|
||||
|
||||
getLastVisibleRow() {
|
||||
const height = this.getWindowHeight();
|
||||
return Math.max(1, Math.floor((this.scrollOffset + height + 1) / this.itemHeight)) - 1;
|
||||
}
|
||||
|
||||
getWindowHeight() {
|
||||
return this.targetElement.getBoundingClientRect().height;
|
||||
}
|
||||
|
||||
getIndexByMouseEventPosition = (yOffset) => {
|
||||
return Math.min(this._getItemCount()-1, Math.floor((yOffset - this.innerElem.getBoundingClientRect().top) / this.itemHeight));
|
||||
}
|
||||
|
||||
getElementByIndex = index => this._renderedRows.get(index);
|
||||
|
||||
/**
|
||||
* @returns {Integer} - the number of fully visible items in the scrollbox
|
||||
*/
|
||||
getPageLength() {
|
||||
const height = this.getWindowHeight();
|
||||
return Math.ceil(height / this.itemHeight);
|
||||
}
|
||||
|
||||
_getItemPosition = (index) => {
|
||||
return (this.itemHeight * index);
|
||||
};
|
||||
|
||||
_getRangeToRender() {
|
||||
const { itemHeight, overscanCount, scrollDirection, scrollOffset } = this;
|
||||
const itemCount = this._getItemCount();
|
||||
const height = this.getWindowHeight();
|
||||
|
||||
if (itemCount === 0) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
|
||||
const startIndex = Math.floor(scrollOffset / itemHeight);
|
||||
const stopIndex = Math.ceil((scrollOffset + height) / itemHeight + 1);
|
||||
|
||||
// Overscan by one item in each direction so that tab/focus works.
|
||||
// If there isn't at least one extra item, tab loops back around.
|
||||
const overscanBackward =
|
||||
!scrollDirection || scrollDirection === -1
|
||||
? Math.max(1, overscanCount)
|
||||
: 1;
|
||||
const overscanForward =
|
||||
!scrollDirection || scrollDirection === 1
|
||||
? Math.max(1, overscanCount)
|
||||
: 1;
|
||||
|
||||
return [
|
||||
Math.max(0, startIndex - overscanBackward),
|
||||
Math.max(0, Math.min(itemCount, stopIndex + overscanForward)),
|
||||
startIndex,
|
||||
stopIndex,
|
||||
];
|
||||
}
|
||||
|
||||
_getItemCount() {
|
||||
const itemCount = this.getItemCount();
|
||||
if (this._lastItemCount != itemCount) {
|
||||
this._lastItemCount = itemCount;
|
||||
this.update();
|
||||
this.invalidate();
|
||||
}
|
||||
return this._lastItemCount;
|
||||
}
|
||||
|
||||
_handleScroll = (event) => {
|
||||
const { scrollOffset: prevScrollOffset } = this;
|
||||
const { clientHeight, scrollHeight, scrollTop } = event.currentTarget;
|
||||
|
||||
if (prevScrollOffset === scrollTop) {
|
||||
// Scroll position may have been updated by cDM/cDU,
|
||||
// In which case we don't need to trigger another render,
|
||||
// And we don't want to update anything.
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent macOS elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
const scrollOffset = Math.max(
|
||||
0,
|
||||
Math.min(scrollTop, scrollHeight - clientHeight)
|
||||
);
|
||||
|
||||
this.scrollDirection = prevScrollOffset < scrollOffset ? 1 : -1;
|
||||
this.scrollOffset = scrollOffset;
|
||||
this._resetScrollDirection();
|
||||
this.render();
|
||||
};
|
||||
|
||||
_resetScrollDirection = Zotero.Utilities.debounce(() => this.scrollDirection = 0, 150);
|
||||
};
|
|
@ -74,6 +74,16 @@ Zotero.TagSelector = class TagSelectorContainer extends React.PureComponent {
|
|||
focusTextbox() {
|
||||
this.searchBoxRef.current.focus();
|
||||
}
|
||||
|
||||
componentDidCatch(error, info) {
|
||||
// Async operations might attempt to update the react components
|
||||
// after window close in tests, which will cause unnecessary crashing.
|
||||
if (this._uninitialized) return;
|
||||
Zotero.debug("TagSelectorContainer: React threw an error");
|
||||
Zotero.logError(error);
|
||||
Zotero.debug(info);
|
||||
Zotero.crash();
|
||||
}
|
||||
|
||||
componentDidUpdate(_prevProps, _prevState) {
|
||||
Zotero.debug("Tag selector updated");
|
||||
|
@ -762,6 +772,7 @@ Zotero.TagSelector = class TagSelectorContainer extends React.PureComponent {
|
|||
}
|
||||
|
||||
uninit() {
|
||||
this._uninitialized = true;
|
||||
ReactDOM.unmountComponentAtNode(this.domEl);
|
||||
Zotero.Notifier.unregisterObserver(this._notifierID);
|
||||
Zotero.Prefs.unregisterObserver(this._prefObserverID);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
import FilePicker from 'zotero/filePicker';
|
||||
import FilePicker from 'zotero/modules/filePicker';
|
||||
|
||||
/****Zotero_File_Exporter****
|
||||
**
|
||||
|
@ -135,7 +135,7 @@ var Zotero_File_Interface = new function() {
|
|||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
this.exportFile = Zotero.Promise.method(function () {
|
||||
this.exportFile = async function () {
|
||||
var exporter = new Zotero_File_Exporter();
|
||||
exporter.libraryID = ZoteroPane_Local.getSelectedLibraryID();
|
||||
if (exporter.libraryID === false) {
|
||||
|
@ -143,7 +143,7 @@ var Zotero_File_Interface = new function() {
|
|||
}
|
||||
exporter.name = Zotero.Libraries.getName(exporter.libraryID);
|
||||
return exporter.save();
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* exports a collection or saved search
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import FilePicker from 'zotero/filePicker';
|
||||
import FilePicker from 'zotero/modules/filePicker';
|
||||
|
||||
var Zotero_Import_Wizard = {
|
||||
_wizard: null,
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
|
||||
var Zotero = Components.classes['@zotero.org/Zotero;1']
|
||||
// Currently uses only nsISupports
|
||||
//.getService(Components.interfaces.chnmIZoteroService).
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
// Currently uses only nsISupports
|
||||
//.getService(Components.interfaces.chnmIZoteroService).
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
|
||||
// Components.utils.import('resource://zotero/require.js');
|
||||
// Not using Cu.import here since we don't want the require module to be cached
|
||||
|
|
|
@ -319,7 +319,7 @@ var Zotero_Citation_Dialog = new function () {
|
|||
_itemSelected(itemDataID);
|
||||
// turn off highlight in item tree
|
||||
_suppressNextTreeSelect = true;
|
||||
document.getElementById("zotero-items-tree").view.selection.clearSelection();
|
||||
itemsView.selection.clearSelection();
|
||||
document.getElementById("remove").disabled = !itemDataID;
|
||||
document.getElementById("add").disabled = true;
|
||||
_configListPosition(!itemDataID, selectedListIndex);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/overlay.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/integration.css" type="text/css"?>
|
||||
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
|
||||
|
@ -43,6 +44,7 @@
|
|||
ondialogcancel="Zotero_Citation_Dialog.cancel();"
|
||||
onclose="Zotero_Citation_Dialog.cancel();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
persist="screenX screenY width height"
|
||||
resizable="true"
|
||||
buttons="extra1,extra2,accept,cancel"
|
||||
|
@ -62,191 +64,13 @@
|
|||
onkeypress="if(event.keyCode == event.DOM_VK_ESCAPE) { if (this.value == '') { cancelDialog(); return false; } this.value = ''; onSearch(); return false; } return true;"/>
|
||||
</hbox>
|
||||
<hbox flex="1" style="margin-top: 5px">
|
||||
<tree id="zotero-collections-tree"
|
||||
style="width: 200px;" hidecolumnpicker="true" seltype="cell"
|
||||
onselect="onCollectionSelected();">
|
||||
<treecols>
|
||||
<treecol
|
||||
id="zotero-collections-name-column"
|
||||
flex="1"
|
||||
primary="true"
|
||||
hideheader="true"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
|
||||
<deck id="zotero-items-pane-content" selectedIndex="0" flex="1">
|
||||
<tree id="zotero-items-tree"
|
||||
enableColumnDrag="true" flex="1" seltype="single"
|
||||
onselect="onItemSelected(); Zotero_Citation_Dialog.treeItemSelected();">
|
||||
<treecols id="zotero-items-columns-header">
|
||||
<treecol
|
||||
id="zotero-items-column-title" primary="true"
|
||||
label="&zotero.items.title_column;"
|
||||
flex="4" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-firstCreator"
|
||||
label="&zotero.items.creator_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-itemType" hidden="true"
|
||||
label="&zotero.items.type_column;"
|
||||
width="40" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-date" hidden="true"
|
||||
label="&zotero.items.date_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-year" hidden="true"
|
||||
label="&zotero.items.year_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-publisher" hidden="true"
|
||||
label="&zotero.items.publisher_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-publicationTitle" hidden="true"
|
||||
label="&zotero.items.publication_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-journalAbbreviation" hidden="true"
|
||||
label="&zotero.items.journalAbbr_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-language" hidden="true"
|
||||
label="&zotero.items.language_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-accessDate" hidden="true"
|
||||
label="&zotero.items.accessDate_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-libraryCatalog" hidden="true"
|
||||
label="&zotero.items.libraryCatalog_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-callNumber" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.callNumber_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-rights" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.rights_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-dateAdded" hidden="true"
|
||||
label="&zotero.items.dateAdded_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-dateModified" hidden="true"
|
||||
label="&zotero.items.dateModified_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-archive" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.archive_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-archiveLocation" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.archiveLocation_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-place" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.place_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-volume" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.volume_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-edition" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.edition_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-pages" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.pages_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-issue" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.issue_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-series" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.series_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-seriesTitle" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.seriesTitle_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-court" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.court_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-medium" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.medium_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-genre" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.genre_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-system" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.system_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-extra" hidden="true"
|
||||
label="&zotero.items.extra_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
|
||||
<!-- Label for displaying messages when items pane is hidden
|
||||
(e.g. "Advanced search mode — press Enter to search.")-->
|
||||
<vbox id="zotero-items-pane-message-box" pack="center" align="center"/>
|
||||
</deck>
|
||||
<vbox id="zotero-collections-tree-container" class="virtualized-table-container" style="min-width: 200px">
|
||||
<html:div id="zotero-collections-tree"></html:div>
|
||||
</vbox>
|
||||
|
||||
<hbox id="zotero-items-pane-content" class="virtualized-table-container" flex="1">
|
||||
<html:div id="zotero-items-tree"></html:div>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ var Zotero_Bibliography_Dialog = new function () {
|
|||
/**
|
||||
* Initializes add citation dialog
|
||||
*/
|
||||
this.load = function() {
|
||||
this.load = async function() {
|
||||
bibEditInterface = window.arguments[0].wrappedJSObject;
|
||||
|
||||
_revertAllButton = document.documentElement.getButton("extra2");
|
||||
|
@ -44,7 +44,6 @@ var Zotero_Bibliography_Dialog = new function () {
|
|||
_addButton = document.getElementById("add");
|
||||
_removeButton = document.getElementById("remove");
|
||||
_itemList = document.getElementById("item-list");
|
||||
_itemTree = document.getElementById("zotero-items-tree");
|
||||
|
||||
_revertAllButton.label = Zotero.getString("integration.revertAll.button");
|
||||
_revertAllButton.disabled = bibEditInterface.isAnyEdited();
|
||||
|
@ -54,7 +53,7 @@ var Zotero_Bibliography_Dialog = new function () {
|
|||
document.getElementById('editor').format = "RTF";
|
||||
|
||||
// load (from selectItemsDialog.js)
|
||||
doLoad();
|
||||
await doLoad();
|
||||
|
||||
// load bibliography entries
|
||||
_loadItems();
|
||||
|
@ -119,7 +118,7 @@ var Zotero_Bibliography_Dialog = new function () {
|
|||
|
||||
if(_itemList.selectedItems.length) {
|
||||
_suppressAllSelectEvents = true;
|
||||
_itemTree.view.selection.clearSelection();
|
||||
itemsView.selection.clearSelection();
|
||||
_suppressAllSelectEvents = false;
|
||||
|
||||
// only show revert button if at least one selected item has been edited
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/overlay.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/integration.css" type="text/css"?>
|
||||
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
|
||||
|
@ -60,51 +61,13 @@
|
|||
<textbox id="zotero-tb-search" type="search" timeout="250" oncommand="onSearch()" dir="reverse" onkeypress="if(event.keyCode == event.DOM_VK_ESCAPE) { this.value = ''; this.doCommand('cmd_zotero_search'); return false; } return true;"/>
|
||||
</hbox>
|
||||
<hbox flex="1" style="margin-top: 5px">
|
||||
<tree id="zotero-collections-tree"
|
||||
style="width: 150px;" hidecolumnpicker="true" seltype="single"
|
||||
onselect="onCollectionSelected();">
|
||||
<treecols>
|
||||
<treecol
|
||||
id="zotero-collections-name-column"
|
||||
flex="1"
|
||||
primary="true"
|
||||
hideheader="true"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
|
||||
<deck id="zotero-items-pane-content" selectedIndex="0" flex="1">
|
||||
<tree id="zotero-items-tree"
|
||||
flex="1" hidecolumnpicker="true" seltype="multiple"
|
||||
onselect="Zotero_Bibliography_Dialog.treeItemSelected();">
|
||||
<treecols>
|
||||
<treecol
|
||||
id="zotero-items-column-title" primary="true"
|
||||
label="&zotero.items.title_column;"
|
||||
flex="4" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-firstCreator"
|
||||
label="&zotero.items.creator_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-dateAdded" hidden="true"
|
||||
label="&zotero.items.dateAdded_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-dateModified" hidden="true"
|
||||
label="&zotero.items.dateModified_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
|
||||
<!-- Label for displaying messages when items pane is hidden
|
||||
(e.g. "Advanced search mode — press Enter to search.")-->
|
||||
<vbox id="zotero-items-pane-message-box" pack="center" align="center"/>
|
||||
</deck>
|
||||
<vbox id="zotero-collections-tree-container" class="virtualized-table-container" style="min-width: 150px">
|
||||
<html:div id="zotero-collections-tree" class="edit-bibl"></html:div>
|
||||
</vbox>
|
||||
|
||||
<hbox id="zotero-items-pane-content" class="virtualized-table-container" flex="1">
|
||||
<html:div id="zotero-items-tree"></html:div>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
|
|
|
@ -224,6 +224,9 @@ var ZoteroItemPane = new function() {
|
|||
yield this.viewItem(_lastItem, null, 1);
|
||||
}
|
||||
}
|
||||
if (viewBox.selectedIndex == 0 && action == 'refresh' && _lastItem) {
|
||||
yield this.viewItem(_lastItem, null, 0);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
</groupbox>
|
||||
|
||||
<!-- Regular item -->
|
||||
<tabbox id="zotero-view-tabbox" class="zotero-view-tabbox" flex="1" onselect="if (!ZoteroPane_Local.collectionsView.selection || event.originalTarget.localName != 'tabpanels') { return; }; ZoteroItemPane.viewItem(ZoteroPane_Local.getSelectedItems()[0], ZoteroPane_Local.collectionsView.editable ? 'edit' : 'view', this.selectedIndex)">
|
||||
<tabbox id="zotero-view-tabbox" class="zotero-view-tabbox" flex="1" onselect="if (!ZoteroPane_Local.getCollectionTreeRow() || event.originalTarget.localName != 'tabpanels') { return; }; ZoteroItemPane.viewItem(ZoteroPane_Local.getSelectedItems()[0], ZoteroPane_Local.collectionsView.editable ? 'edit' : 'view', this.selectedIndex)">
|
||||
<tabs id="zotero-editpane-tabs" class="zotero-editpane-tabs">
|
||||
<tab id="zotero-editpane-info-tab" label="&zotero.tabs.info.label;"/>
|
||||
<tab id="zotero-editpane-notes-tab" label="&zotero.tabs.notes.label;"/>
|
||||
|
|
3547
chrome/content/zotero/itemTree.jsx
Normal file
3547
chrome/content/zotero/itemTree.jsx
Normal file
File diff suppressed because it is too large
Load diff
290
chrome/content/zotero/itemTreeColumns.jsx
Normal file
290
chrome/content/zotero/itemTreeColumns.jsx
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2020 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
(function() {
|
||||
const React = require('react');
|
||||
const Icons = require('components/icons');
|
||||
|
||||
const COLUMNS = [
|
||||
{
|
||||
dataKey: "title",
|
||||
primary: true,
|
||||
defaultIn: new Set(["default", "feed"]),
|
||||
label: "zotero.items.title_column",
|
||||
ignoreInColumnPicker: "true",
|
||||
flex: 4,
|
||||
inMenu: false,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "firstCreator",
|
||||
defaultIn: new Set(["default", "feed"]),
|
||||
label: "zotero.items.creator_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "itemType",
|
||||
label: "zotero.items.type_column",
|
||||
width: "40",
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "date",
|
||||
defaultIn: new Set(["feed"]),
|
||||
label: "zotero.items.date_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "year",
|
||||
disabledIn: "feed",
|
||||
label: "zotero.items.year_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "publisher",
|
||||
label: "zotero.items.publisher_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "publicationTitle",
|
||||
disabledIn: "feed",
|
||||
label: "zotero.items.publication_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "journalAbbreviation",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.journalAbbr_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "language",
|
||||
submenu: true,
|
||||
label: "zotero.items.language_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "accessDate",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.accessDate_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "libraryCatalog",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.libraryCatalog_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "callNumber",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.callNumber_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "rights",
|
||||
submenu: true,
|
||||
label: "zotero.items.rights_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "dateAdded",
|
||||
disabledIn: "feed",
|
||||
label: "zotero.items.dateAdded_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "dateModified",
|
||||
disabledIn: "feed",
|
||||
label: "zotero.items.dateModified_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "archive",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.archive_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "archiveLocation",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.archiveLocation_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "place",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.place_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "volume",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.volume_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "edition",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.edition_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "pages",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.pages_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "issue",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.issue_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "series",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.series_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "seriesTitle",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.seriesTitle_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "court",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.court_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "medium",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.medium_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "genre",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.genre_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "system",
|
||||
disabledIn: "feed",
|
||||
submenu: true,
|
||||
label: "zotero.items.system_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "extra",
|
||||
disabledIn: "feed",
|
||||
label: "zotero.items.extra_column",
|
||||
flex: 1,
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "hasAttachment",
|
||||
defaultIn: new Set(["default"]),
|
||||
disabledIn: "feed",
|
||||
label: "zotero.tabs.attachments.label",
|
||||
iconLabel: <Icons.IconAttachSmall />,
|
||||
fixedWidth: true,
|
||||
width: "14",
|
||||
zoteroPersist: new Set(["hidden", "sortDirection"])
|
||||
},
|
||||
{
|
||||
dataKey: "numNotes",
|
||||
disabledIn: "feed",
|
||||
label: "zotero.tabs.notes.label",
|
||||
iconLabel: <Icons.IconTreeitemNoteSmall />,
|
||||
width: "14",
|
||||
zoteroPersist: new Set(["width", "hidden", "sortDirection"])
|
||||
}
|
||||
];
|
||||
let DATA_KEY_TO_COLUMN = {};
|
||||
for (const column of COLUMNS) {
|
||||
DATA_KEY_TO_COLUMN[column.dataKey] = column;
|
||||
}
|
||||
|
||||
function getDefaultColumnByDataKey(dataKey) {
|
||||
return Object.assign({}, DATA_KEY_TO_COLUMN[dataKey], {hidden: false});
|
||||
}
|
||||
|
||||
function getDefaultColumnsByDataKeys(dataKeys) {
|
||||
return COLUMNS.filter(column => dataKeys.includes(column.dataKey)).map(column => Object.assign({}, column, {hidden: false}));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
COLUMNS,
|
||||
getDefaultColumnByDataKey,
|
||||
getDefaultColumnsByDataKeys,
|
||||
};
|
||||
|
||||
})();
|
249
chrome/content/zotero/libraryTree.js
Normal file
249
chrome/content/zotero/libraryTree.js
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2020 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
const { TreeSelectionStub } = require('components/virtualized-table');
|
||||
const React = require('react');
|
||||
|
||||
/**
|
||||
* Common methods for Zotero.ItemTree and Zotero.CollectionTree
|
||||
* @type {Zotero.LibraryTree}
|
||||
*/
|
||||
var LibraryTree = class LibraryTree extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._rows = [];
|
||||
this._rowMap = {};
|
||||
|
||||
this.domEl = props.domEl;
|
||||
this._ownerDocument = props.domEl.ownerDocument;
|
||||
|
||||
this.onSelect = this.createEventBinding('select');
|
||||
this.onRefresh = this.createEventBinding('refresh');
|
||||
}
|
||||
|
||||
get window() {
|
||||
return this._ownerDocument.defaultView;
|
||||
}
|
||||
|
||||
get selection() {
|
||||
return this.tree ? this.tree.selection : TreeSelectionStub;
|
||||
}
|
||||
|
||||
get rowCount() {
|
||||
return this._rows.length;
|
||||
}
|
||||
|
||||
waitForSelect() {
|
||||
return this._waitForEvent('select');
|
||||
}
|
||||
|
||||
componentDidCatch(error, info) {
|
||||
// Async operations might attempt to update the react components
|
||||
// after window close in tests, which will cause unnecessary crashing
|
||||
// so we set an unintialized flag that we check in select functions
|
||||
// like #notify
|
||||
if (this._uninitialized) return;
|
||||
Zotero.debug("ItemTree: React threw an error");
|
||||
Zotero.logError(error);
|
||||
Zotero.debug(info);
|
||||
if (this.type == 'item') Zotero.Prefs.clear('lastViewedFolder');
|
||||
Zotero.crash();
|
||||
}
|
||||
|
||||
getParentIndex = (index) => {
|
||||
var thisLevel = this.getLevel(index);
|
||||
if (thisLevel == 0) return -1;
|
||||
for (var i = index - 1; i >= 0; i--) {
|
||||
if (this.getLevel(i) < thisLevel) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
getLevel(index) {
|
||||
return this._rows[index].level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a reference to the tree row at a given row
|
||||
*
|
||||
* @return {TreeRow}
|
||||
*/
|
||||
getRow(index) {
|
||||
return this._rows[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index of the row with a given ID (e.g., "C123" for collection 123)
|
||||
*
|
||||
* @param {String} - Row id
|
||||
* @return {Integer|false}
|
||||
*/
|
||||
getRowIndexByID(id) {
|
||||
if (!(id in this._rowMap)) {
|
||||
Zotero.debug(`${this.name}: Trying to access a row with invalid ID ${id}`)
|
||||
return false;
|
||||
}
|
||||
return this._rowMap[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tree row to the main array, update the row count, tell the treebox that the row
|
||||
* count changed, and update the row map
|
||||
*
|
||||
* @param {TreeRow} treeRow
|
||||
* @param {Number} [beforeRow] - Row index to insert new row before
|
||||
*/
|
||||
_addRow(treeRow, beforeRow, skipRowMapRefresh) {
|
||||
this._rows.splice(beforeRow, 0, treeRow);
|
||||
if (!skipRowMapRefresh) {
|
||||
// Increment all rows in map at or above insertion point
|
||||
for (let i in this._rowMap) {
|
||||
if (this._rowMap[i] >= beforeRow) {
|
||||
this._rowMap[i]++;
|
||||
}
|
||||
}
|
||||
// Add new row to map
|
||||
this._rowMap[treeRow.id] = beforeRow;
|
||||
}
|
||||
}
|
||||
|
||||
_removeRows(rows) {
|
||||
rows = Zotero.Utilities.arrayUnique(rows);
|
||||
rows.sort((a, b) => a - b);
|
||||
for (let i = rows.length - 1; i >= 0; i--) {
|
||||
this._removeRow(rows[i], true);
|
||||
}
|
||||
this._refreshRowMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a row from the main array and parent row children arrays,
|
||||
* delete the row from the map, and optionally update all rows above it in the map
|
||||
*/
|
||||
_removeRow(index, skipMapUpdate) {
|
||||
var id = this.getRow(index).id;
|
||||
let level = this.getLevel(index);
|
||||
|
||||
if (index <= this.selection.focused) {
|
||||
this.selection.select(this.selection.focused - 1);
|
||||
}
|
||||
|
||||
this._rows.splice(index, 1);
|
||||
if (index != 0
|
||||
&& this.getLevel(index - 1) < level
|
||||
&& (!this._rows[index] || this.getLevel(index) != level)) {
|
||||
this._rows[index - 1].isOpen = false;
|
||||
}
|
||||
|
||||
delete this._rowMap[id];
|
||||
if (!skipMapUpdate) {
|
||||
for (let i in this._rowMap) {
|
||||
if (this._rowMap[i] > index) {
|
||||
this._rowMap[i]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_refreshRowMap() {
|
||||
var rowMap = {};
|
||||
for (var i = 0; i < this.rowCount; i++) {
|
||||
let row = this.getRow(i);
|
||||
let id = row.id;
|
||||
if (rowMap[id] !== undefined) {
|
||||
Zotero.debug(`WARNING: _refreshRowMap(): ${this.type} row ${rowMap[id]} already found for item ${id} at ${i}`, 2);
|
||||
Zotero.debug(new Error().stack, 2);
|
||||
}
|
||||
rowMap[id] = i;
|
||||
}
|
||||
this._rowMap = rowMap;
|
||||
}
|
||||
|
||||
_onSelectionChange = () => {
|
||||
if (!this._uninitialized) {
|
||||
this.props.onSelectionChange && this.props.onSelectionChange(this.selection);
|
||||
}
|
||||
}
|
||||
|
||||
_onSelectionChangeDebounced = Zotero.Utilities.debounce(this._onSelectionChange, 100)
|
||||
|
||||
handleTwistyMouseUp = (event, index) => {
|
||||
this.toggleOpenState(index);
|
||||
event.stopPropagation();
|
||||
this.tree.focus();
|
||||
}
|
||||
|
||||
// The caller has to ensure the tree is redrawn
|
||||
ensureRowIsVisible(index) {
|
||||
this.tree && this.tree.scrollToRow(index);
|
||||
}
|
||||
|
||||
_updateHeight = () => {
|
||||
this.forceUpdate(() => {
|
||||
if (this.tree) {
|
||||
this.tree.rerender();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateHeight = Zotero.Utilities.debounce(this._updateHeight, 200);
|
||||
|
||||
updateFontSize() {
|
||||
this.tree.updateFontSize();
|
||||
}
|
||||
|
||||
setDropEffect(event, effect) {
|
||||
// On Windows (in Fx26), Firefox uses 'move' for unmodified drags
|
||||
// and 'copy'/'link' for drags with system-default modifier keys
|
||||
// as long as the actions are allowed by the initial effectAllowed set
|
||||
// in onDragStart, regardless of the effectAllowed or dropEffect set
|
||||
// in onDragOver. It doesn't seem to be possible to use 'copy' for
|
||||
// the default and 'move' for modified, as we need to in the collections
|
||||
// tree. To prevent inaccurate cursor feedback, we set effectAllowed to
|
||||
// 'copy' in onDragStart, which locks the cursor at 'copy'. ('none' still
|
||||
// changes the cursor, but 'move'/'link' do not.) It'd be better to use
|
||||
// the unadorned 'move', but we use 'copy' instead because with 'move' text
|
||||
// can't be dragged to some external programs (e.g., Chrome, Notepad++),
|
||||
// which seems worse than always showing 'copy' feedback.
|
||||
//
|
||||
// However, since effectAllowed is enforced, leaving it at 'copy'
|
||||
// would prevent our modified 'move' in the collections tree from working,
|
||||
// so we also have to set effectAllowed here (called from onDragOver) to
|
||||
// the same action as the dropEffect. This allows the dropEffect setting
|
||||
// (which we use in the tree's canDrop() and drop() to determine the desired
|
||||
// action) to be changed, even if the cursor doesn't reflect the new setting.
|
||||
if (Zotero.isWin || Zotero.isLinux) {
|
||||
event.dataTransfer.effectAllowed = effect;
|
||||
}
|
||||
event.dataTransfer.dropEffect = effect;
|
||||
}
|
||||
};
|
||||
|
||||
Zotero.Utilities.Internal.makeClassEventDispatcher(LibraryTree);
|
||||
|
||||
module.exports = LibraryTree;
|
||||
|
141
chrome/content/zotero/locateManager.jsx
Normal file
141
chrome/content/zotero/locateManager.jsx
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2020 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
import VirtualizedTable from 'components/virtualized-table';
|
||||
const { IntlProvider } = require('react-intl');
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
|
||||
var tree;
|
||||
var engines;
|
||||
const columns = [
|
||||
{ dataKey: 'visible', type: 'checkbox', fixedWidth: true, width: 28 },
|
||||
{ dataKey: 'name', label: "zotero.preferences.locate.name" },
|
||||
{ dataKey: 'description', label: "zotero.preferences.locate.description" },
|
||||
];
|
||||
|
||||
function init() {
|
||||
engines = Zotero.LocateManager.getEngines();
|
||||
const domEl = document.querySelector('#locateManager-tree');
|
||||
let elem = (
|
||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
||||
<VirtualizedTable
|
||||
getRowCount={() => engines.length}
|
||||
id="locateManager-table"
|
||||
ref={ref => tree = ref}
|
||||
renderItem={VirtualizedTable.makeRowRenderer(getRowData)}
|
||||
showHeader={true}
|
||||
multiSelect={true}
|
||||
columns={columns}
|
||||
onActivate={handleActivate}
|
||||
/>
|
||||
</IntlProvider>
|
||||
);
|
||||
return new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
||||
}
|
||||
|
||||
function getRowData(index) {
|
||||
var data = {};
|
||||
columns.forEach((column) => {
|
||||
if (column.dataKey == 'visible') {
|
||||
var value = !engines[index].hidden;
|
||||
}
|
||||
else {
|
||||
value = engines[index][column.dataKey];
|
||||
}
|
||||
data[column.dataKey] = value;
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the list of locate engines in the locate pane
|
||||
* @param {String} name of locate engine to select
|
||||
*/
|
||||
function updateTree() {
|
||||
if (!tree) return;
|
||||
tree.forceUpdate(tree.invalidate);
|
||||
}
|
||||
|
||||
function handleActivate(event, indices) {
|
||||
// Ignore Enter, only run on dblclick
|
||||
if (event.key) return;
|
||||
indices.forEach(index => engines[index].hidden = !engines[index].hidden)
|
||||
updateTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new Locate Engine to the locate pane
|
||||
**/
|
||||
/*
|
||||
function addLocateEngine() {
|
||||
// alert(Zotero.LocateManager.activeLocateEngines.join(" || "));
|
||||
var textbox = document.getElementById('locate-add-textbox');
|
||||
Zotero.LocateManager.addLocateEngine(textbox.value);
|
||||
|
||||
refreshLocateEnginesList();
|
||||
}
|
||||
*/
|
||||
|
||||
function toggleLocateEngines() {
|
||||
if (!tree) return;
|
||||
const numSelected = tree.selection.count;
|
||||
const numVisible = engines.filter((_, index) => tree.selection.isSelected(index))
|
||||
.reduce((acc, engine) => acc + (engine.hidden ? 0 : 1), 0);
|
||||
|
||||
// Make all visible, unless all selected are already visible
|
||||
var hideAll = numVisible == numSelected;
|
||||
|
||||
engines.forEach((engine, index) => {
|
||||
if (tree.selection.isSelected(index)) {
|
||||
engine.hidden = hideAll;
|
||||
}
|
||||
});
|
||||
updateTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes selected Locate Engines from the locate pane
|
||||
**/
|
||||
function deleteLocateEngine() {
|
||||
engines.forEach((engine, index) => {
|
||||
if (tree.selection.isSelected(index)) {
|
||||
Zotero.LocateManager.removeLocateEngine(engine);
|
||||
}
|
||||
});
|
||||
|
||||
tree.selection.clearSelection();
|
||||
updateTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores Default Locate Engines
|
||||
**/
|
||||
function restoreDefaultLocateEngines() {
|
||||
Zotero.LocateManager.restoreDefaultEngines();
|
||||
engines = Zotero.LocateManager.getEngines();
|
||||
updateTree();
|
||||
}
|
|
@ -29,6 +29,7 @@
|
|||
<?xml-stylesheet href="chrome://zotero-platform/content/preferences.css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/preferences.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
|
||||
<!--
|
||||
|
||||
|
@ -41,12 +42,17 @@ To add a new preference:
|
|||
in Zotero.Prefs.observe()
|
||||
|
||||
-->
|
||||
<prefwindow id="zotero-locate-manager-prefs" title="&zotero.preferences.title;" onload="refreshLocateEnginesList()"
|
||||
windowtype="zotero:pref" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<prefwindow
|
||||
id="zotero-locate-manager-prefs"
|
||||
title="&zotero.preferences.title;" onload="init()"
|
||||
windowtype="zotero:pref"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<prefpane id="zotero-prefpane-locate"
|
||||
label="&zotero.preferences.prefpane.locate;"
|
||||
image="chrome://zotero/skin/prefs-styles.png">
|
||||
image="chrome://zotero/skin/prefs-styles.png"
|
||||
flex="1">
|
||||
<!-- TODO: pic for locate pane -->
|
||||
<!-- TODO: create labels in dtd -->
|
||||
|
||||
|
@ -63,17 +69,8 @@ To add a new preference:
|
|||
|
||||
<separator class="thin"/>
|
||||
|
||||
<hbox>
|
||||
<tree flex="1" id="locateManager" hidecolumnpicker="true" rows="10"
|
||||
onselect="document.getElementById('locateManager-delete').disabled = undefined"
|
||||
editable="false">
|
||||
<treecols>
|
||||
<treecol type="checkbox" id="locateManager-checkbox" editable="true" flex="0.5"/>
|
||||
<treecol id="locateManager-name" label="&zotero.preferences.locate.name;" flex="1"/>
|
||||
<treecol id="locateManager-description" label="&zotero.preferences.locate.description;" flex="2"/>
|
||||
</treecols>
|
||||
<treechildren id="locateManager-rows"/>
|
||||
</tree>
|
||||
<hbox class="virtualized-table-container" flex="1" height="100">
|
||||
<html:div id="locateManager-tree"/>
|
||||
</hbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
@ -87,11 +84,10 @@ To add a new preference:
|
|||
<button disabled="true" id="locateManager-delete" label="-" onclick="deleteLocateEngine()" flex="0.5"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
|
||||
<!--
|
||||
TODO: Restore a way to add these
|
||||
<separator class="thin"/>
|
||||
<label id="addLocateEngineDescription" style="font-size: 10px; width: 45em; height: 6em">
|
||||
&zotero.preferences.locate.addDescription;
|
||||
</label>
|
||||
|
@ -99,183 +95,6 @@ To add a new preference:
|
|||
</groupbox>
|
||||
</prefpane>
|
||||
|
||||
<script src="chrome://zotero/content/include.js"></script>
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
function treeClick(event) {
|
||||
// We only care about primary button double and triple clicks
|
||||
if (!event || (event.detail != 2 && event.detail != 3) || event.button != 0) {
|
||||
return;
|
||||
}
|
||||
var t = event.originalTarget;
|
||||
|
||||
if (t.localName != 'treechildren') {
|
||||
return;
|
||||
}
|
||||
|
||||
var tree = t.parentNode;
|
||||
|
||||
var row = {}, col = {}, obj = {};
|
||||
var cell = tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
||||
|
||||
var treechildren = document.getElementById('locateManager-rows');
|
||||
var treeitem = treechildren.children[row.value];
|
||||
|
||||
treeitem.engine.hidden = !treeitem.engine.hidden;
|
||||
|
||||
refreshLocateEnginesList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the list of locate engines in the locate pane
|
||||
* @param {String} name of locate engine to select
|
||||
*/
|
||||
function refreshLocateEnginesList() {
|
||||
var tree = document.getElementById('locateManager');
|
||||
var treechildren = document.getElementById('locateManager-rows');
|
||||
|
||||
// add click listener
|
||||
tree.addEventListener("click", treeClick, false);
|
||||
|
||||
// store ranges
|
||||
var nRanges = tree.view.selection.getRangeCount();
|
||||
var start = {};
|
||||
var end = {};
|
||||
var ranges = [];
|
||||
for(var i=0; i<nRanges; i++) {
|
||||
tree.view.selection.getRangeAt(i, start, end);
|
||||
ranges.push([start.value, end.value]);
|
||||
}
|
||||
|
||||
// clear tree
|
||||
while (treechildren.hasChildNodes()) {
|
||||
treechildren.removeChild(treechildren.firstChild);
|
||||
}
|
||||
|
||||
// repopulate tree with available engines
|
||||
var engines = Zotero.LocateManager.getEngines();
|
||||
var i = 0;
|
||||
for (let engine of engines) {
|
||||
var treeitem = document.createElement('treeitem');
|
||||
var treerow = document.createElement('treerow');
|
||||
var checkboxCell = document.createElement('treecell');
|
||||
var nameCell = document.createElement('treecell');
|
||||
var descriptionCell = document.createElement('treecell');
|
||||
|
||||
treeitem.engine = engine;
|
||||
nameCell.setAttribute('label', engine.name);
|
||||
descriptionCell.setAttribute('label', engine.description);
|
||||
if( !engine.hidden ) {
|
||||
checkboxCell.setAttribute('value', 'true');
|
||||
}
|
||||
|
||||
treerow.appendChild(checkboxCell);
|
||||
treerow.appendChild(nameCell);
|
||||
treerow.appendChild(descriptionCell);
|
||||
treeitem.appendChild(treerow);
|
||||
treechildren.appendChild(treeitem);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// restore ranges
|
||||
for (let range of ranges) {
|
||||
if(range[1] <= engines.length-1) {
|
||||
tree.view.selection.rangedSelect(range[0], range[1], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new Locate Engine to the locate pane
|
||||
**/
|
||||
/*
|
||||
function addLocateEngine() {
|
||||
// alert(Zotero.LocateManager.activeLocateEngines.join(" || "));
|
||||
var textbox = document.getElementById('locate-add-textbox');
|
||||
Zotero.LocateManager.addLocateEngine(textbox.value);
|
||||
|
||||
refreshLocateEnginesList();
|
||||
}
|
||||
*/
|
||||
|
||||
function toggleLocateEngines() {
|
||||
// get selected engines names
|
||||
var tree = document.getElementById('locateManager');
|
||||
var treeItems = tree.lastChild.childNodes;
|
||||
var engineNames = [];
|
||||
var start = {};
|
||||
var end = {};
|
||||
var nRanges = tree.view.selection.getRangeCount();
|
||||
var numStatuses = 0;
|
||||
var engineStatusesSum = 0;
|
||||
for(var i=0; i<nRanges; i++) {
|
||||
tree.view.selection.getRangeAt(i, start, end);
|
||||
for(var j=start.value; j<=end.value; j++) {
|
||||
var engineStatus = treeItems[j].engine.hidden ? 0 : 1;
|
||||
numStatuses += 1;
|
||||
engineStatusesSum += engineStatus;
|
||||
}
|
||||
}
|
||||
|
||||
var hidden;
|
||||
switch( engineStatusesSum ) {
|
||||
case 0:
|
||||
// all off, turn all on
|
||||
hidden = false;
|
||||
break;
|
||||
|
||||
case numStatuses:
|
||||
// all on, turn all off
|
||||
hidden = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
// some on, some off. turn all on
|
||||
hidden = false;
|
||||
}
|
||||
|
||||
Zotero.LocateManager.getEngines().forEach(engine => engine.hidden = hidden);
|
||||
|
||||
refreshLocateEnginesList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes selected Locate Engines from the locate pane
|
||||
**/
|
||||
function deleteLocateEngine() {
|
||||
// get selected engines names
|
||||
var tree = document.getElementById('locateManager');
|
||||
var treeItems = tree.lastChild.childNodes;
|
||||
var engineNames = [];
|
||||
var start = {};
|
||||
var end = {};
|
||||
var nRanges = tree.view.selection.getRangeCount();
|
||||
for(var i=0; i<nRanges; i++) {
|
||||
tree.view.selection.getRangeAt(i, start, end);
|
||||
for(var j=start.value; j<=end.value; j++) {
|
||||
Zotero.LocateManager.removeEngine(treeItems[j].engine);
|
||||
}
|
||||
}
|
||||
|
||||
for(var i=0; i<engineNames.length; i++) {
|
||||
Zotero.LocateManager.removeLocateEngine(engineNames[i]);
|
||||
}
|
||||
|
||||
tree.view.selection.clearSelection();
|
||||
refreshLocateEnginesList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores Default Locate Engines
|
||||
**/
|
||||
function restoreDefaultLocateEngines() {
|
||||
Zotero.LocateManager.restoreDefaultEngines();
|
||||
refreshLocateEnginesList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
]]>
|
||||
</script>
|
||||
<script src="include.js"></script>
|
||||
<script src="locateManager.js"/>
|
||||
</prefwindow>
|
||||
|
|
|
@ -60,7 +60,7 @@ var Zotero_LocateMenu = new function() {
|
|||
}
|
||||
|
||||
// add separator at end if necessary
|
||||
if(locateMenu.lastChild.tagName !== "menuseparator") {
|
||||
if(locateMenu.lastChild && locateMenu.lastChild.tagName !== "menuseparator") {
|
||||
locateMenu.appendChild(document.createElement("menuseparator"));
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/preferences.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
|
||||
|
||||
<!DOCTYPE window [
|
||||
|
@ -36,6 +37,7 @@
|
|||
]>
|
||||
|
||||
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="" buttons="accept"
|
||||
id="zotero-librariesToSync"
|
||||
onload="Zotero_Preferences.Sync.initLibrariesToSync(); sizeToContent()" >
|
||||
|
@ -46,17 +48,17 @@
|
|||
|
||||
<groupbox>
|
||||
<caption label="&zotero.preferences.sync.librariesToSync;"/>
|
||||
|
||||
<tree id="libraries-to-sync-tree" flex="1" width="415" hidecolumnpicker="true" rows="10" seltype="single" editable="true"
|
||||
ondblclick="Zotero_Preferences.Sync.dblClickLibraryToSync(event);"
|
||||
onclick="Zotero_Preferences.Sync.clickLibraryToSync(event)"
|
||||
onkeyup="if (event.keyCode == event.DOM_VK_SPACE) { Zotero_Preferences.Sync.toggleLibraryToSync(this.currentIndex); }">
|
||||
<treecols>
|
||||
<treecol editable="false" id="libraries-to-sync-checked" label="&zotero.preferences.sync.librariesToSync.sync;"/>
|
||||
<treecol editable="false" id="libraries-to-sync-name" flex="1" label="&zotero.preferences.sync.librariesToSync.library;"/>
|
||||
</treecols>
|
||||
<treechildren id="libraries-to-sync-rows"/>
|
||||
</tree>
|
||||
|
||||
<hbox class="virtualized-table-container" flex="1" height="200" width="600">
|
||||
<html:div id="libraries-to-sync-tree"/>
|
||||
</hbox>
|
||||
<separator class="thin"/>
|
||||
|
||||
<hbox align="center">
|
||||
<hbox pack="start" flex="1">
|
||||
<button label="&zotero.preferences.sync.toggle;" onclick="Zotero_Preferences.Sync.toggleLibraryToSync()"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<?xml-stylesheet href="chrome://zotero/skin/zotero.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/overlay.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform-version/content/style.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
|
||||
<!--
|
||||
To add an observer for a preference change, add an appropriate case in
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
import FilePicker from 'zotero/filePicker';
|
||||
import FilePicker from 'zotero/modules/filePicker';
|
||||
|
||||
Zotero_Preferences.Advanced = {
|
||||
DEFAULT_OPENURL_RESOLVER: 'https://www.worldcat.org/registry/gateway',
|
||||
|
|
|
@ -25,7 +25,13 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import FilePicker from 'zotero/filePicker';
|
||||
import FilePicker from 'zotero/modules/filePicker';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var VirtualizedTable = require('components/virtualized-table');
|
||||
var { IntlProvider } = require('react-intl');
|
||||
var { makeRowRenderer } = VirtualizedTable;
|
||||
|
||||
Zotero_Preferences.Cite = {
|
||||
wordPluginIDs: new Set([
|
||||
|
@ -78,44 +84,66 @@ Zotero_Preferences.Cite = {
|
|||
* @param {String} cslID Style to select
|
||||
* @return {Promise}
|
||||
*/
|
||||
refreshStylesList: Zotero.Promise.coroutine(function* (cslID) {
|
||||
refreshStylesList: async function (cslID) {
|
||||
Zotero.debug("Refreshing styles list");
|
||||
|
||||
var treechildren = document.getElementById('styleManager-rows');
|
||||
while (treechildren.hasChildNodes()) {
|
||||
treechildren.removeChild(treechildren.firstChild);
|
||||
}
|
||||
await Zotero.Styles.init();
|
||||
|
||||
yield Zotero.Styles.init();
|
||||
var styles = Zotero.Styles.getVisible();
|
||||
var selectIndex = false;
|
||||
styles.forEach(function (style, i) {
|
||||
var treeitem = document.createElement('treeitem');
|
||||
var treerow = document.createElement('treerow');
|
||||
var titleCell = document.createElement('treecell');
|
||||
var updatedCell = document.createElement('treecell');
|
||||
|
||||
if (style.updated) {
|
||||
var updatedDate = Zotero.Date.formatDate(Zotero.Date.strToDate(style.updated), true);
|
||||
if (!this._tree) {
|
||||
const columns = [
|
||||
{
|
||||
dataKey: "title",
|
||||
label: "zotero.preferences.cite.styles.styleManager.title",
|
||||
},
|
||||
{
|
||||
dataKey: "updated",
|
||||
label: "zotero.preferences.cite.styles.styleManager.updated",
|
||||
fixedWidth: true,
|
||||
width: 100
|
||||
}
|
||||
];
|
||||
var handleKeyDown = (event) => {
|
||||
if (event.key == 'Delete' || Zotero.isMac && event.key == 'Backspace') {
|
||||
Zotero_Preferences.Cite.deleteStyle();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let styles = Zotero.Styles.getVisible()
|
||||
.map((style) => {
|
||||
return {
|
||||
title: style.title,
|
||||
updated: Zotero.Date.sqlToDate(style.updated, true).toLocaleDateString()
|
||||
};
|
||||
});
|
||||
let elem = (
|
||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
||||
<VirtualizedTable
|
||||
getRowCount={() => styles.length}
|
||||
id="styleManager-table"
|
||||
ref={ref => this._tree = ref}
|
||||
renderItem={makeRowRenderer(index => styles[index])}
|
||||
showHeader={true}
|
||||
multiSelect={true}
|
||||
columns={columns}
|
||||
staticColumns={true}
|
||||
onSelectionChange={() => document.getElementById('styleManager-delete').disabled = undefined}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</IntlProvider>
|
||||
);
|
||||
await new Promise(resolve => ReactDOM.render(elem, document.getElementById("styleManager"), resolve));
|
||||
}
|
||||
else {
|
||||
this._tree.invalidate();
|
||||
}
|
||||
if (cslID) {
|
||||
var styles = Zotero.Styles.getVisible();
|
||||
var index = styles.findIndex(style => style.styleID == cslID);
|
||||
if (index != -1) {
|
||||
this._tree.selection.select(index);
|
||||
}
|
||||
else {
|
||||
var updatedDate = '';
|
||||
}
|
||||
|
||||
treeitem.setAttribute('id', 'zotero-csl-' + style.styleID);
|
||||
titleCell.setAttribute('label', style.title);
|
||||
updatedCell.setAttribute('label', updatedDate);
|
||||
|
||||
treerow.appendChild(titleCell);
|
||||
treerow.appendChild(updatedCell);
|
||||
treeitem.appendChild(treerow);
|
||||
treechildren.appendChild(treeitem);
|
||||
|
||||
if (cslID == style.styleID) {
|
||||
document.getElementById('styleManager').view.selection.select(i);
|
||||
}
|
||||
});
|
||||
}),
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
openStylesPage: function () {
|
||||
|
@ -168,17 +196,10 @@ Zotero_Preferences.Cite = {
|
|||
**/
|
||||
deleteStyle: Zotero.Promise.coroutine(function* () {
|
||||
// get selected cslIDs
|
||||
var tree = document.getElementById('styleManager');
|
||||
var treeItems = tree.lastChild.childNodes;
|
||||
var styles = Zotero.Styles.getVisible();
|
||||
var cslIDs = [];
|
||||
var start = {};
|
||||
var end = {};
|
||||
var nRanges = tree.view.selection.getRangeCount();
|
||||
for(var i=0; i<nRanges; i++) {
|
||||
tree.view.selection.getRangeAt(i, start, end);
|
||||
for(var j=start.value; j<=end.value; j++) {
|
||||
cslIDs.push(treeItems[j].getAttribute('id').substr(11));
|
||||
}
|
||||
for (let index of this._tree.selection.selected.keys()) {
|
||||
cslIDs.push(styles[index].styleID);
|
||||
}
|
||||
|
||||
if(cslIDs.length == 0) {
|
|
@ -31,6 +31,7 @@
|
|||
]>
|
||||
|
||||
<overlay id="zotero-prefpane-cite-overlay"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<prefpane id="zotero-prefpane-cite"
|
||||
|
@ -52,15 +53,9 @@
|
|||
<groupbox flex="1">
|
||||
<caption label="&zotero.preferences.cite.styles.styleManager;"/>
|
||||
|
||||
<tree flex="1" id="styleManager" hidecolumnpicker="true" rows="6"
|
||||
onselect="document.getElementById('styleManager-delete').disabled = undefined"
|
||||
onkeypress="if (event.keyCode == event.DOM_VK_DELETE) { Zotero_Preferences.Cite.deleteSelectedStyle(); }">
|
||||
<treecols>
|
||||
<treecol id="styleManager-title" label="&zotero.preferences.cite.styles.styleManager.title;" flex="1"/>
|
||||
<treecol id="styleManager-updated" label="&zotero.preferences.cite.styles.styleManager.updated;"/>
|
||||
</treecols>
|
||||
<treechildren id="styleManager-rows"/>
|
||||
</tree>
|
||||
<hbox class="virtualized-table-container" flex="1" height="300px">
|
||||
<html:div id="styleManager"/>
|
||||
</hbox>
|
||||
<separator class="thin"/>
|
||||
<hbox align="center" flex="1" height="40">
|
||||
<label class="zotero-text-link"
|
||||
|
|
|
@ -25,6 +25,12 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var VirtualizedTable = require('components/virtualized-table');
|
||||
var { IntlProvider } = require('react-intl');
|
||||
var { makeRowRenderer } = VirtualizedTable;
|
||||
|
||||
Zotero_Preferences.Export = {
|
||||
init: Zotero.Promise.coroutine(function* () {
|
||||
this.updateQuickCopyInstructions();
|
||||
|
@ -191,29 +197,28 @@ Zotero_Preferences.Export = {
|
|||
document.getElementById('quickCopy-delete').disabled = false;
|
||||
},
|
||||
|
||||
showQuickCopySiteEditor: Zotero.Promise.coroutine(function* (index) {
|
||||
var treechildren = document.getElementById('quickCopy-siteSettings-rows');
|
||||
|
||||
var formattedName = document.getElementById('zotero-quickCopy-menu').label;
|
||||
showQuickCopySiteEditor: async function () {
|
||||
const index = this._tree.selection.focused;
|
||||
var formattedName = document.getElementById('zotero-quickCopy-menu').label;
|
||||
var locale = this._lastSelectedLocale;
|
||||
var asHTML = document.getElementById('zotero-quickCopy-copyAsHTML').checked;
|
||||
|
||||
if (index !== undefined && index > -1 && index < treechildren.childNodes.length) {
|
||||
var treerow = treechildren.childNodes[index].firstChild;
|
||||
var domain = treerow.childNodes[0].getAttribute('label');
|
||||
formattedName = treerow.childNodes[1].getAttribute('label');
|
||||
locale = treerow.childNodes[2].getAttribute('label');
|
||||
asHTML = treerow.childNodes[3].getAttribute('label') !== '';
|
||||
if (index !== undefined && index > -1 && index < this._rows.length) {
|
||||
var row = this._rows[index];
|
||||
var domain = row.domain;
|
||||
formattedName = row.format;
|
||||
locale = row.locale;
|
||||
asHTML = row.copyAsHTML;
|
||||
}
|
||||
|
||||
var format = yield Zotero.QuickCopy.getSettingFromFormattedName(formattedName);
|
||||
var format = await Zotero.QuickCopy.getSettingFromFormattedName(formattedName);
|
||||
if (asHTML) {
|
||||
format = format.replace('bibliography=', 'bibliography/html=');
|
||||
}
|
||||
|
||||
var styles = Zotero.Styles.getVisible();
|
||||
var translation = new Zotero.Translate("export");
|
||||
var translators = yield translation.getTranslators();
|
||||
var translators = await translation.getTranslators();
|
||||
|
||||
var io = { domain, format, locale, asHTML, ok: false, styles, translators };
|
||||
window.openDialog('chrome://zotero/content/preferences/quickCopySiteEditor.xul',
|
||||
|
@ -224,63 +229,95 @@ Zotero_Preferences.Export = {
|
|||
}
|
||||
|
||||
if (domain && domain != io.domain) {
|
||||
yield Zotero.DB.queryAsync("DELETE FROM settings WHERE setting='quickCopySite' AND key=?", [domain]);
|
||||
await Zotero.DB.queryAsync("DELETE FROM settings WHERE setting='quickCopySite' AND key=?", [domain]);
|
||||
}
|
||||
|
||||
var quickCopysetting = Zotero.QuickCopy.unserializeSetting(io.format);
|
||||
quickCopysetting.locale = io.locale;
|
||||
|
||||
yield Zotero.DB.queryAsync("REPLACE INTO settings VALUES ('quickCopySite', ?, ?)", [io.domain, JSON.stringify(quickCopysetting)]);
|
||||
await Zotero.DB.queryAsync("REPLACE INTO settings VALUES ('quickCopySite', ?, ?)", [io.domain, JSON.stringify(quickCopysetting)]);
|
||||
|
||||
yield Zotero.QuickCopy.loadSiteSettings();
|
||||
await Zotero.QuickCopy.loadSiteSettings();
|
||||
|
||||
yield this.refreshQuickCopySiteList();
|
||||
}),
|
||||
await this.refreshQuickCopySiteList();
|
||||
},
|
||||
|
||||
|
||||
refreshQuickCopySiteList: Zotero.Promise.coroutine(function* () {
|
||||
var treechildren = document.getElementById('quickCopy-siteSettings-rows');
|
||||
while (treechildren.hasChildNodes()) {
|
||||
treechildren.removeChild(treechildren.firstChild);
|
||||
}
|
||||
|
||||
refreshQuickCopySiteList: async function () {
|
||||
var sql = "SELECT key AS domainPath, value AS format FROM settings "
|
||||
+ "WHERE setting='quickCopySite' ORDER BY domainPath COLLATE NOCASE";
|
||||
var siteData = yield Zotero.DB.queryAsync(sql);
|
||||
var siteData = await Zotero.DB.queryAsync(sql);
|
||||
|
||||
for (var i=0; i<siteData.length; i++) {
|
||||
var treeitem = document.createElement('treeitem');
|
||||
var treerow = document.createElement('treerow');
|
||||
var domainCell = document.createElement('treecell');
|
||||
var formatCell = document.createElement('treecell');
|
||||
var localeCell = document.createElement('treecell');
|
||||
var htmlCell = document.createElement('treecell');
|
||||
|
||||
domainCell.setAttribute('label', siteData[i].domainPath);
|
||||
|
||||
var formattedName = yield Zotero.QuickCopy.getFormattedNameFromSetting(siteData[i].format);
|
||||
formatCell.setAttribute('label', formattedName);
|
||||
|
||||
var format = Zotero.QuickCopy.unserializeSetting(siteData[i].format);
|
||||
localeCell.setAttribute('label', format.locale);
|
||||
htmlCell.setAttribute('label', format.contentType == 'html' ? ' ✓ ' : '');
|
||||
|
||||
treerow.appendChild(domainCell);
|
||||
treerow.appendChild(formatCell);
|
||||
treerow.appendChild(localeCell);
|
||||
treerow.appendChild(htmlCell);
|
||||
treeitem.appendChild(treerow);
|
||||
treechildren.appendChild(treeitem);
|
||||
this._rows = [];
|
||||
|
||||
for (let row of siteData) {
|
||||
var formattedName = await Zotero.QuickCopy.getFormattedNameFromSetting(row.format);
|
||||
var format = Zotero.QuickCopy.unserializeSetting(row.format);
|
||||
this._rows.push({
|
||||
domain: row.domainPath,
|
||||
format: formattedName,
|
||||
locale: format.locale,
|
||||
copyAsHTML: format.contentType == 'html',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (!this._tree) {
|
||||
const columns = [
|
||||
{
|
||||
dataKey: "domain",
|
||||
label: "zotero.preferences.quickCopy.siteEditor.domainPath",
|
||||
flex: 2
|
||||
},
|
||||
{
|
||||
dataKey: "format",
|
||||
label: "zotero.preferences.quickCopy.siteEditor.format",
|
||||
flex: 4
|
||||
},
|
||||
{
|
||||
dataKey: "locale",
|
||||
label: "zotero.preferences.quickCopy.siteEditor.locale",
|
||||
flex: 1
|
||||
},
|
||||
{
|
||||
dataKey: "copyAsHTML",
|
||||
label: "HTML",
|
||||
type: 'checkbox',
|
||||
fixedWidth: true,
|
||||
width: 55,
|
||||
}
|
||||
];
|
||||
var handleKeyDown = (event) => {
|
||||
if (event.key == 'Delete' || Zotero.isMac && event.key == 'Backspace') {
|
||||
Zotero_Preferences.Export.deleteSelectedQuickCopySite();
|
||||
}
|
||||
};
|
||||
let elem = (
|
||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
||||
<VirtualizedTable
|
||||
getRowCount={() => this._rows.length}
|
||||
id="quickCopy-siteSettings-table"
|
||||
ref={ref => this._tree = ref}
|
||||
renderItem={makeRowRenderer(index => this._rows[index])}
|
||||
showHeader={true}
|
||||
columns={columns}
|
||||
staticColumns={true}
|
||||
onSelectionChange={() => Zotero_Preferences.Export.enableQuickCopySiteButtons()}
|
||||
onKeyDown={handleKeyDown}
|
||||
onActivate={(event, indices) => Zotero_Preferences.Export.showQuickCopySiteEditor()}
|
||||
/>
|
||||
</IntlProvider>
|
||||
);
|
||||
await new Promise(resolve => ReactDOM.render(elem, document.getElementById("quickCopy-siteSettings"), resolve));
|
||||
} else {
|
||||
this._tree.invalidate();
|
||||
}
|
||||
|
||||
this.disableQuickCopySiteButtons();
|
||||
}),
|
||||
},
|
||||
|
||||
|
||||
deleteSelectedQuickCopySite: Zotero.Promise.coroutine(function* () {
|
||||
var tree = document.getElementById('quickCopy-siteSettings');
|
||||
var treeitem = tree.lastChild.childNodes[tree.currentIndex];
|
||||
var domainPath = treeitem.firstChild.firstChild.getAttribute('label');
|
||||
var domainPath = this._rows[this._tree.selection.focused].domain;
|
||||
yield Zotero.DB.queryAsync("DELETE FROM settings WHERE setting='quickCopySite' AND key=?", [domainPath]);
|
||||
yield Zotero.QuickCopy.loadSiteSettings();
|
||||
yield this.refreshQuickCopySiteList();
|
|
@ -30,7 +30,9 @@
|
|||
%common;
|
||||
]>
|
||||
|
||||
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<overlay
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<prefpane id="zotero-prefpane-export"
|
||||
onpaneload="Zotero_Preferences.Export.init()"
|
||||
helpTopic="export">
|
||||
|
@ -67,18 +69,9 @@
|
|||
<separator/>
|
||||
|
||||
<label value="&zotero.preferences.quickCopy.siteEditor.setings;" control="quickCopy-siteSettings"/>
|
||||
<tree flex="1" id="quickCopy-siteSettings" hidecolumnpicker="true" rows="6" seltype="single"
|
||||
ondblclick="Zotero_Preferences.Export.showQuickCopySiteEditor(this.currentIndex)"
|
||||
onselect="Zotero_Preferences.Export.enableQuickCopySiteButtons()"
|
||||
onkeypress="if (event.keyCode == event.DOM_VK_DELETE) { Zotero_Preferences.Export.deleteSelectedQuickCopySite(); }">
|
||||
<treecols>
|
||||
<treecol id="quickCopy-urlColumn" label="&zotero.preferences.quickCopy.siteEditor.domainPath;" flex="1"/>
|
||||
<treecol id="quickCopy-formatColumn" label="&zotero.preferences.quickCopy.siteEditor.format;" flex="2"/>
|
||||
<treecol id="quickCopy-localeColumn" label="&zotero.preferences.quickCopy.siteEditor.locale;"/>
|
||||
<treecol id="quickCopy-copyAsHTML" label="HTML"/>
|
||||
</treecols>
|
||||
<treechildren id="quickCopy-siteSettings-rows"/>
|
||||
</tree>
|
||||
<hbox class="virtualized-table-container" flex="1" height="300px">
|
||||
<html:div id="quickCopy-siteSettings"/>
|
||||
</hbox>
|
||||
<separator class="thin"/>
|
||||
<hbox>
|
||||
<button disabled="true" id="quickCopy-edit" label="&zotero.general.edit;"
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2006–2013 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
-->
|
||||
<!DOCTYPE prefwindow [
|
||||
<!ENTITY % preferencesDTD SYSTEM "chrome://zotero/locale/preferences.dtd"> %preferencesDTD;
|
||||
<!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd"> %zoteroDTD;
|
||||
]>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/preferences.css"?>
|
||||
|
||||
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<prefwindow id="zotero-prefs">
|
||||
<prefpane id="zotero-prefpane-proxies"
|
||||
label="&zotero.preferences.prefpane.proxies;"
|
||||
image="chrome://zotero/skin/prefs-proxies.png"
|
||||
onpaneload="Zotero_Preferences.Proxies.init()"
|
||||
position="6"
|
||||
helpTopic="proxies">
|
||||
<description width="45em" style="font-size: 12px">
|
||||
&zotero.preferences.proxies.desc_before_link;
|
||||
<label class="zotero-text-link" href="http://www.zotero.org/support/proxies"
|
||||
value="&zotero.preferences.proxies.desc_link;"/>
|
||||
&zotero.preferences.proxies.desc_after_link;
|
||||
</description>
|
||||
|
||||
<command id="zotero-proxies-update" oncommand="Zotero_Preferences.Proxies.updateProxyPrefs()"/>
|
||||
<checkbox id="zotero-proxies-transparent" label="&zotero.preferences.proxies.transparent;"
|
||||
command="zotero-proxies-update"/>
|
||||
<vbox style="margin-left: 1em">
|
||||
<checkbox id="zotero-proxies-autoRecognize" label="&zotero.preferences.proxies.autoRecognize;"
|
||||
command="zotero-proxies-update"/>
|
||||
<checkbox id="zotero-proxies-showRedirectNotification" label="&zotero.preferences.proxies.showRedirectNotification;"
|
||||
command="zotero-proxies-update"/>
|
||||
<hbox style="display: block; line-height: 1.75em">
|
||||
<checkbox id="zotero-proxies-disableByDomain-checkbox"
|
||||
label="&zotero.preferences.proxies.disableByDomain;"
|
||||
command="zotero-proxies-update"/>
|
||||
<textbox id="zotero-proxies-disableByDomain-textbox"
|
||||
onchange="Zotero_Preferences.Proxies.updateProxyPrefs()"
|
||||
flex="1" style="max-width: 11.75em"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<groupbox flex="1" id="proxyGroup">
|
||||
<caption label="&zotero.preferences.proxies.configured;"/>
|
||||
|
||||
<tree id="proxyTree" hidecolumnpicker="true" rows="6" seltype="single"
|
||||
ondblclick="Zotero_Preferences.Proxies.showProxyEditor(this.currentIndex)" onselect="Zotero_Preferences.Proxies.enableProxyButtons()"
|
||||
onkeypress="if (event.keyCode == event.DOM_VK_DELETE) { Zotero_Preferences.Proxies.deleteProxy(); }">
|
||||
<treecols>
|
||||
<treecol id="proxyTree-hostname" label="&zotero.preferences.proxies.hostname;" flex="1"/>
|
||||
<treecol id="proxyTree-scheme" label="&zotero.preferences.proxies.scheme;" flex="3"/>
|
||||
</treecols>
|
||||
<treechildren id="proxyTree-rows"/>
|
||||
</tree>
|
||||
<separator class="thin"/>
|
||||
<hbox>
|
||||
<button disabled="true" id="proxyTree-edit" label="&zotero.general.edit;" onclick="Zotero_Preferences.Proxies.showProxyEditor(document.getElementById('proxyTree').currentIndex)"/>
|
||||
<spacer flex="1"/>
|
||||
<button disabled="true" id="proxyTree-delete" label="-" onclick="Zotero_Preferences.Proxies.deleteProxy()"/>
|
||||
<button label="+" id="proxyTree-add" onclick="Zotero_Preferences.Proxies.showProxyEditor()"/>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
||||
<separator/>
|
||||
<separator/>
|
||||
</prefpane>
|
||||
|
||||
<script src="preferences_proxies.js" type="application/javascript"/>
|
||||
</prefwindow>
|
||||
</overlay>
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
import FilePicker from 'zotero/filePicker';
|
||||
import FilePicker from 'zotero/modules/filePicker';
|
||||
|
||||
Zotero_Preferences.General = {
|
||||
init: function () {
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2006–2013 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
Zotero_Preferences.Proxies = {
|
||||
_proxies: null,
|
||||
|
||||
|
||||
init: function () {
|
||||
this.refreshProxyList();
|
||||
this.updateCheckboxState();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates proxy autoRecognize and transparent settings based on checkboxes
|
||||
*/
|
||||
updateProxyPrefs: function () {
|
||||
var transparent = document.getElementById('zotero-proxies-transparent').checked;
|
||||
Zotero.Prefs.set("proxies.transparent", transparent);
|
||||
Zotero.Prefs.set("proxies.autoRecognize", document.getElementById('zotero-proxies-autoRecognize').checked);
|
||||
Zotero.Prefs.set("proxies.showRedirectNotification", document.getElementById('zotero-proxies-showRedirectNotification').checked);
|
||||
Zotero.Prefs.set("proxies.disableByDomainString", document.getElementById('zotero-proxies-disableByDomain-textbox').value);
|
||||
Zotero.Prefs.set("proxies.disableByDomain", document.getElementById('zotero-proxies-disableByDomain-checkbox').checked &&
|
||||
document.getElementById('zotero-proxies-disableByDomain-textbox').value != "");
|
||||
|
||||
Zotero.Proxies.init();
|
||||
|
||||
this.updateCheckboxState();
|
||||
},
|
||||
|
||||
|
||||
updateCheckboxState: function() {
|
||||
var transparent = document.getElementById('zotero-proxies-transparent').checked;
|
||||
|
||||
document.getElementById('proxyTree-add').disabled =
|
||||
document.getElementById('proxyTree-delete').disabled =
|
||||
document.getElementById('proxyTree').disabled =
|
||||
document.getElementById('zotero-proxies-autoRecognize').disabled =
|
||||
document.getElementById('zotero-proxies-showRedirectNotification').disabled =
|
||||
document.getElementById('zotero-proxies-disableByDomain-checkbox').disabled =
|
||||
document.getElementById('zotero-proxies-disableByDomain-textbox').disabled =
|
||||
!transparent;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Enables UI buttons when proxy is selected
|
||||
*/
|
||||
enableProxyButtons: function () {
|
||||
document.getElementById('proxyTree-edit').disabled = false;
|
||||
document.getElementById('proxyTree-delete').disabled = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a proxy to the proxy pane
|
||||
*/
|
||||
showProxyEditor: function (index) {
|
||||
if(index == -1) return;
|
||||
window.openDialog('chrome://zotero/content/preferences/proxyEditor.xul',
|
||||
"zotero-preferences-proxyEditor", "chrome,modal,centerscreen",
|
||||
index !== undefined ? this._proxies[index] : null);
|
||||
this.refreshProxyList();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Deletes the currently selected proxy
|
||||
*/
|
||||
deleteProxy: function () {
|
||||
if(document.getElementById('proxyTree').currentIndex == -1) return;
|
||||
this._proxies[document.getElementById('proxyTree').currentIndex].erase();
|
||||
this.refreshProxyList();
|
||||
document.getElementById('proxyTree-delete').disabled = true;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Refreshes the proxy pane
|
||||
*/
|
||||
refreshProxyList: function () {
|
||||
if(!document.getElementById("zotero-prefpane-proxies")) return;
|
||||
|
||||
// get and sort proxies
|
||||
this._proxies = Zotero.Proxies.proxies.slice();
|
||||
for(var i=0; i<this._proxies.length; i++) {
|
||||
if(!this._proxies[i].proxyID) {
|
||||
this._proxies.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
this._proxies = this._proxies.sort(function(a, b) {
|
||||
if(a.multiHost) {
|
||||
if(b.multiHost) {
|
||||
if(a.hosts[0] < b.hosts[0]) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if(b.multiHost) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(a.scheme < b.scheme) {
|
||||
return -1;
|
||||
} else if(b.scheme > a.scheme) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
// erase old children
|
||||
var treechildren = document.getElementById('proxyTree-rows');
|
||||
while (treechildren.hasChildNodes()) {
|
||||
treechildren.removeChild(treechildren.firstChild);
|
||||
}
|
||||
|
||||
// add proxies to list
|
||||
for (var i=0; i<this._proxies.length; i++) {
|
||||
var treeitem = document.createElement('treeitem');
|
||||
var treerow = document.createElement('treerow');
|
||||
var hostnameCell = document.createElement('treecell');
|
||||
var schemeCell = document.createElement('treecell');
|
||||
|
||||
hostnameCell.setAttribute('label', this._proxies[i].multiHost ? Zotero.getString("proxies.multiSite") : this._proxies[i].hosts[0]);
|
||||
schemeCell.setAttribute('label', this._proxies[i].scheme);
|
||||
|
||||
treerow.appendChild(hostnameCell);
|
||||
treerow.appendChild(schemeCell);
|
||||
treeitem.appendChild(treerow);
|
||||
treechildren.appendChild(treeitem);
|
||||
}
|
||||
|
||||
document.getElementById('proxyTree').currentIndex = -1;
|
||||
document.getElementById('proxyTree-edit').disabled = true;
|
||||
document.getElementById('proxyTree-delete').disabled = true;
|
||||
document.getElementById('zotero-proxies-transparent').checked = Zotero.Prefs.get("proxies.transparent");
|
||||
document.getElementById('zotero-proxies-autoRecognize').checked = Zotero.Prefs.get("proxies.autoRecognize");
|
||||
document.getElementById('zotero-proxies-showRedirectNotification').checked = Zotero.Prefs.get("proxies.showRedirectNotification");
|
||||
document.getElementById('zotero-proxies-disableByDomain-checkbox').checked = Zotero.Prefs.get("proxies.disableByDomain");
|
||||
document.getElementById('zotero-proxies-disableByDomain-textbox').value = Zotero.Prefs.get("proxies.disableByDomainString");
|
||||
}
|
||||
};
|
|
@ -28,6 +28,13 @@ Components.utils.import("resource://gre/modules/Services.jsm");
|
|||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
Components.utils.import("resource://zotero/config.js");
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var VirtualizedTable = require('components/virtualized-table');
|
||||
var { getDOMElement } = require('components/icons');
|
||||
var { IntlProvider } = require('react-intl');
|
||||
var { renderCell } = VirtualizedTable;
|
||||
|
||||
Zotero_Preferences.Sync = {
|
||||
checkmarkChar: '\u2705',
|
||||
noChar: '\uD83D\uDEAB',
|
||||
|
@ -227,102 +234,106 @@ Zotero_Preferences.Sync = {
|
|||
},
|
||||
|
||||
|
||||
dblClickLibraryToSync: function (event) {
|
||||
var tree = document.getElementById("libraries-to-sync-tree");
|
||||
var row = {}, col = {}, child = {};
|
||||
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child);
|
||||
|
||||
// Below the list or on checkmark column
|
||||
if (!col.value || col.value.element.id == 'libraries-to-sync-checked') {
|
||||
return;
|
||||
}
|
||||
// if dblclicked anywhere but the checkbox update pref
|
||||
return this.toggleLibraryToSync(row.value);
|
||||
},
|
||||
|
||||
|
||||
clickLibraryToSync: function (event) {
|
||||
var tree = document.getElementById("libraries-to-sync-tree");
|
||||
var row = {}, col = {}, child = {};
|
||||
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child);
|
||||
|
||||
// Below the list or not on checkmark column
|
||||
if (!col.value || col.value.element.id != 'libraries-to-sync-checked') {
|
||||
return;
|
||||
}
|
||||
// if clicked on checkbox update pref
|
||||
return this.toggleLibraryToSync(row.value);
|
||||
},
|
||||
|
||||
|
||||
toggleLibraryToSync: function (index) {
|
||||
var treechildren = document.getElementById('libraries-to-sync-rows');
|
||||
if (index >= treechildren.childNodes.length) {
|
||||
return;
|
||||
}
|
||||
var row = treechildren.childNodes[index];
|
||||
var val = row.firstChild.childNodes[1].getAttribute('value');
|
||||
if (!val) {
|
||||
return
|
||||
}
|
||||
toggleLibraryToSync: function () {
|
||||
const index = this._tree.selection.focused;
|
||||
if (index == -1 || !this._rows[index].editable) return;
|
||||
const row = this._rows[index];
|
||||
this._rows[index].checked = !this._rows[index].checked;
|
||||
this._tree.invalidateRow(index);
|
||||
|
||||
var librariesToSkip = JSON.parse(Zotero.Prefs.get('sync.librariesToSkip') || '[]');
|
||||
var indexOfId = librariesToSkip.indexOf(val);
|
||||
var indexOfId = librariesToSkip.indexOf(row.id);
|
||||
if (indexOfId == -1) {
|
||||
librariesToSkip.push(val);
|
||||
} else {
|
||||
librariesToSkip.push(row.id);
|
||||
}
|
||||
else {
|
||||
librariesToSkip.splice(indexOfId, 1);
|
||||
}
|
||||
Zotero.Prefs.set('sync.librariesToSkip', JSON.stringify(librariesToSkip));
|
||||
|
||||
var cell = row.firstChild.firstChild;
|
||||
var spacing = Zotero.isWin ? ' ' : ' ';
|
||||
cell.setAttribute('label', spacing + (indexOfId != -1 ? this.checkmarkChar : this.noChar));
|
||||
cell.setAttribute('value', indexOfId != -1);
|
||||
},
|
||||
|
||||
|
||||
initLibrariesToSync: Zotero.Promise.coroutine(function* () {
|
||||
var tree = document.getElementById("libraries-to-sync-tree");
|
||||
var treechildren = document.getElementById('libraries-to-sync-rows');
|
||||
while (treechildren.hasChildNodes()) {
|
||||
treechildren.removeChild(treechildren.firstChild);
|
||||
initLibrariesToSync: async function () {
|
||||
const columns = [
|
||||
{
|
||||
dataKey: "checked",
|
||||
label: "zotero.preferences.sync.librariesToSync.sync",
|
||||
fixedWidth: true,
|
||||
width: '60'
|
||||
},
|
||||
{
|
||||
dataKey: "name",
|
||||
label: "zotero.preferences.sync.librariesToSync.library"
|
||||
}
|
||||
];
|
||||
this._rows = [];
|
||||
function renderItem(index, selection, oldDiv=null, columns) {
|
||||
const row = this._rows[index];
|
||||
let div;
|
||||
if (oldDiv) {
|
||||
div = oldDiv;
|
||||
div.innerHTML = "";
|
||||
}
|
||||
else {
|
||||
div = document.createElementNS("http://www.w3.org/1999/xhtml", 'div');
|
||||
div.className = "row";
|
||||
}
|
||||
div.classList.toggle('selected', selection.isSelected(index));
|
||||
|
||||
for (let column of columns) {
|
||||
if (column.dataKey === 'checked') {
|
||||
let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
||||
span.className = `cell ${column.className}`;
|
||||
if (row.id != 'loading') {
|
||||
span.innerText = row.checked ? this.checkmarkChar : this.noChar;
|
||||
span.style.textAlign = 'center';
|
||||
}
|
||||
div.appendChild(span);
|
||||
}
|
||||
else {
|
||||
div.appendChild(renderCell(index, row[column.dataKey], column));
|
||||
}
|
||||
}
|
||||
return div;
|
||||
}
|
||||
let elem = (
|
||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
||||
<VirtualizedTable
|
||||
getRowCount={() => this._rows.length}
|
||||
id="librariesToSync-table"
|
||||
ref={ref => this._tree = ref}
|
||||
renderItem={renderItem.bind(this)}
|
||||
showHeader={true}
|
||||
columns={columns}
|
||||
staticColumns={true}
|
||||
onActivate={Zotero_Preferences.Sync.toggleLibraryToSync.bind(this)}
|
||||
/>
|
||||
</IntlProvider>
|
||||
);
|
||||
|
||||
ReactDOM.render(elem, document.getElementById("libraries-to-sync-tree"));
|
||||
|
||||
var addRow = function (libraryName, id, checked=false, editable=true) {
|
||||
var treeitem = document.createElement('treeitem');
|
||||
var treerow = document.createElement('treerow');
|
||||
var checkboxCell = document.createElement('treecell');
|
||||
var nameCell = document.createElement('treecell');
|
||||
|
||||
nameCell.setAttribute('label', libraryName);
|
||||
nameCell.setAttribute('value', id);
|
||||
nameCell.setAttribute('editable', false);
|
||||
var spacing = Zotero.isWin ? ' ' : ' ';
|
||||
checkboxCell.setAttribute(
|
||||
'label',
|
||||
id == 'loading' ? '' : (spacing + (checked ? this.checkmarkChar : this.noChar))
|
||||
);
|
||||
checkboxCell.setAttribute('value', checked);
|
||||
checkboxCell.setAttribute('editable', false);
|
||||
|
||||
treerow.appendChild(checkboxCell);
|
||||
treerow.appendChild(nameCell);
|
||||
treeitem.appendChild(treerow);
|
||||
treechildren.appendChild(treeitem);
|
||||
this._rows.push({
|
||||
name: libraryName,
|
||||
id,
|
||||
checked,
|
||||
editable
|
||||
});
|
||||
this._tree.invalidate();
|
||||
}.bind(this);
|
||||
|
||||
// Add loading row while we're loading a group list
|
||||
var loadingLabel = Zotero.getString("zotero.preferences.sync.librariesToSync.loadingLibraries");
|
||||
addRow(loadingLabel, "loading", false, false);
|
||||
|
||||
var apiKey = yield Zotero.Sync.Data.Local.getAPIKey();
|
||||
var client = Zotero.Sync.Runner.getAPIClient({apiKey});
|
||||
var apiKey = await Zotero.Sync.Data.Local.getAPIKey();
|
||||
var client = Zotero.Sync.Runner.getAPIClient({ apiKey });
|
||||
var groups = [];
|
||||
try {
|
||||
// Load up remote groups
|
||||
var keyInfo = yield Zotero.Sync.Runner.checkAccess(client, {timeout: 5000});
|
||||
groups = yield client.getGroups(keyInfo.userID);
|
||||
var keyInfo = await Zotero.Sync.Runner.checkAccess(client, {timeout: 5000});
|
||||
groups = await client.getGroups(keyInfo.userID);
|
||||
}
|
||||
catch (e) {
|
||||
// Connection problems
|
||||
|
@ -342,11 +353,12 @@ Zotero_Preferences.Sync = {
|
|||
}
|
||||
|
||||
// Remove the loading row
|
||||
treechildren.removeChild(treechildren.firstChild);
|
||||
this._rows = [];
|
||||
this._tree.invalidate();
|
||||
|
||||
var librariesToSkip = JSON.parse(Zotero.Prefs.get('sync.librariesToSkip') || '[]');
|
||||
// Add default rows
|
||||
addRow(Zotero.getString("pane.collections.libraryAndFeeds"), "L" + Zotero.Libraries.userLibraryID,
|
||||
addRow(Zotero.getString("pane.collections.libraryAndFeeds"), "L" + Zotero.Libraries.userLibraryID,
|
||||
librariesToSkip.indexOf("L" + Zotero.Libraries.userLibraryID) == -1);
|
||||
|
||||
// Sort groups
|
||||
|
@ -356,8 +368,7 @@ Zotero_Preferences.Sync = {
|
|||
for (let group of groups) {
|
||||
addRow(group.data.name, "G" + group.id, librariesToSkip.indexOf("G" + group.id) == -1);
|
||||
}
|
||||
}),
|
||||
|
||||
},
|
||||
|
||||
updateStorageSettingsUI: Zotero.Promise.coroutine(function* () {
|
||||
this.unverifyStorageServer();
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2009 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
var Zotero_ProxyEditor = new function() {
|
||||
var treechildren;
|
||||
var tree;
|
||||
var treecol;
|
||||
var multiSite;
|
||||
|
||||
/**
|
||||
* Called when this window is first opened. Sets values if necessary
|
||||
*/
|
||||
this.load = function() {
|
||||
treechildren = document.getElementById("zotero-proxies-hostname-multiSite-tree-children");
|
||||
tree = document.getElementById("zotero-proxies-hostname-multiSite-tree");
|
||||
multiSite = document.getElementById("zotero-proxies-multiSite");
|
||||
|
||||
if(window.arguments && window.arguments[0]) {
|
||||
var proxy = window.arguments[0];
|
||||
document.getElementById("zotero-proxies-scheme").value = proxy.scheme;
|
||||
document.getElementById("zotero-proxies-multiSite").checked = !!proxy.multiHost;
|
||||
if(proxy.hosts) {
|
||||
if(proxy.multiHost) {
|
||||
this.multiSiteChanged();
|
||||
for (var i=0; i<proxy.hosts.length; i++) {
|
||||
_addTreeElement(proxy.hosts[i]);
|
||||
}
|
||||
document.getElementById("zotero-proxies-autoAssociate").checked = proxy.autoAssociate;
|
||||
} else {
|
||||
document.getElementById("zotero-proxies-hostname-text").value = proxy.hosts[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.sizeToContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a user checks/unchecks the Multi-Site checkbox. Shows or hides multi-site
|
||||
* hostname specification box as necessary.
|
||||
*/
|
||||
this.multiSiteChanged = function() {
|
||||
document.getElementById("zotero-proxies-hostname-multiSite").hidden = !multiSite.checked;
|
||||
document.getElementById("zotero-proxies-hostname-multiSite-description").hidden = !multiSite.checked;
|
||||
document.getElementById("zotero-proxies-hostname").hidden = multiSite.checked;
|
||||
window.sizeToContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a row is selected
|
||||
*/
|
||||
this.select = function() {
|
||||
document.getElementById("zotero-proxies-delete").disabled = tree.selectedIndex == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a host when in multi-host mode
|
||||
*/
|
||||
this.addHost = function() {
|
||||
_addTreeElement();
|
||||
tree.startEditing(treechildren.childNodes.length-1, tree.columns.getFirstColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a host when in multi-host mode
|
||||
*/
|
||||
this.deleteHost = function() {
|
||||
if(tree.currentIndex == -1) return;
|
||||
treechildren.removeChild(treechildren.childNodes[tree.currentIndex]);
|
||||
document.getElementById("zotero-proxies-delete").disabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user clicks "OK." Updates proxy for Zotero.Proxy.
|
||||
*/
|
||||
this.accept = function() {
|
||||
var proxy = window.arguments && window.arguments[0] ? window.arguments[0] : new Zotero.Proxy();
|
||||
|
||||
proxy.scheme = document.getElementById("zotero-proxies-scheme").value;
|
||||
proxy.multiHost = multiSite.checked;
|
||||
if(proxy.multiHost) {
|
||||
proxy.hosts = [];
|
||||
var treecol = tree.columns.getFirstColumn();
|
||||
for(var i=0; i<tree.view.rowCount; i++) {
|
||||
var host = tree.view.getCellText(i, treecol);
|
||||
if(host) proxy.hosts.push(host);
|
||||
}
|
||||
proxy.autoAssociate = document.getElementById("zotero-proxies-autoAssociate").checked;
|
||||
} else {
|
||||
proxy.hosts = [document.getElementById("zotero-proxies-hostname-text").value];
|
||||
}
|
||||
|
||||
var hasErrors = proxy.validate();
|
||||
if(hasErrors) {
|
||||
error = hasErrors.shift();
|
||||
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
promptService.alert(
|
||||
window, Zotero.getString("proxies.error"),
|
||||
Zotero.getString("proxies.error." + error, hasErrors)
|
||||
);
|
||||
if(window.arguments && window.arguments[0]) proxy.revert(); // async
|
||||
return false;
|
||||
}
|
||||
proxy.save(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an element to the tree
|
||||
*/
|
||||
function _addTreeElement(label) {
|
||||
var treeitem = document.createElement('treeitem');
|
||||
var treerow = document.createElement('treerow');
|
||||
var treecell = document.createElement('treecell');
|
||||
|
||||
if(label) treecell.setAttribute('label', label);
|
||||
|
||||
treerow.appendChild(treecell);
|
||||
treeitem.appendChild(treerow);
|
||||
treechildren.appendChild(treeitem);
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2009 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
-->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/preferences.css"?>
|
||||
|
||||
<!DOCTYPE window SYSTEM "chrome://zotero/locale/preferences.dtd">
|
||||
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="" buttons="cancel,accept"
|
||||
id="zotero-proxyEditor"
|
||||
onload="Zotero_ProxyEditor.load();"
|
||||
ondialogaccept="return Zotero_ProxyEditor.accept();">
|
||||
|
||||
<script src="chrome://zotero/content/include.js"/>
|
||||
<script src="proxyEditor.js"/>
|
||||
|
||||
<checkbox id="zotero-proxies-multiSite" label="&zotero.preferences.proxies.multiSite;"
|
||||
oncommand="Zotero_ProxyEditor.multiSiteChanged()"/>
|
||||
<separator class="thin"/>
|
||||
<vbox id="zotero-proxies-hostname-multiSite" hidden="true">
|
||||
<checkbox id="zotero-proxies-autoAssociate" label="&zotero.preferences.proxies.autoAssociate;"/>
|
||||
<tree flex="1" id="zotero-proxies-hostname-multiSite-tree" hidecolumnpicker="true" editable="true" rows="6"
|
||||
onkeypress="if (event.keyCode == event.DOM_VK_DELETE) { Zotero_ProxyEditor.remove(); }"
|
||||
onselect="Zotero_ProxyEditor.select();">
|
||||
<treecols>
|
||||
<treecol label="&zotero.preferences.proxies.hostname;" id="zotero-proxies-hostname-multiSite-tree-col" flex="1"/>
|
||||
</treecols>
|
||||
<treechildren id="zotero-proxies-hostname-multiSite-tree-children"/>
|
||||
</tree>
|
||||
<hbox pack="end">
|
||||
<button id="zotero-proxies-delete" label="-" onclick="Zotero_ProxyEditor.deleteHost()" disabled="true"/>
|
||||
<button id="zotero-proxies-add" label="+" onclick="Zotero_ProxyEditor.addHost()"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox id="zotero-proxies-hostname">
|
||||
<label value="&zotero.preferences.proxies.hostname;:" control="zotero-proxies-hostname-text"/>
|
||||
<textbox id="zotero-proxies-hostname-text"/>
|
||||
</vbox>
|
||||
<separator class="thin"/>
|
||||
<label value="&zotero.preferences.proxies.scheme;:" control="zotero-proxies-scheme"/>
|
||||
<textbox id="zotero-proxies-scheme"/>
|
||||
<label value="&zotero.preferences.proxies.variables;"/>
|
||||
<label value="&zotero.preferences.proxies.h_variable;" id="zotero-proxies-hostname-multiSite-description" hidden="true"/>
|
||||
<label value="&zotero.preferences.proxies.p_variable;"/>
|
||||
<label value="&zotero.preferences.proxies.d_variable;"/>
|
||||
<label value="&zotero.preferences.proxies.f_variable;"/>
|
||||
<label value="&zotero.preferences.proxies.a_variable;"/>
|
||||
</dialog>
|
134
chrome/content/zotero/progressQueueDialog.jsx
Normal file
134
chrome/content/zotero/progressQueueDialog.jsx
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2018 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import VirtualizedTable from 'components/virtualized-table';
|
||||
const { IntlProvider } = require('react-intl');
|
||||
const { getDOMElement } = require('components/icons');
|
||||
const { renderCell } = VirtualizedTable;
|
||||
|
||||
let _progressIndicator = null;
|
||||
let _progressQueue;
|
||||
let _tree;
|
||||
|
||||
function _getImageByStatus(status) {
|
||||
if (status === Zotero.ProgressQueue.ROW_PROCESSING) {
|
||||
return getDOMElement('IconArrowRefresh');
|
||||
}
|
||||
else if (status === Zotero.ProgressQueue.ROW_FAILED) {
|
||||
return getDOMElement('IconCross');
|
||||
}
|
||||
else if (status === Zotero.ProgressQueue.ROW_SUCCEEDED) {
|
||||
return getDOMElement('IconTick');
|
||||
}
|
||||
return document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
||||
}
|
||||
|
||||
function _rowToTreeItem(index, selection, oldDiv=null, columns) {
|
||||
let rows = _progressQueue.getRows();
|
||||
let row = rows[index];
|
||||
|
||||
let div;
|
||||
if (oldDiv) {
|
||||
div = oldDiv;
|
||||
div.innerHTML = "";
|
||||
}
|
||||
else {
|
||||
div = document.createElementNS("http://www.w3.org/1999/xhtml", 'div');
|
||||
div.className = "row";
|
||||
}
|
||||
|
||||
div.classList.toggle('selected', selection.isSelected(index));
|
||||
|
||||
for (let column of columns) {
|
||||
if (column.dataKey === 'success') {
|
||||
let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
||||
span.className = `cell icon ${column.className}`;
|
||||
span.appendChild(_getImageByStatus(row.status));
|
||||
div.appendChild(span);
|
||||
}
|
||||
else {
|
||||
div.appendChild(renderCell(index, row[column.dataKey], column));
|
||||
}
|
||||
}
|
||||
return div;
|
||||
}
|
||||
|
||||
function _init() {
|
||||
var io = window.arguments[0];
|
||||
_progressQueue = io.progressQueue;
|
||||
document.title = Zotero.getString(_progressQueue.getTitle());
|
||||
|
||||
let columns = _progressQueue.getColumns();
|
||||
|
||||
const tableColumns = [
|
||||
{ dataKey: 'success', fixedWidth: true, width: "26" },
|
||||
{ dataKey: 'fileName', label: Zotero.getString(columns[0]) },
|
||||
{ dataKey: 'message', label: Zotero.getString(columns[1]) },
|
||||
];
|
||||
|
||||
const domEl = document.querySelector('#tree');
|
||||
let elem = (
|
||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
||||
<VirtualizedTable
|
||||
getRowCount={() => _progressQueue.getRows().length}
|
||||
id="locateManager-table"
|
||||
ref={ref => io.tree = _tree = ref}
|
||||
renderItem={_rowToTreeItem}
|
||||
showHeader={true}
|
||||
columns={tableColumns}
|
||||
onActivate={_handleActivate}
|
||||
/>
|
||||
</IntlProvider>
|
||||
);
|
||||
ReactDOM.render(elem, domEl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus items in Zotero library when double-clicking them in the Retrieve
|
||||
* metadata window.
|
||||
* @param {Event} event
|
||||
* @param {Number[]} indices to activate
|
||||
* @private
|
||||
*/
|
||||
async function _handleActivate(event, indices) {
|
||||
if (event && event.type === 'dblclick') {
|
||||
let itemID = _progressQueue.getRows()[indices[0]].id;
|
||||
if (!itemID) return;
|
||||
|
||||
let item = await Zotero.Items.getAsync(itemID);
|
||||
if (!item) return;
|
||||
|
||||
if (item.parentItemID) itemID = item.parentItemID;
|
||||
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (win) {
|
||||
win.ZoteroPane.selectItem(itemID, false, true);
|
||||
win.focus();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,15 @@
|
|||
<?xml version="1.0" ?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
width="550" height="230"
|
||||
onload="_init()"
|
||||
id="zotero-progress">
|
||||
<script src="include.js"></script>
|
||||
<script src="progressQueueDialog.js"/>
|
||||
<vbox style="padding:10px" flex="1">
|
||||
<label id="label" control="progress-indicator" value=""/>
|
||||
<hbox align="center">
|
||||
|
@ -13,15 +18,8 @@
|
|||
<button id="minimize-button" label="&zotero.general.minimize;"/>
|
||||
<button id="close-button" label="&zotero.general.close;"/>
|
||||
</hbox>
|
||||
<tree flex="1" id="tree" hidecolumnpicker="true">
|
||||
<treecols>
|
||||
<treecol id="success-col" style="width:20px;"/>
|
||||
<splitter class="tree-splitter" hidden="true"/>
|
||||
<treecol id="col1" flex="1"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="col2" flex="2"/>
|
||||
</treecols>
|
||||
<treechildren id="treechildren"/>
|
||||
</tree>
|
||||
<hbox class="virtualized-table-container" flex="1" height="200px">
|
||||
<html:div id="tree"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</window>
|
||||
|
|
|
@ -27,7 +27,12 @@
|
|||
* @fileOverview Tools for automatically retrieving a citation for the given PDF
|
||||
*/
|
||||
|
||||
import FilePicker from 'zotero/filePicker';
|
||||
import FilePicker from 'zotero/modules/filePicker';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import VirtualizedTable from 'components/virtualized-table';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { getDOMElement } from 'components/icons';
|
||||
|
||||
/**
|
||||
* Front end for recognizing PDFs
|
||||
|
@ -37,11 +42,24 @@ var Zotero_RTFScan = new function() {
|
|||
const ACCEPT_ICON = "chrome://zotero/skin/rtfscan-accept.png";
|
||||
const LINK_ICON = "chrome://zotero/skin/rtfscan-link.png";
|
||||
const BIBLIOGRAPHY_PLACEHOLDER = "\\{Bibliography\\}";
|
||||
|
||||
|
||||
const columns = [
|
||||
{ dataKey: 'rtf', label: "zotero.rtfScan.citation.label", primary: true, flex: 4 },
|
||||
{ dataKey: 'item', label: "zotero.rtfScan.itemName.label", flex: 5 },
|
||||
{ dataKey: 'action', label: "", fixedWidth: true, width: "26px" },
|
||||
];
|
||||
var ids = 0;
|
||||
var tree;
|
||||
this._rows = [
|
||||
{ id: 'unmapped', rtf: Zotero.Intl.strings['zotero.rtfScan.unmappedCitations.label'], collapsed: false },
|
||||
{ id: 'ambiguous', rtf: Zotero.Intl.strings['zotero.rtfScan.ambiguousCitations.label'], collapsed: false },
|
||||
{ id: 'mapped', rtf: Zotero.Intl.strings['zotero.rtfScan.mappedCitations.label'], collapsed: false },
|
||||
];
|
||||
this._rowMap = {};
|
||||
this._rows.forEach((row, index) => this._rowMap[row.id] = index);
|
||||
|
||||
var inputFile = null, outputFile = null;
|
||||
var unmappedCitationsItem, ambiguousCitationsItem, mappedCitationsItem;
|
||||
var unmappedCitationsChildren, ambiguousCitationsChildren, mappedCitationsChildren;
|
||||
var citations, citationItemIDs, allCitedItemIDs, contents;
|
||||
var citations, citationItemIDs, contents;
|
||||
|
||||
/** INTRO PAGE UI **/
|
||||
|
||||
|
@ -127,28 +145,31 @@ var Zotero_RTFScan = new function() {
|
|||
/**
|
||||
* Called when second page is shown.
|
||||
*/
|
||||
this.scanPageShowing = function() {
|
||||
this.scanPageShowing = async function () {
|
||||
// can't advance
|
||||
document.documentElement.canAdvance = false;
|
||||
|
||||
// wait a ms so that UI thread gets updated
|
||||
window.setTimeout(function() { _scanRTF() }, 1);
|
||||
}
|
||||
try {
|
||||
await this._scanRTF();
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
Zotero.debug(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scans file for citations, then proceeds to next wizard page.
|
||||
*/
|
||||
var _scanRTF = Zotero.Promise.coroutine(function* () {
|
||||
this._scanRTF = async () => {
|
||||
// set up globals
|
||||
citations = [];
|
||||
citationItemIDs = {};
|
||||
|
||||
unmappedCitationsItem = document.getElementById("unmapped-citations-item");
|
||||
ambiguousCitationsItem = document.getElementById("ambiguous-citations-item");
|
||||
mappedCitationsItem = document.getElementById("mapped-citations-item");
|
||||
unmappedCitationsChildren = document.getElementById("unmapped-citations-children");
|
||||
ambiguousCitationsChildren = document.getElementById("ambiguous-citations-children");
|
||||
mappedCitationsChildren = document.getElementById("mapped-citations-children");
|
||||
let unmappedRow = this._rows[this._rowMap['unmapped']];
|
||||
let ambiguousRow = this._rows[this._rowMap['ambiguous']];
|
||||
let mappedRow = this._rows[this._rowMap['mapped']];
|
||||
|
||||
// set up regular expressions
|
||||
// this assumes that names are >=2 chars or only capital initials and that there are no
|
||||
|
@ -165,57 +186,60 @@ var Zotero_RTFScan = new function() {
|
|||
contents = Zotero.File.getContents(inputFile).replace(/([^\\\r])\r?\n/, "$1 ").replace("\\'92", "'", "g").replace("\\rquote ", "’");
|
||||
var m;
|
||||
var lastCitation = false;
|
||||
while((m = citationRe.exec(contents))) {
|
||||
while ((m = citationRe.exec(contents))) {
|
||||
// determine whether suppressed or standard regular expression was used
|
||||
if(m[2]) { // standard parenthetical
|
||||
if (m[2]) { // standard parenthetical
|
||||
var citationString = m[2];
|
||||
var creators = m[3];
|
||||
var etAl = !!m[4];
|
||||
var title = m[5];
|
||||
var date = m[6];
|
||||
var pages = m[7];
|
||||
var start = citationRe.lastIndex-m[0].length;
|
||||
var end = citationRe.lastIndex+2;
|
||||
} else { // suppressed
|
||||
var citationString = m[8];
|
||||
var creators = m[9];
|
||||
var etAl = !!m[10];
|
||||
var title = false;
|
||||
var date = m[12];
|
||||
var pages = false;
|
||||
var start = citationRe.lastIndex-m[11].length;
|
||||
var end = citationRe.lastIndex;
|
||||
var start = citationRe.lastIndex - m[0].length;
|
||||
var end = citationRe.lastIndex + 2;
|
||||
}
|
||||
else { // suppressed
|
||||
citationString = m[8];
|
||||
creators = m[9];
|
||||
etAl = !!m[10];
|
||||
title = false;
|
||||
date = m[12];
|
||||
pages = false;
|
||||
start = citationRe.lastIndex - m[11].length;
|
||||
end = citationRe.lastIndex;
|
||||
}
|
||||
citationString = citationString.replace("\\{", "{", "g").replace("\\}", "}", "g");
|
||||
var suppressAuthor = !m[2];
|
||||
|
||||
if(lastCitation && lastCitation.end >= start) {
|
||||
if (lastCitation && lastCitation.end >= start) {
|
||||
// if this citation is just an extension of the last, add items to it
|
||||
lastCitation.citationStrings.push(citationString);
|
||||
lastCitation.pages.push(pages);
|
||||
lastCitation.end = end;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// otherwise, add another citation
|
||||
var lastCitation = {"citationStrings":[citationString], "pages":[pages], "start":start,
|
||||
"end":end, "suppressAuthor":suppressAuthor};
|
||||
lastCitation = { citationStrings: [citationString], pages: [pages],
|
||||
start, end, suppressAuthor };
|
||||
citations.push(lastCitation);
|
||||
}
|
||||
|
||||
// only add each citation once
|
||||
if(citationItemIDs[citationString]) continue;
|
||||
Zotero.debug("Found citation "+citationString);
|
||||
if (citationItemIDs[citationString]) continue;
|
||||
Zotero.debug("Found citation " + citationString);
|
||||
|
||||
// for each individual match, look for an item in the database
|
||||
var s = new Zotero.Search;
|
||||
creators = creators.replace(".", "");
|
||||
creators = creators.replace(".", "");
|
||||
// TODO: localize "et al." term
|
||||
creators = creators.split(creatorSplitRe);
|
||||
|
||||
for(var i=0; i<creators.length; i++) {
|
||||
if(!creators[i]) {
|
||||
if(i == creators.length-1) {
|
||||
for (let i = 0; i < creators.length; i++) {
|
||||
if (!creators[i]) {
|
||||
if (i == creators.length - 1) {
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
creators.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
@ -224,73 +248,66 @@ var Zotero_RTFScan = new function() {
|
|||
var lastName = spaceIndex == -1 ? creators[i] : creators[i].substr(spaceIndex+1);
|
||||
s.addCondition("lastName", "contains", lastName);
|
||||
}
|
||||
if(title) s.addCondition("title", "contains", title);
|
||||
if (title) s.addCondition("title", "contains", title);
|
||||
s.addCondition("date", "is", date);
|
||||
var ids = yield s.search();
|
||||
Zotero.debug("Mapped to "+ids);
|
||||
var ids = await s.search();
|
||||
Zotero.debug("Mapped to " + ids);
|
||||
citationItemIDs[citationString] = ids;
|
||||
|
||||
if(!ids) { // no mapping found
|
||||
unmappedCitationsChildren.appendChild(_generateItem(citationString, ""));
|
||||
unmappedCitationsItem.hidden = undefined;
|
||||
} else { // some mapping found
|
||||
var items = yield Zotero.Items.getAsync(ids);
|
||||
if(items.length > 1) {
|
||||
if (!ids) { // no mapping found
|
||||
let row = _generateItem(citationString, "");
|
||||
row.parent = unmappedRow;
|
||||
this._insertRows(row, this._rowMap.ambiguous);
|
||||
}
|
||||
else { // some mapping found
|
||||
var items = await Zotero.Items.getAsync(ids);
|
||||
if (items.length > 1) {
|
||||
// check to see how well the author list matches the citation
|
||||
var matchedItems = [];
|
||||
for(var i=0; i<items.length; i++) {
|
||||
yield items[i].loadDataType('creators');
|
||||
if(_matchesItemCreators(creators, items[i])) matchedItems.push(items[i]);
|
||||
for (let item of items) {
|
||||
await item.loadAllData();
|
||||
if (_matchesItemCreators(creators, item)) matchedItems.push(item);
|
||||
}
|
||||
|
||||
if(matchedItems.length != 0) items = matchedItems;
|
||||
if (matchedItems.length != 0) items = matchedItems;
|
||||
}
|
||||
|
||||
if(items.length == 1) { // only one mapping
|
||||
mappedCitationsChildren.appendChild(_generateItem(citationString, items[0].getField("title")));
|
||||
if (items.length == 1) { // only one mapping
|
||||
await items[0].loadAllData();
|
||||
let row = _generateItem(citationString, items[0].getField("title"));
|
||||
row.parent = mappedRow;
|
||||
this._insertRows(row, this._rows.length);
|
||||
citationItemIDs[citationString] = [items[0].id];
|
||||
mappedCitationsItem.hidden = undefined;
|
||||
} else { // ambiguous mapping
|
||||
var treeitem = _generateItem(citationString, "");
|
||||
}
|
||||
else { // ambiguous mapping
|
||||
let row = _generateItem(citationString, "");
|
||||
row.parent = ambiguousRow;
|
||||
this._insertRows(row, this._rowMap.mapped);
|
||||
|
||||
// generate child items
|
||||
var treeitemChildren = document.createElement('treechildren');
|
||||
treeitem.appendChild(treeitemChildren);
|
||||
for(var i=0; i<items.length; i++) {
|
||||
treeitemChildren.appendChild(_generateItem("", items[i].getField("title"), true));
|
||||
let children = [];
|
||||
for (let item of items) {
|
||||
let childRow = _generateItem("", item.getField("title"), true);
|
||||
childRow.parent = row;
|
||||
children.push(childRow);
|
||||
}
|
||||
|
||||
treeitem.setAttribute("container", "true");
|
||||
treeitem.setAttribute("open", "true");
|
||||
ambiguousCitationsChildren.appendChild(treeitem);
|
||||
ambiguousCitationsItem.hidden = undefined;
|
||||
this._insertRows(children, this._rowMap[row.id] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
tree.invalidate();
|
||||
|
||||
// when scanning is complete, go to citations page
|
||||
document.documentElement.canAdvance = true;
|
||||
document.documentElement.advance();
|
||||
});
|
||||
};
|
||||
|
||||
function _generateItem(citationString, itemName, accept) {
|
||||
var treeitem = document.createElement('treeitem');
|
||||
var treerow = document.createElement('treerow');
|
||||
|
||||
var treecell = document.createElement('treecell');
|
||||
treecell.setAttribute("label", citationString);
|
||||
treerow.appendChild(treecell);
|
||||
|
||||
var treecell = document.createElement('treecell');
|
||||
treecell.setAttribute("label", itemName);
|
||||
treerow.appendChild(treecell);
|
||||
|
||||
var treecell = document.createElement('treecell');
|
||||
treecell.setAttribute("src", accept ? ACCEPT_ICON : LINK_ICON);
|
||||
treerow.appendChild(treecell);
|
||||
|
||||
treeitem.appendChild(treerow);
|
||||
return treeitem;
|
||||
function _generateItem(citationString, itemName, action) {
|
||||
return {
|
||||
rtf: citationString,
|
||||
item: itemName,
|
||||
action
|
||||
};
|
||||
}
|
||||
|
||||
function _matchesItemCreators(creators, item, etAl) {
|
||||
|
@ -366,28 +383,23 @@ var Zotero_RTFScan = new function() {
|
|||
* Called when the citations page is rewound. Removes all citations from the list, clears
|
||||
* globals, and returns to intro page.
|
||||
*/
|
||||
this.citationsPageRewound = function() {
|
||||
this.citationsPageRewound = function () {
|
||||
// skip back to intro page
|
||||
document.documentElement.currentPage = document.getElementById('intro-page');
|
||||
|
||||
// remove children from tree
|
||||
while(unmappedCitationsChildren.hasChildNodes()) {
|
||||
unmappedCitationsChildren.removeChild(unmappedCitationsChildren.firstChild);
|
||||
}
|
||||
while(ambiguousCitationsChildren.hasChildNodes()) {
|
||||
ambiguousCitationsChildren.removeChild(ambiguousCitationsChildren.firstChild);
|
||||
}
|
||||
while(mappedCitationsChildren.hasChildNodes()) {
|
||||
mappedCitationsChildren.removeChild(mappedCitationsChildren.firstChild);
|
||||
}
|
||||
// hide headings
|
||||
unmappedCitationsItem.hidden = ambiguousCitationsItem.hidden = mappedCitationsItem.hidden = true;
|
||||
this._rows = [
|
||||
{ id: 'unmapped', rtf: Zotero.Intl.strings['zotero.rtfScan.unmappedCitations.label'], collapsed: false },
|
||||
{ id: 'ambiguous', rtf: Zotero.Intl.strings['zotero.rtfScan.ambiguousCitations.label'], collapsed: false },
|
||||
{ id: 'mapped', rtf: Zotero.Intl.strings['zotero.rtfScan.mappedCitations.label'], collapsed: false },
|
||||
];
|
||||
this._rowMap = {};
|
||||
this._rows.forEach((row, index) => this._rowMap[row.id] = index);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a tree item is clicked to remap a citation, or accept a suggestion for an
|
||||
* Called when a tree item is clicked to remap a citation, or accept a suggestion for an
|
||||
* ambiguous citation
|
||||
*/
|
||||
this.treeClick = function(event) {
|
||||
|
@ -400,53 +412,7 @@ var Zotero_RTFScan = new function() {
|
|||
// figure out which item this corresponds to
|
||||
row = row.value;
|
||||
var level = tree.view.getLevel(row);
|
||||
if(col.value.index == 2 && level > 0) {
|
||||
var iconColumn = col.value;
|
||||
var itemNameColumn = iconColumn.getPrevious();
|
||||
var citationColumn = itemNameColumn.getPrevious();
|
||||
|
||||
if(level == 2) { // ambiguous citation item
|
||||
// get relevant information
|
||||
var parentIndex = tree.view.getParentIndex(row);
|
||||
var citation = tree.view.getCellText(parentIndex, citationColumn);
|
||||
var itemName = tree.view.getCellText(row, itemNameColumn);
|
||||
|
||||
// update item name on parent and delete children
|
||||
tree.view.setCellText(parentIndex, itemNameColumn, itemName);
|
||||
var treeitem = tree.view.getItemAtIndex(row);
|
||||
treeitem.parentNode.parentNode.removeChild(treeitem.parentNode);
|
||||
|
||||
// update array
|
||||
citationItemIDs[citation] = [citationItemIDs[citation][row-parentIndex-1]];
|
||||
} else { // mapped or unmapped citation, or ambiguous citation parent
|
||||
var citation = tree.view.getCellText(row, citationColumn);
|
||||
var io = {singleSelection:true};
|
||||
if(citationItemIDs[citation] && citationItemIDs[citation].length == 1) { // mapped citation
|
||||
// specify that item should be selected in window
|
||||
io.select = citationItemIDs[citation][0];
|
||||
}
|
||||
|
||||
window.openDialog('chrome://zotero/content/selectItemsDialog.xul', '', 'chrome,modal', io);
|
||||
|
||||
if(io.dataOut && io.dataOut.length) {
|
||||
var selectedItemID = io.dataOut[0];
|
||||
var selectedItem = Zotero.Items.get(selectedItemID);
|
||||
|
||||
var treeitem = tree.view.getItemAtIndex(row);
|
||||
|
||||
// remove any children (if ambiguous)
|
||||
var children = treeitem.getElementsByTagName("treechildren");
|
||||
if(children.length) treeitem.removeChild(children[0]);
|
||||
|
||||
// update item name
|
||||
tree.view.setCellText(row, itemNameColumn, selectedItem.getField("title"));
|
||||
|
||||
// update array
|
||||
citationItemIDs[citation] = [selectedItemID];
|
||||
}
|
||||
}
|
||||
}
|
||||
_refreshCanAdvance();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -471,7 +437,8 @@ var Zotero_RTFScan = new function() {
|
|||
/**
|
||||
* Called when style page is shown to add styles to listbox.
|
||||
*/
|
||||
this.stylePageShowing = function() {
|
||||
this.stylePageShowing = async function() {
|
||||
await Zotero.Styles.init();
|
||||
Zotero_File_Interface_Bibliography.init({
|
||||
supportedNotes: ['footnotes', 'endnotes']
|
||||
});
|
||||
|
@ -607,4 +574,210 @@ var Zotero_RTFScan = new function() {
|
|||
document.documentElement.canAdvance = true;
|
||||
document.documentElement.advance();
|
||||
}
|
||||
|
||||
this._onTwistyMouseUp = (event, index) => {
|
||||
const row = this._rows[index];
|
||||
if (!row.collapsed) {
|
||||
// Store children rows on the parent when collapsing
|
||||
row.children = [];
|
||||
const depth = this._getRowLevel(index);
|
||||
for (let childIndex = index + 1; childIndex < this._rows.length && this._getRowLevel(this._rows[childIndex]) > depth; childIndex++) {
|
||||
row.children.push(this._rows[childIndex]);
|
||||
}
|
||||
// And then remove them
|
||||
this._removeRows(row.children.map((_, childIndex) => index + 1 + childIndex));
|
||||
}
|
||||
else {
|
||||
// Insert children rows from the ones stored on the parent
|
||||
this._insertRows(row.children, index + 1);
|
||||
delete row.children;
|
||||
}
|
||||
row.collapsed = !row.collapsed;
|
||||
tree.invalidate();
|
||||
};
|
||||
|
||||
this._onActionMouseUp = (event, index) => {
|
||||
let row = this._rows[index];
|
||||
if (!row.parent) return;
|
||||
let level = this._getRowLevel(row);
|
||||
if (level == 2) { // ambiguous citation item
|
||||
let parentIndex = this._rowMap[row.parent.id];
|
||||
// Update parent item
|
||||
row.parent.item = row.item;
|
||||
|
||||
// Remove children
|
||||
let children = [];
|
||||
for (let childIndex = parentIndex + 1; childIndex < this._rows.length && this._getRowLevel(this._rows[childIndex]) >= level; childIndex++) {
|
||||
children.push(this._rows[childIndex]);
|
||||
}
|
||||
this._removeRows(children.map((_, childIndex) => parentIndex + 1 + childIndex));
|
||||
|
||||
// Move citation to mapped rows
|
||||
row.parent.parent = this._rows[this._rowMap.mapped];
|
||||
this._removeRows(parentIndex);
|
||||
this._insertRows(row.parent, this._rows.length);
|
||||
|
||||
// update array
|
||||
citationItemIDs[row.parent.rtf] = [citationItemIDs[row.parent.rtf][index-parentIndex-1]];
|
||||
}
|
||||
else { // mapped or unmapped citation, or ambiguous citation parent
|
||||
var citation = row.rtf;
|
||||
var io = { singleSelection: true };
|
||||
if (citationItemIDs[citation] && citationItemIDs[citation].length == 1) { // mapped citation
|
||||
// specify that item should be selected in window
|
||||
io.select = citationItemIDs[citation][0];
|
||||
}
|
||||
|
||||
window.openDialog('chrome://zotero/content/selectItemsDialog.xul', '', 'chrome,modal', io);
|
||||
|
||||
if (io.dataOut && io.dataOut.length) {
|
||||
var selectedItemID = io.dataOut[0];
|
||||
var selectedItem = Zotero.Items.get(selectedItemID);
|
||||
// update item name
|
||||
row.item = selectedItem.getField("title");
|
||||
|
||||
// Remove children
|
||||
let children = [];
|
||||
for (let childIndex = index + 1; childIndex < this._rows.length && this._getRowLevel(this._rows[childIndex]) > level; childIndex++) {
|
||||
children.push(this._rows[childIndex]);
|
||||
}
|
||||
this._removeRows(children.map((_, childIndex) => index + 1 + childIndex));
|
||||
|
||||
if (row.parent.id != 'mapped') {
|
||||
// Move citation to mapped rows
|
||||
row.parent = this._rows[this._rowMap.mapped];
|
||||
this._removeRows(index);
|
||||
this._insertRows(row, this._rows.length);
|
||||
}
|
||||
|
||||
// update array
|
||||
citationItemIDs[citation] = [selectedItemID];
|
||||
}
|
||||
}
|
||||
tree.invalidate();
|
||||
_refreshCanAdvance();
|
||||
};
|
||||
|
||||
this._insertRows = (rows, beforeRow) => {
|
||||
if (!Array.isArray(rows)) {
|
||||
rows = [rows];
|
||||
}
|
||||
this._rows.splice(beforeRow, 0, ...rows);
|
||||
rows.forEach(row => row.id = ids++);
|
||||
for (let row of rows) {
|
||||
row.id = ids++;
|
||||
}
|
||||
// Refresh the row map
|
||||
this._rowMap = {};
|
||||
this._rows.forEach((row, index) => this._rowMap[row.id] = index);
|
||||
};
|
||||
|
||||
this._removeRows = (indices) => {
|
||||
if (!Array.isArray(indices)) {
|
||||
indices = [indices];
|
||||
}
|
||||
// Reverse sort so we can safely splice out the entries from the rows array
|
||||
indices.sort((a, b) => b - a);
|
||||
for (const index of indices) {
|
||||
this._rows.splice(index, 1);
|
||||
}
|
||||
// Refresh the row map
|
||||
this._rowMap = {};
|
||||
this._rows.forEach((row, index) => this._rowMap[row.id] = index);
|
||||
};
|
||||
|
||||
this._getRowLevel = (row, depth=0) => {
|
||||
if (typeof row == 'number') {
|
||||
row = this._rows[row];
|
||||
}
|
||||
if (!row.parent) {
|
||||
return depth;
|
||||
}
|
||||
return this._getRowLevel(row.parent, depth+1);
|
||||
}
|
||||
|
||||
this._renderItem = (index, selection, oldDiv=null, columns) => {
|
||||
const row = this._rows[index];
|
||||
let div;
|
||||
if (oldDiv) {
|
||||
div = oldDiv;
|
||||
div.innerHTML = "";
|
||||
}
|
||||
else {
|
||||
div = document.createElementNS("http://www.w3.org/1999/xhtml", 'div');
|
||||
div.className = "row";
|
||||
}
|
||||
|
||||
for (const column of columns) {
|
||||
if (column.primary) {
|
||||
let twisty;
|
||||
if (row.children || (this._rows[index + 1] && this._rows[index + 1].parent == row)) {
|
||||
twisty = getDOMElement("IconTwisty");
|
||||
twisty.classList.add('twisty');
|
||||
if (!row.collapsed) {
|
||||
twisty.classList.add('open');
|
||||
}
|
||||
twisty.style.pointerEvents = 'auto';
|
||||
twisty.addEventListener('mousedown', event => event.stopPropagation());
|
||||
twisty.addEventListener('mouseup', event => this._onTwistyMouseUp(event, index),
|
||||
{ passive: true });
|
||||
}
|
||||
else {
|
||||
twisty = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
||||
twisty.classList.add("spacer-twisty");
|
||||
}
|
||||
|
||||
let textSpan = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
||||
textSpan.className = "cell-text";
|
||||
textSpan.innerText = row[column.dataKey] || "";
|
||||
|
||||
let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
||||
span.className = `cell primary ${column.className}`;
|
||||
span.appendChild(twisty);
|
||||
span.appendChild(textSpan);
|
||||
span.style.paddingLeft = (5 + 20 * this._getRowLevel(row)) + 'px';
|
||||
div.appendChild(span);
|
||||
}
|
||||
else if (column.dataKey == 'action') {
|
||||
let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
||||
span.className = `cell action ${column.className}`;
|
||||
if (row.parent) {
|
||||
if (row.action) {
|
||||
span.appendChild(getDOMElement('IconRTFScanAccept'));
|
||||
}
|
||||
else {
|
||||
span.appendChild(getDOMElement('IconRTFScanLink'));
|
||||
}
|
||||
span.addEventListener('mouseup', e => this._onActionMouseUp(e, index), { passive: true });
|
||||
span.style.pointerEvents = 'auto';
|
||||
}
|
||||
|
||||
div.appendChild(span);
|
||||
}
|
||||
else {
|
||||
let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
||||
span.className = `cell ${column.className}`;
|
||||
span.innerText = row[column.dataKey] || "";
|
||||
div.appendChild(span);
|
||||
}
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
||||
this._initCitationTree = function () {
|
||||
const domEl = document.querySelector('#tree');
|
||||
const elem = (
|
||||
<IntlProvider locale={Zotero.locale} messages={Zotero.Intl.strings}>
|
||||
<VirtualizedTable
|
||||
getRowCount={() => this._rows.length}
|
||||
id="rtfScan-table"
|
||||
ref={ref => tree = ref}
|
||||
renderItem={this._renderItem}
|
||||
showHeader={true}
|
||||
columns={columns}
|
||||
/>
|
||||
</IntlProvider>
|
||||
);
|
||||
return new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
|
||||
};
|
||||
}
|
|
@ -2,10 +2,13 @@
|
|||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/upgrade.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/bibliography.css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
|
||||
<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="&zotero.rtfScan.title;" width="700" height="550"
|
||||
onload="Zotero_RTFScan._initCitationTree()"
|
||||
id="zotero-rtfScan">
|
||||
|
||||
<script src="include.js"/>
|
||||
|
@ -52,34 +55,10 @@
|
|||
onpageshow="Zotero_RTFScan.citationsPageShowing()"
|
||||
onpagerewound="return Zotero_RTFScan.citationsPageRewound();">
|
||||
<description width="700">&zotero.rtfScan.citationsPage.description;</description>
|
||||
<tree flex="1" id="tree" hidecolumnpicker="true" onclick="Zotero_RTFScan.treeClick(event)">
|
||||
<treecols>
|
||||
<treecol label="&zotero.rtfScan.citation.label;" id="pdf-col" flex="1" primary="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol label="&zotero.rtfScan.itemName.label;" id="item-col" flex="2"/>
|
||||
<treecol id="action-col" style="width:40px"/>
|
||||
</treecols>
|
||||
<treechildren id="treechildren">
|
||||
<treeitem id="unmapped-citations-item" container="true" open="true" hidden="true">
|
||||
<treerow>
|
||||
<treecell label="&zotero.rtfScan.unmappedCitations.label;"/>
|
||||
</treerow>
|
||||
<treechildren id="unmapped-citations-children"/>
|
||||
</treeitem>
|
||||
<treeitem id="ambiguous-citations-item" container="true" open="true" hidden="true">
|
||||
<treerow>
|
||||
<treecell label="&zotero.rtfScan.ambiguousCitations.label;"/>
|
||||
</treerow>
|
||||
<treechildren id="ambiguous-citations-children"/>
|
||||
</treeitem>
|
||||
<treeitem id="mapped-citations-item" container="true" open="true" hidden="true">
|
||||
<treerow>
|
||||
<treecell label="&zotero.rtfScan.mappedCitations.label;"/>
|
||||
</treerow>
|
||||
<treechildren id="mapped-citations-children"/>
|
||||
</treeitem>
|
||||
</treechildren>
|
||||
</tree>
|
||||
|
||||
<hbox class="virtualized-table-container" flex="1" height="500">
|
||||
<html:div id="tree"/>
|
||||
</hbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="style-page" label="&zotero.rtfScan.stylePage.label;"
|
||||
|
|
|
@ -23,17 +23,20 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
import CollectionTree from 'zotero/collectionTree';
|
||||
import ItemTree from 'zotero/itemTree';
|
||||
|
||||
var itemsView;
|
||||
var collectionsView;
|
||||
var io;
|
||||
var connectionSelectedDeferred;
|
||||
const isEditBibliographyDialog = !!document.querySelector('#zotero-edit-bibliography-dialog');
|
||||
const isAddEditItemsDialog = !!document.querySelector('#zotero-add-citation-dialog');
|
||||
|
||||
/*
|
||||
* window takes two arguments:
|
||||
* io - used for input/output (dataOut is list of item IDs)
|
||||
* sourcesOnly - whether only sources should be shown in the window
|
||||
*/
|
||||
var doLoad = Zotero.Promise.coroutine(function* () {
|
||||
var doLoad = async function () {
|
||||
// Set font size from pref
|
||||
var sbc = document.getElementById('zotero-select-items-container');
|
||||
Zotero.setFontSize(sbc);
|
||||
|
@ -43,67 +46,75 @@ var doLoad = Zotero.Promise.coroutine(function* () {
|
|||
if(io.addBorder) document.getElementsByTagName("dialog")[0].style.border = "1px solid black";
|
||||
if(io.singleSelection) document.getElementById("zotero-items-tree").setAttribute("seltype", "single");
|
||||
|
||||
setItemsPaneMessage(Zotero.getString('pane.items.loading'));
|
||||
|
||||
collectionsView = new Zotero.CollectionTreeView();
|
||||
itemsView = await ItemTree.init(document.getElementById('zotero-items-tree'), {
|
||||
onSelectionChange: () => {
|
||||
if (isEditBibliographyDialog) {
|
||||
Zotero_Bibliography_Dialog.treeItemSelected();
|
||||
}
|
||||
else if (isAddEditItemsDialog) {
|
||||
onItemSelected();
|
||||
Zotero_Citation_Dialog.treeItemSelected();
|
||||
}
|
||||
else {
|
||||
onItemSelected();
|
||||
}
|
||||
},
|
||||
id: "select-items-dialog",
|
||||
dragAndDrop: false,
|
||||
persistColumns: false,
|
||||
columnPicker: true,
|
||||
emptyMessage: Zotero.getString('pane.items.loading')
|
||||
});
|
||||
itemsView.setItemsPaneMessage(Zotero.getString('pane.items.loading'));
|
||||
|
||||
collectionsView = await CollectionTree.init(document.getElementById('zotero-collections-tree'), {
|
||||
onSelectionChange: Zotero.Utilities.debounce(() => onCollectionSelected(), 100),
|
||||
});
|
||||
collectionsView.hideSources = ['duplicates', 'trash', 'feeds'];
|
||||
document.getElementById('zotero-collections-tree').view = collectionsView;
|
||||
|
||||
yield collectionsView.waitForLoad();
|
||||
|
||||
connectionSelectedDeferred = Zotero.Promise.defer();
|
||||
yield connectionSelectedDeferred.promise;
|
||||
|
||||
|
||||
await collectionsView.makeVisible();
|
||||
|
||||
if (io.select) {
|
||||
yield collectionsView.selectItem(io.select);
|
||||
await collectionsView.selectItem(io.select);
|
||||
}
|
||||
|
||||
Zotero.updateQuickSearchBox(document);
|
||||
});
|
||||
};
|
||||
|
||||
function doUnload()
|
||||
{
|
||||
collectionsView.unregister();
|
||||
if(itemsView)
|
||||
itemsView.unregister();
|
||||
|
||||
io.deferred && io.deferred.resolve();
|
||||
}
|
||||
|
||||
var onCollectionSelected = Zotero.Promise.coroutine(function* ()
|
||||
{
|
||||
if(itemsView)
|
||||
itemsView.unregister();
|
||||
|
||||
if(collectionsView.selection.count == 1 && collectionsView.selection.currentIndex != -1)
|
||||
{
|
||||
var collectionTreeRow = collectionsView.getRow(collectionsView.selection.currentIndex);
|
||||
collectionTreeRow.setSearch('');
|
||||
Zotero.Prefs.set('lastViewedFolder', collectionTreeRow.id);
|
||||
|
||||
setItemsPaneMessage(Zotero.getString('pane.items.loading'));
|
||||
|
||||
// Load library data if necessary
|
||||
var library = Zotero.Libraries.get(collectionTreeRow.ref.libraryID);
|
||||
if (!library.getDataLoaded('item')) {
|
||||
Zotero.debug("Waiting for items to load for library " + library.libraryID);
|
||||
yield library.waitForDataLoad('item');
|
||||
}
|
||||
|
||||
// Create items list and wait for it to load
|
||||
itemsView = new Zotero.ItemTreeView(collectionTreeRow);
|
||||
itemsView.sourcesOnly = !!window.arguments[1];
|
||||
document.getElementById('zotero-items-tree').view = itemsView;
|
||||
yield itemsView.waitForLoad();
|
||||
|
||||
clearItemsPaneMessage();
|
||||
|
||||
connectionSelectedDeferred.resolve();
|
||||
collectionsView.runListeners('select');
|
||||
var onCollectionSelected = async function () {
|
||||
if (!collectionsView.selection.count) return;
|
||||
var collectionTreeRow = collectionsView.getRow(collectionsView.selection.focused);
|
||||
collectionTreeRow.setSearch('');
|
||||
Zotero.Prefs.set('lastViewedFolder', collectionTreeRow.id);
|
||||
|
||||
itemsView.setItemsPaneMessage(Zotero.getString('pane.items.loading'));
|
||||
|
||||
// Load library data if necessary
|
||||
var library = Zotero.Libraries.get(collectionTreeRow.ref.libraryID);
|
||||
if (!library.getDataLoaded('item')) {
|
||||
Zotero.debug("Waiting for items to load for library " + library.libraryID);
|
||||
await library.waitForDataLoad('item');
|
||||
}
|
||||
});
|
||||
|
||||
await itemsView.changeCollectionTreeRow(collectionTreeRow);
|
||||
|
||||
itemsView.clearItemsPaneMessage();
|
||||
|
||||
collectionsView.runListeners('select');
|
||||
};
|
||||
|
||||
function onSearch()
|
||||
{
|
||||
if(itemsView)
|
||||
if (itemsView)
|
||||
{
|
||||
var searchVal = document.getElementById('zotero-tb-search').value;
|
||||
itemsView.setFilter('search', searchVal);
|
||||
|
@ -115,30 +126,6 @@ function onItemSelected()
|
|||
itemsView.runListeners('select');
|
||||
}
|
||||
|
||||
function setItemsPaneMessage(content) {
|
||||
var elem = document.getElementById('zotero-items-pane-message-box');
|
||||
elem.textContent = '';
|
||||
if (typeof content == 'string') {
|
||||
let contentParts = content.split("\n\n");
|
||||
for (let part of contentParts) {
|
||||
var desc = document.createElement('description');
|
||||
desc.appendChild(document.createTextNode(part));
|
||||
elem.appendChild(desc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
elem.appendChild(content);
|
||||
}
|
||||
document.getElementById('zotero-items-pane-content').selectedIndex = 1;
|
||||
}
|
||||
|
||||
|
||||
function clearItemsPaneMessage() {
|
||||
var box = document.getElementById('zotero-items-pane-message-box');
|
||||
document.getElementById('zotero-items-pane-content').selectedIndex = 0;
|
||||
}
|
||||
|
||||
function doAccept()
|
||||
{
|
||||
function doAccept() {
|
||||
io.dataOut = itemsView.getSelectedItems(true);
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/overlay.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
|
||||
<dialog
|
||||
|
@ -40,6 +41,7 @@
|
|||
onload="doLoad();"
|
||||
onunload="doUnload();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
style="padding:2em"
|
||||
persist="screenX screenY width height">
|
||||
|
||||
|
@ -54,203 +56,13 @@
|
|||
</hbox>
|
||||
|
||||
<hbox flex="1">
|
||||
<tree id="zotero-collections-tree"
|
||||
style="width: 200px;" hidecolumnpicker="true" seltype="cell"
|
||||
onselect="onCollectionSelected();">
|
||||
<treecols>
|
||||
<treecol
|
||||
id="zotero-collections-name-column"
|
||||
flex="1"
|
||||
primary="true"
|
||||
hideheader="true"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
|
||||
<deck id="zotero-items-pane-content" selectedIndex="0" flex="1">
|
||||
<tree id="zotero-items-tree"
|
||||
enableColumnDrag="true" flex="1" seltype="multiple"
|
||||
onselect="onItemSelected();">
|
||||
<treecols id="zotero-items-columns-header">
|
||||
<treecol
|
||||
id="zotero-items-column-title" primary="true"
|
||||
label="&zotero.items.title_column;"
|
||||
flex="4" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-firstCreator"
|
||||
label="&zotero.items.creator_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-itemType" hidden="true"
|
||||
label="&zotero.items.type_column;"
|
||||
width="40" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-date" hidden="true"
|
||||
label="&zotero.items.date_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-year" hidden="true"
|
||||
label="&zotero.items.year_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-publisher" hidden="true"
|
||||
label="&zotero.items.publisher_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-publicationTitle" hidden="true"
|
||||
label="&zotero.items.publication_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-journalAbbreviation" hidden="true"
|
||||
label="&zotero.items.journalAbbr_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-language" hidden="true"
|
||||
label="&zotero.items.language_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-accessDate" hidden="true"
|
||||
label="&zotero.items.accessDate_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-libraryCatalog" hidden="true"
|
||||
label="&zotero.items.libraryCatalog_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-callNumber" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.callNumber_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-rights" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.rights_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-dateAdded" hidden="true"
|
||||
label="&zotero.items.dateAdded_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-dateModified" hidden="true"
|
||||
label="&zotero.items.dateModified_column;"
|
||||
flex="1" persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-archive" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.archive_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-archiveLocation" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.archiveLocation_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-place" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.place_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-volume" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.volume_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-edition" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.edition_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-pages" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.pages_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-issue" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.issue_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-series" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.series_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-seriesTitle" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.seriesTitle_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-court" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.court_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-medium" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.medium_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-genre" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.genre_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-system" hidden="true"
|
||||
submenu="true"
|
||||
label="&zotero.items.system_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-extra" hidden="true"
|
||||
label="&zotero.items.extra_column;"
|
||||
flex="1" zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-hasAttachment" hidden="true"
|
||||
class="treecol-image"
|
||||
label="&zotero.tabs.attachments.label;"
|
||||
zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-numNotes" hidden="true"
|
||||
class="treecol-image"
|
||||
label="&zotero.tabs.notes.label;"
|
||||
zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
|
||||
<!-- Label for displaying messages when items pane is hidden
|
||||
(e.g. "Advanced search mode — press Enter to search.")-->
|
||||
<vbox id="zotero-items-pane-message-box" pack="center" align="center"/>
|
||||
</deck>
|
||||
<vbox id="zotero-collections-tree-container" class="virtualized-table-container" style="min-width: 200px">
|
||||
<html:div id="zotero-collections-tree"></html:div>
|
||||
</vbox>
|
||||
|
||||
<hbox id="zotero-items-pane-content" class="virtualized-table-container" flex="1">
|
||||
<html:div id="zotero-items-tree"></html:div>
|
||||
</hbox>
|
||||
</hbox>
|
||||
|
||||
</vbox>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<p id="result"></p>
|
||||
<script src="../include.js"></script>
|
||||
<script type="text/javascript">
|
||||
import FilePicker from 'zotero/filePicker';
|
||||
import FilePicker from 'zotero/modules/filePicker';
|
||||
|
||||
(async function () {
|
||||
// Create schema
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
import FilePicker from 'zotero/filePicker';
|
||||
import FilePicker from 'zotero/modules/filePicker';
|
||||
|
||||
var Zotero_CSL_Editor = new function() {
|
||||
this.init = init;
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
// Run in RunJS:
|
||||
// window.open('chrome://zotero/content/tools/data_generator.html', '_blank', 'chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar')
|
||||
|
||||
const React = require('react');
|
||||
const ReactDOM = require('react-dom');
|
||||
|
||||
|
|
|
@ -29,11 +29,13 @@ Zotero.CollectionTreeRow = function (collectionTreeView, type, ref, level, isOpe
|
|||
this.view = collectionTreeView;
|
||||
this.type = type;
|
||||
this.ref = ref;
|
||||
this.level = level || 0
|
||||
this.level = level || 0;
|
||||
this.isOpen = isOpen || false;
|
||||
this.onUnload = null;
|
||||
}
|
||||
|
||||
Zotero.CollectionTreeRow.IDCounter = 0;
|
||||
|
||||
|
||||
Zotero.CollectionTreeRow.prototype.__defineGetter__('id', function () {
|
||||
switch (this.type) {
|
||||
|
@ -73,7 +75,10 @@ Zotero.CollectionTreeRow.prototype.__defineGetter__('id', function () {
|
|||
break;
|
||||
}
|
||||
|
||||
return '';
|
||||
if (!this._id) {
|
||||
this._id = 'I' + Zotero.CollectionTreeRow.IDCounter++;
|
||||
}
|
||||
return this._id;
|
||||
});
|
||||
|
||||
Zotero.CollectionTreeRow.prototype.isLibrary = function (includeGlobal)
|
||||
|
@ -142,6 +147,10 @@ Zotero.CollectionTreeRow.prototype.isShare = function()
|
|||
return this.type == 'share';
|
||||
}
|
||||
|
||||
Zotero.CollectionTreeRow.prototype.isContainer = function() {
|
||||
return this.isLibrary(true) || this.isCollection() || this.isPublications() || this.isBucket();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Special
|
||||
|
@ -439,3 +448,26 @@ Zotero.CollectionTreeRow.prototype.isSearchMode = function() {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.CollectionTreeCache = {
|
||||
"lastTreeRow":null,
|
||||
"lastTempTable":null,
|
||||
"lastSearch":null,
|
||||
"lastResults":null,
|
||||
|
||||
"clear": function () {
|
||||
this.lastTreeRow = null;
|
||||
this.lastSearch = null;
|
||||
if (this.lastTempTable) {
|
||||
let tableName = this.lastTempTable;
|
||||
let id = Zotero.DB.addCallback('commit', async function () {
|
||||
await Zotero.DB.queryAsync(
|
||||
"DROP TABLE IF EXISTS " + tableName, false, { noCache: true }
|
||||
);
|
||||
Zotero.DB.removeCallback('commit', id);
|
||||
});
|
||||
}
|
||||
this.lastTempTable = null;
|
||||
this.lastResults = null;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -62,7 +62,7 @@ Zotero.Server.Connector = {
|
|||
if (!editable && !allowReadOnly) {
|
||||
let userLibrary = Zotero.Libraries.userLibrary;
|
||||
if (userLibrary && userLibrary.editable) {
|
||||
Zotero.debug("Save target isn't editable -- switching to My Library");
|
||||
Zotero.debug("Save target isn't editable -- switching lastViewedFolder to My Library");
|
||||
let treeViewID = userLibrary.treeViewID;
|
||||
Zotero.Prefs.set('lastViewedFolder', treeViewID);
|
||||
({ library, collection, editable } = this.resolveTarget(treeViewID));
|
||||
|
|
|
@ -66,7 +66,7 @@ Zotero.Feed = function(params = {}) {
|
|||
// Return a proxy so that we can disable the object once it's deleted
|
||||
return new Proxy(this, {
|
||||
get: function(obj, prop) {
|
||||
if (obj._disabled && !(prop == 'libraryID' || prop == 'id' || prop == 'treeViewID')) {
|
||||
if (obj._disabled && !(prop == 'libraryID' || prop == 'id' || prop == 'treeViewID' || prop == 'name')) {
|
||||
throw new Error("Feed " + obj.libraryID + " has been disabled");
|
||||
}
|
||||
return obj[prop];
|
||||
|
|
|
@ -35,7 +35,7 @@ Zotero.Group = function (params = {}) {
|
|||
// Return a proxy so that we can disable the object once it's deleted
|
||||
return new Proxy(this, {
|
||||
get: function(obj, prop) {
|
||||
if (obj._disabled && !(prop == 'libraryID' || prop == 'id')) {
|
||||
if (obj._disabled && !(prop == 'libraryID' || prop == 'id' || prop == 'name')) {
|
||||
throw new Error("Group (" + obj.libraryID + ") has been disabled");
|
||||
}
|
||||
return obj[prop];
|
||||
|
|
|
@ -4253,38 +4253,20 @@ Zotero.Item.prototype.getImageSrc = function() {
|
|||
}
|
||||
|
||||
|
||||
Zotero.Item.prototype.getImageSrcWithTags = Zotero.Promise.coroutine(function* () {
|
||||
//Zotero.debug("Generating tree image for item " + this.id);
|
||||
|
||||
var uri = this.getImageSrc();
|
||||
|
||||
var retracted = Zotero.Retractions.isRetracted(this);
|
||||
|
||||
Zotero.Item.prototype.getTagColors = function () {
|
||||
var tags = this.getTags();
|
||||
if (!tags.length && !retracted) {
|
||||
return uri;
|
||||
}
|
||||
if (!tags.length) return [];
|
||||
|
||||
var colorData = [];
|
||||
if (tags.length) {
|
||||
let tagColors = Zotero.Tags.getColors(this.libraryID);
|
||||
for (let tag of tags) {
|
||||
let data = tagColors.get(tag.tag);
|
||||
if (data) {
|
||||
colorData.push(data);
|
||||
}
|
||||
let colorData = [];
|
||||
let tagColors = Zotero.Tags.getColors(this.libraryID);
|
||||
for (let tag of tags) {
|
||||
let data = tagColors.get(tag.tag);
|
||||
if (data) {
|
||||
colorData.push(data);
|
||||
}
|
||||
if (!colorData.length && !retracted) {
|
||||
return uri;
|
||||
}
|
||||
colorData.sort(function (a, b) {
|
||||
return a.position - b.position;
|
||||
});
|
||||
}
|
||||
var colors = colorData.map(val => val.color);
|
||||
|
||||
return Zotero.Tags.generateItemsListImage(colors, uri, retracted);
|
||||
});
|
||||
return colorData.sort((a, b) => a.position - b.position).map(val => val.color);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ Zotero.Library = function(params = {}) {
|
|||
// Return a proxy so that we can disable the object once it's deleted
|
||||
return new Proxy(this, {
|
||||
get: function(obj, prop) {
|
||||
if (obj._disabled && !(prop == 'libraryID' || prop == 'id')) {
|
||||
if (obj._disabled && !(prop == 'libraryID' || prop == 'id' || prop == 'name')) {
|
||||
throw new Error("Library (" + obj.libraryID + ") has been disabled");
|
||||
}
|
||||
return obj[prop];
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
import FilePicker from 'zotero/filePicker';
|
||||
import FilePicker from 'zotero/modules/filePicker';
|
||||
|
||||
Zotero.DataDirectory = {
|
||||
MIGRATION_MARKER: 'migrate-dir',
|
||||
|
|
245
chrome/content/zotero/xpcom/fileDragDataProvider.js
Normal file
245
chrome/content/zotero/xpcom/fileDragDataProvider.js
Normal file
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2020 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
|
||||
// Implements nsIFlavorDataProvider for dragging attachment files to OS
|
||||
//
|
||||
// Not used on Windows in Firefox 3 or higher
|
||||
Zotero.FileDragDataProvider = function (itemIDs) {
|
||||
this._itemIDs = itemIDs;
|
||||
};
|
||||
|
||||
Zotero.FileDragDataProvider.prototype = {
|
||||
QueryInterface : function(iid) {
|
||||
if (iid.equals(Components.interfaces.nsIFlavorDataProvider) ||
|
||||
iid.equals(Components.interfaces.nsISupports)) {
|
||||
return this;
|
||||
}
|
||||
throw Components.results.NS_NOINTERFACE;
|
||||
},
|
||||
|
||||
getFlavorData : function(transferable, flavor, data, dataLen) {
|
||||
Zotero.debug("Getting flavor data for " + flavor);
|
||||
if (flavor == "application/x-moz-file-promise") {
|
||||
// On platforms other than OS X, the only directory we know of here
|
||||
// is the system temp directory, and we pass the nsIFile of the file
|
||||
// copied there in data.value below
|
||||
var useTemp = !Zotero.isMac;
|
||||
|
||||
// Get the destination directory
|
||||
var dirPrimitive = {};
|
||||
var dataSize = {};
|
||||
transferable.getTransferData("application/x-moz-file-promise-dir", dirPrimitive, dataSize);
|
||||
var destDir = dirPrimitive.value.QueryInterface(Components.interfaces.nsIFile);
|
||||
|
||||
var draggedItems = Zotero.Items.get(this._itemIDs);
|
||||
var items = [];
|
||||
|
||||
// Make sure files exist
|
||||
var notFoundNames = [];
|
||||
for (var i=0; i<draggedItems.length; i++) {
|
||||
// TODO create URL?
|
||||
if (!draggedItems[i].isAttachment() ||
|
||||
draggedItems[i].getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (draggedItems[i].getFile()) {
|
||||
items.push(draggedItems[i]);
|
||||
}
|
||||
else {
|
||||
notFoundNames.push(draggedItems[i].getField('title'));
|
||||
}
|
||||
}
|
||||
|
||||
// If using the temp directory, create a directory to store multiple
|
||||
// files, since we can (it seems) only pass one nsIFile in data.value
|
||||
if (useTemp && items.length > 1) {
|
||||
var tmpDirName = 'Zotero Dragged Files';
|
||||
destDir.append(tmpDirName);
|
||||
if (destDir.exists()) {
|
||||
destDir.remove(true);
|
||||
}
|
||||
destDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
|
||||
}
|
||||
|
||||
var copiedFiles = [];
|
||||
var existingItems = [];
|
||||
var existingFileNames = [];
|
||||
|
||||
for (var i=0; i<items.length; i++) {
|
||||
// TODO create URL?
|
||||
if (!items[i].isAttachment() ||
|
||||
items[i].attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var file = items[i].getFile();
|
||||
|
||||
// Determine if we need to copy multiple files for this item
|
||||
// (web page snapshots)
|
||||
if (items[i].attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_FILE) {
|
||||
var parentDir = file.parent;
|
||||
var files = parentDir.directoryEntries;
|
||||
var numFiles = 0;
|
||||
while (files.hasMoreElements()) {
|
||||
var f = files.getNext();
|
||||
f.QueryInterface(Components.interfaces.nsIFile);
|
||||
if (f.leafName.indexOf('.') != 0) {
|
||||
numFiles++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create folder if multiple files
|
||||
if (numFiles > 1) {
|
||||
var dirName = Zotero.Attachments.getFileBaseNameFromItem(items[i]);
|
||||
try {
|
||||
if (useTemp) {
|
||||
var copiedFile = destDir.clone();
|
||||
copiedFile.append(dirName);
|
||||
if (copiedFile.exists()) {
|
||||
// If item directory already exists in the temp dir,
|
||||
// delete it
|
||||
if (items.length == 1) {
|
||||
copiedFile.remove(true);
|
||||
}
|
||||
// If item directory exists in the container
|
||||
// directory, it's a duplicate, so give this one
|
||||
// a different name
|
||||
else {
|
||||
copiedFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o644);
|
||||
var newName = copiedFile.leafName;
|
||||
copiedFile.remove(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parentDir.copyToFollowingLinks(destDir, newName ? newName : dirName);
|
||||
|
||||
// Store nsIFile
|
||||
if (useTemp) {
|
||||
copiedFiles.push(copiedFile);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (e.name == 'NS_ERROR_FILE_ALREADY_EXISTS') {
|
||||
// Keep track of items that already existed
|
||||
existingItems.push(items[i].id);
|
||||
existingFileNames.push(dirName);
|
||||
}
|
||||
else {
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise just copy
|
||||
else {
|
||||
try {
|
||||
if (useTemp) {
|
||||
var copiedFile = destDir.clone();
|
||||
copiedFile.append(file.leafName);
|
||||
if (copiedFile.exists()) {
|
||||
// If file exists in the temp directory,
|
||||
// delete it
|
||||
if (items.length == 1) {
|
||||
copiedFile.remove(true);
|
||||
}
|
||||
// If file exists in the container directory,
|
||||
// it's a duplicate, so give this one a different
|
||||
// name
|
||||
else {
|
||||
copiedFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o644);
|
||||
var newName = copiedFile.leafName;
|
||||
copiedFile.remove(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.copyToFollowingLinks(destDir, newName ? newName : null);
|
||||
|
||||
// Store nsIFile
|
||||
if (useTemp) {
|
||||
copiedFiles.push(copiedFile);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (e.name == 'NS_ERROR_FILE_ALREADY_EXISTS') {
|
||||
existingItems.push(items[i].id);
|
||||
existingFileNames.push(items[i].getFile().leafName);
|
||||
}
|
||||
else {
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Files passed via data.value will be automatically moved
|
||||
// from the temp directory to the destination directory
|
||||
if (useTemp && copiedFiles.length) {
|
||||
if (items.length > 1) {
|
||||
data.value = destDir.QueryInterface(Components.interfaces.nsISupports);
|
||||
}
|
||||
else {
|
||||
data.value = copiedFiles[0].QueryInterface(Components.interfaces.nsISupports);
|
||||
}
|
||||
dataLen.value = 4;
|
||||
}
|
||||
|
||||
if (notFoundNames.length || existingItems.length) {
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
}
|
||||
|
||||
// Display alert if files were not found
|
||||
if (notFoundNames.length > 0) {
|
||||
// On platforms that use a temporary directory, an alert here
|
||||
// would interrupt the dragging process, so we just log a
|
||||
// warning to the console
|
||||
if (useTemp) {
|
||||
for (let name of notFoundNames) {
|
||||
var msg = "Attachment file for dragged item '" + name + "' not found";
|
||||
Zotero.log(msg, 'warning',
|
||||
'chrome://zotero/content/xpcom/itemTreeView.js');
|
||||
}
|
||||
}
|
||||
else {
|
||||
promptService.alert(null, Zotero.getString('general.warning'),
|
||||
Zotero.getString('dragAndDrop.filesNotFound') + "\n\n"
|
||||
+ notFoundNames.join("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Display alert if existing files were skipped
|
||||
if (existingItems.length > 0) {
|
||||
promptService.alert(null, Zotero.getString('general.warning'),
|
||||
Zotero.getString('dragAndDrop.existingFiles') + "\n\n"
|
||||
+ existingFileNames.join("\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ Zotero.Intl = new function () {
|
|||
Zotero.Utilities.Internal.quitZotero(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Components.utils.import("resource://gre/modules/PluralForm.jsm");
|
||||
|
||||
|
@ -76,7 +76,7 @@ Zotero.Intl = new function () {
|
|||
Zotero.rtl = (Zotero.dir === 'rtl');
|
||||
|
||||
this.strings = {};
|
||||
const intlFiles = ['zotero.dtd', 'mozilla/editMenuOverlay.dtd'];
|
||||
const intlFiles = ['zotero.dtd', 'preferences.dtd', 'mozilla/editMenuOverlay.dtd'];
|
||||
for (let intlFile of intlFiles) {
|
||||
let localeXML = Zotero.File.getContentsFromURL(`chrome://zotero/locale/${intlFile}`);
|
||||
let regexp = /<!ENTITY ([^\s]+)\s+"([^"]+)/g;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,545 +0,0 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2013 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://zotero.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 *****
|
||||
*/
|
||||
|
||||
Zotero.LibraryTreeView = function () {
|
||||
this._initialized = false;
|
||||
this._listeners = {};
|
||||
this._rows = [];
|
||||
this._rowMap = {};
|
||||
|
||||
this.id = Zotero.Utilities.randomString();
|
||||
Zotero.debug("Creating " + this.type + "s view with id " + this.id);
|
||||
|
||||
//
|
||||
// Create .on(Load|Select|Refresh).addListener() methods
|
||||
//
|
||||
var _createEventBinding = function (event, alwaysOnce) {
|
||||
return alwaysOnce
|
||||
? {
|
||||
addListener: listener => this._addListener(event, listener, true)
|
||||
}
|
||||
: {
|
||||
addListener: (listener, once) => this._addListener(event, listener, once)
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
this.onLoad = _createEventBinding('load', true);
|
||||
this.onSelect = _createEventBinding('select');
|
||||
this.onRefresh = _createEventBinding('refresh');
|
||||
};
|
||||
|
||||
Zotero.LibraryTreeView.prototype = {
|
||||
get initialized() {
|
||||
return this._initialized;
|
||||
},
|
||||
|
||||
|
||||
addEventListener: function (event, listener) {
|
||||
Zotero.logError("Zotero.LibraryTreeView::addEventListener() is deprecated");
|
||||
this.addListener(event, listener);
|
||||
},
|
||||
|
||||
|
||||
waitForLoad: function () {
|
||||
return this._waitForEvent('load');
|
||||
},
|
||||
|
||||
|
||||
waitForSelect: function () {
|
||||
return this._waitForEvent('select');
|
||||
},
|
||||
|
||||
|
||||
runListeners: Zotero.Promise.coroutine(function* (event) {
|
||||
//Zotero.debug(`Calling ${event} listeners on ${this.type} tree ${this.id}`);
|
||||
if (!this._listeners[event]) return;
|
||||
for (let [listener, once] of this._listeners[event].entries()) {
|
||||
yield Zotero.Promise.resolve(listener.call(this));
|
||||
if (once) {
|
||||
this._listeners[event].delete(listener);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
_addListener: function(event, listener, once) {
|
||||
// If already initialized run now
|
||||
if (event == 'load' && this._initialized) {
|
||||
listener.call(this);
|
||||
}
|
||||
else {
|
||||
if (!this._listeners[event]) {
|
||||
this._listeners[event] = new Map();
|
||||
}
|
||||
this._listeners[event].set(listener, once);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
_waitForEvent: Zotero.Promise.coroutine(function* (event) {
|
||||
if (event == 'load' && this._initialized) {
|
||||
return;
|
||||
}
|
||||
return new Zotero.Promise((resolve, reject) => {
|
||||
this._addListener(event, () => resolve(), true);
|
||||
});
|
||||
}),
|
||||
|
||||
|
||||
/**
|
||||
* Return a reference to the tree row at a given row
|
||||
*
|
||||
* @return {Zotero.CollectionTreeRow|Zotero.ItemTreeRow}
|
||||
*/
|
||||
getRow: function(row) {
|
||||
return this._rows[row];
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the index of the row with a given ID (e.g., "C123" for collection 123)
|
||||
*
|
||||
* @param {String} - Row id
|
||||
* @return {Integer|false}
|
||||
*/
|
||||
getRowIndexByID: function (id) {
|
||||
var type = "";
|
||||
if (this.type != 'item') {
|
||||
var type = id[0];
|
||||
id = ('' + id).substr(1);
|
||||
}
|
||||
return this._rowMap[type + id] !== undefined ? this._rowMap[type + id] : false;
|
||||
},
|
||||
|
||||
|
||||
getSelectedRowIndexes: function () {
|
||||
var rows = [];
|
||||
var start = {};
|
||||
var end = {};
|
||||
for (let i = 0, len = this.selection.getRangeCount(); i < len; i++) {
|
||||
this.selection.getRangeAt(i, start, end);
|
||||
for (let j = start.value; j <= end.value; j++) {
|
||||
rows.push(j);
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return an object describing the current scroll position to restore after changes
|
||||
*
|
||||
* @return {Object|Boolean} - Object with .id (a treeViewID) and .offset, or false if no rows
|
||||
*/
|
||||
_saveScrollPosition: function() {
|
||||
var treebox = this._treebox;
|
||||
var first = treebox.getFirstVisibleRow();
|
||||
if (!first) {
|
||||
return false;
|
||||
}
|
||||
var last = treebox.getLastVisibleRow();
|
||||
var firstSelected = null;
|
||||
for (let i = first; i <= last; i++) {
|
||||
// If an object is selected, keep the first selected one in position
|
||||
if (this.selection.isSelected(i)) {
|
||||
return {
|
||||
id: this.getRow(i).ref.treeViewID,
|
||||
offset: i - first
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise keep the first visible row in position
|
||||
return {
|
||||
id: this.getRow(first).ref.treeViewID,
|
||||
offset: 0
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Restore a scroll position returned from _saveScrollPosition()
|
||||
*/
|
||||
_rememberScrollPosition: function (scrollPosition) {
|
||||
if (!scrollPosition || !scrollPosition.id) {
|
||||
return;
|
||||
}
|
||||
var row = this.getRowIndexByID(scrollPosition.id);
|
||||
if (row === false) {
|
||||
return;
|
||||
}
|
||||
this._treebox.scrollToRow(Math.max(row - scrollPosition.offset, 0));
|
||||
},
|
||||
|
||||
|
||||
runSelectListeners: function () {
|
||||
return this._runListeners('select');
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Add a tree row to the main array, update the row count, tell the treebox that the row
|
||||
* count changed, and update the row map
|
||||
*
|
||||
* @param {Array} newRows - Array to operate on
|
||||
* @param {Zotero.ItemTreeRow} itemTreeRow
|
||||
* @param {Number} [beforeRow] - Row index to insert new row before
|
||||
*/
|
||||
_addRow: function (treeRow, beforeRow, skipRowMapRefresh) {
|
||||
this._addRowToArray(this._rows, treeRow, beforeRow);
|
||||
this.rowCount++;
|
||||
this._treebox.rowCountChanged(beforeRow, 1);
|
||||
if (!skipRowMapRefresh) {
|
||||
// Increment all rows in map at or above insertion point
|
||||
for (let i in this._rowMap) {
|
||||
if (this._rowMap[i] >= beforeRow) {
|
||||
this._rowMap[i]++
|
||||
}
|
||||
}
|
||||
// Add new row to map
|
||||
this._rowMap[treeRow.id] = beforeRow;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Add a tree row into a given array
|
||||
*
|
||||
* @param {Array} array - Array to operate on
|
||||
* @param {Zotero.CollectionTreeRow|ItemTreeRow} treeRow
|
||||
* @param {Number} beforeRow - Row index to insert new row before
|
||||
*/
|
||||
_addRowToArray: function (array, treeRow, beforeRow) {
|
||||
array.splice(beforeRow, 0, treeRow);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Remove a row from the main array, decrement the row count, tell the treebox that the row
|
||||
* count changed, update the parent isOpen if necessary, delete the row from the map, and
|
||||
* optionally update all rows above it in the map
|
||||
*/
|
||||
_removeRow: function (row, skipMapUpdate) {
|
||||
var id = this._rows[row].id;
|
||||
var level = this.getLevel(row);
|
||||
|
||||
var lastRow = row == this.rowCount - 1;
|
||||
if (lastRow && this.selection.isSelected(row)) {
|
||||
// Deselect removed row
|
||||
this.selection.toggleSelect(row);
|
||||
// If no other rows selected, select first selectable row before
|
||||
if (this.selection.count == 0 && row !== 0) {
|
||||
let previous = row;
|
||||
while (true) {
|
||||
previous--;
|
||||
// Should ever happen
|
||||
if (previous < 0) {
|
||||
break;
|
||||
}
|
||||
if (!this.isSelectable(previous)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.selection.toggleSelect(previous);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._rows.splice(row, 1);
|
||||
this.rowCount--;
|
||||
// According to the example on https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsITreeBoxObject#rowCountChanged
|
||||
// this should start at row + 1 ("rowCountChanged(rowIndex+1, -1);"), but that appears to
|
||||
// just be wrong. A negative count indicates removed rows, but the index should still
|
||||
// start at the place where the removals begin, not after it going backward.
|
||||
this._treebox.rowCountChanged(row, -1);
|
||||
// Update isOpen if parent and no siblings
|
||||
if (row != 0
|
||||
&& this.getLevel(row - 1) < level
|
||||
&& (!this._rows[row] || this.getLevel(row) != level)) {
|
||||
this._rows[row - 1].isOpen = false;
|
||||
this._treebox.invalidateRow(row - 1);
|
||||
}
|
||||
delete this._rowMap[id];
|
||||
if (!skipMapUpdate) {
|
||||
for (let i in this._rowMap) {
|
||||
if (this._rowMap[i] > row) {
|
||||
this._rowMap[i]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
_removeRows: function (rows) {
|
||||
rows = Zotero.Utilities.arrayUnique(rows);
|
||||
rows.sort((a, b) => a - b);
|
||||
for (let i = rows.length - 1; i >= 0; i--) {
|
||||
this._removeRow(rows[i]);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getLevel: function (row) {
|
||||
return this._rows[row].level;
|
||||
},
|
||||
|
||||
|
||||
isContainerOpen: function(row) {
|
||||
return this._rows[row].isOpen;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Called while a drag is over the tree
|
||||
*/
|
||||
canDrop: function(row, orient, dataTransfer) {
|
||||
// onDragOver() calls the view's canDropCheck() and sets the
|
||||
// dropEffect, which we check here. Setting the dropEffect on the
|
||||
// dataTransfer here seems to have no effect.
|
||||
|
||||
// ondragover doesn't have access to the orientation on its own,
|
||||
// so we stuff it in Zotero.DragDrop
|
||||
Zotero.DragDrop.currentOrientation = orient;
|
||||
|
||||
return dataTransfer.dropEffect && dataTransfer.dropEffect != "none";
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* Called by HTML 5 Drag and Drop when dragging over the tree
|
||||
*/
|
||||
onDragEnter: function (event) {
|
||||
Zotero.DragDrop.currentEvent = event;
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Called by HTML 5 Drag and Drop when dragging over the tree
|
||||
*
|
||||
* We use this to set the drag action, which is used by view.canDrop(),
|
||||
* based on the view's canDropCheck() and modifier keys.
|
||||
*/
|
||||
onDragOver: function (event) {
|
||||
// Prevent modifier keys from doing their normal things
|
||||
event.preventDefault();
|
||||
|
||||
Zotero.DragDrop.currentEvent = event;
|
||||
|
||||
var target = event.target;
|
||||
if (target.tagName != 'treechildren') {
|
||||
let doc = target.ownerDocument;
|
||||
// Consider a drop on the items pane message box (e.g., when showing the welcome text)
|
||||
// a drop on the items tree
|
||||
let msgBox = doc.getElementById('zotero-items-pane-message-box');
|
||||
if (msgBox.contains(target) && msgBox.firstChild.hasAttribute('allowdrop')) {
|
||||
target = doc.querySelector('#zotero-items-tree treechildren');
|
||||
}
|
||||
else {
|
||||
this._setDropEffect(event, "none");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var tree = target.parentNode;
|
||||
let row = {}, col = {}, obj = {};
|
||||
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
||||
if (tree.id == 'zotero-collections-tree') {
|
||||
var view = tree.ownerDocument.defaultView.ZoteroPane.collectionsView;
|
||||
}
|
||||
else if (tree.id == 'zotero-items-tree') {
|
||||
var view = tree.ownerDocument.defaultView.ZoteroPane.itemsView;
|
||||
}
|
||||
else {
|
||||
throw new Error("Invalid tree id '" + tree.id + "'");
|
||||
}
|
||||
|
||||
if (!view.canDropCheck(row.value, Zotero.DragDrop.currentOrientation, event.dataTransfer)) {
|
||||
this._setDropEffect(event, "none");
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.dataTransfer.getData("zotero/item")) {
|
||||
var sourceCollectionTreeRow = Zotero.DragDrop.getDragSource(event.dataTransfer);
|
||||
if (sourceCollectionTreeRow) {
|
||||
if (this.type == 'collection') {
|
||||
var targetCollectionTreeRow = Zotero.DragDrop.getDragTarget(event);
|
||||
}
|
||||
else if (this.type == 'item') {
|
||||
var targetCollectionTreeRow = this.collectionTreeRow;
|
||||
}
|
||||
else {
|
||||
throw new Error("Invalid type '" + this.type + "'");
|
||||
}
|
||||
|
||||
if (!targetCollectionTreeRow) {
|
||||
this._setDropEffect(event, "none");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceCollectionTreeRow.id == targetCollectionTreeRow.id) {
|
||||
// Ignore drag into the same collection
|
||||
if (this.type == 'collection') {
|
||||
this._setDropEffect(event, "none");
|
||||
}
|
||||
// If dragging from the same source, do a move
|
||||
else {
|
||||
this._setDropEffect(event, "move");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// If the source isn't a collection, the action has to be a copy
|
||||
if (!sourceCollectionTreeRow.isCollection()) {
|
||||
this._setDropEffect(event, "copy");
|
||||
return false;
|
||||
}
|
||||
// For now, all cross-library drags are copies
|
||||
if (sourceCollectionTreeRow.ref.libraryID != targetCollectionTreeRow.ref.libraryID) {
|
||||
this._setDropEffect(event, "copy");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Zotero.isMac && event.metaKey) || (!Zotero.isMac && event.shiftKey)) {
|
||||
this._setDropEffect(event, "move");
|
||||
}
|
||||
else {
|
||||
this._setDropEffect(event, "copy");
|
||||
}
|
||||
}
|
||||
else if (event.dataTransfer.getData("zotero/collection")) {
|
||||
let collectionID = Zotero.DragDrop.getDataFromDataTransfer(event.dataTransfer).data[0];
|
||||
let { libraryID: sourceLibraryID } = Zotero.Collections.getLibraryAndKeyFromID(collectionID);
|
||||
|
||||
if (this.type == 'collection') {
|
||||
var targetCollectionTreeRow = Zotero.DragDrop.getDragTarget(event);
|
||||
}
|
||||
else {
|
||||
throw new Error("Invalid type '" + this.type + "'");
|
||||
}
|
||||
|
||||
// For now, all cross-library drags are copies
|
||||
if (sourceLibraryID != targetCollectionTreeRow.ref.libraryID) {
|
||||
/*if ((Zotero.isMac && event.metaKey) || (!Zotero.isMac && event.shiftKey)) {
|
||||
this._setDropEffect(event, "move");
|
||||
}
|
||||
else {
|
||||
this._setDropEffect(event, "copy");
|
||||
}*/
|
||||
this._setDropEffect(event, "copy");
|
||||
return false;
|
||||
}
|
||||
|
||||
// And everything else is a move
|
||||
this._setDropEffect(event, "move");
|
||||
}
|
||||
else if (event.dataTransfer.types.contains("application/x-moz-file")) {
|
||||
// As of Aug. 2013 nightlies:
|
||||
//
|
||||
// - Setting the dropEffect only works on Linux and OS X.
|
||||
//
|
||||
// - Modifier keys don't show up in the drag event on OS X until the
|
||||
// drop (https://bugzilla.mozilla.org/show_bug.cgi?id=911918),
|
||||
// so since we can't show a correct effect, we leave it at
|
||||
// the default 'move', the least misleading option, and set it
|
||||
// below in onDrop().
|
||||
//
|
||||
// - The cursor effect gets set by the system on Windows 7 and can't
|
||||
// be overridden.
|
||||
if (!Zotero.isMac) {
|
||||
if (event.shiftKey) {
|
||||
if (event.ctrlKey) {
|
||||
event.dataTransfer.dropEffect = "link";
|
||||
}
|
||||
else {
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
}
|
||||
}
|
||||
else {
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* Called by HTML 5 Drag and Drop when dropping onto the tree
|
||||
*/
|
||||
onDrop: function (event) {
|
||||
// See note above
|
||||
if (event.dataTransfer.types.contains("application/x-moz-file")) {
|
||||
if (Zotero.isMac) {
|
||||
Zotero.DragDrop.currentEvent = event;
|
||||
if (event.metaKey) {
|
||||
if (event.altKey) {
|
||||
event.dataTransfer.dropEffect = 'link';
|
||||
}
|
||||
else {
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
}
|
||||
}
|
||||
else {
|
||||
event.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
onDragExit: function (event) {
|
||||
//Zotero.debug("Clearing drag data");
|
||||
Zotero.DragDrop.currentEvent = null;
|
||||
},
|
||||
|
||||
|
||||
_setDropEffect: function (event, effect) {
|
||||
// On Windows (in Fx26), Firefox uses 'move' for unmodified drags
|
||||
// and 'copy'/'link' for drags with system-default modifier keys
|
||||
// as long as the actions are allowed by the initial effectAllowed set
|
||||
// in onDragStart, regardless of the effectAllowed or dropEffect set
|
||||
// in onDragOver. It doesn't seem to be possible to use 'copy' for
|
||||
// the default and 'move' for modified, as we need to in the collections
|
||||
// tree. To prevent inaccurate cursor feedback, we set effectAllowed to
|
||||
// 'copy' in onDragStart, which locks the cursor at 'copy'. ('none' still
|
||||
// changes the cursor, but 'move'/'link' do not.) It'd be better to use
|
||||
// the unadorned 'move', but we use 'copy' instead because with 'move' text
|
||||
// can't be dragged to some external programs (e.g., Chrome, Notepad++),
|
||||
// which seems worse than always showing 'copy' feedback.
|
||||
//
|
||||
// However, since effectAllowed is enforced, leaving it at 'copy'
|
||||
// would prevent our modified 'move' in the collections tree from working,
|
||||
// so we also have to set effectAllowed here (called from onDragOver) to
|
||||
// the same action as the dropEffect. This allows the dropEffect setting
|
||||
// (which we use in the tree's canDrop() and drop() to determine the desired
|
||||
// action) to be changed, even if the cursor doesn't reflect the new setting.
|
||||
if (Zotero.isWin || Zotero.isLinux) {
|
||||
event.dataTransfer.effectAllowed = effect;
|
||||
}
|
||||
event.dataTransfer.dropEffect = effect;
|
||||
}
|
||||
};
|
|
@ -227,6 +227,8 @@ Zotero.Prefs = new function(){
|
|||
Zotero.setFontSize(
|
||||
Zotero.getActiveZoteroPane().document.getElementById('zotero-pane')
|
||||
);
|
||||
Zotero.getActiveZoteroPane().collectionsView && Zotero.getActiveZoteroPane().collectionsView.updateFontSize();
|
||||
Zotero.getActiveZoteroPane().itemsView && Zotero.getActiveZoteroPane().itemsView.updateFontSize();
|
||||
}],
|
||||
[ "layout", function(val) {
|
||||
Zotero.getActiveZoteroPane().updateLayout();
|
||||
|
@ -466,4 +468,59 @@ Zotero.Prefs = new function(){
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.getVirtualCollectionState = function (type) {
|
||||
const prefKeys = {
|
||||
duplicates: 'duplicateLibraries',
|
||||
unfiled: 'unfiledLibraries',
|
||||
retracted: 'retractedLibraries'
|
||||
};
|
||||
let prefKey = prefKeys[type];
|
||||
if (!prefKey) {
|
||||
throw new Error("Invalid virtual collection type '" + type + "'");
|
||||
}
|
||||
|
||||
var libraries;
|
||||
try {
|
||||
libraries = JSON.parse(Zotero.Prefs.get(prefKey) || '{}');
|
||||
if (typeof libraries != 'object') {
|
||||
throw true;
|
||||
}
|
||||
}
|
||||
// Ignore old/incorrect formats
|
||||
catch (e) {
|
||||
Zotero.Prefs.clear(prefKey);
|
||||
libraries = {};
|
||||
}
|
||||
|
||||
return libraries;
|
||||
};
|
||||
|
||||
|
||||
this.getVirtualCollectionStateForLibrary = function (libraryID, type) {
|
||||
return this.getVirtualCollectionState(type)[libraryID] !== false;
|
||||
};
|
||||
|
||||
|
||||
this.setVirtualCollectionStateForLibrary = function (libraryID, type, show) {
|
||||
const prefKeys = {
|
||||
duplicates: 'duplicateLibraries',
|
||||
unfiled: 'unfiledLibraries',
|
||||
retracted: 'retractedLibraries'
|
||||
};
|
||||
let prefKey = prefKeys[type];
|
||||
if (!prefKey) {
|
||||
throw new Error("Invalid virtual collection type '" + type + "'");
|
||||
}
|
||||
|
||||
var libraries = this.getVirtualCollectionState(type);
|
||||
|
||||
// Update current library
|
||||
libraries[libraryID] = !!show;
|
||||
// Remove libraries that don't exist or that are set to true
|
||||
for (let id of Object.keys(libraries).filter(id => libraries[id] || !Zotero.Libraries.exists(id))) {
|
||||
delete libraries[id];
|
||||
}
|
||||
Zotero.Prefs.set(prefKey, JSON.stringify(libraries));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@ Zotero.ProgressQueue = function (options) {
|
|||
* @param {String} message
|
||||
*/
|
||||
this.updateRow = function(itemID, status, message) {
|
||||
Zotero.debug(`ProgressQueue: updating row ${itemID}, ${status}, ${message}`);
|
||||
for (let row of _rows) {
|
||||
if (row.id === itemID) {
|
||||
row.status = status;
|
||||
|
|
|
@ -32,7 +32,7 @@ Zotero.ProgressQueueDialog = function (progressQueue) {
|
|||
|
||||
let _progressWindow = null;
|
||||
let _progressIndicator = null;
|
||||
let _rowIDs = [];
|
||||
let _io = { progressQueue: _progressQueue };
|
||||
let _status = null;
|
||||
let _showMinimize = true;
|
||||
|
||||
|
@ -45,11 +45,11 @@ Zotero.ProgressQueueDialog = function (progressQueue) {
|
|||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (win) {
|
||||
_progressWindow = win.openDialog("chrome://zotero/content/progressQueueDialog.xul",
|
||||
"", "chrome,close=yes,resizable=yes,dependent,dialog,centerscreen");
|
||||
"", "chrome,close=yes,resizable=yes,dependent,dialog,centerscreen", _io);
|
||||
}
|
||||
else {
|
||||
_progressWindow = Services.ww.openWindow(null, "chrome://zotero/content/progressQueueDialog.xul",
|
||||
"", "chrome,close=yes,resizable=yes,dependent,dialog,centerscreen", null);
|
||||
"", "chrome,close=yes,resizable=yes,dependent,dialog,centerscreen", _io);
|
||||
}
|
||||
|
||||
_progressWindow.addEventListener('pageshow', _onWindowLoaded.bind(this), false);
|
||||
|
@ -82,70 +82,9 @@ Zotero.ProgressQueueDialog = function (progressQueue) {
|
|||
_progressQueue.cancel();
|
||||
};
|
||||
|
||||
function _getImageByStatus(status) {
|
||||
if (status === Zotero.ProgressQueue.ROW_PROCESSING) {
|
||||
return LOADING_IMAGE;
|
||||
}
|
||||
else if (status === Zotero.ProgressQueue.ROW_FAILED) {
|
||||
return FAILURE_IMAGE;
|
||||
}
|
||||
else if (status === Zotero.ProgressQueue.ROW_SUCCEEDED) {
|
||||
return SUCCESS_IMAGE;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function _rowToTreeItem(row) {
|
||||
let treeitem = _progressWindow.document.createElement('treeitem');
|
||||
treeitem.setAttribute('id', 'item-' + row.id);
|
||||
|
||||
let treerow = _progressWindow.document.createElement('treerow');
|
||||
|
||||
let treecell = _progressWindow.document.createElement('treecell');
|
||||
treecell.setAttribute('id', 'item-' + row.id + '-icon');
|
||||
treecell.setAttribute('src', _getImageByStatus(row.status));
|
||||
|
||||
treerow.appendChild(treecell);
|
||||
|
||||
treecell = _progressWindow.document.createElement('treecell');
|
||||
treecell.setAttribute('label', row.fileName);
|
||||
treerow.appendChild(treecell);
|
||||
|
||||
treecell = _progressWindow.document.createElement('treecell');
|
||||
treecell.setAttribute('id', 'item-' + row.id + '-title');
|
||||
treecell.setAttribute('label', row.message);
|
||||
treerow.appendChild(treecell);
|
||||
|
||||
treeitem.appendChild(treerow);
|
||||
return treeitem;
|
||||
}
|
||||
|
||||
function _onWindowLoaded() {
|
||||
let rows = _progressQueue.getRows();
|
||||
_rowIDs = [];
|
||||
|
||||
_progressWindow.document.title = Zotero.getString(_progressQueue.getTitle());
|
||||
|
||||
let col1 = _progressWindow.document.getElementById('col1');
|
||||
let col2 = _progressWindow.document.getElementById('col2');
|
||||
|
||||
let columns = _progressQueue.getColumns();
|
||||
col1.setAttribute('label', Zotero.getString(columns[0]));
|
||||
col2.setAttribute('label', Zotero.getString(columns[1]));
|
||||
|
||||
let treechildren = _progressWindow.document.getElementById('treechildren');
|
||||
|
||||
for (let row of rows) {
|
||||
_rowIDs.push(row.id);
|
||||
let treeitem = _rowToTreeItem(row);
|
||||
treechildren.appendChild(treeitem);
|
||||
}
|
||||
|
||||
_progressWindow.document.getElementById('tree').addEventListener('dblclick',
|
||||
function (event) {
|
||||
_onDblClick(event, this);
|
||||
}
|
||||
);
|
||||
var rootElement = _progressWindow.document.getElementById('zotero-progress');
|
||||
Zotero.setFontSize(rootElement);
|
||||
|
||||
_progressIndicator = _progressWindow.document.getElementById('progress-indicator');
|
||||
_progressWindow.document.getElementById('cancel-button')
|
||||
|
@ -181,33 +120,24 @@ Zotero.ProgressQueueDialog = function (progressQueue) {
|
|||
_progressIndicator = null;
|
||||
_status = null;
|
||||
_showMinimize = true;
|
||||
_rowIDs = [];
|
||||
});
|
||||
|
||||
_updateProgress();
|
||||
|
||||
_progressQueue.addListener('rowadded', function (row) {
|
||||
_rowIDs.push(row.id);
|
||||
let treeitem = _rowToTreeItem(row);
|
||||
treechildren.appendChild(treeitem);
|
||||
_io.tree.invalidate();
|
||||
_updateProgress();
|
||||
});
|
||||
|
||||
_progressQueue.addListener('rowupdated', function (row) {
|
||||
let itemIcon = _progressWindow.document.getElementById('item-' + row.id + '-icon');
|
||||
let itemTitle = _progressWindow.document.getElementById('item-' + row.id + '-title');
|
||||
|
||||
itemIcon.setAttribute('src', _getImageByStatus(row.status));
|
||||
itemTitle.setAttribute('label', row.message);
|
||||
_io.tree.invalidate();
|
||||
_updateProgress();
|
||||
});
|
||||
|
||||
_progressQueue.addListener('rowdeleted', function (row) {
|
||||
_rowIDs.splice(_rowIDs.indexOf(row.id), 1);
|
||||
let treeitem = _progressWindow.document.getElementById('item-' + row.id);
|
||||
treeitem.parentNode.removeChild(treeitem);
|
||||
_io.tree.invalidate();
|
||||
_updateProgress();
|
||||
});
|
||||
|
||||
_updateProgress();
|
||||
}
|
||||
|
||||
function _updateProgress() {
|
||||
|
@ -228,29 +158,4 @@ Zotero.ProgressQueueDialog = function (progressQueue) {
|
|||
_progressWindow.document.getElementById("label").value = _status || Zotero.getString('general.processing');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus items in Zotero library when double-clicking them in the Retrieve
|
||||
* metadata window.
|
||||
* @param {Event} event
|
||||
* @param {tree} tree XUL tree object
|
||||
* @private
|
||||
*/
|
||||
async function _onDblClick(event, tree) {
|
||||
if (event && tree && event.type === 'dblclick') {
|
||||
let itemID = _rowIDs[tree.treeBoxObject.getRowAt(event.clientX, event.clientY)];
|
||||
if (!itemID) return;
|
||||
|
||||
let item = await Zotero.Items.getAsync(itemID);
|
||||
if (!item) return;
|
||||
|
||||
if (item.parentItemID) itemID = item.parentItemID;
|
||||
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (win) {
|
||||
win.ZoteroPane.selectItem(itemID, false, true);
|
||||
win.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -303,7 +303,7 @@ Zotero.Retractions = {
|
|||
// Changed
|
||||
&& (previous != current
|
||||
// Explicitly hidden
|
||||
|| (current && !Zotero.Utilities.Internal.getVirtualCollectionStateForLibrary(libraryID, 'retracted')))) {
|
||||
|| (current && !Zotero.Prefs.getVirtualCollectionStateForLibrary(libraryID, 'retracted')))) {
|
||||
let promises = [];
|
||||
for (let zp of Zotero.getZoteroPanes()) {
|
||||
promises.push(zp.setVirtual(libraryID, 'retracted', current));
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit db52081e6eedfb0aa250dfc7f93ab2cf7ed6e468
|
||||
Subproject commit 10ffb4a766ce7d43aac3b321863a4e585669be02
|
|
@ -31,7 +31,7 @@
|
|||
*/
|
||||
Zotero.Utilities.Internal = {
|
||||
SNAPSHOT_SAVE_TIMEOUT: 30000,
|
||||
|
||||
|
||||
/**
|
||||
* Run a function on chunks of a given size of an array's elements.
|
||||
*
|
||||
|
@ -1797,77 +1797,6 @@ Zotero.Utilities.Internal = {
|
|||
return menu;
|
||||
},
|
||||
|
||||
|
||||
// TODO: Move somewhere better
|
||||
getVirtualCollectionState: function (type) {
|
||||
switch (type) {
|
||||
case 'duplicates':
|
||||
var prefKey = 'duplicateLibraries';
|
||||
break;
|
||||
|
||||
case 'unfiled':
|
||||
var prefKey = 'unfiledLibraries';
|
||||
break;
|
||||
|
||||
case 'retracted':
|
||||
var prefKey = 'retractedLibraries';
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Invalid virtual collection type '" + type + "'");
|
||||
}
|
||||
var libraries;
|
||||
try {
|
||||
libraries = JSON.parse(Zotero.Prefs.get(prefKey) || '{}');
|
||||
if (typeof libraries != 'object') {
|
||||
throw true;
|
||||
}
|
||||
}
|
||||
// Ignore old/incorrect formats
|
||||
catch (e) {
|
||||
Zotero.Prefs.clear(prefKey);
|
||||
libraries = {};
|
||||
}
|
||||
|
||||
return libraries;
|
||||
},
|
||||
|
||||
|
||||
getVirtualCollectionStateForLibrary: function (libraryID, type) {
|
||||
return this.getVirtualCollectionState(type)[libraryID] !== false;
|
||||
},
|
||||
|
||||
|
||||
setVirtualCollectionStateForLibrary: function (libraryID, type, show) {
|
||||
switch (type) {
|
||||
case 'duplicates':
|
||||
var prefKey = 'duplicateLibraries';
|
||||
break;
|
||||
|
||||
case 'unfiled':
|
||||
var prefKey = 'unfiledLibraries';
|
||||
break;
|
||||
|
||||
case 'retracted':
|
||||
var prefKey = 'retractedLibraries';
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Invalid virtual collection type '" + type + "'");
|
||||
}
|
||||
|
||||
var libraries = this.getVirtualCollectionState(type);
|
||||
|
||||
// Update current library
|
||||
libraries[libraryID] = !!show;
|
||||
// Remove libraries that don't exist or that are set to true
|
||||
for (let id of Object.keys(libraries).filter(id => libraries[id] || !Zotero.Libraries.exists(id))) {
|
||||
delete libraries[id];
|
||||
}
|
||||
Zotero.Prefs.set(prefKey, JSON.stringify(libraries));
|
||||
},
|
||||
|
||||
|
||||
openPreferences: function (paneID, options = {}) {
|
||||
if (typeof options == 'string') {
|
||||
Zotero.debug("ZoteroPane.openPreferences() now takes an 'options' object -- update your code", 2);
|
||||
|
@ -2055,14 +1984,20 @@ Zotero.Utilities.Internal = {
|
|||
if (size <= 1) {
|
||||
size = 'small';
|
||||
}
|
||||
else if (size <= 1.25) {
|
||||
else if (size <= 1.15) {
|
||||
size = 'medium';
|
||||
}
|
||||
else {
|
||||
else if (size <= 1.3) {
|
||||
size = 'large';
|
||||
}
|
||||
else {
|
||||
size = 'x-large';
|
||||
}
|
||||
// Custom attribute -- allows for additional customizations in zotero.css
|
||||
rootElement.setAttribute('zoteroFontSize', size);
|
||||
if (Zotero.rtl) {
|
||||
rootElement.setAttribute('dir', 'rtl');
|
||||
}
|
||||
},
|
||||
|
||||
getAncestorByTagName: function (elem, tagName){
|
||||
|
@ -2159,7 +2094,88 @@ Zotero.Utilities.Internal = {
|
|||
return Zotero.ItemTypes.getImageSrc(attachment.mimeType === "application/pdf"
|
||||
? "attachment-pdf" : "attachment-snapshot");
|
||||
},
|
||||
|
||||
/**
|
||||
* Pass a class into this to add generic methods for creating event listeners
|
||||
* (and running those events).
|
||||
*
|
||||
* ```
|
||||
* var MyClass = Zotero.Utilities.Internal.makeClassEventDispatcher(class {
|
||||
* constructor: () => {
|
||||
* this.onFoo = this.createEventBinding('foo');
|
||||
* }
|
||||
* foo: () => this.runListeners('foo');
|
||||
* });
|
||||
* let object = new MyClass();
|
||||
* object.onFoo.addListener(() => console.log('foo ran in object of MyClass'));
|
||||
* object.foo();
|
||||
* ```
|
||||
* @param cls
|
||||
*/
|
||||
makeClassEventDispatcher: function (cls) {
|
||||
cls.prototype._events = null;
|
||||
cls.prototype.runListeners = async function (event) {
|
||||
// Zotero.debug(`Running ${event} listeners on ${cls.toString()}`);
|
||||
if (!this._events) this._events = {};
|
||||
if (!this._events[event]) {
|
||||
this._events[event] = {
|
||||
listeners: new Map(),
|
||||
};
|
||||
}
|
||||
this._events[event].triggered = true;
|
||||
// Array.from(entries) since entries() returns an iterator and we want a snapshot of the entries
|
||||
// at the time of runListeners() call to prevent triggering listeners that are added right
|
||||
// runListeners() invocation
|
||||
for (let [listener, once] of Array.from(this._events[event].listeners.entries())) {
|
||||
await Zotero.Promise.resolve(listener.call(this));
|
||||
if (once) {
|
||||
this._events[event].listeners.delete(listener);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param event {String} name of the event
|
||||
* @param alwaysOnce {Boolean} whether all event listeners on this event will only be triggered once
|
||||
* @param immediateAfterTrigger {Boolean} whether the event listeners should be triggered immediately
|
||||
* upon being added if the event had been triggered at least once
|
||||
* @returns {Object} A listener object with an addListener(listener, once) method
|
||||
* @private
|
||||
*/
|
||||
cls.prototype.createEventBinding = function (event, alwaysOnce, immediateAfterTrigger) {
|
||||
if (!this._events) this._events = {};
|
||||
this._events[event] = {
|
||||
listeners: new Map(),
|
||||
immediateAfterTrigger
|
||||
};
|
||||
return {
|
||||
addListener: (listener, once) => {
|
||||
this._addListener(event, listener, alwaysOnce || once, immediateAfterTrigger);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cls.prototype._addListener = function (event, listener, once, immediateAfterTrigger) {
|
||||
if (!this._events) this._events = {};
|
||||
let ev = this._events[event];
|
||||
if (!ev) {
|
||||
this._events[event] = {
|
||||
listeners: new Map(),
|
||||
immediateAfterTrigger
|
||||
};
|
||||
}
|
||||
if ((immediateAfterTrigger || ev.immediateAfterTrigger) && ev.triggered) {
|
||||
return listener.call(this);
|
||||
}
|
||||
this._events[event].listeners.set(listener, once);
|
||||
};
|
||||
|
||||
cls.prototype._waitForEvent = async function (event) {
|
||||
return new Zotero.Promise((resolve, reject) => {
|
||||
this._addListener(event, () => resolve(), true);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1991,7 +1991,6 @@ Zotero.VersionHeader = {
|
|||
Zotero.DragDrop = {
|
||||
currentEvent: null,
|
||||
currentOrientation: 0,
|
||||
currentSourceNode: null,
|
||||
|
||||
getDataFromDataTransfer: function (dataTransfer, firstOnly) {
|
||||
var dt = dataTransfer;
|
||||
|
@ -2050,28 +2049,8 @@ Zotero.DragDrop = {
|
|||
},
|
||||
|
||||
|
||||
getDragSource: function (dataTransfer) {
|
||||
if (!dataTransfer) {
|
||||
//Zotero.debug("Drag data not available", 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
// For items, the drag source is the CollectionTreeRow of the parent window
|
||||
// of the source tree
|
||||
if (dataTransfer.types.contains("zotero/item")) {
|
||||
let sourceNode = dataTransfer.mozSourceNode || this.currentSourceNode;
|
||||
if (!sourceNode || sourceNode.tagName != 'treechildren'
|
||||
|| sourceNode.parentElement.id != 'zotero-items-tree') {
|
||||
return false;
|
||||
}
|
||||
var win = sourceNode.ownerDocument.defaultView;
|
||||
if (win.document.documentElement.getAttribute('windowtype') == 'zotero:search') {
|
||||
return win.ZoteroAdvancedSearch.itemsView.collectionTreeRow;
|
||||
}
|
||||
return win.ZoteroPane.collectionsView.selectedTreeRow;
|
||||
}
|
||||
|
||||
return false;
|
||||
getDragSource: function () {
|
||||
return this.currentDragSource;
|
||||
},
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -330,32 +330,17 @@
|
|||
<box id="zotero-collections-tree-shim"/>
|
||||
<!-- This extra vbox prevents the toolbar from getting compressed when resizing
|
||||
the tag selector to max height -->
|
||||
<tree id="zotero-collections-tree"
|
||||
hidecolumnpicker="true"
|
||||
oncontextmenu="ZoteroPane.onCollectionsContextMenuOpen(event)"
|
||||
onmouseover="ZoteroPane_Local.collectionsView.setHighlightedRows();"
|
||||
onselect="ZoteroPane_Local.onCollectionSelected();"
|
||||
seltype="cell" flex="1" editable="true">
|
||||
<treecols>
|
||||
<treecol
|
||||
id="zotero-collections-name-column"
|
||||
flex="1"
|
||||
primary="true"
|
||||
hideheader="true"/>
|
||||
</treecols>
|
||||
<treechildren ondragstart="ZoteroPane_Local.collectionsView.onDragStart(event)"
|
||||
ondragenter="return ZoteroPane_Local.collectionsView.onDragEnter(event)"
|
||||
ondragover="return ZoteroPane_Local.collectionsView.onDragOver(event)"
|
||||
ondrop="return ZoteroPane_Local.collectionsView.onDrop(event)"/>
|
||||
</tree>
|
||||
<vbox id="zotero-collections-tree-container" class="virtualized-table-container" flex="1">
|
||||
<html:div id="zotero-collections-tree"></html:div>
|
||||
</vbox>
|
||||
<splitter
|
||||
id="zotero-tags-splitter"
|
||||
orient="vertical"
|
||||
collapse="after"
|
||||
zotero-persist="state"
|
||||
onmousemove="if (this.getAttribute('state') == 'dragging') { ZoteroPane.handleTagSelectorResize(); }"
|
||||
id="zotero-tags-splitter"
|
||||
orient="vertical"
|
||||
collapse="after"
|
||||
zotero-persist="state"
|
||||
onmousemove="if (this.getAttribute('state') == 'dragging') { ZoteroPane.handleTagSelectorResize(); }"
|
||||
>
|
||||
<grippy oncommand="ZoteroPane_Local.toggleTagSelector()"/>
|
||||
<grippy oncommand="ZoteroPane.toggleTagSelector()"/>
|
||||
</splitter>
|
||||
<!-- 'collapsed' is no longer necessary here due to the persisted 'state' on
|
||||
zotero-tags-splitter, but without this an old-style entry for 'collapsed'
|
||||
|
@ -376,214 +361,9 @@
|
|||
</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 -->
|
||||
<tree
|
||||
id="zotero-items-tree"
|
||||
enableColumnDrag="true"
|
||||
disableKeyNavigation="true"
|
||||
onfocus="if (ZoteroPane_Local.itemsView.rowCount && !ZoteroPane_Local.itemsView.selection.count) { ZoteroPane_Local.itemsView.selection.select(0); }"
|
||||
onkeydown="ZoteroPane_Local.handleKeyDown(event, this.id)"
|
||||
onselect="ZoteroPane_Local.itemSelected(event)"
|
||||
oncommand="ZoteroPane_Local.serializePersist()"
|
||||
oncontextmenu="ZoteroPane.onItemsContextMenuOpen(event)"
|
||||
flex="1"
|
||||
hidden="true"
|
||||
zotero-persist="current-view-group">
|
||||
<treecols id="zotero-items-columns-header">
|
||||
<treecol
|
||||
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"/>
|
||||
<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"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-itemType"
|
||||
label="&zotero.items.type_column;"
|
||||
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"/>
|
||||
<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"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-publisher"
|
||||
label="&zotero.items.publisher_column;"
|
||||
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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-hasAttachment" default-in="default" disabled-in="feed"
|
||||
class="treecol-image"
|
||||
label="&zotero.tabs.attachments.label;"
|
||||
fixed="true"
|
||||
zotero-persist="ordinal hidden sortActive sortDirection"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol
|
||||
id="zotero-items-column-numNotes" disabled-in="feed"
|
||||
class="treecol-image"
|
||||
label="&zotero.tabs.notes.label;"
|
||||
zotero-persist="width ordinal hidden sortActive sortDirection"/>
|
||||
</treecols>
|
||||
<treechildren ondragstart="ZoteroPane_Local.itemsView.onDragStart(event)"
|
||||
ondragenter="return ZoteroPane_Local.itemsView.onDragEnter(event)"
|
||||
ondragover="return ZoteroPane_Local.itemsView.onDragOver(event)"
|
||||
ondrop="return ZoteroPane_Local.itemsView.onDrop(event)"
|
||||
ondragend="ZoteroPane_Local.itemsView.onDragEnd(event)"/>
|
||||
</tree>
|
||||
|
||||
<!-- Label for displaying messages when items pane is hidden
|
||||
(e.g. "Advanced search mode — press Enter to search.")-->
|
||||
<vbox id="zotero-items-pane-message-box" pack="center" align="center"
|
||||
ondragenter="return ZoteroPane.itemsView.onDragEnter(event)"
|
||||
ondragover="return ZoteroPane.itemsView.onDragOver(event)"
|
||||
ondrop="ZoteroPane.itemsView.onDrop(event); ZoteroPane.itemsView.drop(-1, -1, event.dataTransfer)"/>
|
||||
</deck>
|
||||
</vbox>
|
||||
<hbox id="zotero-items-pane" class="virtualized-table-container" zotero-persist="width height" flex="1">
|
||||
<html:div id="zotero-items-tree"></html:div>
|
||||
</hbox>
|
||||
|
||||
<splitter id="zotero-items-splitter" resizebefore="closest" resizeafter="closest" collapse="after" orient="horizontal" zotero-persist="state orient"
|
||||
onmousemove="ZoteroPane.updateToolbarPosition(); ZoteroPane.updateTagsBoxSize()"
|
||||
|
@ -592,7 +372,7 @@
|
|||
</splitter>
|
||||
|
||||
<!-- itemPane.xul -->
|
||||
<vbox id="zotero-item-pane"/>
|
||||
<vbox id="zotero-item-pane" zotero-persist="width height"/>
|
||||
</box>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
|
|
@ -83,6 +83,7 @@
|
|||
<!ENTITY zotero.preferences.sync.reset.resetFileSyncHistory.desc "Compare all attachment files with the storage service">
|
||||
<!ENTITY zotero.preferences.sync.reset "Reset">
|
||||
<!ENTITY zotero.preferences.sync.reset.button "Reset…">
|
||||
<!ENTITY zotero.preferences.sync.toggle "Toggle">
|
||||
|
||||
|
||||
<!ENTITY zotero.preferences.prefpane.search "Search">
|
||||
|
|
|
@ -228,6 +228,7 @@ date.relative.daysAgo.multiple = %S days ago
|
|||
date.relative.yearsAgo.one = 1 year ago
|
||||
date.relative.yearsAgo.multiple = %S years ago
|
||||
|
||||
pane.collections.title = Collections
|
||||
pane.collections.delete.title = Delete Collection
|
||||
pane.collections.delete = Are you sure you want to delete the selected collection?
|
||||
pane.collections.delete.keepItems = Items within this collection will not be deleted.
|
||||
|
@ -299,6 +300,7 @@ pane.items.intro.text1 = Welcome to %S!
|
|||
pane.items.intro.text2 = View the [Quick Start Guide] to learn how to begin building your library, and be sure to [install a %S] so you can add items to %S as you browse the web.
|
||||
pane.items.intro.text3 = Already using %S on another computer? [Set up syncing] to pick up right where you left off.
|
||||
|
||||
pane.items.title = Items
|
||||
pane.items.loading = Loading items…
|
||||
pane.items.loadError = Error loading items list
|
||||
pane.items.columnChooser.moreColumns = More Columns
|
||||
|
|
|
@ -4,83 +4,12 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Why is this necessary? */
|
||||
#zotero-collections-tree treechildren::-moz-tree-image,
|
||||
#zotero-items-tree treechildren::-moz-tree-image {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#zotero-collections-pane
|
||||
{
|
||||
min-width: 150px;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
#zotero-collections-tree {
|
||||
min-height: 5.2em;
|
||||
}
|
||||
|
||||
#zotero-collections-tree treechildren::-moz-tree-row {
|
||||
height: 1.7em;
|
||||
}
|
||||
|
||||
/* As of Fx37, the tree doesn't scale HiDPI images properly on Windows and Linux */
|
||||
#zotero-collections-tree treechildren::-moz-tree-image,
|
||||
#zotero-items-tree treechildren::-moz-tree-image {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
#zotero-collections-tree treechildren::-moz-tree-image(primary)
|
||||
{
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#zotero-collections-tree treechildren::-moz-tree-separator {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#zotero-collections-tree treechildren::-moz-tree-twisty(notwisty),
|
||||
#zotero-collections-tree treechildren::-moz-tree-twisty(header) {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
/* Set by setHighlightedRows() and getRowProperties() in collectionTreeView.js) */
|
||||
#zotero-collections-tree treechildren::-moz-tree-row(highlighted)
|
||||
{
|
||||
background: #FFFF99 !important;
|
||||
}
|
||||
|
||||
#zotero-items-column-hasAttachment, #zotero-items-column-numNotes {
|
||||
min-width: 21px;
|
||||
}
|
||||
|
||||
#zotero-items-column-hasAttachment {
|
||||
list-style-image: url(chrome://zotero/skin/attach-small.png);
|
||||
}
|
||||
|
||||
#zotero-items-column-hasAttachment .treecol-icon {
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
#zotero-items-column-numNotes {
|
||||
list-style-image: url(chrome://zotero/skin/treeitem-note-small.png);
|
||||
}
|
||||
|
||||
#zotero-items-column-numNotes .treecol-icon {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
.tree-columnpicker-icon {
|
||||
list-style-image: url(chrome://zotero/skin/firefox/columnpicker@2x.gif);
|
||||
width: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
#zotero-items-column-numNotes {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie)
|
||||
{
|
||||
margin: 1px 0 0;
|
||||
|
@ -219,75 +148,50 @@
|
|||
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie63) { -moz-image-region: rect(32px, 2016px, 64px, 1984px); }
|
||||
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie64) { -moz-image-region: rect(32px, 2048px, 64px, 2016px); }
|
||||
|
||||
/* Style search results, display non-matches in gray */
|
||||
#zotero-items-tree treechildren::-moz-tree-cell-text(contextRow) {
|
||||
color: gray;
|
||||
.items-tree-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
font-size: .7em;
|
||||
}
|
||||
|
||||
#zotero-items-tree treechildren::-moz-tree-cell-text(contextRow, selected, focus) {
|
||||
/* This is the default color, not the (platform-specific) highlight color, but at least it
|
||||
helps to differentiate when clicking on a context row. */
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Style unread items/collections in bold */
|
||||
#zotero-items-tree treechildren::-moz-tree-cell-text(unread),
|
||||
#zotero-collections-tree treechildren::-moz-tree-cell-text(unread) {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#zotero-items-pane
|
||||
{
|
||||
min-width: 290px;
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
/* Used for intro text */
|
||||
#zotero-items-pane-message-box {
|
||||
overflow-y: auto;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#zotero-items-pane-message-box div {
|
||||
.items-tree-message div {
|
||||
padding: 0 35px;
|
||||
}
|
||||
|
||||
#zotero-items-pane-message-box p {
|
||||
.items-tree-message p {
|
||||
max-width: 800px;
|
||||
font-size: 1.45em;
|
||||
line-height: 1.7em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#zotero-items-pane-message-box div.publications p {
|
||||
.items-tree-message div.publications p {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
/* Increase size when window is wider */
|
||||
#zotero-pane.width-1000 #zotero-items-pane-message-box div.publications p {
|
||||
#zotero-pane.width-1000 .items-tree-message div.publications p {
|
||||
font-size: 1.35em;
|
||||
}
|
||||
|
||||
#zotero-items-pane-message-box span.text-link {
|
||||
.items-tree-message span.text-link {
|
||||
color: rgb(0, 149, 221);
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#zotero-items-pane-message-box {
|
||||
-moz-appearance: listbox;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#zotero-items-pane-message-box description:not(:first-child) {
|
||||
margin-top: .75em;
|
||||
}
|
||||
|
||||
#zotero-item-pane
|
||||
{
|
||||
width: 338px;
|
||||
min-width: 338px;
|
||||
min-height: 200px;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
height: 1.5em;
|
||||
}
|
||||
|
||||
*[zoteroFontSize=large] treechildren::-moz-tree-row
|
||||
*[zoteroFontSize=large] treechildren::-moz-tree-row, *[zoteroFontSize=x-large] treechildren::-moz-tree-row
|
||||
{
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
*[zoteroFontSize=large] .treecol-text
|
||||
*[zoteroFontSize=large] .treecol-text, *[zoteroFontSize=x-large] .treecol-text
|
||||
{
|
||||
margin:0;
|
||||
padding:0;
|
||||
|
|
|
@ -65,8 +65,6 @@ const xpcomFilesAll = [
|
|||
|
||||
/** XPCOM files to be loaded only for local translation and DB access **/
|
||||
const xpcomFilesLocal = [
|
||||
'libraryTreeView',
|
||||
'collectionTreeView',
|
||||
'collectionTreeRow',
|
||||
'annotations',
|
||||
'api',
|
||||
|
@ -102,10 +100,10 @@ const xpcomFilesLocal = [
|
|||
'duplicates',
|
||||
'editorInstance',
|
||||
'feedReader',
|
||||
'fileDragDataProvider',
|
||||
'fulltext',
|
||||
'id',
|
||||
'integration',
|
||||
'itemTreeView',
|
||||
'locale',
|
||||
'locateManager',
|
||||
'mime',
|
||||
|
|
1
resource/react-dom-server.js
vendored
Symbolic link
1
resource/react-dom-server.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../node_modules/react-dom/umd/react-dom-server.browser.development.js
|
|
@ -61,13 +61,15 @@ var require = (function() {
|
|||
}
|
||||
|
||||
function getZotero() {
|
||||
if (win.Zotero) Zotero = win.Zotero;
|
||||
|
||||
if (typeof Zotero === 'undefined') {
|
||||
try {
|
||||
Zotero = Components.classes["@zotero.org/Zotero;1"]
|
||||
.getService(Components.interfaces.nsISupports).wrappedJSObject;
|
||||
} catch (e) {}
|
||||
}
|
||||
return Zotero || {};
|
||||
return Zotero || {};
|
||||
}
|
||||
|
||||
var cons;
|
||||
|
@ -87,6 +89,8 @@ var require = (function() {
|
|||
navigator: typeof win.navigator !== 'undefined' && win.navigator || {},
|
||||
setTimeout: win.setTimeout,
|
||||
clearTimeout: win.clearTimeout,
|
||||
requestAnimationFrame: win.setTimeout,
|
||||
cancelAnimationFrame: win.clearTimeout
|
||||
};
|
||||
Object.defineProperty(globals, 'Zotero', { get: getZotero });
|
||||
var loader = Loader({
|
||||
|
@ -95,11 +99,10 @@ var require = (function() {
|
|||
'': 'resource://zotero/',
|
||||
'containers/': 'chrome://zotero/content/containers/',
|
||||
'components/': 'chrome://zotero/content/components/',
|
||||
'zotero/': 'chrome://zotero/content/modules/',
|
||||
'@zotero/': 'chrome://zotero/content/modules/'
|
||||
'zotero/': 'chrome://zotero/content/',
|
||||
},
|
||||
globals
|
||||
});
|
||||
let require = Require(loader, requirer);
|
||||
return require
|
||||
return require;
|
||||
})();
|
||||
|
|
|
@ -37,3 +37,6 @@
|
|||
@import "components/tabBar";
|
||||
@import "components/tagsBox";
|
||||
@import "components/tagSelector";
|
||||
@import "components/collection-tree";
|
||||
@import "components/virtualized-table";
|
||||
@import "components/item-tree";
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
span.menu-marker {
|
||||
-moz-appearance: toolbarbutton-dropdown;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: -5px;
|
||||
}
|
||||
}
|
||||
|
|
45
scss/components/_collection-tree.scss
Normal file
45
scss/components/_collection-tree.scss
Normal file
|
@ -0,0 +1,45 @@
|
|||
#zotero-collections-tree-container {
|
||||
height: 5.2em;
|
||||
}
|
||||
|
||||
#zotero-collections-tree {
|
||||
width: 100%;
|
||||
|
||||
.virtualized-table {
|
||||
overflow-y: auto;
|
||||
flex: 1 0;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cell.primary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:not(.cell-text) {
|
||||
flex-shrink: 0
|
||||
}
|
||||
|
||||
.cell-text {
|
||||
flex-shrink: 1;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
input.cell-text {
|
||||
border: 1px highlight solid;
|
||||
padding: 1px 2px;
|
||||
margin-right: 5px;
|
||||
width: 100%;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.cell-icon {
|
||||
min-width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.row.editing .cell {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,20 @@
|
|||
width: 16px;
|
||||
}
|
||||
|
||||
.icon.icon-downchevron > img {
|
||||
width: 7px;
|
||||
.icon-bg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon.icon-downchevron {
|
||||
width: 7px !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
-moz-appearance: none !important;
|
||||
}
|
37
scss/components/_item-tree.scss
Normal file
37
scss/components/_item-tree.scss
Normal file
|
@ -0,0 +1,37 @@
|
|||
#zotero-items-pane {
|
||||
min-width: 290px;
|
||||
min-height: 150px;
|
||||
height: 150px;
|
||||
width: 290px;
|
||||
}
|
||||
|
||||
#zotero-items-tree {
|
||||
.virtualized-table-header .icon {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
.cell.primary {
|
||||
.retracted {
|
||||
width: 12px;
|
||||
margin-inline-start: 3px;
|
||||
}
|
||||
|
||||
.tag-swatch {
|
||||
display: inline-block;
|
||||
min-width: 8px;
|
||||
min-height: 8px;
|
||||
margin-inline-start: 3px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.cell.hasAttachment {
|
||||
box-sizing: content-box;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.cell.numNotes {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
|
@ -75,6 +75,7 @@
|
|||
text-overflow: ellipsis;
|
||||
white-space: pre;
|
||||
padding: 1px 4px 3px; // See also TagSelectorList.jsx
|
||||
background-color: $tag-selector-bg;
|
||||
|
||||
&.colored {
|
||||
font-weight: bold;
|
||||
|
|
265
scss/components/_virtualized-table.scss
Normal file
265
scss/components/_virtualized-table.scss
Normal file
|
@ -0,0 +1,265 @@
|
|||
//
|
||||
// Virtualized table
|
||||
// --------------------------------------------------
|
||||
|
||||
/**
|
||||
<hbox class="virtualized-table-container" flex="1">
|
||||
<html:div id="virtualized-table-div"/>
|
||||
</hbox>
|
||||
*/
|
||||
.virtualized-table-container {
|
||||
display: flex;
|
||||
height: 0;
|
||||
flex-direction: column;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
background-color: -moz-field;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.virtualized-table, .drag-image-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.resizing {
|
||||
cursor: col-resize;
|
||||
|
||||
.cell {
|
||||
cursor: col-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.cell {
|
||||
min-width: 30px;
|
||||
cursor: default;
|
||||
white-space: nowrap;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.primary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:not(.cell-text) {
|
||||
flex-shrink: 0
|
||||
}
|
||||
|
||||
.cell-text {
|
||||
flex-shrink: 1;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
margin-inline-start: 6px;
|
||||
}
|
||||
|
||||
.twisty + .cell-text, .spacer-twisty + .cell-text {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.cell-icon {
|
||||
min-width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.drop {
|
||||
color: $shade-0 !important;
|
||||
background: $shade-5 !important;
|
||||
|
||||
* {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
span.drop-before, span.drop-after {
|
||||
position: absolute;
|
||||
width: 20%;
|
||||
height: 1px;
|
||||
background-color: $shade-5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
span.drop-before {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
span.drop-after {
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
&.selected:not(.highlighted) {
|
||||
background-color: highlight;
|
||||
color: highlighttext;
|
||||
}
|
||||
|
||||
&.highlighted {
|
||||
background: #FFFF99;
|
||||
}
|
||||
|
||||
&.unread {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.context-row {
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.column-drag-marker {
|
||||
z-index: 99999;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 2px;
|
||||
background-color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.virtualized-table-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: calc(100% - 11px);
|
||||
border-bottom: 1px solid #ccc;
|
||||
background: #f6f6f6;
|
||||
height: 1.8em;
|
||||
overflow: hidden;
|
||||
border-inline-end: 1px solid #ddd;
|
||||
padding-inline-start: 6px;
|
||||
|
||||
&.static-columns {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.column-picker {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cell {
|
||||
display: flex;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
&.dragging {
|
||||
background: #e9e9e9;
|
||||
}
|
||||
|
||||
.resizer {
|
||||
background: linear-gradient(#ddd, #ddd) no-repeat center/1px 80%;
|
||||
cursor: col-resize;
|
||||
height: 100%;
|
||||
content: "\00a0";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: -5px;
|
||||
min-width: 10px;
|
||||
}
|
||||
|
||||
.label {
|
||||
margin-inline-start: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
&:first-child .label {
|
||||
margin-inline-start: 4px;
|
||||
}
|
||||
|
||||
&.cell-icon {
|
||||
> .label {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sort-indicator {
|
||||
-moz-appearance: toolbarbutton-dropdown;
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
|
||||
&.ascending {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.virtualized-table-body, .drag-image-container {
|
||||
flex: 1 0;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.row {
|
||||
padding-inline-start: 6px;
|
||||
}
|
||||
|
||||
.cell {
|
||||
padding: 2px 5px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
min-height: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
.spacer-twisty {
|
||||
display: inline-block;
|
||||
min-width: 13px;
|
||||
}
|
||||
|
||||
.twisty {
|
||||
margin-inline-end: 0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
fill: #444;
|
||||
transition: transform 0.125s ease;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
&.open svg {
|
||||
transform: rotate(0deg) !important;
|
||||
}
|
||||
}
|
||||
|
||||
*[dir=rtl] {
|
||||
.virtualized-table-header {
|
||||
.cell .sort-indicator {
|
||||
left: 10px;
|
||||
right: initial;
|
||||
}
|
||||
|
||||
.resizer {
|
||||
right: -5px;
|
||||
left: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.twisty svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
8
scss/linux/_collection-tree.scss
Normal file
8
scss/linux/_collection-tree.scss
Normal file
|
@ -0,0 +1,8 @@
|
|||
#zotero-collections-tree-container {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
#zotero-collections-tree .virtualized-table .row {
|
||||
height: 1.333em;
|
||||
}
|
||||
|
5
scss/linux/_item-tree.scss
Normal file
5
scss/linux/_item-tree.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
#zotero-items-tree {
|
||||
.cell.hasAttachment, .cell.numNotes {
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
27
scss/linux/_virtualized-table.scss
Normal file
27
scss/linux/_virtualized-table.scss
Normal file
|
@ -0,0 +1,27 @@
|
|||
.virtualized-table {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.virtualized-table, .drag-image-container {
|
||||
.twisty {
|
||||
padding-inline-end: 3px;
|
||||
svg {
|
||||
width: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.virtualized-table-header {
|
||||
background-image: linear-gradient(#fff, #fafafa);
|
||||
|
||||
&.dragging {
|
||||
color: #666;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.cell .sort-indicator {
|
||||
transform: scale(1.25);
|
||||
|
||||
&.ascending {
|
||||
transform: scale(1.25) rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
54
scss/mac/_collection-tree.scss
Normal file
54
scss/mac/_collection-tree.scss
Normal file
|
@ -0,0 +1,54 @@
|
|||
#zotero-collections-tree {
|
||||
.virtualized-table {
|
||||
background-color: #d2d8e2;
|
||||
|
||||
.row {
|
||||
height: 1.818em;
|
||||
|
||||
&.selected:not(.highlighted) {
|
||||
background: -moz-linear-gradient(top, #6494D4, #2559AC) repeat-x;
|
||||
border-top: 1px solid #5382C5;
|
||||
font-weight: bold !important;
|
||||
color: #ffffff !important;
|
||||
height: calc(1.818em - 1px);
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:focus) .row.selected:not(.highlighted) {
|
||||
background: -moz-linear-gradient(top, #A0B0CF, #7386AB) repeat-x;
|
||||
border-top: 1px solid #94A1C0;
|
||||
}
|
||||
|
||||
&:-moz-window-inactive {
|
||||
background-color: rgb(232, 232, 232);
|
||||
|
||||
.row.selected:not(.highlighted) {
|
||||
background: -moz-linear-gradient(top, #B4B4B4, #8A8A8A) repeat-x;
|
||||
border-top: 1px solid #979797;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IDK why, but these heights are all over the place on macOS (not a f(x)=x)
|
||||
*[zoteroFontSize=medium] #zotero-collections-tree .virtualized-table .row {
|
||||
height: 1.739em;
|
||||
&.focused:not(.highlighted) {
|
||||
height: calc(1.738em - 1px);
|
||||
}
|
||||
}
|
||||
|
||||
*[zoteroFontSize=large] #zotero-collections-tree .virtualized-table .row {
|
||||
height: 1.68em;
|
||||
&.focused:not(.highlighted) {
|
||||
height: calc(1.68em - 1px);
|
||||
}
|
||||
}
|
||||
|
||||
*[zoteroFontSize=x-large] #zotero-collections-tree .virtualized-table .row {
|
||||
height: 1.697em;
|
||||
&.focused:not(.highlighted) {
|
||||
height: calc(1.697em - 1px);
|
||||
}
|
||||
}
|
9
scss/mac/_item-tree.scss
Normal file
9
scss/mac/_item-tree.scss
Normal file
|
@ -0,0 +1,9 @@
|
|||
#zotero-items-tree {
|
||||
// Selected rows when the tree is not the focused element
|
||||
.virtualized-table:not(:focus) {
|
||||
.row.selected {
|
||||
background: #dcdcdc;
|
||||
color: initial;
|
||||
}
|
||||
}
|
||||
}
|
28
scss/mac/_virtualized-table.scss
Normal file
28
scss/mac/_virtualized-table.scss
Normal file
|
@ -0,0 +1,28 @@
|
|||
.virtualized-table, .drag-image-container {
|
||||
.twisty {
|
||||
width: 19px;
|
||||
|
||||
svg {
|
||||
fill: #888;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.focused:not(.highlighted) .twisty svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.spacer-twisty {
|
||||
min-width: 19px;
|
||||
}
|
||||
|
||||
.spacer-header {
|
||||
min-width: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.virtualized-table-body, .drag-image-container{
|
||||
.cell:not(:first-child) {
|
||||
border-inline-start: 1px solid #ddd;
|
||||
}
|
||||
}
|
33
scss/win/_item-tree.scss
Normal file
33
scss/win/_item-tree.scss
Normal file
|
@ -0,0 +1,33 @@
|
|||
#zotero-items-tree .virtualized-table {
|
||||
.row {
|
||||
padding-inline-end: 1px;
|
||||
&.selected {
|
||||
background-color: #e5f3ff;
|
||||
border: 1px solid #7bc3ff;
|
||||
color: inherit;
|
||||
padding-inline-start: 1px;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
&:hover {
|
||||
background-color: #e5f3ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#zotero-items-tree .virtualized-table:not(:focus) {
|
||||
.row {
|
||||
&.selected {
|
||||
color: inherit;
|
||||
background: #f0f0f0;
|
||||
border: none;
|
||||
padding-inline-start: 2px;
|
||||
padding-inline-end: 1px;
|
||||
}
|
||||
&.selected:hover {
|
||||
background-color: #e5f3ff;
|
||||
border: 1px solid #7bc3ff;
|
||||
padding-inline-start: 1px;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
}
|
||||
}
|
49
scss/win/_virtualized-table.scss
Normal file
49
scss/win/_virtualized-table.scss
Normal file
|
@ -0,0 +1,49 @@
|
|||
.spacer-twisty {
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.virtualized-table {
|
||||
&:not(:focus) .row.selected:not(.highlighted) {
|
||||
color: inherit;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding-inline-start: 2px;
|
||||
|
||||
.twisty svg {
|
||||
fill: #b6b6b6;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.twisty.open svg {
|
||||
fill: #636363;
|
||||
}
|
||||
|
||||
&:hover .twisty svg {
|
||||
fill: #4ed0f9;
|
||||
}
|
||||
|
||||
&.drop {
|
||||
background-color: highlight;
|
||||
color: highlighttext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.virtualized-table-header {
|
||||
background: #fff;
|
||||
|
||||
.cell {
|
||||
&:hover {
|
||||
background: #d9ebf9;
|
||||
}
|
||||
&:active {
|
||||
background: #bcdcf4;
|
||||
}
|
||||
&.dragging {
|
||||
background: #d9ebf9 !important;
|
||||
color: #6d6d6d;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,3 +11,6 @@
|
|||
@import "mac/search";
|
||||
@import "mac/tabBar";
|
||||
@import "mac/tag-selector";
|
||||
@import "mac/virtualized-table";
|
||||
@import "mac/collection-tree";
|
||||
@import "mac/item-tree";
|
||||
|
|
|
@ -10,3 +10,6 @@
|
|||
@import "linux/search";
|
||||
@import "linux/tagsBox";
|
||||
@import "linux/about";
|
||||
@import "linux/virtualized-table";
|
||||
@import "linux/item-tree";
|
||||
@import "linux/collection-tree";
|
||||
|
|
|
@ -7,3 +7,5 @@
|
|||
@import "win/createParent";
|
||||
@import "win/search";
|
||||
@import "win/tag-selector";
|
||||
@import "win/item-tree";
|
||||
@import "win/virtualized-table";
|
||||
|
|
|
@ -257,6 +257,11 @@ var waitForTagSelector = function (win, numUpdates = 1) {
|
|||
return deferred.promise;
|
||||
};
|
||||
|
||||
var waitForCollectionTree = function(win) {
|
||||
let cv = win.ZoteroPane.collectionsView;
|
||||
return cv._waitForEvent('refresh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a single item event. Returns a promise for the item ID(s).
|
||||
*/
|
||||
|
@ -324,29 +329,10 @@ function waitForCallback(cb, interval, timeout) {
|
|||
}
|
||||
|
||||
|
||||
function clickOnItemsRow(itemsView, row, button = 0) {
|
||||
var x = {};
|
||||
var y = {};
|
||||
var width = {};
|
||||
var height = {};
|
||||
itemsView._treebox.getCoordsForCellItem(
|
||||
row,
|
||||
itemsView._treebox.columns.getNamedColumn('zotero-items-column-title'),
|
||||
'text',
|
||||
x, y, width, height
|
||||
);
|
||||
|
||||
// Select row to trigger multi-select
|
||||
var tree = itemsView._treebox.treeBody;
|
||||
var rect = tree.getBoundingClientRect();
|
||||
var x = rect.left + x.value;
|
||||
var y = rect.top + y.value;
|
||||
tree.dispatchEvent(new MouseEvent("mousedown", {
|
||||
clientX: x,
|
||||
clientY: y,
|
||||
button,
|
||||
detail: 1
|
||||
}));
|
||||
function clickOnItemsRow(win, itemsView, row) {
|
||||
itemsView._treebox.scrollToRow(row);
|
||||
let elem = win.document.querySelector(`#${itemsView.id}-row-${row}`);
|
||||
elem.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, button: 0 }));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
describe("Create Bibliography Dialog", function () {
|
||||
var win, zp;
|
||||
|
||||
before(function* () {
|
||||
win = yield loadZoteroPane();
|
||||
before(async function () {
|
||||
win = await loadZoteroPane();
|
||||
zp = win.ZoteroPane;
|
||||
});
|
||||
|
||||
|
@ -12,19 +12,19 @@ describe("Create Bibliography Dialog", function () {
|
|||
win.close();
|
||||
});
|
||||
|
||||
it("should perform a search", function* () {
|
||||
yield Zotero.Styles.init();
|
||||
var item = yield createDataObject('item');
|
||||
it("should perform a search", async function () {
|
||||
await Zotero.Styles.init();
|
||||
var item = await createDataObject('item');
|
||||
|
||||
var deferred = Zotero.Promise.defer();
|
||||
var called = false;
|
||||
waitForWindow("chrome://zotero/content/bibliography.xul", function (dialog) {
|
||||
waitForWindow("chrome://zotero/content/preferences/preferences.xul", function (window) {
|
||||
// Wait for pane switch
|
||||
Zotero.Promise.coroutine(function* () {
|
||||
(async function () {
|
||||
do {
|
||||
Zotero.debug("Checking for pane");
|
||||
yield Zotero.Promise.delay(5);
|
||||
await Zotero.Promise.delay(5);
|
||||
}
|
||||
while (window.document.documentElement.currentPane.id != 'zotero-prefpane-cite');
|
||||
let pane = window.document.documentElement.currentPane;
|
||||
|
@ -37,8 +37,8 @@ describe("Create Bibliography Dialog", function () {
|
|||
});
|
||||
dialog.document.getElementById('manage-styles').click();
|
||||
});
|
||||
win.Zotero_File_Interface.bibliographyFromItems();
|
||||
yield deferred.promise;
|
||||
await win.Zotero_File_Interface.bibliographyFromItems();
|
||||
await deferred.promise;
|
||||
|
||||
assert.ok(called);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
describe("Zotero.CollectionTreeView", function() {
|
||||
describe("Zotero.CollectionTree", function() {
|
||||
var win, zp, cv, userLibraryID;
|
||||
|
||||
before(function* () {
|
||||
|
@ -51,8 +51,10 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
if (cv.isContainerOpen(group2Row)) {
|
||||
yield cv.toggleOpenState(group2Row);
|
||||
}
|
||||
// Don't wait for delayed save
|
||||
|
||||
cv._saveOpenStates();
|
||||
// #_saveOpenStates is debounced
|
||||
yield Zotero.Promise.delay(500);
|
||||
|
||||
group1Row = cv.getRowIndexByID(group1.treeViewID);
|
||||
group2Row = cv.getRowIndexByID(group2.treeViewID);
|
||||
|
@ -79,30 +81,22 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
describe("collapse/expand", function () {
|
||||
it("should close and open My Library repeatedly", function* () {
|
||||
yield cv.selectLibrary(userLibraryID);
|
||||
var row = cv.selection.currentIndex;
|
||||
var row = cv.selection.focused;
|
||||
|
||||
cv.collapseLibrary(userLibraryID);
|
||||
var nextRow = cv.getRow(row + 1);
|
||||
assert.equal(cv.selection.currentIndex, row);
|
||||
assert.ok(nextRow.isSeparator());
|
||||
assert.equal(cv.selection.focused, row);
|
||||
assert.isFalse(cv.isContainerOpen(row));
|
||||
|
||||
yield cv.expandLibrary(userLibraryID);
|
||||
nextRow = cv.getRow(row + 1);
|
||||
assert.equal(cv.selection.currentIndex, row);
|
||||
assert.ok(!nextRow.isSeparator());
|
||||
assert.equal(cv.selection.focused, row);
|
||||
assert.ok(cv.isContainerOpen(row));
|
||||
|
||||
cv.collapseLibrary(userLibraryID);
|
||||
nextRow = cv.getRow(row + 1);
|
||||
assert.equal(cv.selection.currentIndex, row);
|
||||
assert.ok(nextRow.isSeparator());
|
||||
assert.equal(cv.selection.focused, row);
|
||||
assert.isFalse(cv.isContainerOpen(row));
|
||||
|
||||
yield cv.expandLibrary(userLibraryID);
|
||||
nextRow = cv.getRow(row + 1);
|
||||
assert.equal(cv.selection.currentIndex, row);
|
||||
assert.ok(!nextRow.isSeparator());
|
||||
assert.equal(cv.selection.focused, row);
|
||||
assert.ok(cv.isContainerOpen(row));
|
||||
})
|
||||
})
|
||||
|
@ -112,7 +106,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
|
||||
before(function* () {
|
||||
yield cv.selectLibrary(userLibraryID);
|
||||
libraryRow = cv.selection.currentIndex;
|
||||
libraryRow = cv.selection.focused;
|
||||
});
|
||||
|
||||
beforeEach(function* () {
|
||||
|
@ -128,28 +122,31 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
it("should open a library and respect stored container state", function* () {
|
||||
// Collapse B
|
||||
yield cv.toggleOpenState(cv.getRowIndexByID(col2.treeViewID));
|
||||
yield cv._saveOpenStates();
|
||||
cv._saveOpenStates();
|
||||
// #_saveOpenStates is debounced
|
||||
yield Zotero.Promise.delay(500);
|
||||
|
||||
// Close and reopen library
|
||||
yield cv.toggleOpenState(libraryRow);
|
||||
yield cv.expandLibrary(userLibraryID);
|
||||
|
||||
assert.ok(cv.getRowIndexByID(col1.treeViewID))
|
||||
assert.ok(cv.getRowIndexByID(col2.treeViewID))
|
||||
assert.isFalse(cv.getRowIndexByID(col3.treeViewID))
|
||||
|
||||
assert.isTrue(cv.isContainerOpen(libraryRow));
|
||||
assert.isTrue(cv.isContainerOpen(cv.getRowIndexByID(col1.treeViewID)));
|
||||
assert.isFalse(cv.isContainerOpen(cv.getRowIndexByID(col2.treeViewID)));
|
||||
});
|
||||
|
||||
it("should open a library and all subcollections in recursive mode", function* () {
|
||||
yield cv.toggleOpenState(cv.getRowIndexByID(col2.treeViewID));
|
||||
yield cv._saveOpenStates();
|
||||
cv._saveOpenStates();
|
||||
// #_saveOpenStates is debounced
|
||||
yield Zotero.Promise.delay(500);
|
||||
|
||||
// Close and reopen library
|
||||
yield cv.toggleOpenState(libraryRow);
|
||||
yield cv.expandLibrary(userLibraryID, true);
|
||||
|
||||
assert.ok(cv.getRowIndexByID(col1.treeViewID))
|
||||
assert.ok(cv.getRowIndexByID(col2.treeViewID))
|
||||
assert.ok(cv.getRowIndexByID(col3.treeViewID))
|
||||
|
||||
assert.isTrue(cv.isContainerOpen(cv.getRowIndexByID(col1.treeViewID)));
|
||||
assert.isTrue(cv.isContainerOpen(cv.getRowIndexByID(col2.treeViewID)));
|
||||
});
|
||||
|
||||
it("should open a group and show top-level collections", function* () {
|
||||
|
@ -162,9 +159,11 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
var col5 = yield createDataObject('collection', { libraryID, parentID: col4.id });
|
||||
|
||||
// Close everything
|
||||
[col4, col1, group].forEach(o => cv._closeContainer(cv.getRowIndexByID(o.treeViewID)));
|
||||
yield Zotero.Promise.all([col4, col1, group]
|
||||
.map(o => cv.toggleOpenState(cv.getRowIndexByID(o.treeViewID), false)));
|
||||
|
||||
yield cv.expandLibrary(libraryID);
|
||||
|
||||
assert.isNumber(cv.getRowIndexByID(col1.treeViewID));
|
||||
assert.isNumber(cv.getRowIndexByID(col2.treeViewID));
|
||||
assert.isNumber(cv.getRowIndexByID(col3.treeViewID));
|
||||
|
@ -185,6 +184,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
assert.isFalse(cv.isContainerOpen(row));
|
||||
|
||||
yield cv.expandToCollection(collection2.id);
|
||||
cv.forceUpdate();
|
||||
|
||||
// Make sure parent row position hasn't changed
|
||||
assert.equal(cv.getRowIndexByID("C" + collection1.id), row);
|
||||
|
@ -196,7 +196,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
describe("#selectByID()", function () {
|
||||
it("should select the trash", function* () {
|
||||
yield cv.selectByID("T1");
|
||||
var row = cv.selection.currentIndex;
|
||||
var row = cv.selection.focused;
|
||||
var treeRow = cv.getRow(row);
|
||||
assert.ok(treeRow.isTrash());
|
||||
assert.equal(treeRow.ref.libraryID, userLibraryID);
|
||||
|
@ -206,7 +206,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
describe("#selectWait()", function () {
|
||||
it("shouldn't hang if row is already selected", function* () {
|
||||
var row = cv.getRowIndexByID("T" + userLibraryID);
|
||||
cv.selection.select(row);
|
||||
yield cv.selectWait(row);
|
||||
yield Zotero.Promise.delay(50);
|
||||
yield cv.selectWait(row);
|
||||
})
|
||||
|
@ -288,14 +288,14 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
yield cv.selectLibrary(group.libraryID);
|
||||
yield waitForItemsLoad(win);
|
||||
|
||||
assert.isFalse(cv.selectedTreeRow.editable);
|
||||
assert.isFalse(zp.getCollectionTreeRow().editable);
|
||||
var cmd = win.document.getElementById('cmd_zotero_newStandaloneNote');
|
||||
assert.isTrue(cmd.getAttribute('disabled') == 'true');
|
||||
|
||||
group.editable = true;
|
||||
yield group.saveTx();
|
||||
|
||||
assert.isTrue(cv.selectedTreeRow.editable);
|
||||
assert.isTrue(zp.getCollectionTreeRow().editable);
|
||||
assert.isFalse(cmd.getAttribute('disabled') == 'true');
|
||||
});
|
||||
|
||||
|
@ -375,7 +375,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
assert.isAbove(aRow, 0);
|
||||
assert.isAbove(bRow, 0);
|
||||
// skipSelect is implied for multiple collections, so library should still be selected
|
||||
assert.equal(cv.selection.currentIndex, 0);
|
||||
assert.equal(cv.selection.focused, 0);
|
||||
});
|
||||
|
||||
|
||||
|
@ -432,7 +432,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
// since otherwise they'll interfere with the count
|
||||
yield getGroup();
|
||||
|
||||
var originalRowCount = cv.rowCount;
|
||||
var originalRowCount = cv._rows.length;
|
||||
|
||||
var group = yield createGroup();
|
||||
yield createDataObject('collection', { libraryID: group.libraryID });
|
||||
|
@ -442,7 +442,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
yield createDataObject('collection', { libraryID: group.libraryID });
|
||||
|
||||
// Group, collections, Duplicates, Unfiled, and trash
|
||||
assert.equal(cv.rowCount, originalRowCount + 9);
|
||||
assert.equal(cv._rows.length, originalRowCount + 9);
|
||||
|
||||
// Select group
|
||||
yield cv.selectLibrary(group.libraryID);
|
||||
|
@ -452,7 +452,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
try {
|
||||
yield group.eraseTx();
|
||||
|
||||
assert.equal(cv.rowCount, originalRowCount);
|
||||
assert.equal(cv._rows.length, originalRowCount);
|
||||
// Make sure the tree wasn't refreshed
|
||||
sinon.assert.notCalled(spy);
|
||||
}
|
||||
|
@ -482,7 +482,9 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
yield cv.selectLibrary(feed.libraryID);
|
||||
waitForDialog();
|
||||
var id = feed.treeViewID;
|
||||
let promise = waitForCollectionTree(win);
|
||||
yield win.ZoteroPane.deleteSelectedCollection();
|
||||
yield promise;
|
||||
assert.isFalse(cv.getRowIndexByID(id))
|
||||
})
|
||||
});
|
||||
|
@ -491,10 +493,11 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
it("should switch to library root if item isn't in collection", async function () {
|
||||
var item = await createDataObject('item');
|
||||
var collection = await createDataObject('collection');
|
||||
Zotero.debug(zp.itemsView._rows);
|
||||
await cv.selectItem(item.id);
|
||||
await waitForItemsLoad(win);
|
||||
assert.equal(cv.selection.currentIndex, 0);
|
||||
assert.sameMembers(zp.itemsView.getSelectedItems(true), [item.id]);
|
||||
assert.equal(cv.selection.focused, 0);
|
||||
assert.sameMembers(zp.itemsView.getSelectedItems(), [item]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -505,12 +508,12 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
var item2 = await createDataObject('item');
|
||||
await cv.selectItems([item1.id, item2.id]);
|
||||
await waitForItemsLoad(win);
|
||||
assert.equal(cv.selection.currentIndex, 0);
|
||||
assert.equal(cv.selection.focused, 0);
|
||||
assert.sameMembers(zp.itemsView.getSelectedItems(true), [item1.id, item2.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#drop()", function () {
|
||||
describe("#onDrop()", function () {
|
||||
/**
|
||||
* Simulate a drag and drop
|
||||
*
|
||||
|
@ -521,7 +524,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
* value returned after the drag. Otherwise, an 'add' event will be waited for, and
|
||||
* an object with 'ids' and 'extraData' will be returned.
|
||||
*/
|
||||
var drop = Zotero.Promise.coroutine(function* (objectType, targetRow, ids, promise, action = 'copy') {
|
||||
var onDrop = Zotero.Promise.coroutine(function* (objectType, targetRow, ids, promise, action = 'copy') {
|
||||
if (typeof targetRow == 'string') {
|
||||
var row = cv.getRowIndexByID(targetRow);
|
||||
var orient = 0;
|
||||
|
@ -530,30 +533,33 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
var { row, orient } = targetRow;
|
||||
}
|
||||
|
||||
var stub = sinon.stub(Zotero.DragDrop, "getDragTarget");
|
||||
stub.returns(cv.getRow(row));
|
||||
Zotero.DragDrop.currentDragSource = objectType == "item" && zp.itemsView.collectionTreeRow;
|
||||
|
||||
if (!promise) {
|
||||
promise = waitForNotifierEvent("add", objectType);
|
||||
}
|
||||
yield cv.drop(row, orient, {
|
||||
dropEffect: action,
|
||||
effectAllowed: action,
|
||||
mozSourceNode: win.document.getElementById(`zotero-${objectType}s-tree`).treeBoxObject.treeBody,
|
||||
types: {
|
||||
contains: function (type) {
|
||||
return type == `zotero/${objectType}`;
|
||||
}
|
||||
},
|
||||
getData: function (type) {
|
||||
if (type == `zotero/${objectType}`) {
|
||||
return ids.join(",");
|
||||
yield cv.onDrop({
|
||||
persist: () => 0,
|
||||
target: {ownerDocument: {defaultView: win}},
|
||||
dataTransfer: {
|
||||
dropEffect: action,
|
||||
effectAllowed: action,
|
||||
types: {
|
||||
contains: function (type) {
|
||||
return type == `zotero/${objectType}`;
|
||||
}
|
||||
},
|
||||
getData: function (type) {
|
||||
if (type == `zotero/${objectType}`) {
|
||||
return ids.join(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, row);
|
||||
|
||||
// Add observer to wait for add
|
||||
var result = yield promise;
|
||||
stub.restore();
|
||||
Zotero.DragDrop.currentDragSource = null;
|
||||
return result;
|
||||
});
|
||||
|
||||
|
@ -561,12 +567,9 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
var canDrop = Zotero.Promise.coroutine(function* (type, targetRowID, ids) {
|
||||
var row = cv.getRowIndexByID(targetRowID);
|
||||
|
||||
var stub = sinon.stub(Zotero.DragDrop, "getDragTarget");
|
||||
stub.returns(cv.getRow(row));
|
||||
var dt = {
|
||||
dropEffect: 'copy',
|
||||
effectAllowed: 'copy',
|
||||
mozSourceNode: win.document.getElementById(`zotero-${type}s-tree`),
|
||||
types: {
|
||||
contains: function (type) {
|
||||
return type == `zotero/${type}`;
|
||||
|
@ -582,7 +585,6 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
if (canDrop) {
|
||||
canDrop = yield cv.canDropCheckAsync(row, 0, dt);
|
||||
}
|
||||
stub.restore();
|
||||
return canDrop;
|
||||
});
|
||||
|
||||
|
@ -604,14 +606,14 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'collection-item', 'test');
|
||||
|
||||
yield drop('item', 'C' + collection.id, [item.id], deferred.promise);
|
||||
yield onDrop('item', 'C' + collection.id, [item.id], deferred.promise);
|
||||
|
||||
Zotero.Notifier.unregisterObserver(observerID);
|
||||
|
||||
yield cv.selectCollection(collection.id);
|
||||
yield waitForItemsLoad(win);
|
||||
|
||||
var itemsView = win.ZoteroPane.itemsView
|
||||
var itemsView = win.ZoteroPane.itemsView;
|
||||
assert.equal(itemsView.rowCount, 1);
|
||||
var treeRow = itemsView.getRow(0);
|
||||
assert.equal(treeRow.ref.id, item.id);
|
||||
|
@ -636,7 +638,9 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'collection-item', 'test');
|
||||
|
||||
yield drop('item', 'C' + collection2.id, [item.id], deferred.promise, 'move');
|
||||
let promise = zp.itemsView.waitForSelect();
|
||||
yield onDrop('item', 'C' + collection2.id, [item.id], deferred.promise, 'move');
|
||||
yield promise;
|
||||
|
||||
Zotero.Notifier.unregisterObserver(observerID);
|
||||
|
||||
|
@ -683,7 +687,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'item', 'test');
|
||||
|
||||
yield drop('item', 'P' + libraryID, [item.id], deferred.promise);
|
||||
yield onDrop('item', 'P' + libraryID, [item.id], deferred.promise);
|
||||
|
||||
Zotero.Notifier.unregisterObserver(observerID);
|
||||
stub.restore();
|
||||
|
@ -721,7 +725,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'item', 'test');
|
||||
|
||||
yield drop('item', 'P' + libraryID, [item.id], deferred.promise);
|
||||
yield onDrop('item', 'P' + libraryID, [item.id], deferred.promise);
|
||||
|
||||
Zotero.Notifier.unregisterObserver(observerID);
|
||||
stub.restore();
|
||||
|
@ -760,7 +764,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'item', 'test');
|
||||
|
||||
yield drop('item', 'P' + libraryID, [item.id], deferred.promise);
|
||||
yield onDrop('item', 'P' + libraryID, [item.id], deferred.promise);
|
||||
|
||||
Zotero.Notifier.unregisterObserver(observerID);
|
||||
stub.restore();
|
||||
|
@ -799,7 +803,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'item', 'test');
|
||||
|
||||
yield drop('item', 'P' + libraryID, [item.id], deferred.promise);
|
||||
yield onDrop('item', 'P' + libraryID, [item.id], deferred.promise);
|
||||
|
||||
Zotero.Notifier.unregisterObserver(observerID);
|
||||
stub.restore();
|
||||
|
@ -821,7 +825,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
parentItemID: item.id
|
||||
});
|
||||
|
||||
var ids = (yield drop('item', 'L' + group.libraryID, [item.id])).ids;
|
||||
var ids = (yield onDrop('item', 'L' + group.libraryID, [item.id])).ids;
|
||||
|
||||
yield cv.selectLibrary(group.libraryID);
|
||||
yield waitForItemsLoad(win);
|
||||
|
@ -864,7 +868,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
attachment.setField('title', attachmentTitle);
|
||||
yield attachment.saveTx();
|
||||
|
||||
yield drop('item', 'L' + group.libraryID, [item.id]);
|
||||
yield onDrop('item', 'L' + group.libraryID, [item.id]);
|
||||
assert.isFalse(yield canDrop('item', 'L' + group.libraryID, [item.id]));
|
||||
})
|
||||
|
||||
|
@ -878,7 +882,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
await cv.selectLibrary(group1.libraryID);
|
||||
await waitForItemsLoad(win);
|
||||
|
||||
await drop('item', 'L' + group2.libraryID, [item.id]);
|
||||
await onDrop('item', 'L' + group2.libraryID, [item.id]);
|
||||
|
||||
assert.isFalse(await item.getLinkedItem(group2.libraryID));
|
||||
// New collection should link back to original
|
||||
|
@ -893,14 +897,14 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
var collection = await createDataObject('collection', { libraryID: group.libraryID });
|
||||
|
||||
var item = await createDataObject('item', false, { skipSelect: true });
|
||||
await drop('item', 'L' + group.libraryID, [item.id]);
|
||||
await onDrop('item', 'L' + group.libraryID, [item.id]);
|
||||
|
||||
var droppedItem = await item.getLinkedItem(group.libraryID);
|
||||
droppedItem.setCollections([collection.id]);
|
||||
droppedItem.deleted = true;
|
||||
await droppedItem.saveTx();
|
||||
|
||||
await drop('item', 'L' + group.libraryID, [item.id]);
|
||||
await onDrop('item', 'L' + group.libraryID, [item.id]);
|
||||
|
||||
var linkedItem = await item.getLinkedItem(group.libraryID);
|
||||
assert.notEqual(linkedItem, droppedItem);
|
||||
|
@ -934,7 +938,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'collection', 'test');
|
||||
|
||||
yield drop(
|
||||
yield onDrop(
|
||||
'collection',
|
||||
{
|
||||
row: 0,
|
||||
|
@ -990,7 +994,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'collection', 'test');
|
||||
|
||||
yield drop(
|
||||
yield onDrop(
|
||||
'collection',
|
||||
{
|
||||
row: colIndexE,
|
||||
|
@ -1054,7 +1058,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'collection', 'test');
|
||||
|
||||
yield drop(
|
||||
yield onDrop(
|
||||
'collection',
|
||||
{
|
||||
row: colIndexD,
|
||||
|
@ -1107,7 +1111,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
}
|
||||
}, 'collection', 'test');
|
||||
|
||||
await drop(
|
||||
await onDrop(
|
||||
'collection',
|
||||
'L' + group.libraryID,
|
||||
[collectionA.id],
|
||||
|
@ -1144,7 +1148,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
await cv.selectCollection(collection.id);
|
||||
await waitForItemsLoad(win);
|
||||
|
||||
await drop('collection', 'L' + group2.libraryID, [collection.id]);
|
||||
await onDrop('collection', 'L' + group2.libraryID, [collection.id]);
|
||||
|
||||
assert.isFalse(await collection.getLinkedCollection(group2.libraryID));
|
||||
// New collection should link back to original
|
||||
|
@ -1174,7 +1178,7 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
var deferred = Zotero.Promise.defer();
|
||||
var itemIds;
|
||||
|
||||
var ids = (yield drop('item', 'C' + collection.id, [feedItem.id])).ids;
|
||||
var ids = (yield onDrop('item', 'C' + collection.id, [feedItem.id])).ids;
|
||||
|
||||
// Check that the translated item was the one that was created after drag
|
||||
var item;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue