Preferences: Fix tests, improve clarity, and more
- Fix sync and advanced preferences tests; use a new waitForFirstPaneLoad() functions instead of the old paneload event listener - Remove empty preferences_searchTest.js - Rename some Zotero_Preferences members/functions for better clarity and public/private differentiation - Reorder, also for clarity - Fix tabIndex parameter causing an error if invalid - Remove window.sizeToContent() call in Sync.displayFields() - So *that's* why the window resized every time the sync pane was loaded... - Deprecate openURL() and simplify openInViewer()
This commit is contained in:
parent
286381d0dd
commit
5d6ad703c1
7 changed files with 144 additions and 205 deletions
|
@ -26,15 +26,18 @@
|
|||
"use strict";
|
||||
|
||||
var Zotero_Preferences = {
|
||||
panes: new Map(),
|
||||
|
||||
_firstPaneLoadDeferred: Zotero.Promise.defer(),
|
||||
|
||||
init: function () {
|
||||
this.observerSymbols = [];
|
||||
this.panes = new Map();
|
||||
this._observerSymbols = [];
|
||||
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));
|
||||
event => this._search(event.target.value));
|
||||
|
||||
document.getElementById('prefs-subpane-back-button').addEventListener('command', () => {
|
||||
let parent = this.panes.get(this.navigation.value).parent;
|
||||
|
@ -45,10 +48,14 @@ var Zotero_Preferences = {
|
|||
|
||||
document.getElementById('prefs-search').focus();
|
||||
|
||||
this.initPanes();
|
||||
this.clearAndAddPanes();
|
||||
},
|
||||
|
||||
initPanes: function () {
|
||||
/**
|
||||
* (Re)initialize the preference window, adding all panes registered with Zotero.PreferencePanes.
|
||||
* Current selected pane and scroll position are saved and restored after load.
|
||||
*/
|
||||
clearAndAddPanes: function () {
|
||||
// Save positions and clear content in case we're reinitializing
|
||||
// because of a plugin lifecycle event
|
||||
let navigationValue = this.navigation.value;
|
||||
|
@ -59,12 +66,12 @@ var Zotero_Preferences = {
|
|||
let contentScrollLeft = this.content.scrollLeft;
|
||||
this.content.replaceChildren();
|
||||
|
||||
Zotero.PreferencePanes.builtInPanes.forEach(pane => this.addPane(pane));
|
||||
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));
|
||||
.forEach(pane => this._addPane(pane));
|
||||
}
|
||||
|
||||
if (navigationValue) {
|
||||
|
@ -92,7 +99,10 @@ var Zotero_Preferences = {
|
|||
}
|
||||
// Select tab within pane by index
|
||||
else if (tabIndex !== undefined) {
|
||||
pane.querySelector('tabbox').selectedIndex = tabIndex;
|
||||
let tabBox = pane.querySelector('tabbox');
|
||||
if (tabBox) {
|
||||
tabBox.selectedIndex = tabIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,13 +120,63 @@ var Zotero_Preferences = {
|
|||
},
|
||||
|
||||
onUnload: function () {
|
||||
if (Zotero_Preferences.Debug_Output) {
|
||||
Zotero_Preferences.Debug_Output.onUnload();
|
||||
while (this._observerSymbols.length) {
|
||||
Zotero.Prefs.unregisterObserver(this._observerSymbols.shift());
|
||||
}
|
||||
},
|
||||
|
||||
while (this.observerSymbols.length) {
|
||||
Zotero.Prefs.unregisterObserver(this.observerSymbols.shift());
|
||||
waitForFirstPaneLoad: async function () {
|
||||
await this._firstPaneLoadDeferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
},
|
||||
|
||||
openHelpLink: function () {
|
||||
// TODO: Re-add help and update this for new preferences architecture
|
||||
|
||||
var url = "http://www.zotero.org/support/preferences/";
|
||||
var helpTopic = document.getElementsByTagName("prefwindow")[0].currentPane.helpTopic;
|
||||
url += helpTopic;
|
||||
|
||||
Zotero.launchURL(url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens a URI in the basic viewer
|
||||
*/
|
||||
openInViewer: function (uri) {
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
var win = wm.getMostRecentWindow("zotero:basicViewer");
|
||||
if (win) {
|
||||
win.loadURI(uri);
|
||||
}
|
||||
else {
|
||||
window.openDialog("chrome://zotero/content/standalone/basicViewer.xhtml",
|
||||
"basicViewer", "chrome,resizable,centerscreen,menubar,scrollbars", uri);
|
||||
}
|
||||
},
|
||||
|
||||
_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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -138,7 +198,7 @@ var Zotero_Preferences = {
|
|||
* 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) {
|
||||
_addPane(options) {
|
||||
let { id, parent, label, rawLabel, image } = options;
|
||||
|
||||
let listItem = document.createXULElement('richlistitem');
|
||||
|
@ -184,16 +244,6 @@ var Zotero_Preferences = {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -216,7 +266,7 @@ var Zotero_Preferences = {
|
|||
];
|
||||
let contentFragment = pane.defaultXUL
|
||||
? MozXULElement.parseXULToFragment(markup, dtdFiles)
|
||||
: this.parseXHTMLToFragment(markup, dtdFiles);
|
||||
: this._parseXHTMLToFragment(markup, dtdFiles);
|
||||
contentFragment = document.importNode(contentFragment, true);
|
||||
pane.container.append(contentFragment);
|
||||
pane.imported = true;
|
||||
|
@ -230,7 +280,7 @@ var Zotero_Preferences = {
|
|||
backButton.hidden = !pane.parent;
|
||||
},
|
||||
|
||||
parseXHTMLToFragment(str, entities = []) {
|
||||
_parseXHTMLToFragment(str, entities = []) {
|
||||
// Adapted from MozXULElement.parseXULToFragment
|
||||
|
||||
/* eslint-disable indent */
|
||||
|
@ -260,6 +310,59 @@ ${str}
|
|||
return range.extractContents();
|
||||
},
|
||||
|
||||
_initImportedNodes(root) {
|
||||
// Activate `preference` attributes
|
||||
for (let elem of root.querySelectorAll('[preference]')) {
|
||||
let preference = elem.getAttribute('preference');
|
||||
if (root.querySelector('preferences > preference#' + preference)) {
|
||||
Zotero.warn('<preference> is deprecated -- `preference` attribute values '
|
||||
+ 'should be full preference keys, not <preference> 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));
|
||||
|
||||
// If this is the first pane to be loaded, notify anyone waiting
|
||||
// (for tests)
|
||||
this._firstPaneLoadDeferred.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
// 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'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If term is falsy, clear the current search and show the first pane.
|
||||
* If term is truthy, execute a search:
|
||||
|
@ -270,7 +373,7 @@ ${str}
|
|||
*
|
||||
* @param {String} [term]
|
||||
*/
|
||||
search(term) {
|
||||
_search(term) {
|
||||
// Initial housekeeping:
|
||||
|
||||
// Clear existing highlights
|
||||
|
@ -328,7 +431,7 @@ ${str}
|
|||
if (!root) continue;
|
||||
|
||||
for (let child of root.children) {
|
||||
let matches = this._searchRecursively(child, term);
|
||||
let matches = this._findNodesMatching(child, term);
|
||||
if (matches.length) {
|
||||
let touchedTabPanels = new Set();
|
||||
for (let node of matches) {
|
||||
|
@ -394,7 +497,7 @@ ${str}
|
|||
* @param {String} term Must be normalized (normalizeSearch())
|
||||
* @return {Node[]}
|
||||
*/
|
||||
_searchRecursively(root, term) {
|
||||
_findNodesMatching(root, term) {
|
||||
const EXCLUDE_SELECTOR = 'input, [hidden="true"], [no-highlight]';
|
||||
|
||||
let matched = new Set();
|
||||
|
@ -497,141 +600,11 @@ ${str}
|
|||
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('<preference> is deprecated -- `preference` attribute values '
|
||||
+ 'should be full preference keys, not <preference> 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) {
|
||||
// Non-instantApply prefwindows are usually modal, so we can't open in the topmost window,
|
||||
// since it's probably behind the window
|
||||
var instantApply = Zotero.Prefs.get("browser.preferences.instantApply", true);
|
||||
|
||||
if (instantApply) {
|
||||
window.opener.ZoteroPane_Local.loadURI(url, { shiftKey: true, metaKey: true });
|
||||
}
|
||||
else {
|
||||
if (Zotero.isStandalone) {
|
||||
var io = Components.classes['@mozilla.org/network/io-service;1']
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
var uri = io.newURI(url, null, null);
|
||||
var handler = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
|
||||
.getService(Components.interfaces.nsIExternalProtocolService)
|
||||
.getProtocolHandlerInfo('http');
|
||||
handler.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault;
|
||||
handler.launchWithURI(uri, null);
|
||||
}
|
||||
else {
|
||||
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||
.getService(Components.interfaces.nsIWindowWatcher);
|
||||
var win = ww.openWindow(
|
||||
window,
|
||||
url,
|
||||
windowName ? windowName : null,
|
||||
"chrome=no,menubar=yes,location=yes,toolbar=yes,personalbar=yes,resizable=yes,scrollbars=yes,status=yes",
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
openHelpLink: function () {
|
||||
var url = "http://www.zotero.org/support/preferences/";
|
||||
var helpTopic = document.getElementsByTagName("prefwindow")[0].currentPane.helpTopic;
|
||||
url += helpTopic;
|
||||
|
||||
this.openURL(url, "helpWindow");
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Opens a URI in the basic viewer in Standalone, or a new window in Firefox
|
||||
* @deprecated Use {@link Zotero.launchURL}
|
||||
*/
|
||||
openInViewer: function (uri, newTab) {
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
const features = "menubar=yes,toolbar=no,location=no,scrollbars,centerscreen,resizable";
|
||||
|
||||
if(Zotero.isStandalone) {
|
||||
var win = wm.getMostRecentWindow("zotero:basicViewer");
|
||||
if(win) {
|
||||
win.loadURI(uri);
|
||||
} else {
|
||||
window.openDialog("chrome://zotero/content/standalone/basicViewer.xhtml",
|
||||
"basicViewer", "chrome,resizable,centerscreen,menubar,scrollbars", uri);
|
||||
}
|
||||
} else {
|
||||
var win = wm.getMostRecentWindow("navigator:browser");
|
||||
if(win) {
|
||||
if(newTab) {
|
||||
win.gBrowser.selectedTab = win.gBrowser.addTab(uri);
|
||||
} else {
|
||||
win.open(uri, null, features);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||
.getService(Components.interfaces.nsIWindowWatcher);
|
||||
var win = ww.openWindow(null, uri, null, features + ",width=775,height=575", null);
|
||||
}
|
||||
}
|
||||
openURL: function (url) {
|
||||
Zotero.warn("Zotero_Preferences.openURL() is deprecated -- use Zotero.launchURL()");
|
||||
Zotero.launchURL(url);
|
||||
}
|
||||
};
|
|
@ -399,7 +399,7 @@ Zotero_Preferences.Advanced = {
|
|||
yield Zotero.DataDirectory.choose(
|
||||
true,
|
||||
!newUseDataDir,
|
||||
() => Zotero_Preferences.openURL('https://www.zotero.org/support/zotero_data')
|
||||
() => Zotero.launchURL('https://www.zotero.org/support/zotero_data')
|
||||
);
|
||||
radiogroup.selectedIndex = this._usingDefaultDataDir() ? 0 : 1;
|
||||
}),
|
||||
|
|
|
@ -92,8 +92,6 @@ Zotero_Preferences.Sync = {
|
|||
var img = document.getElementById('sync-status-indicator');
|
||||
img.removeAttribute('verified');
|
||||
img.removeAttribute('animated');
|
||||
|
||||
window.sizeToContent();
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ Zotero.PreferencePanes = {
|
|||
|
||||
_refreshPreferences() {
|
||||
for (let win of Services.wm.getEnumerator("zotero:pref")) {
|
||||
win.Zotero_Preferences.initPanes();
|
||||
win.Zotero_Preferences.clearAndAddPanes();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -65,21 +65,11 @@ describe("Advanced Preferences", function () {
|
|||
describe("Linked Attachment Base Directory", function () {
|
||||
var setBaseDirectory = Zotero.Promise.coroutine(function* (basePath) {
|
||||
var win = yield loadWindow("chrome://zotero/content/preferences/preferences.xhtml", {
|
||||
pane: 'zotero-prefpane-advanced',
|
||||
tabIndex: 1
|
||||
pane: 'zotero-prefpane-advanced'
|
||||
});
|
||||
|
||||
// Wait for tab to load
|
||||
var doc = win.document;
|
||||
var prefwindow = doc.documentElement;
|
||||
var defer = Zotero.Promise.defer();
|
||||
var pane = doc.getElementById('zotero-prefpane-advanced');
|
||||
if (!pane.loaded) {
|
||||
pane.addEventListener('paneload', function () {
|
||||
defer.resolve();
|
||||
})
|
||||
yield defer.promise;
|
||||
}
|
||||
yield win.Zotero_Preferences.waitForFirstPaneLoad();
|
||||
|
||||
var promise = waitForDialog();
|
||||
yield win.Zotero_Preferences.Attachment_Base_Directory.changePath(basePath);
|
||||
|
@ -95,16 +85,7 @@ describe("Advanced Preferences", function () {
|
|||
});
|
||||
|
||||
// Wait for tab to load
|
||||
var doc = win.document;
|
||||
var prefwindow = doc.documentElement;
|
||||
var defer = Zotero.Promise.defer();
|
||||
var pane = doc.getElementById('zotero-prefpane-advanced');
|
||||
if (!pane.loaded) {
|
||||
pane.addEventListener('paneload', function () {
|
||||
defer.resolve();
|
||||
})
|
||||
yield defer.promise;
|
||||
}
|
||||
yield win.Zotero_Preferences.waitForFirstPaneLoad();
|
||||
|
||||
var promise = waitForDialog();
|
||||
yield win.Zotero_Preferences.Attachment_Base_Directory.clearPath();
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
describe("Search Preferences", function () {
|
||||
describe("PDF Indexing", function () {
|
||||
|
||||
})
|
||||
})
|
|
@ -3,18 +3,10 @@ describe("Sync Preferences", function () {
|
|||
before(function* () {
|
||||
// Load prefs with sync pane
|
||||
win = yield loadWindow("chrome://zotero/content/preferences/preferences.xhtml", {
|
||||
pane: 'zotero-prefpane-sync',
|
||||
tabIndex: 0
|
||||
pane: 'zotero-prefpane-sync'
|
||||
});
|
||||
doc = win.document;
|
||||
let defer = Zotero.Promise.defer();
|
||||
let pane = doc.getElementById('zotero-prefpane-sync');
|
||||
if (!pane.loaded) {
|
||||
pane.addEventListener('paneload', function () {
|
||||
defer.resolve();
|
||||
});
|
||||
yield defer.promise;
|
||||
}
|
||||
yield win.Zotero_Preferences.waitForFirstPaneLoad();
|
||||
});
|
||||
|
||||
after(function() {
|
||||
|
|
Loading…
Add table
Reference in a new issue