fx-compat: Restore guidance panels as CE

Fixes #2711.
This commit is contained in:
Abe Jellinek 2022-07-20 11:12:52 -04:00
parent 8bfc42e8d8
commit 26a2398399
10 changed files with 227 additions and 264 deletions

View file

@ -1,242 +0,0 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright © 2011 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
-->
<bindings xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="guidancepanel">
<resources>
<stylesheet src="chrome://zotero/skin/bindings/guidancepanel.css"/>
</resources>
<implementation>
<property name="panel" onget="return document.getAnonymousNodes(this)[0]"/>
<!--
@param {Object} [options]
@param {String} [options.text] - Text to use in place of firstRunGuidance.<about>
@param {DOMElement} [options.forEl] - Anchor node
@param {Boolean} [options.force] - Show even if already shown, and don't update
firstRunGuidanceShown.<about> pref
-->
<method name="show">
<parameter name="options"/>
<body>
<![CDATA[
Components.utils.import("resource://gre/modules/Services.jsm");
if(!Zotero.Prefs.get("firstRunGuidance")) return;
options = options || {};
let text = options.text;
let useLastText = options.useLastText || false;
let forEl = options.forEl || document.getElementById(this.getAttribute("for"));
let force = options.force || false;
if (!forEl) return;
// Don't show two panels at once
if (Zotero.guidanceBeingShown) {
return;
}
var about = this.getAttribute("about");
var pref = false;
if (about) {
pref = "firstRunGuidanceShown." + about;
let shown = false;
try {
shown = Zotero.Prefs.get(pref);
} catch(e) {};
if (shown && !force) {
return;
}
}
Zotero.guidanceBeingShown = true;
var x = this.getAttribute("x"),
y = this.getAttribute("y"),
position = this.getAttribute("position");
if (!useLastText) {
if (!text) {
text = Zotero.getString("firstRunGuidance." + about);
}
text = text.split("\n");
var descriptionNode = this.id('panel-description');
while (descriptionNode.hasChildNodes()) {
descriptionNode.removeChild(descriptionNode.firstChild);
}
while(text.length) {
var textLine = text.shift();
descriptionNode.appendChild(document.createTextNode(textLine));
if(text.length) descriptionNode.appendChild(document.createElementNS(
"http://www.w3.org/1999/xhtml", "br"));
}
}
this.setAttribute(
"onpopuphidden",
"this.hidden = true; "
+ "Zotero.guidanceBeingShown = false; "
+ (this.getAttribute("onpopuphidden") || "")
);
this._initNavButton('back', options.back);
this._initNavButton('forward', options.forward);
var self = this;
var f = function() {
if (self.hasAttribute("foregroundonly") && Services.ww.activeWindow != window) return;
// Hide panel if content page changes
if (self.getAttribute('hideonpagechange') == "true") {
let popupShownListener = function () {
self.removeEventListener("popupshown", popupShownListener);
let appcontent = document.getElementById('appcontent');
let pageHideListener = function (event) {
var doc = event.originalTarget;
if(!(doc instanceof HTMLDocument)) return;
var rootDoc = doc.defaultView.top.document;
// Don't hide when frames and special URLs are unloaded
if (rootDoc != doc || !rootDoc.location.href.startsWith('http')) {
return;
}
appcontent.removeEventListener("pagehide", pageHideListener);
self.hide();
};
appcontent.addEventListener("pagehide", pageHideListener);
};
self.addEventListener("popupshown", popupShownListener);
}
self.hidden = false;
self.panel.openPopup(forEl, position ? position : "after_start",
x ? parseInt(x, 10) : 0, y ? parseInt(y, 10) : 0, false, false, null);
if (pref) {
Zotero.Prefs.set(pref, true);
}
};
if(this.hasAttribute("delay") && !force) {
window.setTimeout(f, this.getAttribute("delay"));
} else {
f();
}
if (this.getAttribute("noautohide") == 'true'
&& !this.hasAttribute('forward')) {
let listener = function () {
this.panel.removeEventListener("click", listener);
this.panel.hidePopup();
}.bind(this);
this.panel.addEventListener("click", listener);
}
]]>
</body>
</method>
<method name="hide">
<body>
<![CDATA[
document.getAnonymousNodes(this)[0].hidePopup();
]]>
</body>
</method>
<method name="_initNavButton">
<parameter name="dir"/>
<parameter name="nextID"/>
<body><![CDATA[
if (!nextID) {
nextID = this.getAttribute(dir);
}
if (!nextID) {
return;
}
var nextElem = document.getElementById(nextID);
button = this.id(dir + '-button');
button.hidden = false;
var target;
// If there's a forward action and no back action, the whole panel triggers
// the forward in noautohide mode
if (dir == 'forward' && !this.hasAttribute('back')
&& this.getAttribute('noautohide') == 'true') {
target = this.panel;
}
else {
target = button;
}
var listener = function (event) {
target.removeEventListener("click", listener);
this.hide();
var data = {
force: true
};
// Point the next panel back to this one
data[dir == 'back' ? 'forward' : 'back'] = this.getAttribute('id');
// When going backwards, don't regenerate text
if (dir == 'back') {
data.useLastText = true;
}
nextElem.show(data);
event.stopPropagation();
}.bind(this);
target.addEventListener("click", listener);
]]></body>
</method>
<method name="id">
<parameter name="id"/>
<body><![CDATA[
return document.getAnonymousNodes(this)[0].getElementsByAttribute('anonid', id)[0];
]]></body>
</method>
</implementation>
<content>
<xul:panel type="arrow" align="top" xbl:inherits="noautohide">
<xul:stack>
<xul:hbox align="center">
<xul:image src="chrome://zotero/skin/zotero-new-z-48px.png" style="margin-right: 10px; width: 48px; height: 48px;"/>
<xul:description anonid="panel-description" flex="1"></xul:description>
</xul:hbox>
<xul:hbox anonid="close-button-box">
<xul:toolbarbutton anonid="close-button" class="close-icon" hidden="true"/>
</xul:hbox>
<xul:hbox anonid="nav-buttons">
<xul:toolbarbutton anonid="back-button" oncommand="hide()" hidden="true"/>
<xul:toolbarbutton anonid="forward-button" oncommand="hide()" hidden="true"/>
</xul:hbox>
</xul:stack>
</xul:panel>
</content>
</binding>
</bindings>

View file

@ -0,0 +1,202 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2022 Corporation for Digital Scholarship
Vienna, Virginia, USA
https://www.zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
"use strict";
{
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/base.js", this);
class GuidancePanel extends XULElementBase {
content = MozXULElement.parseXULToFragment(`
<panel type="arrow" align="top">
<stack>
<hbox align="center">
<image src="chrome://zotero/skin/zotero-new-z-48px.png" style="margin-right: 10px; width: 48px; height: 48px;"></image>
<label id="panel-description" flex="1"></label>
</hbox>
<hbox id="close-button-box">
<toolbarbutton id="close-button" class="close-icon" hidden="true"></toolbarbutton>
</hbox>
<hbox id="nav-buttons">
<toolbarbutton id="back-button" hidden="true"></toolbarbutton>
<toolbarbutton id="forward-button" hidden="true"></toolbarbutton>
</hbox>
</stack>
</panel>
`);
stylesheets = ['chrome://global/skin/', 'chrome://zotero-platform/content/guidancePanel.css'];
get panel() {
return this.shadowRoot.querySelector('panel');
}
init() {
this.panel.addEventListener('popupshown', () => {
Zotero.guidanceBeingShown = true;
});
this.panel.addEventListener('popuphidden', () => {
Zotero.guidanceBeingShown = false;
});
if (this.getAttribute("noautohide") == 'true'
&& !this.hasAttribute('forward')) {
let listener = () => {
this.panel.removeEventListener("click", listener);
this.panel.hidePopup();
};
this.panel.addEventListener("click", listener);
}
}
/**
* @param {Object} [options]
* @param {String} [options.text] Text to use in place of firstRunGuidance.<about>
* @param {DOMElement} [options.forEl] Anchor node
* @param {Boolean} [options.force] Show even if already shown
*/
show(options) {
Components.utils.import("resource://gre/modules/Services.jsm");
if (!Zotero.Prefs.get("firstRunGuidance")) return;
options = options || {};
let text = options.text;
let useLastText = options.useLastText || false;
let forEl = options.forEl || document.getElementById(this.getAttribute("for"));
let force = options.force || false;
if (!forEl) return;
// Don't show two panels at once
if (Zotero.guidanceBeingShown) {
return;
}
var about = this.getAttribute("about");
var pref = false;
if (about) {
pref = "firstRunGuidanceShown." + about;
let shown = false;
try {
shown = Zotero.Prefs.get(pref);
}
catch (e) {}
if (shown && !force) {
return;
}
}
var x = this.getAttribute("x"),
y = this.getAttribute("y"),
position = this.getAttribute("position");
if (!useLastText) {
if (!text) {
text = Zotero.getString("firstRunGuidance." + about);
}
text = text.split("\n");
var descriptionNode = this.id('panel-description');
while (descriptionNode.hasChildNodes()) {
descriptionNode.removeChild(descriptionNode.firstChild);
}
while (text.length) {
var textLine = text.shift();
descriptionNode.appendChild(document.createTextNode(textLine));
if (text.length) descriptionNode.appendChild(document.createElement("br"));
}
}
this._initNavButton('back', options.back);
this._initNavButton('forward', options.forward);
var f = () => {
if (this.hasAttribute("foregroundonly") && Services.ww.activeWindow != window) return;
this.panel.openPopup(forEl, position || "after_start",
x ? parseInt(x, 10) : 0, y ? parseInt(y, 10) : 0);
if (pref) {
Zotero.Prefs.set(pref, true);
}
};
if (this.hasAttribute("delay") && !force) {
window.setTimeout(f, this.getAttribute("delay"));
}
else {
f();
}
}
hide() {
this.panel.hidePopup();
}
_initNavButton(dir, nextID) {
if (!nextID) {
nextID = this.getAttribute(dir);
}
if (!nextID) {
return;
}
var nextElem = document.getElementById(nextID);
var button = this.id(dir + '-button');
button.hidden = false;
var target;
// If there's a forward action and no back action, the whole panel triggers
// the forward in noautohide mode
if (dir == 'forward' && !this.hasAttribute('back')
&& this.getAttribute('noautohide') == 'true') {
target = this.panel;
}
else {
target = button;
}
var listener = (event) => {
target.removeEventListener("click", listener);
this.hide();
var data = {
force: true
};
// Point the next panel back to this one
data[dir == 'back' ? 'forward' : 'back'] = this.getAttribute('id');
// When going backwards, don't regenerate text
if (dir == 'back') {
data.useLastText = true;
}
nextElem.show(data);
event.stopPropagation();
};
target.addEventListener("click", listener);
}
id(id) {
return this.shadowRoot.getElementById(id);
}
}
customElements.define("guidance-panel", GuidancePanel);
}

View file

@ -29,6 +29,7 @@
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/shadowAutocompleteInput.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/guidancePanel.js", this);
class ItemBox extends XULElement {
constructor() {
@ -76,7 +77,7 @@
<menuitem id="zotero-doi-menu-view-online" label="&zotero.item.viewOnline;"/>
<menuitem id="zotero-doi-menu-copy" label="&zotero.item.copyAsURL;"/>
</menupopup>
<zoteroguidancepanel id="zotero-author-guidance" about="authorMenu" position="after_end" x="-25"/>
<guidance-panel id="zotero-author-guidance" about="authorMenu" position="after_end" x="-25"/>
</popupset>
<div id="retraction-box" hidden="hidden">
<div id="retraction-header">
@ -1916,12 +1917,11 @@
// Reset creator mode settings here so that flex attribute gets reset
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
if (Zotero.ItemTypes.getName(this.item.itemTypeID) === "bookSection") {
var creatorTypeLabels = this.getElementsByClassName("creator-type-label");
var creatorTypeLabels = this.shadowRoot.querySelectorAll(".creator-type-label");
Zotero.debug(creatorTypeLabels[creatorTypeLabels.length-1] + "");
// fx-compat TODO: Re-enable this
// document.getElementById("zotero-author-guidance").show({
// forEl: creatorTypeLabels[creatorTypeLabels.length-1]
// });
this._id("zotero-author-guidance").show({
forEl: creatorTypeLabels[creatorTypeLabels.length - 1]
});
}
}

View file

@ -121,6 +121,6 @@
<button id="citation-properties-library-link" onclick="Zotero_QuickFormat.showInLibrary()"/>
</vbox>
</panel>
<zoteroguidancepanel class="citation-dialog guidance" about="quickFormat"
for="zotero-icon" x="26"/>
<guidance-panel class="citation-dialog guidance" about="quickFormat"
for="zotero-icon" x="26"/> <!-- fx-compat TODO: pull in chrome://zotero/content/elements/guidancePanel.js for this -->
</window>

View file

@ -42,11 +42,6 @@ dialog[resizable="true"]
-moz-binding: url('chrome://zotero/content/bindings/resizabledialog.xml#resizabledialog');
}
zoteroguidancepanel
{
-moz-binding: url('chrome://zotero/content/bindings/guidancepanel.xml#guidancepanel');
}
richlistitem {
padding: 0.2em 0.4em;
}

1
scss/_guidancePanel.scss Normal file
View file

@ -0,0 +1 @@
@import "components/guidancePanel";

View file

@ -1,15 +1,19 @@
panel {
min-width: none;
}
stack {
max-width: 400px;
width: 400px;
margin: 0;
padding: 0;
}
*[anonid=nav-buttons] {
#nav-buttons {
-moz-box-align: end;
-moz-box-pack: end;
}
*[anonid=nav-buttons] > toolbarbutton {
#nav-buttons > toolbarbutton {
-moz-appearance: none; /* Necessary on Linux for button to be shown */
width: 22px;
height: 22px;
@ -22,31 +26,31 @@ stack {
margin-bottom: -7px;
}
*[anonid=nav-buttons] > toolbarbutton:hover {
#nav-buttons > toolbarbutton:hover {
border-color: var(--toolbarbutton-hover-bordercolor);
box-shadow: var(--toolbarbutton-hover-boxshadow);
}
*[anonid=nav-buttons] > toolbarbutton:active:hover {
#nav-buttons > toolbarbutton:active:hover {
border-color: var(--toolbarbutton-active-bordercolor);
box-shadow: var(--toolbarbutton-active-boxshadow);
transition-duration: 10ms;
}
*[anonid=back-button] {
#back-button {
background-image: url("chrome://zotero/skin/chevron-left_808080_32.png");
}
*[anonid=forward-button] {
#forward-button {
margin-right: -16px;
background-image: url("chrome://zotero/skin/chevron-right_808080_32.png");
}
*[anonid=close-button-box] {
#close-button-box {
-moz-box-align: start;
-moz-box-pack: end;
}
*[anonid=close-button] {
#close-button {
margin: -16px -16px;
}

View file

@ -0,0 +1 @@
@import "guidancePanel";

View file

@ -0,0 +1 @@
@import "guidancePanel";

View file

@ -0,0 +1 @@
@import "guidancePanel";