| | 1 | /* |
| | 2 | ***** BEGIN LICENSE BLOCK ***** |
| | 3 | |
| | 4 | Copyright © 2009 Center for History and New Media |
| | 5 | George Mason University, Fairfax, Virginia, USA |
| | 6 | http://zotero.org |
| | 7 | |
| | 8 | This file is part of Zotero. |
| | 9 | |
| | 10 | Zotero is free software: you can redistribute it and/or modify |
| | 11 | it under the terms of the GNU General Public License as published by |
| | 12 | the Free Software Foundation, either version 3 of the License, or |
| | 13 | (at your option) any later version. |
| | 14 | |
| | 15 | Zotero is distributed in the hope that it will be useful, |
| | 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | 18 | GNU General Public License for more details. |
| | 19 | |
| | 20 | You should have received a copy of the GNU General Public License |
| | 21 | along with Zotero. If not, see <http://www.gnu.org/licenses/>. |
| | 22 | |
| | 23 | ***** END LICENSE BLOCK ***** |
| | 24 | */ |
| | 25 | |
| | 26 | /* |
| | 27 | * This object contains the various functions for the interface |
| | 28 | */ |
| | 29 | var ZoteroPane = new function() |
| | 30 | { |
| | 31 | this.collectionsView = false; |
| | 32 | this.itemsView = false; |
| | 33 | this.__defineGetter__('loaded', function () _loaded); |
| | 34 | |
| | 35 | //Privileged methods |
| | 36 | this.onLoad = onLoad; |
| | 37 | this.onUnload = onUnload; |
| | 38 | this.toggleDisplay = toggleDisplay; |
| | 39 | this.isShowing = isShowing; |
| | 40 | this.fullScreen = fullScreen; |
| | 41 | this.isFullScreen = isFullScreen; |
| | 42 | this.handleKeyDown = handleKeyDown; |
| | 43 | this.handleKeyUp = handleKeyUp; |
| | 44 | this.setHighlightedRowsCallback = setHighlightedRowsCallback; |
| | 45 | this.handleKeyPress = handleKeyPress; |
| | 46 | this.newItem = newItem; |
| | 47 | this.newCollection = newCollection; |
| | 48 | this.newSearch = newSearch; |
| | 49 | this.openAdvancedSearchWindow = openAdvancedSearchWindow; |
| | 50 | this.toggleTagSelector = toggleTagSelector; |
| | 51 | this.updateTagSelectorSize = updateTagSelectorSize; |
| | 52 | this.getTagSelection = getTagSelection; |
| | 53 | this.clearTagSelection = clearTagSelection; |
| | 54 | this.updateTagFilter = updateTagFilter; |
| | 55 | this.onCollectionSelected = onCollectionSelected; |
| | 56 | this.itemSelected = itemSelected; |
| | 57 | this.reindexItem = reindexItem; |
| | 58 | this.duplicateSubstitute = duplicateSubstitute; |
| | 59 | this.duplicateMarkItem = duplicateMarkItem; |
| | 60 | this.duplicateUnmarkItem = duplicateUnmarkItem; |
| | 61 | this.duplicateUnmarkAll = duplicateUnmarkAll; |
| | 62 | this.duplicateSelectedItem = duplicateSelectedItem; |
| | 63 | this.deleteSelectedCollection = deleteSelectedCollection; |
| | 64 | this.editSelectedCollection = editSelectedCollection; |
| | 65 | this.copySelectedItemsToClipboard = copySelectedItemsToClipboard; |
| | 66 | this.clearQuicksearch = clearQuicksearch; |
| | 67 | this.handleSearchKeypress = handleSearchKeypress; |
| | 68 | this.handleSearchInput = handleSearchInput; |
| | 69 | this.search = search; |
| | 70 | this.selectItem = selectItem; |
| | 71 | this.getSelectedCollection = getSelectedCollection; |
| | 72 | this.getSelectedSavedSearch = getSelectedSavedSearch; |
| | 73 | this.getSelectedItems = getSelectedItems; |
| | 74 | this.getSortedItems = getSortedItems; |
| | 75 | this.getSortField = getSortField; |
| | 76 | this.getSortDirection = getSortDirection; |
| | 77 | this.buildItemContextMenu = buildItemContextMenu; |
| | 78 | this.loadURI = loadURI; |
| | 79 | this.setItemsPaneMessage = setItemsPaneMessage; |
| | 80 | this.clearItemsPaneMessage = clearItemsPaneMessage; |
| | 81 | this.contextPopupShowing = contextPopupShowing; |
| | 82 | this.openNoteWindow = openNoteWindow; |
| | 83 | this.addTextToNote = addTextToNote; |
| | 84 | this.addAttachmentFromDialog = addAttachmentFromDialog; |
| | 85 | this.viewAttachment = viewAttachment; |
| | 86 | this.viewSelectedAttachment = viewSelectedAttachment; |
| | 87 | this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog; |
| | 88 | this.relinkAttachment = relinkAttachment; |
| | 89 | this.reportErrors = reportErrors; |
| | 90 | this.displayErrorMessage = displayErrorMessage; |
| | 91 | |
| | 92 | const DEFAULT_ZPANE_HEIGHT = 300; |
| | 93 | const COLLECTIONS_HEIGHT = 32; // minimum height of the collections pane and toolbar |
| | 94 | |
| | 95 | var self = this; |
| | 96 | var _loaded = false; |
| | 97 | var titlebarcolorState, toolbarCollapseState, titleState; |
| | 98 | |
| | 99 | // Also needs to be changed in collectionTreeView.js |
| | 100 | var _lastViewedFolderRE = /^(?:(C|S|G)([0-9]+)|L)$/; |
| | 101 | |
| | 102 | /* |
| | 103 | * Called when the window is open |
| | 104 | */ |
| | 105 | function onLoad() |
| | 106 | { |
| | 107 | if (!Zotero || !Zotero.initialized) { |
| | 108 | return; |
| | 109 | } |
| | 110 | |
| | 111 | if (Zotero.locked) { |
| | 112 | return; |
| | 113 | } |
| | 114 | _loaded = true; |
| | 115 | |
| | 116 | Zotero.setFontSize(document.getElementById('zotero-pane')) |
| | 117 | |
| | 118 | if (Zotero.isMac) { |
| | 119 | //document.getElementById('zotero-tb-actions-zeroconf-update').setAttribute('hidden', false); |
| | 120 | document.getElementById('zotero-pane-stack').setAttribute('platform', 'mac'); |
| | 121 | } else if(Zotero.isWin) { |
| | 122 | document.getElementById('zotero-pane-stack').setAttribute('platform', 'win'); |
| | 123 | } |
| | 124 | |
| | 125 | if(Zotero.isFx35) document.documentElement.setAttribute("moz-version", "3.5"); |
| | 126 | |
| | 127 | //Initialize collections view |
| | 128 | this.collectionsView = new Zotero.CollectionTreeView(); |
| | 129 | var collectionsTree = document.getElementById('zotero-collections-tree'); |
| | 130 | collectionsTree.view = this.collectionsView; |
| | 131 | collectionsTree.controllers.appendController(new Zotero.CollectionTreeCommandController(collectionsTree)); |
| | 132 | collectionsTree.addEventListener("click", ZoteroPane.onTreeClick, true); |
| | 133 | |
| | 134 | var itemsTree = document.getElementById('zotero-items-tree'); |
| | 135 | itemsTree.controllers.appendController(new Zotero.ItemTreeCommandController(itemsTree)); |
| | 136 | itemsTree.addEventListener("click", ZoteroPane.onTreeClick, true); |
| | 137 | |
| | 138 | this.buildItemTypeMenus(); |
| | 139 | |
| | 140 | var menu = document.getElementById("contentAreaContextMenu"); |
| | 141 | menu.addEventListener("popupshowing", ZoteroPane.contextPopupShowing, false); |
| | 142 | |
| | 143 | Zotero.Keys.windowInit(document); |
| | 144 | |
| | 145 | if (Zotero.restoreFromServer) { |
| | 146 | Zotero.restoreFromServer = false; |
| | 147 | |
| | 148 | setTimeout(function () { |
| | 149 | var pr = Components.classes["@mozilla.org/network/default-prompt;1"] |
| | 150 | .getService(Components.interfaces.nsIPrompt); |
| | 151 | var buttonFlags = (pr.BUTTON_POS_0) * (pr.BUTTON_TITLE_IS_STRING) |
| | 152 | + (pr.BUTTON_POS_1) * (pr.BUTTON_TITLE_CANCEL); |
| | 153 | var index = pr.confirmEx( |
| | 154 | "Zotero Restore", |
| | 155 | "The local Zotero database has been cleared." |
| | 156 | + " " |
| | 157 | + "Would you like to restore from the Zotero server now?", |
| | 158 | buttonFlags, |
| | 159 | "Sync Now", |
| | 160 | null, null, null, {} |
| | 161 | ); |
| | 162 | |
| | 163 | if (index == 0) { |
| | 164 | Zotero.Sync.Server.sync({ |
| | 165 | onSuccess: function () { |
| | 166 | Zotero.Sync.Runner.setSyncIcon(); |
| | 167 | |
| | 168 | pr.alert( |
| | 169 | "Restore Completed", |
| | 170 | "The local Zotero database has been successfully restored." |
| | 171 | ); |
| | 172 | }, |
| | 173 | |
| | 174 | onError: function (msg) { |
| | 175 | pr.alert( |
| | 176 | "Restore Failed", |
| | 177 | "An error occurred while restoring from the server:\n\n" |
| | 178 | + msg |
| | 179 | ); |
| | 180 | |
| | 181 | Zotero.Sync.Runner.error(msg); |
| | 182 | } |
| | 183 | }); |
| | 184 | } |
| | 185 | }, 1000); |
| | 186 | } |
| | 187 | // If the database was initialized or there are no sync credentials and |
| | 188 | // Zotero hasn't been run before in this profile, display the start page |
| | 189 | // -- this way the page won't be displayed when they sync their DB to |
| | 190 | // another profile or if the DB is initialized erroneously (e.g. while |
| | 191 | // switching data directory locations) |
| | 192 | else if (Zotero.Prefs.get('firstRun2')) { |
| | 193 | if (Zotero.Schema.dbInitialized || !Zotero.Sync.Server.enabled) { |
| | 194 | setTimeout(function () { |
| | 195 | var url = "http://zotero.org/start"; |
| | 196 | gBrowser.selectedTab = gBrowser.addTab(url); |
| | 197 | }, 400); |
| | 198 | } |
| | 199 | Zotero.Prefs.set('firstRun2', false); |
| | 200 | try { |
| | 201 | Zotero.Prefs.clear('firstRun'); |
| | 202 | } |
| | 203 | catch (e) {} |
| | 204 | } |
| | 205 | |
| | 206 | // Hide sync debugging menu by default |
| | 207 | if (Zotero.Prefs.get('sync.debugMenu')) { |
| | 208 | var sep = document.getElementById('zotero-tb-actions-sync-separator'); |
| | 209 | sep.hidden = false; |
| | 210 | sep.nextSibling.hidden = false; |
| | 211 | sep.nextSibling.nextSibling.hidden = false; |
| | 212 | sep.nextSibling.nextSibling.nextSibling.hidden = false; |
| | 213 | } |
| | 214 | |
| | 215 | if(Zotero.isStandalone) { |
| | 216 | this.toggleDisplay(); |
| | 217 | this.fullScreen(true); |
| | 218 | } |
| | 219 | } |
| | 220 | |
| | 221 | |
| | 222 | this.buildItemTypeMenus = function () { |
| | 223 | // |
| | 224 | // Create the New Item (+) menu with each item type |
| | 225 | // |
| | 226 | var addMenu = document.getElementById('zotero-tb-add').firstChild; |
| | 227 | var moreMenu = document.getElementById('zotero-tb-add-more'); |
| | 228 | |
| | 229 | // Remove all nodes, in case we're reloading |
| | 230 | var options = addMenu.getElementsByAttribute("class", "zotero-tb-add"); |
| | 231 | while (options.length) { |
| | 232 | var p = options[0].parentNode; |
| | 233 | p.removeChild(options[0]); |
| | 234 | } |
| | 235 | |
| | 236 | var separator = addMenu.firstChild; |
| | 237 | |
| | 238 | // Sort by localized name |
| | 239 | var t = Zotero.ItemTypes.getPrimaryTypes(); |
| | 240 | var itemTypes = []; |
| | 241 | for (var i=0; i<t.length; i++) { |
| | 242 | itemTypes.push({ |
| | 243 | id: t[i].id, |
| | 244 | name: t[i].name, |
| | 245 | localized: Zotero.ItemTypes.getLocalizedString(t[i].id) |
| | 246 | }); |
| | 247 | } |
| | 248 | var collation = Zotero.getLocaleCollation(); |
| | 249 | itemTypes.sort(function(a, b) { |
| | 250 | return collation.compareString(1, a.localized, b.localized); |
| | 251 | }); |
| | 252 | |
| | 253 | for (var i = 0; i<itemTypes.length; i++) { |
| | 254 | var menuitem = document.createElement("menuitem"); |
| | 255 | menuitem.setAttribute("label", itemTypes[i].localized); |
| | 256 | menuitem.setAttribute("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")"); |
| | 257 | menuitem.setAttribute("tooltiptext", ""); |
| | 258 | menuitem.className = "zotero-tb-add"; |
| | 259 | addMenu.insertBefore(menuitem, separator); |
| | 260 | } |
| | 261 | |
| | 262 | |
| | 263 | // |
| | 264 | // Create submenu for secondary item types |
| | 265 | // |
| | 266 | |
| | 267 | // Sort by localized name |
| | 268 | var t = Zotero.ItemTypes.getSecondaryTypes(); |
| | 269 | var itemTypes = []; |
| | 270 | for (var i=0; i<t.length; i++) { |
| | 271 | itemTypes.push({ |
| | 272 | id: t[i].id, |
| | 273 | name: t[i].name, |
| | 274 | localized: Zotero.ItemTypes.getLocalizedString(t[i].id) |
| | 275 | }); |
| | 276 | } |
| | 277 | var collation = Zotero.getLocaleCollation(); |
| | 278 | itemTypes.sort(function(a, b) { |
| | 279 | return collation.compareString(1, a.localized, b.localized); |
| | 280 | }); |
| | 281 | |
| | 282 | for (var i = 0; i<itemTypes.length; i++) { |
| | 283 | var menuitem = document.createElement("menuitem"); |
| | 284 | menuitem.setAttribute("label", itemTypes[i].localized); |
| | 285 | menuitem.setAttribute("oncommand","ZoteroPane.newItem("+itemTypes[i]['id']+")"); |
| | 286 | menuitem.setAttribute("tooltiptext", ""); |
| | 287 | menuitem.className = "zotero-tb-add"; |
| | 288 | moreMenu.appendChild(menuitem); |
| | 289 | } |
| | 290 | } |
| | 291 | |
| | 292 | |
| | 293 | /* |
| | 294 | * Called when the window closes |
| | 295 | */ |
| | 296 | function onUnload() |
| | 297 | { |
| | 298 | if (!Zotero || !Zotero.initialized || !_loaded) { |
| | 299 | return; |
| | 300 | } |
| | 301 | |
| | 302 | var tagSelector = document.getElementById('zotero-tag-selector'); |
| | 303 | tagSelector.unregister(); |
| | 304 | |
| | 305 | this.collectionsView.unregister(); |
| | 306 | if (this.itemsView) |
| | 307 | this.itemsView.unregister(); |
| | 308 | } |
| | 309 | |
| | 310 | /* |
| | 311 | * Hides/displays the Zotero interface |
| | 312 | */ |
| | 313 | function toggleDisplay() |
| | 314 | { |
| | 315 | if (!ZoteroPane.loaded) { |
| | 316 | if (Zotero.locked) { |
| | 317 | var pr = Components.classes["@mozilla.org/network/default-prompt;1"] |
| | 318 | .getService(Components.interfaces.nsIPrompt); |
| | 319 | var msg = Zotero.getString('general.operationInProgress') + '\n\n' + Zotero.getString('general.operationInProgress.waitUntilFinished'); |
| | 320 | pr.alert("", msg); |
| | 321 | return; |
| | 322 | } |
| | 323 | ZoteroPane.onLoad(); |
| | 324 | } |
| | 325 | |
| | 326 | var zoteroPane = document.getElementById('zotero-pane-stack'); |
| | 327 | var zoteroSplitter = document.getElementById('zotero-splitter') |
| | 328 | |
| | 329 | if (zoteroPane.getAttribute('hidden') == 'true') { |
| | 330 | var isHidden = true; |
| | 331 | } |
| | 332 | else if (zoteroPane.getAttribute('collapsed') == 'true') { |
| | 333 | var isCollapsed = true; |
| | 334 | } |
| | 335 | |
| | 336 | if (isHidden || isCollapsed) { |
| | 337 | var makeVisible = true; |
| | 338 | } |
| | 339 | |
| | 340 | // If Zotero not initialized, try to get the error handler |
| | 341 | // or load the default error page |
| | 342 | if (makeVisible && (!Zotero || !Zotero.initialized)) { |
| | 343 | if (Zotero) { |
| | 344 | var errMsg = Zotero.startupError; |
| | 345 | var errFunc = Zotero.startupErrorHandler; |
| | 346 | } |
| | 347 | |
| | 348 | if (!errMsg) { |
| | 349 | // Get the stringbundle manually |
| | 350 | var src = 'chrome://zotero/locale/zotero.properties'; |
| | 351 | var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1']. |
| | 352 | getService(Components.interfaces.nsILocaleService); |
| | 353 | var appLocale = localeService.getApplicationLocale(); |
| | 354 | var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"] |
| | 355 | .getService(Components.interfaces.nsIStringBundleService); |
| | 356 | var stringBundle = stringBundleService.createBundle(src, appLocale); |
| | 357 | |
| | 358 | var errMsg = stringBundle.GetStringFromName('startupError'); |
| | 359 | } |
| | 360 | |
| | 361 | if (errFunc) { |
| | 362 | errFunc(); |
| | 363 | } |
| | 364 | else { |
| | 365 | // TODO: Add a better error page/window here with reporting |
| | 366 | // instructions |
| | 367 | // window.loadURI('chrome://zotero/content/error.xul'); |
| | 368 | var pr = Components.classes["@mozilla.org/network/default-prompt;1"] |
| | 369 | .getService(Components.interfaces.nsIPrompt); |
| | 370 | pr.alert("", errMsg); |
| | 371 | } |
| | 372 | |
| | 373 | return; |
| | 374 | } |
| | 375 | |
| | 376 | zoteroSplitter.setAttribute('hidden', !makeVisible); |
| | 377 | |
| | 378 | // Make sure tags splitter isn't missing for people upgrading from <2.0b7 |
| | 379 | if (makeVisible) { |
| | 380 | document.getElementById('zotero-tags-splitter').collapsed = false; |
| | 381 | } |
| | 382 | |
| | 383 | // Restore fullscreen mode if necessary |
| | 384 | if (makeVisible && isFullScreen()) { |
| | 385 | this.fullScreen(true); |
| | 386 | } |
| | 387 | |
| | 388 | if (zoteroPane.hasAttribute('savedHeight')) { |
| | 389 | var savedHeight = zoteroPane.getAttribute('savedHeight'); |
| | 390 | } |
| | 391 | else { |
| | 392 | var savedHeight = DEFAULT_ZPANE_HEIGHT; |
| | 393 | } |
| | 394 | |
| | 395 | /* |
| | 396 | Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height); |
| | 397 | Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height')); |
| | 398 | Zotero.debug("zoteroPane.getAttribute('minheight'): " + zoteroPane.getAttribute('minheight')); |
| | 399 | Zotero.debug("savedHeight: " + savedHeight); |
| | 400 | */ |
| | 401 | |
| | 402 | if (makeVisible) { |
| | 403 | this.updateTagSelectorSize(); |
| | 404 | |
| | 405 | var max = document.getElementById('appcontent').boxObject.height |
| | 406 | - zoteroSplitter.boxObject.height; |
| | 407 | |
| | 408 | if (isHidden) { |
| | 409 | zoteroPane.setAttribute('height', Math.min(savedHeight, max)); |
| | 410 | zoteroPane.setAttribute('hidden', false); |
| | 411 | } |
| | 412 | else if (isCollapsed) { |
| | 413 | zoteroPane.setAttribute('height', Math.min(savedHeight, max)); |
| | 414 | zoteroPane.setAttribute('collapsed', false); |
| | 415 | } |
| | 416 | |
| | 417 | // Focus the quicksearch on pane open |
| | 418 | setTimeout("document.getElementById('zotero-tb-search').inputField.select();", 1); |
| | 419 | |
| | 420 | // Auto-empty trashed items older than a certain number of days |
| | 421 | var days = Zotero.Prefs.get('trashAutoEmptyDays'); |
| | 422 | if (days) { |
| | 423 | var d = new Date(); |
| | 424 | var deleted = Zotero.Items.emptyTrash(days); |
| | 425 | var d2 = new Date(); |
| | 426 | Zotero.debug("Emptied old items from trash in " + (d2 - d) + " ms"); |
| | 427 | } |
| | 428 | |
| | 429 | var d = new Date(); |
| | 430 | Zotero.purgeDataObjects(); |
| | 431 | var d2 = new Date(); |
| | 432 | Zotero.debug("Purged data tables in " + (d2 - d) + " ms"); |
| | 433 | |
| | 434 | // Auto-sync on pane open |
| | 435 | if (Zotero.Prefs.get('sync.autoSync') && Zotero.Sync.Server.enabled |
| | 436 | && !Zotero.Sync.Server.syncInProgress && !Zotero.Sync.Storage.syncInProgress) { |
| | 437 | setTimeout(function () { |
| | 438 | Zotero.Sync.Runner.sync(true); |
| | 439 | }, 1000); |
| | 440 | } |
| | 441 | } |
| | 442 | else { |
| | 443 | zoteroPane.setAttribute('collapsed', true); |
| | 444 | zoteroPane.height = 0; |
| | 445 | |
| | 446 | document.getElementById('content').setAttribute('collapsed', false); |
| | 447 | |
| | 448 | // turn off full window mode, if it was on |
| | 449 | _setFullWindowMode(false); |
| | 450 | |
| | 451 | // Return focus to the browser content pane |
| | 452 | window.content.window.focus(); |
| | 453 | } |
| | 454 | } |
| | 455 | |
| | 456 | |
| | 457 | function isShowing() { |
| | 458 | var zoteroPane = document.getElementById('zotero-pane-stack'); |
| | 459 | return zoteroPane.getAttribute('hidden') != 'true' && |
| | 460 | zoteroPane.getAttribute('collapsed') != 'true'; |
| | 461 | } |
| | 462 | |
| | 463 | |
| | 464 | function fullScreen(set) |
| | 465 | { |
| | 466 | var zoteroPane = document.getElementById('zotero-pane-stack'); |
| | 467 | |
| | 468 | if (set != undefined) { |
| | 469 | var makeFullScreen = !!set; |
| | 470 | } |
| | 471 | else { |
| | 472 | var makeFullScreen = zoteroPane.getAttribute('fullscreenmode') != 'true'; |
| | 473 | } |
| | 474 | |
| | 475 | // Turn Z-pane flex on to stretch to window in full-screen, but off otherwise so persist works |
| | 476 | zoteroPane.setAttribute('flex', makeFullScreen ? "1" : "0"); |
| | 477 | document.getElementById('content').setAttribute('collapsed', makeFullScreen); |
| | 478 | document.getElementById('zotero-splitter').setAttribute('hidden', makeFullScreen); |
| | 479 | |
| | 480 | zoteroPane.setAttribute('fullscreenmode', makeFullScreen); |
| | 481 | _setFullWindowMode(makeFullScreen); |
| | 482 | } |
| | 483 | |
| | 484 | /** |
| | 485 | * Hides or shows navigation toolbars |
| | 486 | * @param set {Boolean} Whether navigation toolbars should be hidden or shown |
| | 487 | */ |
| | 488 | function _setFullWindowMode(set) { |
| | 489 | // hide or show navigation toolbars |
| | 490 | var toolbox = getNavToolbox(); |
| | 491 | if(set) { |
| | 492 | // the below would be a good thing to do if the whole title bar (and not just the center |
| | 493 | // part) got updated when it happened... |
| | 494 | /*if(Zotero.isMac) { |
| | 495 | titlebarcolorState = document.documentElement.getAttribute("activetitlebarcolor"); |
| | 496 | document.documentElement.removeAttribute("activetitlebarcolor"); |
| | 497 | }*/ |
| | 498 | if(document.title != "Zotero") { |
| | 499 | titleState = document.title; |
| | 500 | document.title = "Zotero"; |
| | 501 | } |
| | 502 | |
| | 503 | if(!toolbarCollapseState) { |
| | 504 | toolbarCollapseState = [node.collapsed for each (node in toolbox.childNodes)]; |
| | 505 | for(var i=0; i<toolbox.childNodes.length; i++) { |
| | 506 | toolbox.childNodes[i].collapsed = true; |
| | 507 | } |
| | 508 | } |
| | 509 | } else { |
| | 510 | /*if(Zotero.isMac) { |
| | 511 | document.documentElement.setAttribute("activetitlebarcolor", titlebarcolorState); |
| | 512 | }*/ |
| | 513 | if(document.title == "Zotero") document.title = titleState; |
| | 514 | |
| | 515 | if(toolbarCollapseState) { |
| | 516 | for(var i=0; i<toolbox.childNodes.length; i++) { |
| | 517 | toolbox.childNodes[i].collapsed = toolbarCollapseState[i]; |
| | 518 | } |
| | 519 | toolbarCollapseState = undefined; |
| | 520 | } |
| | 521 | } |
| | 522 | } |
| | 523 | |
| | 524 | function isFullScreen() { |
| | 525 | return document.getElementById('zotero-pane-stack').getAttribute('fullscreenmode') == 'true'; |
| | 526 | } |
| | 527 | |
| | 528 | |
| | 529 | /* |
| | 530 | * Trigger actions based on keyboard shortcuts |
| | 531 | */ |
| | 532 | function handleKeyDown(event, from) { |
| | 533 | try { |
| | 534 | // Ignore keystrokes outside of Zotero pane |
| | 535 | if (!(event.originalTarget.ownerDocument instanceof XULDocument)) { |
| | 536 | return; |
| | 537 | } |
| | 538 | } |
| | 539 | catch (e) { |
| | 540 | Zotero.debug(e); |
| | 541 | } |
| | 542 | |
| | 543 | if (Zotero.locked) { |
| | 544 | event.preventDefault(); |
| | 545 | return; |
| | 546 | } |
| | 547 | |
| | 548 | if (from == 'zotero-pane') { |
| | 549 | // Highlight collections containing selected items |
| | 550 | // |
| | 551 | // We use Control (17) on Windows because Alt triggers the menubar; |
| | 552 | // otherwise we use Alt/Option (18) |
| | 553 | if ((Zotero.isWin && event.keyCode == 17 && !event.altKey) || |
| | 554 | (!Zotero.isWin && event.keyCode == 18 && !event.ctrlKey) |
| | 555 | && !event.shiftKey && !event.metaKey) { |
| | 556 | |
| | 557 | this.highlightTimer = Components.classes["@mozilla.org/timer;1"]. |
| | 558 | createInstance(Components.interfaces.nsITimer); |
| | 559 | // {} implements nsITimerCallback |
| | 560 | this.highlightTimer.initWithCallback({ |
| | 561 | notify: ZoteroPane.setHighlightedRowsCallback |
| | 562 | }, 225, Components.interfaces.nsITimer.TYPE_ONE_SHOT); |
| | 563 | } |
| | 564 | else if ((Zotero.isWin && event.ctrlKey) || |
| | 565 | (!Zotero.isWin && event.altKey)) { |
| | 566 | if (this.highlightTimer) { |
| | 567 | this.highlightTimer.cancel(); |
| | 568 | this.highlightTimer = null; |
| | 569 | } |
| | 570 | ZoteroPane.collectionsView.setHighlightedRows(); |
| | 571 | } |
| | 572 | |
| | 573 | return; |
| | 574 | } |
| | 575 | |
| | 576 | // Ignore keystrokes if Zotero pane is closed |
| | 577 | var zoteroPane = document.getElementById('zotero-pane-stack'); |
| | 578 | if (zoteroPane.getAttribute('hidden') == 'true' || |
| | 579 | zoteroPane.getAttribute('collapsed') == 'true') { |
| | 580 | return; |
| | 581 | } |
| | 582 | |
| | 583 | var useShift = Zotero.isMac; |
| | 584 | |
| | 585 | var key = String.fromCharCode(event.which); |
| | 586 | if (!key) { |
| | 587 | Zotero.debug('No key'); |
| | 588 | return; |
| | 589 | } |
| | 590 | |
| | 591 | // Ignore modifiers other than Ctrl-Alt or Cmd-Shift |
| | 592 | if (!((Zotero.isMac ? event.metaKey : event.ctrlKey) && |
| | 593 | (useShift ? event.shiftKey : event.altKey))) { |
| | 594 | return; |
| | 595 | } |
| | 596 | |
| | 597 | var command = Zotero.Keys.getCommand(key); |
| | 598 | if (!command) { |
| | 599 | return; |
| | 600 | } |
| | 601 | |
| | 602 | Zotero.debug(command); |
| | 603 | |
| | 604 | // Errors don't seem to make it out otherwise |
| | 605 | try { |
| | 606 | |
| | 607 | switch (command) { |
| | 608 | case 'openZotero': |
| | 609 | try { |
| | 610 | // Ignore Cmd-Shift-Z keystroke in text areas |
| | 611 | if (Zotero.isMac && key == 'Z' && |
| | 612 | event.originalTarget.localName == 'textarea') { |
| | 613 | Zotero.debug('Ignoring keystroke in text area'); |
| | 614 | return; |
| | 615 | } |
| | 616 | } |
| | 617 | catch (e) { |
| | 618 | Zotero.debug(e); |
| | 619 | } |
| | 620 | ZoteroPane.toggleDisplay() |
| | 621 | break; |
| | 622 | case 'library': |
| | 623 | document.getElementById('zotero-collections-tree').focus(); |
| | 624 | ZoteroPane.collectionsView.selection.select(0); |
| | 625 | break; |
| | 626 | case 'quicksearch': |
| | 627 | document.getElementById('zotero-tb-search').select(); |
| | 628 | break; |
| | 629 | case 'newItem': |
| | 630 | ZoteroPane.newItem(2); // book |
| | 631 | var menu = document.getElementById('zotero-editpane-item-box').itemTypeMenu; |
| | 632 | menu.focus(); |
| | 633 | document.getElementById('zotero-editpane-item-box').itemTypeMenu.menupopup.openPopup(menu, "before_start", 0, 0); |
| | 634 | break; |
| | 635 | case 'newNote': |
| | 636 | // Use key that's not the modifier as the popup toggle |
| | 637 | ZoteroPane.newNote(useShift ? event.altKey : event.shiftKey); |
| | 638 | break; |
| | 639 | case 'toggleTagSelector': |
| | 640 | ZoteroPane.toggleTagSelector(); |
| | 641 | break; |
| | 642 | case 'toggleFullscreen': |
| | 643 | ZoteroPane.fullScreen(); |
| | 644 | break; |
| | 645 | case 'copySelectedItemCitationsToClipboard': |
| | 646 | ZoteroPane.copySelectedItemsToClipboard(true) |
| | 647 | break; |
| | 648 | case 'copySelectedItemsToClipboard': |
| | 649 | ZoteroPane.copySelectedItemsToClipboard(); |
| | 650 | break; |
| | 651 | case 'importFromClipboard': |
| | 652 | Zotero_File_Interface.importFromClipboard(); |
| | 653 | break; |
| | 654 | default: |
| | 655 | throw ('Command "' + command + '" not found in ZoteroPane.handleKeyDown()'); |
| | 656 | } |
| | 657 | |
| | 658 | } |
| | 659 | catch (e) { |
| | 660 | Zotero.debug(e, 1); |
| | 661 | Components.utils.reportError(e); |
| | 662 | } |
| | 663 | |
| | 664 | event.preventDefault(); |
| | 665 | } |
| | 666 | |
| | 667 | |
| | 668 | function handleKeyUp(event, from) { |
| | 669 | if (from == 'zotero-pane') { |
| | 670 | if ((Zotero.isWin && event.keyCode == 17) || |
| | 671 | (!Zotero.isWin && event.keyCode == 18)) { |
| | 672 | if (this.highlightTimer) { |
| | 673 | this.highlightTimer.cancel(); |
| | 674 | this.highlightTimer = null; |
| | 675 | } |
| | 676 | ZoteroPane.collectionsView.setHighlightedRows(); |
| | 677 | } |
| | 678 | } |
| | 679 | } |
| | 680 | |
| | 681 | |
| | 682 | /* |
| | 683 | * Highlights collections containing selected items on Ctrl (Win) or |
| | 684 | * Option/Alt (Mac/Linux) press |
| | 685 | */ |
| | 686 | function setHighlightedRowsCallback() { |
| | 687 | var itemIDs = ZoteroPane.getSelectedItems(true); |
| | 688 | if (itemIDs && itemIDs.length) { |
| | 689 | var collectionIDs = Zotero.Collections.getCollectionsContainingItems(itemIDs, true); |
| | 690 | if (collectionIDs) { |
| | 691 | ZoteroPane.collectionsView.setHighlightedRows(collectionIDs); |
| | 692 | } |
| | 693 | } |
| | 694 | } |
| | 695 | |
| | 696 | |
| | 697 | function handleKeyPress(event, from) { |
| | 698 | if (from == 'zotero-collections-tree') { |
| | 699 | if ((event.keyCode == event.DOM_VK_BACK_SPACE && Zotero.isMac) || |
| | 700 | event.keyCode == event.DOM_VK_DELETE) { |
| | 701 | ZoteroPane.deleteSelectedCollection(); |
| | 702 | event.preventDefault(); |
| | 703 | return; |
| | 704 | } |
| | 705 | } |
| | 706 | else if (from == 'zotero-items-tree') { |
| | 707 | if ((event.keyCode == event.DOM_VK_BACK_SPACE && Zotero.isMac) || |
| | 708 | event.keyCode == event.DOM_VK_DELETE) { |
| | 709 | // If Cmd/Ctrl delete, use forced mode, which does different |
| | 710 | // things depending on the context |
| | 711 | var force = event.metaKey || (!Zotero.isMac && event.ctrlKey); |
| | 712 | ZoteroPane.deleteSelectedItems(force); |
| | 713 | event.preventDefault(); |
| | 714 | return; |
| | 715 | } |
| | 716 | } |
| | 717 | } |
| | 718 | |
| | 719 | |
| | 720 | /* |
| | 721 | * Create a new item |
| | 722 | * |
| | 723 | * _data_ is an optional object with field:value for itemData |
| | 724 | */ |
| | 725 | function newItem(typeID, data, row) |
| | 726 | { |
| | 727 | if (!Zotero.stateCheck()) { |
| | 728 | this.displayErrorMessage(true); |
| | 729 | return false; |
| | 730 | } |
| | 731 | |
| | 732 | // Currently selected row |
| | 733 | if (row === undefined) { |
| | 734 | row = this.collectionsView.selection.currentIndex; |
| | 735 | } |
| | 736 | |
| | 737 | if (!this.canEdit(row)) { |
| | 738 | this.displayCannotEditLibraryMessage(); |
| | 739 | return; |
| | 740 | } |
| | 741 | |
| | 742 | if (row !== undefined) { |
| | 743 | var itemGroup = this.collectionsView._getItemAtRow(row); |
| | 744 | var libraryID = itemGroup.ref.libraryID; |
| | 745 | } |
| | 746 | else { |
| | 747 | var libraryID = null; |
| | 748 | var itemGroup = null; |
| | 749 | } |
| | 750 | |
| | 751 | var item = new Zotero.Item(typeID); |
| | 752 | item.libraryID = libraryID; |
| | 753 | for (var i in data) { |
| | 754 | item.setField(i, data[i]); |
| | 755 | } |
| | 756 | var itemID = item.save(); |
| | 757 | |
| | 758 | if (itemGroup && itemGroup.isCollection()) { |
| | 759 | itemGroup.ref.addItem(itemID); |
| | 760 | } |
| | 761 | |
| | 762 | //set to Info tab |
| | 763 | document.getElementById('zotero-view-item').selectedIndex = 0; |
| | 764 | |
| | 765 | this.selectItem(itemID); |
| | 766 | |
| | 767 | return Zotero.Items.get(itemID); |
| | 768 | } |
| | 769 | |
| | 770 | |
| | 771 | function newCollection(parent) |
| | 772 | { |
| | 773 | if (!Zotero.stateCheck()) { |
| | 774 | this.displayErrorMessage(true); |
| | 775 | return false; |
| | 776 | } |
| | 777 | |
| | 778 | if (!this.canEdit()) { |
| | 779 | this.displayCannotEditLibraryMessage(); |
| | 780 | return; |
| | 781 | } |
| | 782 | |
| | 783 | var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] |
| | 784 | .getService(Components.interfaces.nsIPromptService); |
| | 785 | |
| | 786 | var untitled = Zotero.DB.getNextName('collections', 'collectionName', |
| | 787 | Zotero.getString('pane.collections.untitled')); |
| | 788 | |
| | 789 | var newName = { value: untitled }; |
| | 790 | var result = promptService.prompt(window, |
| | 791 | Zotero.getString('pane.collections.newCollection'), |
| | 792 | Zotero.getString('pane.collections.name'), newName, "", {}); |
| | 793 | |
| | 794 | if (!result) |
| | 795 | { |
| | 796 | return; |
| | 797 | } |
| | 798 | |
| | 799 | if (!newName.value) |
| | 800 | { |
| | 801 | newName.value = untitled; |
| | 802 | } |
| | 803 | |
| | 804 | var collection = new Zotero.Collection; |
| | 805 | collection.libraryID = this.getSelectedLibraryID(); |
| | 806 | collection.name = newName.value; |
| | 807 | collection.parent = parent; |
| | 808 | collection.save(); |
| | 809 | } |
| | 810 | |
| | 811 | |
| | 812 | this.newGroup = function () { |
| | 813 | if (this.isFullScreen()) { |
| | 814 | this.toggleDisplay(); |
| | 815 | } |
| | 816 | |
| | 817 | window.loadURI(Zotero.Groups.addGroupURL); |
| | 818 | } |
| | 819 | |
| | 820 | |
| | 821 | function newSearch() |
| | 822 | { |
| | 823 | if (!Zotero.stateCheck()) { |
| | 824 | this.displayErrorMessage(true); |
| | 825 | return false; |
| | 826 | } |
| | 827 | |
| | 828 | var s = new Zotero.Search(); |
| | 829 | s.libraryID = this.getSelectedLibraryID(); |
| | 830 | s.addCondition('title', 'contains', ''); |
| | 831 | |
| | 832 | var untitled = Zotero.getString('pane.collections.untitled'); |
| | 833 | untitled = Zotero.DB.getNextName('savedSearches', 'savedSearchName', |
| | 834 | Zotero.getString('pane.collections.untitled')); |
| | 835 | var io = {dataIn: {search: s, name: untitled}, dataOut: null}; |
| | 836 | window.openDialog('chrome://zotero/content/searchDialog.xul','','chrome,modal',io); |
| | 837 | } |
| | 838 | |
| | 839 | |
| | 840 | this.openLookupWindow = function () { |
| | 841 | if (!Zotero.stateCheck()) { |
| | 842 | this.displayErrorMessage(true); |
| | 843 | return false; |
| | 844 | } |
| | 845 | |
| | 846 | if (!this.canEdit()) { |
| | 847 | this.displayCannotEditLibraryMessage(); |
| | 848 | return; |
| | 849 | } |
| | 850 | |
| | 851 | window.openDialog('chrome://zotero/content/lookup.xul', 'zotero-lookup', 'chrome,modal'); |
| | 852 | } |
| | 853 | |
| | 854 | |
| | 855 | function openAdvancedSearchWindow() { |
| | 856 | var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] |
| | 857 | .getService(Components.interfaces.nsIWindowMediator); |
| | 858 | var enumerator = wm.getEnumerator('zotero:search'); |
| | 859 | while (enumerator.hasMoreElements()) { |
| | 860 | var win = enumerator.getNext(); |
| | 861 | } |
| | 862 | |
| | 863 | if (win) { |
| | 864 | win.focus(); |
| | 865 | return; |
| | 866 | } |
| | 867 | |
| | 868 | var s = new Zotero.Search(); |
| | 869 | s.addCondition('title', 'contains', ''); |
| | 870 | var io = {dataIn: {search: s}, dataOut: null}; |
| | 871 | window.openDialog('chrome://zotero/content/advancedSearch.xul', '', 'chrome,dialog=no,centerscreen', io); |
| | 872 | } |
| | 873 | |
| | 874 | |
| | 875 | function toggleTagSelector(){ |
| | 876 | var tagSelector = document.getElementById('zotero-tag-selector'); |
| | 877 | |
| | 878 | var showing = tagSelector.getAttribute('collapsed') == 'true'; |
| | 879 | tagSelector.setAttribute('collapsed', !showing); |
| | 880 | this.updateTagSelectorSize(); |
| | 881 | |
| | 882 | // If showing, set scope to items in current view |
| | 883 | // and focus filter textbox |
| | 884 | if (showing) { |
| | 885 | _setTagScope(); |
| | 886 | tagSelector.focusTextbox(); |
| | 887 | } |
| | 888 | // If hiding, clear selection |
| | 889 | else { |
| | 890 | tagSelector.uninit(); |
| | 891 | } |
| | 892 | } |
| | 893 | |
| | 894 | |
| | 895 | function updateTagSelectorSize() { |
| | 896 | //Zotero.debug('Updating tag selector size'); |
| | 897 | var zoteroPane = document.getElementById('zotero-pane-stack'); |
| | 898 | var splitter = document.getElementById('zotero-tags-splitter'); |
| | 899 | var tagSelector = document.getElementById('zotero-tag-selector'); |
| | 900 | |
| | 901 | // Nothing should be bigger than appcontent's height |
| | 902 | var max = document.getElementById('appcontent').boxObject.height |
| | 903 | - splitter.boxObject.height; |
| | 904 | |
| | 905 | // Shrink tag selector to appcontent's height |
| | 906 | var maxTS = max - COLLECTIONS_HEIGHT; |
| | 907 | if (parseInt(tagSelector.getAttribute("height")) > maxTS) { |
| | 908 | //Zotero.debug("Limiting tag selector height to appcontent"); |
| | 909 | tagSelector.setAttribute('height', maxTS); |
| | 910 | } |
| | 911 | |
| | 912 | var height = tagSelector.boxObject.height; |
| | 913 | |
| | 914 | /* |
| | 915 | Zotero.debug("tagSelector.boxObject.height: " + tagSelector.boxObject.height); |
| | 916 | Zotero.debug("tagSelector.getAttribute('height'): " + tagSelector.getAttribute('height')); |
| | 917 | Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height); |
| | 918 | Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height')); |
| | 919 | */ |
| | 920 | |
| | 921 | // Don't let the Z-pane jump back down to its previous height |
| | 922 | // (if shrinking or hiding the tag selector let it clear the min-height) |
| | 923 | if (zoteroPane.getAttribute('height') < zoteroPane.boxObject.height) { |
| | 924 | //Zotero.debug("Setting Zotero pane height attribute to " + zoteroPane.boxObject.height); |
| | 925 | zoteroPane.setAttribute('height', zoteroPane.boxObject.height); |
| | 926 | } |
| | 927 | |
| | 928 | if (tagSelector.getAttribute('collapsed') == 'true') { |
| | 929 | // 32px is the default Z pane min-height in overlay.css |
| | 930 | height = 32; |
| | 931 | } |
| | 932 | else { |
| | 933 | // tS.boxObject.height doesn't exist at startup, so get from attribute |
| | 934 | if (!height) { |
| | 935 | height = parseInt(tagSelector.getAttribute('height')); |
| | 936 | } |
| | 937 | // 121px seems to be enough room for the toolbar and collections |
| | 938 | // tree at minimum height |
| | 939 | height = height + COLLECTIONS_HEIGHT; |
| | 940 | } |
| | 941 | |
| | 942 | //Zotero.debug('Setting Zotero pane minheight to ' + height); |
| | 943 | zoteroPane.setAttribute('minheight', height); |
| | 944 | |
| | 945 | if (this.isShowing() && !this.isFullScreen()) { |
| | 946 | zoteroPane.setAttribute('savedHeight', zoteroPane.boxObject.height); |
| | 947 | } |
| | 948 | |
| | 949 | // Fix bug whereby resizing the Z pane downward after resizing |
| | 950 | // the tag selector up and then down sometimes caused the Z pane to |
| | 951 | // stay at a fixed size and get pushed below the bottom |
| | 952 | tagSelector.height++; |
| | 953 | tagSelector.height--; |
| | 954 | } |
| | 955 | |
| | 956 | |
| | 957 | function getTagSelection(){ |
| | 958 | var tagSelector = document.getElementById('zotero-tag-selector'); |
| | 959 | return tagSelector.selection ? tagSelector.selection : {}; |
| | 960 | } |
| | 961 | |
| | 962 | |
| | 963 | function clearTagSelection() { |
| | 964 | if (Zotero.hasValues(this.getTagSelection())) { |
| | 965 | var tagSelector = document.getElementById('zotero-tag-selector'); |
| | 966 | tagSelector.clearAll(); |
| | 967 | } |
| | 968 | } |
| | 969 | |
| | 970 | |
| | 971 | /* |
| | 972 | * Sets the tag filter on the items view |
| | 973 | */ |
| | 974 | function updateTagFilter(){ |
| | 975 | this.itemsView.setFilter('tags', getTagSelection()); |
| | 976 | } |
| | 977 | |
| | 978 | |
| | 979 | /* |
| | 980 | * Set the tags scope to the items in the current view |
| | 981 | * |
| | 982 | * Passed to the items tree to trigger on changes |
| | 983 | */ |
| | 984 | function _setTagScope() { |
| | 985 | var itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex); |
| | 986 | var tagSelector = document.getElementById('zotero-tag-selector'); |
| | 987 | if (!tagSelector.getAttribute('collapsed') || |
| | 988 | tagSelector.getAttribute('collapsed') == 'false') { |
| | 989 | Zotero.debug('Updating tag selector with current tags'); |
| | 990 | if (itemGroup.editable) { |
| | 991 | tagSelector.mode = 'edit'; |
| | 992 | } |
| | 993 | else { |
| | 994 | tagSelector.mode = 'view'; |
| | 995 | } |
| | 996 | tagSelector.libraryID = itemGroup.ref.libraryID; |
| | 997 | tagSelector.scope = itemGroup.getChildTags(); |
| | 998 | } |
| | 999 | } |
| | 1000 | |
| | 1001 | |
| | 1002 | function onCollectionSelected() |
| | 1003 | { |
| | 1004 | if (this.itemsView) |
| | 1005 | { |
| | 1006 | this.itemsView.unregister(); |
| | 1007 | if (this.itemsView.wrappedJSObject.listener) { |
| | 1008 | document.getElementById('zotero-items-tree').removeEventListener( |
| | 1009 | 'keypress', this.itemsView.wrappedJSObject.listener, false |
| | 1010 | ); |
| | 1011 | } |
| | 1012 | this.itemsView.wrappedJSObject.listener = null; |
| | 1013 | document.getElementById('zotero-items-tree').view = this.itemsView = null; |
| | 1014 | } |
| | 1015 | |
| | 1016 | // Selection may have been set to single for duplicates checking. |
| | 1017 | document.getElementById("zotero-items-tree").setAttribute("seltype", "multiple"); |
| | 1018 | document.getElementById('zotero-tb-search').value = ""; |
| | 1019 | |
| | 1020 | if (this.collectionsView.selection.count != 1) { |
| | 1021 | document.getElementById('zotero-items-tree').view = this.itemsView = null; |
| | 1022 | return; |
| | 1023 | } |
| | 1024 | |
| | 1025 | // this.collectionsView.selection.currentIndex != -1 |
| | 1026 | |
| | 1027 | var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 1028 | |
| | 1029 | /* |
| | 1030 | if (itemgroup.isSeparator()) { |
| | 1031 | document.getElementById('zotero-items-tree').view = this.itemsView = null; |
| | 1032 | return; |
| | 1033 | } |
| | 1034 | */ |
| | 1035 | |
| | 1036 | itemgroup.setSearch(''); |
| | 1037 | itemgroup.setTags(getTagSelection()); |
| | 1038 | itemgroup.showDuplicates = false; |
| | 1039 | |
| | 1040 | try { |
| | 1041 | Zotero.UnresponsiveScriptIndicator.disable(); |
| | 1042 | this.itemsView = new Zotero.ItemTreeView(itemgroup); |
| | 1043 | this.itemsView.addCallback(_setTagScope); |
| | 1044 | document.getElementById('zotero-items-tree').view = this.itemsView; |
| | 1045 | this.itemsView.selection.clearSelection(); |
| | 1046 | } |
| | 1047 | finally { |
| | 1048 | Zotero.UnresponsiveScriptIndicator.enable(); |
| | 1049 | } |
| | 1050 | |
| | 1051 | if (itemgroup.isLibrary()) { |
| | 1052 | Zotero.Prefs.set('lastViewedFolder', 'L'); |
| | 1053 | } |
| | 1054 | if (itemgroup.isCollection()) { |
| | 1055 | Zotero.Prefs.set('lastViewedFolder', 'C' + itemgroup.ref.id); |
| | 1056 | } |
| | 1057 | else if (itemgroup.isSearch()) { |
| | 1058 | Zotero.Prefs.set('lastViewedFolder', 'S' + itemgroup.ref.id); |
| | 1059 | } |
| | 1060 | else if (itemgroup.isGroup()) { |
| | 1061 | Zotero.Prefs.set('lastViewedFolder', 'G' + itemgroup.ref.id); |
| | 1062 | } |
| | 1063 | } |
| | 1064 | |
| | 1065 | this.showDuplicates = function () { |
| | 1066 | if (this.collectionsView.selection.count == 1 && this.collectionsView.selection.currentIndex != -1) { |
| | 1067 | var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 1068 | itemGroup.showDuplicates = true; |
| | 1069 | |
| | 1070 | try { |
| | 1071 | Zotero.UnresponsiveScriptIndicator.disable(); |
| | 1072 | // Reset to multiple by onCollectionSelected |
| | 1073 | document.getElementById("zotero-items-tree").setAttribute("seltype", "single"); |
| | 1074 | // Toggle forces acquisition of initial duplicates |
| | 1075 | // match data. |
| | 1076 | this.itemsView.refresh(true); |
| | 1077 | this.itemsView.sort(); |
| | 1078 | } |
| | 1079 | finally { |
| | 1080 | Zotero.UnresponsiveScriptIndicator.enable(); |
| | 1081 | } |
| | 1082 | } |
| | 1083 | } |
| | 1084 | |
| | 1085 | |
| | 1086 | function itemSelected() |
| | 1087 | { |
| | 1088 | if (!Zotero.stateCheck()) { |
| | 1089 | this.displayErrorMessage(); |
| | 1090 | return; |
| | 1091 | } |
| | 1092 | |
| | 1093 | // Display restore button if items selected in Trash |
| | 1094 | if (this.itemsView && this.itemsView.selection.count) { |
| | 1095 | document.getElementById('zotero-item-restore-button').hidden |
| | 1096 | = !this.itemsView._itemGroup.isTrash() |
| | 1097 | || _nonDeletedItemsSelected(this.itemsView); |
| | 1098 | } |
| | 1099 | else { |
| | 1100 | document.getElementById('zotero-item-restore-button').hidden = true; |
| | 1101 | } |
| | 1102 | |
| | 1103 | var tabs = document.getElementById('zotero-view-tabbox'); |
| | 1104 | |
| | 1105 | if (this.itemsView && this.itemsView.selection.count == 1 && this.itemsView.selection.currentIndex != -1) |
| | 1106 | { |
| | 1107 | var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex); |
| | 1108 | |
| | 1109 | if(item.ref.isNote()) { |
| | 1110 | var noteEditor = document.getElementById('zotero-note-editor'); |
| | 1111 | noteEditor.mode = this.collectionsView.editable ? 'edit' : 'view'; |
| | 1112 | |
| | 1113 | // If loading new or different note, disable undo while we repopulate the text field |
| | 1114 | // so Undo doesn't end up clearing the field. This also ensures that Undo doesn't |
| | 1115 | // undo content from another note into the current one. |
| | 1116 | if (!noteEditor.item || noteEditor.item.id != item.ref.id) { |
| | 1117 | noteEditor.disableUndo(); |
| | 1118 | } |
| | 1119 | noteEditor.parent = null; |
| | 1120 | noteEditor.item = item.ref; |
| | 1121 | |
| | 1122 | noteEditor.enableUndo(); |
| | 1123 | |
| | 1124 | var viewButton = document.getElementById('zotero-view-note-button'); |
| | 1125 | if (this.collectionsView.editable) { |
| | 1126 | viewButton.hidden = false; |
| | 1127 | viewButton.setAttribute('noteID', item.ref.id); |
| | 1128 | if (item.ref.getSource()) { |
| | 1129 | viewButton.setAttribute('sourceID', item.ref.getSource()); |
| | 1130 | } |
| | 1131 | else { |
| | 1132 | viewButton.removeAttribute('sourceID'); |
| | 1133 | } |
| | 1134 | } |
| | 1135 | else { |
| | 1136 | viewButton.hidden = true; |
| | 1137 | } |
| | 1138 | |
| | 1139 | document.getElementById('zotero-item-pane-content').selectedIndex = 2; |
| | 1140 | } |
| | 1141 | |
| | 1142 | else if(item.ref.isAttachment()) { |
| | 1143 | var attachmentBox = document.getElementById('zotero-attachment-box'); |
| | 1144 | attachmentBox.mode = this.collectionsView.editable ? 'edit' : 'view'; |
| | 1145 | attachmentBox.item = item.ref; |
| | 1146 | |
| | 1147 | document.getElementById('zotero-item-pane-content').selectedIndex = 3; |
| | 1148 | } |
| | 1149 | |
| | 1150 | // Regular item |
| | 1151 | else |
| | 1152 | { |
| | 1153 | document.getElementById('zotero-item-pane-content').selectedIndex = 1; |
| | 1154 | var pane = document.getElementById('zotero-view-tabbox').selectedIndex; |
| | 1155 | if (this.collectionsView.editable) { |
| | 1156 | ZoteroItemPane.viewItem(item.ref, null, pane); |
| | 1157 | tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex; |
| | 1158 | } |
| | 1159 | else { |
| | 1160 | ZoteroItemPane.viewItem(item.ref, 'view', pane); |
| | 1161 | tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex; |
| | 1162 | } |
| | 1163 | } |
| | 1164 | } |
| | 1165 | else |
| | 1166 | { |
| | 1167 | document.getElementById('zotero-item-pane-content').selectedIndex = 0; |
| | 1168 | |
| | 1169 | var label = document.getElementById('zotero-view-selected-label'); |
| | 1170 | |
| | 1171 | if (this.itemsView && this.itemsView.selection.count) { |
| | 1172 | label.value = Zotero.getString('pane.item.selected.multiple', this.itemsView.selection.count); |
| | 1173 | } |
| | 1174 | else { |
| | 1175 | label.value = Zotero.getString('pane.item.selected.zero'); |
| | 1176 | } |
| | 1177 | } |
| | 1178 | } |
| | 1179 | |
| | 1180 | |
| | 1181 | /** |
| | 1182 | * Check if any selected items in the passed (trash) treeview are not deleted |
| | 1183 | * |
| | 1184 | * @param {nsITreeView} |
| | 1185 | * @return {Boolean} |
| | 1186 | */ |
| | 1187 | function _nonDeletedItemsSelected(itemsView) { |
| | 1188 | var start = {}; |
| | 1189 | var end = {}; |
| | 1190 | for (var i=0, len=itemsView.selection.getRangeCount(); i<len; i++) { |
| | 1191 | itemsView.selection.getRangeAt(i, start, end); |
| | 1192 | for (var j=start.value; j<=end.value; j++) { |
| | 1193 | if (!itemsView._getItemAtRow(j).ref.deleted) { |
| | 1194 | return true; |
| | 1195 | } |
| | 1196 | } |
| | 1197 | } |
| | 1198 | return false; |
| | 1199 | } |
| | 1200 | |
| | 1201 | |
| | 1202 | this.updateNoteButtonMenu = function () { |
| | 1203 | var items = ZoteroPane.getSelectedItems(); |
| | 1204 | var button = document.getElementById('zotero-tb-add-child-note'); |
| | 1205 | button.disabled = !this.canEdit() || |
| | 1206 | !(items.length == 1 && (items[0].isRegularItem() || !items[0].isTopLevelItem())); |
| | 1207 | } |
| | 1208 | |
| | 1209 | |
| | 1210 | this.updateAttachmentButtonMenu = function (popup) { |
| | 1211 | var items = ZoteroPane.getSelectedItems(); |
| | 1212 | |
| | 1213 | var disabled = !this.canEdit() || !(items.length == 1 && items[0].isRegularItem()); |
| | 1214 | |
| | 1215 | if (disabled) { |
| | 1216 | for each(var node in popup.childNodes) { |
| | 1217 | node.disabled = true; |
| | 1218 | } |
| | 1219 | return; |
| | 1220 | } |
| | 1221 | |
| | 1222 | var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 1223 | var canEditFiles = this.canEditFiles(); |
| | 1224 | |
| | 1225 | var prefix = "menuitem-iconic zotero-menuitem-attachments-"; |
| | 1226 | |
| | 1227 | for (var i=0; i<popup.childNodes.length; i++) { |
| | 1228 | var node = popup.childNodes[i]; |
| | 1229 | |
| | 1230 | switch (node.className) { |
| | 1231 | case prefix + 'link': |
| | 1232 | node.disabled = itemgroup.isWithinGroup(); |
| | 1233 | break; |
| | 1234 | |
| | 1235 | case prefix + 'snapshot': |
| | 1236 | case prefix + 'file': |
| | 1237 | node.disabled = !canEditFiles; |
| | 1238 | break; |
| | 1239 | |
| | 1240 | case prefix + 'web-link': |
| | 1241 | node.disabled = false; |
| | 1242 | break; |
| | 1243 | |
| | 1244 | default: |
| | 1245 | throw ("Invalid class name '" + node.className + "' in ZoteroPane.updateAttachmentButtonMenu()"); |
| | 1246 | } |
| | 1247 | } |
| | 1248 | } |
| | 1249 | |
| | 1250 | |
| | 1251 | this.checkPDFConverter = function () { |
| | 1252 | if (Zotero.Fulltext.pdfConverterIsRegistered()) { |
| | 1253 | return true; |
| | 1254 | } |
| | 1255 | |
| | 1256 | var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] |
| | 1257 | .getService(Components.interfaces.nsIPromptService); |
| | 1258 | var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) |
| | 1259 | + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL); |
| | 1260 | var index = ps.confirmEx( |
| | 1261 | null, |
| | 1262 | // TODO: localize |
| | 1263 | "PDF Tools Not Installed", |
| | 1264 | "To use this feature, you must first install the PDF tools in " |
| | 1265 | + "the Zotero preferences.", |
| | 1266 | buttonFlags, |
| | 1267 | "Open Preferences", |
| | 1268 | null, null, null, {} |
| | 1269 | ); |
| | 1270 | if (index == 0) { |
| | 1271 | ZoteroPane.openPreferences('zotero-prefpane-search', 'pdftools-install'); |
| | 1272 | } |
| | 1273 | return false; |
| | 1274 | } |
| | 1275 | |
| | 1276 | |
| | 1277 | function reindexItem() { |
| | 1278 | var items = this.getSelectedItems(); |
| | 1279 | if (!items) { |
| | 1280 | return; |
| | 1281 | } |
| | 1282 | |
| | 1283 | var itemIDs = []; |
| | 1284 | var checkPDF = false; |
| | 1285 | for (var i=0; i<items.length; i++) { |
| | 1286 | // If any PDFs, we need to make sure the converter is installed and |
| | 1287 | // prompt for installation if not |
| | 1288 | if (!checkPDF && items[i].attachmentMIMEType && items[i].attachmentMIMEType == "application/pdf") { |
| | 1289 | checkPDF = true; |
| | 1290 | } |
| | 1291 | itemIDs.push(items[i].id); |
| | 1292 | } |
| | 1293 | |
| | 1294 | if (checkPDF) { |
| | 1295 | var installed = this.checkPDFConverter(); |
| | 1296 | if (!installed) { |
| | 1297 | document.getElementById('zotero-attachment-box').updateItemIndexedState(); |
| | 1298 | return; |
| | 1299 | } |
| | 1300 | } |
| | 1301 | |
| | 1302 | Zotero.Fulltext.indexItems(itemIDs, true); |
| | 1303 | document.getElementById('zotero-attachment-box').updateItemIndexedState(); |
| | 1304 | } |
| | 1305 | |
| | 1306 | function duplicateSubstitute() { |
| | 1307 | var item, pos, len, itemGroup; |
| | 1308 | var items = this.getSelectedItems(); |
| | 1309 | if (items && items.length) { |
| | 1310 | item = items[0]; |
| | 1311 | itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex); |
| | 1312 | var duplicateItemIDs = itemGroup.duplicates._duplicateItemIDs; |
| | 1313 | var newID = false; |
| | 1314 | for (pos = 0, len = duplicateItemIDs[item.id].length; pos < len; pos += 1) { |
| | 1315 | if (!duplicateItemIDs[duplicateItemIDs[item.id][pos]]) { |
| | 1316 | newID = duplicateItemIDs[item.id][pos]; |
| | 1317 | break; |
| | 1318 | } |
| | 1319 | } |
| | 1320 | if (newID) { |
| | 1321 | var newItem = Zotero.Items.get(newID); |
| | 1322 | var targetCollectionIDs = item.getCollections(); |
| | 1323 | for (pos = 0, len = targetCollectionIDs.length; pos < len; pos += 1) { |
| | 1324 | if (!newItem.inCollection(targetCollectionIDs[pos])) { |
| | 1325 | var targetCollection = Zotero.Collections.get(targetCollectionIDs[pos]); |
| | 1326 | targetCollection.addItem(newItem.id); |
| | 1327 | } |
| | 1328 | } |
| | 1329 | } |
| | 1330 | } |
| | 1331 | |
| | 1332 | var ids = [item.id]; |
| | 1333 | |
| | 1334 | if (itemGroup.isBucket()) { |
| | 1335 | itemGroup.ref.deleteItems(ids); |
| | 1336 | } else if (itemGroup.isTrash()) { |
| | 1337 | Zotero.Items.erase(ids); |
| | 1338 | } else if (itemGroup.isGroup() || (itemGroup.isWithinGroup())) { |
| | 1339 | Zotero.Items.erase(ids); |
| | 1340 | } else if (itemGroup.isLibrary()) { |
| | 1341 | Zotero.Items.trash(ids); |
| | 1342 | } |
| | 1343 | } |
| | 1344 | |
| | 1345 | function duplicateMarkItem() { |
| | 1346 | var pos, len, item, itemGroup; |
| | 1347 | var items = this.getSelectedItems(); |
| | 1348 | itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex); |
| | 1349 | Zotero.DB.beginTransaction(); |
| | 1350 | for (pos = 0, len = items.length; pos < len; pos += 1) { |
| | 1351 | item = items[pos]; |
| | 1352 | if (item.isAttachment()) { |
| | 1353 | continue; |
| | 1354 | } |
| | 1355 | var title = "title"; |
| | 1356 | if (item.itemTypeID == 17) { |
| | 1357 | title = "caseName"; |
| | 1358 | } |
| | 1359 | Zotero.DB.query("DELETE FROM duplicateCheckList WHERE itemID=?", [item.id]); |
| | 1360 | Zotero.DB.query("INSERT INTO duplicateCheckList VALUES (?, ?)", [item.id, title]); |
| | 1361 | itemGroup.duplicates.markItem(item.id); |
| | 1362 | } |
| | 1363 | Zotero.DB.commitTransaction(); |
| | 1364 | var savedSelection = this.itemsView.saveSelection(); |
| | 1365 | this.itemsView._treebox.invalidate(); |
| | 1366 | this.itemsView.refresh(); |
| | 1367 | this.itemsView.sort(); |
| | 1368 | this.itemsView.rememberSelection(savedSelection); |
| | 1369 | } |
| | 1370 | |
| | 1371 | function duplicateUnmarkItem() { |
| | 1372 | var itemGroup, pos, len, checkID; |
| | 1373 | if (this.itemsView && this.itemsView._itemGroup && this.itemsView._itemGroup.showDuplicates) { |
| | 1374 | var items = this.getSelectedItems(); |
| | 1375 | if (items && items.length) { |
| | 1376 | var item = items[0]; |
| | 1377 | Zotero.DB.beginTransaction(); |
| | 1378 | Zotero.DB.query("DELETE FROM duplicateCheckList WHERE itemID=?", [item.id]); |
| | 1379 | Zotero.DB.commitTransaction(); |
| | 1380 | itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex); |
| | 1381 | itemGroup.duplicates.deleteItem([item.id], true); |
| | 1382 | |
| | 1383 | var savedSelection = this.itemsView.saveSelection(); |
| | 1384 | this.itemsView._treebox.invalidate(); |
| | 1385 | this.itemsView.refresh(); |
| | 1386 | this.itemsView.sort(); |
| | 1387 | this.itemsView.rememberSelection(savedSelection); |
| | 1388 | } |
| | 1389 | } |
| | 1390 | |
| | 1391 | } |
| | 1392 | |
| | 1393 | function duplicateUnmarkAll() { |
| | 1394 | var pos, len, itemGroup, itemIDs, checkID, duplicateItemIDs, actionIDs, sql; |
| | 1395 | if (this.itemsView && this.itemsView._itemGroup && this.itemsView._itemGroup.showDuplicates) { |
| | 1396 | itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex); |
| | 1397 | itemIDs = itemGroup.duplicates._itemIDs; |
| | 1398 | duplicateItemIDs = itemGroup.duplicates._duplicateItemIDs; |
| | 1399 | actionIDs = []; |
| | 1400 | for (pos = 0, len = itemIDs.length; pos < len; pos += 1) { |
| | 1401 | checkID = itemIDs[pos]; |
| | 1402 | if (duplicateItemIDs[checkID] && duplicateItemIDs[checkID].length === 0) { |
| | 1403 | actionIDs.push(checkID); |
| | 1404 | } |
| | 1405 | } |
| | 1406 | if (actionIDs.length) { |
| | 1407 | sql = "DELETE FROM duplicateCheckList WHERE (itemID="; |
| | 1408 | sql = sql + actionIDs.join(" OR itemID=") + ")"; |
| | 1409 | Zotero.DB.beginTransaction(); |
| | 1410 | Zotero.DB.query(sql); |
| | 1411 | Zotero.DB.commitTransaction(); |
| | 1412 | |
| | 1413 | itemGroup.duplicates.deleteItem(actionIDs, true); |
| | 1414 | |
| | 1415 | var savedSelection = this.itemsView.saveSelection(); |
| | 1416 | this.itemsView._treebox.invalidate(); |
| | 1417 | this.itemsView.refresh(); |
| | 1418 | this.itemsView.sort(); |
| | 1419 | this.itemsView.rememberSelection(savedSelection); |
| | 1420 | } |
| | 1421 | } |
| | 1422 | } |
| | 1423 | |
| | 1424 | function duplicateSelectedItem() { |
| | 1425 | var pos, len, duplicateItemIDs, reverseLookup; |
| | 1426 | if (!this.canEdit()) { |
| | 1427 | this.displayCannotEditLibraryMessage(); |
| | 1428 | return; |
| | 1429 | } |
| | 1430 | |
| | 1431 | var item = this.getSelectedItems()[0]; |
| | 1432 | var itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex); |
| | 1433 | |
| | 1434 | Zotero.DB.beginTransaction(); |
| | 1435 | |
| | 1436 | // Create new unsaved clone item in target library |
| | 1437 | var newItem = new Zotero.Item(item.itemTypeID); |
| | 1438 | newItem.libraryID = item.libraryID; |
| | 1439 | // DEBUG: save here because clone() doesn't currently work on unsaved tagged items |
| | 1440 | var id = newItem.save(); |
| | 1441 | |
| | 1442 | var newItem = Zotero.Items.get(id); |
| | 1443 | item.clone(false, newItem); |
| | 1444 | newItem.save(); |
| | 1445 | |
| | 1446 | // Adjust duplicates if in showDuplicates view. |
| | 1447 | if (itemGroup.showDuplicates) { |
| | 1448 | itemGroup.duplicates.addItem(id, item.id); |
| | 1449 | } |
| | 1450 | |
| | 1451 | if (this.itemsView._itemGroup.isCollection() && !newItem.getSource()) { |
| | 1452 | this.itemsView._itemGroup.ref.addItem(newItem.id); |
| | 1453 | } |
| | 1454 | |
| | 1455 | Zotero.DB.commitTransaction(); |
| | 1456 | |
| | 1457 | var savedSelection = this.itemsView.saveSelection(); |
| | 1458 | this.itemsView._treebox.invalidate(); |
| | 1459 | this.itemsView.refresh(); |
| | 1460 | this.itemsView.sort(); |
| | 1461 | this.itemsView.rememberSelection(savedSelection); |
| | 1462 | } |
| | 1463 | |
| | 1464 | |
| | 1465 | this.deleteSelectedItem = function () { |
| | 1466 | Zotero.debug("ZoteroPane.deleteSelectedItem() is deprecated -- use ZoteroPane.deleteSelectedItems()"); |
| | 1467 | this.deleteSelectedItems(); |
| | 1468 | } |
| | 1469 | |
| | 1470 | /* |
| | 1471 | * Remove, trash, or delete item(s), depending on context |
| | 1472 | * |
| | 1473 | * @param {Boolean} [force=false] Trash or delete even if in a collection or search, |
| | 1474 | * or trash without prompt in library |
| | 1475 | */ |
| | 1476 | this.deleteSelectedItems = function (force) { |
| | 1477 | var pos, len; |
| | 1478 | if (!this.itemsView || !this.itemsView.selection.count) { |
| | 1479 | return; |
| | 1480 | } |
| | 1481 | var itemGroup = this.itemsView._itemGroup; |
| | 1482 | |
| | 1483 | if (!itemGroup.isTrash() && !itemGroup.isBucket() && !this.canEdit()) { |
| | 1484 | this.displayCannotEditLibraryMessage(); |
| | 1485 | return; |
| | 1486 | } |
| | 1487 | |
| | 1488 | var toTrash = { |
| | 1489 | title: Zotero.getString('pane.items.trash.title'), |
| | 1490 | text: Zotero.getString( |
| | 1491 | 'pane.items.trash' + (this.itemsView.selection.count > 1 ? '.multiple' : '') |
| | 1492 | ) |
| | 1493 | }; |
| | 1494 | var toDelete = { |
| | 1495 | title: Zotero.getString('pane.items.delete.title'), |
| | 1496 | text: Zotero.getString( |
| | 1497 | 'pane.items.delete' + (this.itemsView.selection.count > 1 ? '.multiple' : '') |
| | 1498 | ) |
| | 1499 | }; |
| | 1500 | |
| | 1501 | if (itemGroup.isLibrary()) { |
| | 1502 | // In library, don't prompt if meta key was pressed |
| | 1503 | var prompt = force ? false : toTrash; |
| | 1504 | } |
| | 1505 | else if (itemGroup.isCollection()) { |
| | 1506 | // In collection, only prompt if trashing |
| | 1507 | var prompt = force ? toTrash : false; |
| | 1508 | } |
| | 1509 | // This should be changed if/when groups get trash |
| | 1510 | else if (itemGroup.isGroup()) { |
| | 1511 | var prompt = toDelete; |
| | 1512 | } |
| | 1513 | else if (itemGroup.isSearch()) { |
| | 1514 | if (!force) { |
| | 1515 | return; |
| | 1516 | } |
| | 1517 | var prompt = toTrash; |
| | 1518 | } |
| | 1519 | // Do nothing in share views |
| | 1520 | else if (itemGroup.isShare()) { |
| | 1521 | return; |
| | 1522 | } |
| | 1523 | else if (itemGroup.isBucket()) { |
| | 1524 | var prompt = toDelete; |
| | 1525 | } |
| | 1526 | // Do nothing in trash view if any non-deleted items are selected |
| | 1527 | else if (itemGroup.isTrash()) { |
| | 1528 | var start = {}; |
| | 1529 | var end = {}; |
| | 1530 | for (var i=0, len=this.itemsView.selection.getRangeCount(); i<len; i++) { |
| | 1531 | this.itemsView.selection.getRangeAt(i, start, end); |
| | 1532 | for (var j=start.value; j<=end.value; j++) { |
| | 1533 | if (!this.itemsView._getItemAtRow(j).ref.deleted) { |
| | 1534 | return; |
| | 1535 | } |
| | 1536 | } |
| | 1537 | } |
| | 1538 | var prompt = toDelete; |
| | 1539 | } |
| | 1540 | |
| | 1541 | var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] |
| | 1542 | .getService(Components.interfaces.nsIPromptService); |
| | 1543 | if (!prompt || promptService.confirm(window, prompt.title, prompt.text)) { |
| | 1544 | this.itemsView.deleteSelection(force); |
| | 1545 | } |
| | 1546 | } |
| | 1547 | |
| | 1548 | function deleteSelectedCollection() |
| | 1549 | { |
| | 1550 | if (!this.canEdit()) { |
| | 1551 | this.displayCannotEditLibraryMessage(); |
| | 1552 | return; |
| | 1553 | } |
| | 1554 | |
| | 1555 | if (this.collectionsView.selection.count == 1) { |
| | 1556 | var row = |
| | 1557 | this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 1558 | |
| | 1559 | if (row.isCollection()) |
| | 1560 | { |
| | 1561 | if (confirm(Zotero.getString('pane.collections.delete'))) |
| | 1562 | { |
| | 1563 | this.collectionsView.deleteSelection(); |
| | 1564 | } |
| | 1565 | } |
| | 1566 | else if (row.isSearch()) |
| | 1567 | { |
| | 1568 | if (confirm(Zotero.getString('pane.collections.deleteSearch'))) |
| | 1569 | { |
| | 1570 | this.collectionsView.deleteSelection(); |
| | 1571 | } |
| | 1572 | } |
| | 1573 | } |
| | 1574 | } |
| | 1575 | |
| | 1576 | |
| | 1577 | this.restoreSelectedItems = function () { |
| | 1578 | var items = this.getSelectedItems(); |
| | 1579 | if (!items) { |
| | 1580 | return; |
| | 1581 | } |
| | 1582 | |
| | 1583 | Zotero.DB.beginTransaction(); |
| | 1584 | for (var i=0; i<items.length; i++) { |
| | 1585 | items[i].deleted = false; |
| | 1586 | items[i].save(); |
| | 1587 | } |
| | 1588 | Zotero.DB.commitTransaction(); |
| | 1589 | } |
| | 1590 | |
| | 1591 | |
| | 1592 | this.emptyTrash = function () { |
| | 1593 | var prompt = Components.classes["@mozilla.org/network/default-prompt;1"] |
| | 1594 | .getService(Components.interfaces.nsIPrompt); |
| | 1595 | |
| | 1596 | var result = prompt.confirm("", |
| | 1597 | Zotero.getString('pane.collections.emptyTrash') + "\n\n" + |
| | 1598 | Zotero.getString('general.actionCannotBeUndone')); |
| | 1599 | if (result) { |
| | 1600 | Zotero.Items.emptyTrash(); |
| | 1601 | Zotero.purgeDataObjects(true); |
| | 1602 | } |
| | 1603 | } |
| | 1604 | |
| | 1605 | this.createCommonsBucket = function () { |
| | 1606 | var prompt = Components.classes["@mozilla.org/network/default-prompt;1"] |
| | 1607 | .createInstance(Components.interfaces.nsIPrompt); |
| | 1608 | |
| | 1609 | var invalid = false; |
| | 1610 | |
| | 1611 | while (true) { |
| | 1612 | if (invalid) { |
| | 1613 | // TODO: localize |
| | 1614 | prompt.alert("", "Invalid title. Please try again."); |
| | 1615 | invalid = false; |
| | 1616 | } |
| | 1617 | |
| | 1618 | var newTitle = {}; |
| | 1619 | var result = prompt.prompt( |
| | 1620 | "", |
| | 1621 | // TODO: localize |
| | 1622 | "Enter a title for this Zotero Commons collection:", |
| | 1623 | newTitle, |
| | 1624 | "", {} |
| | 1625 | ); |
| | 1626 | |
| | 1627 | if (!result) { |
| | 1628 | return; |
| | 1629 | } |
| | 1630 | |
| | 1631 | var title = Zotero.Utilities.prototype.trim(newTitle.value); |
| | 1632 | |
| | 1633 | if (!title) { |
| | 1634 | return; |
| | 1635 | } |
| | 1636 | |
| | 1637 | if (!Zotero.Commons.isValidBucketTitle(title)) { |
| | 1638 | invalid = true; |
| | 1639 | continue; |
| | 1640 | } |
| | 1641 | |
| | 1642 | break; |
| | 1643 | } |
| | 1644 | |
| | 1645 | invalid = false; |
| | 1646 | |
| | 1647 | var origName = title.toLowerCase(); |
| | 1648 | origName = origName.replace(/[^a-z0-9 ._-]/g, ''); |
| | 1649 | origName = origName.replace(/ /g, '-'); |
| | 1650 | origName = origName.substr(0, 32); |
| | 1651 | |
| | 1652 | while (true) { |
| | 1653 | if (invalid) { |
| | 1654 | // TODO: localize |
| | 1655 | var msg = "'" + name + "' is not a valid Zotero Commons collection identifier.\n\n" |
| | 1656 | + "Collection identifiers can contain basic Latin letters, numbers," |
| | 1657 | + "hyphens, and underscores. Spaces and other characters are not allowed."; |
| | 1658 | prompt.alert("", msg); |
| | 1659 | invalid = false; |
| | 1660 | } |
| | 1661 | |
| | 1662 | var newName = { value: origName }; |
| | 1663 | var result = prompt.prompt( |
| | 1664 | "", |
| | 1665 | // TODO: localize |
| | 1666 | "Enter an identifier for the collection '" + title + "'.\n\n" |
| | 1667 | + "The identifier will form the collection's URL " |
| | 1668 | + "(e.g., http://www.archive.org/details/" + origName + ") " |
| | 1669 | + "and can contain basic Latin letters, numbers, hyphens, and underscores. " |
| | 1670 | + "Spaces and other characters are not allowed.", |
| | 1671 | newName, |
| | 1672 | "", {} |
| | 1673 | ); |
| | 1674 | |
| | 1675 | if (!result) { |
| | 1676 | return; |
| | 1677 | } |
| | 1678 | |
| | 1679 | var name = Zotero.Utilities.prototype.trim(newName.value); |
| | 1680 | |
| | 1681 | if (!name) { |
| | 1682 | return; |
| | 1683 | } |
| | 1684 | |
| | 1685 | if (!Zotero.Commons.isValidBucketName(name)) { |
| | 1686 | invalid = true; |
| | 1687 | continue; |
| | 1688 | } |
| | 1689 | |
| | 1690 | break; |
| | 1691 | } |
| | 1692 | |
| | 1693 | // TEMP |
| | 1694 | var name = "zc-test-" + name; |
| | 1695 | |
| | 1696 | // TODO: localize |
| | 1697 | var progressWin = new Zotero.ProgressWindow(); |
| | 1698 | progressWin.changeHeadline("Creating Zotero Commons Collection"); |
| | 1699 | var icon = this.collectionsView.getImageSrc(this.collectionsView.selection.currentIndex); |
| | 1700 | progressWin.addLines(title, icon) |
| | 1701 | progressWin.show(); |
| | 1702 | |
| | 1703 | Zotero.Commons.createBucket(name, title, function () { |
| | 1704 | progressWin.startCloseTimer(); |
| | 1705 | }); |
| | 1706 | } |
| | 1707 | |
| | 1708 | |
| | 1709 | this.refreshCommonsBucket = function() { |
| | 1710 | if (!this.collectionsView |
| | 1711 | || !this.collectionsView.selection |
| | 1712 | || this.collectionsView.selection.count != 1 |
| | 1713 | || this.collectionsView.selection.currentIndex == -1) { |
| | 1714 | return false; |
| | 1715 | } |
| | 1716 | |
| | 1717 | var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 1718 | if (itemGroup && itemGroup.isBucket()) { |
| | 1719 | var self = this; |
| | 1720 | itemGroup.ref.refreshItems(function () { |
| | 1721 | self.itemsView.refresh(); |
| | 1722 | self.itemsView.sort(); |
| | 1723 | |
| | 1724 | // On a manual refresh, also check for new OCRed files |
| | 1725 | //Zotero.Commons.syncFiles(); |
| | 1726 | }); |
| | 1727 | } |
| | 1728 | } |
| | 1729 | |
| | 1730 | function editSelectedCollection() |
| | 1731 | { |
| | 1732 | if (!this.canEdit()) { |
| | 1733 | this.displayCannotEditLibraryMessage(); |
| | 1734 | return; |
| | 1735 | } |
| | 1736 | |
| | 1737 | if (this.collectionsView.selection.count > 0) { |
| | 1738 | var row = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 1739 | |
| | 1740 | if (row.isCollection()) { |
| | 1741 | var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] |
| | 1742 | .getService(Components.interfaces.nsIPromptService); |
| | 1743 | |
| | 1744 | var newName = { value: row.getName() }; |
| | 1745 | var result = promptService.prompt(window, "", |
| | 1746 | Zotero.getString('pane.collections.rename'), newName, "", {}); |
| | 1747 | |
| | 1748 | if (result && newName.value) { |
| | 1749 | row.ref.name = newName.value; |
| | 1750 | row.ref.save(); |
| | 1751 | } |
| | 1752 | } |
| | 1753 | else { |
| | 1754 | var s = new Zotero.Search(); |
| | 1755 | s.id = row.ref.id; |
| | 1756 | var io = {dataIn: {search: s, name: row.getName()}, dataOut: null}; |
| | 1757 | window.openDialog('chrome://zotero/content/searchDialog.xul','','chrome,modal',io); |
| | 1758 | if (io.dataOut) { |
| | 1759 | this.onCollectionSelected(); //reload itemsView |
| | 1760 | } |
| | 1761 | } |
| | 1762 | } |
| | 1763 | } |
| | 1764 | |
| | 1765 | |
| | 1766 | function copySelectedItemsToClipboard(asCitations) { |
| | 1767 | var items = this.getSelectedItems(); |
| | 1768 | if (!items.length) { |
| | 1769 | return; |
| | 1770 | } |
| | 1771 | |
| | 1772 | // Make sure at least one item is a regular item |
| | 1773 | // |
| | 1774 | // DEBUG: We could copy notes via keyboard shortcut if we altered |
| | 1775 | // Z_F_I.copyItemsToClipboard() to use Z.QuickCopy.getContentFromItems(), |
| | 1776 | // but 1) we'd need to override that function's drag limit and 2) when I |
| | 1777 | // tried it the OS X clipboard seemed to be getting text vs. HTML wrong, |
| | 1778 | // automatically converting text/html to plaintext rather than using |
| | 1779 | // text/unicode. (That may be fixable, however.) |
| | 1780 | var canCopy = false; |
| | 1781 | for each(var item in items) { |
| | 1782 | if (item.isRegularItem()) { |
| | 1783 | canCopy = true; |
| | 1784 | break; |
| | 1785 | } |
| | 1786 | } |
| | 1787 | if (!canCopy) { |
| | 1788 | var pr = Components.classes["@mozilla.org/network/default-prompt;1"] |
| | 1789 | .getService(Components.interfaces.nsIPrompt); |
| | 1790 | pr.alert("", Zotero.getString("fileInterface.noReferencesError")); |
| | 1791 | return; |
| | 1792 | } |
| | 1793 | |
| | 1794 | var url = window.content.location.href; |
| | 1795 | var [mode, format] = Zotero.QuickCopy.getFormatFromURL(url).split('='); |
| | 1796 | var [mode, contentType] = mode.split('/'); |
| | 1797 | |
| | 1798 | if (mode == 'bibliography') { |
| | 1799 | if (asCitations) { |
| | 1800 | Zotero_File_Interface.copyCitationToClipboard(items, format, contentType == 'html'); |
| | 1801 | } |
| | 1802 | else { |
| | 1803 | Zotero_File_Interface.copyItemsToClipboard(items, format, contentType == 'html'); |
| | 1804 | } |
| | 1805 | } |
| | 1806 | else if (mode == 'export') { |
| | 1807 | // Copy citations doesn't work in export mode |
| | 1808 | if (asCitations) { |
| | 1809 | return; |
| | 1810 | } |
| | 1811 | else { |
| | 1812 | Zotero_File_Interface.exportItemsToClipboard(items, format); |
| | 1813 | } |
| | 1814 | } |
| | 1815 | } |
| | 1816 | |
| | 1817 | |
| | 1818 | function clearQuicksearch() { |
| | 1819 | var search = document.getElementById('zotero-tb-search'); |
| | 1820 | if (search.value != '') { |
| | 1821 | search.value = ''; |
| | 1822 | search.doCommand('cmd_zotero_search'); |
| | 1823 | } |
| | 1824 | } |
| | 1825 | |
| | 1826 | |
| | 1827 | function handleSearchKeypress(textbox, event) { |
| | 1828 | // Events that turn find-as-you-type on |
| | 1829 | if (event.keyCode == event.DOM_VK_ESCAPE) { |
| | 1830 | textbox.value = ''; |
| | 1831 | ZoteroPane.setItemsPaneMessage(Zotero.getString('searchInProgress')); |
| | 1832 | setTimeout("ZoteroPane.search(); ZoteroPane.clearItemsPaneMessage();", 1); |
| | 1833 | } |
| | 1834 | else if (event.keyCode == event.DOM_VK_RETURN || event.keyCode == event.DOM_VK_ENTER) { |
| | 1835 | ZoteroPane.setItemsPaneMessage(Zotero.getString('searchInProgress')); |
| | 1836 | setTimeout("ZoteroPane.search(true); ZoteroPane.clearItemsPaneMessage();", 1); |
| | 1837 | } |
| | 1838 | } |
| | 1839 | |
| | 1840 | |
| | 1841 | function handleSearchInput(textbox, event) { |
| | 1842 | // This is the new length, except, it seems, when the change is a |
| | 1843 | // result of Undo or Redo |
| | 1844 | if (!textbox.value.length) { |
| | 1845 | ZoteroPane.setItemsPaneMessage(Zotero.getString('searchInProgress')); |
| | 1846 | setTimeout("ZoteroPane.search(); ZoteroPane.clearItemsPaneMessage();", 1); |
| | 1847 | } |
| | 1848 | else if (textbox.value.indexOf('"') != -1) { |
| | 1849 | ZoteroPane.setItemsPaneMessage(Zotero.getString('advancedSearchMode')); |
| | 1850 | } |
| | 1851 | } |
| | 1852 | |
| | 1853 | |
| | 1854 | function search(runAdvanced) |
| | 1855 | { |
| | 1856 | if (this.itemsView) { |
| | 1857 | var search = document.getElementById('zotero-tb-search'); |
| | 1858 | if (!runAdvanced && search.value.indexOf('"') != -1) { |
| | 1859 | return; |
| | 1860 | } |
| | 1861 | var searchVal = search.value; |
| | 1862 | this.itemsView.setFilter('search', searchVal); |
| | 1863 | } |
| | 1864 | } |
| | 1865 | |
| | 1866 | |
| | 1867 | /* |
| | 1868 | * Select item in current collection or, if not there, in Library |
| | 1869 | * |
| | 1870 | * If _inLibrary_, force switch to Library |
| | 1871 | * If _expand_, open item if it's a container |
| | 1872 | */ |
| | 1873 | function selectItem(itemID, inLibrary, expand) |
| | 1874 | { |
| | 1875 | if (!itemID) { |
| | 1876 | return false; |
| | 1877 | } |
| | 1878 | |
| | 1879 | var item = Zotero.Items.get(itemID); |
| | 1880 | if (!item) { |
| | 1881 | return false; |
| | 1882 | } |
| | 1883 | |
| | 1884 | if (!this.itemsView) { |
| | 1885 | Components.utils.reportError("Items view not set in ZoteroPane.selectItem()"); |
| | 1886 | return false; |
| | 1887 | } |
| | 1888 | |
| | 1889 | var currentLibraryID = this.getSelectedLibraryID(); |
| | 1890 | // If in a different library |
| | 1891 | if (item.libraryID != currentLibraryID) { |
| | 1892 | this.collectionsView.selectLibrary(item.libraryID); |
| | 1893 | } |
| | 1894 | // Force switch to library view |
| | 1895 | else if (!this.itemsView._itemGroup.isLibrary() && inLibrary) { |
| | 1896 | this.collectionsView.selectLibrary(item.libraryID); |
| | 1897 | } |
| | 1898 | |
| | 1899 | var selected = this.itemsView.selectItem(itemID, expand); |
| | 1900 | if (!selected) { |
| | 1901 | this.collectionsView.selectLibrary(item.libraryID); |
| | 1902 | this.itemsView.selectItem(itemID, expand); |
| | 1903 | } |
| | 1904 | |
| | 1905 | return true; |
| | 1906 | } |
| | 1907 | |
| | 1908 | |
| | 1909 | this.getSelectedLibraryID = function () { |
| | 1910 | var group = this.getSelectedGroup(); |
| | 1911 | if (group) { |
| | 1912 | return group.libraryID; |
| | 1913 | } |
| | 1914 | var collection = this.getSelectedCollection(); |
| | 1915 | if (collection) { |
| | 1916 | return collection.libraryID; |
| | 1917 | } |
| | 1918 | return null; |
| | 1919 | } |
| | 1920 | |
| | 1921 | |
| | 1922 | function getSelectedCollection(asID) { |
| | 1923 | if (this.collectionsView) { |
| | 1924 | return this.collectionsView.getSelectedCollection(asID); |
| | 1925 | } |
| | 1926 | return false; |
| | 1927 | } |
| | 1928 | |
| | 1929 | |
| | 1930 | function getSelectedSavedSearch(asID) |
| | 1931 | { |
| | 1932 | if (this.collectionsView.selection.count > 0 && this.collectionsView.selection.currentIndex != -1) { |
| | 1933 | var collection = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 1934 | if (collection && collection.isSearch()) { |
| | 1935 | return asID ? collection.ref.id : collection.ref; |
| | 1936 | } |
| | 1937 | } |
| | 1938 | return false; |
| | 1939 | } |
| | 1940 | |
| | 1941 | |
| | 1942 | /* |
| | 1943 | * Return an array of Item objects for selected items |
| | 1944 | * |
| | 1945 | * If asIDs is true, return an array of itemIDs instead |
| | 1946 | */ |
| | 1947 | function getSelectedItems(asIDs) |
| | 1948 | { |
| | 1949 | if (!this.itemsView) { |
| | 1950 | return []; |
| | 1951 | } |
| | 1952 | |
| | 1953 | return this.itemsView.getSelectedItems(asIDs); |
| | 1954 | } |
| | 1955 | |
| | 1956 | |
| | 1957 | this.getSelectedGroup = function (asID) { |
| | 1958 | if (this.collectionsView.selection |
| | 1959 | && this.collectionsView.selection.count > 0 |
| | 1960 | && this.collectionsView.selection.currentIndex != -1) { |
| | 1961 | var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 1962 | if (itemGroup && itemGroup.isGroup()) { |
| | 1963 | return asID ? itemGroup.ref.id : itemGroup.ref; |
| | 1964 | } |
| | 1965 | } |
| | 1966 | return false; |
| | 1967 | } |
| | 1968 | |
| | 1969 | |
| | 1970 | /* |
| | 1971 | * Returns an array of Zotero.Item objects of visible items in current sort order |
| | 1972 | * |
| | 1973 | * If asIDs is true, return an array of itemIDs instead |
| | 1974 | */ |
| | 1975 | function getSortedItems(asIDs) { |
| | 1976 | if (!this.itemsView) { |
| | 1977 | return []; |
| | 1978 | } |
| | 1979 | |
| | 1980 | return this.itemsView.getSortedItems(asIDs); |
| | 1981 | } |
| | 1982 | |
| | 1983 | |
| | 1984 | function getSortField() { |
| | 1985 | if (!this.itemsView) { |
| | 1986 | return false; |
| | 1987 | } |
| | 1988 | |
| | 1989 | return this.itemsView.getSortField(); |
| | 1990 | } |
| | 1991 | |
| | 1992 | |
| | 1993 | function getSortDirection() { |
| | 1994 | if (!this.itemsView) { |
| | 1995 | return false; |
| | 1996 | } |
| | 1997 | |
| | 1998 | return this.itemsView.getSortDirection(); |
| | 1999 | } |
| | 2000 | |
| | 2001 | |
| | 2002 | this.buildCollectionContextMenu = function buildCollectionContextMenu() |
| | 2003 | { |
| | 2004 | var menu = document.getElementById('zotero-collectionmenu'); |
| | 2005 | var m = { |
| | 2006 | newCollection: 0, |
| | 2007 | newSavedSearch: 1, |
| | 2008 | newSubcollection: 2, |
| | 2009 | sep1: 3, |
| | 2010 | editSelectedCollection: 4, |
| | 2011 | removeCollection: 5, |
| | 2012 | sep2: 6, |
| | 2013 | exportCollection: 7, |
| | 2014 | createBibCollection: 8, |
| | 2015 | exportFile: 9, |
| | 2016 | loadReport: 10, |
| | 2017 | emptyTrash: 11, |
| | 2018 | createCommonsBucket: 12, |
| | 2019 | refreshCommonsBucket: 13, |
| | 2020 | sep3: 14, |
| | 2021 | showDuplicates: 15, |
| | 2022 | unshowDuplicates: 16 |
| | 2023 | }; |
| | 2024 | |
| | 2025 | var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 2026 | |
| | 2027 | var enable = [], disable = [], show = []; |
| | 2028 | |
| | 2029 | // Collection |
| | 2030 | if (itemGroup.isCollection()) { |
| | 2031 | show = [ |
| | 2032 | m.newSubcollection, |
| | 2033 | m.sep1, |
| | 2034 | m.editSelectedCollection, |
| | 2035 | m.removeCollection, |
| | 2036 | m.sep2, |
| | 2037 | m.exportCollection, |
| | 2038 | m.createBibCollection, |
| | 2039 | m.loadReport |
| | 2040 | ]; |
| | 2041 | var s = [m.exportCollection, m.createBibCollection, m.loadReport]; |
| | 2042 | if (this.itemsView.rowCount>0) { |
| | 2043 | enable = s; |
| | 2044 | } |
| | 2045 | else if (!this.collectionsView.isContainerEmpty(this.collectionsView.selection.currentIndex)) { |
| | 2046 | enable = [m.exportCollection]; |
| | 2047 | disable = [m.createBibCollection, m.loadReport]; |
| | 2048 | } |
| | 2049 | else { |
| | 2050 | disable = s; |
| | 2051 | } |
| | 2052 | |
| | 2053 | // Adjust labels |
| | 2054 | menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.rename.collection')); |
| | 2055 | menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.collection')); |
| | 2056 | menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.collection')); |
| | 2057 | menu.childNodes[m.createBibCollection].setAttribute('label', Zotero.getString('pane.collections.menu.createBib.collection')); |
| | 2058 | menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.collection')); |
| | 2059 | } |
| | 2060 | // Saved Search |
| | 2061 | else if (itemGroup.isSearch()) { |
| | 2062 | show = [ |
| | 2063 | m.editSelectedCollection, |
| | 2064 | m.removeCollection, |
| | 2065 | m.sep2, |
| | 2066 | m.exportCollection, |
| | 2067 | m.createBibCollection, |
| | 2068 | m.loadReport |
| | 2069 | ]; |
| | 2070 | |
| | 2071 | var s = [m.exportCollection, m.createBibCollection, m.loadReport]; |
| | 2072 | if (this.itemsView.rowCount>0) { |
| | 2073 | enable = s; |
| | 2074 | } |
| | 2075 | else { |
| | 2076 | disable = s; |
| | 2077 | } |
| | 2078 | |
| | 2079 | // Adjust labels |
| | 2080 | menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.edit.savedSearch')); |
| | 2081 | menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.savedSearch')); |
| | 2082 | menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.savedSearch')); |
| | 2083 | menu.childNodes[m.createBibCollection].setAttribute('label', Zotero.getString('pane.collections.menu.createBib.savedSearch')); |
| | 2084 | menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.savedSearch')); |
| | 2085 | } |
| | 2086 | // Trash |
| | 2087 | else if (itemGroup.isTrash()) { |
| | 2088 | show = [m.emptyTrash]; |
| | 2089 | } |
| | 2090 | else if (itemGroup.isHeader()) { |
| | 2091 | if (itemGroup.ref.id == 'commons-header') { |
| | 2092 | show = [m.createCommonsBucket]; |
| | 2093 | } |
| | 2094 | } |
| | 2095 | else if (itemGroup.isBucket()) { |
| | 2096 | show = [m.refreshCommonsBucket]; |
| | 2097 | } |
| | 2098 | // Group |
| | 2099 | else if (itemGroup.isGroup()) { |
| | 2100 | if (itemGroup.editable) { |
| | 2101 | show = [m.newCollection, m.newSavedSearch]; |
| | 2102 | if (Zotero.Prefs.get('debugShowDuplicates')) { |
| | 2103 | show.push(m.sep3); |
| | 2104 | if (itemGroup.showDuplicates) { |
| | 2105 | show.push(m.unshowDuplicates); |
| | 2106 | } else { |
| | 2107 | show.push(m.showDuplicates); |
| | 2108 | } |
| | 2109 | } |
| | 2110 | } |
| | 2111 | } |
| | 2112 | // Library |
| | 2113 | else |
| | 2114 | { |
| | 2115 | show = [m.newCollection, m.newSavedSearch, m.sep1, m.exportFile]; |
| | 2116 | if (Zotero.Prefs.get('debugShowDuplicates')) { |
| | 2117 | show.push(m.sep3); |
| | 2118 | if (itemGroup.showDuplicates) { |
| | 2119 | show.push(m.unshowDuplicates); |
| | 2120 | } else { |
| | 2121 | show.push(m.showDuplicates); |
| | 2122 | } |
| | 2123 | } |
| | 2124 | } |
| | 2125 | |
| | 2126 | // Disable some actions if user doesn't have write access |
| | 2127 | var s = [m.editSelectedCollection, m.removeCollection, m.newCollection, m.newSavedSearch, m.newSubcollection]; |
| | 2128 | if (itemGroup.isWithinGroup() && !itemGroup.editable) { |
| | 2129 | disable = disable.concat(s); |
| | 2130 | } |
| | 2131 | else { |
| | 2132 | enable = enable.concat(s); |
| | 2133 | } |
| | 2134 | |
| | 2135 | for (var i in disable) |
| | 2136 | { |
| | 2137 | menu.childNodes[disable[i]].setAttribute('disabled', true); |
| | 2138 | } |
| | 2139 | |
| | 2140 | for (var i in enable) |
| | 2141 | { |
| | 2142 | menu.childNodes[enable[i]].setAttribute('disabled', false); |
| | 2143 | } |
| | 2144 | |
| | 2145 | // Hide all items by default |
| | 2146 | for each(var pos in m) { |
| | 2147 | menu.childNodes[pos].setAttribute('hidden', true); |
| | 2148 | } |
| | 2149 | |
| | 2150 | for (var i in show) |
| | 2151 | { |
| | 2152 | menu.childNodes[show[i]].setAttribute('hidden', false); |
| | 2153 | } |
| | 2154 | } |
| | 2155 | |
| | 2156 | function buildItemContextMenu() |
| | 2157 | { |
| | 2158 | var m = { |
| | 2159 | showInLibrary: 0, |
| | 2160 | sep1: 1, |
| | 2161 | addNote: 2, |
| | 2162 | addAttachments: 3, |
| | 2163 | sep2: 4, |
| | 2164 | duplicateItem: 5, |
| | 2165 | deleteItem: 6, |
| | 2166 | deleteFromLibrary: 7, |
| | 2167 | sep3: 8, |
| | 2168 | exportItems: 9, |
| | 2169 | createBib: 10, |
| | 2170 | loadReport: 11, |
| | 2171 | sep4: 12, |
| | 2172 | createParent: 13, |
| | 2173 | recognizePDF: 14, |
| | 2174 | renameAttachments: 15, |
| | 2175 | reindexItem: 16, |
| | 2176 | sep5: 17, |
| | 2177 | duplicateMark: 18, |
| | 2178 | duplicateSubstitute: 19, |
| | 2179 | duplicateUnmark: 20, |
| | 2180 | duplicateUnmarkAll: 21 |
| | 2181 | }; |
| | 2182 | |
| | 2183 | var menu = document.getElementById('zotero-itemmenu'); |
| | 2184 | |
| | 2185 | var enable = [], disable = [], show = [], hide = [], multiple = ''; |
| | 2186 | |
| | 2187 | if (!this.itemsView) { |
| | 2188 | return; |
| | 2189 | } |
| | 2190 | |
| | 2191 | if (!Zotero.Prefs.get('debugShowDuplicates')) { |
| | 2192 | hide.push(m.sep5, m.duplicateMark); |
| | 2193 | } |
| | 2194 | |
| | 2195 | if (this.itemsView.selection.count > 0) { |
| | 2196 | var itemGroup = this.itemsView._itemGroup; |
| | 2197 | |
| | 2198 | enable.push(m.showInLibrary, m.addNote, m.addAttachments, |
| | 2199 | m.sep2, m.duplicateItem, m.deleteItem, m.deleteFromLibrary, |
| | 2200 | m.exportItems, m.createBib, m.loadReport); |
| | 2201 | |
| | 2202 | // Multiple items selected |
| | 2203 | if (this.itemsView.selection.count > 1) { |
| | 2204 | var multiple = '.multiple'; |
| | 2205 | hide.push(m.showInLibrary, m.sep1, m.addNote, m.addAttachments, |
| | 2206 | m.sep2, m.duplicateItem); |
| | 2207 | |
| | 2208 | // If all items can be reindexed, or all items can be recognized, show option |
| | 2209 | var items = this.getSelectedItems(); |
| | 2210 | var canIndex = true; |
| | 2211 | var canRecognize = true; |
| | 2212 | var canMark = false; |
| | 2213 | if (!Zotero.Fulltext.pdfConverterIsRegistered()) { |
| | 2214 | canIndex = false; |
| | 2215 | } |
| | 2216 | for (var i=0; i<items.length; i++) { |
| | 2217 | if (canIndex && !Zotero.Fulltext.canReindex(items[i].id)) { |
| | 2218 | canIndex = false; |
| | 2219 | } |
| | 2220 | if (canRecognize && !Zotero_RecognizePDF.canRecognize(items[i])) { |
| | 2221 | canRecognize = false; |
| | 2222 | } |
| | 2223 | if (!canMark && !items[i].isAttachment() && !this.itemsView._itemGroup.duplicates.isNew(items[i].id)) { |
| | 2224 | canMark = true; |
| | 2225 | } |
| | 2226 | if (!canIndex && !canRecognize && canMark) { |
| | 2227 | break; |
| | 2228 | } |
| | 2229 | } |
| | 2230 | if (canIndex) { |
| | 2231 | show.push(m.reindexItem); |
| | 2232 | } |
| | 2233 | else { |
| | 2234 | hide.push(m.reindexItem); |
| | 2235 | } |
| | 2236 | if (Zotero.Prefs.get('debugShowDuplicates')) { |
| | 2237 | if (!canMark) { |
| | 2238 | disable.push(m.duplicateMark); |
| | 2239 | } else { |
| | 2240 | enable.push(m.duplicateMark); |
| | 2241 | } |
| | 2242 | } |
| | 2243 | if (canRecognize) { |
| | 2244 | show.push(m.recognizePDF); |
| | 2245 | hide.push(m.createParent); |
| | 2246 | } |
| | 2247 | else { |
| | 2248 | hide.push(m.recognizePDF); |
| | 2249 | |
| | 2250 | var canCreateParent = true; |
| | 2251 | for (var i=0; i<items.length; i++) { |
| | 2252 | if (!items[i].isTopLevelItem() || items[i].isRegularItem() || Zotero_RecognizePDF.canRecognize(items[i])) { |
| | 2253 | canCreateParent = false; |
| | 2254 | break; |
| | 2255 | } |
| | 2256 | } |
| | 2257 | if (canCreateParent) { |
| | 2258 | show.push(m.createParent); |
| | 2259 | } |
| | 2260 | else { |
| | 2261 | hide.push(m.createParent); |
| | 2262 | } |
| | 2263 | } |
| | 2264 | |
| | 2265 | // If all items are child attachments, show rename option |
| | 2266 | var canRename = true; |
| | 2267 | for (var i=0; i<items.length; i++) { |
| | 2268 | var item = items[i]; |
| | 2269 | // Same check as in rename function |
| | 2270 | if (!item.isAttachment() || !item.getSource() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) { |
| | 2271 | canRename = false; |
| | 2272 | break; |
| | 2273 | } |
| | 2274 | } |
| | 2275 | if (canRename) { |
| | 2276 | show.push(m.renameAttachments); |
| | 2277 | } |
| | 2278 | else { |
| | 2279 | hide.push(m.renameAttachments); |
| | 2280 | } |
| | 2281 | |
| | 2282 | // Add in attachment separator |
| | 2283 | if (canCreateParent || canRecognize || canRename || canIndex) { |
| | 2284 | show.push(m.sep4); |
| | 2285 | } |
| | 2286 | else { |
| | 2287 | hide.push(m.sep4); |
| | 2288 | } |
| | 2289 | |
| | 2290 | // Block certain actions on files if no access and at least one item |
| | 2291 | // is an imported attachment |
| | 2292 | if (!itemGroup.filesEditable) { |
| | 2293 | var hasImportedAttachment = false; |
| | 2294 | for (var i=0; i<items.length; i++) { |
| | 2295 | var item = items[i]; |
| | 2296 | if (item.isImportedAttachment()) { |
| | 2297 | hasImportedAttachment = true; |
| | 2298 | break; |
| | 2299 | } |
| | 2300 | } |
| | 2301 | if (hasImportedAttachment) { |
| | 2302 | var d = [m.deleteFromLibrary, m.createParent, m.renameAttachments]; |
| | 2303 | for each(var val in d) { |
| | 2304 | disable.push(val); |
| | 2305 | var index = enable.indexOf(val); |
| | 2306 | if (index != -1) { |
| | 2307 | enable.splice(index, 1); |
| | 2308 | } |
| | 2309 | } |
| | 2310 | } |
| | 2311 | } |
| | 2312 | } |
| | 2313 | |
| | 2314 | // Single item selected |
| | 2315 | else |
| | 2316 | { |
| | 2317 | var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex).ref; |
| | 2318 | var itemID = item.id; |
| | 2319 | menu.setAttribute('itemID', itemID); |
| | 2320 | |
| | 2321 | // Show in Library |
| | 2322 | if (!itemGroup.isLibrary() && !itemGroup.isWithinGroup()) { |
| | 2323 | show.push(m.showInLibrary, m.sep1); |
| | 2324 | } |
| | 2325 | else { |
| | 2326 | hide.push(m.showInLibrary, m.sep1); |
| | 2327 | } |
| | 2328 | |
| | 2329 | if (item.isRegularItem()) |
| | 2330 | { |
| | 2331 | show.push(m.addNote, m.addAttachments, m.sep2); |
| | 2332 | } |
| | 2333 | else |
| | 2334 | { |
| | 2335 | hide.push(m.addNote, m.addAttachments, m.sep2); |
| | 2336 | } |
| | 2337 | |
| | 2338 | |
| | 2339 | if (item.isAttachment()) { |
| | 2340 | var showSep4 = false; |
| | 2341 | hide.push(m.duplicateItem); |
| | 2342 | if (Zotero.Prefs.get('debugShowDuplicates')) { |
| | 2343 | disable.push(m.duplicateMark); |
| | 2344 | } |
| | 2345 | |
| | 2346 | if (Zotero_RecognizePDF.canRecognize(item)) { |
| | 2347 | show.push(m.recognizePDF); |
| | 2348 | hide.push(m.createParent); |
| | 2349 | showSep4 = true; |
| | 2350 | } |
| | 2351 | else { |
| | 2352 | hide.push(m.recognizePDF); |
| | 2353 | |
| | 2354 | // If not a PDF, allow parent item creation |
| | 2355 | if (item.isTopLevelItem()) { |
| | 2356 | show.push(m.createParent); |
| | 2357 | showSep4 = true; |
| | 2358 | } |
| | 2359 | else { |
| | 2360 | hide.push(m.createParent); |
| | 2361 | } |
| | 2362 | } |
| | 2363 | |
| | 2364 | // Attachment rename option |
| | 2365 | if (item.getSource() && item.attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) { |
| | 2366 | show.push(m.renameAttachments); |
| | 2367 | showSep4 = true; |
| | 2368 | } |
| | 2369 | else { |
| | 2370 | hide.push(m.renameAttachments); |
| | 2371 | } |
| | 2372 | |
| | 2373 | if (showSep4) { |
| | 2374 | show.push(m.sep4); |
| | 2375 | } |
| | 2376 | else { |
| | 2377 | hide.push(m.sep4); |
| | 2378 | } |
| | 2379 | |
| | 2380 | // If not linked URL, show reindex line |
| | 2381 | if (Zotero.Fulltext.pdfConverterIsRegistered() |
| | 2382 | && Zotero.Fulltext.canReindex(item.id)) { |
| | 2383 | show.push(m.reindexItem); |
| | 2384 | showSep4 = true; |
| | 2385 | } |
| | 2386 | else { |
| | 2387 | hide.push(m.reindexItem); |
| | 2388 | } |
| | 2389 | } |
| | 2390 | else { // not an attachment |
| | 2391 | if (Zotero.Prefs.get('debugShowDuplicates')) { |
| | 2392 | if (!this.itemsView._itemGroup.duplicates.isNew(item.id)) { |
| | 2393 | enable.push(m.duplicateMark); |
| | 2394 | } else { |
| | 2395 | disable.push(m.duplicateMark); |
| | 2396 | } |
| | 2397 | } |
| | 2398 | if (item.isNote() && item.isTopLevelItem()) { |
| | 2399 | show.push(m.sep4, m.createParent); |
| | 2400 | } |
| | 2401 | else { |
| | 2402 | hide.push(m.sep4, m.createParent); |
| | 2403 | } |
| | 2404 | |
| | 2405 | show.push(m.duplicateItem); |
| | 2406 | hide.push(m.recognizePDF, m.renameAttachments, m.reindexItem); |
| | 2407 | } |
| | 2408 | |
| | 2409 | // Update attachment submenu |
| | 2410 | var popup = document.getElementById('zotero-add-attachment-popup') |
| | 2411 | this.updateAttachmentButtonMenu(popup); |
| | 2412 | |
| | 2413 | // Block certain actions on files if no access |
| | 2414 | if (item.isImportedAttachment() && !itemGroup.filesEditable) { |
| | 2415 | var d = [m.deleteFromLibrary, m.createParent, m.renameAttachments]; |
| | 2416 | for each(var val in d) { |
| | 2417 | disable.push(val); |
| | 2418 | var index = enable.indexOf(val); |
| | 2419 | if (index != -1) { |
| | 2420 | enable.splice(index, 1); |
| | 2421 | } |
| | 2422 | } |
| | 2423 | } |
| | 2424 | } |
| | 2425 | } |
| | 2426 | // No items selected |
| | 2427 | else |
| | 2428 | { |
| | 2429 | // Show in Library |
| | 2430 | if (!itemGroup.isLibrary()) { |
| | 2431 | show.push(m.showInLibrary, m.sep1); |
| | 2432 | } |
| | 2433 | else { |
| | 2434 | hide.push(m.showInLibrary, m.sep1); |
| | 2435 | } |
| | 2436 | |
| | 2437 | disable.push(m.showInLibrary, m.duplicateItem, m.deleteItem, |
| | 2438 | m.deleteFromLibrary, m.exportItems, m.createBib, m.loadReport); |
| | 2439 | hide.push(m.addNote, m.addAttachments, m.sep2, m.sep4, m.reindexItem, |
| | 2440 | m.createParent, m.recognizePDF, m.renameAttachments); |
| | 2441 | } |
| | 2442 | |
| | 2443 | // TODO: implement menu for remote items |
| | 2444 | if (!itemGroup.editable) { |
| | 2445 | for (var i in m) { |
| | 2446 | // Still show export/bib/report for non-editable views |
| | 2447 | // (other than Commons buckets, which aren't real items) |
| | 2448 | if (!itemGroup.isBucket()) { |
| | 2449 | switch (i) { |
| | 2450 | case 'exportItems': |
| | 2451 | case 'createBib': |
| | 2452 | case 'loadReport': |
| | 2453 | continue; |
| | 2454 | } |
| | 2455 | } |
| | 2456 | disable.push(m[i]); |
| | 2457 | var index = enable.indexOf(m[i]); |
| | 2458 | if (index != -1) { |
| | 2459 | enable.splice(index, 1); |
| | 2460 | } |
| | 2461 | } |
| | 2462 | } |
| | 2463 | |
| | 2464 | // Remove from collection |
| | 2465 | if (this.itemsView._itemGroup.isCollection() && !(item && item.getSource())) |
| | 2466 | { |
| | 2467 | menu.childNodes[m.deleteItem].setAttribute('label', Zotero.getString('pane.items.menu.remove' + multiple)); |
| | 2468 | show.push(m.deleteItem); |
| | 2469 | } |
| | 2470 | else |
| | 2471 | { |
| | 2472 | hide.push(m.deleteItem); |
| | 2473 | } |
| | 2474 | |
| | 2475 | if (Zotero.Prefs.get('debugShowDuplicates')) { |
| | 2476 | show.push(m.sep5); |
| | 2477 | show.push(m.duplicateMark); |
| | 2478 | } |
| | 2479 | |
| | 2480 | if (this.itemsView._itemGroup.showDuplicates) { |
| | 2481 | var itemID = undefined; |
| | 2482 | var duplicateStatus = false; |
| | 2483 | var items = this.getSelectedItems(); |
| | 2484 | if (items.length) { |
| | 2485 | var item = items[0]; |
| | 2486 | itemID = items[0].id; |
| | 2487 | duplicateStatus = this.itemsView._itemGroup.duplicates.getDuplicateStatus(itemID); |
| | 2488 | show.push(m.duplicateSubstitute); |
| | 2489 | show.push(m.duplicateUnmark); |
| | 2490 | show.push(m.duplicateUnmarkAll); |
| | 2491 | |
| | 2492 | if (!duplicateStatus) { |
| | 2493 | // Handled above with main menu items (actually, this |
| | 2494 | // is probably never reached) |
| | 2495 | // enable.push(m.duplicateMark); |
| | 2496 | disable.push(m.duplicateUnmark); |
| | 2497 | } else { |
| | 2498 | // Handled above with main menu items |
| | 2499 | // disable.push(m.duplicateMark); |
| | 2500 | enable.push(m.duplicateUnmark); |
| | 2501 | } |
| | 2502 | if (duplicateStatus === 1) { |
| | 2503 | // For green items (new items with no match partners) |
| | 2504 | enable.push(m.duplicateUnmarkAll); |
| | 2505 | } else { |
| | 2506 | disable.push(m.duplicateUnmarkAll); |
| | 2507 | } |
| | 2508 | |
| | 2509 | // For yellow items (new items with non-new match partners) |
| | 2510 | if (duplicateStatus === 2 && item.numAttachments() === 0) { |
| | 2511 | enable.push(m.duplicateSubstitute); |
| | 2512 | } else { |
| | 2513 | disable.push(m.duplicateSubstitute); |
| | 2514 | } |
| | 2515 | } else { |
| | 2516 | // XXXX enable.push(m.duplicateMark); |
| | 2517 | hide.push(m.duplicateSubstitute); |
| | 2518 | hide.push(m.duplicateUnmark); |
| | 2519 | hide.push(m.duplicateUnmarkAll); |
| | 2520 | } |
| | 2521 | } else { |
| | 2522 | // XXXXX: Todo: implement getNewStatus, to be checked from |
| | 2523 | // any group. Needs duplicates object to be set on any |
| | 2524 | // group seen by getChildItems in itemTreeView |
| | 2525 | // |
| | 2526 | // Once that's in place, we'll need (a) a link-only option, and |
| | 2527 | // (b) a link-and-merge option, and we're golden for the basics. |
| | 2528 | // |
| | 2529 | // See above (main menu items area) for duplicateMark setting. |
| | 2530 | hide.push(m.duplicateSubstitute); |
| | 2531 | hide.push(m.duplicateUnmark); |
| | 2532 | hide.push(m.duplicateUnmarkAll); |
| | 2533 | } |
| | 2534 | |
| | 2535 | // Plural if necessary |
| | 2536 | menu.childNodes[m.deleteFromLibrary].setAttribute('label', Zotero.getString('pane.items.menu.erase' + multiple)); |
| | 2537 | menu.childNodes[m.exportItems].setAttribute('label', Zotero.getString('pane.items.menu.export' + multiple)); |
| | 2538 | menu.childNodes[m.createBib].setAttribute('label', Zotero.getString('pane.items.menu.createBib' + multiple)); |
| | 2539 | menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.items.menu.generateReport' + multiple)); |
| | 2540 | menu.childNodes[m.createParent].setAttribute('label', Zotero.getString('pane.items.menu.createParent' + multiple)); |
| | 2541 | menu.childNodes[m.recognizePDF].setAttribute('label', Zotero.getString('pane.items.menu.recognizePDF' + multiple)); |
| | 2542 | menu.childNodes[m.renameAttachments].setAttribute('label', Zotero.getString('pane.items.menu.renameAttachments' + multiple)); |
| | 2543 | menu.childNodes[m.reindexItem].setAttribute('label', Zotero.getString('pane.items.menu.reindexItem' + multiple)); |
| | 2544 | menu.childNodes[m.duplicateMark].setAttribute('label', Zotero.getString('pane.items.menu.duplicateMark' + multiple)); |
| | 2545 | menu.childNodes[m.duplicateSubstitute].setAttribute('label', Zotero.getString('pane.items.menu.duplicateSubstitute')); |
| | 2546 | menu.childNodes[m.duplicateUnmark].setAttribute('label', Zotero.getString('pane.items.menu.duplicateUnmark')); |
| | 2547 | menu.childNodes[m.duplicateUnmarkAll].setAttribute('label', Zotero.getString('pane.items.menu.duplicateUnmarkAll')); |
| | 2548 | |
| | 2549 | for (var i in disable) |
| | 2550 | { |
| | 2551 | menu.childNodes[disable[i]].setAttribute('disabled', true); |
| | 2552 | } |
| | 2553 | |
| | 2554 | for (var i in enable) |
| | 2555 | { |
| | 2556 | menu.childNodes[enable[i]].setAttribute('disabled', false); |
| | 2557 | } |
| | 2558 | |
| | 2559 | for (var i in hide) |
| | 2560 | { |
| | 2561 | menu.childNodes[hide[i]].setAttribute('hidden', true); |
| | 2562 | } |
| | 2563 | |
| | 2564 | for (var i in show) |
| | 2565 | { |
| | 2566 | menu.childNodes[show[i]].setAttribute('hidden', false); |
| | 2567 | } |
| | 2568 | } |
| | 2569 | |
| | 2570 | |
| | 2571 | // Adapted from: http://www.xulplanet.com/references/elemref/ref_tree.html#cmnote-9 |
| | 2572 | this.onTreeClick = function (event) { |
| | 2573 | // We only care about primary button double and triple clicks |
| | 2574 | if (!event || (event.detail != 2 && event.detail != 3) || event.button != 0) { |
| | 2575 | return; |
| | 2576 | } |
| | 2577 | |
| | 2578 | var t = event.originalTarget; |
| | 2579 | |
| | 2580 | if (t.localName != 'treechildren') { |
| | 2581 | return; |
| | 2582 | } |
| | 2583 | |
| | 2584 | var tree = t.parentNode; |
| | 2585 | |
| | 2586 | var row = {}, col = {}, obj = {}; |
| | 2587 | tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj); |
| | 2588 | |
| | 2589 | // obj.value == 'cell'/'text'/'image' |
| | 2590 | if (!obj.value) { |
| | 2591 | return; |
| | 2592 | } |
| | 2593 | |
| | 2594 | if (tree.id == 'zotero-collections-tree') { |
| | 2595 | // Ignore triple clicks for collections |
| | 2596 | if (event.detail != 2) { |
| | 2597 | return; |
| | 2598 | } |
| | 2599 | |
| | 2600 | var itemGroup = ZoteroPane.collectionsView._getItemAtRow(tree.view.selection.currentIndex); |
| | 2601 | if (itemGroup.isLibrary()) { |
| | 2602 | var uri = Zotero.URI.getCurrentUserLibraryURI(); |
| | 2603 | if (uri) { |
| | 2604 | ZoteroPane.loadURI(uri); |
| | 2605 | event.stopPropagation(); |
| | 2606 | } |
| | 2607 | return; |
| | 2608 | } |
| | 2609 | |
| | 2610 | if (itemGroup.isSearch()) { |
| | 2611 | ZoteroPane.editSelectedCollection(); |
| | 2612 | return; |
| | 2613 | } |
| | 2614 | |
| | 2615 | if (itemGroup.isGroup()) { |
| | 2616 | var uri = Zotero.URI.getGroupURI(itemGroup.ref, true); |
| | 2617 | ZoteroPane.loadURI(uri); |
| | 2618 | event.stopPropagation(); |
| | 2619 | return; |
| | 2620 | } |
| | 2621 | |
| | 2622 | if (itemGroup.isHeader()) { |
| | 2623 | if (itemGroup.ref.id == 'group-libraries-header') { |
| | 2624 | var uri = Zotero.URI.getGroupsURL(); |
| | 2625 | ZoteroPane.loadURI(uri); |
| | 2626 | event.stopPropagation(); |
| | 2627 | } |
| | 2628 | return; |
| | 2629 | } |
| | 2630 | |
| | 2631 | if (itemGroup.isBucket()) { |
| | 2632 | ZoteroPane.loadURI(itemGroup.ref.uri); |
| | 2633 | event.stopPropagation(); |
| | 2634 | } |
| | 2635 | } |
| | 2636 | else if (tree.id == 'zotero-items-tree') { |
| | 2637 | var viewOnDoubleClick = Zotero.Prefs.get('viewOnDoubleClick'); |
| | 2638 | |
| | 2639 | // Expand/collapse on triple-click |
| | 2640 | if (viewOnDoubleClick) { |
| | 2641 | if (event.detail == 3) { |
| | 2642 | tree.view.toggleOpenState(tree.view.selection.currentIndex); |
| | 2643 | return; |
| | 2644 | } |
| | 2645 | |
| | 2646 | // Don't expand/collapse on double-click |
| | 2647 | event.stopPropagation(); |
| | 2648 | } |
| | 2649 | |
| | 2650 | if (tree.view && tree.view.selection.currentIndex > -1) { |
| | 2651 | var item = ZoteroPane.getSelectedItems()[0]; |
| | 2652 | if (item) { |
| | 2653 | if (item.isRegularItem()) { |
| | 2654 | // Double-click on Commons item should load IA page |
| | 2655 | var itemGroup = ZoteroPane.collectionsView._getItemAtRow( |
| | 2656 | ZoteroPane.collectionsView.selection.currentIndex |
| | 2657 | ); |
| | 2658 | |
| | 2659 | if (itemGroup.isBucket()) { |
| | 2660 | var uri = itemGroup.ref.getItemURI(item); |
| | 2661 | ZoteroPane.loadURI(uri); |
| | 2662 | event.stopPropagation(); |
| | 2663 | return; |
| | 2664 | } |
| | 2665 | |
| | 2666 | if (!viewOnDoubleClick) { |
| | 2667 | return; |
| | 2668 | } |
| | 2669 | |
| | 2670 | var uri = Components.classes["@mozilla.org/network/standard-url;1"]. |
| | 2671 | createInstance(Components.interfaces.nsIURI); |
| | 2672 | var snapID = item.getBestAttachment(); |
| | 2673 | if (snapID) { |
| | 2674 | spec = Zotero.Items.get(snapID).getLocalFileURL(); |
| | 2675 | if (spec) { |
| | 2676 | uri.spec = spec; |
| | 2677 | if (uri.scheme && uri.scheme == 'file') { |
| | 2678 | ZoteroPane.viewAttachment(snapID, event); |
| | 2679 | return; |
| | 2680 | } |
| | 2681 | } |
| | 2682 | } |
| | 2683 | |
| | 2684 | var uri = item.getField('url'); |
| | 2685 | if (!uri) { |
| | 2686 | var doi = item.getField('DOI'); |
| | 2687 | if (doi) { |
| | 2688 | // Pull out DOI, in case there's a prefix |
| | 2689 | doi = Zotero.Utilities.prototype.cleanDOI(doi); |
| | 2690 | if (doi) { |
| | 2691 | uri = "http://dx.doi.org/" + encodeURIComponent(doi); |
| | 2692 | } |
| | 2693 | } |
| | 2694 | } |
| | 2695 | if (uri) { |
| | 2696 | ZoteroPane.loadURI(uri); |
| | 2697 | } |
| | 2698 | } |
| | 2699 | else if (item.isNote()) { |
| | 2700 | if (!ZoteroPane.collectionsView.editable) { |
| | 2701 | return; |
| | 2702 | } |
| | 2703 | document.getElementById('zotero-view-note-button').doCommand(); |
| | 2704 | } |
| | 2705 | else if (item.isAttachment()) { |
| | 2706 | ZoteroPane.viewSelectedAttachment(event); |
| | 2707 | } |
| | 2708 | } |
| | 2709 | } |
| | 2710 | } |
| | 2711 | } |
| | 2712 | |
| | 2713 | |
| | 2714 | this.startDrag = function (event, element) { |
| | 2715 | element.onDragStart(event); |
| | 2716 | } |
| | 2717 | |
| | 2718 | |
| | 2719 | this.dragEnter = function (event, element) { |
| | 2720 | return element.onDragEnter(event); |
| | 2721 | } |
| | 2722 | |
| | 2723 | |
| | 2724 | this.dragOver = function (event, element) { |
| | 2725 | return element.onDragOver(event); |
| | 2726 | } |
| | 2727 | |
| | 2728 | |
| | 2729 | this.dragDrop = function (event, element) { |
| | 2730 | return element.onDrop(event); |
| | 2731 | } |
| | 2732 | |
| | 2733 | |
| | 2734 | this.openPreferences = function (paneID, action) { |
| | 2735 | var io = { |
| | 2736 | pane: paneID, |
| | 2737 | action: action |
| | 2738 | }; |
| | 2739 | window.openDialog('chrome://zotero/content/preferences/preferences.xul', |
| | 2740 | 'zotero-prefs', |
| | 2741 | 'chrome,titlebar,toolbar,centerscreen,' |
| | 2742 | + Zotero.Prefs.get('browser.preferences.instantApply', true) ? 'dialog=no' : 'modal', |
| | 2743 | io |
| | 2744 | ); |
| | 2745 | } |
| | 2746 | |
| | 2747 | |
| | 2748 | /* |
| | 2749 | * Loads a URL following the standard modifier key behavior |
| | 2750 | * (e.g. meta-click == new background tab, meta-shift-click == new front tab, |
| | 2751 | * shift-click == new window, no modifier == frontmost tab |
| | 2752 | */ |
| | 2753 | function loadURI(uri, event, data) { |
| | 2754 | // Open in new tab |
| | 2755 | if (event && (event.metaKey || (!Zotero.isMac && event.ctrlKey))) { |
| | 2756 | var tab = gBrowser.addTab(uri); |
| | 2757 | var browser = gBrowser.getBrowserForTab(tab); |
| | 2758 | |
| | 2759 | if (event.shiftKey) { |
| | 2760 | gBrowser.selectedTab = tab; |
| | 2761 | } |
| | 2762 | } |
| | 2763 | else if (event && event.shiftKey) { |
| | 2764 | window.open(uri, "zotero-loaded-page", |
| | 2765 | "menubar=yes,location=yes,toolbar=yes,personalbar=yes,resizable=yes,scrollbars=yes,status=yes"); |
| | 2766 | } |
| | 2767 | else { |
| | 2768 | window.loadURI(uri); |
| | 2769 | } |
| | 2770 | } |
| | 2771 | |
| | 2772 | |
| | 2773 | function setItemsPaneMessage(msg, lock) { |
| | 2774 | var elem = document.getElementById('zotero-items-pane-message-box'); |
| | 2775 | |
| | 2776 | if (elem.getAttribute('locked') == 'true') { |
| | 2777 | return; |
| | 2778 | } |
| | 2779 | |
| | 2780 | while (elem.hasChildNodes()) { |
| | 2781 | elem.removeChild(elem.firstChild); |
| | 2782 | } |
| | 2783 | var msgParts = msg.split("\n\n"); |
| | 2784 | for (var i=0; i<msgParts.length; i++) { |
| | 2785 | var desc = document.createElement('description'); |
| | 2786 | desc.appendChild(document.createTextNode(msgParts[i])); |
| | 2787 | elem.appendChild(desc); |
| | 2788 | } |
| | 2789 | |
| | 2790 | // Make message permanent |
| | 2791 | if (lock) { |
| | 2792 | elem.setAttribute('locked', true); |
| | 2793 | } |
| | 2794 | |
| | 2795 | document.getElementById('zotero-items-pane-content').selectedIndex = 1; |
| | 2796 | } |
| | 2797 | |
| | 2798 | |
| | 2799 | function clearItemsPaneMessage() { |
| | 2800 | // If message box is locked, don't clear |
| | 2801 | var box = document.getElementById('zotero-items-pane-message-box'); |
| | 2802 | if (box.getAttribute('locked') == 'true') { |
| | 2803 | return; |
| | 2804 | } |
| | 2805 | |
| | 2806 | document.getElementById('zotero-items-pane-content').selectedIndex = 0; |
| | 2807 | } |
| | 2808 | |
| | 2809 | |
| | 2810 | // Updates browser context menu options |
| | 2811 | function contextPopupShowing() |
| | 2812 | { |
| | 2813 | if (!Zotero.Prefs.get('browserContentContextMenu')) { |
| | 2814 | return; |
| | 2815 | } |
| | 2816 | |
| | 2817 | var menuitem = document.getElementById("zotero-context-add-to-current-note"); |
| | 2818 | var showing = false; |
| | 2819 | if (menuitem){ |
| | 2820 | var items = ZoteroPane.getSelectedItems(); |
| | 2821 | if (ZoteroPane.itemsView.selection && ZoteroPane.itemsView.selection.count==1 |
| | 2822 | && items[0] && items[0].isNote() |
| | 2823 | && window.gContextMenu.isTextSelected) |
| | 2824 | { |
| | 2825 | menuitem.hidden = false; |
| | 2826 | showing = true; |
| | 2827 | } |
| | 2828 | else |
| | 2829 | { |
| | 2830 | menuitem.hidden = true; |
| | 2831 | } |
| | 2832 | } |
| | 2833 | |
| | 2834 | var menuitem = document.getElementById("zotero-context-add-to-new-note"); |
| | 2835 | if (menuitem){ |
| | 2836 | if (window.gContextMenu.isTextSelected) |
| | 2837 | { |
| | 2838 | menuitem.hidden = false; |
| | 2839 | showing = true; |
| | 2840 | } |
| | 2841 | else |
| | 2842 | { |
| | 2843 | menuitem.hidden = true; |
| | 2844 | } |
| | 2845 | } |
| | 2846 | |
| | 2847 | var menuitem = document.getElementById("zotero-context-save-link-as-item"); |
| | 2848 | if (menuitem) { |
| | 2849 | if (window.gContextMenu.onLink) { |
| | 2850 | menuitem.hidden = false; |
| | 2851 | showing = true; |
| | 2852 | } |
| | 2853 | else { |
| | 2854 | menuitem.hidden = true; |
| | 2855 | } |
| | 2856 | } |
| | 2857 | |
| | 2858 | var menuitem = document.getElementById("zotero-context-save-image-as-item"); |
| | 2859 | if (menuitem) { |
| | 2860 | // Not using window.gContextMenu.hasBGImage -- if the user wants it, |
| | 2861 | // they can use the Firefox option to view and then import from there |
| | 2862 | if (window.gContextMenu.onImage) { |
| | 2863 | menuitem.hidden = false; |
| | 2864 | showing = true; |
| | 2865 | } |
| | 2866 | else { |
| | 2867 | menuitem.hidden = true; |
| | 2868 | } |
| | 2869 | } |
| | 2870 | |
| | 2871 | // If Zotero is locked or library is read-only, disable menu items |
| | 2872 | var menu = document.getElementById('zotero-content-area-context-menu'); |
| | 2873 | menu.hidden = !showing; |
| | 2874 | var disabled = Zotero.locked; |
| | 2875 | if (!disabled && self.collectionsView.selection && self.collectionsView.selection.count) { |
| | 2876 | var itemGroup = self.collectionsView._getItemAtRow(self.collectionsView.selection.currentIndex); |
| | 2877 | disabled = !itemGroup.editable; |
| | 2878 | } |
| | 2879 | for each(var menuitem in menu.firstChild.childNodes) { |
| | 2880 | menuitem.disabled = disabled; |
| | 2881 | } |
| | 2882 | } |
| | 2883 | |
| | 2884 | |
| | 2885 | this.newNote = function (popup, parent, text, citeURI) { |
| | 2886 | if (!Zotero.stateCheck()) { |
| | 2887 | this.displayErrorMessage(true); |
| | 2888 | return; |
| | 2889 | } |
| | 2890 | |
| | 2891 | if (!this.canEdit()) { |
| | 2892 | this.displayCannotEditLibraryMessage(); |
| | 2893 | return; |
| | 2894 | } |
| | 2895 | |
| | 2896 | if (!popup) { |
| | 2897 | if (!text) { |
| | 2898 | text = ''; |
| | 2899 | } |
| | 2900 | text = Zotero.Utilities.prototype.trim(text); |
| | 2901 | |
| | 2902 | if (text) { |
| | 2903 | text = '<blockquote' |
| | 2904 | + (citeURI ? ' cite="' + citeURI + '"' : '') |
| | 2905 | + '>' + Zotero.Utilities.prototype.text2html(text) + "</blockquote>"; |
| | 2906 | } |
| | 2907 | |
| | 2908 | var item = new Zotero.Item('note'); |
| | 2909 | item.libraryID = this.getSelectedLibraryID(); |
| | 2910 | item.setNote(text); |
| | 2911 | if (parent) { |
| | 2912 | item.setSource(parent); |
| | 2913 | } |
| | 2914 | var itemID = item.save(); |
| | 2915 | |
| | 2916 | if (!parent && this.itemsView && this.itemsView._itemGroup.isCollection()) { |
| | 2917 | this.itemsView._itemGroup.ref.addItem(itemID); |
| | 2918 | } |
| | 2919 | |
| | 2920 | this.selectItem(itemID); |
| | 2921 | |
| | 2922 | document.getElementById('zotero-note-editor').focus(); |
| | 2923 | } |
| | 2924 | else |
| | 2925 | { |
| | 2926 | // TODO: _text_ |
| | 2927 | var c = this.getSelectedCollection(); |
| | 2928 | if (c) { |
| | 2929 | this.openNoteWindow(null, c.id, parent); |
| | 2930 | } |
| | 2931 | else { |
| | 2932 | this.openNoteWindow(null, null, parent); |
| | 2933 | } |
| | 2934 | } |
| | 2935 | } |
| | 2936 | |
| | 2937 | |
| | 2938 | function addTextToNote(text, citeURI) |
| | 2939 | { |
| | 2940 | if (!this.canEdit()) { |
| | 2941 | this.displayCannotEditLibraryMessage(); |
| | 2942 | return; |
| | 2943 | } |
| | 2944 | |
| | 2945 | if (!text) { |
| | 2946 | return false; |
| | 2947 | } |
| | 2948 | |
| | 2949 | text = Zotero.Utilities.prototype.trim(text); |
| | 2950 | |
| | 2951 | if (!text.length) { |
| | 2952 | return false; |
| | 2953 | } |
| | 2954 | |
| | 2955 | text = '<blockquote' |
| | 2956 | + (citeURI ? ' cite="' + citeURI + '"' : '') |
| | 2957 | + '>' + Zotero.Utilities.prototype.text2html(text) + "</blockquote>"; |
| | 2958 | |
| | 2959 | var items = this.getSelectedItems(); |
| | 2960 | if (this.itemsView.selection.count == 1 && items[0] && items[0].isNote()) { |
| | 2961 | var note = items[0].getNote() |
| | 2962 | |
| | 2963 | items[0].setNote(note + text); |
| | 2964 | items[0].save(); |
| | 2965 | |
| | 2966 | var noteElem = document.getElementById('zotero-note-editor') |
| | 2967 | noteElem.focus(); |
| | 2968 | return true; |
| | 2969 | } |
| | 2970 | |
| | 2971 | return false; |
| | 2972 | } |
| | 2973 | |
| | 2974 | function openNoteWindow(itemID, col, parentItemID) |
| | 2975 | { |
| | 2976 | if (!this.canEdit()) { |
| | 2977 | this.displayCannotEditLibraryMessage(); |
| | 2978 | return; |
| | 2979 | } |
| | 2980 | |
| | 2981 | var name = null; |
| | 2982 | |
| | 2983 | if (itemID) { |
| | 2984 | // Create a name for this window so we can focus it later |
| | 2985 | // |
| | 2986 | // Collection is only used on new notes, so we don't need to |
| | 2987 | // include it in the name |
| | 2988 | name = 'zotero-note-' + itemID; |
| | 2989 | |
| | 2990 | var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] |
| | 2991 | .getService(Components.interfaces.nsIWindowMediator); |
| | 2992 | var e = wm.getEnumerator(''); |
| | 2993 | while (e.hasMoreElements()) { |
| | 2994 | var w = e.getNext(); |
| | 2995 | if (w.name == name) { |
| | 2996 | w.focus(); |
| | 2997 | return; |
| | 2998 | } |
| | 2999 | } |
| | 3000 | } |
| | 3001 | |
| | 3002 | window.open('chrome://zotero/content/note.xul?v=1' |
| | 3003 | + (itemID ? '&id=' + itemID : '') + (col ? '&coll=' + col : '') |
| | 3004 | + (parentItemID ? '&p=' + parentItemID : ''), |
| | 3005 | name, 'chrome,resizable,centerscreen'); |
| | 3006 | } |
| | 3007 | |
| | 3008 | |
| | 3009 | function addAttachmentFromDialog(link, id) |
| | 3010 | { |
| | 3011 | if (!this.canEdit()) { |
| | 3012 | this.displayCannotEditLibraryMessage(); |
| | 3013 | return; |
| | 3014 | } |
| | 3015 | |
| | 3016 | var itemGroup = ZoteroPane.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex); |
| | 3017 | if (link && itemGroup.isWithinGroup()) { |
| | 3018 | var pr = Components.classes["@mozilla.org/network/default-prompt;1"] |
| | 3019 | .getService(Components.interfaces.nsIPrompt); |
| | 3020 | pr.alert("", "Linked files cannot be added to group libraries."); |
| | 3021 | return; |
| | 3022 | } |
| | 3023 | |
| | 3024 | // TODO: disable in menu |
| | 3025 | if (!this.canEditFiles()) { |
| | 3026 | this.displayCannotEditLibraryFilesMessage(); |
| | 3027 | return; |
| | 3028 | } |
| | 3029 | |
| | 3030 | var libraryID = itemGroup.ref.libraryID; |
| | 3031 | |
| | 3032 | var nsIFilePicker = Components.interfaces.nsIFilePicker; |
| | 3033 | var fp = Components.classes["@mozilla.org/filepicker;1"] |
| | 3034 | .createInstance(nsIFilePicker); |
| | 3035 | fp.init(window, Zotero.getString('pane.item.attachments.select'), nsIFilePicker.modeOpenMultiple); |
| | 3036 | fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll); |
| | 3037 | |
| | 3038 | if(fp.show() == nsIFilePicker.returnOK) |
| | 3039 | { |
| | 3040 | var files = fp.files; |
| | 3041 | while (files.hasMoreElements()){ |
| | 3042 | var file = files.getNext(); |
| | 3043 | file.QueryInterface(Components.interfaces.nsILocalFile); |
| | 3044 | var attachmentID; |
| | 3045 | if(link) |
| | 3046 | attachmentID = Zotero.Attachments.linkFromFile(file, id); |
| | 3047 | else |
| | 3048 | attachmentID = Zotero.Attachments.importFromFile(file, id, libraryID); |
| | 3049 | |
| | 3050 | if(attachmentID && !id) |
| | 3051 | { |
| | 3052 | var c = this.getSelectedCollection(); |
| | 3053 | if(c) |
| | 3054 | c.addItem(attachmentID); |
| | 3055 | } |
| | 3056 | } |
| | 3057 | } |
| | 3058 | } |
| | 3059 | |
| | 3060 | |
| | 3061 | this.addItemFromPage = function (itemType, saveSnapshot, row) { |
| | 3062 | if (!this.canEdit(row)) { |
| | 3063 | this.displayCannotEditLibraryMessage(); |
| | 3064 | return; |
| | 3065 | } |
| | 3066 | |
| | 3067 | return this.addItemFromDocument(window.content.document, itemType, saveSnapshot, row); |
| | 3068 | } |
| | 3069 | |
| | 3070 | |
| | 3071 | /** |
| | 3072 | * @param {Document} doc |
| | 3073 | * @param {String|Integer} [itemType='webpage'] Item type id or name |
| | 3074 | * @param {Boolean} [saveSnapshot] Force saving or non-saving of a snapshot, |
| | 3075 | * regardless of automaticSnapshots pref |
| | 3076 | */ |
| | 3077 | this.addItemFromDocument = function (doc, itemType, saveSnapshot, row) { |
| | 3078 | var progressWin = new Zotero.ProgressWindow(); |
| | 3079 | progressWin.changeHeadline(Zotero.getString('ingester.scraping')); |
| | 3080 | var icon = 'chrome://zotero/skin/treeitem-webpage.png'; |
| | 3081 | progressWin.addLines(doc.title, icon) |
| | 3082 | progressWin.show(); |
| | 3083 | progressWin.startCloseTimer(); |
| | 3084 | |
| | 3085 | // Save snapshot if explicitly enabled or automatically pref is set and not explicitly disabled |
| | 3086 | saveSnapshot = saveSnapshot || (saveSnapshot !== false && Zotero.Prefs.get('automaticSnapshots')); |
| | 3087 | |
| | 3088 | // TODO: this, needless to say, is a temporary hack |
| | 3089 | if (itemType == 'temporaryPDFHack') { |
| | 3090 | itemType = null; |
| | 3091 | var isPDF = false; |
| | 3092 | if (doc.title.indexOf('application/pdf') != -1) { |
| | 3093 | isPDF = true; |
| | 3094 | } |
| | 3095 | else { |
| | 3096 | var ios = Components.classes["@mozilla.org/network/io-service;1"]. |
| | 3097 | getService(Components.interfaces.nsIIOService); |
| | 3098 | try { |
| | 3099 | var uri = ios.newURI(doc.location, null, null); |
| | 3100 | if (uri.fileName && uri.fileName.match(/pdf$/)) { |
| | 3101 | isPDF = true; |
| | 3102 | } |
| | 3103 | } |
| | 3104 | catch (e) { |
| | 3105 | Zotero.debug(e); |
| | 3106 | Components.utils.reportError(e); |
| | 3107 | } |
| | 3108 | } |
| | 3109 | |
| | 3110 | if (isPDF && saveSnapshot) { |
| | 3111 | // |
| | 3112 | // Duplicate newItem() checks here |
| | 3113 | // |
| | 3114 | if (!Zotero.stateCheck()) { |
| | 3115 | this.displayErrorMessage(true); |
| | 3116 | return false; |
| | 3117 | } |
| | 3118 | |
| | 3119 | // Currently selected row |
| | 3120 | if (row === undefined) { |
| | 3121 | row = this.collectionsView.selection.currentIndex; |
| | 3122 | } |
| | 3123 | |
| | 3124 | if (!this.canEdit(row)) { |
| | 3125 | this.displayCannotEditLibraryMessage(); |
| | 3126 | return; |
| | 3127 | } |
| | 3128 | |
| | 3129 | if (row !== undefined) { |
| | 3130 | var itemGroup = this.collectionsView._getItemAtRow(row); |
| | 3131 | var libraryID = itemGroup.ref.libraryID; |
| | 3132 | } |
| | 3133 | else { |
| | 3134 | var libraryID = null; |
| | 3135 | var itemGroup = null; |
| | 3136 | } |
| | 3137 | // |
| | 3138 | // |
| | 3139 | // |
| | 3140 | |
| | 3141 | if (!this.canEditFiles(row)) { |
| | 3142 | this.displayCannotEditLibraryFilesMessage(); |
| | 3143 | return; |
| | 3144 | } |
| | 3145 | |
| | 3146 | if (itemGroup && itemGroup.isCollection()) { |
| | 3147 | var collectionID = itemGroup.ref.id; |
| | 3148 | } |
| | 3149 | else { |
| | 3150 | var collectionID = false; |
| | 3151 | } |
| | 3152 | |
| | 3153 | var itemID = Zotero.Attachments.importFromDocument(doc, false, false, collectionID, null, libraryID); |
| | 3154 | |
| | 3155 | // importFromDocument() doesn't trigger the notifier for a second |
| | 3156 | // |
| | 3157 | // The one-second delay is weird but better than nothing |
| | 3158 | var self = this; |
| | 3159 | setTimeout(function () { |
| | 3160 | self.selectItem(itemID); |
| | 3161 | }, 1001); |
| | 3162 | |
| | 3163 | return; |
| | 3164 | } |
| | 3165 | } |
| | 3166 | |
| | 3167 | // Save web page item by default |
| | 3168 | if (!itemType) { |
| | 3169 | itemType = 'webpage'; |
| | 3170 | } |
| | 3171 | var data = { |
| | 3172 | title: doc.title, |
| | 3173 | url: doc.location.href, |
| | 3174 | accessDate: "CURRENT_TIMESTAMP" |
| | 3175 | } |
| | 3176 | itemType = Zotero.ItemTypes.getID(itemType); |
| | 3177 | var item = this.newItem(itemType, data, row); |
| | 3178 | |
| | 3179 | if (item.libraryID) { |
| | 3180 | var group = Zotero.Groups.getByLibraryID(item.libraryID); |
| | 3181 | filesEditable = group.filesEditable; |
| | 3182 | } |
| | 3183 | else { |
| | 3184 | filesEditable = true; |
| | 3185 | } |
| | 3186 | |
| | 3187 | if (saveSnapshot) { |
| | 3188 | var link = false; |
| | 3189 | |
| | 3190 | if (link) { |
| | 3191 | Zotero.Attachments.linkFromDocument(doc, item.id); |
| | 3192 | } |
| | 3193 | else if (filesEditable) { |
| | 3194 | Zotero.Attachments.importFromDocument(doc, item.id); |
| | 3195 | } |
| | 3196 | } |
| | 3197 | |
| | 3198 | return item.id; |
| | 3199 | } |
| | 3200 | |
| | 3201 | |
| | 3202 | this.addItemFromURL = function (url, itemType, saveSnapshot, row) { |
| | 3203 | if (url == window.content.document.location.href) { |
| | 3204 | return this.addItemFromPage(itemType, saveSnapshot, row); |
| | 3205 | } |
| | 3206 | |
| | 3207 | var self = this; |
| | 3208 | |
| | 3209 | Zotero.MIME.getMIMETypeFromURL(url, function (mimeType, hasNativeHandler) { |
| | 3210 | // If native type, save using a hidden browser |
| | 3211 | if (hasNativeHandler) { |
| | 3212 | var processor = function (doc) { |
| | 3213 | ZoteroPane.addItemFromDocument(doc, itemType, saveSnapshot, row); |
| | 3214 | }; |
| | 3215 | |
| | 3216 | var done = function () {} |
| | 3217 | |
| | 3218 | var exception = function (e) { |
| | 3219 | Zotero.debug(e); |
| | 3220 | } |
| | 3221 | |
| | 3222 | Zotero.Utilities.HTTP.processDocuments([url], processor, done, exception); |
| | 3223 | } |
| | 3224 | // Otherwise create placeholder item, attach attachment, and update from that |
| | 3225 | else { |
| | 3226 | // TODO: this, needless to say, is a temporary hack |
| | 3227 | if (itemType == 'temporaryPDFHack') { |
| | 3228 | itemType = null; |
| | 3229 | |
| | 3230 | if (mimeType == 'application/pdf') { |
| | 3231 | // |
| | 3232 | // Duplicate newItem() checks here |
| | 3233 | // |
| | 3234 | if (!Zotero.stateCheck()) { |
| | 3235 | ZoteroPane.displayErrorMessage(true); |
| | 3236 | return false; |
| | 3237 | } |
| | 3238 | |
| | 3239 | // Currently selected row |
| | 3240 | if (row === undefined) { |
| | 3241 | row = ZoteroPane.collectionsView.selection.currentIndex; |
| | 3242 | } |
| | 3243 | |
| | 3244 | if (!ZoteroPane.canEdit(row)) { |
| | 3245 | ZoteroPane.displayCannotEditLibraryMessage(); |
| | 3246 | return; |
| | 3247 | } |
| | 3248 | |
| | 3249 | if (row !== undefined) { |
| | 3250 | var itemGroup = ZoteroPane.collectionsView._getItemAtRow(row); |
| | 3251 | var libraryID = itemGroup.ref.libraryID; |
| | 3252 | } |
| | 3253 | else { |
| | 3254 | var libraryID = null; |
| | 3255 | var itemGroup = null; |
| | 3256 | } |
| | 3257 | // |
| | 3258 | // |
| | 3259 | // |
| | 3260 | |
| | 3261 | if (!ZoteroPane.canEditFiles(row)) { |
| | 3262 | ZoteroPane.displayCannotEditLibraryFilesMessage(); |
| | 3263 | return; |
| | 3264 | } |
| | 3265 | |
| | 3266 | if (itemGroup && itemGroup.isCollection()) { |
| | 3267 | var collectionID = itemGroup.ref.id; |
| | 3268 | } |
| | 3269 | else { |
| | 3270 | var collectionID = false; |
| | 3271 | } |
| | 3272 | |
| | 3273 | var attachmentItem = Zotero.Attachments.importFromURL(url, false, false, false, collectionID, mimeType, libraryID); |
| | 3274 | |
| | 3275 | // importFromURL() doesn't trigger the notifier until |
| | 3276 | // after download is complete |
| | 3277 | // |
| | 3278 | // TODO: add a callback to importFromURL() |
| | 3279 | setTimeout(function () { |
| | 3280 | self.selectItem(attachmentItem.id); |
| | 3281 | }, 1001); |
| | 3282 | |
| | 3283 | return; |
| | 3284 | } |
| | 3285 | } |
| | 3286 | |
| | 3287 | if (!itemType) { |
| | 3288 | itemType = 'webpage'; |
| | 3289 | } |
| | 3290 | |
| | 3291 | var item = ZoteroPane.newItem(itemType, {}, row); |
| | 3292 | |
| | 3293 | if (item.libraryID) { |
| | 3294 | var group = Zotero.Groups.getByLibraryID(item.libraryID); |
| | 3295 | filesEditable = group.filesEditable; |
| | 3296 | } |
| | 3297 | else { |
| | 3298 | filesEditable = true; |
| | 3299 | } |
| | 3300 | |
| | 3301 | // Save snapshot if explicitly enabled or automatically pref is set and not explicitly disabled |
| | 3302 | if (saveSnapshot || (saveSnapshot !== false && Zotero.Prefs.get('automaticSnapshots'))) { |
| | 3303 | var link = false; |
| | 3304 | |
| | 3305 | if (link) { |
| | 3306 | //Zotero.Attachments.linkFromURL(doc, item.id); |
| | 3307 | } |
| | 3308 | else if (filesEditable) { |
| | 3309 | var attachmentItem = Zotero.Attachments.importFromURL(url, item.id, false, false, false, mimeType); |
| | 3310 | if (attachmentItem) { |
| | 3311 | item.setField('title', attachmentItem.getField('title')); |
| | 3312 | item.setField('url', attachmentItem.getField('url')); |
| | 3313 | item.setField('accessDate', attachmentItem.getField('accessDate')); |
| | 3314 | item.save(); |
| | 3315 | } |
| | 3316 | } |
| | 3317 | } |
| | 3318 | |
| | 3319 | return item.id; |
| | 3320 | |
| | 3321 | } |
| | 3322 | }); |
| | 3323 | } |
| | 3324 | |
| | 3325 | |
| | 3326 | /* |
| | 3327 | * Create an attachment from the current page |
| | 3328 | * |
| | 3329 | * |itemID| -- itemID of parent item |
| | 3330 | * |link| -- create web link instead of snapshot |
| | 3331 | */ |
| | 3332 | this.addAttachmentFromPage = function (link, itemID) |
| | 3333 | { |
| | 3334 | if (!Zotero.stateCheck()) { |
| | 3335 | this.displayErrorMessage(true); |
| | 3336 | return; |
| | 3337 | } |
| | 3338 | |
| | 3339 | if (typeof itemID != 'number') { |
| | 3340 | throw ("itemID must be an integer in ZoteroPane.addAttachmentFromPage()"); |
| | 3341 | } |
| | 3342 | |
| | 3343 | var progressWin = new Zotero.ProgressWindow(); |
| | 3344 | progressWin.changeHeadline(Zotero.getString('save.' + (link ? 'link' : 'attachment'))); |
| | 3345 | var type = link ? 'web-link' : 'snapshot'; |
| | 3346 | var icon = 'chrome://zotero/skin/treeitem-attachment-' + type + '.png'; |
| | 3347 | progressWin.addLines(window.content.document.title, icon) |
| | 3348 | progressWin.show(); |
| | 3349 | progressWin.startCloseTimer(); |
| | 3350 | |
| | 3351 | if (link) { |
| | 3352 | Zotero.Attachments.linkFromDocument(window.content.document, itemID); |
| | 3353 | } |
| | 3354 | else { |
| | 3355 | Zotero.Attachments.importFromDocument(window.content.document, itemID); |
| | 3356 | } |
| | 3357 | } |
| | 3358 | |
| | 3359 | |
| | 3360 | function viewAttachment(itemID, event, noLocateOnMissing) { |
| | 3361 | var attachment = Zotero.Items.get(itemID); |
| | 3362 | if (!attachment.isAttachment()) { |
| | 3363 | throw ("Item " + itemID + " is not an attachment in ZoteroPane.viewAttachment()"); |
| | 3364 | } |
| | 3365 | |
| | 3366 | if (attachment.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) { |
| | 3367 | this.loadURI(attachment.getField('url'), event); |
| | 3368 | return; |
| | 3369 | } |
| | 3370 | |
| | 3371 | var file = attachment.getFile(); |
| | 3372 | if (file) { |
| | 3373 | var mimeType = attachment.getAttachmentMIMEType(); |
| | 3374 | // If no MIME type specified, try to detect again (I guess in case |
| | 3375 | // we've gotten smarter since the file was imported?) |
| | 3376 | if (!mimeType) { |
| | 3377 | var mimeType = Zotero.MIME.getMIMETypeFromFile(file); |
| | 3378 | var ext = Zotero.File.getExtension(file); |
| | 3379 | |
| | 3380 | // TODO: update DB with new info |
| | 3381 | } |
| | 3382 | var ext = Zotero.File.getExtension(file); |
| | 3383 | var isNative = Zotero.MIME.hasNativeHandler(mimeType, ext); |
| | 3384 | var internal = Zotero.MIME.hasInternalHandler(mimeType, ext); |
| | 3385 | |
| | 3386 | if (isNative || |
| | 3387 | (internal && !Zotero.Prefs.get('launchNonNativeFiles'))) { |
| | 3388 | |
| | 3389 | var url = 'zotero://attachment/' + itemID + '/'; |
| | 3390 | this.loadURI(url, event, { attachmentID: itemID}); |
| | 3391 | } |
| | 3392 | else { |
| | 3393 | var fileURL = attachment.getLocalFileURL(); |
| | 3394 | |
| | 3395 | // Some platforms don't have nsILocalFile.launch, so we just load it and |
| | 3396 | // let the Firefox external helper app window handle it |
| | 3397 | try { |
| | 3398 | file.launch(); |
| | 3399 | } |
| | 3400 | catch (e) { |
| | 3401 | window.loadURI(fileURL); |
| | 3402 | } |
| | 3403 | } |
| | 3404 | } |
| | 3405 | else { |
| | 3406 | this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing); |
| | 3407 | } |
| | 3408 | } |
| | 3409 | |
| | 3410 | |
| | 3411 | function viewSelectedAttachment(event, noLocateOnMissing) |
| | 3412 | { |
| | 3413 | if (this.itemsView && this.itemsView.selection.count == 1) { |
| | 3414 | this.viewAttachment(this.getSelectedItems(true)[0], event, noLocateOnMissing); |
| | 3415 | } |
| | 3416 | } |
| | 3417 | |
| | 3418 | |
| | 3419 | this.showAttachmentInFilesystem = function (itemID, noLocateOnMissing) { |
| | 3420 | var attachment = Zotero.Items.get(itemID) |
| | 3421 | if (attachment.attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) { |
| | 3422 | var file = attachment.getFile(); |
| | 3423 | if (file){ |
| | 3424 | try { |
| | 3425 | file.reveal(); |
| | 3426 | } |
| | 3427 | catch (e) { |
| | 3428 | // On platforms that don't support nsILocalFile.reveal() (e.g. Linux), |
| | 3429 | // "double-click" the parent directory |
| | 3430 | try { |
| | 3431 | var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile); |
| | 3432 | parent.launch(); |
| | 3433 | } |
| | 3434 | // If launch also fails, try the OS handler |
| | 3435 | catch (e) { |
| | 3436 | var uri = Components.classes["@mozilla.org/network/io-service;1"]. |
| | 3437 | getService(Components.interfaces.nsIIOService). |
| | 3438 | newFileURI(parent); |
| | 3439 | var protocolService = |
| | 3440 | Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]. |
| | 3441 | getService(Components.interfaces.nsIExternalProtocolService); |
| | 3442 | protocolService.loadUrl(uri); |
| | 3443 | } |
| | 3444 | } |
| | 3445 | } |
| | 3446 | else { |
| | 3447 | this.showAttachmentNotFoundDialog(attachment.id, noLocateOnMissing) |
| | 3448 | } |
| | 3449 | } |
| | 3450 | } |
| | 3451 | |
| | 3452 | |
| | 3453 | /** |
| | 3454 | * Test if the user can edit the currently selected library/collection, |
| | 3455 | * and display an error if not |
| | 3456 | * |
| | 3457 | * @param {Integer} [row] |
| | 3458 | * |
| | 3459 | * @return {Boolean} TRUE if user can edit, FALSE if not |
| | 3460 | */ |
| | 3461 | this.canEdit = function (row) { |
| | 3462 | // Currently selected row |
| | 3463 | if (row === undefined) { |
| | 3464 | row = this.collectionsView.selection.currentIndex; |
| | 3465 | } |
| | 3466 | |
| | 3467 | var itemGroup = this.collectionsView._getItemAtRow(row); |
| | 3468 | return itemGroup.editable; |
| | 3469 | } |
| | 3470 | |
| | 3471 | |
| | 3472 | /** |
| | 3473 | * Test if the user can edit the currently selected library/collection, |
| | 3474 | * and display an error if not |
| | 3475 | * |
| | 3476 | * @param {Integer} [row] |
| | 3477 | * |
| | 3478 | * @return {Boolean} TRUE if user can edit, FALSE if not |
| | 3479 | */ |
| | 3480 | this.canEditFiles = function (row) { |
| | 3481 | // Currently selected row |
| | 3482 | if (row === undefined) { |
| | 3483 | row = this.collectionsView.selection.currentIndex; |
| | 3484 | } |
| | 3485 | |
| | 3486 | var itemGroup = this.collectionsView._getItemAtRow(row); |
| | 3487 | return itemGroup.filesEditable; |
| | 3488 | } |
| | 3489 | |
| | 3490 | |
| | 3491 | this.displayCannotEditLibraryMessage = function () { |
| | 3492 | var pr = Components.classes["@mozilla.org/network/default-prompt;1"] |
| | 3493 | .getService(Components.interfaces.nsIPrompt); |
| | 3494 | pr.alert("", "You cannot make changes to the currently selected library."); |
| | 3495 | } |
| | 3496 | |
| | 3497 | |
| | 3498 | this.displayCannotEditLibraryFilesMessage = function () { |
| | 3499 | var pr = Components.classes["@mozilla.org/network/default-prompt;1"] |
| | 3500 | .getService(Components.interfaces.nsIPrompt); |
| | 3501 | pr.alert("", "You cannot add files to the currently selected library."); |
| | 3502 | } |
| | 3503 | |
| | 3504 | |
| | 3505 | function showAttachmentNotFoundDialog(itemID, noLocate) { |
| | 3506 | var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. |
| | 3507 | createInstance(Components.interfaces.nsIPromptService); |
| | 3508 | |
| | 3509 | |
| | 3510 | // Don't show Locate button |
| | 3511 | if (noLocate) { |
| | 3512 | var index = ps.alert(null, |
| | 3513 | Zotero.getString('pane.item.attachments.fileNotFound.title'), |
| | 3514 | Zotero.getString('pane.item.attachments.fileNotFound.text') |
| | 3515 | ); |
| | 3516 | return; |
| | 3517 | } |
| | 3518 | |
| | 3519 | var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) |
| | 3520 | + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL); |
| | 3521 | var index = ps.confirmEx(null, |
| | 3522 | Zotero.getString('pane.item.attachments.fileNotFound.title'), |
| | 3523 | Zotero.getString('pane.item.attachments.fileNotFound.text'), |
| | 3524 | buttonFlags, Zotero.getString('general.locate'), null, |
| | 3525 | null, null, {}); |
| | 3526 | |
| | 3527 | if (index == 0) { |
| | 3528 | this.relinkAttachment(itemID); |
| | 3529 | } |
| | 3530 | } |
| | 3531 | |
| | 3532 | |
| | 3533 | this.createParentItemsFromSelected = function () { |
| | 3534 | if (!this.canEdit()) { |
| | 3535 | this.displayCannotEditLibraryMessage(); |
| | 3536 | return; |
| | 3537 | } |
| | 3538 | |
| | 3539 | |
| | 3540 | var items = this.getSelectedItems(); |
| | 3541 | for (var i=0; i<items.length; i++) { |
| | 3542 | var item = items[i]; |
| | 3543 | if (!item.isTopLevelItem() || item.isRegularItem()) { |
| | 3544 | throw('Item ' + itemID + ' is not a top-level attachment or note in ZoteroPane.createParentItemsFromSelected()'); |
| | 3545 | } |
| | 3546 | |
| | 3547 | Zotero.DB.beginTransaction(); |
| | 3548 | // TODO: remove once there are no top-level web attachments |
| | 3549 | if (item.isWebAttachment()) { |
| | 3550 | var parent = new Zotero.Item('webpage'); |
| | 3551 | } |
| | 3552 | else { |
| | 3553 | var parent = new Zotero.Item('document'); |
| | 3554 | } |
| | 3555 | parent.libraryID = item.libraryID; |
| | 3556 | parent.setField('title', item.getField('title')); |
| | 3557 | if (item.isWebAttachment()) { |
| | 3558 | parent.setField('accessDate', item.getField('accessDate')); |
| | 3559 | parent.setField('url', item.getField('url')); |
| | 3560 | } |
| | 3561 | var itemID = parent.save(); |
| | 3562 | item.setSource(itemID); |
| | 3563 | item.save(); |
| | 3564 | Zotero.DB.commitTransaction(); |
| | 3565 | } |
| | 3566 | } |
| | 3567 | |
| | 3568 | |
| | 3569 | this.renameSelectedAttachmentsFromParents = function () { |
| | 3570 | if (!this.canEdit()) { |
| | 3571 | this.displayCannotEditLibraryMessage(); |
| | 3572 | return; |
| | 3573 | } |
| | 3574 | |
| | 3575 | var items = this.getSelectedItems(); |
| | 3576 | |
| | 3577 | for (var i=0; i<items.length; i++) { |
| | 3578 | var item = items[i]; |
| | 3579 | |
| | 3580 | if (!item.isAttachment() || !item.getSource() || item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) { |
| | 3581 | throw('Item ' + itemID + ' is not a child file attachment in ZoteroPane.renameAttachmentFromParent()'); |
| | 3582 | } |
| | 3583 | |
| | 3584 | var file = item.getFile(); |
| | 3585 | if (!file) { |
| | 3586 | continue; |
| | 3587 | } |
| | 3588 | |
| | 3589 | var parentItemID = item.getSource(); |
| | 3590 | var newName = Zotero.Attachments.getFileBaseNameFromItem(parentItemID); |
| | 3591 | |
| | 3592 | var ext = file.leafName.match(/[^\.]+$/); |
| | 3593 | if (ext) { |
| | 3594 | newName = newName + '.' + ext; |
| | 3595 | } |
| | 3596 | |
| | 3597 | var renamed = item.renameAttachmentFile(newName); |
| | 3598 | if (renamed !== true) { |
| | 3599 | Zotero.debug("Could not rename file (" + renamed + ")"); |
| | 3600 | continue; |
| | 3601 | } |
| | 3602 | |
| | 3603 | item.setField('title', newName); |
| | 3604 | item.save(); |
| | 3605 | } |
| | 3606 | |
| | 3607 | return true; |
| | 3608 | } |
| | 3609 | |
| | 3610 | |
| | 3611 | function relinkAttachment(itemID) { |
| | 3612 | if (!this.canEdit()) { |
| | 3613 | this.displayCannotEditLibraryMessage(); |
| | 3614 | return; |
| | 3615 | } |
| | 3616 | |
| | 3617 | var item = Zotero.Items.get(itemID); |
| | 3618 | if (!item) { |
| | 3619 | throw('Item ' + itemID + ' not found in ZoteroPane.relinkAttachment()'); |
| | 3620 | } |
| | 3621 | |
| | 3622 | var nsIFilePicker = Components.interfaces.nsIFilePicker; |
| | 3623 | var fp = Components.classes["@mozilla.org/filepicker;1"] |
| | 3624 | .createInstance(nsIFilePicker); |
| | 3625 | fp.init(window, Zotero.getString('pane.item.attachments.select'), nsIFilePicker.modeOpen); |
| | 3626 | |
| | 3627 | |
| | 3628 | var file = item.getFile(false, true); |
| | 3629 | var dir = Zotero.File.getClosestDirectory(file); |
| | 3630 | if (dir) { |
| | 3631 | dir.QueryInterface(Components.interfaces.nsILocalFile); |
| | 3632 | fp.displayDirectory = dir; |
| | 3633 | } |
| | 3634 | |
| | 3635 | fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll); |
| | 3636 | |
| | 3637 | if (fp.show() == nsIFilePicker.returnOK) { |
| | 3638 | var file = fp.file; |
| | 3639 | file.QueryInterface(Components.interfaces.nsILocalFile); |
| | 3640 | item.relinkAttachmentFile(file); |
| | 3641 | } |
| | 3642 | } |
| | 3643 | |
| | 3644 | |
| | 3645 | function reportErrors() { |
| | 3646 | var errors = Zotero.getErrors(true); |
| | 3647 | var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] |
| | 3648 | .getService(Components.interfaces.nsIWindowWatcher); |
| | 3649 | var data = { |
| | 3650 | msg: Zotero.getString('errorReport.followingErrors', Zotero.appName), |
| | 3651 | e: errors.join('\n\n'), |
| | 3652 | askForSteps: true |
| | 3653 | }; |
| | 3654 | var io = { wrappedJSObject: { Zotero: Zotero, data: data } }; |
| | 3655 | var win = ww.openWindow(null, "chrome://zotero/content/errorReport.xul", |
| | 3656 | "zotero-error-report", "chrome,centerscreen,modal", io); |
| | 3657 | } |
| | 3658 | |
| | 3659 | |
| | 3660 | /* |
| | 3661 | * Display an error message saying that an error has occurred and Firefox |
| | 3662 | * needs to be restarted. |
| | 3663 | * |
| | 3664 | * If |popup| is TRUE, display in popup progress window; otherwise, display |
| | 3665 | * as items pane message |
| | 3666 | */ |
| | 3667 | function displayErrorMessage(popup) { |
| | 3668 | var reportErrorsStr = Zotero.getString('errorReport.reportErrors'); |
| | 3669 | var reportInstructions = |
| | 3670 | Zotero.getString('errorReport.reportInstructions', reportErrorsStr) |
| | 3671 | |
| | 3672 | // Display as popup progress window |
| | 3673 | if (popup) { |
| | 3674 | var pw = new Zotero.ProgressWindow(); |
| | 3675 | pw.changeHeadline(Zotero.getString('general.errorHasOccurred')); |
| | 3676 | var msg = Zotero.getString('general.restartFirefox') + ' ' |
| | 3677 | + reportInstructions; |
| | 3678 | pw.addDescription(msg); |
| | 3679 | pw.show(); |
| | 3680 | pw.startCloseTimer(8000); |
| | 3681 | } |
| | 3682 | // Display as items pane message |
| | 3683 | else { |
| | 3684 | var msg = Zotero.getString('general.errorHasOccurred') + ' ' |
| | 3685 | + Zotero.getString('general.restartFirefox') + '\n\n' |
| | 3686 | + reportInstructions; |
| | 3687 | self.setItemsPaneMessage(msg, true); |
| | 3688 | } |
| | 3689 | Zotero.debug(msg, 1); |
| | 3690 | } |
| | 3691 | } |
| | 3692 | |
| | 3693 | window.addEventListener("load", function(e) { ZoteroPane.onLoad(e); }, false); |
| | 3694 | window.addEventListener("unload", function(e) { ZoteroPane.onUnload(e); }, false); |