standalone.js (15008B)
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 Affero 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 Affero General Public License for more details. 19 20 You should have received a copy of the GNU Affero General Public License 21 along with Zotero. If not, see <http://www.gnu.org/licenses/>. 22 23 ***** END LICENSE BLOCK ***** 24 */ 25 26 Components.utils.import("resource://gre/modules/Services.jsm"); 27 28 /** 29 * This object contains the various functions for the interface 30 */ 31 const ZoteroStandalone = new function() { 32 /** 33 * Run when standalone window first opens 34 */ 35 this.onLoad = function() { 36 // Fix window without menubar/titlebar when Zotero is closed in full-screen mode in OS X 10.11+ 37 if (Zotero.isMac && window.document.documentElement.getAttribute('sizemode') == 'fullscreen') { 38 window.document.documentElement.setAttribute('sizemode', 'normal'); 39 } 40 41 Zotero.Promise.try(function () { 42 if(!Zotero) { 43 throw true; 44 } 45 if(Zotero.initializationPromise.isPending()) { 46 Zotero.showZoteroPaneProgressMeter(); 47 } 48 return Zotero.initializationPromise; 49 }) 50 .then(function () { 51 if (Zotero.Prefs.get('devtools.errorconsole.enabled', true)) { 52 document.getElementById('menu_errorConsole').hidden = false; 53 } 54 55 document.getElementById('key_copyCitation') 56 .setAttribute('key', Zotero.Keys.getKeyForCommand('copySelectedItemCitationsToClipboard')); 57 document.getElementById('key_copyBibliography') 58 .setAttribute('key', Zotero.Keys.getKeyForCommand('copySelectedItemsToClipboard')); 59 60 ZoteroStandalone.DebugOutput.init(); 61 62 Zotero.hideZoteroPaneOverlays(); 63 ZoteroPane.init(); 64 ZoteroPane.makeVisible(); 65 66 // Don't ask before handing http and https URIs 67 var eps = Components.classes['@mozilla.org/uriloader/external-protocol-service;1'] 68 .getService(Components.interfaces.nsIExternalProtocolService); 69 var hs = Components.classes["@mozilla.org/uriloader/handler-service;1"] 70 .getService(Components.interfaces.nsIHandlerService); 71 for (let scheme of ["http", "https"]) { 72 var handlerInfo = eps.getProtocolHandlerInfo(scheme); 73 handlerInfo.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault; 74 handlerInfo.alwaysAskBeforeHandling = false; 75 hs.store(handlerInfo); 76 } 77 78 // Add add-on listeners (not yet hooked up) 79 Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false); 80 Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false); 81 Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false); 82 Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false); 83 Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false); 84 }) 85 .catch(function (e) { 86 try { Zotero.debug(e, 1); } catch (e) {} 87 Components.utils.reportError(e); 88 ZoteroPane.displayStartupError(); 89 window.close(); 90 return; 91 }); 92 } 93 94 /** 95 * Builds new item menu 96 */ 97 this.buildNewItemMenu = function() { 98 var addMenu = document.getElementById('menu_NewItemPopup'); 99 100 // Remove all nodes so we can regenerate 101 while(addMenu.hasChildNodes()) addMenu.removeChild(addMenu.firstChild); 102 103 var typeSets = [Zotero.ItemTypes.getPrimaryTypes(), Zotero.ItemTypes.getSecondaryTypes()]; 104 for(var j=0; j<typeSets.length; j++) { 105 var t = typeSets[j]; 106 107 // Sort by localized name 108 var itemTypes = []; 109 for (var i=0; i<t.length; i++) { 110 itemTypes.push({ 111 id: t[i].id, 112 name: t[i].name, 113 localized: Zotero.ItemTypes.getLocalizedString(t[i].id) 114 }); 115 } 116 var collation = Zotero.getLocaleCollation(); 117 itemTypes.sort(function(a, b) { 118 return collation.compareString(1, a.localized, b.localized); 119 }); 120 121 for (var i = 0; i<itemTypes.length; i++) { 122 var menuitem = document.createElement("menuitem"); 123 menuitem.setAttribute("label", itemTypes[i].localized); 124 menuitem.setAttribute("tooltiptext", ""); 125 let type = itemTypes[i].id; 126 menuitem.addEventListener("command", function() { 127 ZoteroPane_Local.newItem(type, null, null, true); 128 }, false); 129 menuitem.className = "zotero-tb-add"; 130 addMenu.appendChild(menuitem); 131 } 132 133 // add separator between sets 134 if(j !== typeSets.length-1) { 135 addMenu.appendChild(document.createElement("menuseparator")); 136 } 137 } 138 } 139 140 141 this.updateQuickCopyOptions = function () { 142 var selected = false; 143 try { 144 selected = Zotero.getActiveZoteroPane() 145 .getSelectedItems() 146 .filter(item => item.isRegularItem()) 147 .length; 148 } 149 catch (e) {} 150 151 var format = Zotero.QuickCopy.getFormatFromURL(Zotero.QuickCopy.lastActiveURL); 152 format = Zotero.QuickCopy.unserializeSetting(format); 153 154 var copyCitation = document.getElementById('menu_copyCitation'); 155 var copyBibliography = document.getElementById('menu_copyBibliography'); 156 var copyExport = document.getElementById('menu_copyExport'); 157 158 copyCitation.hidden = !selected || format.mode != 'bibliography'; 159 copyBibliography.hidden = !selected || format.mode != 'bibliography'; 160 copyExport.hidden = !selected || format.mode != 'export'; 161 if (format.mode == 'export') { 162 try { 163 let obj = Zotero.Translators.get(format.id); 164 copyExport.label = Zotero.getString('quickCopy.copyAs', obj.label); 165 } 166 catch (e) { 167 if (!(e instanceof Zotero.Exception.UnloadedDataException && e.dataType == 'translators')) { 168 Zotero.logError(e); 169 } 170 copyExport.hidden = true; 171 } 172 } 173 }; 174 175 176 this.updateAddonsPane = function (doc) { 177 // Hide unsigned add-on verification warnings 178 // 179 // This only works for the initial load of the window. If the user switches to Appearance 180 // or Plugins and then back to Extensions, the warnings will appear again. A better way to 181 // disable this might be discoverable by studying 182 // https://dxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/content/extensions.js 183 var addonList = doc.getElementById('addon-list'); 184 setTimeout(function () { 185 for (let i = 0; i < addonList.itemCount; i++) { 186 let richListItem = addonList.getItemAtIndex(i); 187 let container = doc.getAnonymousElementByAttribute( 188 richListItem, 'anonid', 'warning-container' 189 ); 190 if (container) { 191 let link = doc.getAnonymousElementByAttribute( 192 richListItem, 'anonid', 'warning-link' 193 ); 194 if (link && link.href.indexOf('unsigned-addons') != -1) { 195 richListItem.removeAttribute('notification'); 196 container.hidden = true; 197 } 198 } 199 } 200 }); 201 } 202 203 /** 204 * Handles help menu requests 205 */ 206 this.openHelp = function(type) { 207 Components.utils.import("resource://zotero/config.js"); 208 209 switch (type) { 210 case "troubleshooting": 211 ZoteroPane.loadURI(ZOTERO_CONFIG.TROUBLESHOOTING_URL); 212 break; 213 214 case "feedback": 215 ZoteroPane.loadURI(ZOTERO_CONFIG.FEEDBACK_URL); 216 break; 217 218 case "connectors": 219 ZoteroPane.loadURI(ZOTERO_CONFIG.CONNECTORS_URL); 220 break; 221 222 default: 223 ZoteroPane.loadURI(ZOTERO_CONFIG.SUPPORT_URL); 224 } 225 } 226 227 /** 228 * Checks for updates 229 */ 230 this.checkForUpdates = function() { 231 window.open('chrome://mozapps/content/update/updates.xul', 'updateChecker', 'chrome,centerscreen'); 232 } 233 234 /** 235 * Called before standalone window is closed 236 */ 237 this.onUnload = function() { 238 ZoteroPane.destroy(); 239 } 240 } 241 242 243 ZoteroStandalone.DebugOutput = { 244 _timer: null, 245 246 init: function () { 247 var storing = Zotero.Debug.storing; 248 this._showMenu(); 249 this.update(); 250 }, 251 252 253 toggleStore: function () { 254 Zotero.Debug.setStore(!Zotero.Debug.storing); 255 }, 256 257 258 update: function () { 259 var enabled = Zotero.Debug.storing; 260 var lines = Zotero.Debug.count(); 261 var empty = lines == 0; 262 263 // Show "Submit" when enabled, but leave disabled until there's output 264 var menuitem = document.getElementById('debug-output-submit'); 265 menuitem.hidden = !enabled && empty; 266 menuitem.disabled = empty; 267 268 // Toggle between "Enable" and "Disable" 269 menuitem = document.getElementById('debug-output-enable-disable'); 270 menuitem.label = Zotero.getString('general.' + (enabled ? 'disable' : 'enable')); 271 272 // Update line count 273 var str = Zotero.getString('zotero.debugOutputLogging.linesLogged', lines, lines); 274 document.getElementById('debug-output-status').label = str; 275 276 // Enable "Clear" when there's output 277 document.getElementById('debug-output-clear').disabled = empty; 278 }, 279 280 281 submit: function () { 282 // 'Zotero' isn't defined yet when this function is created, so do it inline 283 return Zotero.Promise.coroutine(function* () { 284 Zotero.debug("Submitting debug output"); 285 286 Components.utils.import("resource://zotero/config.js"); 287 288 var url = ZOTERO_CONFIG.REPOSITORY_URL + "report?debug=1"; 289 var output = yield Zotero.Debug.get( 290 Zotero.Prefs.get('debug.store.submitSize'), 291 Zotero.Prefs.get('debug.store.submitLineLength') 292 ); 293 Zotero.Debug.setStore(false); 294 295 var ps = Services.prompt; 296 try { 297 var xmlhttp = yield Zotero.HTTP.request( 298 "POST", 299 url, 300 { 301 compressBody: true, 302 body: output, 303 logBodyLength: 30, 304 timeout: 15000, 305 requestObserver: function (req) { 306 // Don't fail during tests, with fake XHR 307 if (!req.channel) { 308 return; 309 } 310 req.channel.notificationCallbacks = { 311 onProgress: function (request, context, progress, progressMax) {}, 312 313 // nsIInterfaceRequestor 314 getInterface: function (iid) { 315 try { 316 return this.QueryInterface(iid); 317 } 318 catch (e) { 319 throw Components.results.NS_NOINTERFACE; 320 } 321 }, 322 323 QueryInterface: function(iid) { 324 if (iid.equals(Components.interfaces.nsISupports) || 325 iid.equals(Components.interfaces.nsIInterfaceRequestor) || 326 iid.equals(Components.interfaces.nsIProgressEventSink)) { 327 return this; 328 } 329 throw Components.results.NS_NOINTERFACE; 330 }, 331 332 } 333 } 334 } 335 ); 336 } 337 catch (e) { 338 Zotero.logError(e); 339 let title = Zotero.getString('general.error'); 340 let msg; 341 if (e instanceof Zotero.HTTP.UnexpectedStatusException) { 342 msg = Zotero.getString('general.invalidResponseServer'); 343 } 344 else if (e instanceof Zotero.HTTP.BrowserOfflineException) { 345 msg = Zotero.getString('general.browserIsOffline', Zotero.appName); 346 } 347 else { 348 msg = Zotero.getString('zotero.debugOutputLogging.dialog.error'); 349 } 350 ps.alert(null, title, msg); 351 return false; 352 } 353 354 Zotero.debug(xmlhttp.responseText); 355 356 var reported = xmlhttp.responseXML.getElementsByTagName('reported'); 357 if (reported.length != 1) { 358 ps.alert( 359 null, 360 Zotero.getString('general.error'), 361 Zotero.getString('general.serverError') 362 ); 363 return false; 364 } 365 366 var reportID = reported[0].getAttribute('reportID'); 367 368 var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING 369 + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL; 370 var index = ps.confirmEx( 371 null, 372 Zotero.getString('zotero.debugOutputLogging.dialog.title'), 373 Zotero.getString('zotero.debugOutputLogging.dialog.sent', [ZOTERO_CONFIG.DOMAIN_NAME, reportID]), 374 buttonFlags, 375 Zotero.getString('general.copyToClipboard'), 376 null, null, null, {} 377 ); 378 if (index == 0) { 379 const helper = Components.classes["@mozilla.org/widget/clipboardhelper;1"] 380 .getService(Components.interfaces.nsIClipboardHelper); 381 helper.copyString("D" + reportID); 382 } 383 384 Zotero.Debug.clear(); 385 return true; 386 }.bind(this))(); 387 }, 388 389 390 view: function () { 391 Zotero.openInViewer("chrome://zotero/content/debugViewer.html", function (doc) { 392 var submitted = false; 393 doc.querySelector('#submit-button').addEventListener('click', function (event) { 394 submitted = true; 395 }); 396 doc.querySelector('#clear-button').addEventListener('click', function (event) { 397 Zotero.Debug.clear(); 398 }); 399 // If output has been submitted, disable logging when window is closed 400 doc.defaultView.addEventListener('unload', function (event) { 401 if (submitted) { 402 Zotero.Debug.setStore(false); 403 Zotero.Debug.clear(); 404 } 405 }); 406 }); 407 }, 408 409 410 clear: function () { 411 Zotero.Debug.clear(); 412 }, 413 414 415 restartEnabled: function () { 416 var ps = Services.prompt; 417 var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING 418 + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL 419 + ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING; 420 var index = ps.confirmEx( 421 null, 422 Zotero.getString('zotero.debugOutputLogging'), 423 Zotero.getString('zotero.debugOutputLogging.enabledAfterRestart', [Zotero.clientName]), 424 buttonFlags, 425 Zotero.getString('general.restartNow'), 426 null, Zotero.getString('general.restartLater'), null, {} 427 ); 428 if (index != 1) { 429 Zotero.Prefs.set('debug.store', true); 430 } 431 if (index == 0) { 432 Zotero.Utilities.Internal.quit(true); 433 } 434 }, 435 436 437 _showMenu: function () { 438 document.getElementById('debug-output-menu').hidden = false; 439 } 440 }; 441 442 443 /** Taken from browser.js **/ 444 function toJavaScriptConsole() { 445 toOpenWindowByType("global:console", "chrome://global/content/console.xul"); 446 } 447 448 function toOpenWindowByType(inType, uri, features) 449 { 450 var topWindow = Services.wm.getMostRecentWindow(inType); 451 452 if (topWindow) { 453 topWindow.focus(); 454 } else if(features) { 455 window.open(uri, "_blank", features); 456 } else { 457 window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar"); 458 } 459 } 460 461 const gXPInstallObserver = { 462 observe: function (aSubject, aTopic, aData) { 463 var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo); 464 var win = installInfo.originatingWindow; 465 switch (aTopic) { 466 case "addon-install-disabled": 467 case "addon-install-blocked": 468 case "addon-install-failed": 469 var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] 470 .getService(Components.interfaces.nsIPromptService); 471 promptService.alert(win, Zotero.getString("standalone.addonInstallationFailed.title"), 472 Zotero.getString("standalone.addonInstallationFailed.body", installInfo.installs[0].name)); 473 break; 474 /*case "addon-install-started": 475 case "addon-install-complete":*/ 476 } 477 } 478 }; 479 480 // Used by update prompt 481 function openUILinkIn(url) { 482 ZoteroPane.loadURI(url); 483 } 484 485 window.addEventListener("load", function(e) { ZoteroStandalone.onLoad(e); }, false); 486 window.addEventListener("unload", function(e) { ZoteroStandalone.onUnload(e); }, false);