fx-compat: Preferences: Manage pref observers for elements added later

This commit is contained in:
Abe Jellinek 2022-11-03 11:52:54 -04:00
parent e4b290a394
commit 3af5976aa2
2 changed files with 82 additions and 26 deletions

View file

@ -30,8 +30,9 @@ var Zotero_Preferences = {
_firstPaneLoadDeferred: Zotero.Promise.defer(), _firstPaneLoadDeferred: Zotero.Promise.defer(),
_observerSymbols: new Map(),
init: function () { init: function () {
this._observerSymbols = [];
this.navigation = document.getElementById('prefs-navigation'); this.navigation = document.getElementById('prefs-navigation');
this.content = document.getElementById('prefs-content'); this.content = document.getElementById('prefs-content');
this.helpContainer = document.getElementById('prefs-help-container'); this.helpContainer = document.getElementById('prefs-help-container');
@ -96,9 +97,10 @@ var Zotero_Preferences = {
}, },
onUnload: function () { onUnload: function () {
while (this._observerSymbols.length) { for (let symbol of this._observerSymbols.values()) {
Zotero.Prefs.unregisterObserver(this._observerSymbols.shift()); Zotero.Prefs.unregisterObserver(symbol);
} }
this._observerSymbols.clear();
}, },
waitForFirstPaneLoad: async function () { waitForFirstPaneLoad: async function () {
@ -326,6 +328,49 @@ ${str}
* @private * @private
*/ */
_initImportedNodesPostInsert(container) { _initImportedNodesPostInsert(container) {
let useChecked = elem => (
(elem instanceof HTMLInputElement && elem.type == 'checkbox')
|| elem.tagName == 'checkbox'
);
let syncFromPref = (elem, preference) => {
let value = Zotero.Prefs.get(preference, true);
if (useChecked(elem)) {
elem.checked = value;
}
else {
elem.value = value;
}
elem.dispatchEvent(new Event('syncfrompreference'));
};
// We use a single listener function shared between all elements so we can easily detach it later
let syncToPrefOnModify = (event) => {
if (event.currentTarget.getAttribute('preference')) {
let value = useChecked(event.currentTarget) ? event.currentTarget.checked : event.currentTarget.value;
Zotero.Prefs.set(event.currentTarget.getAttribute('preference'), value, true);
event.currentTarget.dispatchEvent(new Event('synctopreference'));
}
};
let attachToPreference = (elem, preference) => {
Zotero.debug(`Attaching <${elem.tagName}> element to ${preference}`);
let symbol = Zotero.Prefs.registerObserver(
preference,
() => syncFromPref(elem, preference),
true
);
this._observerSymbols.set(elem, symbol);
};
let detachFromPreference = (elem) => {
if (this._observerSymbols.has(elem)) {
Zotero.debug(`Detaching <${elem.tagName}> element from preference`);
Zotero.Prefs.unregisterObserver(this._observerSymbols.get(elem));
this._observerSymbols.delete(elem);
}
};
// Activate `preference` attributes // Activate `preference` attributes
for (let elem of container.querySelectorAll('[preference]')) { for (let elem of container.querySelectorAll('[preference]')) {
let preference = elem.getAttribute('preference'); let preference = elem.getAttribute('preference');
@ -336,30 +381,13 @@ ${str}
.getAttribute('name'); .getAttribute('name');
} }
let useChecked = (elem instanceof HTMLInputElement && elem.type == 'checkbox') attachToPreference(elem, preference);
|| elem.tagName == 'checkbox';
elem.addEventListener(elem instanceof XULElement ? 'command' : 'input', () => { elem.addEventListener(elem instanceof XULElement ? 'command' : 'input', syncToPrefOnModify);
let value = useChecked ? elem.checked : elem.value;
Zotero.Prefs.set(preference, value, true);
elem.dispatchEvent(new Event('synctopreference'));
});
let syncFromPref = () => { // Set timeout before populating the value so the pane can add listeners first
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(() => { setTimeout(() => {
syncFromPref(); syncFromPref(elem, preference);
this._observerSymbols.push(Zotero.Prefs.registerObserver(preference, syncFromPref, true));
// If this is the first pane to be loaded, notify anyone waiting // If this is the first pane to be loaded, notify anyone waiting
// (for tests) // (for tests)
@ -367,6 +395,34 @@ ${str}
}); });
} }
new MutationObserver((mutations) => {
for (let mutation of mutations) {
if (mutation.type == 'attributes') {
let target = mutation.target;
detachFromPreference(target);
if (target.hasAttribute('preference')) {
attachToPreference(target, target.getAttribute('preference'));
target.addEventListener(target instanceof XULElement ? 'command' : 'input', syncToPrefOnModify);
}
}
else if (mutation.type == 'childList') {
for (let node of mutation.removedNodes) {
detachFromPreference(node);
}
for (let node of mutation.addedNodes) {
if (node.nodeType == Node.ELEMENT_NODE && node.hasAttribute('preference')) {
attachToPreference(node);
node.addEventListener(node instanceof XULElement ? 'command' : 'input', syncToPrefOnModify);
}
}
}
}
}).observe(container, {
childList: true,
subtree: true,
attributeFilter: ['preference']
});
// parseXULToFragment() doesn't convert oncommand attributes into actual // parseXULToFragment() doesn't convert oncommand attributes into actual
// listeners, so we'll do it here // listeners, so we'll do it here
for (let elem of container.querySelectorAll('[oncommand]')) { for (let elem of container.querySelectorAll('[oncommand]')) {

View file

@ -65,12 +65,12 @@ Zotero_Preferences.Export = {
this.buildQuickCopyFormatDropDown( this.buildQuickCopyFormatDropDown(
menulist, format.contentType, format, translators menulist, format.contentType, format, translators
); );
menulist.setAttribute('preference', "pref-quickCopy-setting"); menulist.setAttribute('preference', "extensions.zotero.export.quickCopy.setting");
// Initialize locale drop-down // Initialize locale drop-down
var localeMenulist = document.getElementById("zotero-quickCopy-locale-menu"); var localeMenulist = document.getElementById("zotero-quickCopy-locale-menu");
Zotero.Styles.populateLocaleList(localeMenulist); Zotero.Styles.populateLocaleList(localeMenulist);
localeMenulist.setAttribute('preference', "pref-quickCopy-locale"); localeMenulist.setAttribute('preference', "extensions.zotero.export.quickCopy.locale");
this._lastSelectedLocale = Zotero.Prefs.get("export.quickCopy.locale"); this._lastSelectedLocale = Zotero.Prefs.get("export.quickCopy.locale");
this.updateQuickCopyUI(); this.updateQuickCopyUI();