www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit ecf0f3397c49e975d98ad98d7a240c9b771a572a
parent 520fc2c196d9df44e58a53edfd222090a3bb1b71
Author: Simon Kornblith <simon@simonster.com>
Date:   Sun, 30 Jan 2011 09:44:01 +0000

Zotero as a tab. This may need to be backed out for 2.1 depending on the amount of trouble it generates.


Diffstat:
Mchrome.manifest | 1-
Mchrome/content/zotero-platform/mac/overlay.css | 6++----
Mchrome/content/zotero/advancedSearch.js | 2+-
Mchrome/content/zotero/bindings/noteeditor.xml | 2+-
Mchrome/content/zotero/browser.js | 19++++++++++++++-----
Mchrome/content/zotero/overlay.js | 3559+++++--------------------------------------------------------------------------
Mchrome/content/zotero/overlay.xul | 497++-----------------------------------------------------------------------------
Mchrome/content/zotero/preferences/preferences.js | 16++++++++++++++++
Mchrome/content/zotero/preferences/preferences.xul | 20++++----------------
Mchrome/content/zotero/preferences/preferences_firefox.xul | 33+++++++++++++++++++++++++++++++++
Achrome/content/zotero/standalone.js | 44++++++++++++++++++++++++++++++++++++++++++++
Mchrome/content/zotero/standalone.xul | 20++++++++------------
Achrome/content/zotero/tab.js | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mchrome/content/zotero/tab.xul | 25+++++++++++++------------
Mchrome/content/zotero/timelineInterface.js | 6+++---
Achrome/content/zotero/zoteroPane.js | 3426+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Achrome/content/zotero/zoteroPane.xul | 440+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mchrome/locale/en-US/zotero/preferences.dtd | 3+++
Mchrome/skin/default/zotero/overlay.css | 2--
Mchrome/skin/default/zotero/preferences.css | 4++++
Mchrome/skin/default/zotero/timeline/timeline.html | 6++----
Mchrome/skin/default/zotero/toolbar-fullscreen-bottom.png | 0
Mcomponents/zotero-protocol-handler.js | 28+++-------------------------
Mdefaults/preferences/zotero.js | 1+
24 files changed, 4276 insertions(+), 3952 deletions(-)

diff --git a/chrome.manifest b/chrome.manifest @@ -46,7 +46,6 @@ skin zotero default chrome/skin/default/zotero/ overlay chrome://browser/content/browser.xul chrome://zotero/content/statusBarOverlay.xul appversion<4.0 overlay chrome://browser/content/browser.xul chrome://zotero/content/overlay.xul -overlay chrome://browser/content/browser.xul chrome://zotero/content/itemPane.xul overlay chrome://zotero/content/preferences/preferences.xul chrome://zotero/content/preferences/preferences_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} overlay chrome://zotero/content/preferences/preferences.xul#cite chrome://zotero/content/preferences/preferences_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} diff --git a/chrome/content/zotero-platform/mac/overlay.css b/chrome/content/zotero-platform/mac/overlay.css @@ -36,8 +36,7 @@ } .zotero-tb-button[open="true"], -.zotero-tb-button:hover:active, -#zotero-pane-stack[fullscreenmode="true"] #zotero-tb-fullscreen { +.zotero-tb-button:hover:active { background: url("chrome://zotero/skin/mac/menubutton-end-pressed.png") right center no-repeat; } @@ -63,8 +62,7 @@ } .zotero-tb-button[open="true"] > .toolbarbutton-icon, -.zotero-tb-button:hover:active > .toolbarbutton-icon, -#zotero-pane-stack[fullscreenmode="true"] #zotero-tb-fullscreen > .toolbarbutton-icon { +.zotero-tb-button:hover:active > .toolbarbutton-icon { background: url("chrome://zotero/skin/mac/menubutton-start-pressed.png") left center no-repeat; } diff --git a/chrome/content/zotero/advancedSearch.js b/chrome/content/zotero/advancedSearch.js @@ -153,7 +153,7 @@ var ZoteroAdvancedSearch = new function() { } if (lastWin.document.getElementById('zotero-pane').getAttribute('hidden') == 'true') { - lastWin.ZoteroPane.toggleDisplay(); + lastWin.ZoteroOverlay.toggleDisplay(); } lastWin.ZoteroPane.selectItem(item.getID(), false, true); diff --git a/chrome/content/zotero/bindings/noteeditor.xml b/chrome/content/zotero/bindings/noteeditor.xml @@ -318,7 +318,7 @@ } if (lastWin.document.getElementById('zotero-pane').getAttribute('hidden') == 'true') { - lastWin.ZoteroPane.toggleDisplay(); + lastWin.ZoteroOverlay.toggleDisplay(); // DEBUG: The actions below seem to crash Firefox 2.0.0.1 on OS X if // the Z pane isn't already open, so we don't try diff --git a/chrome/content/zotero/browser.js b/chrome/content/zotero/browser.js @@ -122,13 +122,10 @@ var Zotero_Browser = new function() { } /** - * Scrapes a page (called when the capture icon is clicked) - * - * @param {Integer} libraryID - * @param {Integer} collectionID + * Scrapes a page (called when the capture icon is clicked * @return void */ - function scrapeThisPage(libraryID, collectionID) { + function scrapeThisPage(/*libraryID, collectionID*/) { if (Zotero.locked) { Zotero_Browser.progress.changeHeadline(Zotero.getString("ingester.scrapeError")); var desc = Zotero.localeJoin([ @@ -151,6 +148,18 @@ var Zotero_Browser = new function() { Zotero_Browser.progress.startCloseTimer(8000); return; } + + // get libraryID and collectionID + var libraryID, collectionID; + var pane = ZoteroOverlay.getActiveZoteroPane(); + if(pane) { + libraryID = ZoteroPane.getSelectedLibraryID(); + collectionID = ZoteroPane.getSelectedCollection(true); + } else { + libraryID = collectionID = null; + } + + // translate into specified library and collection _getTabObject(this.tabbrowser.selectedBrowser).translate(libraryID, collectionID); } diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js @@ -26,350 +26,94 @@ /* * This object contains the various functions for the interface */ -var ZoteroPane = new function() +var ZoteroOverlay = new function() { - this.collectionsView = false; - this.itemsView = false; - this.__defineGetter__('loaded', function () _loaded); - - //Privileged methods - this.onLoad = onLoad; - this.onUnload = onUnload; - this.toggleDisplay = toggleDisplay; - this.isShowing = isShowing; - this.fullScreen = fullScreen; - 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.itemSelected = itemSelected; - this.reindexItem = reindexItem; - this.duplicateSelectedItem = duplicateSelectedItem; - this.deleteSelectedCollection = deleteSelectedCollection; - 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; - + const ZOTERO_TAB_URL = "chrome://zotero/content/tab.xul"; const DEFAULT_ZPANE_HEIGHT = 300; - const COLLECTIONS_HEIGHT = 32; // minimum height of the collections pane and toolbar - - var self = this; - var _loaded = false; - var _isStandaloneOrTab = false; - var titlebarcolorState, toolbarCollapseState, titleState; - - // Also needs to be changed in collectionTreeView.js - var _lastViewedFolderRE = /^(?:(C|S|G)([0-9]+)|L)$/; - - /* - * Called when the window is open - */ - function onLoad() - { - _isStandaloneOrTab = Zotero.isStandalone || document.getElementById('zotero-tab'); - - if (!Zotero) return; - if (!Zotero.initialized) { - if(_isStandaloneOrTab) { - _loaded = true; - this.toggleDisplay(); - } - return; - } - - if (Zotero.locked) { - return; - } - _loaded = true; - - Zotero.setFontSize(document.getElementById('zotero-pane')) - - 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'); - } - - if(Zotero.isFx4) { - // hack, since Fx 4 no longer sets active, and the reverse in polarity of the preferred - // property makes things painful to handle otherwise - // DEBUG: remove this once we only support Fx 4 - document.documentElement.setAttribute("active", "true"); - window.addEventListener("focus", - function() { document.documentElement.setAttribute("active", "true") }, false); - window.addEventListener("blur", - function() { document.documentElement.removeAttribute("active") }, false); - } - - //Initialize collections view - this.collectionsView = new Zotero.CollectionTreeView(); - var collectionsTree = document.getElementById('zotero-collections-tree'); - collectionsTree.view = this.collectionsView; - collectionsTree.controllers.appendController(new Zotero.CollectionTreeCommandController(collectionsTree)); - collectionsTree.addEventListener("click", ZoteroPane.onTreeClick, true); - - var itemsTree = document.getElementById('zotero-items-tree'); - itemsTree.controllers.appendController(new Zotero.ItemTreeCommandController(itemsTree)); - itemsTree.addEventListener("click", ZoteroPane.onTreeClick, true); - - this.buildItemTypeMenus(); - - var menu = document.getElementById("contentAreaContextMenu"); - menu.addEventListener("popupshowing", ZoteroPane.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); - } - }); + var toolbarCollapseState, isFx36, showInPref; + + this.isTab = false; + + this.onLoad = function() { + var appInfo = Components.classes["@mozilla.org/xre/app-info;1"] + .getService(Components.interfaces.nsIXULAppInfo); + isFx36 = appInfo.platformVersion.indexOf('1.9') === 0; + + // Open Zotero app tab, if in Fx 4 and requested by pref + showInPref = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService) + .getBranch('extensions.zotero.').getIntPref('showIn'); + this.isTab = showInPref === 2; + if(!isFx36) { + var observerService = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + var zoteroObserver = {"observe":function(subject, topic, data) { + if(subject != window) return; + observerService.removeObserver(this, "browser-delayed-startup-finished"); + Zotero.wait(1000); // there ought to be a better way to determine when the tab + // will have a reasonable URI instead of returning about:blank, + // but I'm not sure what it is + if(showInPref === 2) { + var tabbar = document.getElementById("TabsToolbar"); + if(tabbar && window.getComputedStyle(tabbar).display !== "none") { + // load Zotero as a tab, if it isn't loading by default + ZoteroOverlay.loadZoteroTab(true); + } + } else { + // close Zotero as a tab, in case it was pinned + var zoteroTab = ZoteroOverlay.findZoteroTab(); + if(zoteroTab) gBrowser.removeTab(zoteroTab); + } + }}; + + observerService.addObserver(zoteroObserver, "browser-delayed-startup-finished", false); + } + + // Make Zotero icon visible, if requested + var iconPref = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService) + .getBranch('extensions.zotero.').getIntPref('statusBarIcon'); + + var fx36Icon = document.getElementById('zotero-status-bar-icon'); + var addonBar = document.getElementById('addon-bar'); + + // Status bar in Fx3.6 + if (isFx36) { + var icon = fx36Icon; + } + // In >=Fx4, add to add-on bar + else { + // add Zotero icon + var icon = document.createElement('toolbarbutton'); + icon.id = 'zotero-addon-bar-icon'; + icon.setAttribute('oncommand', 'ZoteroOverlay.toggleDisplay()'); + icon.setAttribute('hidden', true); + addonBar.appendChild(icon); + if (addonBar.collapsed) { + // If no Zotero or icon isn't set to hidden, show add-on bar + if (iconPref != 0) { + setToolbarVisibility(addonBar, true); } - }, 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 () { - var url = "http://zotero.org/start"; - gBrowser.selectedTab = gBrowser.addTab(url); - }, 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.Prefs.get('debugShowDuplicates')) { - document.getElementById('zotero-tb-actions-showDuplicates').hidden = false; - } - - if(_isStandaloneOrTab) { - this.toggleDisplay(true); - this.fullScreen(true); - } else { - // Hide browser chrome on Zotero tab - if(Zotero.isFx4) { - XULBrowserWindow.inContentWhitelist.push("chrome://zotero/content/tab.xul"); - } - } - } - - - this.buildItemTypeMenus = function () { - // - // Create the New Item (+) menu with each item type - // - var addMenu = document.getElementById('zotero-tb-add').firstChild; - var moreMenu = document.getElementById('zotero-tb-add-more'); - - // Remove all nodes, in case we're reloading - 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("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")"); - menuitem.setAttribute("tooltiptext", ""); - menuitem.className = "zotero-tb-add"; - addMenu.insertBefore(menuitem, separator); - } - - - // - // Create submenu for secondary item types - // - - // 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("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")"); - menuitem.setAttribute("tooltiptext", ""); - menuitem.className = "zotero-tb-add"; - moreMenu.appendChild(menuitem); - } - } - - - /* - * Called when the window closes - */ - function onUnload() - { - if (!Zotero || !Zotero.initialized || !_loaded) { - return; - } - - var tagSelector = document.getElementById('zotero-tag-selector'); - tagSelector.unregister(); - - this.collectionsView.unregister(); - if (this.itemsView) - this.itemsView.unregister(); - } - - /* - * Hides/displays the Zotero interface - */ - function toggleDisplay() - { - if (!ZoteroPane.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; + if (Zotero && Zotero.initialized){ + switch (iconPref) { + case 2: + icon.setAttribute('hidden', false); + break; + case 1: + icon.setAttribute('hidden', false); + icon.setAttribute('compact', true); + break; } - ZoteroPane.onLoad(); - } - - var zoteroPane = document.getElementById('zotero-pane-stack'); - var zoteroSplitter = document.getElementById('zotero-splitter') - - if (zoteroPane.getAttribute('hidden') == 'true') { - var isHidden = true; - } - else if (zoteroPane.getAttribute('collapsed') == 'true') { - var isCollapsed = true; - } - - if (isHidden || isCollapsed || _isStandaloneOrTab) { - var makeVisible = true; } - - // If Zotero not initialized, try to get the error handler - // or load the default error page - if (makeVisible && (!Zotero || !Zotero.initialized)) { + else { if (Zotero) { var errMsg = Zotero.startupError; - var errFunc = Zotero.startupErrorHandler; } + // Use defaults if necessary if (!errMsg) { // Get the stringbundle manually var src = 'chrome://zotero/locale/zotero.properties'; @@ -383,41 +127,56 @@ var ZoteroPane = new function() var errMsg = stringBundle.GetStringFromName('startupError'); } - if (errFunc) { - errFunc(); - } - else { - if(Zotero.isStandalone) window.close(); - // TODO: Add a better error page/window here with reporting - // instructions - // window.loadURI('chrome://zotero/content/error.xul'); - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - ps.alert(null, "", errMsg); - } - - return; + icon.setAttribute('tooltiptext', errMsg); + icon.setAttribute('error', 'true'); + icon.setAttribute('hidden', false); } - zoteroSplitter.setAttribute('hidden', !makeVisible); - - // Make sure tags splitter isn't missing for people upgrading from <2.0b7 - if (makeVisible) { - document.getElementById('zotero-tags-splitter').collapsed = false; + // Used for loading pages from upgrade wizard + if (Zotero && Zotero.initialURL) { + setTimeout("gBrowser.selectedTab = gBrowser.addTab(Zotero.initialURL); Zotero.initialURL = null;", 1); } - // Restore fullscreen mode if necessary - if (makeVisible && isFullScreen()) { - this.fullScreen(true); - } + ZoteroPane.init(); - if (zoteroPane.hasAttribute('savedHeight')) { - var savedHeight = zoteroPane.getAttribute('savedHeight'); + // Hide browser chrome on Zotero tab + if(Zotero.isFx4) { + XULBrowserWindow.inContentWhitelist.push("chrome://zotero/content/tab.xul"); } - else { - var savedHeight = DEFAULT_ZPANE_HEIGHT; + } + + this.onUnload = function() { + ZoteroPane.destroy(); + } + + this.onBeforeUnload = function() { + // close Zotero as a tab, so it won't be pinned + var zoteroTab = ZoteroOverlay.findZoteroTab(); + if(zoteroTab) gBrowser.removeTab(zoteroTab); + } + + /** + * Hides/displays the Zotero interface + */ + this.toggleDisplay = function() + { + if(this.isTab) { + // If in separate tab mode, just open the tab + this.loadZoteroTab(); + return; } + var zoteroPane = document.getElementById('zotero-pane-stack'); + var zoteroSplitter = document.getElementById('zotero-splitter'); + var isHidden = zoteroPane.getAttribute('hidden') == 'true'; + var isCollapsed = zoteroPane.getAttribute('collapsed') == 'true'; + + var makeVisible = isHidden || isCollapsed; + + zoteroSplitter.setAttribute('hidden', !makeVisible); + zoteroPane.setAttribute('hidden', false); + zoteroPane.setAttribute('collapsed', false); + /* Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height); Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height')); @@ -425,52 +184,32 @@ var ZoteroPane = new function() Zotero.debug("savedHeight: " + savedHeight); */ - if (makeVisible) { - this.updateTagSelectorSize(); - - var max = document.getElementById('appcontent').boxObject.height - - zoteroSplitter.boxObject.height; - - if (isHidden) { - zoteroPane.setAttribute('height', Math.min(savedHeight, max)); - zoteroPane.setAttribute('hidden', false); + if(makeVisible) { + // Get saved height (makeVisible() may change it) + if (zoteroPane.hasAttribute('savedHeight')) { + var savedHeight = zoteroPane.getAttribute('savedHeight'); } - else if (isCollapsed) { - zoteroPane.setAttribute('height', Math.min(savedHeight, max)); - zoteroPane.setAttribute('collapsed', false); + else { + var savedHeight = DEFAULT_ZPANE_HEIGHT; } - // Focus the quicksearch on pane open - setTimeout("document.getElementById('zotero-tb-search').inputField.select();", 1); - - // Auto-empty trashed items older than a certain number of days - var days = Zotero.Prefs.get('trashAutoEmptyDays'); - if (days) { - var d = new Date(); - var deleted = Zotero.Items.emptyTrash(days); - var d2 = new Date(); - Zotero.debug("Emptied old items from trash in " + (d2 - d) + " ms"); - } + // Restore height + var max = document.getElementById('appcontent').boxObject.height + - zoteroSplitter.boxObject.height; + zoteroPane.setAttribute('height', Math.min(savedHeight, max)); - var d = new Date(); - Zotero.purgeDataObjects(); - var d2 = new Date(); - Zotero.debug("Purged data tables in " + (d2 - d) + " ms"); + // Make visible + ZoteroPane.makeVisible(); - // Auto-sync on pane open - if (Zotero.Prefs.get('sync.autoSync') && Zotero.Sync.Server.enabled - && !Zotero.Sync.Server.syncInProgress && !Zotero.Sync.Storage.syncInProgress) { - if (Zotero.Sync.Server.manualSyncRequired) { - Zotero.debug('Manual sync required -- skipping auto-sync', 4); - } - else { - setTimeout(function () { - Zotero.Sync.Runner.sync(true); - }, 1000); - } + // Restore fullscreen mode if necessary + if (ZoteroPane.isFullScreen()) { + this.fullScreen(true); } - } - else { + + // Make sure tags splitter isn't missing for people upgrading from <2.0b7 + document.getElementById('zotero-tags-splitter').collapsed = false; + } else { + // Collapse pane zoteroPane.setAttribute('collapsed', true); zoteroPane.height = 0; @@ -484,42 +223,13 @@ var ZoteroPane = new function() } } - - function isShowing() { - var zoteroPane = document.getElementById('zotero-pane-stack'); - return zoteroPane.getAttribute('hidden') != 'true' && - zoteroPane.getAttribute('collapsed') != 'true'; - } - - - function fullScreen(set) - { - var zoteroPane = document.getElementById('zotero-pane-stack'); - - if (set != undefined) { - var makeFullScreen = !!set; - } - else { - var makeFullScreen = zoteroPane.getAttribute('fullscreenmode') != 'true'; - } - - // Turn Z-pane flex on to stretch to window in full-screen, but off otherwise so persist works - zoteroPane.setAttribute('flex', makeFullScreen ? "1" : "0"); - document.getElementById('content').setAttribute('collapsed', makeFullScreen); - document.getElementById('zotero-splitter').setAttribute('hidden', makeFullScreen); - - zoteroPane.setAttribute('fullscreenmode', makeFullScreen); - _setFullWindowMode(makeFullScreen); - } - /** * Hides or shows navigation toolbars * @param set {Boolean} Whether navigation toolbars should be hidden or shown */ function _setFullWindowMode(set) { // hide or show navigation toolbars - if(_isStandaloneOrTab) return; - + if(!getNavToolbox) return; var toolbox = getNavToolbox(); if(set) { // the below would be a good thing to do if the whole title bar (and not just the center @@ -554,2966 +264,63 @@ var ZoteroPane = new function() } } - function isFullScreen() { - return document.getElementById('zotero-pane-stack').getAttribute('fullscreenmode') == 'true'; - } - - - /* - * Trigger actions based on keyboard shortcuts + /** + * Determines whether there is an open Zotero tab */ - 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.setHighlightedRowsCallback - }, 225, Components.interfaces.nsITimer.TYPE_ONE_SHOT); - } - else if ((Zotero.isWin && event.ctrlKey) || - (!Zotero.isWin && event.altKey)) { - if (this.highlightTimer) { - this.highlightTimer.cancel(); - this.highlightTimer = null; - } - ZoteroPane.collectionsView.setHighlightedRows(); - } - - 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); - } - ZoteroPane.toggleDisplay() + this.findZoteroTab = function() { + // Look for an existing tab + var tab = false; + var numTabs = gBrowser.browsers.length; + for(var index = 0; index < numTabs; index++) { + var currentBrowser = gBrowser.getBrowserAtIndex(index); + if(ZOTERO_TAB_URL == currentBrowser.currentURI.spec) { + tab = gBrowser.tabs[index]; break; - case 'library': - document.getElementById('zotero-collections-tree').focus(); - ZoteroPane.collectionsView.selection.select(0); - break; - case 'quicksearch': - document.getElementById('zotero-tb-search').select(); - break; - case 'newItem': - ZoteroPane.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': - // Use key that's not the modifier as the popup toggle - ZoteroPane.newNote(useShift ? event.altKey : event.shiftKey); - break; - case 'toggleTagSelector': - ZoteroPane.toggleTagSelector(); - break; - case 'toggleFullscreen': - ZoteroPane.fullScreen(); - break; - case 'copySelectedItemCitationsToClipboard': - ZoteroPane.copySelectedItemsToClipboard(true) - break; - case 'copySelectedItemsToClipboard': - ZoteroPane.copySelectedItemsToClipboard(); - break; - case 'importFromClipboard': - Zotero_File_Interface.importFromClipboard(); - break; - default: - throw ('Command "' + command + '" not found in ZoteroPane.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.collectionsView.setHighlightedRows(); - } - } - } - - - /* - * Highlights collections containing selected items on Ctrl (Win) or - * Option/Alt (Mac/Linux) press - */ - function setHighlightedRowsCallback() { - var itemIDs = ZoteroPane.getSelectedItems(true); - if (itemIDs && itemIDs.length) { - var collectionIDs = Zotero.Collections.getCollectionsContainingItems(itemIDs, true); - if (collectionIDs) { - ZoteroPane.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.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.deleteSelectedItems(force); - event.preventDefault(); - return; - } - } - } - - - /* - * Create a new item - * - * _data_ is an optional object with field:value for itemData - */ - function newItem(typeID, data, row) - { - if (!Zotero.stateCheck()) { - this.displayErrorMessage(true); - return false; - } - - // Currently selected row - if (row === undefined) { - row = this.collectionsView.selection.currentIndex; - } - - if (!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; - } - - 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); - } - - //set to Info tab - document.getElementById('zotero-view-item').selectedIndex = 0; - - this.selectItem(itemID); - - return Zotero.Items.get(itemID); + return tab; } - - function newCollection(parent) - { - if (!Zotero.stateCheck()) { - this.displayErrorMessage(true); - return false; - } - - if (!this.canEdit()) { - 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; - } + /** + * Loads the Zotero tab, or adds a new tab if no tab yet exists + * @param {Boolean} background Whether the Zotero tab should be loaded in the background + */ + this.loadZoteroTab = function(background) { + var tab = this.findZoteroTab(); - var collection = new Zotero.Collection; - collection.libraryID = this.getSelectedLibraryID(); - collection.name = newName.value; - collection.parent = parent; - collection.save(); + // If no existing tab, add a new tab + if(!tab) tab = gBrowser.addTab(ZOTERO_TAB_URL); + // Pin tab + if(!isFx36) gBrowser.pinTab(tab); + // If requested, activate tab + if(!background) gBrowser.selectedTab = tab; } - - this.newGroup = function () { - if (this.isFullScreen()) { + /** + * Toggle between Zotero as a tab and Zotero as a pane + */ + this.toggleTab = function() { + var tab = this.findZoteroTab(); + if(tab) { // Zotero is running in a tab + // don't do anything if Zotero tab is the only tab + if(tab && gBrowser.tabs.length === 1) return; + + // otherwise, close Zotero tab and open Zotero pane + gBrowser.removeTab(tab); + this.isTab = false; + this.toggleDisplay(); + } else { // Zotero is running in the pane + // close Zotero pane this.toggleDisplay(); - } - 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.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(){ - 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; - } - - document.getElementById('zotero-tb-search').value = ""; - - 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()); - itemgroup.showDuplicates = false; - - 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(); - } - - if (itemgroup.isLibrary()) { - Zotero.Prefs.set('lastViewedFolder', 'L'); - } - if (itemgroup.isCollection()) { - Zotero.Prefs.set('lastViewedFolder', 'C' + itemgroup.ref.id); - } - else if (itemgroup.isSearch()) { - Zotero.Prefs.set('lastViewedFolder', 'S' + itemgroup.ref.id); - } - else if (itemgroup.isGroup()) { - Zotero.Prefs.set('lastViewedFolder', 'G' + itemgroup.ref.id); - } - } - - - this.showDuplicates = function () { - if (this.collectionsView.selection.count == 1 && this.collectionsView.selection.currentIndex != -1) { - var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); - itemGroup.showDuplicates = true; - - try { - Zotero.UnresponsiveScriptIndicator.disable(); - this.itemsView.refresh(); - } - finally { - Zotero.UnresponsiveScriptIndicator.enable(); - } - } - } - - this.getItemGroup = function () { - return this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); - } - - - function itemSelected() - { - if (!Zotero.stateCheck()) { - this.displayErrorMessage(); - return; - } - - // Display restore button if items selected in Trash - if (this.itemsView && 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'); - - // Single item selected - if (this.itemsView && this.itemsView.selection.count == 1 && this.itemsView.selection.currentIndex != -1) - { - var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex); - - if(item.ref.isNote()) { - var noteEditor = document.getElementById('zotero-note-editor'); - noteEditor.mode = this.collectionsView.editable ? 'edit' : 'view'; - - // 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 (!noteEditor.item || noteEditor.item.id != item.ref.id) { - noteEditor.disableUndo(); - } - noteEditor.parent = null; - noteEditor.item = item.ref; - - noteEditor.enableUndo(); - - var viewButton = document.getElementById('zotero-view-note-button'); - if (this.collectionsView.editable) { - viewButton.hidden = false; - viewButton.setAttribute('noteID', item.ref.id); - if (item.ref.getSource()) { - viewButton.setAttribute('sourceID', item.ref.getSource()); - } - else { - viewButton.removeAttribute('sourceID'); - } - } - else { - viewButton.hidden = true; - } - - document.getElementById('zotero-item-pane-content').selectedIndex = 2; - } - - else if(item.ref.isAttachment()) { - var attachmentBox = document.getElementById('zotero-attachment-box'); - attachmentBox.mode = this.collectionsView.editable ? 'edit' : 'view'; - attachmentBox.item = item.ref; - - document.getElementById('zotero-item-pane-content').selectedIndex = 3; - } - - // Regular item - else { - var isCommons = this.getItemGroup().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.ref, null, pane); - tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex; - } - else { - ZoteroItemPane.viewItem(item.ref, 'view', pane); - tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex; - } - } - } - // Zero or multiple items selected - else { - document.getElementById('zotero-item-pane-content').selectedIndex = 0; - - var label = document.getElementById('zotero-view-selected-label'); - - if (this.itemsView && this.itemsView.selection.count) { - label.value = Zotero.getString('pane.item.selected.multiple', this.itemsView.selection.count); - } - else { - label.value = Zotero.getString('pane.item.selected.zero'); - } - } - } - - - /** - * 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.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.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.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.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); - 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.deleteSelectedItem() is deprecated -- use ZoteroPane.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 - */ - this.deleteSelectedItems = function (force) { - 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()) { - // In library, don't prompt if meta key was pressed - var prompt = force ? false : toTrash; - } - else if (itemGroup.isCollection()) { - // In collection, only prompt if trashing - var prompt = force ? (itemGroup.isWithinGroup() ? toDelete : toTrash) : false; - } - // This should be changed if/when groups get trash - else if (itemGroup.isGroup()) { - var prompt = toDelete; - } - else if (itemGroup.isSearch()) { - if (!force) { - return; - } - var prompt = toTrash; - } - // Do nothing in share views - else if (itemGroup.isShare()) { - return; - } - else if (itemGroup.isBucket()) { - var prompt = toDelete; - } - // 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; - } - - 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); - } - } - - function deleteSelectedCollection() - { - if (!this.canEdit()) { - this.displayCannotEditLibraryMessage(); - return; - } - - if (this.collectionsView.selection.count == 1) { - var row = - this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); - - if (row.isCollection()) - { - if (confirm(Zotero.getString('pane.collections.delete'))) - { - this.collectionsView.deleteSelection(); - } - } - else if (row.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 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(); - Zotero.purgeDataObjects(true); - } - } - - this.createCommonsBucket = function () { - var self = this; - - Zotero.Commons.getBuckets(function () { - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - - var invalid = false; - - while (true) { - if (invalid) { - // TODO: localize - ps.alert(null, "", "Invalid title. Please try again."); - invalid = false; - } - - var newTitle = {}; - var result = prompt.prompt( - null, - "", - // TODO: localize - "Enter a title for this Zotero Commons collection:", - newTitle, - "", {} - ); - - if (!result) { - return; - } - - var title = Zotero.Utilities.trim(newTitle.value); - - if (!title) { - return; - } - - if (!Zotero.Commons.isValidBucketTitle(title)) { - invalid = true; - continue; - } - - break; - } - - invalid = false; - - var origName = title.toLowerCase(); - origName = origName.replace(/[^a-z0-9 ._-]/g, ''); - origName = origName.replace(/ /g, '-'); - origName = origName.substr(0, 32); - - while (true) { - if (invalid) { - // TODO: localize - var msg = "'" + testName + "' is not a valid Zotero Commons collection identifier.\n\n" - + "Collection identifiers can contain basic Latin letters, numbers, " - + "hyphens, and underscores and must be no longer than 32 characters. " - + "Spaces and other characters are not allowed."; - ps.alert(null, "", msg); - invalid = false; - } - - var newName = { value: origName }; - var result = ps.prompt( - null, - "", - // TODO: localize - "Enter an identifier for the collection '" + title + "'.\n\n" - + "The identifier will form the collection's URL on archive.org. " - + "Identifiers can contain basic Latin letters, numbers, hyphens, and underscores " - + "and must be no longer than 32 characters. " - + "Spaces and other characters are not allowed.\n\n" - + '"' + Zotero.Commons.userNameSlug + '-" ' - + "will be automatically prepended to your entry.", - newName, - "", {} - ); - - if (!result) { - return; - } - - var name = Zotero.Utilities.trim(newName.value); - - if (!name) { - return; - } - - var testName = Zotero.Commons.userNameSlug + '-' + name; - if (!Zotero.Commons.isValidBucketName(testName)) { - invalid = true; - continue; - } - - break; - } - - // TODO: localize - var progressWin = new Zotero.ProgressWindow(); - progressWin.changeHeadline("Creating Zotero Commons Collection"); - var icon = self.collectionsView.getImageSrc(self.collectionsView.selection.currentIndex); - progressWin.addLines(title, icon) - progressWin.show(); - - Zotero.Commons.createBucket(name, title, function () { - progressWin.startCloseTimer(); - }); - }); - } - - - this.refreshCommonsBucket = function() { - if (!this.collectionsView - || !this.collectionsView.selection - || this.collectionsView.selection.count != 1 - || this.collectionsView.selection.currentIndex == -1) { - return false; - } - - var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); - if (itemGroup && itemGroup.isBucket()) { - var self = this; - itemGroup.ref.refreshItems(function () { - self.itemsView.refresh(); - self.itemsView.sort(); - - // On a manual refresh, also check for new OCRed files - //Zotero.Commons.syncFiles(); - }); - } - } - - 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 promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - ps.alert(null, "", Zotero.getString("fileInterface.noReferencesError")); - return; - } - - var url = window.content.location.href; - 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 = ''; - search.doCommand('cmd_zotero_search'); - } - } - - - function handleSearchKeypress(textbox, event) { - // Events that turn find-as-you-type on - if (event.keyCode == event.DOM_VK_ESCAPE) { - textbox.value = ''; - ZoteroPane.setItemsPaneMessage(Zotero.getString('searchInProgress')); - setTimeout("ZoteroPane.search(); ZoteroPane.clearItemsPaneMessage();", 1); - } - else if (event.keyCode == event.DOM_VK_RETURN || event.keyCode == event.DOM_VK_ENTER) { - ZoteroPane.setItemsPaneMessage(Zotero.getString('searchInProgress')); - setTimeout("ZoteroPane.search(true); ZoteroPane.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.setItemsPaneMessage(Zotero.getString('searchInProgress')); - setTimeout("ZoteroPane.search(); ZoteroPane.clearItemsPaneMessage();", 1); - } - else if (textbox.value.indexOf('"') != -1) { - ZoteroPane.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.selectItem()"); - return false; - } - - var currentLibraryID = this.getSelectedLibraryID(); - // If in a different library - if (item.libraryID != currentLibraryID) { - this.collectionsView.selectLibrary(item.libraryID); - } - // Force switch to library view - else if (!this.itemsView._itemGroup.isLibrary() && inLibrary) { - this.collectionsView.selectLibrary(item.libraryID); - } - - var selected = this.itemsView.selectItem(itemID, expand); - if (!selected) { - this.collectionsView.selectLibrary(item.libraryID); - this.itemsView.selectItem(itemID, expand); - } - - return true; - } - - - this.getSelectedLibraryID = function () { - var group = this.getSelectedGroup(); - if (group) { - return group.libraryID; - } - var collection = this.getSelectedCollection(); - if (collection) { - return collection.libraryID; - } - return null; - } - - - function getSelectedCollection(asID) { - if (this.collectionsView) { - return this.collectionsView.getSelectedCollection(asID); - } - return 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.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); - 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 menu = document.getElementById('zotero-collectionmenu'); - var m = { - newCollection: 0, - newSavedSearch: 1, - newSubcollection: 2, - sep1: 3, - editSelectedCollection: 4, - removeCollection: 5, - sep2: 6, - exportCollection: 7, - createBibCollection: 8, - exportFile: 9, - loadReport: 10, - emptyTrash: 11, - createCommonsBucket: 12, - refreshCommonsBucket: 13 - }; - - var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); - - var enable = [], disable = [], show = []; - - // Collection - 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>0) { - enable = s; - } - else if (!this.collectionsView.isContainerEmpty(this.collectionsView.selection.currentIndex)) { - enable = [m.exportCollection]; - 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')); - } - // Saved Search - else if (itemGroup.isSearch()) { - show = [ - m.editSelectedCollection, - m.removeCollection, - m.sep2, - m.exportCollection, - m.createBibCollection, - m.loadReport - ]; - - var s = [m.exportCollection, m.createBibCollection, m.loadReport]; - if (this.itemsView.rowCount>0) { - enable = s; - } - else { - disable = s; - } - - // Adjust labels - menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.edit.savedSearch')); - menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.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')); - } - // Trash - else if (itemGroup.isTrash()) { - show = [m.emptyTrash]; - } - else if (itemGroup.isHeader()) { - if (itemGroup.ref.id == 'commons-header') { - show = [m.createCommonsBucket]; - } - } - else if (itemGroup.isBucket()) { - show = [m.refreshCommonsBucket]; - } - // Group - else if (itemGroup.isGroup()) { - show = [m.newCollection, m.newSavedSearch]; - } - // Library - else - { - show = [m.newCollection, m.newSavedSearch, m.sep1, m.exportFile]; - } - - // Disable some actions if user doesn't have write access - var s = [m.editSelectedCollection, m.removeCollection, m.newCollection, m.newSavedSearch, m.newSubcollection]; - if (itemGroup.isWithinGroup() && !itemGroup.editable) { - disable = disable.concat(s); - } - else { - enable = enable.concat(s); - } - - for (var i in disable) - { - menu.childNodes[disable[i]].setAttribute('disabled', true); - } - - for (var i in enable) - { - menu.childNodes[enable[i]].setAttribute('disabled', false); - } - - // Hide all items by default - for each(var pos in m) { - menu.childNodes[pos].setAttribute('hidden', true); - } - - for (var i in show) - { - menu.childNodes[show[i]].setAttribute('hidden', false); - } - } - - function buildItemContextMenu() - { - var m = { - showInLibrary: 0, - sep1: 1, - addNote: 2, - addAttachments: 3, - sep2: 4, - duplicateItem: 5, - deleteItem: 6, - deleteFromLibrary: 7, - sep3: 8, - exportItems: 9, - createBib: 10, - loadReport: 11, - sep4: 12, - createParent: 13, - recognizePDF: 14, - renameAttachments: 15, - reindexItem: 16 - }; - - var menu = document.getElementById('zotero-itemmenu'); - - var enable = [], disable = [], show = [], hide = [], multiple = ''; - - if (!this.itemsView) { - return; - } - - if (this.itemsView.selection.count > 0) { - var itemGroup = this.itemsView._itemGroup; - - enable.push(m.showInLibrary, m.addNote, m.addAttachments, - m.sep2, m.duplicateItem, m.deleteItem, m.deleteFromLibrary, - m.exportItems, m.createBib, m.loadReport); - - // Multiple items selected - if (this.itemsView.selection.count > 1) { - var multiple = '.multiple'; - hide.push(m.showInLibrary, m.sep1, m.addNote, m.addAttachments, - m.sep2, m.duplicateItem); - - // If all items can be reindexed, or all items can be recognized, show option - var items = this.getSelectedItems(); - var canIndex = true; - var canRecognize = true; - if (!Zotero.Fulltext.pdfConverterIsRegistered()) { - canIndex = false; - } - for (var i=0; i<items.length; i++) { - if (canIndex && !Zotero.Fulltext.canReindex(items[i].id)) { - canIndex = false; - } - if (canRecognize && !Zotero_RecognizePDF.canRecognize(items[i])) { - canRecognize = false; - } - if (!canIndex && !canRecognize) { - break; - } - } - if (canIndex) { - show.push(m.reindexItem); - } - else { - hide.push(m.reindexItem); - } - if (canRecognize) { - show.push(m.recognizePDF); - hide.push(m.createParent); - } - else { - hide.push(m.recognizePDF); - - var canCreateParent = true; - for (var i=0; i<items.length; i++) { - if (!items[i].isTopLevelItem() || items[i].isRegularItem() || Zotero_RecognizePDF.canRecognize(items[i])) { - canCreateParent = false; - break; - } - } - if (canCreateParent) { - show.push(m.createParent); - } - else { - hide.push(m.createParent); - } - } - - // If all items are child attachments, show rename option - var canRename = true; - for (var i=0; i<items.length; i++) { - var item = items[i]; - // Same check as in rename function - if (!item.isAttachment() || !item.getSource() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) { - canRename = false; - break; - } - } - if (canRename) { - show.push(m.renameAttachments); - } - else { - hide.push(m.renameAttachments); - } - - // Add in attachment separator - if (canCreateParent || canRecognize || canRename || canIndex) { - show.push(m.sep4); - } - else { - hide.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) { - var d = [m.deleteFromLibrary, m.createParent, m.renameAttachments]; - for each(var val in d) { - disable.push(val); - var index = enable.indexOf(val); - if (index != -1) { - enable.splice(index, 1); - } - } - } - } - } - - // Single item selected - else - { - var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex).ref; - var itemID = item.id; - menu.setAttribute('itemID', itemID); - - // Show in Library - if (!itemGroup.isLibrary() && !itemGroup.isWithinGroup()) { - show.push(m.showInLibrary, m.sep1); - } - else { - hide.push(m.showInLibrary, m.sep1); - } - - if (item.isRegularItem()) - { - show.push(m.addNote, m.addAttachments, m.sep2); - } - else - { - hide.push(m.addNote, m.addAttachments, m.sep2); - } - - if (item.isAttachment()) { - var showSep4 = false; - hide.push(m.duplicateItem); - - if (Zotero_RecognizePDF.canRecognize(item)) { - show.push(m.recognizePDF); - hide.push(m.createParent); - showSep4 = true; - } - else { - hide.push(m.recognizePDF); - - // If not a PDF, allow parent item creation - if (item.isTopLevelItem()) { - show.push(m.createParent); - showSep4 = true; - } - else { - hide.push(m.createParent); - } - } - - // Attachment rename option - if (item.getSource() && item.attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) { - show.push(m.renameAttachments); - showSep4 = true; - } - else { - hide.push(m.renameAttachments); - } - - if (showSep4) { - show.push(m.sep4); - } - else { - hide.push(m.sep4); - } - - // If not linked URL, show reindex line - if (Zotero.Fulltext.pdfConverterIsRegistered() - && Zotero.Fulltext.canReindex(item.id)) { - show.push(m.reindexItem); - showSep4 = true; - } - else { - hide.push(m.reindexItem); - } - } - else { - if (item.isNote() && item.isTopLevelItem()) { - show.push(m.sep4, m.createParent); - } - else { - hide.push(m.sep4, m.createParent); - } - - show.push(m.duplicateItem); - hide.push(m.recognizePDF, m.renameAttachments, m.reindexItem); - } - - // 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); - var index = enable.indexOf(val); - if (index != -1) { - enable.splice(index, 1); - } - } - } - } - } - // No items selected - else - { - // Show in Library - if (!itemGroup.isLibrary()) { - show.push(m.showInLibrary, m.sep1); - } - else { - hide.push(m.showInLibrary, m.sep1); - } - - disable.push(m.showInLibrary, m.duplicateItem, m.deleteItem, - m.deleteFromLibrary, m.exportItems, m.createBib, m.loadReport); - hide.push(m.addNote, m.addAttachments, m.sep2, m.sep4, m.reindexItem, - m.createParent, m.recognizePDF, m.renameAttachments); - } - - // 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': - continue; - } - } - disable.push(m[i]); - var index = enable.indexOf(m[i]); - if (index != -1) { - enable.splice(index, 1); - } - } - } - - // Remove from collection - if (this.itemsView._itemGroup.isCollection() && !(item && item.getSource())) - { - menu.childNodes[m.deleteItem].setAttribute('label', Zotero.getString('pane.items.menu.remove' + multiple)); - show.push(m.deleteItem); - } - else - { - hide.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)); - - for (var i in disable) - { - menu.childNodes[disable[i]].setAttribute('disabled', true); - } - - for (var i in enable) - { - menu.childNodes[enable[i]].setAttribute('disabled', false); - } - - for (var i in hide) - { - menu.childNodes[hide[i]].setAttribute('hidden', true); - } - - for (var i in show) - { - menu.childNodes[show[i]].setAttribute('hidden', false); - } - } - - - // Adapted from: http://www.xulplanet.com/references/elemref/ref_tree.html#cmnote-9 - this.onTreeClick = function (event) { - // We only care about primary button double and triple clicks - if (!event || (event.detail != 2 && event.detail != 3) || event.button != 0) { - return; - } - - var t = event.originalTarget; - - if (t.localName != 'treechildren') { - return; - } - - var tree = t.parentNode; - - var row = {}, col = {}, obj = {}; - tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj); - - // 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; - } - - var itemGroup = ZoteroPane.collectionsView._getItemAtRow(tree.view.selection.currentIndex); - if (itemGroup.isLibrary()) { - var uri = Zotero.URI.getCurrentUserLibraryURI(); - if (uri) { - ZoteroPane.loadURI(uri); - event.stopPropagation(); - } - return; - } - - if (itemGroup.isSearch()) { - ZoteroPane.editSelectedCollection(); - return; - } - - if (itemGroup.isGroup()) { - var uri = Zotero.URI.getGroupURI(itemGroup.ref, true); - ZoteroPane.loadURI(uri); - event.stopPropagation(); - return; - } - - if (itemGroup.isHeader()) { - if (itemGroup.ref.id == 'group-libraries-header') { - var uri = Zotero.URI.getGroupsURL(); - ZoteroPane.loadURI(uri); - event.stopPropagation(); - } - return; - } - - if (itemGroup.isBucket()) { - ZoteroPane.loadURI(itemGroup.ref.uri); - event.stopPropagation(); - } - } - else if (tree.id == 'zotero-items-tree') { - var viewOnDoubleClick = Zotero.Prefs.get('viewOnDoubleClick'); - - // Expand/collapse on triple-click - if (viewOnDoubleClick) { - 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.getSelectedItems()[0]; - if (item) { - if (item.isRegularItem()) { - // Double-click on Commons item should load IA page - var itemGroup = ZoteroPane.collectionsView._getItemAtRow( - ZoteroPane.collectionsView.selection.currentIndex - ); - - if (itemGroup.isBucket()) { - var uri = itemGroup.ref.getItemURI(item); - ZoteroPane.loadURI(uri); - event.stopPropagation(); - return; - } - - if (!viewOnDoubleClick) { - return; - } - - var uri = Components.classes["@mozilla.org/network/standard-url;1"]. - createInstance(Components.interfaces.nsIURI); - var snapID = item.getBestAttachment(); - if (snapID) { - spec = Zotero.Items.get(snapID).getLocalFileURL(); - if (spec) { - uri.spec = spec; - if (uri.scheme && uri.scheme == 'file') { - ZoteroPane.viewAttachment(snapID, event); - return; - } - } - } - - 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.loadURI(uri); - } - } - else if (item.isNote()) { - if (!ZoteroPane.collectionsView.editable) { - return; - } - document.getElementById('zotero-view-note-button').doCommand(); - } - else if (item.isAttachment()) { - ZoteroPane.viewSelectedAttachment(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(uri, event, data) { - // Ignore javascript: and data: URIs - if (uri.match(/^(javascript|data):/)) { - return; - } - - if (Zotero.isStandalone && uri.match(/^https?/)) { - var io = Components.classes['@mozilla.org/network/io-service;1'] - .getService(Components.interfaces.nsIIOService); - var uri = io.newURI(uri, 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); - return; - } - - // Open in new tab - if (event && (event.metaKey || (!Zotero.isMac && event.ctrlKey))) { - var tab = gBrowser.addTab(uri); - var browser = gBrowser.getBrowserForTab(tab); - - if (event.shiftKey) { - gBrowser.selectedTab = tab; - } - } - else if (event && event.shiftKey) { - window.open(uri, "zotero-loaded-page", - "menubar=yes,location=yes,toolbar=yes,personalbar=yes,resizable=yes,scrollbars=yes,status=yes"); - } - 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; - } - - - // Updates browser context menu options - function contextPopupShowing() - { - if (!Zotero.Prefs.get('browserContentContextMenu')) { - return; - } - - var menuitem = document.getElementById("zotero-context-add-to-current-note"); - var showing = false; - if (menuitem){ - var items = ZoteroPane.getSelectedItems(); - if (ZoteroPane.itemsView.selection && ZoteroPane.itemsView.selection.count==1 - && items[0] && items[0].isNote() - && window.gContextMenu.isTextSelected) - { - menuitem.hidden = false; - showing = true; - } - else - { - menuitem.hidden = true; - } - } - - var menuitem = document.getElementById("zotero-context-add-to-new-note"); - if (menuitem){ - if (window.gContextMenu.isTextSelected) - { - menuitem.hidden = false; - showing = true; - } - else - { - menuitem.hidden = true; - } - } - - var menuitem = document.getElementById("zotero-context-save-link-as-item"); - if (menuitem) { - if (window.gContextMenu.onLink) { - menuitem.hidden = false; - showing = true; - } - 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; - showing = true; - } - 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'); - menu.hidden = !showing; - 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'); - } - - - function addAttachmentFromDialog(link, id) - { - if (!this.canEdit()) { - this.displayCannotEditLibraryMessage(); - return; - } - - var itemGroup = ZoteroPane.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 (!this.canEdit(row)) { - this.displayCannotEditLibraryMessage(); - return; - } - - return this.addItemFromDocument(window.content.document, itemType, saveSnapshot, row); - } - - - /** - * @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) { - var progressWin = new Zotero.ProgressWindow(); - progressWin.changeHeadline(Zotero.getString('ingester.scraping')); - var icon = 'chrome://zotero/skin/treeitem-webpage.png'; - progressWin.addLines(doc.title, icon) - progressWin.show(); - progressWin.startCloseTimer(); - - // 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) { - 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) { - row = this.collectionsView.selection.currentIndex; - } - - if (!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); - filesEditable = group.filesEditable; - } - else { - 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 (url == window.content.document.location.href) { - return this.addItemFromPage(itemType, saveSnapshot, row); - } - - var self = this; - - Zotero.MIME.getMIMETypeFromURL(url, function (mimeType, hasNativeHandler) { - // If native type, save using a hidden browser - if (hasNativeHandler) { - var processor = function (doc) { - ZoteroPane.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.displayErrorMessage(true); - return false; - } - - // Currently selected row - if (row === undefined) { - row = ZoteroPane.collectionsView.selection.currentIndex; - } - - if (!ZoteroPane.canEdit(row)) { - ZoteroPane.displayCannotEditLibraryMessage(); - return; - } - - if (row !== undefined) { - var itemGroup = ZoteroPane.collectionsView._getItemAtRow(row); - var libraryID = itemGroup.ref.libraryID; - } - else { - var libraryID = null; - var itemGroup = null; - } - // - // - // - - if (!ZoteroPane.canEditFiles(row)) { - ZoteroPane.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.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.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); - } - } - - - function viewAttachment(itemID, event, noLocateOnMissing) { - var attachment = Zotero.Items.get(itemID); - if (!attachment.isAttachment()) { - throw ("Item " + itemID + " is not an attachment in ZoteroPane.viewAttachment()"); - } - - if (attachment.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) { - this.loadURI(attachment.getField('url'), event); - return; - } - - var file = attachment.getFile(); - if (file) { - var mimeType = attachment.getAttachmentMIMEType(); - // If no MIME type specified, try to detect again (I guess in case - // we've gotten smarter since the file was imported?) - if (!mimeType) { - var mimeType = Zotero.MIME.getMIMETypeFromFile(file); - var ext = Zotero.File.getExtension(file); - - // TODO: update DB with new info - } - var ext = Zotero.File.getExtension(file); - var isNative = Zotero.MIME.hasNativeHandler(mimeType, ext); - var internal = Zotero.MIME.hasInternalHandler(mimeType, ext); - - if (isNative || - (internal && !Zotero.Prefs.get('launchNonNativeFiles'))) { - - var url = 'zotero://attachment/' + itemID + '/'; - this.loadURI(url, event, { attachmentID: itemID}); - } - else { - var fileURL = attachment.getLocalFileURL(); - - // Some platforms don't have nsILocalFile.launch, so we just load it and - // let the Firefox external helper app window handle it - try { - file.launch(); - } - catch (e) { - window.loadURI(fileURL); - } - } - } - else { - this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing); - } - } - - - 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), - // "double-click" the parent directory - try { - var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile); - parent.launch(); - } - // If launch also fails, try the OS handler - catch (e) { - var uri = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Components.interfaces.nsIIOService). - newFileURI(parent); - var protocolService = - Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]. - getService(Components.interfaces.nsIExternalProtocolService); - protocolService.loadUrl(uri); - } - } - } - else { - this.showAttachmentNotFoundDialog(attachment.id, noLocateOnMissing) - } - } - } - - - /** - * Test if the user can edit the currently selected library/collection, - * and display an error if not - * - * @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 currently selected library/collection, - * and display an error if not - * - * @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, "", "You cannot make changes to the currently selected library."); - } - - - this.displayCannotEditLibraryFilesMessage = function () { - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - ps.alert(null, "", "You cannot add files to the currently selected library."); - } - - - 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.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.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.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.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') + ' ' - + 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') + '\n\n' - + reportInstructions; - self.setItemsPaneMessage(msg, true); + // open Zotero tab + this.isTab = true; + this.loadZoteroTab(); } - Zotero.debug(msg, 1); } } -window.addEventListener("load", function(e) { ZoteroPane.onLoad(e); }, false); -window.addEventListener("unload", function(e) { ZoteroPane.onUnload(e); }, false); +window.addEventListener("load", function(e) { ZoteroOverlay.onLoad(e); }, false); +window.addEventListener("unload", function(e) { ZoteroOverlay.onUnload(e); }, false); +window.addEventListener("beforeunload", function(e) { ZoteroOverlay.onBeforeUnload(e); }, false); diff --git a/chrome/content/zotero/overlay.xul b/chrome/content/zotero/overlay.xul @@ -27,6 +27,8 @@ <?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?> <?xml-stylesheet href="chrome://zotero-platform/content/overlay.css" type="text/css"?> +<?xul-overlay href="chrome://zotero/content/zoteroPane.xul"?> +<?xul-overlay href="chrome://zotero/content/itemPane.xul"?> <!DOCTYPE overlay [ <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD; @@ -37,420 +39,21 @@ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <!-- Include the global XPCOM object --> - <script src="include.js"/> - <script src="overlay.js"/> - <script src="fileInterface.js"/> - <script src="reportInterface.js"/> - <script src="timelineInterface.js"/> - <script src="recognizePDF.js"/> - <script src="browser.js"/> - - <commandset id="mainCommandSet"> - <command id="cmd_zotero_search" oncommand="ZoteroPane.search();"/> - <command id="cmd_zotero_reportErrors" oncommand="ZoteroPane.reportErrors();"/> - </commandset> <toolbarpalette id="BrowserToolbarPalette"> <toolbarbutton id="scholar-toolbar-button"/> <!-- May be necessary to keep pre-1.0b2 installs from breaking --> <toolbarbutton id="zotero-toolbar-button" class="toolbarbutton-1" label="Zotero" - oncommand="ZoteroPane.toggleDisplay();"/> + oncommand="ZoteroOverlay.toggleDisplay();"/> </toolbarpalette> - <popup id="contentAreaContextMenu"> - <menu id="zotero-content-area-context-menu" label="Zotero"> - <menupopup> - <menuitem id="zotero-context-add-to-current-note" class="menu-iconic" - label="&zotero.contextMenu.addTextToCurrentNote;" hidden="true" - oncommand="var str = event.currentTarget.ownerDocument.popupNode.ownerDocument.defaultView.getSelection().toString(); var uri = event.currentTarget.ownerDocument.popupNode.ownerDocument.location.href; ZoteroPane.addTextToNote(str, uri)"/> - <menuitem id="zotero-context-add-to-new-note" class="menu-iconic" - label="&zotero.contextMenu.addTextToNewNote;" hidden="true" - oncommand="var str = event.currentTarget.ownerDocument.popupNode.ownerDocument.defaultView.getSelection().toString(); var uri = event.currentTarget.ownerDocument.popupNode.ownerDocument.location.href; var itemID = ZoteroPane.addItemFromPage(); ZoteroPane.newNote(false, itemID, str, uri)"/> - <menuitem id="zotero-context-save-link-as-item" class="menu-iconic" - label="&zotero.contextMenu.saveLinkAsItem;" hidden="true" - oncommand="ZoteroPane.addItemFromURL(window.gContextMenu.linkURL, 'temporaryPDFHack')"/> - <menuitem id="zotero-context-save-image-as-item" class="menu-iconic" - label="&zotero.contextMenu.saveImageAsItem;" hidden="true" - oncommand="ZoteroPane.addItemFromURL(window.gContextMenu.onImage ? (window.gContextMenu.mediaURL ? window.gContextMenu.mediaURL : window.gContextMenu.imageURL) : window.gContextMenu.bgImageURL, 'artwork')"/> - </menupopup> - </menu> - </popup> - <vbox id="appcontent"> <!-- onmouseup shouldn't be necessary but seems to help prevent tag selector from sometimes going off the screen --> <splitter id="zotero-splitter" resizebefore="closest" resizeafter="closest" hidden="true" onmouseup="ZoteroPane.updateTagSelectorSize()"/> - <stack id="zotero-pane-stack" persist="savedHeight,fullscreenmode" hidden="true"> - - <!-- Barrier to prevent tabbing into Zotero pane when busy --> - <box id="zotero-pane-tab-catcher-top" hidden="true" align="center" pack="center" style="opacity: 0"> - <checkbox/> - </box> - - <hbox id="zotero-pane" - onkeydown="ZoteroPane.handleKeyDown(event, this.id)" - onkeyup="ZoteroPane.handleKeyUp(event, this.id)" - chromedir="&locale.dir;"> - <popupset> - <menupopup id="zotero-collectionmenu" onpopupshowing="ZoteroPane.buildCollectionContextMenu();"> - <menuitem label="&zotero.toolbar.newCollection.label;" oncommand="ZoteroPane.newCollection()"/> - <menuitem label="&zotero.toolbar.newSavedSearch.label;" oncommand="ZoteroPane.newSearch()"/> - <menuitem label="&zotero.toolbar.newSubcollection.label;" oncommand="ZoteroPane.newCollection(ZoteroPane.getSelectedCollection().id)"/> - <menuseparator/> - <menuitem oncommand="ZoteroPane.editSelectedCollection();"/> - <menuitem oncommand="ZoteroPane.deleteSelectedCollection();"/> - <menuseparator/> - <menuitem oncommand="Zotero_File_Interface.exportCollection();"/> - <menuitem oncommand="Zotero_File_Interface.bibliographyFromCollection();"/> - <menuitem label="&zotero.toolbar.export.label;" oncommand="Zotero_File_Interface.exportFile()"/> - <menuitem oncommand="Zotero_Report_Interface.loadCollectionReport()"/> - <menuitem label="&zotero.toolbar.emptyTrash.label;" oncommand="ZoteroPane.emptyTrash();"/> - <menuitem label="&zotero.toolbar.newCollection.label;" oncommand="ZoteroPane.createCommonsBucket();"/><!--TODO localize --> - <menuitem label="Refresh" oncommand="ZoteroPane.refreshCommonsBucket();"/><!--TODO localize --> - </menupopup> - <menupopup id="zotero-itemmenu" onpopupshowing="ZoteroPane.buildItemContextMenu();"> - <menuitem label="&zotero.items.menu.showInLibrary;" oncommand="ZoteroPane.selectItem(this.parentNode.getAttribute('itemID'), true)"/> - <menuseparator/> - <!-- with icon: <menuitem class="menuitem-iconic" id="zotero-menuitem-note" label="&zotero.items.menu.attach.note;" oncommand="ZoteroPane.newNote(false, this.parentNode.getAttribute('itemID'))"/>--> - <menuitem label="&zotero.items.menu.attach.note;" oncommand="ZoteroPane.newNote(false, this.parentNode.getAttribute('itemID'))"/> - <menu label="&zotero.items.menu.attach;"> - <menupopup id="zotero-add-attachment-popup"> - <menuitem class="menuitem-iconic zotero-menuitem-attachments-snapshot" label="&zotero.items.menu.attach.snapshot;" oncommand="var itemID = parseInt(this.parentNode.parentNode.parentNode.getAttribute('itemID')); ZoteroPane.addAttachmentFromPage(false, itemID)"/> - <menuitem class="menuitem-iconic zotero-menuitem-attachments-web-link" label="&zotero.items.menu.attach.link;" oncommand="var itemID = parseInt(this.parentNode.parentNode.parentNode.getAttribute('itemID')); ZoteroPane.addAttachmentFromPage(true, itemID)"/> - <menuitem class="menuitem-iconic zotero-menuitem-attachments-file" label="&zotero.items.menu.attach.file;" oncommand="var itemID = parseInt(this.parentNode.parentNode.parentNode.getAttribute('itemID')); ZoteroPane.addAttachmentFromDialog(false, itemID);"/> - <menuitem class="menuitem-iconic zotero-menuitem-attachments-link" label="&zotero.items.menu.attach.fileLink;" oncommand="var itemID = parseInt(this.parentNode.parentNode.parentNode.getAttribute('itemID')); ZoteroPane.addAttachmentFromDialog(true, itemID);"/> - </menupopup> - </menu> - <menuseparator/> - <menuitem label="&zotero.items.menu.duplicateItem;" oncommand="ZoteroPane.duplicateSelectedItem();"/> - <menuitem oncommand="ZoteroPane.deleteSelectedItems();"/> - <menuitem oncommand="ZoteroPane.deleteSelectedItems(true);"/> - <menuseparator/> - <menuitem oncommand="Zotero_File_Interface.exportItems();"/> - <menuitem oncommand="Zotero_File_Interface.bibliographyFromItems();"/> - <menuitem oncommand="Zotero_Report_Interface.loadItemReport()"/> - <menuseparator/> - <menuitem oncommand="ZoteroPane.createParentItemsFromSelected();"/> - <menuitem oncommand="Zotero_RecognizePDF.recognizeSelected();"/> - <menuitem oncommand="ZoteroPane.renameSelectedAttachmentsFromParents()"/> - <menuitem oncommand="ZoteroPane.reindexItem();"/> - </menupopup> - </popupset> - - <vbox id="zotero-collections-pane" persist="width" flex="1"> - <!-- This extra vbox prevents the toolbar from getting compressed when resizing - the tag selector to max height --> - <vbox flex="1"> - <hbox class="toolbar"> - <toolbarbutton id="zotero-tb-collection-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newCollection.label;" oncommand="ZoteroPane.newCollection()"/> - <toolbarbutton id="zotero-tb-group-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newGroup;" oncommand="ZoteroPane.newGroup()"/> - <spacer flex="1"/> - <toolbarbutton id="zotero-tb-actions-menu" class="zotero-tb-button" tooltiptext="&zotero.toolbar.actions.label;" type="menu"> - <menupopup id="zotero-tb-actions-popup" onpopupshowing="document.getElementById('cmd_zotero_reportErrors').setAttribute('disabled', Zotero.getErrors().length == 0)"> - <menuitem id="zotero-tb-actions-import" label="&zotero.toolbar.import.label;" oncommand="Zotero_File_Interface.importFile();"/> - <menuitem id="zotero-tb-actions-import-clipboard" label="&zotero.toolbar.importFromClipboard;" oncommand="Zotero_File_Interface.importFromClipboard();" /> - <menuitem id="zotero-tb-actions-export" label="&zotero.toolbar.export.label;" oncommand="Zotero_File_Interface.exportFile();"/> - <menuitem id="zotero-tb-actions-rtfScan" label="&zotero.toolbar.rtfScan.label;" oncommand="window.openDialog('chrome://zotero/content/rtfScan.xul', 'rtfScan', 'chrome,centerscreen')"/> - <menuitem hidden="true" id="zotero-tb-actions-zeroconf-update" - label="Search for Shared Libraries" oncommand="Zotero.Zeroconf.findInstances()"/> - <menuseparator id="zotero-tb-actions-plugins-separator"/> - <menuitem id="zotero-tb-actions-timeline" label="&zotero.toolbar.timeline.label;" oncommand="Zotero_Timeline_Interface.loadTimeline()"/> - <!-- TODO: localize <menuitem id="zotero-tb-actions-duplicate" label="&zotero.toolbar.duplicate.label;" oncommand="ZoteroPane.showDuplicates()"/>--> - <menuitem id="zotero-tb-actions-showDuplicates" label="Show Duplicates" oncommand="ZoteroPane.showDuplicates()" hidden="true"/> - <menuseparator hidden="true" id="zotero-tb-actions-sync-separator"/> - <menuitem hidden="true" label="WebDAV Sync Debugging" disabled="true"/> - <menuitem hidden="true" label=" Purge Deleted Storage Files" oncommand="Zotero.Sync.Storage.purgeDeletedStorageFiles('webdav', function(results) { Zotero.debug(results); })"/> - <menuitem hidden="true" label=" Purge Orphaned Storage Files" oncommand="Zotero.Sync.Storage.purgeOrphanedStorageFiles('webdav', function(results) { Zotero.debug(results); })"/> - <menuseparator id="zotero-tb-actions-separator"/> - <menuitem id="zotero-tb-actions-prefs" label="&zotero.toolbar.preferences.label;" - oncommand="ZoteroPane.openPreferences()"/> - <menuitem id="zotero-tb-actions-reportErrors" command="cmd_zotero_reportErrors" disabled="true"/> - <menuitem id="zotero-tb-actions-support" label="&zotero.toolbar.supportAndDocumentation;" oncommand="gBrowser.selectedTab = gBrowser.addTab('http://www.zotero.org/support/')"/> - <menuitem id="zotero-tb-actions-about" label="&zotero.toolbar.about.label;" oncommand="window.openDialog('chrome://zotero/content/about.xul', 'about', 'chrome')"/> - </menupopup> - </toolbarbutton> - </hbox> - <tree id="zotero-collections-tree" hidecolumnpicker="true" context="zotero-collectionmenu" - onmouseover="ZoteroPane.collectionsView.setHighlightedRows();" - onkeypress="ZoteroPane.handleKeyPress(event, this.id)" - onselect="ZoteroPane.onCollectionSelected();" seltype="cell" - ondragstart="if (event.target.localName == 'treechildren') { ZoteroPane.collectionsView.onDragStart(event); }" - ondragenter="return ZoteroPane.collectionsView.onDragEnter(event)" - ondragover="return ZoteroPane.collectionsView.onDragOver(event)" - ondrop="return ZoteroPane.collectionsView.onDrop(event)" - flex="1"> - <treecols> - <treecol - id="zotero-collections-name-column" - flex="1" - primary="true" - hideheader="true"/> - </treecols> - <treechildren/> - </tree> - </vbox> - <splitter id="zotero-tags-splitter" onmouseup="ZoteroPane.updateTagSelectorSize()" collapse="after"> - <grippy oncommand="ZoteroPane.toggleTagSelector()"/> - </splitter> - <zoterotagselector id="zotero-tag-selector" persist="height,collapsed,showAutomatic,filterToScope" - oncommand="ZoteroPane.updateTagFilter()"/> - </vbox> - - <splitter id="zotero-tree-splitter" resizebefore="closest" resizeafter="closest" collapse="before" - onmousemove="document.getElementById('zotero-items-toolbar').setAttribute('state', this.getAttribute('state'));"> - <grippy/> - </splitter> - - <vbox id="zotero-items-pane" persist="width" flex="1"> - <hbox class="toolbar" id="zotero-items-toolbar" align="center"> - <toolbarbutton id="zotero-tb-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newItem.label;" type="menu"> - <!-- New Item drop-down built in overlay.js::onLoad() --> - <menupopup> - <menuseparator/> - <menuitem label="&zotero.toolbar.attachment.linked;" oncommand="ZoteroPane.addAttachmentFromDialog(true);" tooltiptext=""/> - <menuitem label="&zotero.toolbar.attachment.add;" oncommand="ZoteroPane.addAttachmentFromDialog();" tooltiptext=""/> - <menuseparator/> - <menu label="&zotero.toolbar.moreItemTypes.label;" tooltiptext=""> - <menupopup id="zotero-tb-add-more"/> - </menu> - </menupopup> - </toolbarbutton> - <toolbarbutton id="zotero-tb-item-from-page" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newItemFromPage.label;" oncommand="ZoteroPane.addItemFromPage('temporaryPDFHack', event.shiftKey ? !Zotero.Prefs.get('automaticSnapshots') : null)"/> - <toolbarbutton id="zotero-tb-lookup" class="zotero-tb-button" tooltiptext="&zotero.toolbar.lookup.label;" oncommand="ZoteroPane.openLookupWindow()"/> - <!--<toolbarbutton id="zotero-tb-note-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.note.standalone;" oncommand="ZoteroPane.newNote(event.shiftKey);"/>--> - <toolbarbutton id="zotero-tb-note-add" class="zotero-tb-button" tooltiptext="New Note" type="menu"> - <menupopup onpopupshowing="ZoteroPane.updateNoteButtonMenu()"> - <menuitem label="Add Standalone Note" oncommand="ZoteroPane.newNote(event.shiftKey);"/> - <menuitem id="zotero-tb-add-child-note" label="Add Child Note" oncommand="var selected = ZoteroPane.getSelectedItems()[0]; var parent = selected.getSource(); parent = parent ? parent : selected.id; ZoteroPane.newNote(event.shiftKey, parent);"/> - </menupopup> - </toolbarbutton> - <toolbarbutton id="zotero-tb-attachment-add" class="zotero-tb-button" tooltiptext="New Child Attachment" type="menu"> - <menupopup onpopupshowing="ZoteroPane.updateAttachmentButtonMenu(this)"> - <menuitem class="menuitem-iconic zotero-menuitem-attachments-snapshot" label="&zotero.items.menu.attach.snapshot;" oncommand="var itemID = ZoteroPane.getSelectedItems()[0].id; ZoteroPane.addAttachmentFromPage(false, itemID)"/> - <menuitem class="menuitem-iconic zotero-menuitem-attachments-web-link" label="&zotero.items.menu.attach.link;" oncommand="var itemID = ZoteroPane.getSelectedItems()[0].id; ZoteroPane.addAttachmentFromPage(true, itemID)"/> - <menuitem class="menuitem-iconic zotero-menuitem-attachments-file" label="Attach Stored Copy of File..." oncommand="var itemID = ZoteroPane.getSelectedItems()[0].id; ZoteroPane.addAttachmentFromDialog(false, itemID);"/> - <menuitem class="menuitem-iconic zotero-menuitem-attachments-link" label="Attach Link to File..." oncommand="var itemID = ZoteroPane.getSelectedItems()[0].id; ZoteroPane.addAttachmentFromDialog(true, itemID);"/> - </menupopup> - </toolbarbutton> - <toolbarseparator/> - <toolbarbutton id="zotero-tb-advanced-search" class="zotero-tb-button" tooltiptext="&zotero.toolbar.advancedSearch;" oncommand="ZoteroPane.openAdvancedSearchWindow()"/> - <spacer flex="1"/> - <textbox id="zotero-tb-search" type="search" timeout="250" command="cmd_zotero_search" dir="reverse" - onkeypress="ZoteroPane.handleSearchKeypress(this, event)" - oninput="ZoteroPane.handleSearchInput(this, event)"> - </textbox> - </hbox> - - <deck id="zotero-items-pane-content" selectedIndex="0" flex="1"> - <tree - id="zotero-items-tree" context="zotero-itemmenu" - enableColumnDrag="true" - onfocus="if (ZoteroPane.itemsView.rowCount &amp;&amp; !ZoteroPane.itemsView.selection.count) { ZoteroPane.itemsView.selection.select(0); }" - onkeypress="ZoteroPane.handleKeyPress(event, this.id)" - onselect="ZoteroPane.itemSelected();" - ondragstart="if (event.target.localName == 'treechildren') { ZoteroPane.itemsView.onDragStart(event); }" - ondragenter="return ZoteroPane.itemsView.onDragEnter(event)" - ondragover="return ZoteroPane.itemsView.onDragOver(event)" - ondragdrop="return ZoteroPane.itemsView.onDrop(event)" - flex="1"> - <treecols> - <treecol - id="zotero-items-column-title" primary="true" - label="&zotero.items.title_column;" - flex="4" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-firstCreator" - label="&zotero.items.creator_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-type" hidden="true" - label="&zotero.items.type_column;" - width="40" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-date" hidden="true" - label="&zotero.items.date_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-year" hidden="true" - label="&zotero.items.year_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-publisher" hidden="true" - label="&zotero.items.publisher_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-publicationTitle" hidden="true" - label="&zotero.items.publication_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-journalAbbreviation" hidden="true" - label="&zotero.items.journalAbbr_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-language" hidden="true" - label="&zotero.items.language_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-accessDate" hidden="true" - label="&zotero.items.accessDate_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-libraryCatalog" hidden="true" - label="&zotero.items.libraryCatalog_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-callNumber" hidden="true" - label="&zotero.items.callNumber_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-rights" hidden="true" - label="&zotero.items.rights_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-dateAdded" hidden="true" - label="&zotero.items.dateAdded_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-dateModified" hidden="true" - label="&zotero.items.dateModified_column;" - flex="1" persist="width ordinal hidden sortActive sortDirection"/> - <splitter class="tree-splitter"/> - <treecol - id="zotero-items-column-numChildren" - label="&zotero.items.numChildren_column;" - persist="width ordinal hidden sortActive sortDirection"/> - </treecols> - <treechildren/> - </tree> - - <!-- Label for displaying messages when items pane is hidden - (e.g. "Advanced search mode — press Enter to search.")--> - <vbox id="zotero-items-pane-message-box" pack="center" align="center"/> - </deck> - </vbox> - - <splitter id="zotero-view-splitter" resizebefore="closest" resizeafter="closest"/> - - <vbox id="zotero-item-pane" persist="width"> - <hbox class="toolbar" align="center" pack="end"> - <hbox id="zotero-tb-sync-progress-box" hidden="true" align="center"> - <toolbarbutton id="zotero-tb-sync-storage-cancel" - tooltiptext="Cancel Storage Sync" - oncommand="Zotero.Sync.Storage.QueueManager.cancel()"/> - <progressmeter id="zotero-tb-sync-progress" mode="determined" - value="0" tooltip="zotero-tb-sync-progress-tooltip"> - </progressmeter> - <tooltip id="zotero-tb-sync-progress-tooltip" noautohide="true"> - <grid> - <columns> - <column/> - <column/> - </columns> - <rows> - <row> - <label value="&zotero.sync.storage.progress;"/> - <label id="zotero-tb-sync-progress-tooltip-progress"/> - </row> - <row> - <label value="&zotero.sync.storage.downloads;"/> - <label - id="zotero-tb-sync-progress-tooltip-downloads"/> - </row> - <row> - <label value="&zotero.sync.storage.uploads;"/> - <label - id="zotero-tb-sync-progress-tooltip-uploads"/> - </row> - </rows> - </grid> - </tooltip> - </hbox> - <toolbarbutton id="zotero-tb-sync-warning" hidden="true"/> - <toolbarbutton id="zotero-tb-sync" class="zotero-tb-button" tooltip="_child" - oncommand="Zotero.Sync.Server.canAutoResetClient = true; Zotero.Sync.Server.manualSyncRequired = false; Zotero.Sync.Runner.sync()"> - <tooltip - id="zotero-tb-sync-tooltip" - onpopupshowing="Zotero.Sync.Runner.registerSyncStatusLabel(this.firstChild.nextSibling, this.firstChild.nextSibling.nextSibling)" - onpopuphiding="Zotero.Sync.Runner.registerSyncStatusLabel()" - noautohide="true"> - <label value="&zotero.sync.button;"/> - <label id="zotero-tb-sync-status" hidden="true"/> - <label id="zotero-tb-sync-last-sync"/> - </tooltip> - </toolbarbutton> - <toolbarseparator id="zotero-fullscreen-close-separator"/> - <toolbarbutton id="zotero-tb-fullscreen" tooltiptext="&zotero.toolbar.fullscreen.tooltip;" oncommand="ZoteroPane.fullScreen();" class="zotero-tb-button"/> - <toolbarbutton id="zotero-close-button" class="tabs-closebutton" oncommand="ZoteroPane.toggleDisplay()"/> - </hbox> - <!-- TODO: localize --> - <button id="zotero-item-restore-button" label="Restore to Library" - oncommand="ZoteroPane.restoreSelectedItems()" hidden="true"/> - <!-- TODO: localize --> - <button id="zotero-item-show-original" label="Show Original" - oncommand="ZoteroPane.showOriginalItem()" hidden="true"/> - <deck id="zotero-item-pane-content" selectedIndex="0" flex="1"> - <groupbox pack="center" align="center"> - <label id="zotero-view-selected-label"/> - </groupbox> - <tabbox id="zotero-view-tabbox" flex="1" onselect="if (!ZoteroPane.collectionsView.selection || event.originalTarget.localName != 'tabpanels') { return; }; ZoteroItemPane.viewItem(ZoteroPane.getSelectedItems()[0], ZoteroPane.collectionsView.editable ? 'edit' : 'view', this.selectedIndex)"> - <tabs> - <tab label="&zotero.tabs.info.label;"/> - <tab label="&zotero.tabs.notes.label;"/> - <tab label="&zotero.tabs.tags.label;"/> - <tab label="&zotero.tabs.related.label;"/> - </tabs> - <tabpanels id="zotero-view-item" flex="1"/> - </tabbox> - <!-- Note info pane --> - <groupbox id="zotero-view-note" flex="1"> - <zoteronoteeditor id="zotero-note-editor" flex="1"/> - <button id="zotero-view-note-button" label="&zotero.notes.separate;" oncommand="ZoteroPane.openNoteWindow(this.getAttribute('noteID')); if(this.hasAttribute('sourceID')) ZoteroPane.selectItem(this.getAttribute('sourceID'));"/> - </groupbox> - <!-- Attachment info pane --> - <groupbox flex="1"> - <zoteroattachmentbox id="zotero-attachment-box" flex="1"/> - </groupbox> - </deck> - </vbox> - </hbox> - - <!-- Barrier to prevent tabbing into Zotero pane when busy --> - <box id="zotero-pane-tab-catcher-bottom" hidden="true" align="center" pack="center" style="opacity: 0"> - <checkbox/> - </box> - - <stack id="zotero-pane-overlay" flex="1" hidden="true"> - <box style="background: black; opacity: .3" flex="1"/> - - <deck id="zotero-pane-overlay-deck" flex="1"> - <box id="zotero-pane-progress" flex="1" align="center" pack="center"> - <box style="background: white; -moz-border-radius: 1px; -moz-box-shadow: gray 4px 6px 4px;" width="300" height="30"> - <vbox style="padding:10px" flex="1"> - <label id="zotero-pane-progress-label"/> - <progressmeter id="zotero-pane-progressmeter" mode="undetermined"/> - </vbox> - </box> - </box> - </deck> - </stack> - - </stack> + <stack id="zotero-pane-stack" persist="savedHeight" savedHeight="300" hidden="true"/> <!-- Annotation Toolbar --> <toolbar id="zotero-annotate-tb" crop="end" insertbefore="content" hidden="true"> @@ -464,97 +67,11 @@ <!-- Scrape Code --> <hbox id="urlbar-icons"> - <image src="chrome://zotero/skin/treeitem-book.png" id="zotero-status-image" onclick="Zotero_Browser.scrapeThisPage(ZoteroPane.getSelectedLibraryID(), ZoteroPane.getSelectedCollection(true))" position="1" hidden="true"/> + <image src="chrome://zotero/skin/treeitem-book.png" id="zotero-status-image" onclick="Zotero_Browser.scrapeThisPage()" position="1" hidden="true"/> </hbox> <script> <![CDATA[ - window.addEventListener('load', function(e){ - // Set label for "Report Errors..." - if(Zotero && Zotero.initialized) { - // 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')); - } - - var fx36Icon = document.getElementById('zotero-status-bar-icon'); - var addonBar = document.getElementById('addon-bar'); - - // Check whether standalone is running, and return if so - if(!addonBar && !fx36Icon) return; - - // THE FOLLOWING CODE IS NOT APPLIED IN STANDALONE - // (It relates almost entirely to the Zotero icon) - - var iconPref = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefService) - .getBranch('extensions.zotero.').getIntPref('statusBarIcon'); - - var appInfo = Components.classes["@mozilla.org/xre/app-info;1"] - .getService(Components.interfaces.nsIXULAppInfo); - var isFx36 = appInfo.platformVersion.indexOf('1.9') === 0; - // Status bar in Fx3.6 - if (isFx36) { - var icon = fx36Icon; - } - // In >=Fx4, add to add-on bar - else { - var icon = document.createElement('toolbarbutton'); - icon.id = 'zotero-addon-bar-icon'; - icon.setAttribute('oncommand', 'ZoteroPane.toggleDisplay()'); - icon.setAttribute('hidden', true); - addonBar.appendChild(icon); - if (addonBar.collapsed) { - // If no Zotero or icon isn't set to hidden, show add-on bar - if (iconPref != 0) { - setToolbarVisibility(addonBar, true); - } - } - } - - if (Zotero && Zotero.initialized){ - switch (iconPref) { - case 2: - icon.setAttribute('hidden', false); - break; - case 1: - icon.setAttribute('hidden', false); - icon.setAttribute('compact', true); - break; - } - } - else { - if (Zotero) { - var errMsg = Zotero.startupError; - } - - // Use defaults if necessary - 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'); - } - - icon.setAttribute('tooltiptext', errMsg); - icon.setAttribute('error', 'true'); - icon.setAttribute('hidden', false); - } - - // Used for loading pages from upgrade wizard - if (Zotero.initialURL) { - setTimeout("gBrowser.selectedTab = gBrowser.addTab(Zotero.initialURL); Zotero.initialURL = null;", 1); - } - }, false); - - document.getElementById('appcontent').addEventListener('keydown', ZoteroPane.handleKeyDown, true); // Make sure open progress windows are fading document.getElementById('appcontent').addEventListener('mousemove', Zotero.ProgressWindowSet.updateTimers, false); ]]> @@ -563,7 +80,7 @@ <menupopup id="menu_ToolsPopup"> <menuseparator id="zoteroSeparator" insertbefore="devToolsSeparator"/> <menuitem id="tools-zotero" insertbefore="devToolsSeparator" - oncommand="ZoteroPane.toggleDisplay();" label="Zotero" + oncommand="ZoteroOverlay.toggleDisplay();" label="Zotero" key="key_openZotero"/> </menupopup> @@ -574,7 +91,7 @@ --> <key id="key_openZotero" key="Z" - oncommand="ZoteroPane.toggleDisplay();" + oncommand="ZoteroOverlay.toggleDisplay();" modifiers="accel alt" /> </keyset> </overlay> diff --git a/chrome/content/zotero/preferences/preferences.js b/chrome/content/zotero/preferences/preferences.js @@ -1779,4 +1779,20 @@ function updateWordProcessorInstructions() { if(Zotero.isStandalone) { document.getElementById("wordProcessors-getWordProcessorPlugins").hidden = true; } +} + +/** + * Sets "Status bar icon" to "None" if Zotero is set to load in separate tab on Fx 4 + */ +function handleShowInPreferenceChange() { + var showInSeparateTab = document.getElementById("zotero-prefpane-general-showIn-separateTab"); + if(Zotero.isFx4) { + if(showInSeparateTab.selected) { + document.getElementById('statusBarIcon').selectedItem = document.getElementById('statusBarIcon-none'); + Zotero.Prefs.set("statusBarIcon", 0); + } else if(Zotero.isFx4) { + document.getElementById('statusBarIcon').selectedItem = document.getElementById('statusBarIcon-full'); + Zotero.Prefs.set("statusBarIcon", 2); + } + } } \ No newline at end of file diff --git a/chrome/content/zotero/preferences/preferences.xul b/chrome/content/zotero/preferences/preferences.xul @@ -47,10 +47,9 @@ To add a new preference: <prefpane id="zotero-prefpane-general" label="&zotero.preferences.prefpane.general;" image="chrome://zotero/skin/prefs-general.png"> - <preferences> + <preferences id="zotero-prefpane-general-preferences"> <preference id="pref-fontSize" name="extensions.zotero.fontSize" type="string"/> <preference id="pref-noteFontSize" name="extensions.zotero.note.fontSize" type="string"/> - <preference id="pref-statusBarIcon" name="extensions.zotero.statusBarIcon" type="int"/> <preference id="pref-automaticScraperUpdates" name="extensions.zotero.automaticScraperUpdates" type="bool"/> <preference id="pref-reportTranslationFailure" name="extensions.zotero.reportTranslationFailure" type="bool"/> <preference id="pref-zoteroDotOrgVersionHeader" name="extensions.zotero.zoteroDotOrgVersionHeader" type="bool"/> @@ -65,27 +64,16 @@ To add a new preference: <preference id="pref-groups-copyChildLinks" name="extensions.zotero.groups.copyChildLinks" type="bool"/> </preferences> - <groupbox> + <groupbox id="zotero-prefpane-general-groupbox"> <caption label="&zotero.preferences.userInterface;"/> - <grid> + <grid id="zotero-prefpane-general-grid"> <columns> <column/> <column flex="1"/> </columns> - <rows> - <row> - <hbox align="center"> - <label value="&zotero.preferences.statusBarIcon;" control="statusBarIcon"/> - </hbox> - <radiogroup id="statusBarIcon" orient="horizontal" preference="pref-statusBarIcon"> - <radio src="chrome://zotero/skin/zotero_status_bar.png" value="2"/> - <radio src="chrome://zotero/skin/zotero_status_bar_compact.png" value="1"/> - <radio label="&zotero.preferences.statusBarIcon.none;" value="0"/> - </radiogroup> - </row> - + <rows id="zotero-prefpane-general-rows"> <row> <hbox align="center"> <label value="&zotero.preferences.fontSize;" control="fontSize"/> diff --git a/chrome/content/zotero/preferences/preferences_firefox.xul b/chrome/content/zotero/preferences/preferences_firefox.xul @@ -42,6 +42,39 @@ To add a new preference: --> <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <prefwindow id="zotero-prefs"> + <prefpane id="zotero-prefpane-general" + label="&zotero.preferences.prefpane.general;" + image="chrome://zotero/skin/prefs-general.png"> + <preferences id="zotero-prefpane-general-preferences"> + <preference id="pref-showIn" name="extensions.zotero.showIn" type="int"/> + <preference id="pref-statusBarIcon" name="extensions.zotero.statusBarIcon" type="int"/> + </preferences> + <groupbox id="zotero-prefpane-general-groupbox"> + <grid id="zotero-prefpane-general-grid"> + <rows id="zotero-prefpane-general-rows"> + <row position="1"> + <hbox align="center"> + <label value="&zotero.preferences.showIn;" control="showAs"/> + </hbox> + <radiogroup id="showIn" orient="horizontal" preference="pref-showIn" oncommand="handleShowInPreferenceChange()"> + <radio id="zotero-prefpane-general-showIn-browserPane" label="&zotero.preferences.showIn.browserPane;" value="1"/> + <radio id="zotero-prefpane-general-showIn-separateTab" label="&zotero.preferences.showIn.separateTab;" value="2"/> + </radiogroup> + </row> + <row position="2" id="zotero-prefpane-general-statusBarIcon-row"> + <hbox align="center"> + <label id="statusBarIcon-label" value="&zotero.preferences.statusBarIcon;" control="statusBarIcon"/> + </hbox> + <radiogroup id="statusBarIcon" orient="horizontal" preference="pref-statusBarIcon"> + <radio id="statusBarIcon-full" src="chrome://zotero/skin/zotero_status_bar.png" value="2"/> + <radio id="statusBarIcon-compact" src="chrome://zotero/skin/zotero_status_bar_compact.png" value="1"/> + <radio id="statusBarIcon-none" label="&zotero.preferences.statusBarIcon.none;" value="0"/> + </radiogroup> + </row> + </rows> + </grid> + </groupbox> + </prefpane> <prefpane id="zotero-prefpane-proxies" label="&zotero.preferences.prefpane.proxies;" image="chrome://zotero/skin/prefs-proxies.png" position="6"> diff --git a/chrome/content/zotero/standalone.js b/chrome/content/zotero/standalone.js @@ -0,0 +1,43 @@ +/* + ***** 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Zotero. If not, see <http://www.gnu.org/licenses/>. + + ***** END LICENSE BLOCK ***** +*/ + +/* + * This object contains the various functions for the interface + */ +var ZoteroStandalone = new function() +{ + this.onLoad = function() { + ZoteroPane.init(); + var success = ZoteroPane.makeVisible(); + if(!success) window.close(); + } + + this.onUnload = function() { + ZoteroPane.destroy(); + } +} + +window.addEventListener("load", function(e) { ZoteroStandalone.onLoad(e); }, false); +window.addEventListener("unload", function(e) { ZoteroStandalone.onUnload(e); }, false); +\ No newline at end of file diff --git a/chrome/content/zotero/standalone.xul b/chrome/content/zotero/standalone.xul @@ -4,7 +4,7 @@ <?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?> <?xml-stylesheet href="chrome://zotero/skin/standalone.css" type="text/css"?> <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?> -<?xul-overlay href="chrome://zotero/content/overlay.xul"?> +<?xul-overlay href="chrome://zotero/content/zoteroPane.xul"?> <?xul-overlay href="chrome://zotero/content/itemPane.xul"?> <!DOCTYPE window [ @@ -23,14 +23,15 @@ <window id="main-window" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="window.sizeToContent()" - windowtype="navigator:browser" + windowtype="navigator:browser" title="&brandShortName;" width="900" height="500" persist="screenX screenY width height sizemode"> - <script type="application/x-javascript" src="chrome://global/content/globalOverlay.js"/> - <script type="application/x-javascript" src="chrome://global/content/viewZoomOverlay.js"/> - <script type="application/x-javascript" src="chrome://global/content/contentAreaUtils.js"/> - <script type="application/x-javascript" src="chrome://global/content/inlineSpellCheckUI.js"/> + <script type="application/javascript" src="standalone.js"/> + <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/> + <script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/> + <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/> + <script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/> <commandset id="mainCommandSet"> <command id="cmd_toggleTaskbar" oncommand="goToggleToolbar('status-bar','toggle_taskbar');"/> <command id="cmd_quitApplication" oncommand="goQuitApplication()"/> @@ -214,12 +215,7 @@ <hbox flex="1" id="browser"> <vbox id="appcontent" flex="1"> - <tabbrowser id="content" disablehistory="true" - flex="1" contenttooltip="aHTMLTooltip" - contentcontextmenu="contentAreaContextMenu" - onnewtab="BrowserOpenTab();" - autocompletepopup="PopupAutoComplete" - onclick="return contentAreaClick(event, false);"/> + <stack id="zotero-pane-stack" fullscreenmode="true" flex="1"/> </vbox> </hbox> <keyset id="mainKeyset"/> diff --git a/chrome/content/zotero/tab.js b/chrome/content/zotero/tab.js @@ -0,0 +1,67 @@ +/* + ***** 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Zotero. If not, see <http://www.gnu.org/licenses/>. + + ***** END LICENSE BLOCK ***** +*/ + +/* + * This object contains the various functions for the interface + */ +var ZoteroTab = new function() +{ + this.onLoad = function() { + // find window this tab is loaded in + var windowMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var enumerator = windowMediator.getZOrderDOMWindowEnumerator("navigator:browser", true); + while(enumerator.hasMoreElements()) { + var window = enumerator.getNext(); + var browserIndex = window.gBrowser.getBrowserIndexForDocument(document); + if(browserIndex !== -1) break; + } + if(browserIndex === -1) return; + + // get tab for browser + var tab = window.gBrowser.tabs[browserIndex]; + if(window.gBrowser.selectedTab === tab) { + // if tab is already selected, init now + ZoteroPane.init(); + ZoteroPane.makeVisible(); + } else { + // otherwise, add a handler to wait until this tab is selected + var listener = function(event) { + if(event.target !== tab) return; + window.gBrowser.tabContainer.removeEventListener("TabSelect", listener, false); + ZoteroPane.init(); + ZoteroPane.makeVisible(); + } + window.gBrowser.tabContainer.addEventListener("TabSelect", listener, false); + } + } + + this.onUnload = function() { + ZoteroPane.destroy(); + } +} + +window.addEventListener("load", function(e) { ZoteroTab.onLoad(e); }, false); +window.addEventListener("unload", function(e) { ZoteroTab.onUnload(e); }, false); +\ No newline at end of file diff --git a/chrome/content/zotero/tab.xul b/chrome/content/zotero/tab.xul @@ -2,8 +2,7 @@ <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?> -<?xml-stylesheet href="chrome://zotero/skin/standalone.css" type="text/css"?> -<?xul-overlay href="chrome://zotero/content/overlay.xul"?> +<?xul-overlay href="chrome://zotero/content/zoteroPane.xul"?> <?xul-overlay href="chrome://zotero/content/itemPane.xul"?> <!DOCTYPE window [ @@ -19,9 +18,16 @@ %brandDTD; ]> -<window id="zotero-tab" title="Zotero" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - flex="1"> +<page id="zotero-tab" title="Zotero" role="application" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:xhtml="http://www.w3.org/1999/xhtml" disablefastfind="true" + flex="1"> + + <script type="application/javascript" src="standalone.js"/> + <script type="application/javascript" + src="chrome://global/content/contentAreaUtils.js"/> + <xhtml:link rel="shortcut icon" + href="chrome://zotero/skin/zotero-z-16px.png" style="display:none"/> <popup id="contentAreaContextMenu"> <menuitem id="context-undo" label="&undoCmd.label;" @@ -52,13 +58,8 @@ </popup> <hbox flex="1" id="browser"> <vbox id="appcontent" flex="1"> - <tabbrowser id="content" disablehistory="true" - flex="1" contenttooltip="aHTMLTooltip" - contentcontextmenu="contentAreaContextMenu" - onnewtab="BrowserOpenTab();" - autocompletepopup="PopupAutoComplete" - onclick="return contentAreaClick(event, false);"/> + <stack id="zotero-pane-stack" fullscreenmode="true" flex="1"/> </vbox> </hbox> <keyset id="mainKeyset"/> -</window> +</page> diff --git a/chrome/content/zotero/timelineInterface.js b/chrome/content/zotero/timelineInterface.js @@ -33,16 +33,16 @@ var Zotero_Timeline_Interface = new function() { var col = ZoteroPane.getSelectedCollection(); if (col) { - window.loadURI(uri + 'collection/' + Zotero.Collections.getLibraryKeyHash(col)); + ZoteroPane.loadURI(uri + 'collection/' + Zotero.Collections.getLibraryKeyHash(col)); return; } var s = ZoteroPane.getSelectedSavedSearch(); if (s) { - window.loadURI(uri + 'search/' + Zotero.Searches.getLibraryKeyHash(s)); + ZoteroPane.loadURI(uri + 'search/' + Zotero.Searches.getLibraryKeyHash(s)); return; } - window.loadURI(uri); + ZoteroPane.loadURI(uri); } } diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js @@ -0,0 +1,3425 @@ +/* + ***** 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Zotero. If not, see <http://www.gnu.org/licenses/>. + + ***** END LICENSE BLOCK ***** +*/ + +/* + * This object contains the various functions for the interface + */ +var ZoteroPane = new function() +{ + 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.itemSelected = itemSelected; + this.reindexItem = reindexItem; + this.duplicateSelectedItem = duplicateSelectedItem; + this.deleteSelectedCollection = deleteSelectedCollection; + 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; + + const COLLECTIONS_HEIGHT = 32; // minimum height of the collections pane and toolbar + + var self = this; + var _loaded = false; + var titlebarcolorState, titleState; + + // Also needs to be changed in collectionTreeView.js + var _lastViewedFolderRE = /^(?:(C|S|G)([0-9]+)|L)$/; + + /* + * Called when the window containing Zotero pane is open + */ + function init() + { + if(!Zotero || !Zotero.initialized) return; + + // 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.handleKeyDown, true); + + if (Zotero.locked) { + return; + } + _loaded = true; + + Zotero.setFontSize(document.getElementById('zotero-pane')) + + 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'); + } + + if(Zotero.isFx4) { + // hack, since Fx 4 no longer sets active, and the reverse in polarity of the preferred + // property makes things painful to handle otherwise + // DEBUG: remove this once we only support Fx 4 + document.documentElement.setAttribute("active", "true"); + window.addEventListener("focus", + function() { document.documentElement.setAttribute("active", "true") }, false); + window.addEventListener("blur", + function() { document.documentElement.removeAttribute("active") }, false); + } + + //Initialize collections view + this.collectionsView = new Zotero.CollectionTreeView(); + var collectionsTree = document.getElementById('zotero-collections-tree'); + collectionsTree.view = this.collectionsView; + collectionsTree.controllers.appendController(new Zotero.CollectionTreeCommandController(collectionsTree)); + collectionsTree.addEventListener("click", ZoteroPane.onTreeClick, true); + + var itemsTree = document.getElementById('zotero-items-tree'); + itemsTree.controllers.appendController(new Zotero.ItemTreeCommandController(itemsTree)); + itemsTree.addEventListener("click", ZoteroPane.onTreeClick, true); + + this.buildItemTypeMenus(); + + var menu = document.getElementById("contentAreaContextMenu"); + menu.addEventListener("popupshowing", ZoteroPane.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 () { + var url = "http://zotero.org/start"; + gBrowser.selectedTab = gBrowser.addTab(url); + }, 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.Prefs.get('debugShowDuplicates')) { + document.getElementById('zotero-tb-actions-showDuplicates').hidden = false; + } + } + + + this.buildItemTypeMenus = function () { + // + // Create the New Item (+) menu with each item type + // + var addMenu = document.getElementById('zotero-tb-add').firstChild; + var moreMenu = document.getElementById('zotero-tb-add-more'); + + // Remove all nodes, in case we're reloading + 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("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")"); + menuitem.setAttribute("tooltiptext", ""); + menuitem.className = "zotero-tb-add"; + addMenu.insertBefore(menuitem, separator); + } + + + // + // Create submenu for secondary item types + // + + // 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("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")"); + menuitem.setAttribute("tooltiptext", ""); + menuitem.className = "zotero-tb-add"; + moreMenu.appendChild(menuitem); + } + } + + + /* + * Called when the window closes + */ + function destroy() + { + if (!Zotero || !Zotero.initialized || !_loaded) { + return; + } + + var tagSelector = document.getElementById('zotero-tag-selector'); + tagSelector.unregister(); + + this.collectionsView.unregister(); + if (this.itemsView) + this.itemsView.unregister(); + } + + /** + * 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.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.onLoad(); + } + + // If Zotero could not be initialized, display an error message and return + 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'); + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + ps.alert(null, "", errMsg); + } + + return false; + } + + this.updateTagSelectorSize(); + + // Focus the quicksearch on pane open + setTimeout("document.getElementById('zotero-tb-search').inputField.select();", 1); + + // Auto-empty trashed items older than a certain number of days + var days = Zotero.Prefs.get('trashAutoEmptyDays'); + if (days) { + var d = new Date(); + var deleted = Zotero.Items.emptyTrash(days); + var d2 = new Date(); + Zotero.debug("Emptied old items from trash in " + (d2 - d) + " ms"); + } + + 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') && Zotero.Sync.Server.enabled + && !Zotero.Sync.Server.syncInProgress && !Zotero.Sync.Storage.syncInProgress) { + if (Zotero.Sync.Server.manualSyncRequired) { + Zotero.debug('Manual sync required -- skipping auto-sync', 4); + } + else { + setTimeout(function () { + Zotero.Sync.Runner.sync(true); + }, 1000); + } + } + + return true; + } + + + 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.setHighlightedRowsCallback + }, 225, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + } + else if ((Zotero.isWin && event.ctrlKey) || + (!Zotero.isWin && event.altKey)) { + if (this.highlightTimer) { + this.highlightTimer.cancel(); + this.highlightTimer = null; + } + ZoteroPane.collectionsView.setHighlightedRows(); + } + + 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(ZoteroOverlay) ZoteroOverlay.toggleDisplay() + break; + case 'library': + document.getElementById('zotero-collections-tree').focus(); + ZoteroPane.collectionsView.selection.select(0); + break; + case 'quicksearch': + document.getElementById('zotero-tb-search').select(); + break; + case 'newItem': + ZoteroPane.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': + // Use key that's not the modifier as the popup toggle + ZoteroPane.newNote(useShift ? event.altKey : event.shiftKey); + break; + case 'toggleTagSelector': + ZoteroPane.toggleTagSelector(); + break; + case 'toggleFullscreen': + ZoteroPane.toggleTab(); + break; + case 'copySelectedItemCitationsToClipboard': + ZoteroPane.copySelectedItemsToClipboard(true) + break; + case 'copySelectedItemsToClipboard': + ZoteroPane.copySelectedItemsToClipboard(); + break; + case 'importFromClipboard': + Zotero_File_Interface.importFromClipboard(); + break; + default: + throw ('Command "' + command + '" not found in ZoteroPane.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.collectionsView.setHighlightedRows(); + } + } + } + + + /* + * Highlights collections containing selected items on Ctrl (Win) or + * Option/Alt (Mac/Linux) press + */ + function setHighlightedRowsCallback() { + var itemIDs = ZoteroPane.getSelectedItems(true); + if (itemIDs && itemIDs.length) { + var collectionIDs = Zotero.Collections.getCollectionsContainingItems(itemIDs, true); + if (collectionIDs) { + ZoteroPane.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.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.deleteSelectedItems(force); + event.preventDefault(); + return; + } + } + } + + + /* + * Create a new item + * + * _data_ is an optional object with field:value for itemData + */ + function newItem(typeID, data, row) + { + if (!Zotero.stateCheck()) { + this.displayErrorMessage(true); + return false; + } + + // Currently selected row + if (row === undefined) { + row = this.collectionsView.selection.currentIndex; + } + + if (!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; + } + + 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); + } + + //set to Info tab + document.getElementById('zotero-view-item').selectedIndex = 0; + + this.selectItem(itemID); + + return Zotero.Items.get(itemID); + } + + + function newCollection(parent) + { + if (!Zotero.stateCheck()) { + this.displayErrorMessage(true); + return false; + } + + if (!this.canEdit()) { + 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.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(){ + 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; + } + + document.getElementById('zotero-tb-search').value = ""; + + 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()); + itemgroup.showDuplicates = false; + + 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(); + } + + if (itemgroup.isLibrary()) { + Zotero.Prefs.set('lastViewedFolder', 'L'); + } + if (itemgroup.isCollection()) { + Zotero.Prefs.set('lastViewedFolder', 'C' + itemgroup.ref.id); + } + else if (itemgroup.isSearch()) { + Zotero.Prefs.set('lastViewedFolder', 'S' + itemgroup.ref.id); + } + else if (itemgroup.isGroup()) { + Zotero.Prefs.set('lastViewedFolder', 'G' + itemgroup.ref.id); + } + } + + + this.showDuplicates = function () { + if (this.collectionsView.selection.count == 1 && this.collectionsView.selection.currentIndex != -1) { + var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + itemGroup.showDuplicates = true; + + try { + Zotero.UnresponsiveScriptIndicator.disable(); + this.itemsView.refresh(); + } + finally { + Zotero.UnresponsiveScriptIndicator.enable(); + } + } + } + + this.getItemGroup = function () { + return this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + } + + + function itemSelected() + { + if (!Zotero.stateCheck()) { + this.displayErrorMessage(); + return; + } + + // Display restore button if items selected in Trash + if (this.itemsView && 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'); + + // Single item selected + if (this.itemsView && this.itemsView.selection.count == 1 && this.itemsView.selection.currentIndex != -1) + { + var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex); + + if(item.ref.isNote()) { + var noteEditor = document.getElementById('zotero-note-editor'); + noteEditor.mode = this.collectionsView.editable ? 'edit' : 'view'; + + // 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 (!noteEditor.item || noteEditor.item.id != item.ref.id) { + noteEditor.disableUndo(); + } + noteEditor.parent = null; + noteEditor.item = item.ref; + + noteEditor.enableUndo(); + + var viewButton = document.getElementById('zotero-view-note-button'); + if (this.collectionsView.editable) { + viewButton.hidden = false; + viewButton.setAttribute('noteID', item.ref.id); + if (item.ref.getSource()) { + viewButton.setAttribute('sourceID', item.ref.getSource()); + } + else { + viewButton.removeAttribute('sourceID'); + } + } + else { + viewButton.hidden = true; + } + + document.getElementById('zotero-item-pane-content').selectedIndex = 2; + } + + else if(item.ref.isAttachment()) { + var attachmentBox = document.getElementById('zotero-attachment-box'); + attachmentBox.mode = this.collectionsView.editable ? 'edit' : 'view'; + attachmentBox.item = item.ref; + + document.getElementById('zotero-item-pane-content').selectedIndex = 3; + } + + // Regular item + else { + var isCommons = this.getItemGroup().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.ref, null, pane); + tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex; + } + else { + ZoteroItemPane.viewItem(item.ref, 'view', pane); + tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex; + } + } + } + // Zero or multiple items selected + else { + document.getElementById('zotero-item-pane-content').selectedIndex = 0; + + var label = document.getElementById('zotero-view-selected-label'); + + if (this.itemsView && this.itemsView.selection.count) { + label.value = Zotero.getString('pane.item.selected.multiple', this.itemsView.selection.count); + } + else { + label.value = Zotero.getString('pane.item.selected.zero'); + } + } + } + + + /** + * 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.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.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.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.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); + 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.deleteSelectedItem() is deprecated -- use ZoteroPane.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 + */ + this.deleteSelectedItems = function (force) { + 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()) { + // In library, don't prompt if meta key was pressed + var prompt = force ? false : toTrash; + } + else if (itemGroup.isCollection()) { + // In collection, only prompt if trashing + var prompt = force ? (itemGroup.isWithinGroup() ? toDelete : toTrash) : false; + } + // This should be changed if/when groups get trash + else if (itemGroup.isGroup()) { + var prompt = toDelete; + } + else if (itemGroup.isSearch()) { + if (!force) { + return; + } + var prompt = toTrash; + } + // Do nothing in share views + else if (itemGroup.isShare()) { + return; + } + else if (itemGroup.isBucket()) { + var prompt = toDelete; + } + // 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; + } + + 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); + } + } + + function deleteSelectedCollection() + { + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + + if (this.collectionsView.selection.count == 1) { + var row = + this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + + if (row.isCollection()) + { + if (confirm(Zotero.getString('pane.collections.delete'))) + { + this.collectionsView.deleteSelection(); + } + } + else if (row.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 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(); + Zotero.purgeDataObjects(true); + } + } + + this.createCommonsBucket = function () { + var self = this; + + Zotero.Commons.getBuckets(function () { + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + + var invalid = false; + + while (true) { + if (invalid) { + // TODO: localize + ps.alert(null, "", "Invalid title. Please try again."); + invalid = false; + } + + var newTitle = {}; + var result = prompt.prompt( + null, + "", + // TODO: localize + "Enter a title for this Zotero Commons collection:", + newTitle, + "", {} + ); + + if (!result) { + return; + } + + var title = Zotero.Utilities.trim(newTitle.value); + + if (!title) { + return; + } + + if (!Zotero.Commons.isValidBucketTitle(title)) { + invalid = true; + continue; + } + + break; + } + + invalid = false; + + var origName = title.toLowerCase(); + origName = origName.replace(/[^a-z0-9 ._-]/g, ''); + origName = origName.replace(/ /g, '-'); + origName = origName.substr(0, 32); + + while (true) { + if (invalid) { + // TODO: localize + var msg = "'" + testName + "' is not a valid Zotero Commons collection identifier.\n\n" + + "Collection identifiers can contain basic Latin letters, numbers, " + + "hyphens, and underscores and must be no longer than 32 characters. " + + "Spaces and other characters are not allowed."; + ps.alert(null, "", msg); + invalid = false; + } + + var newName = { value: origName }; + var result = ps.prompt( + null, + "", + // TODO: localize + "Enter an identifier for the collection '" + title + "'.\n\n" + + "The identifier will form the collection's URL on archive.org. " + + "Identifiers can contain basic Latin letters, numbers, hyphens, and underscores " + + "and must be no longer than 32 characters. " + + "Spaces and other characters are not allowed.\n\n" + + '"' + Zotero.Commons.userNameSlug + '-" ' + + "will be automatically prepended to your entry.", + newName, + "", {} + ); + + if (!result) { + return; + } + + var name = Zotero.Utilities.trim(newName.value); + + if (!name) { + return; + } + + var testName = Zotero.Commons.userNameSlug + '-' + name; + if (!Zotero.Commons.isValidBucketName(testName)) { + invalid = true; + continue; + } + + break; + } + + // TODO: localize + var progressWin = new Zotero.ProgressWindow(); + progressWin.changeHeadline("Creating Zotero Commons Collection"); + var icon = self.collectionsView.getImageSrc(self.collectionsView.selection.currentIndex); + progressWin.addLines(title, icon) + progressWin.show(); + + Zotero.Commons.createBucket(name, title, function () { + progressWin.startCloseTimer(); + }); + }); + } + + + this.refreshCommonsBucket = function() { + if (!this.collectionsView + || !this.collectionsView.selection + || this.collectionsView.selection.count != 1 + || this.collectionsView.selection.currentIndex == -1) { + return false; + } + + var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + if (itemGroup && itemGroup.isBucket()) { + var self = this; + itemGroup.ref.refreshItems(function () { + self.itemsView.refresh(); + self.itemsView.sort(); + + // On a manual refresh, also check for new OCRed files + //Zotero.Commons.syncFiles(); + }); + } + } + + 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 promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + ps.alert(null, "", Zotero.getString("fileInterface.noReferencesError")); + return; + } + + var url = window.content.location.href; + 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 = ''; + search.doCommand('cmd_zotero_search'); + } + } + + + function handleSearchKeypress(textbox, event) { + // Events that turn find-as-you-type on + if (event.keyCode == event.DOM_VK_ESCAPE) { + textbox.value = ''; + ZoteroPane.setItemsPaneMessage(Zotero.getString('searchInProgress')); + setTimeout("ZoteroPane.search(); ZoteroPane.clearItemsPaneMessage();", 1); + } + else if (event.keyCode == event.DOM_VK_RETURN || event.keyCode == event.DOM_VK_ENTER) { + ZoteroPane.setItemsPaneMessage(Zotero.getString('searchInProgress')); + setTimeout("ZoteroPane.search(true); ZoteroPane.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.setItemsPaneMessage(Zotero.getString('searchInProgress')); + setTimeout("ZoteroPane.search(); ZoteroPane.clearItemsPaneMessage();", 1); + } + else if (textbox.value.indexOf('"') != -1) { + ZoteroPane.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.selectItem()"); + return false; + } + + var currentLibraryID = this.getSelectedLibraryID(); + // If in a different library + if (item.libraryID != currentLibraryID) { + this.collectionsView.selectLibrary(item.libraryID); + } + // Force switch to library view + else if (!this.itemsView._itemGroup.isLibrary() && inLibrary) { + this.collectionsView.selectLibrary(item.libraryID); + } + + var selected = this.itemsView.selectItem(itemID, expand); + if (!selected) { + this.collectionsView.selectLibrary(item.libraryID); + this.itemsView.selectItem(itemID, expand); + } + + return true; + } + + + this.getSelectedLibraryID = function () { + var group = this.getSelectedGroup(); + if (group) { + return group.libraryID; + } + var collection = this.getSelectedCollection(); + if (collection) { + return collection.libraryID; + } + return null; + } + + + function getSelectedCollection(asID) { + if (this.collectionsView) { + return this.collectionsView.getSelectedCollection(asID); + } + return 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.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + 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 menu = document.getElementById('zotero-collectionmenu'); + var m = { + newCollection: 0, + newSavedSearch: 1, + newSubcollection: 2, + sep1: 3, + editSelectedCollection: 4, + removeCollection: 5, + sep2: 6, + exportCollection: 7, + createBibCollection: 8, + exportFile: 9, + loadReport: 10, + emptyTrash: 11, + createCommonsBucket: 12, + refreshCommonsBucket: 13 + }; + + var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); + + var enable = [], disable = [], show = []; + + // Collection + 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>0) { + enable = s; + } + else if (!this.collectionsView.isContainerEmpty(this.collectionsView.selection.currentIndex)) { + enable = [m.exportCollection]; + 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')); + } + // Saved Search + else if (itemGroup.isSearch()) { + show = [ + m.editSelectedCollection, + m.removeCollection, + m.sep2, + m.exportCollection, + m.createBibCollection, + m.loadReport + ]; + + var s = [m.exportCollection, m.createBibCollection, m.loadReport]; + if (this.itemsView.rowCount>0) { + enable = s; + } + else { + disable = s; + } + + // Adjust labels + menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.edit.savedSearch')); + menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.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')); + } + // Trash + else if (itemGroup.isTrash()) { + show = [m.emptyTrash]; + } + else if (itemGroup.isHeader()) { + if (itemGroup.ref.id == 'commons-header') { + show = [m.createCommonsBucket]; + } + } + else if (itemGroup.isBucket()) { + show = [m.refreshCommonsBucket]; + } + // Group + else if (itemGroup.isGroup()) { + show = [m.newCollection, m.newSavedSearch]; + } + // Library + else + { + show = [m.newCollection, m.newSavedSearch, m.sep1, m.exportFile]; + } + + // Disable some actions if user doesn't have write access + var s = [m.editSelectedCollection, m.removeCollection, m.newCollection, m.newSavedSearch, m.newSubcollection]; + if (itemGroup.isWithinGroup() && !itemGroup.editable) { + disable = disable.concat(s); + } + else { + enable = enable.concat(s); + } + + for (var i in disable) + { + menu.childNodes[disable[i]].setAttribute('disabled', true); + } + + for (var i in enable) + { + menu.childNodes[enable[i]].setAttribute('disabled', false); + } + + // Hide all items by default + for each(var pos in m) { + menu.childNodes[pos].setAttribute('hidden', true); + } + + for (var i in show) + { + menu.childNodes[show[i]].setAttribute('hidden', false); + } + } + + function buildItemContextMenu() + { + var m = { + showInLibrary: 0, + sep1: 1, + addNote: 2, + addAttachments: 3, + sep2: 4, + duplicateItem: 5, + deleteItem: 6, + deleteFromLibrary: 7, + sep3: 8, + exportItems: 9, + createBib: 10, + loadReport: 11, + sep4: 12, + createParent: 13, + recognizePDF: 14, + renameAttachments: 15, + reindexItem: 16 + }; + + var menu = document.getElementById('zotero-itemmenu'); + + var enable = [], disable = [], show = [], hide = [], multiple = ''; + + if (!this.itemsView) { + return; + } + + if (this.itemsView.selection.count > 0) { + var itemGroup = this.itemsView._itemGroup; + + enable.push(m.showInLibrary, m.addNote, m.addAttachments, + m.sep2, m.duplicateItem, m.deleteItem, m.deleteFromLibrary, + m.exportItems, m.createBib, m.loadReport); + + // Multiple items selected + if (this.itemsView.selection.count > 1) { + var multiple = '.multiple'; + hide.push(m.showInLibrary, m.sep1, m.addNote, m.addAttachments, + m.sep2, m.duplicateItem); + + // If all items can be reindexed, or all items can be recognized, show option + var items = this.getSelectedItems(); + var canIndex = true; + var canRecognize = true; + if (!Zotero.Fulltext.pdfConverterIsRegistered()) { + canIndex = false; + } + for (var i=0; i<items.length; i++) { + if (canIndex && !Zotero.Fulltext.canReindex(items[i].id)) { + canIndex = false; + } + if (canRecognize && !Zotero_RecognizePDF.canRecognize(items[i])) { + canRecognize = false; + } + if (!canIndex && !canRecognize) { + break; + } + } + if (canIndex) { + show.push(m.reindexItem); + } + else { + hide.push(m.reindexItem); + } + if (canRecognize) { + show.push(m.recognizePDF); + hide.push(m.createParent); + } + else { + hide.push(m.recognizePDF); + + var canCreateParent = true; + for (var i=0; i<items.length; i++) { + if (!items[i].isTopLevelItem() || items[i].isRegularItem() || Zotero_RecognizePDF.canRecognize(items[i])) { + canCreateParent = false; + break; + } + } + if (canCreateParent) { + show.push(m.createParent); + } + else { + hide.push(m.createParent); + } + } + + // If all items are child attachments, show rename option + var canRename = true; + for (var i=0; i<items.length; i++) { + var item = items[i]; + // Same check as in rename function + if (!item.isAttachment() || !item.getSource() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) { + canRename = false; + break; + } + } + if (canRename) { + show.push(m.renameAttachments); + } + else { + hide.push(m.renameAttachments); + } + + // Add in attachment separator + if (canCreateParent || canRecognize || canRename || canIndex) { + show.push(m.sep4); + } + else { + hide.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) { + var d = [m.deleteFromLibrary, m.createParent, m.renameAttachments]; + for each(var val in d) { + disable.push(val); + var index = enable.indexOf(val); + if (index != -1) { + enable.splice(index, 1); + } + } + } + } + } + + // Single item selected + else + { + var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex).ref; + var itemID = item.id; + menu.setAttribute('itemID', itemID); + + // Show in Library + if (!itemGroup.isLibrary() && !itemGroup.isWithinGroup()) { + show.push(m.showInLibrary, m.sep1); + } + else { + hide.push(m.showInLibrary, m.sep1); + } + + if (item.isRegularItem()) + { + show.push(m.addNote, m.addAttachments, m.sep2); + } + else + { + hide.push(m.addNote, m.addAttachments, m.sep2); + } + + if (item.isAttachment()) { + var showSep4 = false; + hide.push(m.duplicateItem); + + if (Zotero_RecognizePDF.canRecognize(item)) { + show.push(m.recognizePDF); + hide.push(m.createParent); + showSep4 = true; + } + else { + hide.push(m.recognizePDF); + + // If not a PDF, allow parent item creation + if (item.isTopLevelItem()) { + show.push(m.createParent); + showSep4 = true; + } + else { + hide.push(m.createParent); + } + } + + // Attachment rename option + if (item.getSource() && item.attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) { + show.push(m.renameAttachments); + showSep4 = true; + } + else { + hide.push(m.renameAttachments); + } + + if (showSep4) { + show.push(m.sep4); + } + else { + hide.push(m.sep4); + } + + // If not linked URL, show reindex line + if (Zotero.Fulltext.pdfConverterIsRegistered() + && Zotero.Fulltext.canReindex(item.id)) { + show.push(m.reindexItem); + showSep4 = true; + } + else { + hide.push(m.reindexItem); + } + } + else { + if (item.isNote() && item.isTopLevelItem()) { + show.push(m.sep4, m.createParent); + } + else { + hide.push(m.sep4, m.createParent); + } + + show.push(m.duplicateItem); + hide.push(m.recognizePDF, m.renameAttachments, m.reindexItem); + } + + // 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); + var index = enable.indexOf(val); + if (index != -1) { + enable.splice(index, 1); + } + } + } + } + } + // No items selected + else + { + // Show in Library + if (!itemGroup.isLibrary()) { + show.push(m.showInLibrary, m.sep1); + } + else { + hide.push(m.showInLibrary, m.sep1); + } + + disable.push(m.showInLibrary, m.duplicateItem, m.deleteItem, + m.deleteFromLibrary, m.exportItems, m.createBib, m.loadReport); + hide.push(m.addNote, m.addAttachments, m.sep2, m.sep4, m.reindexItem, + m.createParent, m.recognizePDF, m.renameAttachments); + } + + // 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': + continue; + } + } + disable.push(m[i]); + var index = enable.indexOf(m[i]); + if (index != -1) { + enable.splice(index, 1); + } + } + } + + // Remove from collection + if (this.itemsView._itemGroup.isCollection() && !(item && item.getSource())) + { + menu.childNodes[m.deleteItem].setAttribute('label', Zotero.getString('pane.items.menu.remove' + multiple)); + show.push(m.deleteItem); + } + else + { + hide.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)); + + for (var i in disable) + { + menu.childNodes[disable[i]].setAttribute('disabled', true); + } + + for (var i in enable) + { + menu.childNodes[enable[i]].setAttribute('disabled', false); + } + + for (var i in hide) + { + menu.childNodes[hide[i]].setAttribute('hidden', true); + } + + for (var i in show) + { + menu.childNodes[show[i]].setAttribute('hidden', false); + } + } + + + // Adapted from: http://www.xulplanet.com/references/elemref/ref_tree.html#cmnote-9 + this.onTreeClick = function (event) { + // We only care about primary button double and triple clicks + if (!event || (event.detail != 2 && event.detail != 3) || event.button != 0) { + return; + } + + var t = event.originalTarget; + + if (t.localName != 'treechildren') { + return; + } + + var tree = t.parentNode; + + var row = {}, col = {}, obj = {}; + tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj); + + // 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; + } + + var itemGroup = ZoteroPane.collectionsView._getItemAtRow(tree.view.selection.currentIndex); + if (itemGroup.isLibrary()) { + var uri = Zotero.URI.getCurrentUserLibraryURI(); + if (uri) { + ZoteroPane.loadURI(uri); + event.stopPropagation(); + } + return; + } + + if (itemGroup.isSearch()) { + ZoteroPane.editSelectedCollection(); + return; + } + + if (itemGroup.isGroup()) { + var uri = Zotero.URI.getGroupURI(itemGroup.ref, true); + ZoteroPane.loadURI(uri); + event.stopPropagation(); + return; + } + + if (itemGroup.isHeader()) { + if (itemGroup.ref.id == 'group-libraries-header') { + var uri = Zotero.URI.getGroupsURL(); + ZoteroPane.loadURI(uri); + event.stopPropagation(); + } + return; + } + + if (itemGroup.isBucket()) { + ZoteroPane.loadURI(itemGroup.ref.uri); + event.stopPropagation(); + } + } + else if (tree.id == 'zotero-items-tree') { + var viewOnDoubleClick = Zotero.Prefs.get('viewOnDoubleClick'); + + // Expand/collapse on triple-click + if (viewOnDoubleClick) { + 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.getSelectedItems()[0]; + if (item) { + if (item.isRegularItem()) { + // Double-click on Commons item should load IA page + var itemGroup = ZoteroPane.collectionsView._getItemAtRow( + ZoteroPane.collectionsView.selection.currentIndex + ); + + if (itemGroup.isBucket()) { + var uri = itemGroup.ref.getItemURI(item); + ZoteroPane.loadURI(uri); + event.stopPropagation(); + return; + } + + if (!viewOnDoubleClick) { + return; + } + + var uri = Components.classes["@mozilla.org/network/standard-url;1"]. + createInstance(Components.interfaces.nsIURI); + var snapID = item.getBestAttachment(); + if (snapID) { + spec = Zotero.Items.get(snapID).getLocalFileURL(); + if (spec) { + uri.spec = spec; + if (uri.scheme && uri.scheme == 'file') { + ZoteroPane.viewAttachment(snapID, event); + return; + } + } + } + + 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.loadURI(uri); + } + } + else if (item.isNote()) { + if (!ZoteroPane.collectionsView.editable) { + return; + } + document.getElementById('zotero-view-note-button').doCommand(); + } + else if (item.isAttachment()) { + ZoteroPane.viewSelectedAttachment(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(uri, event, data) { + // Ignore javascript: and data: URIs + if (uri.match(/^(javascript|data):/)) { + return; + } + + if (Zotero.isStandalone && uri.match(/^https?/)) { + var io = Components.classes['@mozilla.org/network/io-service;1'] + .getService(Components.interfaces.nsIIOService); + var uri = io.newURI(uri, 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); + return; + } + + // Open in new tab + var openInNewTab = event && (event.metaKey || (!Zotero.isMac && event.ctrlKey)); + if (event && event.shiftKey) { + 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) { + // 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; + } + + + // Updates browser context menu options + function contextPopupShowing() + { + if (!Zotero.Prefs.get('browserContentContextMenu')) { + return; + } + + var menuitem = document.getElementById("zotero-context-add-to-current-note"); + var showing = false; + if (menuitem){ + var items = ZoteroPane.getSelectedItems(); + if (ZoteroPane.itemsView.selection && ZoteroPane.itemsView.selection.count==1 + && items[0] && items[0].isNote() + && window.gContextMenu.isTextSelected) + { + menuitem.hidden = false; + showing = true; + } + else + { + menuitem.hidden = true; + } + } + + var menuitem = document.getElementById("zotero-context-add-to-new-note"); + if (menuitem){ + if (window.gContextMenu.isTextSelected) + { + menuitem.hidden = false; + showing = true; + } + else + { + menuitem.hidden = true; + } + } + + var menuitem = document.getElementById("zotero-context-save-link-as-item"); + if (menuitem) { + if (window.gContextMenu.onLink) { + menuitem.hidden = false; + showing = true; + } + 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; + showing = true; + } + 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'); + menu.hidden = !showing; + 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'); + } + + + function addAttachmentFromDialog(link, id) + { + if (!this.canEdit()) { + this.displayCannotEditLibraryMessage(); + return; + } + + var itemGroup = ZoteroPane.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 (!this.canEdit(row)) { + this.displayCannotEditLibraryMessage(); + return; + } + + return this.addItemFromDocument(window.content.document, itemType, saveSnapshot, row); + } + + + /** + * @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) { + var progressWin = new Zotero.ProgressWindow(); + progressWin.changeHeadline(Zotero.getString('ingester.scraping')); + var icon = 'chrome://zotero/skin/treeitem-webpage.png'; + progressWin.addLines(doc.title, icon) + progressWin.show(); + progressWin.startCloseTimer(); + + // 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) { + 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) { + row = this.collectionsView.selection.currentIndex; + } + + if (!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); + filesEditable = group.filesEditable; + } + else { + 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 (url == window.content.document.location.href) { + return this.addItemFromPage(itemType, saveSnapshot, row); + } + + var self = this; + + Zotero.MIME.getMIMETypeFromURL(url, function (mimeType, hasNativeHandler) { + // If native type, save using a hidden browser + if (hasNativeHandler) { + var processor = function (doc) { + ZoteroPane.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.displayErrorMessage(true); + return false; + } + + // Currently selected row + if (row === undefined) { + row = ZoteroPane.collectionsView.selection.currentIndex; + } + + if (!ZoteroPane.canEdit(row)) { + ZoteroPane.displayCannotEditLibraryMessage(); + return; + } + + if (row !== undefined) { + var itemGroup = ZoteroPane.collectionsView._getItemAtRow(row); + var libraryID = itemGroup.ref.libraryID; + } + else { + var libraryID = null; + var itemGroup = null; + } + // + // + // + + if (!ZoteroPane.canEditFiles(row)) { + ZoteroPane.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.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.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); + } + } + + + function viewAttachment(itemID, event, noLocateOnMissing) { + var attachment = Zotero.Items.get(itemID); + if (!attachment.isAttachment()) { + throw ("Item " + itemID + " is not an attachment in ZoteroPane.viewAttachment()"); + } + + if (attachment.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) { + this.loadURI(attachment.getField('url'), event); + return; + } + + var file = attachment.getFile(); + if (file) { + var mimeType = attachment.getAttachmentMIMEType(); + // If no MIME type specified, try to detect again (I guess in case + // we've gotten smarter since the file was imported?) + if (!mimeType) { + var mimeType = Zotero.MIME.getMIMETypeFromFile(file); + var ext = Zotero.File.getExtension(file); + + // TODO: update DB with new info + } + var ext = Zotero.File.getExtension(file); + var isNative = Zotero.MIME.hasNativeHandler(mimeType, ext); + var internal = Zotero.MIME.hasInternalHandler(mimeType, ext); + + if (isNative || + (internal && !Zotero.Prefs.get('launchNonNativeFiles'))) { + + var url = 'zotero://attachment/' + itemID + '/'; + this.loadURI(url, event, { attachmentID: itemID}); + } + else { + var fileURL = attachment.getLocalFileURL(); + + // Some platforms don't have nsILocalFile.launch, so we just load it and + // let the Firefox external helper app window handle it + try { + file.launch(); + } + catch (e) { + window.loadURI(fileURL); + } + } + } + else { + this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing); + } + } + + + 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), + // "double-click" the parent directory + try { + var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile); + parent.launch(); + } + // If launch also fails, try the OS handler + catch (e) { + var uri = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService). + newFileURI(parent); + var protocolService = + Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]. + getService(Components.interfaces.nsIExternalProtocolService); + protocolService.loadUrl(uri); + } + } + } + else { + this.showAttachmentNotFoundDialog(attachment.id, noLocateOnMissing) + } + } + } + + + /** + * Test if the user can edit the currently selected library/collection, + * and display an error if not + * + * @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 currently selected library/collection, + * and display an error if not + * + * @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, "", "You cannot make changes to the currently selected library."); + } + + + this.displayCannotEditLibraryFilesMessage = function () { + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + ps.alert(null, "", "You cannot add files to the currently selected library."); + } + + + 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.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.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.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.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') + ' ' + + 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') + '\n\n' + + reportInstructions; + self.setItemsPaneMessage(msg, true); + } + Zotero.debug(msg, 1); + } + + /** + * 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(); + } + + /** + * Gets the ZoteroPane object that is currently active in this window. Should be used for + * determining the active collection for scraping, etc. + */ + this.getActiveZoteroPane = function() { + if(ZoteroOverlay && ZoteroOverlay.isTab) { + // If a Zotero tab is open, return the pane for the tab + var tab = ZoteroOverlay.findZoteroTab(); + if(!tab) return null; + return gBrowser.getBrowserForTab(tab).contentWindow.ZoteroPane; + } else { + // Otherwise, return the pane for this tab + return ZoteroPane; + } + } + + /** + * Shows the Zotero pane, making it visible if it is not and switching to the appropriate tab + * if necessary. + */ + this.show = function() { + if(ZoteroOverlay) { + if(ZoteroOverlay.isTab) { + ZoteroOverlay.loadZoteroTab(); + } else if(!this.isShowing()) { + ZoteroOverlay.toggleDisplay(); + } + } + } +} +\ No newline at end of file diff --git a/chrome/content/zotero/zoteroPane.xul b/chrome/content/zotero/zoteroPane.xul @@ -0,0 +1,440 @@ +<?xml version="1.0"?> +<!-- + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2009 Center for History and New Media + George Mason University, Fairfax, Virginia, USA + http://zotero.org + + This file is part of Zotero. + + Zotero is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Zotero. If not, see <http://www.gnu.org/licenses/>. + + ***** END LICENSE BLOCK ***** +--> + + +<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?> +<?xml-stylesheet href="chrome://zotero-platform/content/overlay.css" type="text/css"?> + +<!DOCTYPE overlay [ + <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD; + <!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd"> %zoteroDTD; +]> + +<overlay id="zotero" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="include.js"/> + <script src="zoteroPane.js"/> + <script src="fileInterface.js"/> + <script src="reportInterface.js"/> + <script src="timelineInterface.js"/> + <script src="recognizePDF.js"/> + <script src="browser.js"/> + + <commandset id="mainCommandSet"> + <command id="cmd_zotero_search" oncommand="ZoteroPane.search();"/> + <command id="cmd_zotero_reportErrors" oncommand="ZoteroPane.reportErrors();"/> + </commandset> + + <popup id="contentAreaContextMenu"> + <menu id="zotero-content-area-context-menu" label="Zotero"> + <menupopup> + <menuitem id="zotero-context-add-to-current-note" class="menu-iconic" + label="&zotero.contextMenu.addTextToCurrentNote;" hidden="true" + oncommand="var str = event.currentTarget.ownerDocument.popupNode.ownerDocument.defaultView.getSelection().toString(); var uri = event.currentTarget.ownerDocument.popupNode.ownerDocument.location.href; ZoteroPane.addTextToNote(str, uri)"/> + <menuitem id="zotero-context-add-to-new-note" class="menu-iconic" + label="&zotero.contextMenu.addTextToNewNote;" hidden="true" + oncommand="var str = event.currentTarget.ownerDocument.popupNode.ownerDocument.defaultView.getSelection().toString(); var uri = event.currentTarget.ownerDocument.popupNode.ownerDocument.location.href; var itemID = ZoteroPane.addItemFromPage(); ZoteroPane.newNote(false, itemID, str, uri)"/> + <menuitem id="zotero-context-save-link-as-item" class="menu-iconic" + label="&zotero.contextMenu.saveLinkAsItem;" hidden="true" + oncommand="ZoteroPane.addItemFromURL(window.gContextMenu.linkURL, 'temporaryPDFHack')"/> + <menuitem id="zotero-context-save-image-as-item" class="menu-iconic" + label="&zotero.contextMenu.saveImageAsItem;" hidden="true" + oncommand="ZoteroPane.addItemFromURL(window.gContextMenu.onImage ? (window.gContextMenu.mediaURL ? window.gContextMenu.mediaURL : window.gContextMenu.imageURL) : window.gContextMenu.bgImageURL, 'artwork')"/> + </menupopup> + </menu> + </popup> + + <stack id="zotero-pane-stack"> + + <!-- Barrier to prevent tabbing into Zotero pane when busy --> + <box id="zotero-pane-tab-catcher-top" hidden="true" align="center" pack="center" style="opacity: 0"> + <checkbox/> + </box> + + <hbox id="zotero-pane" + onkeydown="ZoteroPane.handleKeyDown(event, this.id)" + onkeyup="ZoteroPane.handleKeyUp(event, this.id)" + chromedir="&locale.dir;"> + <popupset> + <menupopup id="zotero-collectionmenu" onpopupshowing="ZoteroPane.buildCollectionContextMenu();"> + <menuitem label="&zotero.toolbar.newCollection.label;" oncommand="ZoteroPane.newCollection()"/> + <menuitem label="&zotero.toolbar.newSavedSearch.label;" oncommand="ZoteroPane.newSearch()"/> + <menuitem label="&zotero.toolbar.newSubcollection.label;" oncommand="ZoteroPane.newCollection(ZoteroPane.getSelectedCollection().id)"/> + <menuseparator/> + <menuitem oncommand="ZoteroPane.editSelectedCollection();"/> + <menuitem oncommand="ZoteroPane.deleteSelectedCollection();"/> + <menuseparator/> + <menuitem oncommand="Zotero_File_Interface.exportCollection();"/> + <menuitem oncommand="Zotero_File_Interface.bibliographyFromCollection();"/> + <menuitem label="&zotero.toolbar.export.label;" oncommand="Zotero_File_Interface.exportFile()"/> + <menuitem oncommand="Zotero_Report_Interface.loadCollectionReport()"/> + <menuitem label="&zotero.toolbar.emptyTrash.label;" oncommand="ZoteroPane.emptyTrash();"/> + <menuitem label="&zotero.toolbar.newCollection.label;" oncommand="ZoteroPane.createCommonsBucket();"/><!--TODO localize --> + <menuitem label="Refresh" oncommand="ZoteroPane.refreshCommonsBucket();"/><!--TODO localize --> + </menupopup> + <menupopup id="zotero-itemmenu" onpopupshowing="ZoteroPane.buildItemContextMenu();"> + <menuitem label="&zotero.items.menu.showInLibrary;" oncommand="ZoteroPane.selectItem(this.parentNode.getAttribute('itemID'), true)"/> + <menuseparator/> + <!-- with icon: <menuitem class="menuitem-iconic" id="zotero-menuitem-note" label="&zotero.items.menu.attach.note;" oncommand="ZoteroPane.newNote(false, this.parentNode.getAttribute('itemID'))"/>--> + <menuitem label="&zotero.items.menu.attach.note;" oncommand="ZoteroPane.newNote(false, this.parentNode.getAttribute('itemID'))"/> + <menu label="&zotero.items.menu.attach;"> + <menupopup id="zotero-add-attachment-popup"> + <menuitem class="menuitem-iconic zotero-menuitem-attachments-snapshot" label="&zotero.items.menu.attach.snapshot;" oncommand="var itemID = parseInt(this.parentNode.parentNode.parentNode.getAttribute('itemID')); ZoteroPane.addAttachmentFromPage(false, itemID)"/> + <menuitem class="menuitem-iconic zotero-menuitem-attachments-web-link" label="&zotero.items.menu.attach.link;" oncommand="var itemID = parseInt(this.parentNode.parentNode.parentNode.getAttribute('itemID')); ZoteroPane.addAttachmentFromPage(true, itemID)"/> + <menuitem class="menuitem-iconic zotero-menuitem-attachments-file" label="&zotero.items.menu.attach.file;" oncommand="var itemID = parseInt(this.parentNode.parentNode.parentNode.getAttribute('itemID')); ZoteroPane.addAttachmentFromDialog(false, itemID);"/> + <menuitem class="menuitem-iconic zotero-menuitem-attachments-link" label="&zotero.items.menu.attach.fileLink;" oncommand="var itemID = parseInt(this.parentNode.parentNode.parentNode.getAttribute('itemID')); ZoteroPane.addAttachmentFromDialog(true, itemID);"/> + </menupopup> + </menu> + <menuseparator/> + <menuitem label="&zotero.items.menu.duplicateItem;" oncommand="ZoteroPane.duplicateSelectedItem();"/> + <menuitem oncommand="ZoteroPane.deleteSelectedItems();"/> + <menuitem oncommand="ZoteroPane.deleteSelectedItems(true);"/> + <menuseparator/> + <menuitem oncommand="Zotero_File_Interface.exportItems();"/> + <menuitem oncommand="Zotero_File_Interface.bibliographyFromItems();"/> + <menuitem oncommand="Zotero_Report_Interface.loadItemReport()"/> + <menuseparator/> + <menuitem oncommand="ZoteroPane.createParentItemsFromSelected();"/> + <menuitem oncommand="Zotero_RecognizePDF.recognizeSelected();"/> + <menuitem oncommand="ZoteroPane.renameSelectedAttachmentsFromParents()"/> + <menuitem oncommand="ZoteroPane.reindexItem();"/> + </menupopup> + </popupset> + + <vbox id="zotero-collections-pane" persist="width" flex="1"> + <!-- This extra vbox prevents the toolbar from getting compressed when resizing + the tag selector to max height --> + <vbox flex="1"> + <hbox class="toolbar"> + <toolbarbutton id="zotero-tb-collection-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newCollection.label;" oncommand="ZoteroPane.newCollection()"/> + <toolbarbutton id="zotero-tb-group-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newGroup;" oncommand="ZoteroPane.newGroup()"/> + <spacer flex="1"/> + <toolbarbutton id="zotero-tb-actions-menu" class="zotero-tb-button" tooltiptext="&zotero.toolbar.actions.label;" type="menu"> + <menupopup id="zotero-tb-actions-popup" onpopupshowing="document.getElementById('cmd_zotero_reportErrors').setAttribute('disabled', Zotero.getErrors().length == 0)"> + <menuitem id="zotero-tb-actions-import" label="&zotero.toolbar.import.label;" oncommand="Zotero_File_Interface.importFile();"/> + <menuitem id="zotero-tb-actions-import-clipboard" label="&zotero.toolbar.importFromClipboard;" oncommand="Zotero_File_Interface.importFromClipboard();" /> + <menuitem id="zotero-tb-actions-export" label="&zotero.toolbar.export.label;" oncommand="Zotero_File_Interface.exportFile();"/> + <menuitem id="zotero-tb-actions-rtfScan" label="&zotero.toolbar.rtfScan.label;" oncommand="window.openDialog('chrome://zotero/content/rtfScan.xul', 'rtfScan', 'chrome,centerscreen')"/> + <menuitem hidden="true" id="zotero-tb-actions-zeroconf-update" + label="Search for Shared Libraries" oncommand="Zotero.Zeroconf.findInstances()"/> + <menuseparator id="zotero-tb-actions-plugins-separator"/> + <menuitem id="zotero-tb-actions-timeline" label="&zotero.toolbar.timeline.label;" oncommand="Zotero_Timeline_Interface.loadTimeline()"/> + <!-- TODO: localize <menuitem id="zotero-tb-actions-duplicate" label="&zotero.toolbar.duplicate.label;" oncommand="ZoteroPane.showDuplicates()"/>--> + <menuitem id="zotero-tb-actions-showDuplicates" label="Show Duplicates" oncommand="ZoteroPane.showDuplicates()" hidden="true"/> + <menuseparator hidden="true" id="zotero-tb-actions-sync-separator"/> + <menuitem hidden="true" label="WebDAV Sync Debugging" disabled="true"/> + <menuitem hidden="true" label=" Purge Deleted Storage Files" oncommand="Zotero.Sync.Storage.purgeDeletedStorageFiles('webdav', function(results) { Zotero.debug(results); })"/> + <menuitem hidden="true" label=" Purge Orphaned Storage Files" oncommand="Zotero.Sync.Storage.purgeOrphanedStorageFiles('webdav', function(results) { Zotero.debug(results); })"/> + <menuseparator id="zotero-tb-actions-separator"/> + <menuitem id="zotero-tb-actions-prefs" label="&zotero.toolbar.preferences.label;" + oncommand="ZoteroPane.openPreferences()"/> + <menuitem id="zotero-tb-actions-reportErrors" command="cmd_zotero_reportErrors" disabled="true"/> + <menuitem id="zotero-tb-actions-support" label="&zotero.toolbar.supportAndDocumentation;" oncommand="gBrowser.selectedTab = gBrowser.addTab('http://www.zotero.org/support/')"/> + <menuitem id="zotero-tb-actions-about" label="&zotero.toolbar.about.label;" oncommand="window.openDialog('chrome://zotero/content/about.xul', 'about', 'chrome')"/> + </menupopup> + </toolbarbutton> + </hbox> + <tree id="zotero-collections-tree" hidecolumnpicker="true" context="zotero-collectionmenu" + onmouseover="ZoteroPane.collectionsView.setHighlightedRows();" + onkeypress="ZoteroPane.handleKeyPress(event, this.id)" + onselect="ZoteroPane.onCollectionSelected();" seltype="cell" + ondragstart="if (event.target.localName == 'treechildren') { ZoteroPane.collectionsView.onDragStart(event); }" + ondragenter="return ZoteroPane.collectionsView.onDragEnter(event)" + ondragover="return ZoteroPane.collectionsView.onDragOver(event)" + ondrop="return ZoteroPane.collectionsView.onDrop(event)" + flex="1"> + <treecols> + <treecol + id="zotero-collections-name-column" + flex="1" + primary="true" + hideheader="true"/> + </treecols> + <treechildren/> + </tree> + </vbox> + <splitter id="zotero-tags-splitter" onmouseup="ZoteroPane.updateTagSelectorSize()" collapse="after"> + <grippy oncommand="ZoteroPane.toggleTagSelector()"/> + </splitter> + <zoterotagselector id="zotero-tag-selector" persist="height,collapsed,showAutomatic,filterToScope" + oncommand="ZoteroPane.updateTagFilter()"/> + </vbox> + + <splitter id="zotero-tree-splitter" resizebefore="closest" resizeafter="closest" collapse="before" + onmousemove="document.getElementById('zotero-items-toolbar').setAttribute('state', this.getAttribute('state'));"> + <grippy/> + </splitter> + + <vbox id="zotero-items-pane" persist="width" flex="1"> + <hbox class="toolbar" id="zotero-items-toolbar" align="center"> + <toolbarbutton id="zotero-tb-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newItem.label;" type="menu"> + <!-- New Item drop-down built in overlay.js::onLoad() --> + <menupopup> + <menuseparator/> + <menuitem label="&zotero.toolbar.attachment.linked;" oncommand="ZoteroPane.addAttachmentFromDialog(true);" tooltiptext=""/> + <menuitem label="&zotero.toolbar.attachment.add;" oncommand="ZoteroPane.addAttachmentFromDialog();" tooltiptext=""/> + <menuseparator/> + <menu label="&zotero.toolbar.moreItemTypes.label;" tooltiptext=""> + <menupopup id="zotero-tb-add-more"/> + </menu> + </menupopup> + </toolbarbutton> + <toolbarbutton id="zotero-tb-item-from-page" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newItemFromPage.label;" oncommand="ZoteroPane.addItemFromPage('temporaryPDFHack', event.shiftKey ? !Zotero.Prefs.get('automaticSnapshots') : null)"/> + <toolbarbutton id="zotero-tb-lookup" class="zotero-tb-button" tooltiptext="&zotero.toolbar.lookup.label;" oncommand="ZoteroPane.openLookupWindow()"/> + <!--<toolbarbutton id="zotero-tb-note-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.note.standalone;" oncommand="ZoteroPane.newNote(event.shiftKey);"/>--> + <toolbarbutton id="zotero-tb-note-add" class="zotero-tb-button" tooltiptext="New Note" type="menu"> + <menupopup onpopupshowing="ZoteroPane.updateNoteButtonMenu()"> + <menuitem label="Add Standalone Note" oncommand="ZoteroPane.newNote(event.shiftKey);"/> + <menuitem id="zotero-tb-add-child-note" label="Add Child Note" oncommand="var selected = ZoteroPane.getSelectedItems()[0]; var parent = selected.getSource(); parent = parent ? parent : selected.id; ZoteroPane.newNote(event.shiftKey, parent);"/> + </menupopup> + </toolbarbutton> + <toolbarbutton id="zotero-tb-attachment-add" class="zotero-tb-button" tooltiptext="New Child Attachment" type="menu"> + <menupopup onpopupshowing="ZoteroPane.updateAttachmentButtonMenu(this)"> + <menuitem class="menuitem-iconic zotero-menuitem-attachments-snapshot" label="&zotero.items.menu.attach.snapshot;" oncommand="var itemID = ZoteroPane.getSelectedItems()[0].id; ZoteroPane.addAttachmentFromPage(false, itemID)"/> + <menuitem class="menuitem-iconic zotero-menuitem-attachments-web-link" label="&zotero.items.menu.attach.link;" oncommand="var itemID = ZoteroPane.getSelectedItems()[0].id; ZoteroPane.addAttachmentFromPage(true, itemID)"/> + <menuitem class="menuitem-iconic zotero-menuitem-attachments-file" label="Attach Stored Copy of File..." oncommand="var itemID = ZoteroPane.getSelectedItems()[0].id; ZoteroPane.addAttachmentFromDialog(false, itemID);"/> + <menuitem class="menuitem-iconic zotero-menuitem-attachments-link" label="Attach Link to File..." oncommand="var itemID = ZoteroPane.getSelectedItems()[0].id; ZoteroPane.addAttachmentFromDialog(true, itemID);"/> + </menupopup> + </toolbarbutton> + <toolbarseparator/> + <toolbarbutton id="zotero-tb-advanced-search" class="zotero-tb-button" tooltiptext="&zotero.toolbar.advancedSearch;" oncommand="ZoteroPane.openAdvancedSearchWindow()"/> + <spacer flex="1"/> + <textbox id="zotero-tb-search" type="search" timeout="250" command="cmd_zotero_search" dir="reverse" + onkeypress="ZoteroPane.handleSearchKeypress(this, event)" + oninput="ZoteroPane.handleSearchInput(this, event)"> + </textbox> + </hbox> + + <deck id="zotero-items-pane-content" selectedIndex="0" flex="1"> + <tree + id="zotero-items-tree" context="zotero-itemmenu" + enableColumnDrag="true" + onfocus="if (ZoteroPane.itemsView.rowCount &amp;&amp; !ZoteroPane.itemsView.selection.count) { ZoteroPane.itemsView.selection.select(0); }" + onkeypress="ZoteroPane.handleKeyPress(event, this.id)" + onselect="ZoteroPane.itemSelected();" + ondragstart="if (event.target.localName == 'treechildren') { ZoteroPane.itemsView.onDragStart(event); }" + ondragenter="return ZoteroPane.itemsView.onDragEnter(event)" + ondragover="return ZoteroPane.itemsView.onDragOver(event)" + ondragdrop="return ZoteroPane.itemsView.onDrop(event)" + flex="1"> + <treecols> + <treecol + id="zotero-items-column-title" primary="true" + label="&zotero.items.title_column;" + flex="4" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-firstCreator" + label="&zotero.items.creator_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-type" hidden="true" + label="&zotero.items.type_column;" + width="40" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-date" hidden="true" + label="&zotero.items.date_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-year" hidden="true" + label="&zotero.items.year_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-publisher" hidden="true" + label="&zotero.items.publisher_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-publicationTitle" hidden="true" + label="&zotero.items.publication_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-journalAbbreviation" hidden="true" + label="&zotero.items.journalAbbr_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-language" hidden="true" + label="&zotero.items.language_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-accessDate" hidden="true" + label="&zotero.items.accessDate_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-libraryCatalog" hidden="true" + label="&zotero.items.libraryCatalog_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-callNumber" hidden="true" + label="&zotero.items.callNumber_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-rights" hidden="true" + label="&zotero.items.rights_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-dateAdded" hidden="true" + label="&zotero.items.dateAdded_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-dateModified" hidden="true" + label="&zotero.items.dateModified_column;" + flex="1" persist="width ordinal hidden sortActive sortDirection"/> + <splitter class="tree-splitter"/> + <treecol + id="zotero-items-column-numChildren" + label="&zotero.items.numChildren_column;" + persist="width ordinal hidden sortActive sortDirection"/> + </treecols> + <treechildren/> + </tree> + + <!-- Label for displaying messages when items pane is hidden + (e.g. "Advanced search mode — press Enter to search.")--> + <vbox id="zotero-items-pane-message-box" pack="center" align="center"/> + </deck> + </vbox> + + <splitter id="zotero-view-splitter" resizebefore="closest" resizeafter="closest"/> + + <vbox id="zotero-item-pane" persist="width"> + <hbox class="toolbar" align="center" pack="end"> + <hbox id="zotero-tb-sync-progress-box" hidden="true" align="center"> + <toolbarbutton id="zotero-tb-sync-storage-cancel" + tooltiptext="Cancel Storage Sync" + oncommand="Zotero.Sync.Storage.QueueManager.cancel()"/> + <progressmeter id="zotero-tb-sync-progress" mode="determined" + value="0" tooltip="zotero-tb-sync-progress-tooltip"> + </progressmeter> + <tooltip id="zotero-tb-sync-progress-tooltip" noautohide="true"> + <grid> + <columns> + <column/> + <column/> + </columns> + <rows> + <row> + <label value="&zotero.sync.storage.progress;"/> + <label id="zotero-tb-sync-progress-tooltip-progress"/> + </row> + <row> + <label value="&zotero.sync.storage.downloads;"/> + <label + id="zotero-tb-sync-progress-tooltip-downloads"/> + </row> + <row> + <label value="&zotero.sync.storage.uploads;"/> + <label + id="zotero-tb-sync-progress-tooltip-uploads"/> + </row> + </rows> + </grid> + </tooltip> + </hbox> + <toolbarbutton id="zotero-tb-sync-warning" hidden="true"/> + <toolbarbutton id="zotero-tb-sync" class="zotero-tb-button" tooltip="_child" + oncommand="Zotero.Sync.Server.canAutoResetClient = true; Zotero.Sync.Server.manualSyncRequired = false; Zotero.Sync.Runner.sync()"> + <tooltip + id="zotero-tb-sync-tooltip" + onpopupshowing="Zotero.Sync.Runner.registerSyncStatusLabel(this.firstChild.nextSibling, this.firstChild.nextSibling.nextSibling)" + onpopuphiding="Zotero.Sync.Runner.registerSyncStatusLabel()" + noautohide="true"> + <label value="&zotero.sync.button;"/> + <label id="zotero-tb-sync-status" hidden="true"/> + <label id="zotero-tb-sync-last-sync"/> + </tooltip> + </toolbarbutton> + <toolbarseparator id="zotero-fullscreen-close-separator"/> + <toolbarbutton id="zotero-tb-fullscreen" tooltiptext="&zotero.toolbar.fullscreen.tooltip;" oncommand="ZoteroPane.toggleTab();" class="zotero-tb-button"/> + <toolbarbutton id="zotero-close-button" class="tabs-closebutton" oncommand="ZoteroOverlay.toggleDisplay()"/> + </hbox> + <!-- TODO: localize --> + <button id="zotero-item-restore-button" label="Restore to Library" + oncommand="ZoteroPane.restoreSelectedItems()" hidden="true"/> + <!-- TODO: localize --> + <button id="zotero-item-show-original" label="Show Original" + oncommand="ZoteroPane.showOriginalItem()" hidden="true"/> + <deck id="zotero-item-pane-content" selectedIndex="0" flex="1"> + <groupbox pack="center" align="center"> + <label id="zotero-view-selected-label"/> + </groupbox> + <tabbox id="zotero-view-tabbox" flex="1" onselect="if (!ZoteroPane.collectionsView.selection || event.originalTarget.localName != 'tabpanels') { return; }; ZoteroItemPane.viewItem(ZoteroPane.getSelectedItems()[0], ZoteroPane.collectionsView.editable ? 'edit' : 'view', this.selectedIndex)"> + <tabs> + <tab label="&zotero.tabs.info.label;"/> + <tab label="&zotero.tabs.notes.label;"/> + <tab label="&zotero.tabs.tags.label;"/> + <tab label="&zotero.tabs.related.label;"/> + </tabs> + <tabpanels id="zotero-view-item" flex="1"/> + </tabbox> + <!-- Note info pane --> + <groupbox id="zotero-view-note" flex="1"> + <zoteronoteeditor id="zotero-note-editor" flex="1"/> + <button id="zotero-view-note-button" label="&zotero.notes.separate;" oncommand="ZoteroPane.openNoteWindow(this.getAttribute('noteID')); if(this.hasAttribute('sourceID')) ZoteroPane.selectItem(this.getAttribute('sourceID'));"/> + </groupbox> + <!-- Attachment info pane --> + <groupbox flex="1"> + <zoteroattachmentbox id="zotero-attachment-box" flex="1"/> + </groupbox> + </deck> + </vbox> + </hbox> + + <!-- Barrier to prevent tabbing into Zotero pane when busy --> + <box id="zotero-pane-tab-catcher-bottom" hidden="true" align="center" pack="center" style="opacity: 0"> + <checkbox/> + </box> + + <stack id="zotero-pane-overlay" flex="1" hidden="true"> + <box style="background: black; opacity: .3" flex="1"/> + + <deck id="zotero-pane-overlay-deck" flex="1"> + <box id="zotero-pane-progress" flex="1" align="center" pack="center"> + <box style="background: white; -moz-border-radius: 1px; -moz-box-shadow: gray 4px 6px 4px;" width="300" height="30"> + <vbox style="padding:10px" flex="1"> + <label id="zotero-pane-progress-label"/> + <progressmeter id="zotero-pane-progressmeter" mode="undetermined"/> + </vbox> + </box> + </box> + </deck> + </stack> + + </stack> +</overlay> diff --git a/chrome/locale/en-US/zotero/preferences.dtd b/chrome/locale/en-US/zotero/preferences.dtd @@ -7,6 +7,9 @@ <!ENTITY zotero.preferences.prefpane.general "General"> <!ENTITY zotero.preferences.userInterface "User Interface"> +<!ENTITY zotero.preferences.showIn "Load Zotero in:"> +<!ENTITY zotero.preferences.showIn.browserPane "Browser pane"> +<!ENTITY zotero.preferences.showIn.separateTab "Separate tab"> <!ENTITY zotero.preferences.statusBarIcon "Status bar icon:"> <!ENTITY zotero.preferences.statusBarIcon.none "None"> <!ENTITY zotero.preferences.fontSize "Font size:"> diff --git a/chrome/skin/default/zotero/overlay.css b/chrome/skin/default/zotero/overlay.css @@ -263,8 +263,6 @@ #zotero-pane-stack[fullscreenmode="true"] #zotero-tb-fullscreen { list-style-image: url('chrome://zotero/skin/toolbar-fullscreen-top.png'); - background: #666666; - -moz-border-radius: 6px; } #zotero-tb-search diff --git a/chrome/skin/default/zotero/preferences.css b/chrome/skin/default/zotero/preferences.css @@ -62,6 +62,10 @@ grid row hbox:first-child { width: 90px; } +#showIn radio +{ + width: 135px; +} #fontSize radio .radio-icon, #statusBarIcon radio .radio-icon { diff --git a/chrome/skin/default/zotero/timeline/timeline.html b/chrome/skin/default/zotero/timeline/timeline.html @@ -83,10 +83,8 @@ .getService(Components.interfaces.nsIWindowMediator); var win = wm.getMostRecentWindow('navigator:browser'); var zp = win.ZoteroPane; - if (!zp.isShowing()) { - zp.toggleDisplay(); - } - zp.selectItem(evt.getDescription()); + zp.show(); + zp.getActiveZoteroPane().selectItem(evt.getDescription()); } document.write("<title>" + getString("general.title") + "</title>"); diff --git a/chrome/skin/default/zotero/toolbar-fullscreen-bottom.png b/chrome/skin/default/zotero/toolbar-fullscreen-bottom.png Binary files differ. diff --git a/components/zotero-protocol-handler.js b/components/zotero-protocol-handler.js @@ -840,10 +840,7 @@ function ChromeExtensionHandler() { var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator); var win = wm.getMostRecentWindow(null); - - if(!win.ZoteroPane.isShowing()){ - win.ZoteroPane.toggleDisplay(); - } + win.ZoteroPane.show(); var lkh = Zotero.Items.parseLibraryKeyHash(id); if (lkh) { @@ -859,7 +856,7 @@ function ChromeExtensionHandler() { return; } - win.ZoteroPane.selectItem(item.id); + win.ZoteroPane.getActiveZoteroPane().selectItem(item.id); } catch (e){ Zotero.debug(e); @@ -885,26 +882,7 @@ function ChromeExtensionHandler() { var win = Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator) .getMostRecentWindow("navigator:browser"); - var zp = win.ZoteroPane; - - // When using fullscreen as home page, Zotero pane is reset to - // 0 height, so get saved height and set it below - var pane = win.document.getElementById('zotero-pane'); - var height = pane.getAttribute('savedHeight'); - - zp.fullScreen(true); - - if(!zp.isShowing()) { - zp.toggleDisplay(); - } - - pane.setAttribute('height', height); - - // FIXME: The above should run in a callback after about:blank - // is loaded so that the window title is set correctly, but I - // can't get the event handlers to work. - D.S. - - win.loadURI("about:blank"); + win.loadURI("chrome://zotero/content/tab.xul"); } catch (e) { Zotero.debug(e); diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js @@ -18,6 +18,7 @@ pref("extensions.zotero.debug.time", false); pref("extensions.zotero.automaticScraperUpdates",true); pref("extensions.zotero.zoteroDotOrgVersionHeader", true); pref("extensions.zotero.cacheTranslatorData",true); +pref("extensions.zotero.showIn", 1); pref("extensions.zotero.statusBarIcon", 2); pref("extensions.zotero.browserContentContextMenu", true); pref("extensions.zotero.openURL.resolver","http://worldcatlibraries.org/registry/gateway");