39e8b226cb
This reverts commit 7f732f56df
.
4058 lines
113 KiB
JavaScript
4058 lines
113 KiB
JavaScript
/*
|
|
***** 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 *****
|
|
*/
|
|
const ZOTERO_TAB_URL = "chrome://zotero/content/tab.xul";
|
|
|
|
/*
|
|
* This object contains the various functions for the interface
|
|
*/
|
|
var ZoteroPane = new function()
|
|
{
|
|
var _unserialized = false;
|
|
this.collectionsView = false;
|
|
this.itemsView = false;
|
|
this.__defineGetter__('loaded', function () _loaded);
|
|
|
|
//Privileged methods
|
|
this.init = init;
|
|
this.destroy = destroy;
|
|
this.makeVisible = makeVisible;
|
|
this.isShowing = isShowing;
|
|
this.isFullScreen = isFullScreen;
|
|
this.handleKeyDown = handleKeyDown;
|
|
this.handleKeyUp = handleKeyUp;
|
|
this.setHighlightedRowsCallback = setHighlightedRowsCallback;
|
|
this.handleKeyPress = handleKeyPress;
|
|
this.newItem = newItem;
|
|
this.newCollection = newCollection;
|
|
this.newSearch = newSearch;
|
|
this.openAdvancedSearchWindow = openAdvancedSearchWindow;
|
|
this.toggleTagSelector = toggleTagSelector;
|
|
this.updateTagSelectorSize = updateTagSelectorSize;
|
|
this.getTagSelection = getTagSelection;
|
|
this.clearTagSelection = clearTagSelection;
|
|
this.updateTagFilter = updateTagFilter;
|
|
this.onCollectionSelected = onCollectionSelected;
|
|
this.reindexItem = reindexItem;
|
|
this.duplicateSelectedItem = duplicateSelectedItem;
|
|
this.editSelectedCollection = editSelectedCollection;
|
|
this.copySelectedItemsToClipboard = copySelectedItemsToClipboard;
|
|
this.clearQuicksearch = clearQuicksearch;
|
|
this.handleSearchKeypress = handleSearchKeypress;
|
|
this.handleSearchInput = handleSearchInput;
|
|
this.search = search;
|
|
this.selectItem = selectItem;
|
|
this.getSelectedCollection = getSelectedCollection;
|
|
this.getSelectedSavedSearch = getSelectedSavedSearch;
|
|
this.getSelectedItems = getSelectedItems;
|
|
this.getSortedItems = getSortedItems;
|
|
this.getSortField = getSortField;
|
|
this.getSortDirection = getSortDirection;
|
|
this.buildItemContextMenu = buildItemContextMenu;
|
|
this.loadURI = loadURI;
|
|
this.setItemsPaneMessage = setItemsPaneMessage;
|
|
this.clearItemsPaneMessage = clearItemsPaneMessage;
|
|
this.contextPopupShowing = contextPopupShowing;
|
|
this.openNoteWindow = openNoteWindow;
|
|
this.addTextToNote = addTextToNote;
|
|
this.addAttachmentFromDialog = addAttachmentFromDialog;
|
|
this.viewAttachment = viewAttachment;
|
|
this.viewSelectedAttachment = viewSelectedAttachment;
|
|
this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog;
|
|
this.relinkAttachment = relinkAttachment;
|
|
this.reportErrors = reportErrors;
|
|
this.displayErrorMessage = displayErrorMessage;
|
|
|
|
this.document = document;
|
|
|
|
const COLLECTIONS_HEIGHT = 32; // minimum height of the collections pane and toolbar
|
|
|
|
var self = this,
|
|
_loaded = false, _madeVisible = false,
|
|
titlebarcolorState, titleState, observerService,
|
|
_reloadFunctions = [];
|
|
|
|
/**
|
|
* Called when the window containing Zotero pane is open
|
|
*/
|
|
function init() {
|
|
// Set "Report Errors..." label via property rather than DTD entity,
|
|
// since we need to reference it in script elsewhere
|
|
document.getElementById('zotero-tb-actions-reportErrors').setAttribute('label',
|
|
Zotero.getString('errorReport.reportErrors'));
|
|
// Set key down handler
|
|
document.getElementById('appcontent').addEventListener('keydown', ZoteroPane_Local.handleKeyDown, true);
|
|
|
|
if (Zotero.locked) {
|
|
return;
|
|
}
|
|
_loaded = true;
|
|
|
|
var zp = document.getElementById('zotero-pane');
|
|
Zotero.setFontSize(zp);
|
|
ZoteroPane_Local.updateToolbarPosition();
|
|
window.addEventListener("resize", ZoteroPane_Local.updateToolbarPosition, false);
|
|
window.setTimeout(ZoteroPane_Local.updateToolbarPosition, 0);
|
|
|
|
Zotero.updateQuickSearchBox(document);
|
|
|
|
if (Zotero.isMac) {
|
|
//document.getElementById('zotero-tb-actions-zeroconf-update').setAttribute('hidden', false);
|
|
document.getElementById('zotero-pane-stack').setAttribute('platform', 'mac');
|
|
} else if(Zotero.isWin) {
|
|
document.getElementById('zotero-pane-stack').setAttribute('platform', 'win');
|
|
}
|
|
|
|
// register an observer for Zotero reload
|
|
observerService = Components.classes["@mozilla.org/observer-service;1"]
|
|
.getService(Components.interfaces.nsIObserverService);
|
|
observerService.addObserver(_reloadObserver, "zotero-reloaded", false);
|
|
this.addReloadListener(_loadPane);
|
|
|
|
// continue loading pane
|
|
_loadPane();
|
|
}
|
|
|
|
/**
|
|
* Called on window load or when has been reloaded after switching into or out of connector
|
|
* mode
|
|
*/
|
|
function _loadPane() {
|
|
if(!Zotero || !Zotero.initialized) return;
|
|
|
|
if(Zotero.isConnector) {
|
|
ZoteroPane_Local.setItemsPaneMessage(Zotero.getString('connector.standaloneOpen'));
|
|
return;
|
|
} else {
|
|
ZoteroPane_Local.clearItemsPaneMessage();
|
|
}
|
|
|
|
//Initialize collections view
|
|
ZoteroPane_Local.collectionsView = new Zotero.CollectionTreeView();
|
|
var collectionsTree = document.getElementById('zotero-collections-tree');
|
|
collectionsTree.view = ZoteroPane_Local.collectionsView;
|
|
collectionsTree.controllers.appendController(new Zotero.CollectionTreeCommandController(collectionsTree));
|
|
collectionsTree.addEventListener("mousedown", ZoteroPane_Local.onTreeMouseDown, true);
|
|
collectionsTree.addEventListener("click", ZoteroPane_Local.onTreeClick, true);
|
|
|
|
var itemsTree = document.getElementById('zotero-items-tree');
|
|
itemsTree.controllers.appendController(new Zotero.ItemTreeCommandController(itemsTree));
|
|
itemsTree.addEventListener("mousedown", ZoteroPane_Local.onTreeMouseDown, true);
|
|
itemsTree.addEventListener("click", ZoteroPane_Local.onTreeClick, true);
|
|
|
|
var menu = document.getElementById("contentAreaContextMenu");
|
|
menu.addEventListener("popupshowing", ZoteroPane_Local.contextPopupShowing, false);
|
|
|
|
Zotero.Keys.windowInit(document);
|
|
|
|
if (Zotero.restoreFromServer) {
|
|
Zotero.restoreFromServer = false;
|
|
|
|
setTimeout(function () {
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
|
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
|
var index = ps.confirmEx(
|
|
null,
|
|
"Zotero Restore",
|
|
"The local Zotero database has been cleared."
|
|
+ " "
|
|
+ "Would you like to restore from the Zotero server now?",
|
|
buttonFlags,
|
|
"Sync Now",
|
|
null, null, null, {}
|
|
);
|
|
|
|
if (index == 0) {
|
|
Zotero.Sync.Server.sync({
|
|
onSuccess: function () {
|
|
Zotero.Sync.Runner.setSyncIcon();
|
|
|
|
ps.alert(
|
|
null,
|
|
"Restore Completed",
|
|
"The local Zotero database has been successfully restored."
|
|
);
|
|
},
|
|
|
|
onError: function (msg) {
|
|
ps.alert(
|
|
null,
|
|
"Restore Failed",
|
|
"An error occurred while restoring from the server:\n\n"
|
|
+ msg
|
|
);
|
|
|
|
Zotero.Sync.Runner.error(msg);
|
|
}
|
|
});
|
|
}
|
|
}, 1000);
|
|
}
|
|
// If the database was initialized or there are no sync credentials and
|
|
// Zotero hasn't been run before in this profile, display the start page
|
|
// -- this way the page won't be displayed when they sync their DB to
|
|
// another profile or if the DB is initialized erroneously (e.g. while
|
|
// switching data directory locations)
|
|
else if (Zotero.Prefs.get('firstRun2')) {
|
|
if (Zotero.Schema.dbInitialized || !Zotero.Sync.Server.enabled) {
|
|
setTimeout(function () {
|
|
if(Zotero.isStandalone) {
|
|
ZoteroPane_Local.loadURI("https://www.zotero.org/start_standalone");
|
|
} else {
|
|
gBrowser.selectedTab = gBrowser.addTab("https://www.zotero.org/start");
|
|
}
|
|
}, 400);
|
|
}
|
|
Zotero.Prefs.set('firstRun2', false);
|
|
try {
|
|
Zotero.Prefs.clear('firstRun');
|
|
}
|
|
catch (e) {}
|
|
}
|
|
|
|
// Hide sync debugging menu by default
|
|
if (Zotero.Prefs.get('sync.debugMenu')) {
|
|
var sep = document.getElementById('zotero-tb-actions-sync-separator');
|
|
sep.hidden = false;
|
|
sep.nextSibling.hidden = false;
|
|
sep.nextSibling.nextSibling.hidden = false;
|
|
sep.nextSibling.nextSibling.nextSibling.hidden = false;
|
|
}
|
|
|
|
if (Zotero.openPane) {
|
|
Zotero.openPane = false;
|
|
setTimeout(function () {
|
|
ZoteroPane_Local.show();
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Create the New Item (+) submenu with each item type
|
|
*/
|
|
this.buildItemTypeSubMenu = function () {
|
|
var moreMenu = document.getElementById('zotero-tb-add-more');
|
|
|
|
while (moreMenu.hasChildNodes()) {
|
|
moreMenu.removeChild(moreMenu.firstChild);
|
|
}
|
|
|
|
// Sort by localized name
|
|
var t = Zotero.ItemTypes.getSecondaryTypes();
|
|
var itemTypes = [];
|
|
for (var i=0; i<t.length; i++) {
|
|
itemTypes.push({
|
|
id: t[i].id,
|
|
name: t[i].name,
|
|
localized: Zotero.ItemTypes.getLocalizedString(t[i].id)
|
|
});
|
|
}
|
|
var collation = Zotero.getLocaleCollation();
|
|
itemTypes.sort(function(a, b) {
|
|
return collation.compareString(1, a.localized, b.localized);
|
|
});
|
|
|
|
for (var i = 0; i<itemTypes.length; i++) {
|
|
var menuitem = document.createElement("menuitem");
|
|
menuitem.setAttribute("label", itemTypes[i].localized);
|
|
menuitem.setAttribute("tooltiptext", "");
|
|
let type = itemTypes[i].id;
|
|
menuitem.addEventListener("command", function() { ZoteroPane_Local.newItem(type, {}, null, true); }, false);
|
|
moreMenu.appendChild(menuitem);
|
|
}
|
|
}
|
|
|
|
|
|
this.updateNewItemTypes = function () {
|
|
var addMenu = document.getElementById('zotero-tb-add').firstChild;
|
|
|
|
// Remove all nodes so we can regenerate
|
|
var options = addMenu.getElementsByAttribute("class", "zotero-tb-add");
|
|
while (options.length) {
|
|
var p = options[0].parentNode;
|
|
p.removeChild(options[0]);
|
|
}
|
|
|
|
var separator = addMenu.firstChild;
|
|
|
|
// Sort by localized name
|
|
var t = Zotero.ItemTypes.getPrimaryTypes();
|
|
var itemTypes = [];
|
|
for (var i=0; i<t.length; i++) {
|
|
itemTypes.push({
|
|
id: t[i].id,
|
|
name: t[i].name,
|
|
localized: Zotero.ItemTypes.getLocalizedString(t[i].id)
|
|
});
|
|
}
|
|
var collation = Zotero.getLocaleCollation();
|
|
itemTypes.sort(function(a, b) {
|
|
return collation.compareString(1, a.localized, b.localized);
|
|
});
|
|
|
|
for (var i = 0; i<itemTypes.length; i++) {
|
|
var menuitem = document.createElement("menuitem");
|
|
menuitem.setAttribute("label", itemTypes[i].localized);
|
|
menuitem.setAttribute("tooltiptext", "");
|
|
let type = itemTypes[i].id;
|
|
menuitem.addEventListener("command", function() { ZoteroPane_Local.newItem(type, {}, null, true); }, false);
|
|
menuitem.className = "zotero-tb-add";
|
|
addMenu.insertBefore(menuitem, separator);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Called when the window closes
|
|
*/
|
|
function destroy()
|
|
{
|
|
if (!Zotero || !Zotero.initialized || !_loaded) {
|
|
return;
|
|
}
|
|
|
|
if(this.isShowing()) {
|
|
this.serializePersist();
|
|
}
|
|
|
|
var tagSelector = document.getElementById('zotero-tag-selector');
|
|
tagSelector.unregister();
|
|
|
|
if(this.collectionsView) this.collectionsView.unregister();
|
|
if(this.itemsView) this.itemsView.unregister();
|
|
|
|
observerService.removeObserver(_reloadObserver, "zotero-reloaded");
|
|
}
|
|
|
|
/**
|
|
* Called before Zotero pane is to be made visible
|
|
* @return {Boolean} True if Zotero pane should be loaded, false otherwise (if an error
|
|
* occurred)
|
|
*/
|
|
function makeVisible()
|
|
{
|
|
// If pane not loaded, load it or display an error message
|
|
if (!ZoteroPane_Local.loaded) {
|
|
if (Zotero.locked) {
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
var msg = Zotero.getString('general.operationInProgress') + '\n\n' + Zotero.getString('general.operationInProgress.waitUntilFinished');
|
|
ps.alert(null, "", msg);
|
|
return false;
|
|
}
|
|
ZoteroPane_Local.init();
|
|
}
|
|
|
|
// If Zotero could not be initialized, display an error message and return
|
|
if(!Zotero || !Zotero.initialized) {
|
|
this.displayStartupError();
|
|
return false;
|
|
}
|
|
|
|
if(!_madeVisible) {
|
|
this.buildItemTypeSubMenu();
|
|
}
|
|
_madeVisible = true;
|
|
|
|
this.unserializePersist();
|
|
this.updateToolbarPosition();
|
|
this.updateTagSelectorSize();
|
|
|
|
// restore saved row selection (for tab switching)
|
|
var containerWindow = (window.ZoteroTab ? window.ZoteroTab.containerWindow : window);
|
|
if(containerWindow.zoteroSavedCollectionSelection) {
|
|
this.collectionsView.rememberSelection(containerWindow.zoteroSavedCollectionSelection);
|
|
delete containerWindow.zoteroSavedCollectionSelection;
|
|
}
|
|
|
|
// restore saved item selection (for tab switching)
|
|
if(containerWindow.zoteroSavedItemSelection) {
|
|
var me = this;
|
|
// hack to restore saved selection after itemTreeView finishes loading
|
|
window.setTimeout(function() {
|
|
if(containerWindow.zoteroSavedItemSelection) {
|
|
me.itemsView.rememberSelection(containerWindow.zoteroSavedItemSelection);
|
|
delete containerWindow.zoteroSavedItemSelection;
|
|
}
|
|
}, 51);
|
|
}
|
|
|
|
// Focus the quicksearch on pane open
|
|
var searchBar = document.getElementById('zotero-tb-search');
|
|
setTimeout(function () {
|
|
searchBar.inputField.select();
|
|
}, 1);
|
|
|
|
var d = new Date();
|
|
Zotero.purgeDataObjects();
|
|
var d2 = new Date();
|
|
Zotero.debug("Purged data tables in " + (d2 - d) + " ms");
|
|
|
|
// Auto-sync on pane open
|
|
if (Zotero.Prefs.get('sync.autoSync')) {
|
|
setTimeout(function () {
|
|
if (!Zotero.Sync.Server.enabled) {
|
|
Zotero.debug('Sync not enabled -- skipping auto-sync', 4);
|
|
return;
|
|
}
|
|
|
|
if (Zotero.Sync.Server.syncInProgress || Zotero.Sync.Storage.syncInProgress) {
|
|
Zotero.debug('Sync already running -- skipping auto-sync', 4);
|
|
return;
|
|
}
|
|
|
|
if (Zotero.Sync.Server.manualSyncRequired) {
|
|
Zotero.debug('Manual sync required -- skipping auto-sync', 4);
|
|
return;
|
|
}
|
|
|
|
Zotero.Sync.Runner.sync(true);
|
|
}, 1000);
|
|
}
|
|
|
|
// Set sync icon to spinning or not
|
|
//
|
|
// We don't bother setting an error state at open
|
|
if (Zotero.Sync.Server.syncInProgress || Zotero.Sync.Storage.syncInProgress) {
|
|
Zotero.Sync.Runner.setSyncIcon('animate');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Function to be called before ZoteroPane_Local is hidden. Does not actually hide the Zotero pane.
|
|
*/
|
|
this.makeHidden = function() {
|
|
this.serializePersist();
|
|
}
|
|
|
|
function isShowing() {
|
|
var zoteroPane = document.getElementById('zotero-pane-stack');
|
|
return zoteroPane.getAttribute('hidden') != 'true' &&
|
|
zoteroPane.getAttribute('collapsed') != 'true';
|
|
}
|
|
|
|
function isFullScreen() {
|
|
return document.getElementById('zotero-pane-stack').getAttribute('fullscreenmode') == 'true';
|
|
}
|
|
|
|
|
|
/*
|
|
* Trigger actions based on keyboard shortcuts
|
|
*/
|
|
function handleKeyDown(event, from) {
|
|
try {
|
|
// Ignore keystrokes outside of Zotero pane
|
|
if (!(event.originalTarget.ownerDocument instanceof XULDocument)) {
|
|
return;
|
|
}
|
|
}
|
|
catch (e) {
|
|
Zotero.debug(e);
|
|
}
|
|
|
|
if (Zotero.locked) {
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
|
|
if (from == 'zotero-pane') {
|
|
// Highlight collections containing selected items
|
|
//
|
|
// We use Control (17) on Windows because Alt triggers the menubar;
|
|
// otherwise we use Alt/Option (18)
|
|
if ((Zotero.isWin && event.keyCode == 17 && !event.altKey) ||
|
|
(!Zotero.isWin && event.keyCode == 18 && !event.ctrlKey)
|
|
&& !event.shiftKey && !event.metaKey) {
|
|
|
|
this.highlightTimer = Components.classes["@mozilla.org/timer;1"].
|
|
createInstance(Components.interfaces.nsITimer);
|
|
// {} implements nsITimerCallback
|
|
this.highlightTimer.initWithCallback({
|
|
notify: ZoteroPane_Local.setHighlightedRowsCallback
|
|
}, 225, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
|
|
}
|
|
// Unhighlight on key up
|
|
else if ((Zotero.isWin && event.ctrlKey) ||
|
|
(!Zotero.isWin && event.altKey)) {
|
|
if (this.highlightTimer) {
|
|
this.highlightTimer.cancel();
|
|
this.highlightTimer = null;
|
|
}
|
|
ZoteroPane_Local.collectionsView.setHighlightedRows();
|
|
}
|
|
|
|
return;
|
|
}
|
|
else if (from == 'zotero-items-tree') {
|
|
// Focus TinyMCE explicitly on tab key, since the normal focusing
|
|
// doesn't work right
|
|
if (!event.shiftKey && event.keyCode == event.DOM_VK_TAB) {
|
|
var deck = document.getElementById('zotero-item-pane-content');
|
|
if (deck.selectedPanel.id == 'zotero-view-note') {
|
|
setTimeout(function () {
|
|
document.getElementById('zotero-note-editor').focus();
|
|
}, 0);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Ignore keystrokes if Zotero pane is closed
|
|
var zoteroPane = document.getElementById('zotero-pane-stack');
|
|
if (zoteroPane.getAttribute('hidden') == 'true' ||
|
|
zoteroPane.getAttribute('collapsed') == 'true') {
|
|
return;
|
|
}
|
|
|
|
var useShift = Zotero.isMac;
|
|
|
|
var key = String.fromCharCode(event.which);
|
|
if (!key) {
|
|
Zotero.debug('No key');
|
|
return;
|
|
}
|
|
|
|
// Ignore modifiers other than Ctrl-Alt or Cmd-Shift
|
|
if (!((Zotero.isMac ? event.metaKey : event.ctrlKey) &&
|
|
(useShift ? event.shiftKey : event.altKey))) {
|
|
return;
|
|
}
|
|
|
|
var command = Zotero.Keys.getCommand(key);
|
|
if (!command) {
|
|
return;
|
|
}
|
|
|
|
Zotero.debug(command);
|
|
|
|
// Errors don't seem to make it out otherwise
|
|
try {
|
|
|
|
switch (command) {
|
|
case 'openZotero':
|
|
try {
|
|
// Ignore Cmd-Shift-Z keystroke in text areas
|
|
if (Zotero.isMac && key == 'Z' &&
|
|
event.originalTarget.localName == 'textarea') {
|
|
Zotero.debug('Ignoring keystroke in text area');
|
|
return;
|
|
}
|
|
}
|
|
catch (e) {
|
|
Zotero.debug(e);
|
|
}
|
|
if(window.ZoteroOverlay) window.ZoteroOverlay.toggleDisplay()
|
|
break;
|
|
case 'library':
|
|
document.getElementById('zotero-collections-tree').focus();
|
|
break;
|
|
case 'quicksearch':
|
|
document.getElementById('zotero-tb-search').select();
|
|
break;
|
|
case 'newItem':
|
|
ZoteroPane_Local.newItem(2); // book
|
|
var menu = document.getElementById('zotero-editpane-item-box').itemTypeMenu;
|
|
menu.focus();
|
|
document.getElementById('zotero-editpane-item-box').itemTypeMenu.menupopup.openPopup(menu, "before_start", 0, 0);
|
|
break;
|
|
case 'newNote':
|
|
// If a regular item is selected, use that as the parent.
|
|
// If a child item is selected, use its parent as the parent.
|
|
// Otherwise create a standalone note.
|
|
var parent = false;
|
|
var items = ZoteroPane_Local.getSelectedItems();
|
|
if (items.length == 1) {
|
|
if (items[0].isRegularItem()) {
|
|
parent = items[0].id;
|
|
}
|
|
else {
|
|
parent = items[0].getSource();
|
|
}
|
|
}
|
|
// Use key that's not the modifier as the popup toggle
|
|
ZoteroPane_Local.newNote(
|
|
useShift ? event.altKey : event.shiftKey, parent
|
|
);
|
|
break;
|
|
case 'toggleTagSelector':
|
|
ZoteroPane_Local.toggleTagSelector();
|
|
break;
|
|
case 'toggleFullscreen':
|
|
ZoteroPane_Local.toggleTab();
|
|
break;
|
|
case 'copySelectedItemCitationsToClipboard':
|
|
ZoteroPane_Local.copySelectedItemsToClipboard(true)
|
|
break;
|
|
case 'copySelectedItemsToClipboard':
|
|
ZoteroPane_Local.copySelectedItemsToClipboard();
|
|
break;
|
|
case 'importFromClipboard':
|
|
Zotero_File_Interface.importFromClipboard();
|
|
break;
|
|
default:
|
|
throw ('Command "' + command + '" not found in ZoteroPane_Local.handleKeyDown()');
|
|
}
|
|
|
|
}
|
|
catch (e) {
|
|
Zotero.debug(e, 1);
|
|
Components.utils.reportError(e);
|
|
}
|
|
|
|
event.preventDefault();
|
|
}
|
|
|
|
|
|
function handleKeyUp(event, from) {
|
|
if (from == 'zotero-pane') {
|
|
if ((Zotero.isWin && event.keyCode == 17) ||
|
|
(!Zotero.isWin && event.keyCode == 18)) {
|
|
if (this.highlightTimer) {
|
|
this.highlightTimer.cancel();
|
|
this.highlightTimer = null;
|
|
}
|
|
ZoteroPane_Local.collectionsView.setHighlightedRows();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Highlights collections containing selected items on Ctrl (Win) or
|
|
* Option/Alt (Mac/Linux) press
|
|
*/
|
|
function setHighlightedRowsCallback() {
|
|
var itemIDs = ZoteroPane_Local.getSelectedItems(true);
|
|
if (itemIDs && itemIDs.length) {
|
|
var collectionIDs = Zotero.Collections.getCollectionsContainingItems(itemIDs, true);
|
|
if (collectionIDs) {
|
|
ZoteroPane_Local.collectionsView.setHighlightedRows(collectionIDs);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function handleKeyPress(event, from) {
|
|
if (from == 'zotero-collections-tree') {
|
|
if ((event.keyCode == event.DOM_VK_BACK_SPACE && Zotero.isMac) ||
|
|
event.keyCode == event.DOM_VK_DELETE) {
|
|
ZoteroPane_Local.deleteSelectedCollection();
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
}
|
|
else if (from == 'zotero-items-tree') {
|
|
if ((event.keyCode == event.DOM_VK_BACK_SPACE && Zotero.isMac) ||
|
|
event.keyCode == event.DOM_VK_DELETE) {
|
|
// If Cmd/Ctrl delete, use forced mode, which does different
|
|
// things depending on the context
|
|
var force = event.metaKey || (!Zotero.isMac && event.ctrlKey);
|
|
ZoteroPane_Local.deleteSelectedItems(force);
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
else if (event.keyCode == event.DOM_VK_RETURN) {
|
|
var items = this.itemsView.getSelectedItems();
|
|
// Don't do anything if more than 20 items selected
|
|
if (!items.length || items.length > 20) {
|
|
return;
|
|
}
|
|
ZoteroPane_Local.viewItems(items, event);
|
|
// These don't seem to do anything. Instead we override
|
|
// the tree binding's _handleEnter method in itemTreeView.js.
|
|
//event.preventDefault();
|
|
//event.stopPropagation();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new item
|
|
*
|
|
* _data_ is an optional object with field:value for itemData
|
|
*/
|
|
function newItem(typeID, data, row, manual)
|
|
{
|
|
if (!Zotero.stateCheck()) {
|
|
this.displayErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
if ((row === undefined || row === null) && this.collectionsView.selection) {
|
|
row = this.collectionsView.selection.currentIndex;
|
|
|
|
// Make sure currently selected view is editable
|
|
if (!this.canEdit(row)) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (row !== undefined && row !== null) {
|
|
var itemGroup = this.collectionsView._getItemAtRow(row);
|
|
var libraryID = itemGroup.ref.libraryID;
|
|
}
|
|
else {
|
|
var libraryID = null;
|
|
var itemGroup = null;
|
|
}
|
|
|
|
Zotero.DB.beginTransaction();
|
|
|
|
var item = new Zotero.Item(typeID);
|
|
item.libraryID = libraryID;
|
|
for (var i in data) {
|
|
item.setField(i, data[i]);
|
|
}
|
|
var itemID = item.save();
|
|
|
|
if (itemGroup && itemGroup.isCollection()) {
|
|
itemGroup.ref.addItem(itemID);
|
|
}
|
|
|
|
Zotero.DB.commitTransaction();
|
|
|
|
//set to Info tab
|
|
document.getElementById('zotero-view-item').selectedIndex = 0;
|
|
|
|
// Update most-recently-used list for New Item menu
|
|
if (manual) {
|
|
var mru = Zotero.Prefs.get('newItemTypeMRU');
|
|
if (mru) {
|
|
var mru = mru.split(',');
|
|
var pos = mru.indexOf(typeID + '');
|
|
if (pos != -1) {
|
|
mru.splice(pos, 1);
|
|
}
|
|
mru.unshift(typeID);
|
|
}
|
|
else {
|
|
var mru = [typeID + ''];
|
|
}
|
|
Zotero.Prefs.set('newItemTypeMRU', mru.slice(0, 5).join(','));
|
|
}
|
|
|
|
return Zotero.Items.get(itemID);
|
|
}
|
|
|
|
|
|
function newCollection(parent)
|
|
{
|
|
if (!Zotero.stateCheck()) {
|
|
this.displayErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
if (!this.canEditLibrary()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
|
|
var untitled = Zotero.DB.getNextName('collections', 'collectionName',
|
|
Zotero.getString('pane.collections.untitled'));
|
|
|
|
var newName = { value: untitled };
|
|
var result = promptService.prompt(window,
|
|
Zotero.getString('pane.collections.newCollection'),
|
|
Zotero.getString('pane.collections.name'), newName, "", {});
|
|
|
|
if (!result)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!newName.value)
|
|
{
|
|
newName.value = untitled;
|
|
}
|
|
|
|
var collection = new Zotero.Collection;
|
|
collection.libraryID = this.getSelectedLibraryID();
|
|
collection.name = newName.value;
|
|
collection.parent = parent;
|
|
collection.save();
|
|
}
|
|
|
|
|
|
this.newGroup = function () {
|
|
this.loadURI(Zotero.Groups.addGroupURL);
|
|
}
|
|
|
|
|
|
function newSearch()
|
|
{
|
|
if (!Zotero.stateCheck()) {
|
|
this.displayErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
var s = new Zotero.Search();
|
|
s.libraryID = this.getSelectedLibraryID();
|
|
s.addCondition('title', 'contains', '');
|
|
|
|
var untitled = Zotero.getString('pane.collections.untitled');
|
|
untitled = Zotero.DB.getNextName('savedSearches', 'savedSearchName',
|
|
Zotero.getString('pane.collections.untitled'));
|
|
var io = {dataIn: {search: s, name: untitled}, dataOut: null};
|
|
window.openDialog('chrome://zotero/content/searchDialog.xul','','chrome,modal',io);
|
|
}
|
|
|
|
|
|
this.setVirtual = function (libraryID, mode, show) {
|
|
switch (mode) {
|
|
case 'duplicates':
|
|
var prefKey = 'duplicateLibraries';
|
|
var lastViewedFolderID = 'D' + (libraryID ? libraryID : 0);
|
|
break;
|
|
|
|
case 'unfiled':
|
|
var prefKey = 'unfiledLibraries';
|
|
var lastViewedFolderID = 'U' + (libraryID ? libraryID : 0);
|
|
break;
|
|
|
|
default:
|
|
throw ("Invalid virtual mode '" + mode + "' in ZoteroPane.setVirtual()");
|
|
}
|
|
|
|
try {
|
|
var ids = Zotero.Prefs.get(prefKey).split(',');
|
|
}
|
|
catch (e) {
|
|
var ids = [];
|
|
}
|
|
|
|
if (!libraryID) {
|
|
libraryID = 0;
|
|
}
|
|
|
|
var newids = [];
|
|
for each(var id in ids) {
|
|
id = parseInt(id);
|
|
if (isNaN(id)) {
|
|
continue;
|
|
}
|
|
// Remove current library if hiding
|
|
if (id == libraryID && !show) {
|
|
continue;
|
|
}
|
|
// Remove libraryIDs that no longer exist
|
|
if (id != 0 && !Zotero.Libraries.exists(id)) {
|
|
continue;
|
|
}
|
|
newids.push(id);
|
|
}
|
|
|
|
// Add the current library if it's not already set
|
|
if (show && newids.indexOf(libraryID) == -1) {
|
|
newids.push(libraryID);
|
|
}
|
|
|
|
newids.sort();
|
|
|
|
Zotero.Prefs.set(prefKey, newids.join());
|
|
|
|
this.collectionsView.refresh();
|
|
|
|
// If group is closed, open it
|
|
this.collectionsView.selectLibrary(libraryID);
|
|
row = this.collectionsView.selection.currentIndex;
|
|
if (!this.collectionsView.isContainerOpen(row)) {
|
|
this.collectionsView.toggleOpenState(row);
|
|
}
|
|
|
|
// Select new row
|
|
if (show) {
|
|
Zotero.Prefs.set('lastViewedFolder', lastViewedFolderID);
|
|
var row = this.collectionsView.getLastViewedRow();
|
|
this.collectionsView.selection.select(row);
|
|
}
|
|
}
|
|
|
|
|
|
this.openLookupWindow = function () {
|
|
if (!Zotero.stateCheck()) {
|
|
this.displayErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
window.openDialog('chrome://zotero/content/lookup.xul', 'zotero-lookup', 'chrome,modal');
|
|
}
|
|
|
|
|
|
function openAdvancedSearchWindow() {
|
|
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
.getService(Components.interfaces.nsIWindowMediator);
|
|
var enumerator = wm.getEnumerator('zotero:search');
|
|
while (enumerator.hasMoreElements()) {
|
|
var win = enumerator.getNext();
|
|
}
|
|
|
|
if (win) {
|
|
win.focus();
|
|
return;
|
|
}
|
|
|
|
var s = new Zotero.Search();
|
|
s.addCondition('title', 'contains', '');
|
|
var io = {dataIn: {search: s}, dataOut: null};
|
|
window.openDialog('chrome://zotero/content/advancedSearch.xul', '', 'chrome,dialog=no,centerscreen', io);
|
|
}
|
|
|
|
|
|
function toggleTagSelector(){
|
|
var tagSelector = document.getElementById('zotero-tag-selector');
|
|
|
|
var showing = tagSelector.getAttribute('collapsed') == 'true';
|
|
tagSelector.setAttribute('collapsed', !showing);
|
|
this.updateTagSelectorSize();
|
|
|
|
// If showing, set scope to items in current view
|
|
// and focus filter textbox
|
|
if (showing) {
|
|
_setTagScope();
|
|
tagSelector.focusTextbox();
|
|
}
|
|
// If hiding, clear selection
|
|
else {
|
|
tagSelector.uninit();
|
|
}
|
|
}
|
|
|
|
|
|
function updateTagSelectorSize() {
|
|
//Zotero.debug('Updating tag selector size');
|
|
var zoteroPane = document.getElementById('zotero-pane-stack');
|
|
var splitter = document.getElementById('zotero-tags-splitter');
|
|
var tagSelector = document.getElementById('zotero-tag-selector');
|
|
|
|
// Nothing should be bigger than appcontent's height
|
|
var max = document.getElementById('appcontent').boxObject.height
|
|
- splitter.boxObject.height;
|
|
|
|
// Shrink tag selector to appcontent's height
|
|
var maxTS = max - COLLECTIONS_HEIGHT;
|
|
if (parseInt(tagSelector.getAttribute("height")) > maxTS) {
|
|
//Zotero.debug("Limiting tag selector height to appcontent");
|
|
tagSelector.setAttribute('height', maxTS);
|
|
}
|
|
|
|
var height = tagSelector.boxObject.height;
|
|
|
|
|
|
/*Zotero.debug("tagSelector.boxObject.height: " + tagSelector.boxObject.height);
|
|
Zotero.debug("tagSelector.getAttribute('height'): " + tagSelector.getAttribute('height'));
|
|
Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height);
|
|
Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));*/
|
|
|
|
|
|
// Don't let the Z-pane jump back down to its previous height
|
|
// (if shrinking or hiding the tag selector let it clear the min-height)
|
|
if (zoteroPane.getAttribute('height') < zoteroPane.boxObject.height) {
|
|
//Zotero.debug("Setting Zotero pane height attribute to " + zoteroPane.boxObject.height);
|
|
zoteroPane.setAttribute('height', zoteroPane.boxObject.height);
|
|
}
|
|
|
|
if (tagSelector.getAttribute('collapsed') == 'true') {
|
|
// 32px is the default Z pane min-height in overlay.css
|
|
height = 32;
|
|
}
|
|
else {
|
|
// tS.boxObject.height doesn't exist at startup, so get from attribute
|
|
if (!height) {
|
|
height = parseInt(tagSelector.getAttribute('height'));
|
|
}
|
|
// 121px seems to be enough room for the toolbar and collections
|
|
// tree at minimum height
|
|
height = height + COLLECTIONS_HEIGHT;
|
|
}
|
|
|
|
//Zotero.debug('Setting Zotero pane minheight to ' + height);
|
|
zoteroPane.setAttribute('minheight', height);
|
|
|
|
if (this.isShowing() && !this.isFullScreen()) {
|
|
zoteroPane.setAttribute('savedHeight', zoteroPane.boxObject.height);
|
|
}
|
|
|
|
// Fix bug whereby resizing the Z pane downward after resizing
|
|
// the tag selector up and then down sometimes caused the Z pane to
|
|
// stay at a fixed size and get pushed below the bottom
|
|
tagSelector.height++;
|
|
tagSelector.height--;
|
|
}
|
|
|
|
|
|
function getTagSelection(){
|
|
var tagSelector = document.getElementById('zotero-tag-selector');
|
|
return tagSelector.selection ? tagSelector.selection : {};
|
|
}
|
|
|
|
|
|
function clearTagSelection() {
|
|
if (!Zotero.Utilities.isEmpty(this.getTagSelection())) {
|
|
var tagSelector = document.getElementById('zotero-tag-selector');
|
|
tagSelector.clearAll();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Sets the tag filter on the items view
|
|
*/
|
|
function updateTagFilter(){
|
|
if (this.itemsView) {
|
|
this.itemsView.setFilter('tags', getTagSelection());
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the tags scope to the items in the current view
|
|
*
|
|
* Passed to the items tree to trigger on changes
|
|
*/
|
|
function _setTagScope() {
|
|
var itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex);
|
|
var tagSelector = document.getElementById('zotero-tag-selector');
|
|
if (!tagSelector.getAttribute('collapsed') ||
|
|
tagSelector.getAttribute('collapsed') == 'false') {
|
|
Zotero.debug('Updating tag selector with current tags');
|
|
if (itemGroup.editable) {
|
|
tagSelector.mode = 'edit';
|
|
}
|
|
else {
|
|
tagSelector.mode = 'view';
|
|
}
|
|
tagSelector.libraryID = itemGroup.ref.libraryID;
|
|
tagSelector.scope = itemGroup.getChildTags();
|
|
}
|
|
}
|
|
|
|
|
|
function onCollectionSelected()
|
|
{
|
|
if (this.itemsView)
|
|
{
|
|
this.itemsView.unregister();
|
|
if (this.itemsView.wrappedJSObject.listener) {
|
|
document.getElementById('zotero-items-tree').removeEventListener(
|
|
'keypress', this.itemsView.wrappedJSObject.listener, false
|
|
);
|
|
}
|
|
this.itemsView.wrappedJSObject.listener = null;
|
|
document.getElementById('zotero-items-tree').view = this.itemsView = null;
|
|
}
|
|
|
|
// Clear quick search and tag selector when switching views
|
|
document.getElementById('zotero-tb-search').value = "";
|
|
document.getElementById('zotero-tag-selector').clearAll();
|
|
|
|
if (this.collectionsView.selection.count != 1) {
|
|
document.getElementById('zotero-items-tree').view = this.itemsView = null;
|
|
return;
|
|
}
|
|
|
|
// this.collectionsView.selection.currentIndex != -1
|
|
|
|
var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
|
|
|
/*
|
|
if (itemgroup.isSeparator()) {
|
|
document.getElementById('zotero-items-tree').view = this.itemsView = null;
|
|
return;
|
|
}
|
|
*/
|
|
|
|
itemgroup.setSearch('');
|
|
itemgroup.setTags(getTagSelection());
|
|
|
|
// Enable or disable toolbar icons and menu options as necessary
|
|
const disableIfNoEdit = [
|
|
"cmd_zotero_newCollection",
|
|
"cmd_zotero_newSavedSearch",
|
|
"zotero-tb-add",
|
|
"cmd_zotero_newItemFromCurrentPage",
|
|
"zotero-tb-lookup",
|
|
"cmd_zotero_newStandaloneNote",
|
|
"zotero-tb-note-add",
|
|
"zotero-tb-attachment-add"
|
|
];
|
|
for(var i=0; i<disableIfNoEdit.length; i++) {
|
|
var el = document.getElementById(disableIfNoEdit[i]);
|
|
|
|
// If a trash is selected, new collection depends on the
|
|
// editability of the library
|
|
if (itemgroup.isTrash() &&
|
|
disableIfNoEdit[i] == 'cmd_zotero_newCollection') {
|
|
if (itemgroup.ref.libraryID) {
|
|
var overrideEditable =
|
|
Zotero.Libraries.isEditable(itemgroup.ref.libraryID);
|
|
}
|
|
else {
|
|
var overrideEditable = true;
|
|
}
|
|
}
|
|
else {
|
|
var overrideEditable = false;
|
|
}
|
|
|
|
if (itemgroup.editable || overrideEditable) {
|
|
if(el.hasAttribute("disabled")) el.removeAttribute("disabled");
|
|
} else {
|
|
el.setAttribute("disabled", "true");
|
|
}
|
|
}
|
|
|
|
try {
|
|
Zotero.UnresponsiveScriptIndicator.disable();
|
|
this.itemsView = new Zotero.ItemTreeView(itemgroup);
|
|
this.itemsView.addCallback(_setTagScope);
|
|
document.getElementById('zotero-items-tree').view = this.itemsView;
|
|
this.itemsView.selection.clearSelection();
|
|
}
|
|
finally {
|
|
Zotero.UnresponsiveScriptIndicator.enable();
|
|
}
|
|
|
|
Zotero.Prefs.set('lastViewedFolder', itemgroup.id);
|
|
}
|
|
|
|
|
|
this.getItemGroup = function () {
|
|
if (!this.collectionsView.selection) {
|
|
return false;
|
|
}
|
|
return this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
|
}
|
|
|
|
|
|
this.itemSelected = function (event) {
|
|
if (!Zotero.stateCheck()) {
|
|
this.displayErrorMessage();
|
|
return;
|
|
}
|
|
|
|
// DEBUG: Is this actually possible?
|
|
if (!this.itemsView) {
|
|
Components.utils.reportError("this.itemsView is not defined in ZoteroPane.itemSelected()");
|
|
}
|
|
|
|
// Display restore button if items selected in Trash
|
|
if (this.itemsView.selection.count) {
|
|
document.getElementById('zotero-item-restore-button').hidden
|
|
= !this.itemsView._itemGroup.isTrash()
|
|
|| _nonDeletedItemsSelected(this.itemsView);
|
|
}
|
|
else {
|
|
document.getElementById('zotero-item-restore-button').hidden = true;
|
|
}
|
|
|
|
var tabs = document.getElementById('zotero-view-tabbox');
|
|
|
|
// save note when switching from a note
|
|
if(document.getElementById('zotero-item-pane-content').selectedIndex == 2) {
|
|
document.getElementById('zotero-note-editor').save();
|
|
}
|
|
|
|
var itemGroup = this.getItemGroup();
|
|
|
|
// Single item selected
|
|
if (this.itemsView.selection.count == 1 && this.itemsView.selection.currentIndex != -1)
|
|
{
|
|
var item = this.itemsView.getSelectedItems()[0];
|
|
|
|
if (item.isNote()) {
|
|
var noteEditor = document.getElementById('zotero-note-editor');
|
|
noteEditor.mode = this.collectionsView.editable ? 'edit' : 'view';
|
|
|
|
var clearUndo = noteEditor.item ? noteEditor.item.id != item.id : false;
|
|
|
|
noteEditor.parent = null;
|
|
noteEditor.item = item;
|
|
|
|
// If loading new or different note, disable undo while we repopulate the text field
|
|
// so Undo doesn't end up clearing the field. This also ensures that Undo doesn't
|
|
// undo content from another note into the current one.
|
|
if (clearUndo) {
|
|
noteEditor.clearUndo();
|
|
}
|
|
|
|
var viewButton = document.getElementById('zotero-view-note-button');
|
|
if (this.collectionsView.editable) {
|
|
viewButton.hidden = false;
|
|
viewButton.setAttribute('noteID', item.id);
|
|
if (item.getSource()) {
|
|
viewButton.setAttribute('sourceID', item.getSource());
|
|
}
|
|
else {
|
|
viewButton.removeAttribute('sourceID');
|
|
}
|
|
}
|
|
else {
|
|
viewButton.hidden = true;
|
|
}
|
|
|
|
document.getElementById('zotero-item-pane-content').selectedIndex = 2;
|
|
}
|
|
|
|
else if (item.isAttachment()) {
|
|
var attachmentBox = document.getElementById('zotero-attachment-box');
|
|
attachmentBox.mode = this.collectionsView.editable ? 'edit' : 'view';
|
|
attachmentBox.item = item;
|
|
|
|
document.getElementById('zotero-item-pane-content').selectedIndex = 3;
|
|
}
|
|
|
|
// Regular item
|
|
else {
|
|
var isCommons = itemGroup.isBucket();
|
|
|
|
document.getElementById('zotero-item-pane-content').selectedIndex = 1;
|
|
var tabBox = document.getElementById('zotero-view-tabbox');
|
|
var pane = tabBox.selectedIndex;
|
|
tabBox.firstChild.hidden = isCommons;
|
|
|
|
var button = document.getElementById('zotero-item-show-original');
|
|
if (isCommons) {
|
|
button.hidden = false;
|
|
button.disabled = !this.getOriginalItem();
|
|
}
|
|
else {
|
|
button.hidden = true;
|
|
}
|
|
|
|
if (this.collectionsView.editable) {
|
|
ZoteroItemPane.viewItem(item, null, pane);
|
|
tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex;
|
|
}
|
|
else {
|
|
ZoteroItemPane.viewItem(item, 'view', pane);
|
|
tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex;
|
|
}
|
|
}
|
|
}
|
|
// Zero or multiple items selected
|
|
else {
|
|
var count = this.itemsView.selection.count;
|
|
|
|
// Display duplicates merge interface in item pane
|
|
if (itemGroup.isDuplicates()) {
|
|
if (!itemGroup.editable) {
|
|
if (count) {
|
|
// TODO: localize
|
|
var msg = "Library write access is required to merge items.";
|
|
}
|
|
else {
|
|
var msg = Zotero.getString('pane.item.selected.zero');
|
|
}
|
|
this.setItemPaneMessage(msg);
|
|
}
|
|
else if (count) {
|
|
document.getElementById('zotero-item-pane-content').selectedIndex = 4;
|
|
|
|
// Load duplicates UI code
|
|
if (typeof Zotero_Duplicates_Pane == 'undefined') {
|
|
Zotero.debug("Loading duplicatesMerge.js");
|
|
Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
|
|
.getService(Components.interfaces.mozIJSSubScriptLoader)
|
|
.loadSubScript("chrome://zotero/content/duplicatesMerge.js");
|
|
}
|
|
|
|
// On a Select All of more than a few items, display a row
|
|
// count instead of the usual item type mismatch error
|
|
var displayNumItemsOnTypeError = count > 5 && count == this.itemsView.rowCount;
|
|
|
|
// Initialize the merge pane with the selected items
|
|
Zotero_Duplicates_Pane.setItems(this.getSelectedItems(), displayNumItemsOnTypeError);
|
|
}
|
|
else {
|
|
var msg = Zotero.getString('pane.item.selectToMerge');
|
|
this.setItemPaneMessage(msg);
|
|
}
|
|
}
|
|
// Display label in the middle of the item pane
|
|
else {
|
|
if (count) {
|
|
var msg = Zotero.getString('pane.item.selected.multiple', count);
|
|
}
|
|
else {
|
|
var rowCount = this.itemsView.rowCount;
|
|
var str = 'pane.item.unselected.';
|
|
switch (rowCount){
|
|
case 0:
|
|
str += 'zero';
|
|
break;
|
|
case 1:
|
|
str += 'singular';
|
|
break;
|
|
default:
|
|
str += 'plural';
|
|
break;
|
|
}
|
|
var msg = Zotero.getString(str, [rowCount]);
|
|
}
|
|
|
|
this.setItemPaneMessage(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check if any selected items in the passed (trash) treeview are not deleted
|
|
*
|
|
* @param {nsITreeView}
|
|
* @return {Boolean}
|
|
*/
|
|
function _nonDeletedItemsSelected(itemsView) {
|
|
var start = {};
|
|
var end = {};
|
|
for (var i=0, len=itemsView.selection.getRangeCount(); i<len; i++) {
|
|
itemsView.selection.getRangeAt(i, start, end);
|
|
for (var j=start.value; j<=end.value; j++) {
|
|
if (!itemsView._getItemAtRow(j).ref.deleted) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
this.updateNoteButtonMenu = function () {
|
|
var items = ZoteroPane_Local.getSelectedItems();
|
|
var button = document.getElementById('zotero-tb-add-child-note');
|
|
button.disabled = !this.canEdit() ||
|
|
!(items.length == 1 && (items[0].isRegularItem() || !items[0].isTopLevelItem()));
|
|
}
|
|
|
|
|
|
this.updateAttachmentButtonMenu = function (popup) {
|
|
var items = ZoteroPane_Local.getSelectedItems();
|
|
|
|
var disabled = !this.canEdit() || !(items.length == 1 && items[0].isRegularItem());
|
|
|
|
if (disabled) {
|
|
for each(var node in popup.childNodes) {
|
|
node.disabled = true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
|
var canEditFiles = this.canEditFiles();
|
|
|
|
var prefix = "menuitem-iconic zotero-menuitem-attachments-";
|
|
|
|
for (var i=0; i<popup.childNodes.length; i++) {
|
|
var node = popup.childNodes[i];
|
|
|
|
switch (node.className) {
|
|
case prefix + 'link':
|
|
node.disabled = itemgroup.isWithinGroup();
|
|
break;
|
|
|
|
case prefix + 'snapshot':
|
|
case prefix + 'file':
|
|
node.disabled = !canEditFiles;
|
|
break;
|
|
|
|
case prefix + 'web-link':
|
|
node.disabled = false;
|
|
break;
|
|
|
|
default:
|
|
throw ("Invalid class name '" + node.className + "' in ZoteroPane_Local.updateAttachmentButtonMenu()");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
this.checkPDFConverter = function () {
|
|
if (Zotero.Fulltext.pdfConverterIsRegistered()) {
|
|
return true;
|
|
}
|
|
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
|
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
|
var index = ps.confirmEx(
|
|
null,
|
|
// TODO: localize
|
|
"PDF Tools Not Installed",
|
|
"To use this feature, you must first install the PDF tools in "
|
|
+ "the Zotero preferences.",
|
|
buttonFlags,
|
|
"Open Preferences",
|
|
null, null, null, {}
|
|
);
|
|
if (index == 0) {
|
|
ZoteroPane_Local.openPreferences('zotero-prefpane-search', 'pdftools-install');
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
function reindexItem() {
|
|
var items = this.getSelectedItems();
|
|
if (!items) {
|
|
return;
|
|
}
|
|
|
|
var itemIDs = [];
|
|
var checkPDF = false;
|
|
for (var i=0; i<items.length; i++) {
|
|
// If any PDFs, we need to make sure the converter is installed and
|
|
// prompt for installation if not
|
|
if (!checkPDF && items[i].attachmentMIMEType && items[i].attachmentMIMEType == "application/pdf") {
|
|
checkPDF = true;
|
|
}
|
|
itemIDs.push(items[i].id);
|
|
}
|
|
|
|
if (checkPDF) {
|
|
var installed = this.checkPDFConverter();
|
|
if (!installed) {
|
|
document.getElementById('zotero-attachment-box').updateItemIndexedState();
|
|
return;
|
|
}
|
|
}
|
|
|
|
Zotero.Fulltext.indexItems(itemIDs, true);
|
|
document.getElementById('zotero-attachment-box').updateItemIndexedState();
|
|
}
|
|
|
|
|
|
function duplicateSelectedItem() {
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
var item = this.getSelectedItems()[0];
|
|
|
|
Zotero.DB.beginTransaction();
|
|
|
|
// Create new unsaved clone item in target library
|
|
var newItem = new Zotero.Item(item.itemTypeID);
|
|
newItem.libraryID = item.libraryID;
|
|
// DEBUG: save here because clone() doesn't currently work on unsaved tagged items
|
|
var id = newItem.save();
|
|
|
|
var newItem = Zotero.Items.get(id);
|
|
item.clone(false, newItem, false, !Zotero.Prefs.get('groups.copyTags'));
|
|
newItem.save();
|
|
|
|
if (this.itemsView._itemGroup.isCollection() && !newItem.getSource()) {
|
|
this.itemsView._itemGroup.ref.addItem(newItem.id);
|
|
}
|
|
|
|
Zotero.DB.commitTransaction();
|
|
|
|
this.selectItem(newItem.id);
|
|
}
|
|
|
|
|
|
this.deleteSelectedItem = function () {
|
|
Zotero.debug("ZoteroPane_Local.deleteSelectedItem() is deprecated -- use ZoteroPane_Local.deleteSelectedItems()");
|
|
this.deleteSelectedItems();
|
|
}
|
|
|
|
/*
|
|
* Remove, trash, or delete item(s), depending on context
|
|
*
|
|
* @param {Boolean} [force=false] Trash or delete even if in a collection or search,
|
|
* or trash without prompt in library
|
|
* @param {Boolean} [fromMenu=false] If triggered from context menu, which always prompts for deletes
|
|
*/
|
|
this.deleteSelectedItems = function (force, fromMenu) {
|
|
if (!this.itemsView || !this.itemsView.selection.count) {
|
|
return;
|
|
}
|
|
var itemGroup = this.itemsView._itemGroup;
|
|
|
|
if (!itemGroup.isTrash() && !itemGroup.isBucket() && !this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
var toTrash = {
|
|
title: Zotero.getString('pane.items.trash.title'),
|
|
text: Zotero.getString(
|
|
'pane.items.trash' + (this.itemsView.selection.count > 1 ? '.multiple' : '')
|
|
)
|
|
};
|
|
var toDelete = {
|
|
title: Zotero.getString('pane.items.delete.title'),
|
|
text: Zotero.getString(
|
|
'pane.items.delete' + (this.itemsView.selection.count > 1 ? '.multiple' : '')
|
|
)
|
|
};
|
|
|
|
if (itemGroup.isLibrary(true)) {
|
|
// In library, don't prompt if meta key was pressed
|
|
var prompt = (force && !fromMenu) ? false : toTrash;
|
|
}
|
|
else if (itemGroup.isCollection()) {
|
|
// In collection, only prompt if trashing
|
|
var prompt = force ? toTrash : false;
|
|
}
|
|
else if (itemGroup.isSearch() || itemGroup.isUnfiled() || itemGroup.isDuplicates()) {
|
|
if (!force) {
|
|
return;
|
|
}
|
|
var prompt = toTrash;
|
|
}
|
|
// Do nothing in trash view if any non-deleted items are selected
|
|
else if (itemGroup.isTrash()) {
|
|
var start = {};
|
|
var end = {};
|
|
for (var i=0, len=this.itemsView.selection.getRangeCount(); i<len; i++) {
|
|
this.itemsView.selection.getRangeAt(i, start, end);
|
|
for (var j=start.value; j<=end.value; j++) {
|
|
if (!this.itemsView._getItemAtRow(j).ref.deleted) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
var prompt = toDelete;
|
|
}
|
|
else if (itemGroup.isBucket()) {
|
|
var prompt = toDelete;
|
|
}
|
|
// Do nothing in share views
|
|
else if (itemGroup.isShare()) {
|
|
return;
|
|
}
|
|
|
|
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
if (!prompt || promptService.confirm(window, prompt.title, prompt.text)) {
|
|
this.itemsView.deleteSelection(force);
|
|
}
|
|
}
|
|
|
|
|
|
this.mergeSelectedItems = function () {
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
document.getElementById('zotero-item-pane-content').selectedIndex = 4;
|
|
|
|
if (typeof Zotero_Duplicates_Pane == 'undefined') {
|
|
Zotero.debug("Loading duplicatesMerge.js");
|
|
Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
|
|
.getService(Components.interfaces.mozIJSSubScriptLoader)
|
|
.loadSubScript("chrome://zotero/content/duplicatesMerge.js");
|
|
}
|
|
|
|
// Initialize the merge pane with the selected items
|
|
Zotero_Duplicates_Pane.setItems(this.getSelectedItems());
|
|
}
|
|
|
|
|
|
this.deleteSelectedCollection = function () {
|
|
var itemGroup = this.getItemGroup();
|
|
|
|
// Remove virtual duplicates collection
|
|
if (itemGroup.isDuplicates()) {
|
|
this.setVirtual(itemGroup.ref.libraryID, 'duplicates', false);
|
|
return;
|
|
}
|
|
// Remove virtual unfiled collection
|
|
else if (itemGroup.isUnfiled()) {
|
|
this.setVirtual(itemGroup.ref.libraryID, 'unfiled', false);
|
|
return;
|
|
}
|
|
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
if (this.collectionsView.selection.count == 1) {
|
|
if (itemGroup.isCollection())
|
|
{
|
|
if (confirm(Zotero.getString('pane.collections.delete')))
|
|
{
|
|
this.collectionsView.deleteSelection();
|
|
}
|
|
}
|
|
else if (itemGroup.isSearch())
|
|
{
|
|
if (confirm(Zotero.getString('pane.collections.deleteSearch')))
|
|
{
|
|
this.collectionsView.deleteSelection();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Currently used only for Commons to find original linked item
|
|
this.getOriginalItem = function () {
|
|
var item = this.getSelectedItems()[0];
|
|
var itemGroup = this.getItemGroup();
|
|
// TEMP: Commons buckets only
|
|
return itemGroup.ref.getLocalItem(item);
|
|
}
|
|
|
|
|
|
this.showOriginalItem = function () {
|
|
var item = this.getOriginalItem();
|
|
if (!item) {
|
|
Zotero.debug("Original item not found");
|
|
return;
|
|
}
|
|
this.selectItem(item.id);
|
|
}
|
|
|
|
|
|
this.restoreSelectedItems = function () {
|
|
var items = this.getSelectedItems();
|
|
if (!items) {
|
|
return;
|
|
}
|
|
|
|
Zotero.DB.beginTransaction();
|
|
for (var i=0; i<items.length; i++) {
|
|
items[i].deleted = false;
|
|
items[i].save();
|
|
}
|
|
Zotero.DB.commitTransaction();
|
|
}
|
|
|
|
|
|
this.emptyTrash = function () {
|
|
var libraryID = this.getSelectedLibraryID();
|
|
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
|
|
var result = ps.confirm(
|
|
null,
|
|
"",
|
|
Zotero.getString('pane.collections.emptyTrash') + "\n\n"
|
|
+ Zotero.getString('general.actionCannotBeUndone')
|
|
);
|
|
if (result) {
|
|
Zotero.Items.emptyTrash(libraryID);
|
|
Zotero.purgeDataObjects(true);
|
|
}
|
|
}
|
|
|
|
|
|
function editSelectedCollection()
|
|
{
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
if (this.collectionsView.selection.count > 0) {
|
|
var row = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
|
|
|
if (row.isCollection()) {
|
|
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
|
|
var newName = { value: row.getName() };
|
|
var result = promptService.prompt(window, "",
|
|
Zotero.getString('pane.collections.rename'), newName, "", {});
|
|
|
|
if (result && newName.value) {
|
|
row.ref.name = newName.value;
|
|
row.ref.save();
|
|
}
|
|
}
|
|
else {
|
|
var s = new Zotero.Search();
|
|
s.id = row.ref.id;
|
|
var io = {dataIn: {search: s, name: row.getName()}, dataOut: null};
|
|
window.openDialog('chrome://zotero/content/searchDialog.xul','','chrome,modal',io);
|
|
if (io.dataOut) {
|
|
this.onCollectionSelected(); //reload itemsView
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function copySelectedItemsToClipboard(asCitations) {
|
|
var items = this.getSelectedItems();
|
|
if (!items.length) {
|
|
return;
|
|
}
|
|
|
|
// Make sure at least one item is a regular item
|
|
//
|
|
// DEBUG: We could copy notes via keyboard shortcut if we altered
|
|
// Z_F_I.copyItemsToClipboard() to use Z.QuickCopy.getContentFromItems(),
|
|
// but 1) we'd need to override that function's drag limit and 2) when I
|
|
// tried it the OS X clipboard seemed to be getting text vs. HTML wrong,
|
|
// automatically converting text/html to plaintext rather than using
|
|
// text/unicode. (That may be fixable, however.)
|
|
var canCopy = false;
|
|
for each(var item in items) {
|
|
if (item.isRegularItem()) {
|
|
canCopy = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!canCopy) {
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
ps.alert(null, "", Zotero.getString("fileInterface.noReferencesError"));
|
|
return;
|
|
}
|
|
|
|
var url = (window.content && window.content.location ? window.content.location.href : null);
|
|
var [mode, format] = Zotero.QuickCopy.getFormatFromURL(url).split('=');
|
|
var [mode, contentType] = mode.split('/');
|
|
|
|
if (mode == 'bibliography') {
|
|
if (asCitations) {
|
|
Zotero_File_Interface.copyCitationToClipboard(items, format, contentType == 'html');
|
|
}
|
|
else {
|
|
Zotero_File_Interface.copyItemsToClipboard(items, format, contentType == 'html');
|
|
}
|
|
}
|
|
else if (mode == 'export') {
|
|
// Copy citations doesn't work in export mode
|
|
if (asCitations) {
|
|
return;
|
|
}
|
|
else {
|
|
Zotero_File_Interface.exportItemsToClipboard(items, format);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function clearQuicksearch() {
|
|
var search = document.getElementById('zotero-tb-search');
|
|
if (search.value != '') {
|
|
search.value = '';
|
|
ZoteroPane_Local.search();
|
|
}
|
|
}
|
|
|
|
|
|
function handleSearchKeypress(textbox, event) {
|
|
// Events that turn find-as-you-type on
|
|
if (event.keyCode == event.DOM_VK_ESCAPE) {
|
|
textbox.value = '';
|
|
ZoteroPane_Local.setItemsPaneMessage(Zotero.getString('searchInProgress'));
|
|
setTimeout(function () {
|
|
ZoteroPane_Local.search();
|
|
ZoteroPane_Local.clearItemsPaneMessage();
|
|
}, 1);
|
|
}
|
|
else if (event.keyCode == event.DOM_VK_RETURN || event.keyCode == event.DOM_VK_ENTER) {
|
|
ZoteroPane_Local.setItemsPaneMessage(Zotero.getString('searchInProgress'));
|
|
setTimeout(function () {
|
|
ZoteroPane_Local.search(true);
|
|
ZoteroPane_Local.clearItemsPaneMessage();
|
|
}, 1);
|
|
}
|
|
}
|
|
|
|
|
|
function handleSearchInput(textbox, event) {
|
|
// This is the new length, except, it seems, when the change is a
|
|
// result of Undo or Redo
|
|
if (!textbox.value.length) {
|
|
ZoteroPane_Local.setItemsPaneMessage(Zotero.getString('searchInProgress'));
|
|
setTimeout(function () {
|
|
ZoteroPane_Local.search();
|
|
ZoteroPane_Local.clearItemsPaneMessage();
|
|
}, 1);
|
|
}
|
|
else if (textbox.value.indexOf('"') != -1) {
|
|
ZoteroPane_Local.setItemsPaneMessage(Zotero.getString('advancedSearchMode'));
|
|
}
|
|
}
|
|
|
|
|
|
function search(runAdvanced)
|
|
{
|
|
if (this.itemsView) {
|
|
var search = document.getElementById('zotero-tb-search');
|
|
if (!runAdvanced && search.value.indexOf('"') != -1) {
|
|
return;
|
|
}
|
|
var searchVal = search.value;
|
|
this.itemsView.setFilter('search', searchVal);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Select item in current collection or, if not there, in Library
|
|
*
|
|
* If _inLibrary_, force switch to Library
|
|
* If _expand_, open item if it's a container
|
|
*/
|
|
function selectItem(itemID, inLibrary, expand)
|
|
{
|
|
if (!itemID) {
|
|
return false;
|
|
}
|
|
|
|
var item = Zotero.Items.get(itemID);
|
|
if (!item) {
|
|
return false;
|
|
}
|
|
|
|
if (!this.itemsView) {
|
|
Components.utils.reportError("Items view not set in ZoteroPane_Local.selectItem()");
|
|
return false;
|
|
}
|
|
|
|
var currentLibraryID = this.getSelectedLibraryID();
|
|
// If in a different library
|
|
if (item.libraryID != currentLibraryID) {
|
|
Zotero.debug("Library ID differs; switching library");
|
|
this.collectionsView.selectLibrary(item.libraryID);
|
|
}
|
|
// Force switch to library view
|
|
else if (!this.itemsView._itemGroup.isLibrary() && inLibrary) {
|
|
Zotero.debug("Told to select in library; switching to library");
|
|
this.collectionsView.selectLibrary(item.libraryID);
|
|
}
|
|
|
|
var selected = this.itemsView.selectItem(itemID, expand);
|
|
if (!selected) {
|
|
Zotero.debug("Item was not selected; switching to library");
|
|
this.collectionsView.selectLibrary(item.libraryID);
|
|
this.itemsView.selectItem(itemID, expand);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
this.getSelectedLibraryID = function () {
|
|
return this.collectionsView.getSelectedLibraryID();
|
|
}
|
|
|
|
|
|
function getSelectedCollection(asID) {
|
|
return this.collectionsView ? this.collectionsView.getSelectedCollection(asID) : false;
|
|
}
|
|
|
|
|
|
function getSelectedSavedSearch(asID)
|
|
{
|
|
if (this.collectionsView.selection.count > 0 && this.collectionsView.selection.currentIndex != -1) {
|
|
var collection = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
|
if (collection && collection.isSearch()) {
|
|
return asID ? collection.ref.id : collection.ref;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return an array of Item objects for selected items
|
|
*
|
|
* If asIDs is true, return an array of itemIDs instead
|
|
*/
|
|
function getSelectedItems(asIDs)
|
|
{
|
|
if (!this.itemsView) {
|
|
return [];
|
|
}
|
|
|
|
return this.itemsView.getSelectedItems(asIDs);
|
|
}
|
|
|
|
|
|
this.getSelectedGroup = function (asID) {
|
|
if (this.collectionsView.selection
|
|
&& this.collectionsView.selection.count > 0
|
|
&& this.collectionsView.selection.currentIndex != -1) {
|
|
|
|
var itemGroup = this.getItemGroup();
|
|
if (itemGroup && itemGroup.isGroup()) {
|
|
return asID ? itemGroup.ref.id : itemGroup.ref;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns an array of Zotero.Item objects of visible items in current sort order
|
|
*
|
|
* If asIDs is true, return an array of itemIDs instead
|
|
*/
|
|
function getSortedItems(asIDs) {
|
|
if (!this.itemsView) {
|
|
return [];
|
|
}
|
|
|
|
return this.itemsView.getSortedItems(asIDs);
|
|
}
|
|
|
|
|
|
function getSortField() {
|
|
if (!this.itemsView) {
|
|
return false;
|
|
}
|
|
|
|
return this.itemsView.getSortField();
|
|
}
|
|
|
|
|
|
function getSortDirection() {
|
|
if (!this.itemsView) {
|
|
return false;
|
|
}
|
|
|
|
return this.itemsView.getSortDirection();
|
|
}
|
|
|
|
|
|
this.buildCollectionContextMenu = function buildCollectionContextMenu()
|
|
{
|
|
var options = [
|
|
"newCollection",
|
|
"newSavedSearch",
|
|
"newSubcollection",
|
|
"sep1",
|
|
"showDuplicates",
|
|
"showUnfiled",
|
|
"editSelectedCollection",
|
|
"removeCollection",
|
|
"sep2",
|
|
"exportCollection",
|
|
"createBibCollection",
|
|
"exportFile",
|
|
"loadReport",
|
|
"emptyTrash",
|
|
"createCommonsBucket",
|
|
"refreshCommonsBucket"
|
|
];
|
|
|
|
var m = {};
|
|
var i = 0;
|
|
for each(var option in options) {
|
|
m[option] = i++;
|
|
}
|
|
|
|
var menu = document.getElementById('zotero-collectionmenu');
|
|
|
|
var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
|
|
|
// By default things are hidden and visible, so we only need to record
|
|
// when things are visible and when they're visible but disabled
|
|
var show = [], disable = [];
|
|
|
|
if (itemGroup.isCollection()) {
|
|
show = [
|
|
m.newSubcollection,
|
|
m.sep1,
|
|
m.editSelectedCollection,
|
|
m.removeCollection,
|
|
m.sep2,
|
|
m.exportCollection,
|
|
m.createBibCollection,
|
|
m.loadReport
|
|
];
|
|
var s = [m.exportCollection, m.createBibCollection, m.loadReport];
|
|
if (!this.itemsView.rowCount) {
|
|
if (!this.collectionsView.isContainerEmpty(this.collectionsView.selection.currentIndex)) {
|
|
disable = [m.createBibCollection, m.loadReport];
|
|
}
|
|
else {
|
|
disable = s;
|
|
}
|
|
}
|
|
|
|
// Adjust labels
|
|
menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.rename.collection'));
|
|
menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.collection'));
|
|
menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.collection'));
|
|
menu.childNodes[m.createBibCollection].setAttribute('label', Zotero.getString('pane.collections.menu.createBib.collection'));
|
|
menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.collection'));
|
|
}
|
|
else if (itemGroup.isSearch()) {
|
|
show = [
|
|
m.editSelectedCollection,
|
|
m.removeCollection,
|
|
m.sep2,
|
|
m.exportCollection,
|
|
m.createBibCollection,
|
|
m.loadReport
|
|
];
|
|
|
|
menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.savedSearch'));
|
|
|
|
var s = [m.exportCollection, m.createBibCollection, m.loadReport];
|
|
if (!this.itemsView.rowCount) {
|
|
disable = s;
|
|
}
|
|
|
|
// Adjust labels
|
|
menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.edit.savedSearch'));
|
|
menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.savedSearch'));
|
|
menu.childNodes[m.createBibCollection].setAttribute('label', Zotero.getString('pane.collections.menu.createBib.savedSearch'));
|
|
menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.savedSearch'));
|
|
}
|
|
else if (itemGroup.isTrash()) {
|
|
show = [m.emptyTrash];
|
|
}
|
|
else if (itemGroup.isGroup()) {
|
|
show = [m.newCollection, m.newSavedSearch, m.sep1, m.showDuplicates, m.showUnfiled];
|
|
}
|
|
else if (itemGroup.isDuplicates() || itemGroup.isUnfiled()) {
|
|
show = [
|
|
m.removeCollection
|
|
];
|
|
|
|
menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('general.remove'));
|
|
}
|
|
else if (itemGroup.isHeader()) {
|
|
if (itemGroup.ref.id == 'commons-header') {
|
|
show = [m.createCommonsBucket];
|
|
}
|
|
}
|
|
else if (itemGroup.isBucket()) {
|
|
show = [m.refreshCommonsBucket];
|
|
}
|
|
// Library
|
|
else
|
|
{
|
|
show = [m.newCollection, m.newSavedSearch, m.sep1, m.showDuplicates, m.showUnfiled, m.sep2, m.exportFile];
|
|
}
|
|
|
|
// Disable some actions if user doesn't have write access
|
|
//
|
|
// Some actions are disabled via their commands in onCollectionSelected()
|
|
var s = [m.newSubcollection, m.editSelectedCollection, m.removeCollection];
|
|
if (itemGroup.isWithinGroup() && !itemGroup.editable && !itemGroup.isDuplicates() && !itemGroup.isUnfiled()) {
|
|
disable = disable.concat(s);
|
|
}
|
|
|
|
// If within non-editable group or trash it empty, disable Empty Trash
|
|
if (itemGroup.isTrash()) {
|
|
if ((itemGroup.isWithinGroup() && !itemGroup.isWithinEditableGroup()) || !this.itemsView.rowCount) {
|
|
disable.push(m.emptyTrash);
|
|
}
|
|
}
|
|
|
|
// Hide and enable all actions by default (so if they're shown they're enabled)
|
|
for each(var pos in m) {
|
|
menu.childNodes[pos].setAttribute('hidden', true);
|
|
menu.childNodes[pos].setAttribute('disabled', false);
|
|
}
|
|
|
|
for (var i in show)
|
|
{
|
|
menu.childNodes[show[i]].setAttribute('hidden', false);
|
|
}
|
|
|
|
for (var i in disable)
|
|
{
|
|
menu.childNodes[disable[i]].setAttribute('disabled', true);
|
|
}
|
|
}
|
|
|
|
function buildItemContextMenu()
|
|
{
|
|
var options = [
|
|
'showInLibrary',
|
|
'sep1',
|
|
'addNote',
|
|
'addAttachments',
|
|
'sep2',
|
|
'duplicateItem',
|
|
'deleteItem',
|
|
'deleteFromLibrary',
|
|
'restoreToLibrary',
|
|
'mergeItems',
|
|
'sep3',
|
|
'exportItems',
|
|
'createBib',
|
|
'loadReport',
|
|
'sep4',
|
|
'recognizePDF',
|
|
'createParent',
|
|
'renameAttachments',
|
|
'reindexItem'
|
|
];
|
|
|
|
var m = {};
|
|
var i = 0;
|
|
for each(var option in options) {
|
|
m[option] = i++;
|
|
}
|
|
|
|
var menu = document.getElementById('zotero-itemmenu');
|
|
|
|
// remove old locate menu items
|
|
while(menu.firstChild && menu.firstChild.getAttribute("zotero-locate")) {
|
|
menu.removeChild(menu.firstChild);
|
|
}
|
|
|
|
var disable = [], show = [], multiple = '';
|
|
|
|
if (!this.itemsView) {
|
|
return;
|
|
}
|
|
|
|
var itemGroup = this.getItemGroup();
|
|
|
|
if(itemGroup.isTrash()) {
|
|
show.push(m.restoreToLibrary);
|
|
} else {
|
|
show.push(m.deleteFromLibrary);
|
|
}
|
|
|
|
show.push(m.sep3, m.exportItems, m.createBib, m.loadReport);
|
|
|
|
if (this.itemsView.selection.count > 0) {
|
|
// Multiple items selected
|
|
if (this.itemsView.selection.count > 1) {
|
|
var multiple = '.multiple';
|
|
|
|
var items = this.getSelectedItems();
|
|
var canMerge = true, canIndex = true, canRecognize = true, canRename = true;
|
|
|
|
if (!Zotero.Fulltext.pdfConverterIsRegistered()) {
|
|
canIndex = false;
|
|
}
|
|
|
|
for each(var item in items) {
|
|
if (canMerge && !item.isRegularItem() || itemGroup.isDuplicates()) {
|
|
canMerge = false;
|
|
}
|
|
|
|
if (canIndex && !Zotero.Fulltext.canReindex(item.id)) {
|
|
canIndex = false;
|
|
}
|
|
|
|
if (canRecognize && !Zotero_RecognizePDF.canRecognize(item)) {
|
|
canRecognize = false;
|
|
}
|
|
|
|
// Show rename option only if all items are child attachments
|
|
if (canRename && (!item.isAttachment() || !item.getSource() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL)) {
|
|
canRename = false;
|
|
}
|
|
}
|
|
|
|
if (canMerge) {
|
|
show.push(m.mergeItems);
|
|
}
|
|
|
|
if (canIndex) {
|
|
show.push(m.reindexItem);
|
|
}
|
|
|
|
if (canRecognize) {
|
|
show.push(m.recognizePDF);
|
|
}
|
|
|
|
var canCreateParent = true;
|
|
for each(var item in items) {
|
|
if (!item.isTopLevelItem() || !item.isAttachment()) {
|
|
canCreateParent = false;
|
|
break;
|
|
}
|
|
}
|
|
if (canCreateParent) {
|
|
show.push(m.createParent);
|
|
}
|
|
|
|
if (canRename) {
|
|
show.push(m.renameAttachments);
|
|
}
|
|
|
|
// Add in attachment separator
|
|
if (canCreateParent || canRecognize || canRename || canIndex) {
|
|
show.push(m.sep4);
|
|
}
|
|
|
|
// Block certain actions on files if no access and at least one item
|
|
// is an imported attachment
|
|
if (!itemGroup.filesEditable) {
|
|
var hasImportedAttachment = false;
|
|
for (var i=0; i<items.length; i++) {
|
|
var item = items[i];
|
|
if (item.isImportedAttachment()) {
|
|
hasImportedAttachment = true;
|
|
break;
|
|
}
|
|
}
|
|
if (hasImportedAttachment) {
|
|
disable.push(m.deleteFromLibrary, m.createParent, m.renameAttachments);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Single item selected
|
|
else
|
|
{
|
|
var item = this.getSelectedItems()[0];
|
|
var itemID = item.id;
|
|
menu.setAttribute('itemID', itemID);
|
|
|
|
// Show in Library
|
|
if (!itemGroup.isLibrary() && !itemGroup.isWithinGroup()) {
|
|
show.push(m.showInLibrary, m.sep1);
|
|
}
|
|
|
|
// Disable actions in the trash
|
|
if (itemGroup.isTrash()) {
|
|
disable.push(m.deleteItem);
|
|
}
|
|
|
|
if (item.isRegularItem()) {
|
|
show.push(m.addNote, m.addAttachments, m.sep2);
|
|
}
|
|
|
|
if (item.isAttachment()) {
|
|
var showSep4 = false;
|
|
|
|
if (Zotero_RecognizePDF.canRecognize(item)) {
|
|
show.push(m.recognizePDF);
|
|
showSep4 = true;
|
|
}
|
|
|
|
// Allow parent item creation for standalone attachments
|
|
if (item.isTopLevelItem()) {
|
|
show.push(m.createParent);
|
|
showSep4 = true;
|
|
}
|
|
|
|
// Attachment rename option
|
|
if (item.getSource() && item.attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
|
show.push(m.renameAttachments);
|
|
showSep4 = true;
|
|
}
|
|
|
|
// If not linked URL, show reindex line
|
|
if (Zotero.Fulltext.pdfConverterIsRegistered()
|
|
&& Zotero.Fulltext.canReindex(item.id)) {
|
|
show.push(m.reindexItem);
|
|
showSep4 = true;
|
|
}
|
|
|
|
if (showSep4) {
|
|
show.push(m.sep4);
|
|
}
|
|
}
|
|
else {
|
|
show.push(m.duplicateItem);
|
|
}
|
|
|
|
// Update attachment submenu
|
|
var popup = document.getElementById('zotero-add-attachment-popup')
|
|
this.updateAttachmentButtonMenu(popup);
|
|
|
|
// Block certain actions on files if no access
|
|
if (item.isImportedAttachment() && !itemGroup.filesEditable) {
|
|
var d = [m.deleteFromLibrary, m.createParent, m.renameAttachments];
|
|
for each(var val in d) {
|
|
disable.push(val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// No items selected
|
|
else
|
|
{
|
|
// Show in Library
|
|
if (!itemGroup.isLibrary()) {
|
|
show.push(m.showInLibrary, m.sep1);
|
|
}
|
|
|
|
disable.push(m.showInLibrary, m.duplicateItem, m.deleteItem,
|
|
m.deleteFromLibrary, m.exportItems, m.createBib, m.loadReport);
|
|
}
|
|
|
|
// TODO: implement menu for remote items
|
|
if (!itemGroup.editable) {
|
|
for (var i in m) {
|
|
// Still show export/bib/report for non-editable views
|
|
// (other than Commons buckets, which aren't real items)
|
|
if (!itemGroup.isBucket()) {
|
|
switch (i) {
|
|
case 'exportItems':
|
|
case 'createBib':
|
|
case 'loadReport':
|
|
case 'restoreToLibrary':
|
|
continue;
|
|
}
|
|
}
|
|
disable.push(m[i]);
|
|
}
|
|
}
|
|
|
|
// Remove from collection
|
|
if (itemGroup.isCollection() && !(item && item.getSource()))
|
|
{
|
|
menu.childNodes[m.deleteItem].setAttribute('label', Zotero.getString('pane.items.menu.remove' + multiple));
|
|
show.push(m.deleteItem);
|
|
}
|
|
|
|
// Plural if necessary
|
|
menu.childNodes[m.deleteFromLibrary].setAttribute('label', Zotero.getString('pane.items.menu.erase' + multiple));
|
|
menu.childNodes[m.exportItems].setAttribute('label', Zotero.getString('pane.items.menu.export' + multiple));
|
|
menu.childNodes[m.createBib].setAttribute('label', Zotero.getString('pane.items.menu.createBib' + multiple));
|
|
menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.items.menu.generateReport' + multiple));
|
|
menu.childNodes[m.createParent].setAttribute('label', Zotero.getString('pane.items.menu.createParent' + multiple));
|
|
menu.childNodes[m.recognizePDF].setAttribute('label', Zotero.getString('pane.items.menu.recognizePDF' + multiple));
|
|
menu.childNodes[m.renameAttachments].setAttribute('label', Zotero.getString('pane.items.menu.renameAttachments' + multiple));
|
|
menu.childNodes[m.reindexItem].setAttribute('label', Zotero.getString('pane.items.menu.reindexItem' + multiple));
|
|
|
|
// Hide and enable all actions by default (so if they're shown they're enabled)
|
|
for each(var pos in m) {
|
|
menu.childNodes[pos].setAttribute('hidden', true);
|
|
menu.childNodes[pos].setAttribute('disabled', false);
|
|
}
|
|
|
|
for (var i in disable)
|
|
{
|
|
menu.childNodes[disable[i]].setAttribute('disabled', true);
|
|
}
|
|
|
|
for (var i in show)
|
|
{
|
|
menu.childNodes[show[i]].setAttribute('hidden', false);
|
|
}
|
|
|
|
// add locate menu options
|
|
Zotero_LocateMenu.buildContextMenu(menu);
|
|
}
|
|
|
|
|
|
this.onTreeMouseDown = function (event) {
|
|
var t = event.originalTarget;
|
|
var tree = t.parentNode;
|
|
|
|
var row = {}, col = {}, obj = {};
|
|
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
|
if (row.value == -1) {
|
|
return;
|
|
}
|
|
|
|
var itemGroup = ZoteroPane_Local.collectionsView._getItemAtRow(row.value);
|
|
|
|
// Prevent the tree's select event from being called for a click
|
|
// on a library sync error icon
|
|
if (tree.id == 'zotero-collections-tree') {
|
|
if (itemGroup.isLibrary(true)) {
|
|
if (col.value.id == 'zotero-collections-sync-status-column') {
|
|
var libraryID = itemGroup.isLibrary() ? 0 : itemGroup.ref.libraryID;
|
|
var errors = Zotero.Sync.Runner.getErrors(libraryID);
|
|
if (errors) {
|
|
event.stopPropagation();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Automatically select all equivalent items when clicking on an item
|
|
// in duplicates view
|
|
else if (tree.id == 'zotero-items-tree' && itemGroup.isDuplicates()) {
|
|
// Trigger only on primary-button single clicks with modifiers
|
|
// (so that items can still be selected and deselected manually)
|
|
if (!event || event.detail != 1 || event.button != 0 || event.metaKey || event.shiftKey) {
|
|
return;
|
|
}
|
|
|
|
var t = event.originalTarget;
|
|
|
|
if (t.localName != 'treechildren') {
|
|
return;
|
|
}
|
|
|
|
var tree = t.parentNode;
|
|
|
|
var row = {}, col = {}, obj = {};
|
|
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
|
|
|
// obj.value == 'cell'/'text'/'image'/'twisty'
|
|
if (!obj.value) {
|
|
return;
|
|
}
|
|
|
|
// Duplicated in itemTreeView.js::notify()
|
|
var itemID = ZoteroPane_Local.itemsView._getItemAtRow(row.value).ref.id;
|
|
var setItemIDs = itemGroup.ref.getSetItemsByItemID(itemID);
|
|
ZoteroPane_Local.itemsView.selectItems(setItemIDs);
|
|
|
|
// Prevent the tree's select event from being called here,
|
|
// since it's triggered by the multi-select
|
|
event.stopPropagation();
|
|
}
|
|
}
|
|
|
|
|
|
// Adapted from: http://www.xulplanet.com/references/elemref/ref_tree.html#cmnote-9
|
|
this.onTreeClick = function (event) {
|
|
var t = event.originalTarget;
|
|
|
|
if (t.localName != 'treechildren') {
|
|
return;
|
|
}
|
|
|
|
var tree = t.parentNode;
|
|
|
|
var row = {}, col = {}, obj = {};
|
|
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
|
|
|
// We care only about primary-button double and triple clicks
|
|
if (!event || (event.detail != 2 && event.detail != 3) || event.button != 0) {
|
|
if (row.value == -1) {
|
|
return;
|
|
}
|
|
var itemGroup = ZoteroPane_Local.collectionsView._getItemAtRow(row.value);
|
|
|
|
// Show the error panel when clicking a library-specific
|
|
// sync error icon
|
|
if (itemGroup.isLibrary(true)) {
|
|
if (col.value.id == 'zotero-collections-sync-status-column') {
|
|
var libraryID = itemGroup.isLibrary() ? 0 : itemGroup.ref.libraryID;
|
|
var errors = Zotero.Sync.Runner.getErrors(libraryID);
|
|
if (!errors) {
|
|
return;
|
|
}
|
|
|
|
var panel = Zotero.Sync.Runner.updateErrorPanel(window.document, errors);
|
|
|
|
var anchor = document.getElementById('zotero-collections-tree-shim');
|
|
|
|
var x = {}, y = {}, width = {}, height = {};
|
|
tree.treeBoxObject.getCoordsForCellItem(row.value, col.value, 'image', x, y, width, height);
|
|
|
|
x = x.value + Math.round(width.value / 2);
|
|
y = y.value + height.value + 3;
|
|
|
|
panel.openPopup(anchor, "after_start", x, y, false, false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// The Mozilla tree binding fires select() in mousedown(),
|
|
// but if when it gets to click() the selection differs from
|
|
// what it expects (say, because multiple items had been
|
|
// selected during mousedown(), as is the case in duplicates mode),
|
|
// it fires select() again. We prevent that here.
|
|
else if (itemGroup.isDuplicates() && tree.id == 'zotero-items-tree') {
|
|
if (event.metaKey || event.shiftKey) {
|
|
return;
|
|
}
|
|
|
|
if (obj.value == 'twisty') {
|
|
return;
|
|
}
|
|
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
var itemGroup = ZoteroPane_Local.getItemGroup();
|
|
|
|
// Ignore double-clicks in duplicates view on everything except attachments
|
|
if (itemGroup.isDuplicates()) {
|
|
var items = ZoteroPane_Local.getSelectedItems();
|
|
if (items.length != 1 || !items[0].isAttachment()) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// obj.value == 'cell'/'text'/'image'
|
|
if (!obj.value) {
|
|
return;
|
|
}
|
|
|
|
if (tree.id == 'zotero-collections-tree') {
|
|
// Ignore triple clicks for collections
|
|
if (event.detail != 2) {
|
|
return;
|
|
}
|
|
|
|
if (itemGroup.isLibrary()) {
|
|
var uri = Zotero.URI.getCurrentUserLibraryURI();
|
|
if (uri) {
|
|
ZoteroPane_Local.loadURI(uri);
|
|
event.stopPropagation();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (itemGroup.isSearch()) {
|
|
ZoteroPane_Local.editSelectedCollection();
|
|
return;
|
|
}
|
|
|
|
if (itemGroup.isGroup()) {
|
|
var uri = Zotero.URI.getGroupURI(itemGroup.ref, true);
|
|
ZoteroPane_Local.loadURI(uri);
|
|
event.stopPropagation();
|
|
return;
|
|
}
|
|
|
|
// Ignore double-clicks on Unfiled Items source row
|
|
if (itemGroup.isUnfiled()) {
|
|
return;
|
|
}
|
|
|
|
if (itemGroup.isHeader()) {
|
|
if (itemGroup.ref.id == 'group-libraries-header') {
|
|
var uri = Zotero.URI.getGroupsURL();
|
|
ZoteroPane_Local.loadURI(uri);
|
|
event.stopPropagation();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (itemGroup.isBucket()) {
|
|
ZoteroPane_Local.loadURI(itemGroup.ref.uri);
|
|
event.stopPropagation();
|
|
}
|
|
}
|
|
else if (tree.id == 'zotero-items-tree') {
|
|
var viewOnDoubleClick = Zotero.Prefs.get('viewOnDoubleClick');
|
|
if (viewOnDoubleClick) {
|
|
// Expand/collapse on triple-click, though the double-click
|
|
// will still trigger
|
|
if (event.detail == 3) {
|
|
tree.view.toggleOpenState(tree.view.selection.currentIndex);
|
|
return;
|
|
}
|
|
|
|
// Don't expand/collapse on double-click
|
|
event.stopPropagation();
|
|
}
|
|
|
|
if (tree.view && tree.view.selection.currentIndex > -1) {
|
|
var item = ZoteroPane_Local.getSelectedItems()[0];
|
|
if (item) {
|
|
if (!viewOnDoubleClick && item.isRegularItem()) {
|
|
return;
|
|
}
|
|
ZoteroPane_Local.viewItems([item], event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
this.openPreferences = function (paneID, action) {
|
|
var io = {
|
|
pane: paneID,
|
|
action: action
|
|
};
|
|
window.openDialog('chrome://zotero/content/preferences/preferences.xul',
|
|
'zotero-prefs',
|
|
'chrome,titlebar,toolbar,centerscreen,'
|
|
+ Zotero.Prefs.get('browser.preferences.instantApply', true) ? 'dialog=no' : 'modal',
|
|
io
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Loads a URL following the standard modifier key behavior
|
|
* (e.g. meta-click == new background tab, meta-shift-click == new front tab,
|
|
* shift-click == new window, no modifier == frontmost tab
|
|
*/
|
|
function loadURI(uris, event) {
|
|
if(typeof uris === "string") {
|
|
uris = [uris];
|
|
}
|
|
|
|
for each(var uri in uris) {
|
|
// Ignore javascript: and data: URIs
|
|
if (uri.match(/^(javascript|data):/)) {
|
|
return;
|
|
}
|
|
|
|
if (Zotero.isStandalone) {
|
|
if(uri.match(/^https?/)) {
|
|
this.launchURL(uri);
|
|
} else {
|
|
ZoteroStandalone.openInViewer(uri);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Open in new tab
|
|
var openInNewTab = event && (event.metaKey || (!Zotero.isMac && event.ctrlKey));
|
|
if (event && event.shiftKey && !openInNewTab) {
|
|
window.open(uri, "zotero-loaded-page",
|
|
"menubar=yes,location=yes,toolbar=yes,personalbar=yes,resizable=yes,scrollbars=yes,status=yes");
|
|
}
|
|
else if (openInNewTab || !window.loadURI || uris.length > 1) {
|
|
// if no gBrowser, find it
|
|
if(!gBrowser) {
|
|
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
.getService(Components.interfaces.nsIWindowMediator);
|
|
var browserWindow = wm.getMostRecentWindow("navigator:browser");
|
|
var gBrowser = browserWindow.gBrowser;
|
|
}
|
|
|
|
// load in a new tab
|
|
var tab = gBrowser.addTab(uri);
|
|
var browser = gBrowser.getBrowserForTab(tab);
|
|
|
|
if (event && event.shiftKey || !openInNewTab) {
|
|
// if shift key is down, or we are opening in a new tab because there is no loadURI,
|
|
// select new tab
|
|
gBrowser.selectedTab = tab;
|
|
}
|
|
}
|
|
else {
|
|
window.loadURI(uri);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function setItemsPaneMessage(msg, lock) {
|
|
var elem = document.getElementById('zotero-items-pane-message-box');
|
|
|
|
if (elem.getAttribute('locked') == 'true') {
|
|
return;
|
|
}
|
|
|
|
while (elem.hasChildNodes()) {
|
|
elem.removeChild(elem.firstChild);
|
|
}
|
|
var msgParts = msg.split("\n\n");
|
|
for (var i=0; i<msgParts.length; i++) {
|
|
var desc = document.createElement('description');
|
|
desc.appendChild(document.createTextNode(msgParts[i]));
|
|
elem.appendChild(desc);
|
|
}
|
|
|
|
// Make message permanent
|
|
if (lock) {
|
|
elem.setAttribute('locked', true);
|
|
}
|
|
|
|
document.getElementById('zotero-items-pane-content').selectedIndex = 1;
|
|
}
|
|
|
|
|
|
function clearItemsPaneMessage() {
|
|
// If message box is locked, don't clear
|
|
var box = document.getElementById('zotero-items-pane-message-box');
|
|
if (box.getAttribute('locked') == 'true') {
|
|
return;
|
|
}
|
|
|
|
document.getElementById('zotero-items-pane-content').selectedIndex = 0;
|
|
}
|
|
|
|
|
|
this.setItemPaneMessage = function (msg) {
|
|
document.getElementById('zotero-item-pane-content').selectedIndex = 0;
|
|
|
|
var label = document.getElementById('zotero-item-pane-message');
|
|
label.value = msg;
|
|
}
|
|
|
|
|
|
// Updates browser context menu options
|
|
function contextPopupShowing()
|
|
{
|
|
if (!Zotero.Prefs.get('browserContentContextMenu')) {
|
|
return;
|
|
}
|
|
|
|
var menuitem = document.getElementById("zotero-context-add-to-current-note");
|
|
if (menuitem){
|
|
var items = ZoteroPane_Local.getSelectedItems();
|
|
if (ZoteroPane_Local.itemsView.selection && ZoteroPane_Local.itemsView.selection.count==1
|
|
&& items[0] && items[0].isNote()
|
|
&& window.gContextMenu.isTextSelected)
|
|
{
|
|
menuitem.hidden = false;
|
|
}
|
|
else
|
|
{
|
|
menuitem.hidden = true;
|
|
}
|
|
}
|
|
|
|
var menuitem = document.getElementById("zotero-context-add-to-new-note");
|
|
if (menuitem){
|
|
if (window.gContextMenu.isTextSelected)
|
|
{
|
|
menuitem.hidden = false;
|
|
}
|
|
else
|
|
{
|
|
menuitem.hidden = true;
|
|
}
|
|
}
|
|
|
|
var menuitem = document.getElementById("zotero-context-save-link-as-item");
|
|
if (menuitem) {
|
|
if (window.gContextMenu.onLink) {
|
|
menuitem.hidden = false;
|
|
}
|
|
else {
|
|
menuitem.hidden = true;
|
|
}
|
|
}
|
|
|
|
var menuitem = document.getElementById("zotero-context-save-image-as-item");
|
|
if (menuitem) {
|
|
// Not using window.gContextMenu.hasBGImage -- if the user wants it,
|
|
// they can use the Firefox option to view and then import from there
|
|
if (window.gContextMenu.onImage) {
|
|
menuitem.hidden = false;
|
|
}
|
|
else {
|
|
menuitem.hidden = true;
|
|
}
|
|
}
|
|
|
|
// If Zotero is locked or library is read-only, disable menu items
|
|
var menu = document.getElementById('zotero-content-area-context-menu');
|
|
var disabled = Zotero.locked;
|
|
if (!disabled && self.collectionsView.selection && self.collectionsView.selection.count) {
|
|
var itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex);
|
|
disabled = !itemGroup.editable;
|
|
}
|
|
for each(var menuitem in menu.firstChild.childNodes) {
|
|
menuitem.disabled = disabled;
|
|
}
|
|
}
|
|
|
|
|
|
this.newNote = function (popup, parent, text, citeURI) {
|
|
if (!Zotero.stateCheck()) {
|
|
this.displayErrorMessage(true);
|
|
return;
|
|
}
|
|
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
if (!popup) {
|
|
if (!text) {
|
|
text = '';
|
|
}
|
|
text = Zotero.Utilities.trim(text);
|
|
|
|
if (text) {
|
|
text = '<blockquote'
|
|
+ (citeURI ? ' cite="' + citeURI + '"' : '')
|
|
+ '>' + Zotero.Utilities.text2html(text) + "</blockquote>";
|
|
}
|
|
|
|
var item = new Zotero.Item('note');
|
|
item.libraryID = this.getSelectedLibraryID();
|
|
item.setNote(text);
|
|
if (parent) {
|
|
item.setSource(parent);
|
|
}
|
|
var itemID = item.save();
|
|
|
|
if (!parent && this.itemsView && this.itemsView._itemGroup.isCollection()) {
|
|
this.itemsView._itemGroup.ref.addItem(itemID);
|
|
}
|
|
|
|
this.selectItem(itemID);
|
|
|
|
document.getElementById('zotero-note-editor').focus();
|
|
}
|
|
else
|
|
{
|
|
// TODO: _text_
|
|
var c = this.getSelectedCollection();
|
|
if (c) {
|
|
this.openNoteWindow(null, c.id, parent);
|
|
}
|
|
else {
|
|
this.openNoteWindow(null, null, parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function addTextToNote(text, citeURI)
|
|
{
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
if (!text) {
|
|
return false;
|
|
}
|
|
|
|
text = Zotero.Utilities.trim(text);
|
|
|
|
if (!text.length) {
|
|
return false;
|
|
}
|
|
|
|
text = '<blockquote'
|
|
+ (citeURI ? ' cite="' + citeURI + '"' : '')
|
|
+ '>' + Zotero.Utilities.text2html(text) + "</blockquote>";
|
|
|
|
var items = this.getSelectedItems();
|
|
if (this.itemsView.selection.count == 1 && items[0] && items[0].isNote()) {
|
|
var note = items[0].getNote()
|
|
|
|
items[0].setNote(note + text);
|
|
items[0].save();
|
|
|
|
var noteElem = document.getElementById('zotero-note-editor')
|
|
noteElem.focus();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function openNoteWindow(itemID, col, parentItemID)
|
|
{
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
var name = null;
|
|
|
|
if (itemID) {
|
|
// Create a name for this window so we can focus it later
|
|
//
|
|
// Collection is only used on new notes, so we don't need to
|
|
// include it in the name
|
|
name = 'zotero-note-' + itemID;
|
|
|
|
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
.getService(Components.interfaces.nsIWindowMediator);
|
|
var e = wm.getEnumerator('');
|
|
while (e.hasMoreElements()) {
|
|
var w = e.getNext();
|
|
if (w.name == name) {
|
|
w.focus();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
window.open('chrome://zotero/content/note.xul?v=1'
|
|
+ (itemID ? '&id=' + itemID : '') + (col ? '&coll=' + col : '')
|
|
+ (parentItemID ? '&p=' + parentItemID : ''),
|
|
name, 'chrome,resizable,centerscreen');
|
|
}
|
|
|
|
|
|
this.addAttachmentFromURI = function (link, itemID) {
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
|
|
var input = {};
|
|
var check = {value : false};
|
|
|
|
// TODO: Localize
|
|
// TODO: Allow title to be specified?
|
|
var result = ps.prompt(null, "Attach Link to URI", "Enter a URI:", input, "", {});
|
|
if (!result || !input.value) return false;
|
|
|
|
// Create a new attachment
|
|
Zotero.Attachments.linkFromURL(input.value, itemID);
|
|
}
|
|
|
|
|
|
function addAttachmentFromDialog(link, id)
|
|
{
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
var itemGroup = ZoteroPane_Local.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
|
if (link && itemGroup.isWithinGroup()) {
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
ps.alert(null, "", "Linked files cannot be added to group libraries.");
|
|
return;
|
|
}
|
|
|
|
// TODO: disable in menu
|
|
if (!this.canEditFiles()) {
|
|
this.displayCannotEditLibraryFilesMessage();
|
|
return;
|
|
}
|
|
|
|
var libraryID = itemGroup.ref.libraryID;
|
|
|
|
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
|
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
|
.createInstance(nsIFilePicker);
|
|
fp.init(window, Zotero.getString('pane.item.attachments.select'), nsIFilePicker.modeOpenMultiple);
|
|
fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
|
|
|
|
if(fp.show() == nsIFilePicker.returnOK)
|
|
{
|
|
var files = fp.files;
|
|
while (files.hasMoreElements()){
|
|
var file = files.getNext();
|
|
file.QueryInterface(Components.interfaces.nsILocalFile);
|
|
var attachmentID;
|
|
if(link)
|
|
attachmentID = Zotero.Attachments.linkFromFile(file, id);
|
|
else
|
|
attachmentID = Zotero.Attachments.importFromFile(file, id, libraryID);
|
|
|
|
if(attachmentID && !id)
|
|
{
|
|
var c = this.getSelectedCollection();
|
|
if(c)
|
|
c.addItem(attachmentID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
this.addItemFromPage = function (itemType, saveSnapshot, row) {
|
|
if(Zotero.isConnector) {
|
|
// In connector, save page via Zotero Standalone
|
|
var doc = window.content.document;
|
|
Zotero.Connector.callMethod("saveSnapshot", {"url":doc.location.toString(),
|
|
"cookie":doc.cookie, "html":doc.documentElement.innerHTML},
|
|
function(returnValue, status) {
|
|
_showPageSaveStatus(doc.title);
|
|
});
|
|
return;
|
|
}
|
|
|
|
if ((row || (this.collectionsView && this.collectionsView.selection)) && !this.canEdit(row)) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
return this.addItemFromDocument(window.content.document, itemType, saveSnapshot, row);
|
|
}
|
|
|
|
/**
|
|
* Shows progress dialog for a webpage/snapshot save request
|
|
*/
|
|
function _showPageSaveStatus(title) {
|
|
var progressWin = new Zotero.ProgressWindow();
|
|
progressWin.changeHeadline(Zotero.getString('ingester.scraping'));
|
|
var icon = 'chrome://zotero/skin/treeitem-webpage.png';
|
|
progressWin.addLines(title, icon)
|
|
progressWin.show();
|
|
progressWin.startCloseTimer();
|
|
}
|
|
|
|
/**
|
|
* @param {Document} doc
|
|
* @param {String|Integer} [itemType='webpage'] Item type id or name
|
|
* @param {Boolean} [saveSnapshot] Force saving or non-saving of a snapshot,
|
|
* regardless of automaticSnapshots pref
|
|
*/
|
|
this.addItemFromDocument = function (doc, itemType, saveSnapshot, row) {
|
|
_showPageSaveStatus(doc.title);
|
|
|
|
// Save snapshot if explicitly enabled or automatically pref is set and not explicitly disabled
|
|
saveSnapshot = saveSnapshot || (saveSnapshot !== false && Zotero.Prefs.get('automaticSnapshots'));
|
|
|
|
// TODO: this, needless to say, is a temporary hack
|
|
if (itemType == 'temporaryPDFHack') {
|
|
itemType = null;
|
|
var isPDF = false;
|
|
if (doc.title.indexOf('application/pdf') != -1 || Zotero.Attachments.isPDFJS(doc)) {
|
|
isPDF = true;
|
|
}
|
|
else {
|
|
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
|
getService(Components.interfaces.nsIIOService);
|
|
try {
|
|
var uri = ios.newURI(doc.location, null, null);
|
|
if (uri.fileName && uri.fileName.match(/pdf$/)) {
|
|
isPDF = true;
|
|
}
|
|
}
|
|
catch (e) {
|
|
Zotero.debug(e);
|
|
Components.utils.reportError(e);
|
|
}
|
|
}
|
|
|
|
if (isPDF && saveSnapshot) {
|
|
//
|
|
// Duplicate newItem() checks here
|
|
//
|
|
if (!Zotero.stateCheck()) {
|
|
this.displayErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
// Currently selected row
|
|
if (row === undefined && this.collectionsView && this.collectionsView.selection) {
|
|
row = this.collectionsView.selection.currentIndex;
|
|
}
|
|
|
|
if (row && !this.canEdit(row)) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
if (row !== undefined) {
|
|
var itemGroup = this.collectionsView._getItemAtRow(row);
|
|
var libraryID = itemGroup.ref.libraryID;
|
|
}
|
|
else {
|
|
var libraryID = null;
|
|
var itemGroup = null;
|
|
}
|
|
//
|
|
//
|
|
//
|
|
|
|
if (!this.canEditFiles(row)) {
|
|
this.displayCannotEditLibraryFilesMessage();
|
|
return;
|
|
}
|
|
|
|
if (itemGroup && itemGroup.isCollection()) {
|
|
var collectionID = itemGroup.ref.id;
|
|
}
|
|
else {
|
|
var collectionID = false;
|
|
}
|
|
|
|
var itemID = Zotero.Attachments.importFromDocument(doc, false, false, collectionID, null, libraryID);
|
|
|
|
// importFromDocument() doesn't trigger the notifier for a second
|
|
//
|
|
// The one-second delay is weird but better than nothing
|
|
var self = this;
|
|
setTimeout(function () {
|
|
self.selectItem(itemID);
|
|
}, 1001);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Save web page item by default
|
|
if (!itemType) {
|
|
itemType = 'webpage';
|
|
}
|
|
var data = {
|
|
title: doc.title,
|
|
url: doc.location.href,
|
|
accessDate: "CURRENT_TIMESTAMP"
|
|
}
|
|
itemType = Zotero.ItemTypes.getID(itemType);
|
|
var item = this.newItem(itemType, data, row);
|
|
|
|
if (item.libraryID) {
|
|
var group = Zotero.Groups.getByLibraryID(item.libraryID);
|
|
var filesEditable = group.filesEditable;
|
|
}
|
|
else {
|
|
var filesEditable = true;
|
|
}
|
|
|
|
if (saveSnapshot) {
|
|
var link = false;
|
|
|
|
if (link) {
|
|
Zotero.Attachments.linkFromDocument(doc, item.id);
|
|
}
|
|
else if (filesEditable) {
|
|
Zotero.Attachments.importFromDocument(doc, item.id);
|
|
}
|
|
}
|
|
|
|
return item.id;
|
|
}
|
|
|
|
|
|
this.addItemFromURL = function (url, itemType, saveSnapshot, row) {
|
|
if (window.content && url == window.content.document.location.href) {
|
|
return this.addItemFromPage(itemType, saveSnapshot, row);
|
|
}
|
|
|
|
var self = this;
|
|
|
|
url = Zotero.Utilities.resolveIntermediateURL(url);
|
|
|
|
Zotero.MIME.getMIMETypeFromURL(url, function (mimeType, hasNativeHandler) {
|
|
// If native type, save using a hidden browser
|
|
if (hasNativeHandler) {
|
|
var processor = function (doc) {
|
|
ZoteroPane_Local.addItemFromDocument(doc, itemType, saveSnapshot, row);
|
|
};
|
|
|
|
var done = function () {}
|
|
|
|
var exception = function (e) {
|
|
Zotero.debug(e);
|
|
}
|
|
|
|
Zotero.HTTP.processDocuments([url], processor, done, exception);
|
|
}
|
|
// Otherwise create placeholder item, attach attachment, and update from that
|
|
else {
|
|
// TODO: this, needless to say, is a temporary hack
|
|
if (itemType == 'temporaryPDFHack') {
|
|
itemType = null;
|
|
|
|
if (mimeType == 'application/pdf') {
|
|
//
|
|
// Duplicate newItem() checks here
|
|
//
|
|
if (!Zotero.stateCheck()) {
|
|
ZoteroPane_Local.displayErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
// Currently selected row
|
|
if (row === undefined) {
|
|
row = ZoteroPane_Local.collectionsView.selection.currentIndex;
|
|
}
|
|
|
|
if (!ZoteroPane_Local.canEdit(row)) {
|
|
ZoteroPane_Local.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
if (row !== undefined) {
|
|
var itemGroup = ZoteroPane_Local.collectionsView._getItemAtRow(row);
|
|
var libraryID = itemGroup.ref.libraryID;
|
|
}
|
|
else {
|
|
var libraryID = null;
|
|
var itemGroup = null;
|
|
}
|
|
//
|
|
//
|
|
//
|
|
|
|
if (!ZoteroPane_Local.canEditFiles(row)) {
|
|
ZoteroPane_Local.displayCannotEditLibraryFilesMessage();
|
|
return;
|
|
}
|
|
|
|
if (itemGroup && itemGroup.isCollection()) {
|
|
var collectionID = itemGroup.ref.id;
|
|
}
|
|
else {
|
|
var collectionID = false;
|
|
}
|
|
|
|
var attachmentItem = Zotero.Attachments.importFromURL(url, false, false, false, collectionID, mimeType, libraryID);
|
|
|
|
// importFromURL() doesn't trigger the notifier until
|
|
// after download is complete
|
|
//
|
|
// TODO: add a callback to importFromURL()
|
|
setTimeout(function () {
|
|
self.selectItem(attachmentItem.id);
|
|
}, 1001);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!itemType) {
|
|
itemType = 'webpage';
|
|
}
|
|
|
|
var item = ZoteroPane_Local.newItem(itemType, {}, row);
|
|
|
|
if (item.libraryID) {
|
|
var group = Zotero.Groups.getByLibraryID(item.libraryID);
|
|
filesEditable = group.filesEditable;
|
|
}
|
|
else {
|
|
filesEditable = true;
|
|
}
|
|
|
|
// Save snapshot if explicitly enabled or automatically pref is set and not explicitly disabled
|
|
if (saveSnapshot || (saveSnapshot !== false && Zotero.Prefs.get('automaticSnapshots'))) {
|
|
var link = false;
|
|
|
|
if (link) {
|
|
//Zotero.Attachments.linkFromURL(doc, item.id);
|
|
}
|
|
else if (filesEditable) {
|
|
var attachmentItem = Zotero.Attachments.importFromURL(url, item.id, false, false, false, mimeType);
|
|
if (attachmentItem) {
|
|
item.setField('title', attachmentItem.getField('title'));
|
|
item.setField('url', attachmentItem.getField('url'));
|
|
item.setField('accessDate', attachmentItem.getField('accessDate'));
|
|
item.save();
|
|
}
|
|
}
|
|
}
|
|
|
|
return item.id;
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
/*
|
|
* Create an attachment from the current page
|
|
*
|
|
* |itemID| -- itemID of parent item
|
|
* |link| -- create web link instead of snapshot
|
|
*/
|
|
this.addAttachmentFromPage = function (link, itemID)
|
|
{
|
|
if (!Zotero.stateCheck()) {
|
|
this.displayErrorMessage(true);
|
|
return;
|
|
}
|
|
|
|
if (typeof itemID != 'number') {
|
|
throw ("itemID must be an integer in ZoteroPane_Local.addAttachmentFromPage()");
|
|
}
|
|
|
|
var progressWin = new Zotero.ProgressWindow();
|
|
progressWin.changeHeadline(Zotero.getString('save.' + (link ? 'link' : 'attachment')));
|
|
var type = link ? 'web-link' : 'snapshot';
|
|
var icon = 'chrome://zotero/skin/treeitem-attachment-' + type + '.png';
|
|
progressWin.addLines(window.content.document.title, icon)
|
|
progressWin.show();
|
|
progressWin.startCloseTimer();
|
|
|
|
if (link) {
|
|
Zotero.Attachments.linkFromDocument(window.content.document, itemID);
|
|
}
|
|
else {
|
|
Zotero.Attachments.importFromDocument(window.content.document, itemID);
|
|
}
|
|
}
|
|
|
|
|
|
this.viewItems = function (items, event) {
|
|
if (items.length > 1) {
|
|
if (!event || (!event.metaKey && !event.shiftKey)) {
|
|
event = { metaKey: true, shiftKey: true };
|
|
}
|
|
}
|
|
|
|
for each(var item in items) {
|
|
if (item.isRegularItem()) {
|
|
var uri = Components.classes["@mozilla.org/network/standard-url;1"]
|
|
.createInstance(Components.interfaces.nsIURI);
|
|
var snapID = item.getBestAttachment();
|
|
if (snapID) {
|
|
ZoteroPane_Local.viewAttachment(snapID, event);
|
|
continue;
|
|
}
|
|
|
|
var uri = item.getField('url');
|
|
if (!uri) {
|
|
var doi = item.getField('DOI');
|
|
if (doi) {
|
|
// Pull out DOI, in case there's a prefix
|
|
doi = Zotero.Utilities.cleanDOI(doi);
|
|
if (doi) {
|
|
uri = "http://dx.doi.org/" + encodeURIComponent(doi);
|
|
}
|
|
}
|
|
}
|
|
if (uri) {
|
|
ZoteroPane_Local.loadURI(uri, event);
|
|
}
|
|
}
|
|
else if (item.isNote()) {
|
|
if (!ZoteroPane_Local.collectionsView.editable) {
|
|
continue;
|
|
}
|
|
document.getElementById('zotero-view-note-button').doCommand();
|
|
}
|
|
else if (item.isAttachment()) {
|
|
ZoteroPane_Local.viewAttachment(item.id, event);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function viewAttachment(itemIDs, event, noLocateOnMissing, forceExternalViewer) {
|
|
Components.utils.import("resource://zotero/q.js");
|
|
|
|
// If view isn't editable, don't show Locate button, since the updated
|
|
// path couldn't be sent back up
|
|
if (!this.collectionsView.editable) {
|
|
noLocateOnMissing = true;
|
|
}
|
|
|
|
if(typeof itemIDs != "object") itemIDs = [itemIDs];
|
|
|
|
// If multiple items, set up event so we open in new tab
|
|
if(itemIDs.length > 1) {
|
|
if(!event || (!event.metaKey && !event.shiftKey)) {
|
|
event = {"metaKey":true, "shiftKey":true};
|
|
}
|
|
}
|
|
|
|
for each(var itemID in itemIDs) {
|
|
var item = Zotero.Items.get(itemID);
|
|
if (!item.isAttachment()) {
|
|
throw ("Item " + itemID + " is not an attachment in ZoteroPane_Local.viewAttachment()");
|
|
}
|
|
|
|
if (item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
|
this.loadURI(item.getField('url'), event);
|
|
continue;
|
|
}
|
|
|
|
var file = item.getFile();
|
|
if (file) {
|
|
if(forceExternalViewer !== undefined) {
|
|
var externalViewer = forceExternalViewer;
|
|
} else {
|
|
var mimeType = item.attachmentMIMEType;
|
|
// If no MIME type specified, try to detect again (I guess in case
|
|
// we've gotten smarter since the file was imported?)
|
|
if (!mimeType) {
|
|
mimeType = Zotero.MIME.getMIMETypeFromFile(file);
|
|
|
|
// TODO: update DB with new info
|
|
}
|
|
|
|
var ext = Zotero.File.getExtension(file);
|
|
var externalViewer = Zotero.isStandalone || (!Zotero.MIME.hasNativeHandler(mimeType, ext) &&
|
|
(!Zotero.MIME.hasInternalHandler(mimeType, ext) || Zotero.Prefs.get('launchNonNativeFiles')));
|
|
}
|
|
|
|
if (!externalViewer) {
|
|
var url = 'zotero://attachment/' + itemID + '/';
|
|
this.loadURI(url, event);
|
|
}
|
|
else {
|
|
this.launchFile(file);
|
|
}
|
|
}
|
|
else {
|
|
if (!item.isImportedAttachment() || !Zotero.Sync.Storage.downloadAsNeeded(item.libraryID)) {
|
|
this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing);
|
|
return;
|
|
}
|
|
|
|
let downloadedItem = item;
|
|
Q.fcall(function () {
|
|
return Zotero.Sync.Storage.downloadFile(
|
|
downloadedItem,
|
|
{
|
|
onProgress: function (progress, progressMax) {}
|
|
});
|
|
})
|
|
.then(function () {
|
|
if (!downloadedItem.getFile()) {
|
|
ZoteroPane_Local.showAttachmentNotFoundDialog(downloadedItem.id, noLocateOnMissing);
|
|
return;
|
|
}
|
|
|
|
// check if unchanged?
|
|
// maybe not necessary, since we'll get an error if there's an error
|
|
|
|
|
|
Zotero.Notifier.trigger('redraw', 'item', []);
|
|
|
|
ZoteroPane_Local.viewAttachment(downloadedItem.id, event, false, forceExternalViewer);
|
|
})
|
|
.fail(function (e) {
|
|
// TODO: show error somewhere else
|
|
Zotero.debug(e, 1);
|
|
ZoteroPane_Local.syncAlert(e);
|
|
})
|
|
.end();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Launch a file, the best way we can
|
|
*/
|
|
this.launchFile = function (file) {
|
|
try {
|
|
file.launch();
|
|
}
|
|
catch (e) {
|
|
Zotero.debug("launch() not supported -- trying fallback executable");
|
|
|
|
try {
|
|
if (Zotero.isWin) {
|
|
var pref = "fallbackLauncher.windows";
|
|
}
|
|
else {
|
|
var pref = "fallbackLauncher.unix";
|
|
}
|
|
var path = Zotero.Prefs.get(pref);
|
|
|
|
var exec = Components.classes["@mozilla.org/file/local;1"]
|
|
.createInstance(Components.interfaces.nsILocalFile);
|
|
exec.initWithPath(path);
|
|
if (!exec.exists()) {
|
|
throw (path + " does not exist");
|
|
}
|
|
|
|
var proc = Components.classes["@mozilla.org/process/util;1"]
|
|
.createInstance(Components.interfaces.nsIProcess);
|
|
proc.init(exec);
|
|
|
|
var args = [file.path];
|
|
proc.runw(true, args, args.length);
|
|
}
|
|
catch (e) {
|
|
Zotero.debug(e);
|
|
Zotero.debug("Launching via executable failed -- passing to loadUrl()");
|
|
|
|
// If nsILocalFile.launch() isn't available and the fallback
|
|
// executable doesn't exist, we just let the Firefox external
|
|
// helper app window handle it
|
|
var nsIFPH = Components.classes["@mozilla.org/network/protocol;1?name=file"]
|
|
.getService(Components.interfaces.nsIFileProtocolHandler);
|
|
var uri = nsIFPH.newFileURI(file);
|
|
|
|
var nsIEPS = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"].
|
|
getService(Components.interfaces.nsIExternalProtocolService);
|
|
nsIEPS.loadUrl(uri);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Launch an HTTP URL externally, the best way we can
|
|
*
|
|
* Used only by Standalone
|
|
*/
|
|
this.launchURL = function (url) {
|
|
if (!url.match(/^https?/)) {
|
|
throw new Error("launchURL() requires an HTTP(S) URL");
|
|
}
|
|
|
|
try {
|
|
var io = Components.classes['@mozilla.org/network/io-service;1']
|
|
.getService(Components.interfaces.nsIIOService);
|
|
var uri = io.newURI(url, null, null);
|
|
var handler = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
|
|
.getService(Components.interfaces.nsIExternalProtocolService)
|
|
.getProtocolHandlerInfo('http');
|
|
handler.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault;
|
|
handler.launchWithURI(uri, null);
|
|
}
|
|
catch (e) {
|
|
Zotero.debug("launchWithURI() not supported -- trying fallback executable");
|
|
|
|
if (Zotero.isWin) {
|
|
var pref = "fallbackLauncher.windows";
|
|
}
|
|
else {
|
|
var pref = "fallbackLauncher.unix";
|
|
}
|
|
var path = Zotero.Prefs.get(pref);
|
|
|
|
var exec = Components.classes["@mozilla.org/file/local;1"]
|
|
.createInstance(Components.interfaces.nsILocalFile);
|
|
exec.initWithPath(path);
|
|
if (!exec.exists()) {
|
|
throw ("Fallback executable not found -- check extensions.zotero." + pref + " in about:config");
|
|
}
|
|
|
|
var proc = Components.classes["@mozilla.org/process/util;1"]
|
|
.createInstance(Components.interfaces.nsIProcess);
|
|
proc.init(exec);
|
|
|
|
var args = [url];
|
|
proc.runw(false, args, args.length);
|
|
}
|
|
}
|
|
|
|
|
|
function viewSelectedAttachment(event, noLocateOnMissing)
|
|
{
|
|
if (this.itemsView && this.itemsView.selection.count == 1) {
|
|
this.viewAttachment(this.getSelectedItems(true)[0], event, noLocateOnMissing);
|
|
}
|
|
}
|
|
|
|
|
|
this.showAttachmentInFilesystem = function (itemID, noLocateOnMissing) {
|
|
var attachment = Zotero.Items.get(itemID)
|
|
if (attachment.attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
|
var file = attachment.getFile();
|
|
if (file) {
|
|
try {
|
|
file.reveal();
|
|
}
|
|
catch (e) {
|
|
// On platforms that don't support nsILocalFile.reveal() (e.g. Linux),
|
|
// launch the parent directory
|
|
var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile);
|
|
this.launchFile(parent);
|
|
}
|
|
}
|
|
else {
|
|
this.showAttachmentNotFoundDialog(attachment.id, noLocateOnMissing)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Test if the user can edit the currently selected view
|
|
*
|
|
* @param {Integer} [row]
|
|
*
|
|
* @return {Boolean} TRUE if user can edit, FALSE if not
|
|
*/
|
|
this.canEdit = function (row) {
|
|
// Currently selected row
|
|
if (row === undefined) {
|
|
row = this.collectionsView.selection.currentIndex;
|
|
}
|
|
|
|
var itemGroup = this.collectionsView._getItemAtRow(row);
|
|
return itemGroup.editable;
|
|
}
|
|
|
|
|
|
/**
|
|
* Test if the user can edit the parent library of the selected view
|
|
*
|
|
* @param {Integer} [row]
|
|
* @return {Boolean} TRUE if user can edit, FALSE if not
|
|
*/
|
|
this.canEditLibrary = function (row) {
|
|
// Currently selected row
|
|
if (row === undefined) {
|
|
row = this.collectionsView.selection.currentIndex;
|
|
}
|
|
|
|
var itemGroup = this.collectionsView._getItemAtRow(row);
|
|
// TODO: isEditable for user library should just return true
|
|
if (itemGroup.ref.libraryID) {
|
|
return Zotero.Libraries.isEditable(itemGroup.ref.libraryID);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Test if the user can edit the currently selected library/collection
|
|
*
|
|
* @param {Integer} [row]
|
|
*
|
|
* @return {Boolean} TRUE if user can edit, FALSE if not
|
|
*/
|
|
this.canEditFiles = function (row) {
|
|
// Currently selected row
|
|
if (row === undefined) {
|
|
row = this.collectionsView.selection.currentIndex;
|
|
}
|
|
|
|
var itemGroup = this.collectionsView._getItemAtRow(row);
|
|
return itemGroup.filesEditable;
|
|
}
|
|
|
|
|
|
this.displayCannotEditLibraryMessage = function () {
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
ps.alert(null, "", Zotero.getString('save.error.cannotMakeChangesToCollection'));
|
|
}
|
|
|
|
|
|
this.displayCannotEditLibraryFilesMessage = function () {
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
ps.alert(null, "", Zotero.getString('save.error.cannotAddFilesToCollection'));
|
|
}
|
|
|
|
|
|
function showAttachmentNotFoundDialog(itemID, noLocate) {
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
|
|
createInstance(Components.interfaces.nsIPromptService);
|
|
|
|
|
|
// Don't show Locate button
|
|
if (noLocate) {
|
|
var index = ps.alert(null,
|
|
Zotero.getString('pane.item.attachments.fileNotFound.title'),
|
|
Zotero.getString('pane.item.attachments.fileNotFound.text')
|
|
);
|
|
return;
|
|
}
|
|
|
|
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
|
|
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
|
|
var index = ps.confirmEx(null,
|
|
Zotero.getString('pane.item.attachments.fileNotFound.title'),
|
|
Zotero.getString('pane.item.attachments.fileNotFound.text'),
|
|
buttonFlags, Zotero.getString('general.locate'), null,
|
|
null, null, {});
|
|
|
|
if (index == 0) {
|
|
this.relinkAttachment(itemID);
|
|
}
|
|
}
|
|
|
|
|
|
this.syncAlert = function (e) {
|
|
e = Zotero.Sync.Runner.parseSyncError(e);
|
|
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK
|
|
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
|
|
|
|
// Warning
|
|
if (e.status == 'warning') {
|
|
var title = Zotero.getString('general.warning');
|
|
|
|
// If secondary button not specified, just use an alert
|
|
if (e.buttonText) {
|
|
var buttonText = e.buttonText;
|
|
}
|
|
else {
|
|
ps.alert(null, title, e.message);
|
|
return;
|
|
}
|
|
|
|
var index = ps.confirmEx(
|
|
null,
|
|
title,
|
|
e.message,
|
|
buttonFlags,
|
|
"",
|
|
buttonText,
|
|
"", null, {}
|
|
);
|
|
|
|
if (index == 1) {
|
|
setTimeout(function () { buttonCallback(); }, 1);
|
|
}
|
|
}
|
|
// Error
|
|
else if (e.status == 'error') {
|
|
var title = Zotero.getString('general.error');
|
|
|
|
// If secondary button is explicitly null, just use an alert
|
|
if (buttonText === null) {
|
|
ps.alert(null, title, e.message);
|
|
return;
|
|
}
|
|
|
|
if (typeof buttonText == 'undefined') {
|
|
var buttonText = Zotero.getString('errorReport.reportError');
|
|
var buttonCallback = function () {
|
|
ZoteroPane.reportErrors();
|
|
};
|
|
}
|
|
else {
|
|
var buttonText = e.buttonText;
|
|
var buttonCallback = e.buttonCallback;
|
|
}
|
|
|
|
var index = ps.confirmEx(
|
|
null,
|
|
title,
|
|
e.message,
|
|
buttonFlags,
|
|
"",
|
|
buttonText,
|
|
"", null, {}
|
|
);
|
|
|
|
if (index == 1) {
|
|
setTimeout(function () { buttonCallback(); }, 1);
|
|
}
|
|
}
|
|
// Upgrade
|
|
else if (e.status == 'upgrade') {
|
|
ps.alert(null, "", e.message);
|
|
}
|
|
};
|
|
|
|
|
|
this.createParentItemsFromSelected = function () {
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
|
|
var items = this.getSelectedItems();
|
|
for (var i=0; i<items.length; i++) {
|
|
var item = items[i];
|
|
if (!item.isTopLevelItem() || item.isRegularItem()) {
|
|
throw('Item ' + itemID + ' is not a top-level attachment or note in ZoteroPane_Local.createParentItemsFromSelected()');
|
|
}
|
|
|
|
Zotero.DB.beginTransaction();
|
|
// TODO: remove once there are no top-level web attachments
|
|
if (item.isWebAttachment()) {
|
|
var parent = new Zotero.Item('webpage');
|
|
}
|
|
else {
|
|
var parent = new Zotero.Item('document');
|
|
}
|
|
parent.libraryID = item.libraryID;
|
|
parent.setField('title', item.getField('title'));
|
|
if (item.isWebAttachment()) {
|
|
parent.setField('accessDate', item.getField('accessDate'));
|
|
parent.setField('url', item.getField('url'));
|
|
}
|
|
var itemID = parent.save();
|
|
item.setSource(itemID);
|
|
item.save();
|
|
Zotero.DB.commitTransaction();
|
|
}
|
|
}
|
|
|
|
|
|
this.renameSelectedAttachmentsFromParents = function () {
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
var items = this.getSelectedItems();
|
|
|
|
for (var i=0; i<items.length; i++) {
|
|
var item = items[i];
|
|
|
|
if (!item.isAttachment() || !item.getSource() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
|
throw('Item ' + itemID + ' is not a child file attachment in ZoteroPane_Local.renameAttachmentFromParent()');
|
|
}
|
|
|
|
var file = item.getFile();
|
|
if (!file) {
|
|
continue;
|
|
}
|
|
|
|
var parentItemID = item.getSource();
|
|
var newName = Zotero.Attachments.getFileBaseNameFromItem(parentItemID);
|
|
|
|
var ext = file.leafName.match(/\.[^\.]+$/);
|
|
if (ext) {
|
|
newName = newName + ext;
|
|
}
|
|
|
|
var renamed = item.renameAttachmentFile(newName);
|
|
if (renamed !== true) {
|
|
Zotero.debug("Could not rename file (" + renamed + ")");
|
|
continue;
|
|
}
|
|
|
|
item.setField('title', newName);
|
|
item.save();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
function relinkAttachment(itemID) {
|
|
if (!this.canEdit()) {
|
|
this.displayCannotEditLibraryMessage();
|
|
return;
|
|
}
|
|
|
|
var item = Zotero.Items.get(itemID);
|
|
if (!item) {
|
|
throw('Item ' + itemID + ' not found in ZoteroPane_Local.relinkAttachment()');
|
|
}
|
|
|
|
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
|
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
|
.createInstance(nsIFilePicker);
|
|
fp.init(window, Zotero.getString('pane.item.attachments.select'), nsIFilePicker.modeOpen);
|
|
|
|
|
|
var file = item.getFile(false, true);
|
|
var dir = Zotero.File.getClosestDirectory(file);
|
|
if (dir) {
|
|
dir.QueryInterface(Components.interfaces.nsILocalFile);
|
|
fp.displayDirectory = dir;
|
|
}
|
|
|
|
fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
|
|
|
|
if (fp.show() == nsIFilePicker.returnOK) {
|
|
var file = fp.file;
|
|
file.QueryInterface(Components.interfaces.nsILocalFile);
|
|
item.relinkAttachmentFile(file);
|
|
}
|
|
}
|
|
|
|
|
|
function reportErrors() {
|
|
var errors = Zotero.getErrors(true);
|
|
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
|
.getService(Components.interfaces.nsIWindowWatcher);
|
|
var data = {
|
|
msg: Zotero.getString('errorReport.followingErrors', Zotero.appName),
|
|
e: errors.join('\n\n'),
|
|
askForSteps: true
|
|
};
|
|
var io = { wrappedJSObject: { Zotero: Zotero, data: data } };
|
|
var win = ww.openWindow(null, "chrome://zotero/content/errorReport.xul",
|
|
"zotero-error-report", "chrome,centerscreen,modal", io);
|
|
}
|
|
|
|
/*
|
|
* Display an error message saying that an error has occurred and Firefox
|
|
* needs to be restarted.
|
|
*
|
|
* If |popup| is TRUE, display in popup progress window; otherwise, display
|
|
* as items pane message
|
|
*/
|
|
function displayErrorMessage(popup) {
|
|
var reportErrorsStr = Zotero.getString('errorReport.reportErrors');
|
|
var reportInstructions =
|
|
Zotero.getString('errorReport.reportInstructions', reportErrorsStr)
|
|
|
|
// Display as popup progress window
|
|
if (popup) {
|
|
var pw = new Zotero.ProgressWindow();
|
|
pw.changeHeadline(Zotero.getString('general.errorHasOccurred'));
|
|
var msg = Zotero.getString('general.restartFirefox', Zotero.appName) + ' '
|
|
+ reportInstructions;
|
|
pw.addDescription(msg);
|
|
pw.show();
|
|
pw.startCloseTimer(8000);
|
|
}
|
|
// Display as items pane message
|
|
else {
|
|
var msg = Zotero.getString('general.errorHasOccurred') + ' '
|
|
+ Zotero.getString('general.restartFirefox', Zotero.appName) + '\n\n'
|
|
+ reportInstructions;
|
|
self.setItemsPaneMessage(msg, true);
|
|
}
|
|
Zotero.debug(msg, 1);
|
|
}
|
|
|
|
this.displayStartupError = function(asPaneMessage) {
|
|
if(!Zotero || !Zotero.initialized) {
|
|
if (Zotero) {
|
|
var errMsg = Zotero.startupError;
|
|
var errFunc = Zotero.startupErrorHandler;
|
|
}
|
|
|
|
if (!errMsg) {
|
|
// Get the stringbundle manually
|
|
var src = 'chrome://zotero/locale/zotero.properties';
|
|
var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1'].
|
|
getService(Components.interfaces.nsILocaleService);
|
|
var appLocale = localeService.getApplicationLocale();
|
|
var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"]
|
|
.getService(Components.interfaces.nsIStringBundleService);
|
|
var stringBundle = stringBundleService.createBundle(src, appLocale);
|
|
|
|
var errMsg = stringBundle.GetStringFromName('startupError');
|
|
}
|
|
|
|
if (errFunc) {
|
|
errFunc();
|
|
}
|
|
else {
|
|
// TODO: Add a better error page/window here with reporting
|
|
// instructions
|
|
// window.loadURI('chrome://zotero/content/error.xul');
|
|
//if(asPaneMessage) {
|
|
// ZoteroPane_Local.setItemsPaneMessage(errMsg, true);
|
|
//} else {
|
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
ps.alert(null, "", errMsg);
|
|
//}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggles Zotero-as-a-tab by passing off the request to the ZoteroOverlay object associated
|
|
* with the present window
|
|
*/
|
|
this.toggleTab = function() {
|
|
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
.getService(Components.interfaces.nsIWindowMediator);
|
|
var browserWindow = wm.getMostRecentWindow("navigator:browser");
|
|
if(browserWindow.ZoteroOverlay) browserWindow.ZoteroOverlay.toggleTab();
|
|
}
|
|
|
|
/**
|
|
* Shows the Zotero pane, making it visible if it is not and switching to the appropriate tab
|
|
* if necessary.
|
|
*/
|
|
this.show = function() {
|
|
if(window.ZoteroOverlay) {
|
|
if(ZoteroOverlay.isTab) {
|
|
ZoteroOverlay.loadZoteroTab();
|
|
} else if(!this.isShowing()) {
|
|
ZoteroOverlay.toggleDisplay();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unserializes zotero-persist elements from preferences
|
|
*/
|
|
this.unserializePersist = function() {
|
|
_unserialized = true;
|
|
var serializedValues = Zotero.Prefs.get("pane.persist");
|
|
if(!serializedValues) return;
|
|
serializedValues = JSON.parse(serializedValues);
|
|
for(var id in serializedValues) {
|
|
var el = document.getElementById(id);
|
|
if(!el) return;
|
|
var elValues = serializedValues[id];
|
|
for(var attr in elValues) {
|
|
el.setAttribute(attr, elValues[attr]);
|
|
}
|
|
}
|
|
|
|
if(this.itemsView) {
|
|
// may not yet be initialized
|
|
try {
|
|
this.itemsView.sort();
|
|
} catch(e) {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Serializes zotero-persist elements to preferences
|
|
*/
|
|
this.serializePersist = function() {
|
|
if(!_unserialized) return;
|
|
var serializedValues = {};
|
|
for each(var el in document.getElementsByAttribute("zotero-persist", "*")) {
|
|
if(!el.getAttribute) continue;
|
|
var id = el.getAttribute("id");
|
|
if(!id) continue;
|
|
var elValues = {};
|
|
for each(var attr in el.getAttribute("zotero-persist").split(/[\s,]+/)) {
|
|
var attrValue = el.getAttribute(attr);
|
|
elValues[attr] = attrValue;
|
|
}
|
|
serializedValues[id] = elValues;
|
|
}
|
|
Zotero.Prefs.set("pane.persist", JSON.stringify(serializedValues));
|
|
}
|
|
|
|
/**
|
|
* Moves around the toolbar when the user moves around the pane
|
|
*/
|
|
this.updateToolbarPosition = function() {
|
|
if(document.getElementById("zotero-pane-stack").hidden) return;
|
|
const PANES = ["collections", "items"];
|
|
for each(var paneName in PANES) {
|
|
var pane = document.getElementById("zotero-"+paneName+"-pane");
|
|
var splitter = document.getElementById("zotero-"+paneName+"-splitter");
|
|
var toolbar = document.getElementById("zotero-"+paneName+"-toolbar");
|
|
|
|
var paneComputedStyle = window.getComputedStyle(pane, null);
|
|
var splitterComputedStyle = window.getComputedStyle(splitter, null);
|
|
|
|
toolbar.style.width = paneComputedStyle.getPropertyValue("width");
|
|
toolbar.style.marginRight = splitterComputedStyle.getPropertyValue("width");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Opens the about dialog
|
|
*/
|
|
this.openAboutDialog = function() {
|
|
window.openDialog('chrome://zotero/content/about.xul', 'about', 'chrome');
|
|
}
|
|
|
|
/**
|
|
* Adds or removes a function to be called when Zotero is reloaded by switching into or out of
|
|
* the connector
|
|
*/
|
|
this.addReloadListener = function(/** @param {Function} **/func) {
|
|
if(_reloadFunctions.indexOf(func) === -1) _reloadFunctions.push(func);
|
|
}
|
|
|
|
/**
|
|
* Implements nsIObserver for Zotero reload
|
|
*/
|
|
var _reloadObserver = {
|
|
/**
|
|
* Called when Zotero is reloaded (i.e., if it is switched into or out of connector mode)
|
|
*/
|
|
"observe":function() {
|
|
Zotero.debug("Reloading Zotero pane");
|
|
for each(var func in _reloadFunctions) func();
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Keep track of which ZoteroPane was local (since ZoteroPane object might get swapped out for a
|
|
* tab's ZoteroPane)
|
|
*/
|
|
var ZoteroPane_Local = ZoteroPane;
|