diff --git a/chrome/content/zotero-platform/mac/preferences.css b/chrome/content/zotero-platform/mac/preferences.css index 41f7eaed3a..c4b9f6faee 100644 --- a/chrome/content/zotero-platform/mac/preferences.css +++ b/chrome/content/zotero-platform/mac/preferences.css @@ -2,56 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -prefwindow, -prefwindow:root /* override :root from above */ { - -moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#prefwindow"); - -moz-box-orient: vertical; -} - -prefpane { - -moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#prefpane"); - -moz-box-orient: vertical; -} - -prefwindow > .paneDeckContainer { - overflow: hidden; -} - -prefpane > .content-box { - overflow: hidden; -} - -prefwindow[type="child"] > .paneDeckContainer { - overflow: -moz-hidden-unscrollable; -} - -prefwindow[type="child"] > prefpane > .content-box { - -moz-box-flex: 1; - overflow: -moz-hidden-unscrollable; -} - -preferences { - -moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#preferences"); - visibility: collapse; -} - -preference { - -moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#preference"); - visibility: collapse; -} - -radio[pane] { - -moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#panebutton") !important; - -moz-box-orient: vertical; - -moz-box-align: center; -} - -prefwindow[chromehidden~="toolbar"] .chromeclass-toolbar { - display: none; -} - - -prefwindow { +window[windowtype="zotero:pref"] { padding: 0; font: -moz-dialog !important; @@ -59,79 +10,11 @@ prefwindow { color: -moz-DialogText; } -prefpane { - padding: 12px 12px 0 12px; -} - -prefwindow[type="child"] > prefpane { - padding: 0; -} - -.prefWindow-dlgbuttons { - margin: 0 12px 12px; - padding-top: 0 !important; -} - -.paneSelector { - font: message-box; - padding: 1px 4px; - -moz-appearance: toolbar; - margin: 0; -} - -radio[pane] { - border: solid transparent; - border-width: 0 2px; - padding: 5px 4px 3px; - margin: 0; - -moz-appearance: none; - text-shadow: rgba(255, 255, 255, 0.4) 0 1px; -} - -radio[pane]:active:hover { - text-shadow: none; -} - -radio[pane]:active:hover > .paneButtonIcon { - filter: brightness(0.55); -} - -radio[pane][selected="true"] { - -moz-border-image: url("chrome://zotero-platform/content/panebutton-active.png") 0 2 fill repeat stretch; -} - -radio[pane][selected="true"]:-moz-window-inactive { - -moz-border-image: url("chrome://zotero-platform/content/panebutton-inactive.png") 0 2 fill repeat stretch; -} - -.paneButtonLabel { - margin: 0 !important; -} - -groupbox { - padding: 5px 1px 1px; - padding-inline-start: 0; - margin: 6px; -} - -groupbox > label > h2 { +groupbox > label > h2, groupbox > * > label > h2 { border-bottom: #b5b5b5 1px solid; } -groupbox > .groupbox-body { - padding: 8px 8px 3px; - margin: 0; -} - -groupbox > label > h2, caption { - padding-inline-start: 4px; - padding-bottom: 1px; +groupbox > label > h2, groupbox > * > label > h2, caption { + padding-bottom: 0.2em; font: caption; } - -/* !important is needed to override label in global.css */ -.caption-text { - margin-top: 0 !important; - margin-bottom: 0 !important; - margin-inline-start: 1px !important; -} \ No newline at end of file diff --git a/chrome/content/zotero-platform/unix/preferences.css b/chrome/content/zotero-platform/unix/preferences.css index 6db578c088..b6876a830f 100644 --- a/chrome/content/zotero-platform/unix/preferences.css +++ b/chrome/content/zotero-platform/unix/preferences.css @@ -2,56 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -prefwindow, -prefwindow:root /* override :root from above */ { - -moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#prefwindow"); - -moz-box-orient: vertical; -} - -prefpane { - -moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#prefpane"); - -moz-box-orient: vertical; -} - -prefwindow > .paneDeckContainer { - overflow: hidden; -} - -prefpane > .content-box { - overflow: hidden; -} - -prefwindow[type="child"] > .paneDeckContainer { - overflow: -moz-hidden-unscrollable; -} - -prefwindow[type="child"] > prefpane > .content-box { - -moz-box-flex: 1; - overflow: -moz-hidden-unscrollable; -} - -preferences { - -moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#preferences"); - visibility: collapse; -} - -preference { - -moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#preference"); - visibility: collapse; -} - -radio[pane] { - -moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#panebutton") !important; - -moz-box-orient: vertical; - -moz-box-align: center; -} - -prefwindow[chromehidden~="toolbar"] .chromeclass-toolbar { - display: none; -} - - -prefwindow { + window[windowtype="zotero:pref"] { padding: 0px; -moz-appearance: window; @@ -60,52 +11,6 @@ prefwindow { font: message-box; } -prefpane { - padding: 8px; -} - -prefwindow[type="child"] { - padding: 8px; -} - -prefwindow[type="child"] > prefpane { - padding: 0px; -} - -.prefWindow-dlgbuttons { - padding-bottom: 8px; - padding-inline-start: 8px; - padding-inline-end: 8px; -} - -prefwindow[type="child"] .prefWindow-dlgbuttons { - padding: 0px; -} - -radio[pane] { - -moz-appearance: none; - min-width: 4.5em; - margin: 0; - padding: 3px; - color: -moz-FieldText; -} - -.paneSelector { - -moz-appearance: listbox; - margin: 8px 8px 0 8px; - padding: 0; -} - -.paneButtonIcon { - width: 32px; - height: 32px; -} - -radio[pane][selected="true"] { - background-color: Highlight; - color: HighlightText; -} - #sync-reset-library-menu { height: initial !important; } diff --git a/chrome/content/zotero-platform/win/preferences.css b/chrome/content/zotero-platform/win/preferences.css index 1b3990d879..1832672d94 100644 --- a/chrome/content/zotero-platform/win/preferences.css +++ b/chrome/content/zotero-platform/win/preferences.css @@ -2,120 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -prefwindow, -prefwindow:root /* override :root from above */ { - -moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#prefwindow"); - -moz-box-orient: vertical; -} - -prefpane { - -moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#prefpane"); - -moz-box-orient: vertical; -} - -prefwindow > .paneDeckContainer { - overflow: hidden; -} - -prefpane > .content-box { - overflow: hidden; -} - -prefwindow[type="child"] > .paneDeckContainer { - overflow: -moz-hidden-unscrollable; -} - -prefwindow[type="child"] > prefpane > .content-box { - -moz-box-flex: 1; - overflow: -moz-hidden-unscrollable; -} - -preferences { - -moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#preferences"); - visibility: collapse; -} - -preference { - -moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#preference"); - visibility: collapse; -} - -radio[pane] { - -moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#panebutton") !important; - -moz-box-orient: vertical; - -moz-box-align: center; -} - -prefwindow[chromehidden~="toolbar"] .chromeclass-toolbar { - display: none; -} - - -prefwindow { - padding: 0px; + window[windowtype="zotero:pref"] { + padding: 0; -moz-appearance: window; background-color: -moz-Dialog; color: -moz-DialogText; font: message-box; } - -prefpane { - padding-top: 8px; - padding-bottom: 10px; - padding-inline-start: 8px; - padding-inline-end: 10px; -} - -prefwindow[type="child"] { - padding-top: 8px; - padding-bottom: 10px; - padding-inline-start: 8px; - padding-inline-end: 10px; -} - -prefwindow[type="child"] > prefpane { - padding: 0px; -} - -.prefWindow-dlgbuttons { - padding-bottom: 10px; - padding-inline-start: 8px; - padding-inline-end: 10px; -} - -prefwindow[type="child"] .prefWindow-dlgbuttons { - padding: 0px; -} - -radio[pane] { - -moz-appearance: none; - margin: 0px 1px 0px 1px; - padding: 1px 3px 1px 3px; - min-width: 4.5em; -} - -.paneSelector { - border-bottom: 2px groove ThreeDFace; - margin: 0px; - padding-inline-start: 10px; - background-color: -moz-Field; - color: -moz-FieldText; -} - -.paneButtonIcon { - width: 32px; - height: 32px; -} - -radio[pane]:hover { - background-color: #E0E8F6; - color: black; - -moz-appearance: none; -} - -radio[pane][selected="true"] { - background-color: #C1D2EE; - color: black; - -moz-appearance: none; -} diff --git a/chrome/content/zotero/bindings/preferences-mac.xml b/chrome/content/zotero/bindings/preferences-mac.xml deleted file mode 100644 index 7fc6fe939d..0000000000 --- a/chrome/content/zotero/bindings/preferences-mac.xml +++ /dev/null @@ -1,1358 +0,0 @@ - - - - %preferencesDTD; - - %globalKeysDTD; -]> - - - - - - - elements is constructed. Its purpose is to propagate - // the values to the associated form elements - - var elements = this.getElementsByTagName("preference"); - for (let element of elements) { - if (!element._constructed) { - return; - } - } - for (let element of elements) { - element.updateElements(); - } - ]]> - - - - - - - - - - - - - - - - - - - - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefService); - - - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - - - this.service.getDefaultBranch(""); - - - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - - - - - - - - - - - - - - - - - - - - - this.preferences.rootBranchInternal - .removeObserver(this.name, this.preferences); - - false - - - if (this.getAttribute("instantApply") == "false") - return false; - return this.getAttribute("instantApply") == "true" || this.preferences.instantApply; - - - - - - - if (val == this.name) - return val; - - this.preferences.rootBranchInternal - .removeObserver(this.name, this.preferences); - this.setAttribute('name', val); - this.preferences.rootBranchInternal - .addObserver(val, this.preferences, false); - - return val; - - - - - - - null - - - - - - - - - - - return this.preferences.rootBranch.prefIsLocked(this.name); - - - - - - return this.getAttribute("disabled") == "true"; - - - - - - - - - return parseInt(this.getAttribute("tabindex")); - - - - - - - - - - - - - - - // defer reset until preference update - this.value = undefined; - - - - false - - - - - - - - - return this._useDefault ? this.preferences.defaultBranch : this.preferences.rootBranch; - - - - false - - - - with id='" + this.id + "' and name='" + - this.name + "' has unknown type '" + this.type + "'."; - consoleService.logStringMessage(msg); - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - this.updateElements(); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - false - - - - - - - - - - this.setAttribute("lastSelected", val); - document.persist(this.id, "lastSelected"); - return val; - - - - - if (!this._currentPane) - this._currentPane = this.preferencePanes[0]; - - return this._currentPane; - - - null - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - 1) - aPaneElement.removeAttribute("flex"); - // Calling sizeToContent after the first prefpane is loaded - // will size the windows contents so style information is - // available to calculate correct sizing. - if (!this._initialized && prefpanes.length > 1) { - if (this._shouldAnimate) - this.style.minHeight = 0; - window.sizeToContent(); - } - - var oldPane = this.lastSelected ? document.getElementById(this.lastSelected) : this.preferencePanes[0]; - oldPane.selected = !(aPaneElement.selected = true); - this.lastSelected = aPaneElement.id; - this.currentPane = aPaneElement; - this._initialized = true; - - // Only animate if we've switched between prefpanes - if (this._shouldAnimate && oldPane.id != aPaneElement.id) { - aPaneElement.style.opacity = 0.0; - this.animate(oldPane, aPaneElement); - } - else if (!this._shouldAnimate && prefpanes.length > 1) { - var targetHeight = parseInt(window.getComputedStyle(this._paneDeckContainer, "").height); - var verticalPadding = parseInt(window.getComputedStyle(aPaneElement, "").paddingTop); - verticalPadding += parseInt(window.getComputedStyle(aPaneElement, "").paddingBottom); - if (aPaneElement.contentHeight > targetHeight - verticalPadding) { - // To workaround the bottom border of a groupbox from being - // cutoff an hbox with a class of bottomBox may enclose it. - // This needs to include its padding to resize properly. - // See bug 394433 - var bottomPadding = 0; - var bottomBox = aPaneElement.getElementsByAttribute("class", "bottomBox")[0]; - if (bottomBox) - bottomPadding = parseInt(window.getComputedStyle(bottomBox, "").paddingBottom); - window.innerHeight += bottomPadding + verticalPadding + aPaneElement.contentHeight - targetHeight; - } - - // XXX rstrong - extend the contents of the prefpane to - // prevent elements from being cutoff (see bug 349098). - if (aPaneElement.contentHeight + verticalPadding < targetHeight) - aPaneElement._content.style.height = targetHeight - verticalPadding + "px"; - } - } - break; - } - } - ]]> - - - - - - - - - - - - - - oldHeight ? 1 : -1; - var sizeDelta = Math.abs(oldHeight - aNewPane.contentHeight); - this._animateRemainder = sizeDelta % this._animateIncrement; - - this._setUpAnimationTimer(oldHeight); - ]]> - - - - - - 0 && this._currentHeight >= lastSelectedPane.contentHeight) || - (this._multiplier < 0 && this._currentHeight <= lastSelectedPane.contentHeight)) - return 0; - - if ((this._multiplier > 0 && newHeight > lastSelectedPane.contentHeight) || - (this._multiplier < 0 && newHeight < lastSelectedPane.contentHeight)) - increment = this._animateRemainder * this._multiplier; - return increment; - ]]> - - - - - - - - - - - - - - - - - - - - - - - - null - null - 15 - 40 - 5 - 0.40 - 0 - 0 - 0 - - - - - - - - - - - - - - return openDialog(aURL, "", "modal,centerscreen,resizable=no" + (aFeatures != "" ? ("," + aFeatures) : ""), aParams); - - - - - - - - - - - - - - - - - - - if (event.originalTarget.hasAttribute("pane")) { - var pane = document.getElementById(event.originalTarget.getAttribute("pane")); - this.showPane(pane); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - - - return document.getElementById(aElement.getAttribute("preference")); - - - - - - - - - - - - - - - - - - - - - new Set(); - - - - - - - - - - - - - - - - var targetHeight = parseInt(window.getComputedStyle(this._content, "").height); - targetHeight += parseInt(window.getComputedStyle(this._content, "").marginTop); - targetHeight += parseInt(window.getComputedStyle(this._content, "").marginBottom); - return targetHeight; - - - - document.getAnonymousElementByAttribute(this, "class", "content-box"); - - - - - // This "command" event handler tracks changes made to preferences by - // the user in this window. - if (event.sourceEvent) - event = event.sourceEvent; - this.userChangedValue(event.target); - - - // This "select" event handler tracks changes made to colorpicker - // preferences by the user in this window. - if (event.target.localName == "colorpicker") - this.userChangedValue(event.target); - - - // This "change" event handler tracks changes made to preferences by - // the user in this window. - this.userChangedValue(event.target); - - - // This "input" event handler tracks changes made to preferences by - // the user in this window. - this.userChangedValue(event.target); - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/zotero/bindings/preferences-unix.xml b/chrome/content/zotero/bindings/preferences-unix.xml deleted file mode 100644 index 28c6bdf081..0000000000 --- a/chrome/content/zotero/bindings/preferences-unix.xml +++ /dev/null @@ -1,1358 +0,0 @@ - - - - %preferencesDTD; - - %globalKeysDTD; -]> - - - - - - - elements is constructed. Its purpose is to propagate - // the values to the associated form elements - - var elements = this.getElementsByTagName("preference"); - for (let element of elements) { - if (!element._constructed) { - return; - } - } - for (let element of elements) { - element.updateElements(); - } - ]]> - - - - - - - - - - - - - - - - - - - - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefService); - - - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - - - this.service.getDefaultBranch(""); - - - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - - - - - - - - - - - - - - - - - - - - - this.preferences.rootBranchInternal - .removeObserver(this.name, this.preferences); - - false - - - if (this.getAttribute("instantApply") == "false") - return false; - return this.getAttribute("instantApply") == "true" || this.preferences.instantApply; - - - - - - - if (val == this.name) - return val; - - this.preferences.rootBranchInternal - .removeObserver(this.name, this.preferences); - this.setAttribute('name', val); - this.preferences.rootBranchInternal - .addObserver(val, this.preferences, false); - - return val; - - - - - - - null - - - - - - - - - - - return this.preferences.rootBranch.prefIsLocked(this.name); - - - - - - return this.getAttribute("disabled") == "true"; - - - - - - - - - return parseInt(this.getAttribute("tabindex")); - - - - - - - - - - - - - - - // defer reset until preference update - this.value = undefined; - - - - false - - - - - - - - - return this._useDefault ? this.preferences.defaultBranch : this.preferences.rootBranch; - - - - false - - - - with id='" + this.id + "' and name='" + - this.name + "' has unknown type '" + this.type + "'."; - consoleService.logStringMessage(msg); - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - this.updateElements(); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - false - - - - - - - - - - this.setAttribute("lastSelected", val); - document.persist(this.id, "lastSelected"); - return val; - - - - - if (!this._currentPane) - this._currentPane = this.preferencePanes[0]; - - return this._currentPane; - - - null - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - 1) - aPaneElement.removeAttribute("flex"); - // Calling sizeToContent after the first prefpane is loaded - // will size the windows contents so style information is - // available to calculate correct sizing. - if (!this._initialized && prefpanes.length > 1) { - if (this._shouldAnimate) - this.style.minHeight = 0; - window.sizeToContent(); - } - - var oldPane = this.lastSelected ? document.getElementById(this.lastSelected) : this.preferencePanes[0]; - oldPane.selected = !(aPaneElement.selected = true); - this.lastSelected = aPaneElement.id; - this.currentPane = aPaneElement; - this._initialized = true; - - // Only animate if we've switched between prefpanes - if (this._shouldAnimate && oldPane.id != aPaneElement.id) { - aPaneElement.style.opacity = 0.0; - this.animate(oldPane, aPaneElement); - } - else if (!this._shouldAnimate && prefpanes.length > 1) { - var targetHeight = parseInt(window.getComputedStyle(this._paneDeckContainer, "").height); - var verticalPadding = parseInt(window.getComputedStyle(aPaneElement, "").paddingTop); - verticalPadding += parseInt(window.getComputedStyle(aPaneElement, "").paddingBottom); - if (aPaneElement.contentHeight > targetHeight - verticalPadding) { - // To workaround the bottom border of a groupbox from being - // cutoff an hbox with a class of bottomBox may enclose it. - // This needs to include its padding to resize properly. - // See bug 394433 - var bottomPadding = 0; - var bottomBox = aPaneElement.getElementsByAttribute("class", "bottomBox")[0]; - if (bottomBox) - bottomPadding = parseInt(window.getComputedStyle(bottomBox, "").paddingBottom); - window.innerHeight += bottomPadding + verticalPadding + aPaneElement.contentHeight - targetHeight; - } - - // XXX rstrong - extend the contents of the prefpane to - // prevent elements from being cutoff (see bug 349098). - if (aPaneElement.contentHeight + verticalPadding < targetHeight) - aPaneElement._content.style.height = targetHeight - verticalPadding + "px"; - } - } - break; - } - } - ]]> - - - - - - - - - - - - - - oldHeight ? 1 : -1; - var sizeDelta = Math.abs(oldHeight - aNewPane.contentHeight); - this._animateRemainder = sizeDelta % this._animateIncrement; - - this._setUpAnimationTimer(oldHeight); - ]]> - - - - - - 0 && this._currentHeight >= lastSelectedPane.contentHeight) || - (this._multiplier < 0 && this._currentHeight <= lastSelectedPane.contentHeight)) - return 0; - - if ((this._multiplier > 0 && newHeight > lastSelectedPane.contentHeight) || - (this._multiplier < 0 && newHeight < lastSelectedPane.contentHeight)) - increment = this._animateRemainder * this._multiplier; - return increment; - ]]> - - - - - - - - - - - - - - - - - - - - - - - - null - null - 15 - 40 - 5 - 0.40 - 0 - 0 - 0 - - - - - - - - - - - - - - return openDialog(aURL, "", "modal,centerscreen,resizable=no" + (aFeatures != "" ? ("," + aFeatures) : ""), aParams); - - - - - - - - - - - - - - - - - - - if (event.originalTarget.hasAttribute("pane")) { - var pane = document.getElementById(event.originalTarget.getAttribute("pane")); - this.showPane(pane); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - - - return document.getElementById(aElement.getAttribute("preference")); - - - - - - - - - - - - - - - - - - - - - new Set(); - - - - - - - - - - - - - - - - var targetHeight = parseInt(window.getComputedStyle(this._content, "").height); - targetHeight += parseInt(window.getComputedStyle(this._content, "").marginTop); - targetHeight += parseInt(window.getComputedStyle(this._content, "").marginBottom); - return targetHeight; - - - - document.getAnonymousElementByAttribute(this, "class", "content-box"); - - - - - // This "command" event handler tracks changes made to preferences by - // the user in this window. - if (event.sourceEvent) - event = event.sourceEvent; - this.userChangedValue(event.target); - - - // This "select" event handler tracks changes made to colorpicker - // preferences by the user in this window. - if (event.target.localName == "colorpicker") - this.userChangedValue(event.target); - - - // This "change" event handler tracks changes made to preferences by - // the user in this window. - this.userChangedValue(event.target); - - - // This "input" event handler tracks changes made to preferences by - // the user in this window. - this.userChangedValue(event.target); - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/zotero/bindings/preferences-win.xml b/chrome/content/zotero/bindings/preferences-win.xml deleted file mode 100644 index e9afdb8758..0000000000 --- a/chrome/content/zotero/bindings/preferences-win.xml +++ /dev/null @@ -1,1358 +0,0 @@ - - - - %preferencesDTD; - - %globalKeysDTD; -]> - - - - - - - elements is constructed. Its purpose is to propagate - // the values to the associated form elements - - var elements = this.getElementsByTagName("preference"); - for (let element of elements) { - if (!element._constructed) { - return; - } - } - for (let element of elements) { - element.updateElements(); - } - ]]> - - - - - - - - - - - - - - - - - - - - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefService); - - - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - - - this.service.getDefaultBranch(""); - - - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - - - - - - - - - - - - - - - - - - - - - this.preferences.rootBranchInternal - .removeObserver(this.name, this.preferences); - - false - - - if (this.getAttribute("instantApply") == "false") - return false; - return this.getAttribute("instantApply") == "true" || this.preferences.instantApply; - - - - - - - if (val == this.name) - return val; - - this.preferences.rootBranchInternal - .removeObserver(this.name, this.preferences); - this.setAttribute('name', val); - this.preferences.rootBranchInternal - .addObserver(val, this.preferences, false); - - return val; - - - - - - - null - - - - - - - - - - - return this.preferences.rootBranch.prefIsLocked(this.name); - - - - - - return this.getAttribute("disabled") == "true"; - - - - - - - - - return parseInt(this.getAttribute("tabindex")); - - - - - - - - - - - - - - - // defer reset until preference update - this.value = undefined; - - - - false - - - - - - - - - return this._useDefault ? this.preferences.defaultBranch : this.preferences.rootBranch; - - - - false - - - - with id='" + this.id + "' and name='" + - this.name + "' has unknown type '" + this.type + "'."; - consoleService.logStringMessage(msg); - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - this.updateElements(); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - false - - - - - - - - - - this.setAttribute("lastSelected", val); - document.persist(this.id, "lastSelected"); - return val; - - - - - if (!this._currentPane) - this._currentPane = this.preferencePanes[0]; - - return this._currentPane; - - - null - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - 1) - aPaneElement.removeAttribute("flex"); - // Calling sizeToContent after the first prefpane is loaded - // will size the windows contents so style information is - // available to calculate correct sizing. - if (!this._initialized && prefpanes.length > 1) { - if (this._shouldAnimate) - this.style.minHeight = 0; - window.sizeToContent(); - } - - var oldPane = this.lastSelected ? document.getElementById(this.lastSelected) : this.preferencePanes[0]; - oldPane.selected = !(aPaneElement.selected = true); - this.lastSelected = aPaneElement.id; - this.currentPane = aPaneElement; - this._initialized = true; - - // Only animate if we've switched between prefpanes - if (this._shouldAnimate && oldPane.id != aPaneElement.id) { - aPaneElement.style.opacity = 0.0; - this.animate(oldPane, aPaneElement); - } - else if (!this._shouldAnimate && prefpanes.length > 1) { - var targetHeight = parseInt(window.getComputedStyle(this._paneDeckContainer, "").height); - var verticalPadding = parseInt(window.getComputedStyle(aPaneElement, "").paddingTop); - verticalPadding += parseInt(window.getComputedStyle(aPaneElement, "").paddingBottom); - if (aPaneElement.contentHeight > targetHeight - verticalPadding) { - // To workaround the bottom border of a groupbox from being - // cutoff an hbox with a class of bottomBox may enclose it. - // This needs to include its padding to resize properly. - // See bug 394433 - var bottomPadding = 0; - var bottomBox = aPaneElement.getElementsByAttribute("class", "bottomBox")[0]; - if (bottomBox) - bottomPadding = parseInt(window.getComputedStyle(bottomBox, "").paddingBottom); - window.innerHeight += bottomPadding + verticalPadding + aPaneElement.contentHeight - targetHeight; - } - - // XXX rstrong - extend the contents of the prefpane to - // prevent elements from being cutoff (see bug 349098). - if (aPaneElement.contentHeight + verticalPadding < targetHeight) - aPaneElement._content.style.height = targetHeight - verticalPadding + "px"; - } - } - break; - } - } - ]]> - - - - - - - - - - - - - - oldHeight ? 1 : -1; - var sizeDelta = Math.abs(oldHeight - aNewPane.contentHeight); - this._animateRemainder = sizeDelta % this._animateIncrement; - - this._setUpAnimationTimer(oldHeight); - ]]> - - - - - - 0 && this._currentHeight >= lastSelectedPane.contentHeight) || - (this._multiplier < 0 && this._currentHeight <= lastSelectedPane.contentHeight)) - return 0; - - if ((this._multiplier > 0 && newHeight > lastSelectedPane.contentHeight) || - (this._multiplier < 0 && newHeight < lastSelectedPane.contentHeight)) - increment = this._animateRemainder * this._multiplier; - return increment; - ]]> - - - - - - - - - - - - - - - - - - - - - - - - null - null - 15 - 40 - 5 - 0.40 - 0 - 0 - 0 - - - - - - - - - - - - - - return openDialog(aURL, "", "modal,centerscreen,resizable=no" + (aFeatures != "" ? ("," + aFeatures) : ""), aParams); - - - - - - - - - - - - - - - - - - - if (event.originalTarget.hasAttribute("pane")) { - var pane = document.getElementById(event.originalTarget.getAttribute("pane")); - this.showPane(pane); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - - - return document.getElementById(aElement.getAttribute("preference")); - - - - - - - - - - - - - - - - - - - - - new Set(); - - - - - - - - - - - - - - - - var targetHeight = parseInt(window.getComputedStyle(this._content, "").height); - targetHeight += parseInt(window.getComputedStyle(this._content, "").marginTop); - targetHeight += parseInt(window.getComputedStyle(this._content, "").marginBottom); - return targetHeight; - - - - document.getAnonymousElementByAttribute(this, "class", "content-box"); - - - - - // This "command" event handler tracks changes made to preferences by - // the user in this window. - if (event.sourceEvent) - event = event.sourceEvent; - this.userChangedValue(event.target); - - - // This "select" event handler tracks changes made to colorpicker - // preferences by the user in this window. - if (event.target.localName == "colorpicker") - this.userChangedValue(event.target); - - - // This "change" event handler tracks changes made to preferences by - // the user in this window. - this.userChangedValue(event.target); - - - // This "input" event handler tracks changes made to preferences by - // the user in this window. - this.userChangedValue(event.target); - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/zotero/bindings/text-link.xml b/chrome/content/zotero/bindings/text-link.xml deleted file mode 100644 index a42fa48120..0000000000 --- a/chrome/content/zotero/bindings/text-link.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/zotero/charsetMenu.js b/chrome/content/zotero/charsetMenu.js index 0d4d0a4458..51ee1b80dd 100644 --- a/chrome/content/zotero/charsetMenu.js +++ b/chrome/content/zotero/charsetMenu.js @@ -23,6 +23,7 @@ ***** END LICENSE BLOCK ***** */ + var Zotero_Charset_Menu = new function() { this.populate = populate; @@ -64,31 +65,16 @@ var Zotero_Charset_Menu = new function() { else { var charsetSeparator = document.createXULElement("menuseparator"); charsetPopup.appendChild(charsetSeparator); + + charsets = [ + { "label": "Unicode (UTF-8)", "value": "UTF-8" }, + { "label": "Western", "value": "windows-1252" }, + { "label": "UTF-16LE", "value": "UTF-16LE" }, + { "label": "UTF-16BE", "value": "UTF-16BE" }, + { "label": "Western (IBM-850)", "value": "IBM850" }, + { "label": "Western (MacRoman)", "value": "macintosh" } + ]; - Components.utils.import("resource://gre/modules/CharsetMenu.jsm"); - var cmData = CharsetMenu.getData(); - for (let charsetList of [cmData.pinnedCharsets, cmData.otherCharsets]) { - for (let charsetInfo of charsetList) { - if(charsetInfo.value == "UTF-8") { - charsets.push({ - "label":"Unicode (UTF-8)", - "value":"UTF-8" - }); - } else { - charsets.push({ - "label":charsetInfo.label, - "value":charsetInfo.value - }); - } - } - } - charsets = charsets.concat([ - {"label":"UTF-16LE", "value":"UTF-16LE"}, - {"label":"UTF-16BE", "value":"UTF-16BE"}, - {"label":"Western (IBM-850)", "value":"IBM850"}, - {"label":"Western (MacRoman)", "value":"macintosh"} - ]); - for(var i=0; i { + if (event.button == 0) { + this.open(event); + } + }, true); + this.addEventListener('keypress', (event) => { + if (event.key == 'Enter' || event.key == 'Return') { + event.preventDefault(); + this.click(); + } + }); + } + + connectedCallback() { + this.classList.add('zotero-text-link'); + } + + get href() { + return this.getAttribute('href'); + } + + set href(href) { + this.setAttribute('href', href); + } + + open(event) { + let href = this.href; + if (!href || this.disabled || event.defaultPrevented) { + return; + } + + var uri = null; + try { + const nsISSM = Components.interfaces.nsIScriptSecurityManager; + const secMan = + Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(nsISSM); + + const ioService = + Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + + uri = ioService.newURI(href, null, null); + + var nullPrincipal = secMan.createNullPrincipal({}); + try { + secMan.checkLoadURIWithPrincipal(nullPrincipal, uri, + nsISSM.DISALLOW_INHERIT_PRINCIPAL); + } + catch (ex) { + var msg = "Error: Cannot open a " + uri.scheme + ": link using the zotero-text-link CE."; + Components.utils.reportError(msg); + return; + } + + // Open HTTP URLs externally + if (window.Zotero && ["http", "https"].includes(uri.scheme)) { + Zotero.launchURL(uri.spec); + event.preventDefault(); + return; + } + } + catch (ex) { + Components.utils.reportError(ex); + } + + // otherwise, fall back to opening the anchor directly + var win = window; + if (window instanceof Components.interfaces.nsIDOMChromeWindow) { + while (win.opener && !win.opener.closed) + win = win.opener; + } + + if (uri) + win.open(uri.spec); + else + win.open(href); + + event.preventDefault(); + } + } + + customElements.define('zotero-text-link', ZoteroTextLink, { extends: 'label' }); +} diff --git a/chrome/content/zotero/locateManager.xhtml b/chrome/content/zotero/locateManager.xhtml index 9f6f68f2c5..7f5f1c153f 100644 --- a/chrome/content/zotero/locateManager.xhtml +++ b/chrome/content/zotero/locateManager.xhtml @@ -31,17 +31,6 @@ - + + + + + + + + + %prefWindow; + + %common; +]> + + + + + + + + + + + + + + + + + diff --git a/chrome/content/zotero/preferences/librariesToSync.xul b/chrome/content/zotero/preferences/librariesToSync.xul deleted file mode 100644 index 4b00f30e59..0000000000 --- a/chrome/content/zotero/preferences/librariesToSync.xul +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - %prefWindow; - - %common; -]> - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/chrome/content/zotero/preferences/preferences.js b/chrome/content/zotero/preferences/preferences.js index 1aebb6db1d..9c41c9de7a 100644 --- a/chrome/content/zotero/preferences/preferences.js +++ b/chrome/content/zotero/preferences/preferences.js @@ -27,56 +27,85 @@ var Zotero_Preferences = { init: function () { - if(Zotero.isConnector) { - Zotero.activateStandalone(); - window.close(); - return; + this.observerSymbols = []; + this.panes = new Map(); + this.navigation = document.getElementById('prefs-navigation'); + this.content = document.getElementById('prefs-content'); + + this.navigation.addEventListener('select', () => this._onNavigationSelect()); + document.getElementById('prefs-search').addEventListener('command', + event => this.search(event.target.value)); + + document.getElementById('prefs-subpane-back-button').addEventListener('command', () => { + let parent = this.panes.get(this.navigation.value).parent; + if (parent) { + this.navigation.value = parent; + } + }); + + document.getElementById('prefs-search').focus(); + + this.initPanes(); + }, + + initPanes: function () { + // Save positions and clear content in case we're reinitializing + // because of a plugin lifecycle event + let navigationValue = this.navigation.value; + let navigationScrollTop = this.navigation.scrollTop; + let navigationScrollLeft = this.navigation.scrollLeft; + this.navigation.replaceChildren(); + let contentScrollTop = this.content.scrollTop; + let contentScrollLeft = this.content.scrollLeft; + this.content.replaceChildren(); + + Zotero.PreferencePanes.builtInPanes.forEach(pane => this.addPane(pane)); + if (Zotero.PreferencePanes.pluginPanes.length) { + this.navigation.append(document.createElement('hr')); + Zotero.PreferencePanes.pluginPanes + .sort((a, b) => Zotero.localeCompare(a.rawLabel, b.rawLabel)) + .forEach(pane => this.addPane(pane)); } - - observerService.addObserver(function() { - if(Zotero.isConnector) window.close(); - }, "zotero-reloaded", false); - - if(window.arguments) { + + if (navigationValue) { + this.navigation.value = navigationValue; + this.navigation.scrollTop = navigationScrollTop; + this.navigation.scrollLeft = navigationScrollLeft; + this.content.scrollTop = contentScrollTop; + this.content.scrollLeft = contentScrollLeft; + } + else if (window.arguments) { var io = window.arguments[0]; io = io.wrappedJSObject || io; - - if(io.pane) { + + if (io.pane) { let tabID = io.tab; let tabIndex = io.tabIndex; var pane = document.getElementById(io.pane); - document.getElementById('zotero-prefs').showPane(pane); + this.navigation.value = io.pane; // Select tab within pane by tab id if (tabID !== undefined) { - if (pane.loaded) { - let tab = document.querySelector('tab#' + tabID); - if (tab) { - document.getElementsByTagName('tabbox')[0].selectedTab = tab; - } - } - else { - pane.addEventListener('paneload', function () { - let tab = document.querySelector('tab#' + tabID); - if (tab) { - document.getElementsByTagName('tabbox')[0].selectedTab = tab; - } - }) + let tab = document.getElementById(tabID); + if (tab) { + tab.control.selectedItem = tab; } } // Select tab within pane by index else if (tabIndex !== undefined) { - if (pane.loaded) { - document.getElementsByTagName('tabbox')[0].selectedIndex = tabIndex; - } - else { - pane.addEventListener('paneload', function () { - document.getElementsByTagName('tabbox')[0].selectedIndex = tabIndex; - }) - } + pane.querySelector('tabbox').selectedIndex = tabIndex; } } - } else if(document.location.hash == "#cite") { - document.getElementById('zotero-prefs').showPane(document.getElementById("zotero-prefpane-cite")); + } + else if (document.location.hash == "#cite") { + this.navigation.value = 'zotero-prefpane-cite'; + } + + if (!this.navigation.value) { + this.navigation.value = Zotero.Prefs.get('lastSelectedPrefPane'); + // If no last selected pane or ID is invalid, select General + if (!this.navigation.value) { + this.navigation.value = 'zotero-prefpane-general'; + } } }, @@ -84,6 +113,451 @@ var Zotero_Preferences = { if (Zotero_Preferences.Debug_Output) { Zotero_Preferences.Debug_Output.onUnload(); } + + while (this.observerSymbols.length) { + Zotero.Prefs.unregisterObserver(this.observerSymbols.shift()); + } + }, + + /** + * Add a pane to the left navigation sidebar. The pane source (`src`) is + * loaded as a fragment, not a full document. + * + * @param {Object} options + * @param {String} options.id Must be unique + * @param {String} [options.pluginID] ID of the plugin that registered the pane + * @param {String} [options.parent] ID of parent pane (if provided, pane is hidden from the sidebar) + * @param {String} [options.label] A DTD/.properties key (optional for panes with parents) + * @param {String} [options.rawLabel] A raw string to use as the label if optios.label is not provided + * @param {String} [options.image] URI of an icon (displayed in the navigation sidebar) + * @param {String} options.src URI of an XHTML fragment + * @param {String[]} [options.extraDTD] Array of URIs of DTD files to use for parsing the XHTML fragment + * @param {String[]} [options.scripts] Array of URIs of scripts to load along with the pane + * @param {Boolean} [options.defaultXUL] If true, parse the markup at `src` as XUL instead of XHTML: + * whitespace-only text nodes are ignored, XUL is the default namespace, and HTML tags are + * namespaced under `html:`. Default behavior is the opposite: whitespace nodes are preserved, + * HTML is the default namespace, and XUL tags are under `xul:`. + */ + async addPane(options) { + let { id, parent, label, rawLabel, image } = options; + + let listItem = document.createXULElement('richlistitem'); + listItem.value = id; + + if (image) { + let imageElem = document.createXULElement('image'); + imageElem.src = image; + listItem.append(imageElem); + } + + // We still add a hidden richlistitem even if this is a subpane, + // so we can invisibly select it and prevent richlistbox from selecting + // its first visible child on focus (which would hide the visible subpane) + if (parent) { + listItem.hidden = true; + } + else { + let labelElem = document.createXULElement('label'); + if (rawLabel) { + labelElem.value = rawLabel; + } + else if (Zotero.Intl.strings.hasOwnProperty(label)) { + labelElem.value = Zotero.Intl.strings[label]; + } + else { + labelElem.value = Zotero.getString(label); + } + listItem.append(labelElem); + } + + this.navigation.append(listItem); + + let container = document.createXULElement('vbox'); + container.id = id; + container.hidden = true; + this.content.append(container); + + this.panes.set(id, { + ...options, + imported: false, + container, + }); + }, + + /** + * Select a pane in the navigation sidebar, displaying its content. + * Clears the current search and hides all other panes' content. + * + * @param {String} id + */ + navigateToPane(id) { + this.navigation.value = id; + }, + + /** + * Display a pane's content, alongside any other panes already showing. + * If the pane is not yet loaded, it will be loaded first. + * + * @param {String} id + */ + _loadAndDisplayPane(id) { + let pane = this.panes.get(id); + if (!pane.imported) { + if (pane.scripts) { + for (let script of pane.scripts) { + Services.scriptloader.loadSubScript(script, this); + } + } + let markup = Zotero.File.getContentsFromURL(pane.src); + let dtdFiles = [ + 'chrome://zotero/locale/zotero.dtd', + 'chrome://zotero/locale/preferences.dtd', + ...(pane.extraDTD || []), + ]; + let contentFragment = pane.defaultXUL + ? MozXULElement.parseXULToFragment(markup, dtdFiles) + : this.parseXHTMLToFragment(markup, dtdFiles); + contentFragment = document.importNode(contentFragment, true); + pane.container.append(contentFragment); + pane.imported = true; + + this._initImportedNodes(pane.container); + } + + pane.container.hidden = false; + + let backButton = document.getElementById('prefs-subpane-back-button'); + backButton.hidden = !pane.parent; + }, + + parseXHTMLToFragment(str, entities = []) { + // Adapted from MozXULElement.parseXULToFragment + + /* eslint-disable indent */ + let parser = new DOMParser(); + parser.forceEnableXULXBL(); + let doc = parser.parseFromSafeString( + ` +${entities.length + ? ` { + return preamble + ` %_dtd-${index}; `; + }, '')}]>` + : ""} + +${str} +`, "application/xml"); + /* eslint-enable indent */ + + if (doc.documentElement.localName === 'parsererror') { + throw new Error('not well-formed XHTML'); + } + + // We use a range here so that we don't access the inner DOM elements from + // JavaScript before they are imported and inserted into a document. + let range = doc.createRange(); + range.selectNodeContents(doc.querySelector('div')); + return range.extractContents(); + }, + + /** + * If term is falsy, clear the current search and show the first pane. + * If term is truthy, execute a search: + * - Deselect the selected section + * - Show all preferences from all sections + * - Hide those not matching the search term (by full text and data-search-strings[-raw]) + * - Highlight full-text matches and show tooltips by search string matches + * + * @param {String} [term] + */ + search(term) { + // Initial housekeeping: + + // Clear existing highlights + this._getSearchSelection().removeAllRanges(); + + // Remove existing tooltips + // Need to convert to array before iterating so elements being removed from the + // live collection doesn't mess with the iteration + for (let oldTooltipParent of [...this.content.getElementsByClassName('search-tooltip-parent')]) { + oldTooltipParent.replaceWith(oldTooltipParent.firstElementChild); + } + + // Show hidden sections + for (let hidden of [...this.content.getElementsByClassName('hidden-by-search')]) { + hidden.classList.remove('hidden-by-search'); + hidden.ariaHidden = false; + } + + if (!term) { + if (this.navigation.selectedIndex == -1) { + this.navigation.selectedIndex = 0; + } + return; + } + + // Clear pane selection + this.navigation.clearSelection(); + + // Make sure all panes are loaded into the DOM and show top-level ones + for (let [id, pane] of this.panes) { + if (pane.parent) { + pane.container.hidden = true; + } + else { + this._loadAndDisplayPane(id); + } + } + + // Replace with abc + // This renders exactly the same and enables highlighting using ranges + for (let label of document.getElementsByTagNameNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', 'label')) { + if (label.getAttribute('value') && !label.textContent) { + label.textContent = label.getAttribute('value'); + label.removeAttribute('value'); + } + } + + // Clean the search term but keep the original - + //displaying with diacritics removed is confusing + let termForDisplay = Zotero.Utilities.trimInternal(term).toLowerCase(); + term = this._normalizeSearch(term); + + for (let container of this.content.children) { + let root = container.firstElementChild; + if (!root) continue; + + for (let child of root.children) { + let matches = this._searchRecursively(child, term); + if (matches.length) { + let touchedTabPanels = new Set(); + for (let node of matches) { + if (node.nodeType === Node.TEXT_NODE) { + // For text nodes, add a native highlight on the matched range + let value = node.nodeValue.toLowerCase(); + let index = value.indexOf(term); + if (index == -1) continue; // Should not happen + + let range = document.createRange(); + range.setStart(node, index); + range.setEnd(node, index + term.length); + this._getSearchSelection().addRange(range); + } + else if (node.nodeType == Node.ELEMENT_NODE) { + // For element nodes, wrap the element and add a tooltip + // (So please don't use .parentElement etc. in event listeners) + + // Structure: + // hbox.search-tooltip-parent + // | + // | span.search-tooltip + // | span + // | + let tooltipParent = document.createXULElement('hbox'); + tooltipParent.className = 'search-tooltip-parent'; + node.replaceWith(tooltipParent); + let tooltip = document.createElement('span'); + tooltip.className = 'search-tooltip'; + let tooltipText = document.createElement('span'); + tooltipText.append(termForDisplay); + tooltip.append(tooltipText); + tooltipParent.append(node, tooltip); + + // https://searchfox.org/mozilla-central/rev/703391c381f92a73d9a938cbe0d33ca64d94583b/browser/components/preferences/findInPage.js#689-691 + let tooltipRect = tooltip.getBoundingClientRect(); + tooltip.style.left = `calc(50% - ${tooltipRect.width / 2}px)`; + } + + let tabPanel = this._closest(node, 'tabpanels > tabpanel'); + let tabPanels = tabPanel?.parentElement; + if (tabPanels && !touchedTabPanels.has(tabPanels)) { + let tab = tabPanels.getRelatedElement(tabPanel); + if (tab.control) { + tab.control.selectedItem = tab; + touchedTabPanels.add(tabPanels); + } + } + } + } + else { + child.classList.add('hidden-by-search'); + child.ariaHidden = true; + } + } + } + }, + + /** + * Search for the given term (case-insensitive) in the tree. + * + * @param {Element} root + * @param {String} term Must be normalized (normalizeSearch()) + * @return {Node[]} + */ + _searchRecursively(root, term) { + const EXCLUDE_SELECTOR = 'input, [hidden="true"], [no-highlight]'; + + let matched = new Set(); + let treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT); + let currentNode; + while ((currentNode = treeWalker.nextNode())) { + if (this._closest(currentNode, EXCLUDE_SELECTOR) + || !currentNode.nodeValue + || currentNode.length < term.length) { + continue; + } + if (this._normalizeSearch(currentNode.nodeValue).includes(term)) { + let unhighlightableParent = this._closest(currentNode, 'menulist'); + if (unhighlightableParent) { + matched.add(unhighlightableParent); + } + else { + matched.add(currentNode); + } + } + } + + for (let elem of root.querySelectorAll('[data-search-strings-raw], [data-search-strings]')) { + if (elem.closest(EXCLUDE_SELECTOR)) { + continue; + } + + if (elem.hasAttribute('data-search-strings-raw')) { + let rawStrings = elem.getAttribute('data-search-strings-raw') + .split(',') + .map(this._normalizeSearch) + .filter(Boolean); + if (rawStrings.some(s => s.includes(term))) { + matched.add(elem); + continue; + } + } + + if (elem.hasAttribute('data-search-strings')) { + let stringKeys = elem.getAttribute('data-search-strings') + .split(',') + .map(s => s.trim()) + .filter(Boolean); + for (let key of stringKeys) { + if (Zotero.Intl.strings.hasOwnProperty(key)) { + if (this._normalizeSearch(Zotero.Intl.strings[key]).includes(term)) { + matched.add(elem); + break; + } + } + else if (this._normalizeSearch(Zotero.getString(key).replace(/%(\d+\$)?S/g, '')) + .includes(term)) { + matched.add(elem); + break; + } + } + } + } + + return [...matched]; + }, + + /** + * @param {String} s + * @return {String} + */ + _normalizeSearch(s) { + return Zotero.Utilities.removeDiacritics( + Zotero.Utilities.trimInternal(s).toLowerCase(), + true); + }, + + /** + * @return {Selection} + */ + _getSearchSelection() { + // https://searchfox.org/mozilla-central/rev/703391c381f92a73d9a938cbe0d33ca64d94583b/browser/components/preferences/findInPage.js#226-239 + let controller = window.docShell + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsISelectionDisplay) + .QueryInterface(Ci.nsISelectionController); + let selection = controller.getSelection( + Ci.nsISelectionController.SELECTION_FIND + ); + selection.setColors('currentColor', '#ffe900', 'currentColor', '#003eaa'); + return selection; + }, + + /** + * Like {@link Element#closest} for all nodes. + * + * @param {Node} node + * @param {String} selector + * @return {Element | null} + */ + _closest(node, selector) { + while (node && node.nodeType != Node.ELEMENT_NODE) { + node = node.parentNode; + } + return node?.closest(selector); + }, + + _onNavigationSelect() { + for (let child of this.content.children) { + child.setAttribute('hidden', true); + } + let paneID = this.navigation.value; + if (paneID) { + this.content.scrollTop = 0; + document.getElementById('prefs-search').value = ''; + this.search(''); + this._loadAndDisplayPane(paneID); + } + Zotero.Prefs.set('lastSelectedPrefPane', paneID); + }, + + _initImportedNodes(root) { + // Activate `preference` attributes + for (let elem of root.querySelectorAll('[preference]')) { + let preference = elem.getAttribute('preference'); + if (root.querySelector('preferences > preference#' + preference)) { + Zotero.debug(' is deprecated -- `preference` attribute values ' + + 'should be full preference keys, not IDs'); + preference = root.querySelector('preferences > preference#' + preference) + .getAttribute('name'); + } + + let useChecked = (elem instanceof HTMLInputElement && elem.type == 'checkbox') + || elem.tagName == 'checkbox'; + + elem.addEventListener(elem instanceof XULElement ? 'command' : 'input', () => { + let value = useChecked ? elem.checked : elem.value; + Zotero.Prefs.set(preference, value, true); + elem.dispatchEvent(new Event('synctopreference')); + }); + + let syncFromPref = () => { + let value = Zotero.Prefs.get(preference, true); + if (useChecked) { + elem.checked = value; + } + else { + elem.value = value; + } + elem.dispatchEvent(new Event('syncfrompreference')); + }; + + // Set timeout so pane can add listeners first + setTimeout(() => { + syncFromPref(); + this.observerSymbols.push(Zotero.Prefs.registerObserver(preference, syncFromPref, true)); + }); + } + + // parseXULToFragment() doesn't convert oncommand attributes into actual + // listeners, so we'll do it here + for (let elem of root.querySelectorAll('[oncommand]')) { + elem.oncommand = elem.getAttribute('oncommand'); + } + + for (let child of root.children) { + child.dispatchEvent(new Event('load')); + } }, openURL: function (url, windowName) { diff --git a/chrome/content/zotero/preferences/preferences.xhtml b/chrome/content/zotero/preferences/preferences.xhtml new file mode 100644 index 0000000000..542c57e75a --- /dev/null +++ b/chrome/content/zotero/preferences/preferences.xhtml @@ -0,0 +1,98 @@ + + + %globalDTD; + %editMenuOverlayDTD; + %zoteroDTD; + %preferencesDTD; +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/chrome/content/zotero/preferences/preferences.xul b/chrome/content/zotero/preferences/preferences.xul deleted file mode 100644 index 67624b0e0a..0000000000 --- a/chrome/content/zotero/preferences/preferences.xul +++ /dev/null @@ -1,966 +0,0 @@ - - - %zoteroDTD; - %preferencesDTD; -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &zotero.preferences.sync.fileSyncing.tos1; - - &zotero.preferences.period; - - - - - - - - - &zotero.preferences.sync.reset.warning1;&zotero.preferences.sync.reset.warning2;&zotero.preferences.sync.reset.warning3; - - - - Library: - - - - - - - - - &zotero.preferences.sync.reset.restoreToServer; - &zotero.preferences.sync.reset.restoreToServer.desc; - - - - - - - - - &zotero.preferences.sync.reset.resetFileSyncHistory; - &zotero.preferences.sync.reset.resetFileSyncHistory.desc; - - - - - &zotero.preferences.sync.reset.button; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &zotero.preferences.export.citePaperJournalArticleURL.description; - - - - - - - - - - - - - - - Some word processor plugins are disabled. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &zotero.preferences.attachmentBaseDir.message; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/zotero/preferences/preferences_advanced.js b/chrome/content/zotero/preferences/preferences_advanced.js index 4dad20253a..9b9845de0b 100644 --- a/chrome/content/zotero/preferences/preferences_advanced.js +++ b/chrome/content/zotero/preferences/preferences_advanced.js @@ -26,11 +26,7 @@ Components.utils.import("resource://gre/modules/Services.jsm"); import FilePicker from 'zotero/modules/filePicker'; -Zotero_Preferences.Advanced = { - DEFAULT_OPENURL_RESOLVER: 'https://www.worldcat.org/registry/gateway', - - _openURLResolvers: null, - +Zotero_Preferences.Advanced = { init: function () { Zotero_Preferences.Keys.init(); @@ -51,16 +47,31 @@ Zotero_Preferences.Advanced = { input.value = Zotero.Prefs.get(preferenceName); } - // Set OpenURL resolver drop-down to last-known name - if (Zotero.Prefs.get('openURL.resolver')) { - let name = Zotero.Prefs.get('openURL.name'); - if (name) { - document.getElementById('openurl-primary-popup').firstChild.setAttribute('label', name); - } - } + document.getElementById('baseAttachmentPath').addEventListener('syncfrompreference', + () => Zotero_Preferences.Attachment_Base_Directory.updateUI()); + + document.getElementById('data-dir').addEventListener('syncfrompreference', (event) => { + event.target.value = this.onDataDirLoad(); + }); + + document.getElementById('data-dir').addEventListener('synctopreference', (event) => { + this.onDataDirUpdate(event); + }); + + document.getElementById('data-dir-path').addEventListener('syncfrompreference', (event) => { + event.target.value = this.getDataDirPath(); + }); this.onDataDirLoad(); - this.refreshLocale(); + + document.getElementById('fulltext-rebuildIndex').setAttribute('label', + Zotero.getString('zotero.preferences.search.rebuildIndex') + + Zotero.getString('punctuation.ellipsis')); + document.getElementById('fulltext-clearIndex').setAttribute('label', + Zotero.getString('zotero.preferences.search.clearIndex') + + Zotero.getString('punctuation.ellipsis')); + + this.updateIndexStats(); }, @@ -429,238 +440,97 @@ Zotero_Preferences.Advanced = { return false; }, - - handleOpenURLPopupShowing: async function (event) { - if (event.target.id != 'openurl-primary-popup') { - return; - } - if (!this._openURLResolvers) { - let menupopup = document.getElementById('openurl-primary-popup'); - menupopup.firstChild.setAttribute('label', Zotero.getString('general.loading')); - try { - this._openURLResolvers = await Zotero.Utilities.Internal.OpenURL.getResolvers(); - } - catch (e) { - Zotero.logError(e); - menupopup.firstChild.setAttribute('label', "Error loading resolvers"); - return; - } - } - this.updateOpenURLResolversMenu(); - }, + updateIndexStats: Zotero.Promise.coroutine(function* () { + var stats = yield Zotero.Fulltext.getIndexStats(); + document.getElementById('fulltext-stats-indexed') + .setAttribute('value', stats.indexed); + document.getElementById('fulltext-stats-partial') + .setAttribute('value', stats.partial); + document.getElementById('fulltext-stats-unindexed') + .setAttribute('value', stats.unindexed); + document.getElementById('fulltext-stats-words') + .setAttribute('value', stats.words); + }), - updateOpenURLResolversMenu: function () { - if (!this._openURLResolvers) { - Zotero.debug("Resolvers not loaded -- not updating menu"); - return; - } - - var currentResolver = Zotero.Prefs.get('openURL.resolver'); - - var openURLMenu = document.getElementById('openurl-menu'); - var menupopup = openURLMenu.firstChild; - menupopup.innerHTML = ''; - - var defaultMenuItem = document.createElement('menuitem'); - defaultMenuItem.setAttribute('label', Zotero.getString('general.default')); - defaultMenuItem.setAttribute('value', this.DEFAULT_OPENURL_RESOLVER); - defaultMenuItem.setAttribute('type', 'checkbox'); - menupopup.appendChild(defaultMenuItem); - - var customMenuItem = document.createElement('menuitem'); - customMenuItem.setAttribute('label', Zotero.getString('general.custom')); - customMenuItem.setAttribute('value', 'custom'); - customMenuItem.setAttribute('type', 'checkbox'); - menupopup.appendChild(customMenuItem); - - menupopup.appendChild(document.createElement('menuseparator')); - - var selectedName; - var lastContinent; - var lastCountry; - var currentContinentPopup; - var currentMenuPopup; - for (let r of this._openURLResolvers) { - // Create submenus for continents - if (r.continent != lastContinent) { - let menu = document.createElement('menu'); - menu.setAttribute('label', r.continent); - openURLMenu.firstChild.appendChild(menu); - - currentContinentPopup = currentMenuPopup = document.createElement('menupopup'); - menu.appendChild(currentContinentPopup); - lastContinent = r.continent; - } - if (r.country != lastCountry) { - // If there's a country, create a submenu for it - if (r.country) { - let menu = document.createElement('menu'); - menu.setAttribute('label', r.country); - currentContinentPopup.appendChild(menu); - - let menupopup = document.createElement('menupopup'); - menu.appendChild(menupopup); - currentMenuPopup = menupopup; - } - // Otherwise use the continent popup - else { - currentMenuPopup = currentContinentPopup; - } - lastCountry = r.country; - } - let menuitem = document.createElement('menuitem'); - menuitem.setAttribute('label', r.name); - menuitem.setAttribute('value', r.url); - menuitem.setAttribute('type', 'checkbox'); - currentMenuPopup.appendChild(menuitem); - var checked = r.url == Zotero.Prefs.get('openURL.resolver'); - menuitem.setAttribute('checked', checked); - if (checked) { - selectedName = r.name; - } - } - - // Default - if (currentResolver == this.DEFAULT_OPENURL_RESOLVER) { - openURLMenu.setAttribute('label', Zotero.getString('general.default')); - defaultMenuItem.setAttribute('checked', true); - Zotero.Prefs.clear('openURL.name'); - } - else if (selectedName) { - openURLMenu.setAttribute('label', selectedName); - // If we found a match, update stored name - Zotero.Prefs.set('openURL.name', selectedName); - } - // Custom - else { - openURLMenu.setAttribute('label', Zotero.getString('general.custom')); - customMenuItem.setAttribute('checked', true); - Zotero.Prefs.clear('openURL.name'); - } - }, - - - handleOpenURLSelected: function (event) { - event.stopPropagation(); - event.preventDefault(); - - if (event.target.localName != 'menuitem') { - Zotero.debug("Ignoring click on " + event.target.localName); - return; - } - - var openURLMenu = document.getElementById('openurl-menu'); - - var openURLServerField = document.getElementById('openURLServerField'); - var openURLVersionMenu = document.getElementById('openURLVersionMenu'); - - // Default - if (event.target.value == this.DEFAULT_OPENURL_RESOLVER) { - Zotero.Prefs.clear('openURL.name'); - Zotero.Prefs.clear('openURL.resolver'); - Zotero.Prefs.clear('openURL.version'); - openURLServerField.value = this.DEFAULT_OPENURL_RESOLVER; - } - // If "Custom" selected, clear URL field - else if (event.target.value == "custom") { - Zotero.Prefs.clear('openURL.name'); - Zotero.Prefs.set('openURL.resolver', ''); - Zotero.Prefs.clear('openURL.version'); - openURLServerField.value = ''; - openURLServerField.focus(); - } - else { - Zotero.Prefs.set('openURL.name', openURLServerField.value = event.target.label); - Zotero.Prefs.set('openURL.resolver', openURLServerField.value = event.target.value); - Zotero.Prefs.set('openURL.version', openURLVersionMenu.value = "1.0"); - } - - openURLMenu.firstChild.hidePopup(); - - setTimeout(() => { - this.updateOpenURLResolversMenu(); - }); - }, - - onOpenURLCustomized: function () { - setTimeout(() => { - this.updateOpenURLResolversMenu(); - }); - }, - - - _getAutomaticLocaleMenuLabel: function () { - return Zotero.getString( - 'zotero.preferences.locale.automaticWithLocale', - Zotero.Locale.availableLocales[Zotero.locale] || Zotero.locale - ); - }, - - - refreshLocale: function () { - var autoLocaleName, currentValue; - - // If matching OS, get the name of the current locale - if (Zotero.Prefs.get('intl.locale.requested', true) === '') { - autoLocaleName = this._getAutomaticLocaleMenuLabel(); - currentValue = 'automatic'; - } - // Otherwise get the name of the locale specified in the pref - else { - autoLocaleName = Zotero.getString('zotero.preferences.locale.automatic'); - currentValue = Zotero.locale; - } - - // Populate menu - var menu = document.getElementById('locale-menu'); - var menupopup = menu.firstChild; - menupopup.textContent = ''; - // Show "Automatic (English)", "Automatic (Français)", etc. - menu.appendItem(autoLocaleName, 'automatic'); - menu.menupopup.appendChild(document.createElement('menuseparator')); - // Add all available locales - for (let locale in Zotero.Locale.availableLocales) { - menu.appendItem(Zotero.Locale.availableLocales[locale], locale); - } - menu.value = currentValue; - }, - - onLocaleChange: function () { - var requestedLocale = Services.locale.requestedLocale; - var menu = document.getElementById('locale-menu'); - - if (menu.value == 'automatic') { - // Changed if not already set to automatic (unless we have the automatic locale name, - // meaning we just switched away to the same manual locale and back to automatic) - var changed = requestedLocale - && requestedLocale == Zotero.locale - && menu.label != this._getAutomaticLocaleMenuLabel(); - Services.locale.requestedLocales = null; - } - else { - // Changed if moving to a locale other than the current one - var changed = requestedLocale != menu.value - Services.locale.requestedLocales = [menu.value]; - } - - if (!changed) { - return; - } + rebuildIndexPrompt: async function () { + var buttons = [ + document.getElementById('fulltext-rebuildIndex'), + document.getElementById('fulltext-clearIndex') + ]; + buttons.forEach(b => b.disabled = true); var ps = Services.prompt; var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING - + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING; - var index = ps.confirmEx(null, - Zotero.getString('general.restartRequired'), - Zotero.getString('general.restartRequiredForChange', Zotero.appName), - buttonFlags, - Zotero.getString('general.restartNow'), - Zotero.getString('general.restartLater'), - null, null, {}); + + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL + + ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING; - if (index == 0) { - Zotero.Utilities.Internal.quitZotero(true); + var index = ps.confirmEx(null, + Zotero.getString('zotero.preferences.search.rebuildIndex'), + Zotero.getString('zotero.preferences.search.rebuildWarning', + Zotero.getString('zotero.preferences.search.indexUnindexed')), + buttonFlags, + Zotero.getString('zotero.preferences.search.rebuildIndex'), + null, + // Position 2 because of https://bugzilla.mozilla.org/show_bug.cgi?id=345067 + Zotero.getString('zotero.preferences.search.indexUnindexed'), + null, {}); + + try { + if (index == 0) { + await Zotero.Fulltext.rebuildIndex(); + } + else if (index == 2) { + await Zotero.Fulltext.rebuildIndex(true) + } + + await this.updateIndexStats(); + } + catch (e) { + Zotero.alert(null, Zotero.getString('general.error'), e); + } + finally { + buttons.forEach(b => b.disabled = false); + } + }, + + clearIndexPrompt: async function () { + var buttons = [ + document.getElementById('fulltext-rebuildIndex'), + document.getElementById('fulltext-clearIndex') + ]; + buttons.forEach(b => b.disabled = true); + + var ps = Services.prompt; + var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING + + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL + + ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING; + + var index = ps.confirmEx(null, + Zotero.getString('zotero.preferences.search.clearIndex'), + Zotero.getString('zotero.preferences.search.clearWarning', + Zotero.getString('zotero.preferences.search.clearNonLinkedURLs')), + buttonFlags, + Zotero.getString('zotero.preferences.search.clearIndex'), + null, + // Position 2 because of https://bugzilla.mozilla.org/show_bug.cgi?id=345067 + Zotero.getString('zotero.preferences.search.clearNonLinkedURLs'), null, {}); + + try { + if (index == 0) { + await Zotero.Fulltext.clearIndex(); + } + else if (index == 2) { + await Zotero.Fulltext.clearIndex(true); + } + + await this.updateIndexStats(); + } + catch (e) { + Zotero.alert(null, Zotero.getString('general.error'), e); + } + finally { + buttons.forEach(b => b.disabled = false); } } }; @@ -943,50 +813,47 @@ Zotero_Preferences.Attachment_Base_Directory = { }), - updateUI: Zotero.Promise.coroutine(function* () { + updateUI: async function () { var filefield = document.getElementById('baseAttachmentPath'); var path = Zotero.Prefs.get('baseAttachmentPath'); Components.utils.import("resource://gre/modules/osfile.jsm"); - if (yield OS.File.exists(path)) { - filefield.file = Zotero.File.pathToFile(path); - filefield.label = path; + if (await OS.File.exists(path)) { + filefield.style.backgroundImage = 'url(moz-icon://file://' + path + '?size=16)'; + filefield.value = path; } else { - filefield.label = ''; + filefield.value = ''; } document.getElementById('resetBasePath').disabled = !path; - }) + } }; Zotero_Preferences.Keys = { init: function () { - var rows = document.getElementById('zotero-prefpane-advanced-keys-tab').getElementsByTagName('row'); - for (var i=0; i { + textbox.value = Zotero_Preferences.Keys.capitalizePref(textbox.id) || ''; + }); + textbox.addEventListener('input', () => { + textbox.value = textbox.value.toUpperCase(); + }); } }, capitalizePref: function (id) { var elem = document.getElementById(id); - var pref = document.getElementById(elem.getAttribute('preference')); - if (pref.value) { - return pref.value.toUpperCase(); - } + return Zotero.Prefs.get(elem.getAttribute('preference'), true).toUpperCase(); } }; diff --git a/chrome/content/zotero/preferences/preferences_advanced.xhtml b/chrome/content/zotero/preferences/preferences_advanced.xhtml new file mode 100644 index 0000000000..cc3d976573 --- /dev/null +++ b/chrome/content/zotero/preferences/preferences_advanced.xhtml @@ -0,0 +1,265 @@ + + + + + &zotero.preferences.prefpane.general; + + + &zotero.preferences.miscellaneous; + + + + + + + + + + + &zotero.preferences.advanced.advancedConfiguration; + + + + + + + + + + &zotero.preferences.advanced.filesAndFolders; + + + &zotero.preferences.attachmentBaseDir.caption; + + + + &zotero.preferences.attachmentBaseDir.message; + + + + + + + + + + + + + + + + &zotero.preferences.dataDir; + + + + + + + + + + + + + + + + + + + + + + + &zotero.preferences.dbMaintenance; + + + + + + + + + + + &zotero.preferences.advanced.keys; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &zotero.preferences.feeds; + + + + + + + + + + + + + + + + + &zotero.preferences.feeds.feedDefaults; + + + + + + + + + + + + + + + + + + + + + + + + + + &zotero.preferences.prefpane.search; + + + &zotero.preferences.search.fulltextCache; + + + + + + + + + + + + + + + + + + + + + + + + + &zotero.preferences.search.indexStats; + + + + + + + + + + + + + + + + + diff --git a/chrome/content/zotero/preferences/preferences_advanced.xul b/chrome/content/zotero/preferences/preferences_advanced.xul deleted file mode 100644 index ffb8d2b2dc..0000000000 --- a/chrome/content/zotero/preferences/preferences_advanced.xul +++ /dev/null @@ -1,333 +0,0 @@ - - - - %zoteroDTD; - - %preferencesDTD; -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &zotero.preferences.attachmentBaseDir.message; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/zotero/preferences/preferences_cite.jsx b/chrome/content/zotero/preferences/preferences_cite.jsx index ae9b538736..4d200f9752 100644 --- a/chrome/content/zotero/preferences/preferences_cite.jsx +++ b/chrome/content/zotero/preferences/preferences_cite.jsx @@ -41,11 +41,11 @@ Zotero_Preferences.Cite = { 'zoteroWinWordIntegration@zotero.org' ]), - init: Zotero.Promise.coroutine(function* () { + init: async function () { Components.utils.import("resource://gre/modules/AddonManager.jsm"); this.updateWordProcessorInstructions(); - yield this.refreshStylesList(); - }), + await this.refreshStylesList(); + }, /** @@ -129,13 +129,18 @@ Zotero_Preferences.Cite = { columns={columns} staticColumns={true} disableFontSizeScaling={true} - onSelectionChange={() => document.getElementById('styleManager-delete').disabled = undefined} + onSelectionChange={selection => document.getElementById('styleManager-delete').disabled = !selection.count} onKeyDown={handleKeyDown} getRowString={index => this.styles[index].title} /> ); - await new Promise(resolve => ReactDOM.render(elem, document.getElementById("styleManager"), resolve)); + + let styleManager = document.getElementById("styleManager"); + await new Promise(resolve => ReactDOM.render(elem, styleManager, resolve)); + + // Fix style manager showing partially blank until scrolled + setTimeout(() => this._tree.invalidate()); } else { this._tree.invalidate(); @@ -147,6 +152,9 @@ Zotero_Preferences.Cite = { this._tree.selection.select(index); } } + else if ([...this._tree.selection.selected].some(i => i >= this.styles.length)) { + this._tree.selection.clearSelection(); + } }, @@ -228,7 +236,6 @@ Zotero_Preferences.Cite = { } yield this.refreshStylesList(); - document.getElementById('styleManager-delete').disabled = true; } }), diff --git a/chrome/content/zotero/preferences/preferences_cite.xhtml b/chrome/content/zotero/preferences/preferences_cite.xhtml new file mode 100644 index 0000000000..1389294b09 --- /dev/null +++ b/chrome/content/zotero/preferences/preferences_cite.xhtml @@ -0,0 +1,84 @@ + + + + + &zotero.preferences.cite.styles; + + + + &zotero.preferences.cite.styles.styleManager; + + + + + + + + + + + + + + &zotero.preferences.citationOptions.caption; + + + + + &zotero.preferences.export.citePaperJournalArticleURL.description; + + + + + &zotero.general.tools; + + + + + + + + + &zotero.preferences.cite.wordProcessors; + + + Some word processor plugins are disabled. + + + + + + + diff --git a/chrome/content/zotero/preferences/preferences_cite.xul b/chrome/content/zotero/preferences/preferences_cite.xul deleted file mode 100644 index 090733bfed..0000000000 --- a/chrome/content/zotero/preferences/preferences_cite.xul +++ /dev/null @@ -1,109 +0,0 @@ - - - - %zoteroDTD; - - %preferencesDTD; -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &zotero.preferences.export.citePaperJournalArticleURL.description; - - - - - - - - - - - - - - - Some word processor plugins are disabled. - - - - - - - - - - - - diff --git a/chrome/content/zotero/preferences/preferences_export.jsx b/chrome/content/zotero/preferences/preferences_export.jsx index fa60a58064..f2068749d9 100644 --- a/chrome/content/zotero/preferences/preferences_export.jsx +++ b/chrome/content/zotero/preferences/preferences_export.jsx @@ -36,12 +36,6 @@ Zotero_Preferences.Export = { this.updateQuickCopyInstructions(); yield this.populateQuickCopyList(); yield this.populateNoteQuickCopyList(); - - var charsetMenu = document.getElementById("zotero-import-charsetMenu"); - var charsetMap = Zotero_Charset_Menu.populate(charsetMenu, false); - charsetMenu.selectedItem = - charsetMap[Zotero.Prefs.get("import.charset")] ? - charsetMap[Zotero.Prefs.get("import.charset")] : charsetMap["auto"]; }), @@ -104,7 +98,7 @@ Zotero_Preferences.Export = { menulist.selectedItem = null; menulist.removeAllItems(); - var popup = document.createElement('menupopup'); + var popup = document.createXULElement('menupopup'); menulist.appendChild(popup); // add export formats to list @@ -150,7 +144,7 @@ Zotero_Preferences.Export = { } var val = JSON.stringify({ mode: 'export', id: translator.translatorID }); - var itemNode = document.createElement('menuitem'); + var itemNode = document.createXULElement('menuitem'); itemNode.setAttribute('value', val); itemNode.setAttribute('label', translator.label); // itemNode.setAttribute('oncommand', 'Zotero_Preferences.Export.updateQuickCopyUI()'); @@ -178,17 +172,10 @@ Zotero_Preferences.Export = { menulist.selectedItem = null; menulist.removeAllItems(); - // Prevent Cmd-w from setting "Wikipedia" - menulist.onkeydown = function (event) { - if ((Zotero.isMac && event.metaKey) || event.ctrlKey) { - event.preventDefault(); - } - } - - var popup = document.createElement('menupopup'); + var popup = document.createXULElement('menupopup'); menulist.appendChild(popup); - var itemNode = document.createElement("menuitem"); + var itemNode = document.createXULElement("menuitem"); itemNode.setAttribute("label", Zotero.getString('zotero.preferences.export.quickCopy.citationStyles')); itemNode.setAttribute("disabled", true); popup.appendChild(itemNode); @@ -197,7 +184,7 @@ Zotero_Preferences.Export = { var styles = Zotero.Styles.getVisible(); styles.forEach(function (style) { var val = 'bibliography' + (contentType == 'html' ? '/html' : '') + '=' + style.styleID; - var itemNode = document.createElement("menuitem"); + var itemNode = document.createXULElement("menuitem"); itemNode.setAttribute("value", val); itemNode.setAttribute("label", style.title); itemNode.setAttribute("oncommand", 'Zotero_Preferences.Export.updateQuickCopyUI()'); @@ -208,7 +195,7 @@ Zotero_Preferences.Export = { } }); - var itemNode = document.createElement("menuitem"); + var itemNode = document.createXULElement("menuitem"); itemNode.setAttribute("label", Zotero.getString('zotero.preferences.export.quickCopy.exportFormats')); itemNode.setAttribute("disabled", true); popup.appendChild(itemNode); @@ -223,7 +210,7 @@ Zotero_Preferences.Export = { return; } var val = 'export=' + translator.translatorID; - var itemNode = document.createElement("menuitem"); + var itemNode = document.createXULElement("menuitem"); itemNode.setAttribute("value", val); itemNode.setAttribute("label", translator.label); itemNode.setAttribute("oncommand", 'Zotero_Preferences.Export.updateQuickCopyUI()'); @@ -307,7 +294,7 @@ Zotero_Preferences.Export = { var translators = await translation.getTranslators(); var io = { domain, format, locale, asHTML, ok: false, styles, translators }; - window.openDialog('chrome://zotero/content/preferences/quickCopySiteEditor.xul', + window.openDialog('chrome://zotero/content/preferences/quickCopySiteEditor.xhtml', "zotero-preferences-quickCopySiteEditor", "chrome,modal,centerscreen", io); if (!io.ok || !io.domain) { @@ -400,7 +387,7 @@ Zotero_Preferences.Export = { onSelectionChange={handleSelectionChange} onKeyDown={handleKeyDown} getRowString={index => this._rows[index].domain} - onActivate={(event, indices) => Zotero_Preferences.Export.showQuickCopySiteEditor()} + onActivate={(event, indices) => Zotero_Preferences.Export.showQuickCopySiteEditor(true)} /> ); diff --git a/chrome/content/zotero/preferences/preferences_export.xhtml b/chrome/content/zotero/preferences/preferences_export.xhtml new file mode 100644 index 0000000000..c6daab017c --- /dev/null +++ b/chrome/content/zotero/preferences/preferences_export.xhtml @@ -0,0 +1,79 @@ + + + + + + &zotero.preferences.quickCopy.caption; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/chrome/content/zotero/preferences/preferences_export.xul b/chrome/content/zotero/preferences/preferences_export.xul deleted file mode 100644 index c8b7e4ad0c..0000000000 --- a/chrome/content/zotero/preferences/preferences_export.xul +++ /dev/null @@ -1,115 +0,0 @@ - - - - %prefWindow; - - %common; -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/zotero/preferences/preferences_general.js b/chrome/content/zotero/preferences/preferences_general.js index d0d4edc91c..9d5cb77f56 100644 --- a/chrome/content/zotero/preferences/preferences_general.js +++ b/chrome/content/zotero/preferences/preferences_general.js @@ -30,6 +30,10 @@ Components.utils.import("resource://gre/modules/osfile.jsm"); import FilePicker from 'zotero/modules/filePicker'; Zotero_Preferences.General = { + DEFAULT_OPENURL_RESOLVER: 'https://www.worldcat.org/registry/gateway', + + _openURLResolvers: null, + init: function () { // JS-based strings var checkbox = document.getElementById('launchNonNativeFiles-checkbox'); @@ -40,10 +44,93 @@ Zotero_Preferences.General = { } var menuitem = document.getElementById('fileHandler-internal'); menuitem.setAttribute('label', Zotero.appName); + + // Set OpenURL resolver drop-down to last-known name + if (Zotero.Prefs.get('openURL.resolver')) { + let name = Zotero.Prefs.get('openURL.name'); + if (name) { + document.getElementById('openurl-primary-popup').firstChild.setAttribute('label', name); + } + } + this.refreshLocale(); this.updateAutoRenameFilesUI(); this._updateFileHandlerUI(); }, + + _getAutomaticLocaleMenuLabel: function () { + return Zotero.getString( + 'zotero.preferences.locale.automaticWithLocale', + Zotero.Locale.availableLocales[Zotero.locale] || Zotero.locale + ); + }, + + + refreshLocale: function () { + var autoLocaleName, currentValue; + + // If matching OS, get the name of the current locale + if (Zotero.Prefs.get('intl.locale.requested', true) === '') { + autoLocaleName = this._getAutomaticLocaleMenuLabel(); + currentValue = 'automatic'; + } + // Otherwise get the name of the locale specified in the pref + else { + autoLocaleName = Zotero.getString('zotero.preferences.locale.automatic'); + currentValue = Zotero.locale; + } + + // Populate menu + var menu = document.getElementById('locale-menu'); + var menupopup = menu.firstChild; + menupopup.textContent = ''; + // Show "Automatic (English)", "Automatic (Français)", etc. + menu.appendItem(autoLocaleName, 'automatic'); + menu.menupopup.appendChild(document.createXULElement('menuseparator')); + // Add all available locales + for (let locale in Zotero.Locale.availableLocales) { + menu.appendItem(Zotero.Locale.availableLocales[locale], locale); + } + menu.value = currentValue; + }, + + onLocaleChange: function () { + var requestedLocale = Services.locale.requestedLocale; + var menu = document.getElementById('locale-menu'); + + if (menu.value == 'automatic') { + // Changed if not already set to automatic (unless we have the automatic locale name, + // meaning we just switched away to the same manual locale and back to automatic) + var changed = requestedLocale + && requestedLocale == Zotero.locale + && menu.label != this._getAutomaticLocaleMenuLabel(); + Services.locale.requestedLocales = null; + } + else { + // Changed if moving to a locale other than the current one + var changed = requestedLocale != menu.value + Services.locale.requestedLocales = [menu.value]; + } + + if (!changed) { + return; + } + + var ps = Services.prompt; + var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING + + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING; + var index = ps.confirmEx(null, + Zotero.getString('general.restartRequired'), + Zotero.getString('general.restartRequiredForChange', Zotero.appName), + buttonFlags, + Zotero.getString('general.restartNow'), + Zotero.getString('general.restartLater'), + null, null, {}); + + if (index == 0) { + Zotero.Utilities.Internal.quitZotero(true); + } + }, updateAutoRenameFilesUI: function () { setTimeout(() => { @@ -116,6 +203,10 @@ Zotero_Preferences.General = { } customMenuItem.hidden = false; menulist.selectedIndex = 2; + + // There's almost certainly a better way to do this... + // but why doesn't the icon just behave by default? + menulist.shadowRoot.querySelector('[part="icon"]').style.height = '16px'; } // Zotero else { @@ -130,5 +221,164 @@ Zotero_Preferences.General = { throw new Error(`Unknown file type ${type}`); } return 'fileHandler.pdf'; + }, + + handleOpenURLPopupShowing: async function (event) { + if (event.target.id != 'openurl-primary-popup') { + return; + } + if (!this._openURLResolvers) { + let menupopup = document.getElementById('openurl-primary-popup'); + menupopup.firstChild.setAttribute('label', Zotero.getString('general.loading')); + try { + this._openURLResolvers = await Zotero.Utilities.Internal.OpenURL.getResolvers(); + } + catch (e) { + Zotero.logError(e); + menupopup.firstChild.setAttribute('label', "Error loading resolvers"); + return; + } + } + this.updateOpenURLResolversMenu(); + }, + + + updateOpenURLResolversMenu: function () { + if (!this._openURLResolvers) { + Zotero.debug("Resolvers not loaded -- not updating menu"); + return; + } + + var currentResolver = Zotero.Prefs.get('openURL.resolver'); + + var openURLMenu = document.getElementById('openurl-menu'); + var menupopup = openURLMenu.firstChild; + menupopup.innerHTML = ''; + + var defaultMenuItem = document.createXULElement('menuitem'); + defaultMenuItem.setAttribute('label', Zotero.getString('general.default')); + defaultMenuItem.setAttribute('value', this.DEFAULT_OPENURL_RESOLVER); + defaultMenuItem.setAttribute('type', 'checkbox'); + menupopup.appendChild(defaultMenuItem); + + var customMenuItem = document.createXULElement('menuitem'); + customMenuItem.setAttribute('label', Zotero.getString('general.custom')); + customMenuItem.setAttribute('value', 'custom'); + customMenuItem.setAttribute('type', 'checkbox'); + menupopup.appendChild(customMenuItem); + + menupopup.appendChild(document.createXULElement('menuseparator')); + + var selectedName; + var lastContinent; + var lastCountry; + var currentContinentPopup; + var currentMenuPopup; + for (let r of this._openURLResolvers) { + // Create submenus for continents + if (r.continent != lastContinent) { + let menu = document.createXULElement('menu'); + menu.setAttribute('label', r.continent); + openURLMenu.firstChild.appendChild(menu); + + currentContinentPopup = currentMenuPopup = document.createXULElement('menupopup'); + menu.appendChild(currentContinentPopup); + lastContinent = r.continent; + } + if (r.country != lastCountry) { + // If there's a country, create a submenu for it + if (r.country) { + let menu = document.createXULElement('menu'); + menu.setAttribute('label', r.country); + currentContinentPopup.appendChild(menu); + + let menupopup = document.createXULElement('menupopup'); + menu.appendChild(menupopup); + currentMenuPopup = menupopup; + } + // Otherwise use the continent popup + else { + currentMenuPopup = currentContinentPopup; + } + lastCountry = r.country; + } + let menuitem = document.createXULElement('menuitem'); + menuitem.setAttribute('label', r.name); + menuitem.setAttribute('value', r.url); + menuitem.setAttribute('type', 'checkbox'); + currentMenuPopup.appendChild(menuitem); + var checked = r.url == Zotero.Prefs.get('openURL.resolver'); + menuitem.setAttribute('checked', checked); + if (checked) { + selectedName = r.name; + } + } + + // Default + if (currentResolver == this.DEFAULT_OPENURL_RESOLVER) { + openURLMenu.setAttribute('label', Zotero.getString('general.default')); + defaultMenuItem.setAttribute('checked', true); + Zotero.Prefs.clear('openURL.name'); + } + else if (selectedName) { + openURLMenu.setAttribute('label', selectedName); + // If we found a match, update stored name + Zotero.Prefs.set('openURL.name', selectedName); + } + // Custom + else { + openURLMenu.setAttribute('label', Zotero.getString('general.custom')); + customMenuItem.setAttribute('checked', true); + Zotero.Prefs.clear('openURL.name'); + } + }, + + + handleOpenURLSelected: function (event) { + event.stopPropagation(); + event.preventDefault(); + + if (event.target.localName != 'menuitem') { + Zotero.debug("Ignoring click on " + event.target.localName); + return; + } + + var openURLMenu = document.getElementById('openurl-menu'); + + var openURLServerField = document.getElementById('openURLServerField'); + var openURLVersionMenu = document.getElementById('openURLVersionMenu'); + + // Default + if (event.target.value == this.DEFAULT_OPENURL_RESOLVER) { + Zotero.Prefs.clear('openURL.name'); + Zotero.Prefs.clear('openURL.resolver'); + Zotero.Prefs.clear('openURL.version'); + openURLServerField.value = this.DEFAULT_OPENURL_RESOLVER; + } + // If "Custom" selected, clear URL field + else if (event.target.value == "custom") { + Zotero.Prefs.clear('openURL.name'); + Zotero.Prefs.set('openURL.resolver', ''); + Zotero.Prefs.clear('openURL.version'); + openURLServerField.value = ''; + openURLServerField.focus(); + } + else { + Zotero.Prefs.set('openURL.name', openURLServerField.value = event.target.label); + Zotero.Prefs.set('openURL.resolver', openURLServerField.value = event.target.value); + Zotero.Prefs.set('openURL.version', openURLVersionMenu.value = "1.0"); + } + + openURLMenu.firstChild.hidePopup(); + + setTimeout(() => { + this.updateOpenURLResolversMenu(); + }); + }, + + onOpenURLCustomized: function () { + setTimeout(() => { + this.updateOpenURLResolversMenu(); + }); } } diff --git a/chrome/content/zotero/preferences/preferences_general.xhtml b/chrome/content/zotero/preferences/preferences_general.xhtml new file mode 100644 index 0000000000..794f682fde --- /dev/null +++ b/chrome/content/zotero/preferences/preferences_general.xhtml @@ -0,0 +1,133 @@ + + + + &zotero.preferences.interface; + + + + + + + + + + + &zotero.preferences.fileHandling; + + + + + + + + + + + + + + + + + + + + + + + + &zotero.preferences.prefpane.locate; + + + + + + + + + + + + + + + + + + + + + + + + + + + &zotero.preferences.miscellaneous; + + + + + + + + + + + &zotero.preferences.groups; + + + + + + + + + + + diff --git a/chrome/content/zotero/preferences/preferences_general.xul b/chrome/content/zotero/preferences/preferences_general.xul deleted file mode 100644 index 289aaf2fab..0000000000 --- a/chrome/content/zotero/preferences/preferences_general.xul +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/zotero/preferences/preferences_search.js b/chrome/content/zotero/preferences/preferences_search.js deleted file mode 100644 index 5003de8b49..0000000000 --- a/chrome/content/zotero/preferences/preferences_search.js +++ /dev/null @@ -1,136 +0,0 @@ -/* - ***** BEGIN LICENSE BLOCK ***** - - Copyright © 2006–2013 Center for History and New Media - George Mason University, Fairfax, Virginia, USA - http://zotero.org - - This file is part of Zotero. - - Zotero is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Zotero is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with Zotero. If not, see . - - ***** END LICENSE BLOCK ***** -*/ - -"use strict"; -Components.utils.import("resource://gre/modules/Services.jsm"); - -Zotero_Preferences.Search = { - init: function () { - document.getElementById('fulltext-rebuildIndex').setAttribute('label', - Zotero.getString('zotero.preferences.search.rebuildIndex') - + Zotero.getString('punctuation.ellipsis')); - document.getElementById('fulltext-clearIndex').setAttribute('label', - Zotero.getString('zotero.preferences.search.clearIndex') - + Zotero.getString('punctuation.ellipsis')); - - this.updateIndexStats(); - }, - - updateIndexStats: Zotero.Promise.coroutine(function* () { - var stats = yield Zotero.Fulltext.getIndexStats(); - document.getElementById('fulltext-stats-indexed'). - lastChild.setAttribute('value', stats.indexed); - document.getElementById('fulltext-stats-partial'). - lastChild.setAttribute('value', stats.partial); - document.getElementById('fulltext-stats-unindexed'). - lastChild.setAttribute('value', stats.unindexed); - document.getElementById('fulltext-stats-words'). - lastChild.setAttribute('value', stats.words); - }), - - - rebuildIndexPrompt: async function () { - var buttons = [ - document.getElementById('fulltext-rebuildIndex'), - document.getElementById('fulltext-clearIndex') - ]; - buttons.forEach(b => b.disabled = true); - - var ps = Services.prompt; - var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING - + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL - + ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING; - - var index = ps.confirmEx(null, - Zotero.getString('zotero.preferences.search.rebuildIndex'), - Zotero.getString('zotero.preferences.search.rebuildWarning', - Zotero.getString('zotero.preferences.search.indexUnindexed')), - buttonFlags, - Zotero.getString('zotero.preferences.search.rebuildIndex'), - null, - // Position 2 because of https://bugzilla.mozilla.org/show_bug.cgi?id=345067 - Zotero.getString('zotero.preferences.search.indexUnindexed'), - null, {}); - - try { - if (index == 0) { - await Zotero.Fulltext.rebuildIndex(); - } - else if (index == 2) { - await Zotero.Fulltext.rebuildIndex(true) - } - - await this.updateIndexStats(); - } - catch (e) { - Zotero.alert(null, Zotero.getString('general.error'), e); - } - finally { - buttons.forEach(b => b.disabled = false); - } - - }, - - - clearIndexPrompt: async function () { - var buttons = [ - document.getElementById('fulltext-rebuildIndex'), - document.getElementById('fulltext-clearIndex') - ]; - buttons.forEach(b => b.disabled = true); - - var ps = Services.prompt; - var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING - + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL - + ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING; - - var index = ps.confirmEx(null, - Zotero.getString('zotero.preferences.search.clearIndex'), - Zotero.getString('zotero.preferences.search.clearWarning', - Zotero.getString('zotero.preferences.search.clearNonLinkedURLs')), - buttonFlags, - Zotero.getString('zotero.preferences.search.clearIndex'), - null, - // Position 2 because of https://bugzilla.mozilla.org/show_bug.cgi?id=345067 - Zotero.getString('zotero.preferences.search.clearNonLinkedURLs'), null, {}); - - try { - if (index == 0) { - await Zotero.Fulltext.clearIndex(); - } - else if (index == 2) { - await Zotero.Fulltext.clearIndex(true); - } - - await this.updateIndexStats(); - } - catch (e) { - Zotero.alert(null, Zotero.getString('general.error'), e); - } - finally { - buttons.forEach(b => b.disabled = false); - } - } -}; diff --git a/chrome/content/zotero/preferences/preferences_sync.jsx b/chrome/content/zotero/preferences/preferences_sync.jsx index 74674c9300..9c63136f93 100644 --- a/chrome/content/zotero/preferences/preferences_sync.jsx +++ b/chrome/content/zotero/preferences/preferences_sync.jsx @@ -39,12 +39,12 @@ Zotero_Preferences.Sync = { checkmarkChar: '\u2705', noChar: '\uD83D\uDEAB', - init: Zotero.Promise.coroutine(function* () { + init: async function () { this.updateStorageSettingsUI(); this.updateStorageSettingsGroupsUI(); var username = Zotero.Users.getCurrentUsername() || Zotero.Prefs.get('sync.server.username') || " "; - var apiKey = yield Zotero.Sync.Data.Local.getAPIKey(); + var apiKey = await Zotero.Sync.Data.Local.getAPIKey(); this.displayFields(apiKey ? username : ""); var pass = Zotero.Sync.Runner.getStorageController('webdav').password; @@ -54,7 +54,7 @@ Zotero_Preferences.Sync = { if (apiKey) { try { - var keyInfo = yield Zotero.Sync.Runner.checkAccess( + var keyInfo = await Zotero.Sync.Runner.checkAccess( Zotero.Sync.Runner.getAPIClient({apiKey}), {timeout: 5000} ); @@ -75,14 +75,16 @@ Zotero_Preferences.Sync = { } } } - - this.initResetPane(); - }), + + document.getElementById('storage-url-prefix').addEventListener('synctopreference', () => { + this.unverifyStorageServer(); + }); + }, displayFields: function (username) { document.getElementById('sync-unauthorized').hidden = !!username; document.getElementById('sync-authorized').hidden = !username; - document.getElementById('sync-reset-tab').disabled = !username; + document.getElementById('sync-reset').hidden = !username; document.getElementById('sync-username').value = username; document.getElementById('sync-password').value = ''; document.getElementById('sync-username-textbox').value = Zotero.Prefs.get('sync.server.username'); @@ -235,7 +237,7 @@ Zotero_Preferences.Sync = { showLibrariesToSyncDialog: function() { var io = {}; - window.openDialog('chrome://zotero/content/preferences/librariesToSync.xul', + window.openDialog('chrome://zotero/content/preferences/librariesToSync.xhtml', "zotero-preferences-librariesToSyncDialog", "chrome,modal,centerscreen", io); }, @@ -284,7 +286,7 @@ Zotero_Preferences.Sync = { div.innerHTML = ""; } else { - div = document.createElementNS("http://www.w3.org/1999/xhtml", 'div'); + div = document.createElement('div'); div.className = "row"; div.addEventListener('dblclick', () => { this.toggleLibraryToSync(index); @@ -294,7 +296,7 @@ Zotero_Preferences.Sync = { for (let column of columns) { if (column.dataKey === 'checked') { - let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span'); + let span = document.createElement('span'); span.className = `cell ${column.className}`; if (row.id != 'loading') { span.innerText = row.checked ? this.checkmarkChar : this.noChar; @@ -397,8 +399,8 @@ Zotero_Preferences.Sync = { updateStorageSettingsUI: Zotero.Promise.coroutine(function* () { this.unverifyStorageServer(); - var protocol = document.getElementById('pref-storage-protocol').value; - var enabled = document.getElementById('pref-storage-enabled').value; + var protocol = Zotero.Prefs.get('sync.storage.protocol'); + var enabled = Zotero.Prefs.get('sync.storage.enabled'); var storageSettings = document.getElementById('storage-settings'); var protocolMenu = document.getElementById('storage-protocol'); @@ -423,7 +425,7 @@ Zotero_Preferences.Sync = { updateStorageSettingsGroupsUI: function () { setTimeout(() => { - var enabled = document.getElementById('pref-storage-groups-enabled').value; + var enabled = Zotero.Prefs.get('sync.storage.groups.enabled'); document.getElementById('storage-groups-download-mode').disabled = !enabled; this.updateStorageTerms(); }); @@ -433,9 +435,9 @@ Zotero_Preferences.Sync = { updateStorageTerms: function () { var terms = document.getElementById('storage-terms'); - var libraryEnabled = document.getElementById('pref-storage-enabled').value; - var storageProtocol = document.getElementById('pref-storage-protocol').value; - var groupsEnabled = document.getElementById('pref-storage-groups-enabled').value; + var libraryEnabled = Zotero.Prefs.get('sync.storage.enabled'); + var storageProtocol = Zotero.Prefs.get('sync.storage.protocol'); + var groupsEnabled = Zotero.Prefs.get('sync.storage.groups.enabled'); terms.hidden = !((libraryEnabled && storageProtocol == 'zotero') || groupsEnabled); }, @@ -450,16 +452,14 @@ Zotero_Preferences.Sync = { onStorageSettingsChange: Zotero.Promise.coroutine(function* () { // Clean URL - var urlPref = document.getElementById('pref-storage-url'); - urlPref.value = urlPref.value.replace(/(^https?:\/\/|\/zotero\/?$|\/$)/g, ''); + Zotero.Prefs.set('sync.storage.url', Zotero.Prefs.get('sync.storage.url') + .replace(/(^https?:\/\/|\/zotero\/?$|\/$)/g, '')); - var oldProtocol = document.getElementById('pref-storage-protocol').value; - var oldEnabled = document.getElementById('pref-storage-enabled').value; + var oldProtocol = Zotero.Prefs.get('sync.storage.protocol'); yield Zotero.Promise.delay(1); - var newProtocol = document.getElementById('pref-storage-protocol').value; - var newEnabled = document.getElementById('pref-storage-enabled').value; + var newProtocol = Zotero.Prefs.get('sync.storage.protocol'); if (oldProtocol != newProtocol) { yield Zotero.Sync.Storage.Local.resetAllSyncStates(Zotero.Libraries.userLibraryID); @@ -634,17 +634,18 @@ Zotero_Preferences.Sync = { this.onResetLibraryChange(parseInt(event.target.value)); } this.onResetLibraryChange(Zotero.Libraries.userLibraryID); + document.querySelectorAll('#sync-reset-radiogroup radio') + .forEach(radio => radio.removeAttribute('selected')); var libraries = Zotero.Libraries.getAll() .filter(x => x.libraryType == 'user' || x.libraryType == 'group'); - Zotero.Utilities.Internal.buildLibraryMenuHTML(libraryMenu, libraries); + Zotero.Utilities.Internal.buildLibraryMenu(libraryMenu, libraries); // Disable read-only libraries, at least until there are options that make sense for those - Array.from(libraryMenu.querySelectorAll('option')) + Array.from(libraryMenu.querySelectorAll('menuitem')) .filter(x => x.getAttribute('data-editable') == 'false') .forEach(x => x.disabled = true); - var list = document.getElementById('sync-reset-list'); - for (let li of document.querySelectorAll('#sync-reset-list li')) { - li.addEventListener('click', function (event) { + for (let row of document.querySelectorAll('#sync-reset-radiogroup > *')) { + row.addEventListener('click', function (event) { // Ignore clicks if disabled if (this.hasAttribute('disabled')) { event.stopPropagation(); @@ -659,19 +660,19 @@ Zotero_Preferences.Sync = { onResetLibraryChange: function (libraryID) { var library = Zotero.Libraries.get(libraryID); var section = document.getElementById('reset-file-sync-history'); - var input = section.querySelector('input'); + var radio = section.querySelector('radio'); if (library.filesEditable) { section.removeAttribute('disabled'); - input.disabled = false; + radio.disabled = false; } else { section.setAttribute('disabled', ''); // If radio we're disabling is already selected, select the first one in the list // instead - if (input.checked) { - document.querySelector('#sync-reset-list li:first-child input').checked = true; + if (radio.selected) { + document.querySelector('#sync-reset-radiogroup > div:first-child radio').selected = true; } - input.disabled = true; + radio.disabled = true; } }, @@ -690,14 +691,10 @@ Zotero_Preferences.Sync = { return; } - var libraryID = parseInt( - Array.from(document.querySelectorAll('#sync-reset-library-menu option')) - .filter(x => x.selected)[0] - .value - ); + var libraryID = document.getElementById('sync-reset-library-menu').value; var library = Zotero.Libraries.get(libraryID); - var action = Array.from(document.querySelectorAll('#sync-reset-list input[name=sync-reset-radiogroup]')) - .filter(x => x.checked)[0] + var action = Array.from(document.querySelectorAll('#sync-reset-radiogroup radio')) + .filter(x => x.selected)[0] .getAttribute('value'); switch (action) { diff --git a/chrome/content/zotero/preferences/preferences_sync.xhtml b/chrome/content/zotero/preferences/preferences_sync.xhtml new file mode 100644 index 0000000000..a105ae615e --- /dev/null +++ b/chrome/content/zotero/preferences/preferences_sync.xhtml @@ -0,0 +1,249 @@ + + + + &zotero.preferences.settings; + + + + &zotero.preferences.sync.syncServer; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &zotero.preferences.sync.syncServer; + + + + + + + + + + + + + + + + + + + + + + + + + + + &zotero.preferences.sync.fileSyncing; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &zotero.preferences.sync.fileSyncing.tos1; + + &zotero.preferences.period; + + + + + + + + &zotero.preferences.sync.reset; + + + diff --git a/chrome/content/zotero/preferences/preferences_sync_reset.xhtml b/chrome/content/zotero/preferences/preferences_sync_reset.xhtml new file mode 100644 index 0000000000..ae566610af --- /dev/null +++ b/chrome/content/zotero/preferences/preferences_sync_reset.xhtml @@ -0,0 +1,40 @@ + + &zotero.preferences.sync.reset; + + + &zotero.preferences.sync.reset.warning1;&zotero.preferences.sync.reset.warning2;&zotero.preferences.sync.reset.warning3; + + + + Library: + + + + + + + + + &zotero.preferences.sync.reset.restoreToServer.desc; + + + + + + + &zotero.preferences.sync.reset.resetFileSyncHistory.desc; + + + + &zotero.preferences.sync.reset.button; + + diff --git a/chrome/content/zotero/preferences/quickCopySiteEditor.xul b/chrome/content/zotero/preferences/quickCopySiteEditor.xhtml similarity index 83% rename from chrome/content/zotero/preferences/quickCopySiteEditor.xul rename to chrome/content/zotero/preferences/quickCopySiteEditor.xhtml index f22172c47a..7eabe3130d 100644 --- a/chrome/content/zotero/preferences/quickCopySiteEditor.xul +++ b/chrome/content/zotero/preferences/quickCopySiteEditor.xhtml @@ -35,11 +35,13 @@ %common; ]> - + + @@ -47,18 +49,18 @@ - + - + - + @@ -69,17 +71,20 @@ this.checked ? 'html' : '', null, io.translators - )"/> + )" + native="true"/> + diff --git a/chrome/content/zotero/tools/cslpreview.xul b/chrome/content/zotero/tools/cslpreview.xhtml similarity index 100% rename from chrome/content/zotero/tools/cslpreview.xul rename to chrome/content/zotero/tools/cslpreview.xhtml diff --git a/chrome/content/zotero/xpcom/plugins.js b/chrome/content/zotero/xpcom/plugins.js index a830319c40..e63991090d 100644 --- a/chrome/content/zotero/xpcom/plugins.js +++ b/chrome/content/zotero/xpcom/plugins.js @@ -27,6 +27,7 @@ Zotero.Plugins = new function () { var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); var scopes = new Map(); + var observers = new Set(); const REASONS = { APP_STARTUP: 1, @@ -135,7 +136,21 @@ Zotero.Plugins = new function () { Zotero.logError(`Error running bootstrap method '${method}' on ${id}`); Zotero.logError(e); } - + + for (let observer of observers) { + if (observer[method]) { + try { + let maybePromise = observer[method](params, reason); + if (maybePromise && maybePromise.then) { + await maybePromise; + } + } + catch (e) { + Zotero.logError(e); + } + } + } + // TODO: Needed? /*if (method == "startup") { activeAddon.startupPromise = Promise.resolve(result); @@ -159,6 +174,42 @@ Zotero.Plugins = new function () { }; + this.getName = async function (id) { + var addon = await AddonManager.getAddonByID(id); + return addon.name; + }; + + + /** + * @param {String} id + * @param {Number} idealSize In logical pixels (scaled automatically on hiDPI displays) + * @returns {Promise} + */ + this.getIconURI = async function (id, idealSize) { + var addon = await AddonManager.getAddonByID(id); + return AddonManager.getPreferredIconURL(addon, idealSize, Services.appShell.hiddenDOMWindow); + }; + + + /** + * Add an observer to be notified of lifecycle events on all plugins. + * + * @param observer + * @param {Function} [observer.install] + * @param {Function} [observer.startup] + * @param {Function} [observer.shutdown] + * @param {Function} [observer.uninstall] + */ + this.addObserver = function (observer) { + observers.add(observer); + }; + + + this.removeObserver = function (observer) { + observers.delete(observer); + }; + + this._addonObserver = { initialized: false, diff --git a/chrome/content/zotero/xpcom/preferencePanes.js b/chrome/content/zotero/xpcom/preferencePanes.js new file mode 100644 index 0000000000..87b47c29a4 --- /dev/null +++ b/chrome/content/zotero/xpcom/preferencePanes.js @@ -0,0 +1,164 @@ +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2022 Corporation for Digital Scholarship + Vienna, Virginia, USA + http://zotero.org + + This file is part of Zotero. + + Zotero is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Zotero is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with Zotero. If not, see . + + ***** END LICENSE BLOCK ***** +*/ + +/** + * Manages preference panes. + */ +Zotero.PreferencePanes = { + builtInPanes: Object.freeze([ + { + id: 'zotero-prefpane-general', + label: 'zotero.preferences.prefpane.general', + image: 'chrome://zotero/skin/prefs-general.png', + src: 'chrome://zotero/content/preferences/preferences_general.xhtml', + scripts: ['chrome://zotero/content/preferences/preferences_general.js'], + defaultXUL: true, + }, + { + id: 'zotero-prefpane-sync', + label: 'zotero.preferences.prefpane.sync', + image: 'chrome://zotero/skin/prefs-sync.png', + src: 'chrome://zotero/content/preferences/preferences_sync.xhtml', + scripts: ['chrome://zotero/content/preferences/preferences_sync.js'], + defaultXUL: true, + }, + { + id: 'zotero-prefpane-export', + label: 'zotero.preferences.prefpane.export', + image: 'chrome://zotero/skin/prefs-export.png', + src: 'chrome://zotero/content/preferences/preferences_export.xhtml', + scripts: ['chrome://zotero/content/preferences/preferences_export.js'], + defaultXUL: true, + }, + { + id: 'zotero-prefpane-cite', + label: 'zotero.preferences.prefpane.cite', + image: 'chrome://zotero/skin/prefs-styles.png', + src: 'chrome://zotero/content/preferences/preferences_cite.xhtml', + scripts: ['chrome://zotero/content/preferences/preferences_cite.js'], + defaultXUL: true, + }, + { + id: 'zotero-prefpane-advanced', + label: 'zotero.preferences.prefpane.advanced', + image: 'chrome://zotero/skin/prefs-advanced.png', + src: 'chrome://zotero/content/preferences/preferences_advanced.xhtml', + scripts: ['chrome://zotero/content/preferences/preferences_advanced.js'], + defaultXUL: true, + }, + { + id: 'zotero-subpane-reset-sync', + parent: 'zotero-prefpane-sync', + label: 'zotero.preferences.subpane.resetSync', + src: 'chrome://zotero/content/preferences/preferences_sync_reset.xhtml', + scripts: ['chrome://zotero/content/preferences/preferences_sync.js'], + defaultXUL: true, + } + ]), + + pluginPanes: [], + + /** + * Register a pane to be displayed in the preferences. The pane XHTML (`src`) + * is loaded as a fragment, not a full document, with XUL as the default + * namespace and (X)HTML tags available under `html:`. + * + * The pane will be unregistered automatically when the registering plugin + * shuts down. + * + * @param {Object} options + * @param {String} options.id Represents the pane and must be unique + * @param {String} options.pluginID ID of the plugin registering the pane + * @param {String} [options.parent] ID of parent pane (if provided, pane is hidden from the sidebar) + * @param {String} [options.label] Displayed as the pane's label in the sidebar. + * If not provided, the plugin's name is used + * @param {String} [options.image] URI of an icon to be displayed in the navigation sidebar. + * If not provided, the plugin's icon (from manifest.json) is used + * @param {String} options.src URI of an XHTML fragment + * @param {String[]} [options.extraDTD] Array of URIs of DTD files to use for parsing the XHTML fragment + * @param {String[]} [options.scripts] Array of URIs of scripts to load along with the pane + * @return {Promise} + */ + register: async function (options) { + if (!options.id || !options.pluginID || !options.src) { + throw new Error('id, pluginID, and src must be provided'); + } + if (this.builtInPanes.some(p => p.id === options.id) + || this.pluginPanes.some(p => p.id === options.id)) { + throw new Error(`Pane with ID ${options.id} already registered`); + } + + let addPaneOptions = { + id: options.id, + pluginID: options.pluginID, + parent: options.parent, + rawLabel: options.label || await Zotero.Plugins.getName(options.pluginID), + image: options.image || await Zotero.Plugins.getIconURI(options.pluginID, 24), + src: options.src, + extraDTD: options.extraDTD, + scripts: options.scripts, + defaultXUL: false, + }; + + this.pluginPanes.push(addPaneOptions); + Zotero.debug(`Plugin ${options.pluginID} registered preference pane ${options.id} ("${addPaneOptions.rawLabel}")`); + this._refreshPreferences(); + this._ensureObserverAdded(); + }, + + /** + * Called automatically on plugin shutdown. + * + * @param {String} id + */ + unregister: function (id) { + this.pluginPanes = this.pluginPanes.filter(p => p.id !== id); + this._refreshPreferences(); + }, + + _refreshPreferences() { + for (let win of Services.wm.getEnumerator("zotero:pref")) { + win.Zotero_Preferences.initPanes(); + } + }, + + _ensureObserverAdded() { + if (this._observerAdded) { + return; + } + + Zotero.Plugins.addObserver({ + shutdown({ id: pluginID }) { + let beforeLength = this.pluginPanes.length; + this.pluginPanes = this.pluginPanes.filter(pane => pane.pluginID !== pluginID); + if (this.pluginPanes.length !== beforeLength) { + Zotero.debug(`Preference panes registered by plugin ${pluginID} unregistered due to shutdown`); + this._refreshPreferences(); + } + } + }); + this._observerAdded = true; + } +}; diff --git a/chrome/content/zotero/xpcom/style.js b/chrome/content/zotero/xpcom/style.js index ef23d4b281..ed5c6429b3 100644 --- a/chrome/content/zotero/xpcom/style.js +++ b/chrome/content/zotero/xpcom/style.js @@ -564,7 +564,7 @@ Zotero.Styles = new function() { for (let i=0; i + @@ -83,6 +84,7 @@ + diff --git a/chrome/skin/default/zotero/itemPane.css b/chrome/skin/default/zotero/itemPane.css index 3cc182f41c..b39253e2ae 100644 --- a/chrome/skin/default/zotero/itemPane.css +++ b/chrome/skin/default/zotero/itemPane.css @@ -37,6 +37,7 @@ .zotero-view-item > tabpanel > * { overflow: auto; + min-width: 0; } .zotero-view-item:not([selectedIndex="0"]) > tabpanel > * { diff --git a/chrome/skin/default/zotero/preferences.css b/chrome/skin/default/zotero/preferences.css index 57ecf271e4..9f58c74921 100644 --- a/chrome/skin/default/zotero/preferences.css +++ b/chrome/skin/default/zotero/preferences.css @@ -1,3 +1,142 @@ +#prefs-content { + min-height: 600px; + overflow-y: scroll; +} + +#prefs-content > * { + margin: 5px 15px; +} + +#prefs-search-container { + height: 40px; + padding-right: 8px; +} + +#prefs-search { + width: 16em; + -moz-box-pack: end; +} + +#prefs-navigation { + width: 180px; + appearance: none; + background: transparent; + margin: 6px; +} + +#prefs-navigation > richlistitem { + padding: 1em; + border-radius: 5px; + -moz-box-align: center; + font-size: 1.1em; + transition: background-color 0.2s ease; +} + +#prefs-navigation > richlistitem[selected="true"] { + background-color: highlight; + color: highlighttext; +} + +#prefs-navigation > richlistitem:not([selected="true"]):hover { + background-color: color-mix(in srgb, transparent 60%, highlight); +} + +#prefs-navigation > richlistitem image { + width: 24px; +} + +#prefs-navigation > hr { + margin: 8px 0; + border: none; + border-top: 1px solid lightgray; +} + +#prefs-subpane-back-button { + appearance: none; + font-size: 32px; + min-width: auto; + padding: 8px; + color: -moz-dialogtext; + background-color: transparent; + transition: color 0.1s ease; +} + +#prefs-subpane-back-button:hover { + color: color-mix(in srgb, -moz-dialogtext 30%, SelectedItem); +} + +#prefs-subpane-back-button:hover:active { + color: SelectedItem; +} + +h1 { + margin: 0 0 4px 0; + font-size: 1.3em; + font-weight: normal; +} + +.main-section { + margin-bottom: 16px; +} + +.main-section:not([hidden]):not(.hidden-by-search) ~ .main-section { + padding-top: 16px; + margin-top: 32px; + border-top: 1px solid lightgray; +} + +groupbox > label > h2, groupbox > * > label > h2 { + border-bottom: none; + font-weight: bold; +} + +/* Approach to search tooltips (mostly borrowed from Fx prefs): */ +.search-tooltip-parent { + position: relative; +} + +.search-tooltip { + min-width: 20px; + max-width: 200px; + position: absolute; + bottom: 24px; + background-color: #ffe900; + border: 1px solid rgba(0, 0, 0, 40%); + padding: 2px 8px; + text-align: center; +} + +.search-tooltip::before, .search-tooltip::after { + content: ''; + position: absolute; + width: 0; + height: 0; + left: calc(50% - 3px); + border-right: 6px solid transparent; + border-left: 6px solid transparent; +} + +.search-tooltip::before { + top: calc(100% + 1px); + border-top: 6px solid rgba(0, 0, 0, 40%); +} + +.search-tooltip::after { + top: 100%; + border-top: 6px solid #ffe900; +} + +.search-tooltip > span { + user-select: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.hidden-by-search { + display: none; +} + prefwindow .chromeclass-toolbar { display: -moz-box !important; /* Ignore toolbar collapse button on OS X */ @@ -23,6 +162,12 @@ checkbox[disabled="true"] { color: gray !important; } +menulist[disabled="true"] { + opacity: 75%; + /* Disable default hover styling */ + pointer-events: none; +} + #export-citePaperJournalArticleURL { font-size: .85em; @@ -41,17 +186,17 @@ description label[class=zotero-text-link], label[class=zotero-text-link] margin: 0; } -/* General pane */ -grid row:not(:first-child) -{ - margin-top: .3em; +.form-grid { + display: grid; + grid-template-columns: max-content 1fr; + row-gap: .3em; } -grid row hbox:first-child -{ +.form-grid > :nth-child(odd) { -moz-box-pack: end; /* Right-justify left column */ } +/* General pane */ #zotero-prefpane-general .statusLine { margin-left: .75em; } @@ -69,30 +214,36 @@ grid row hbox:first-child */ /* Settings tab */ -#zotero-prefpane-sync row, #zotero-prefpane-sync row hbox -{ +#zotero-prefpane-sync .form-grid { + align-items: center; +} + +#zotero-prefpane-sync .form-grid > hbox { -moz-box-align: center; } -#zotero-prefpane-sync row label:first-child + +#zotero-prefpane-sync .form-grid > label:nth-child(odd) { text-align: right; } -#zotero-prefpane-sync row hbox + +#zotero-prefpane-sync .form-grid > hbox { margin-left: 4px; } -#zotero-prefpane-sync row hbox label:first-child, -#zotero-prefpane-sync row hbox menulist:first-child + +#zotero-prefpane-sync .form-grid > hbox label:first-child, +#zotero-prefpane-sync .form-grid > hbox menulist:first-child { margin-left: 0; margin-right: 0; } -#zotero-prefpane-sync row hbox textbox +#zotero-prefpane-sync .form-grid > hbox textbox { margin-left: 3px; margin-right: 3px; } -#zotero-prefpane-sync row hbox label:last-child +#zotero-prefpane-sync .form-grid > hbox label:last-child { margin-left: 0; margin-right: 10px; @@ -166,7 +317,7 @@ grid row hbox:first-child margin-top: 1em; } -#sync-reset-library-menu-container { +#sync-reset-library-menu-container > label { font-weight: bold; font-size: 15px; } @@ -178,47 +329,45 @@ grid row hbox:first-child height: 1.6em; } -#sync-reset-list { - margin: 0; - padding: 0; - height: 9em; +#sync-reset-radiogroup { + margin-bottom: 1.4em; } -#sync-reset-list li { +#sync-reset-radiogroup > div { margin: 0; - margin-top: 1.6em; padding: 0; list-style: none; - height: 2.8em; } /* Allow a click between lines to select the radio */ -#sync-reset-list li label { +#sync-reset-radiogroup > div label { display: block; } -#sync-reset-list li:first-child { +#sync-reset-radiogroup > div:first-child { margin-top: 1.4em; } -#sync-reset-list li .sync-reset-option-name { +#sync-reset-radiogroup > div radio .radio-label { font-weight: bold; - display: block; font-size: 15px; margin-bottom: .2em; } -#sync-reset-list li .sync-reset-option-desc { +#sync-reset-radiogroup > div .sync-reset-option-desc { font-size: 12px; } -#sync-reset-list li input { +#sync-reset-radiogroup > div radio { float: left; margin-top: 1em; +} + +#sync-reset-radiogroup > div radio .radio-check { margin-right: 1.05em; } -#sync-reset-list li[disabled] span { +#sync-reset-radiogroup > div[disabled] span { color: gray; } @@ -235,7 +384,7 @@ grid row hbox:first-child margin-top: 0 !important; } -#fulltext-stats row > label:first-child +#fulltext-stats > .form-grid > label:nth-child(odd) { text-align: right; } @@ -288,28 +437,18 @@ grid row hbox:first-child } /* Shortcut Keys pane */ -#zotero-prefpane-advanced-keys-tab row -{ - -moz-box-align: center; -} - -#zotero-prefpane-advanced-keys-tab textbox +#zotero-prefpane-advanced-keys-tab input { margin-left: -1px; } -treechildren::-moz-tree-checkbox { - /* unchecked checkbox treecells. This style MUST come before treechildren::-moz-tree-checkbox(checked) otherwise it won't take effect. */ - list-style-image: none; -} - -treechildren::-moz-tree-checkbox(checked){ - /* css for checked cells. cbox-check.gif isn't available in Firefox 1, 2, and 3 on Mac OS X, so you should specify a URL to an image - in your extension or elsewhere. */ - list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif"); -} - /* Advanced pane */ +#zotero-keys-grid { + display: grid; + align-items: center; + grid-template-columns: 1fr max-content max-content; +} + #command-line-data-dir description { font-size: 12px; cursor: text; @@ -333,6 +472,17 @@ treechildren::-moz-tree-checkbox(checked){ align-items: center; } +#baseAttachmentPath { + padding-inline-start: 24px; + /* Background set in JS */ + background: none; + background-repeat: no-repeat; + background-size: 16px; + background-position: center left 4px; + appearance: none; + border: none; +} + /* BEGIN 2X BLOCK -- DO NOT EDIT MANUALLY -- USE 2XIZE */ @media (min-resolution: 1.25dppx) { diff --git a/chrome/skin/default/zotero/prefs-search.png b/chrome/skin/default/zotero/prefs-search.png deleted file mode 100644 index 5594785d10..0000000000 Binary files a/chrome/skin/default/zotero/prefs-search.png and /dev/null differ diff --git a/chrome/skin/default/zotero/zotero.css b/chrome/skin/default/zotero/zotero.css index fc45350334..3b03d0f450 100644 --- a/chrome/skin/default/zotero/zotero.css +++ b/chrome/skin/default/zotero/zotero.css @@ -52,14 +52,13 @@ richlistitem, richlistitem > * { text-overflow: ellipsis; } -groupbox > label > h2 { +groupbox > label > h2, groupbox > * > label > h2 { font-size: 14px; margin-top: 1em; margin-bottom: .3em; } label.zotero-text-link { - -moz-binding: url('chrome://zotero/content/bindings/text-link.xml#text-link'); -moz-user-focus: normal; color: -moz-nativehyperlinktext; text-decoration: underline; diff --git a/components/zotero-service.js b/components/zotero-service.js index 640f252cd6..df6dc811fa 100644 --- a/components/zotero-service.js +++ b/components/zotero-service.js @@ -61,6 +61,7 @@ const xpcomFilesAll = [ 'translate/src/tlds', 'translation/translate_firefox', 'isbn', + 'preferencePanes', ]; /** XPCOM files to be loaded only for local translation and DB access **/ diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js index 834c2069e3..962b3a2566 100644 --- a/defaults/preferences/zotero.js +++ b/defaults/preferences/zotero.js @@ -117,7 +117,6 @@ pref("extensions.zotero.export.displayCharsetOption", true); pref("extensions.zotero.export.citePaperJournalArticleURL", false); pref("extensions.zotero.cite.automaticJournalAbbreviations", true); pref("extensions.zotero.cite.useCiteprocRs", false); -pref("extensions.zotero.import.charset", "auto"); pref("extensions.zotero.import.createNewCollection.fromFileOpenHandler", true); pref("extensions.zotero.rtfScan.lastInputFile", ""); pref("extensions.zotero.rtfScan.lastOutputFile", ""); diff --git a/test/content/support.js b/test/content/support.js index f6b67c663c..5a064505d4 100644 --- a/test/content/support.js +++ b/test/content/support.js @@ -110,7 +110,7 @@ var loadZoteroPane = async function (win) { var loadPrefPane = Zotero.Promise.coroutine(function* (paneName) { var id = 'zotero-prefpane-' + paneName; - var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xul", { + var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xhtml", { pane: id }); var doc = win.document; diff --git a/test/tests/bibliographyTest.js b/test/tests/bibliographyTest.js index 26bf8265c6..bf79a18be6 100644 --- a/test/tests/bibliographyTest.js +++ b/test/tests/bibliographyTest.js @@ -19,7 +19,7 @@ describe("Create Bibliography Dialog", function () { var deferred = Zotero.Promise.defer(); var called = false; waitForWindow("chrome://zotero/content/bibliography.xhtml", function (dialog) { - waitForWindow("chrome://zotero/content/preferences/preferences.xul", function (window) { + waitForWindow("chrome://zotero/content/preferences/preferences.xhtml", function (window) { // Wait for pane switch (async function () { do { diff --git a/test/tests/preferences_advancedTest.js b/test/tests/preferences_advancedTest.js index f54321cadc..6544a3f727 100644 --- a/test/tests/preferences_advancedTest.js +++ b/test/tests/preferences_advancedTest.js @@ -16,7 +16,7 @@ describe("Advanced Preferences", function () { describe("Debug Output", function () { it("should log output and submit to server", function* () { - var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xul", { + var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xhtml", { pane: 'zotero-prefpane-advanced', tabIndex: 0 }); @@ -64,7 +64,7 @@ describe("Advanced Preferences", function () { describe("Files & Folders", function () { describe("Linked Attachment Base Directory", function () { var setBaseDirectory = Zotero.Promise.coroutine(function* (basePath) { - var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xul", { + var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xhtml", { pane: 'zotero-prefpane-advanced', tabIndex: 1 }); @@ -89,7 +89,7 @@ describe("Advanced Preferences", function () { }); var clearBaseDirectory = Zotero.Promise.coroutine(function* (basePath) { - var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xul", { + var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xhtml", { pane: 'zotero-prefpane-advanced', tabIndex: 1 }); diff --git a/test/tests/preferences_syncTest.js b/test/tests/preferences_syncTest.js index f6574a6674..de73bc157a 100644 --- a/test/tests/preferences_syncTest.js +++ b/test/tests/preferences_syncTest.js @@ -2,7 +2,7 @@ describe("Sync Preferences", function () { var win, doc; before(function* () { // Load prefs with sync pane - win = yield loadWindow("chrome://zotero/content/preferences/preferences.xul", { + win = yield loadWindow("chrome://zotero/content/preferences/preferences.xhtml", { pane: 'zotero-prefpane-sync', tabIndex: 0 });