diff --git a/chrome/content/zotero/preferences/preferences_export.jsx b/chrome/content/zotero/preferences/preferences_export.jsx index a99e6ff1d4..2a1b315f06 100644 --- a/chrome/content/zotero/preferences/preferences_export.jsx +++ b/chrome/content/zotero/preferences/preferences_export.jsx @@ -35,6 +35,7 @@ Zotero_Preferences.Export = { this.updateQuickCopyInstructions(); await this.populateQuickCopyList(); await this.populateNoteQuickCopyList(); + this.updateQuickCopyUI(); }, @@ -52,65 +53,44 @@ Zotero_Preferences.Export = { /* - * Builds the main Quick Copy drop-down from the current global pref + * Builds the bibliography, export, and locale dropdowns. Selection state + * is applied later by updateQuickCopyUI() reading from prefs. */ - populateQuickCopyList: async function() { - // Initialize default format drop-down - var format = Zotero.Prefs.get("export.quickCopy.setting"); - format = Zotero.QuickCopy.unserializeSetting(format); - var menulist = document.getElementById("zotero-quickCopy-menu"); + populateQuickCopyList: async function () { await Zotero.Styles.init(); - var translators = await this.getQuickCopyTranslators(); - this.buildQuickCopyFormatDropDown( - menulist, format.contentType, format, translators + + this.buildBibliographyQuickCopyDropDown( + document.getElementById("zotero-quickCopy-bibliography-menu") ); - menulist.setAttribute('preference', "extensions.zotero.export.quickCopy.setting"); - - // Initialize locale drop-down - var localeMenulist = document.getElementById("zotero-quickCopy-locale-menu"); - Zotero.Styles.populateLocaleList(localeMenulist); - localeMenulist.addEventListener('syncfrompreference', () => { - this._lastSelectedLocale = Zotero.Prefs.get("export.quickCopy.locale"); - this.updateQuickCopyUI(); - }); - localeMenulist.setAttribute('preference', "extensions.zotero.export.quickCopy.locale"); - + + this.buildExportQuickCopyDropDown( + document.getElementById("zotero-quickCopy-export-menu"), + await this.getQuickCopyTranslators() + ); + + // Initial locale options; updateQuickCopyUI re-filters by selected style + Zotero.Styles.populateLocaleList(document.getElementById("zotero-quickCopy-locale-menu")); + await this.refreshQuickCopySiteList(); }, /* - * Builds the note Quick Copy drop-down from the current global pref + * Builds the note Quick Copy drop-down. Items carry just the translator ID; + * options live in the dedicated checkboxes and are read by updateQuickCopyPrefs. */ populateNoteQuickCopyList: async function () { document.getElementById('noteQuickCopy-format-options').removeAttribute('hidden'); - - // Initialize default format drop-down - var format = Zotero.Prefs.get("export.noteQuickCopy.setting"); - format = Zotero.QuickCopy.unserializeSetting(format); - var menulist = document.getElementById("zotero-noteQuickCopy-menu"); - menulist.setAttribute('preference', "extensions.zotero.export.noteQuickCopy.setting"); - menulist.removeEventListener('command', this.updateNoteQuickCopyUI); - menulist.addEventListener('command', this.updateNoteQuickCopyUI); - if (!format) { - format = menulist.value; - } - - format = Zotero.QuickCopy.unserializeSetting(format); - - menulist.selectedItem = null; + var menulist = document.getElementById("zotero-noteQuickCopy-menu"); menulist.removeAllItems(); - var popup = document.createXULElement('menupopup'); menulist.appendChild(popup); - // add export formats to list var translation = new Zotero.Translate("export"); var translators = await translation.getTranslators(); - translators.sort((a, b) => a.label.localeCompare(b.label)); - + // Remove "Note" prefix from Note HTML translator let htmlTranslator = translators.find( x => x.translatorID == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML @@ -118,7 +98,7 @@ Zotero_Preferences.Export = { if (htmlTranslator) { htmlTranslator.label = 'HTML'; } - + // Make sure virtual "Markdown + Rich Text" translator doesn't actually exist translators = translators.filter( x => x.translatorID != Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT @@ -127,186 +107,55 @@ Zotero_Preferences.Export = { let markdownTranslatorIdx = translators.findIndex( x => x.translatorID == Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN ); - // Make sure we actually have both translators if (markdownTranslatorIdx != -1 && htmlTranslator) { - // Exclude standalone Note Markdown translator translators.splice(markdownTranslatorIdx, 1); - // Add virtual "Markdown + Rich Text" translator to the top translators.unshift({ translatorID: Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT, label: 'Markdown + ' + Zotero.getString('general.richText'), - configOptions: { - noteTranslator: true - } + configOptions: { noteTranslator: true } }); } - + translators.forEach(function (translator) { - // Allow only note export translators if (!translator.configOptions || !translator.configOptions.noteTranslator) { return; } - - var value = { mode: 'export', id: translator.translatorID }; - if (translator.translatorID == format.id) { - value = format; - } - else if (translator.translatorID == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) { - value = { - mode: 'export', - id: translator.translatorID, - markdownOptions: { - includeAppLinks: true - }, - htmlOptions: { - includeAppLinks: false - } - }; - if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML && format.options) { - value.htmlOptions = format.options; - } - } - else if (translator.translatorID == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) { - value = { - mode: 'export', - id: translator.translatorID, - options: { - includeAppLinks: false - } - }; - if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT && format.htmlOptions) { - value.options = format.htmlOptions; - } - } - - value = JSON.stringify(value); var itemNode = document.createXULElement('menuitem'); - itemNode.setAttribute('value', value); + itemNode.setAttribute('value', translator.translatorID); itemNode.setAttribute('label', translator.label); popup.appendChild(itemNode); - - if (format.mode == 'export' && format.id == translator.translatorID) { - menulist.selectedItem = itemNode; - } }); - - menulist.click(); - this.updateNoteQuickCopyUI(); }, - updateNoteQuickCopyUI: () => { - var format = document.getElementById('zotero-noteQuickCopy-menu').value; - format = JSON.parse(format); - var markdownOptions = document.getElementById('noteQuickCopy-markdown-options'); - var htmlOptions = document.getElementById('noteQuickCopy-html-options'); - var markdownOptionsLabel = document.querySelector('#noteQuickCopy-markdown-options label'); - var htmlOptionsLabel = document.querySelector('#noteQuickCopy-html-options label'); - var markdownIncludeAppLinks = document.getElementById("noteQuickCopy-markdown-includeAppLinks"); - var htmlIncludeAppLinks = document.getElementById("noteQuickCopy-html-includeAppLinks"); - - markdownOptionsLabel.value = Zotero.Utilities.Internal.stringWithColon("Markdown"); - htmlOptionsLabel.value = Zotero.Utilities.Internal.stringWithColon( - Zotero.getString('zotero.preferences.export.quickCopy.note.htmlOptions.label') - ); - markdownIncludeAppLinks.label = Zotero.getString('exportOptions.includeAppLinks', Zotero.appName); - htmlIncludeAppLinks.label = Zotero.getString('exportOptions.includeAppLinks', Zotero.appName); - - if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) { - markdownOptions.hidden = false; - htmlOptions.hidden = false; - markdownIncludeAppLinks.checked = format.markdownOptions && format.markdownOptions.includeAppLinks; - htmlIncludeAppLinks.checked = format.htmlOptions && format.htmlOptions.includeAppLinks; - } - else if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) { - markdownOptions.hidden = true; - htmlOptions.hidden = false; - htmlIncludeAppLinks.checked = format.options && format.options.includeAppLinks; - } - else { - markdownOptions.hidden = true; - htmlOptions.hidden = true; - } - }, - - onUpdateNoteExportOptions() { - var menulist = document.getElementById("zotero-noteQuickCopy-menu"); - var markdownIncludeAppLinks = document.getElementById("noteQuickCopy-markdown-includeAppLinks"); - var htmlIncludeAppLinks = document.getElementById("noteQuickCopy-html-includeAppLinks"); - - for (let i = 0; i < menulist.itemCount; i++) { - let item = menulist.getItemAtIndex(i); - let format = JSON.parse(item.getAttribute('value')); - if (format.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) { - if (!format.markdownOptions) { - format.markdownOptions = {}; - } - if (!format.htmlOptions) { - format.htmlOptions = {}; - } - format.markdownOptions.includeAppLinks = markdownIncludeAppLinks.checked; - format.htmlOptions.includeAppLinks = htmlIncludeAppLinks.checked; - } - else if (format.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) { - if (!format.options) { - format.options = {}; - } - format.options.includeAppLinks = htmlIncludeAppLinks.checked; - } - else { - continue; - } - item.value = JSON.stringify(format); - } - // After updating item's value we have to wait before dispatching event. - // menulist.value does not reflect changes immediately item.value is updated. - setTimeout(() => menulist.dispatchEvent(new Event("change", { bubbles: true })), 50); - }, - - /* - * Builds a Quick Copy drop-down + * Builds the bibliography Quick Copy drop-down. Items carry the styleID; + * selection is applied by updateQuickCopyUI(). */ - buildQuickCopyFormatDropDown: function (menulist, contentType, format, translators) { - if (!format) { - format = menulist.value; - } - - format = Zotero.QuickCopy.unserializeSetting(format); - - menulist.selectedItem = null; + buildBibliographyQuickCopyDropDown: function (menulist) { menulist.removeAllItems(); - var popup = document.createXULElement('menupopup'); menulist.appendChild(popup); - - var itemNode = document.createXULElement("menuitem"); - itemNode.setAttribute("label", Zotero.getString('zotero.preferences.export.quickCopy.citationStyles')); - itemNode.setAttribute("disabled", true); - popup.appendChild(itemNode); - - // add styles to list - var styles = Zotero.Styles.getVisible(); - styles.forEach(function (style) { - var val = 'bibliography' + (contentType == 'html' ? '/html' : '') + '=' + style.styleID; + + Zotero.Styles.getVisible().forEach(function (style) { var itemNode = document.createXULElement("menuitem"); - itemNode.setAttribute("value", val); + itemNode.setAttribute("value", style.styleID); itemNode.setAttribute("label", style.title); - itemNode.setAttribute("oncommand", 'Zotero_Preferences.Export.updateQuickCopyUI()'); popup.appendChild(itemNode); - - if (format.mode == 'bibliography' && format.id == style.styleID) { - menulist.selectedItem = itemNode; - } }); - - var itemNode = document.createXULElement("menuitem"); - itemNode.setAttribute("label", Zotero.getString('zotero.preferences.export.quickCopy.exportFormats')); - itemNode.setAttribute("disabled", true); - popup.appendChild(itemNode); - - // add export formats to list - translators.sort((a, b) => a.label.localeCompare(b.label)) + }, + + + /* + * Builds the export Quick Copy drop-down. Items carry the translatorID; + * selection is applied by updateQuickCopyUI(). + */ + buildExportQuickCopyDropDown: function (menulist, translators) { + menulist.removeAllItems(); + var popup = document.createXULElement('menupopup'); + menulist.appendChild(popup); + + translators.sort((a, b) => a.label.localeCompare(b.label)); translators.forEach(function (translator) { // Skip RDF formats switch (translator.translatorID) { @@ -314,46 +163,147 @@ Zotero_Preferences.Export = { case '14763d24-8ba0-45df-8f52-b8d1108e7ac9': return; } - var val = 'export=' + translator.translatorID; var itemNode = document.createXULElement("menuitem"); - itemNode.setAttribute("value", val); + itemNode.setAttribute("value", translator.translatorID); itemNode.setAttribute("label", translator.label); - itemNode.setAttribute("oncommand", 'Zotero_Preferences.Export.updateQuickCopyUI()'); popup.appendChild(itemNode); - - if (format.mode == 'export' && format.id == translator.translatorID) { - menulist.selectedItem = itemNode; - } }); - - menulist.click(); }, - - - onCopyAsHTMLChange: async function (checked) { - var menulist = document.getElementById('zotero-quickCopy-menu'); - var translators = await this.getQuickCopyTranslators(); - this.buildQuickCopyFormatDropDown(menulist, checked ? 'html' : '', null, translators); + + + /* + * Single writer: read the current state of every Quick Copy control and + * persist it across the bibliography, export, note-format, drag-preference, + * and locale prefs. Then call updateQuickCopyUI to refresh anything that + * cascades off the new state (locale list filtered by style, note option + * visibility for the new translator, etc). + */ + updateQuickCopyPrefs: function () { + var bibMenu = document.getElementById('zotero-quickCopy-bibliography-menu'); + var asHTML = document.getElementById('zotero-quickCopy-copyAsHTML').checked; + Zotero.Prefs.set('export.quickCopy.bibliographySetting', JSON.stringify({ + mode: 'bibliography', + id: bibMenu.value || '', + contentType: asHTML ? 'html' : '', + locale: document.getElementById('zotero-quickCopy-locale-menu').value || '' + })); + + var exportMenu = document.getElementById('zotero-quickCopy-export-menu'); + Zotero.Prefs.set('export.quickCopy.exportSetting', JSON.stringify({ + mode: 'export', + id: exportMenu.value || '' + })); + + var noteMenu = document.getElementById('zotero-noteQuickCopy-menu'); + var noteId = noteMenu.value || ''; + var notePref = { mode: 'export', id: noteId }; + var markdownLinks = document.getElementById('noteQuickCopy-markdown-includeAppLinks').checked; + var htmlLinks = document.getElementById('noteQuickCopy-html-includeAppLinks').checked; + if (noteId == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) { + notePref.markdownOptions = { includeAppLinks: markdownLinks }; + notePref.htmlOptions = { includeAppLinks: htmlLinks }; + } + else if (noteId == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) { + notePref.options = { includeAppLinks: htmlLinks }; + } + Zotero.Prefs.set('export.noteQuickCopy.setting', JSON.stringify(notePref)); + + Zotero.Prefs.set( + 'export.quickCopy.preferredFormatOnDrag', + document.getElementById('zotero-quickCopy-preferredFormatOnDrag').value || 'bibliography' + ); + + this.updateQuickCopyUI(); }, - - + + + /* + * Single reader: pull every Quick Copy pref and apply it to the matching + * control. Programmatic property assignments here do not fire `command`, + * so this does not loop back into updateQuickCopyPrefs. + */ updateQuickCopyUI: function () { - var format = document.getElementById('zotero-quickCopy-menu').value; - - var mode, contentType; - - [mode, format] = format.split('='); - [mode, contentType] = mode.split('/'); - - var checkbox = document.getElementById('zotero-quickCopy-copyAsHTML'); - checkbox.checked = contentType == 'html'; - checkbox.disabled = mode != 'bibliography'; - + var bibPref = Zotero.QuickCopy.unserializeSetting( + Zotero.Prefs.get('export.quickCopy.bibliographySetting') + ); + this._selectMenuItemByValue( + document.getElementById('zotero-quickCopy-bibliography-menu'), + bibPref.id + ); + + document.getElementById('zotero-quickCopy-copyAsHTML').checked + = bibPref.contentType == 'html'; + Zotero.Styles.updateLocaleList( document.getElementById('zotero-quickCopy-locale-menu'), - mode == 'bibliography' ? Zotero.Styles.get(format) : null, - this._lastSelectedLocale + bibPref.id ? Zotero.Styles.get(bibPref.id) : null, + bibPref.locale ); + + var exportPref = Zotero.QuickCopy.unserializeSetting( + Zotero.Prefs.get('export.quickCopy.exportSetting') + ); + this._selectMenuItemByValue( + document.getElementById('zotero-quickCopy-export-menu'), + exportPref.id + ); + + var notePref = Zotero.QuickCopy.unserializeSetting( + Zotero.Prefs.get('export.noteQuickCopy.setting') + ); + this._selectMenuItemByValue( + document.getElementById('zotero-noteQuickCopy-menu'), + notePref.id + ); + this._updateNoteOptionUI(notePref); + + document.getElementById('zotero-quickCopy-preferredFormatOnDrag').value + = Zotero.Prefs.get('export.quickCopy.preferredFormatOnDrag') || 'bibliography'; + }, + + + _selectMenuItemByValue: function (menulist, value) { + for (let i = 0; i < menulist.itemCount; i++) { + let item = menulist.getItemAtIndex(i); + if (item.value == value) { + menulist.selectedItem = item; + return; + } + } + menulist.selectedItem = null; + }, + + + _updateNoteOptionUI: function (notePref) { + var markdownGroup = document.getElementById('noteQuickCopy-markdown-options'); + var htmlGroup = document.getElementById('noteQuickCopy-html-options'); + var markdownLabel = document.querySelector('#noteQuickCopy-markdown-options label'); + var htmlLabel = document.querySelector('#noteQuickCopy-html-options label'); + var markdownCheckbox = document.getElementById('noteQuickCopy-markdown-includeAppLinks'); + var htmlCheckbox = document.getElementById('noteQuickCopy-html-includeAppLinks'); + + markdownLabel.value = Zotero.Utilities.Internal.stringWithColon('Markdown'); + htmlLabel.value = Zotero.Utilities.Internal.stringWithColon( + Zotero.getString('zotero.preferences.export.quickCopy.note.htmlOptions.label') + ); + markdownCheckbox.label = Zotero.getString('exportOptions.includeAppLinks', Zotero.appName); + htmlCheckbox.label = Zotero.getString('exportOptions.includeAppLinks', Zotero.appName); + + if (notePref.id == Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT) { + markdownGroup.hidden = false; + htmlGroup.hidden = false; + markdownCheckbox.checked = !!(notePref.markdownOptions && notePref.markdownOptions.includeAppLinks); + htmlCheckbox.checked = !!(notePref.htmlOptions && notePref.htmlOptions.includeAppLinks); + } + else if (notePref.id == Zotero.Translators.TRANSLATOR_ID_NOTE_HTML) { + markdownGroup.hidden = true; + htmlGroup.hidden = false; + htmlCheckbox.checked = !!(notePref.options && notePref.options.includeAppLinks); + } + else { + markdownGroup.hidden = true; + htmlGroup.hidden = true; + } }, /** @@ -389,65 +339,81 @@ Zotero_Preferences.Export = { if (editExisting) { index = this._tree.selection.focused; } - var formattedName = document.getElementById('zotero-quickCopy-menu').label; - var locale = this._lastSelectedLocale; - var asHTML = document.getElementById('zotero-quickCopy-copyAsHTML').checked; - + + var domain = ''; + var existingSiteSetting = null; + if (index !== undefined && index > -1 && index < this._rows.length) { - var row = this._rows[index]; - var domain = row.domain; - formattedName = row.format; - locale = row.locale; - asHTML = row.copyAsHTML; + let row = this._rows[index]; + domain = row.domain; + existingSiteSetting = row.siteSetting; } - - var format = await Zotero.QuickCopy.getSettingFromFormattedName(formattedName); - if (asHTML) { - format = format.replace('bibliography=', 'bibliography/html='); - } - - var styles = Zotero.Styles.getVisible(); + var translation = new Zotero.Translate("export"); var translators = await translation.getTranslators(); - - var io = { domain, format, locale, asHTML, ok: false, styles, translators }; + + var io = { + domain, + siteSetting: existingSiteSetting, + translators, + ok: false + }; window.openDialog('chrome://zotero/content/preferences/quickCopySiteEditor.xhtml', "zotero-preferences-quickCopySiteEditor", "chrome,modal,centerscreen", io); - - if (!io.ok || !io.domain) { + + if (!io.ok || !io.domain || !io.siteSetting) { return; } - + // Defensive: at least one of bibliography/export must be present + if (!io.siteSetting.bibliography && !io.siteSetting.export) { + return; + } + if (domain && domain != io.domain) { await Zotero.DB.queryAsync("DELETE FROM settings WHERE setting='quickCopySite' AND key=?", [domain]); } - - var quickCopysetting = Zotero.QuickCopy.unserializeSetting(io.format); - quickCopysetting.locale = io.locale; - - await Zotero.DB.queryAsync("REPLACE INTO settings VALUES ('quickCopySite', ?, ?)", [io.domain, JSON.stringify(quickCopysetting)]); - + + await Zotero.DB.queryAsync("REPLACE INTO settings VALUES ('quickCopySite', ?, ?)", + [io.domain, JSON.stringify(io.siteSetting)]); + await Zotero.QuickCopy.loadSiteSettings(); - + await this.refreshQuickCopySiteList(); }, - - + + refreshQuickCopySiteList: async function () { var sql = "SELECT key AS domainPath, value AS format FROM settings " + "WHERE setting='quickCopySite' ORDER BY domainPath COLLATE NOCASE"; var siteData = await Zotero.DB.queryAsync(sql); - + this._rows = []; for (let row of siteData) { - var formattedName = await Zotero.QuickCopy.getFormattedNameFromSetting(row.format); - var format = Zotero.QuickCopy.unserializeSetting(row.format); + let site = Zotero.QuickCopy.parseSiteFormat(row.format); + let bibName = ''; + let exportName = ''; + if (site.bibliography && site.bibliography.id) { + bibName = await Zotero.QuickCopy.getFormattedNameFromSetting(site.bibliography); + } + if (site.export && site.export.id) { + exportName = await Zotero.QuickCopy.getFormattedNameFromSetting(site.export); + } + let dragLabel = ''; + if (site.drag === 'bibliography') { + dragLabel = Zotero.getString('preferences-quickCopy-preferredFormatOnDrag-bibliography'); + } + else if (site.drag === 'export') { + dragLabel = Zotero.getString('preferences-quickCopy-preferredFormatOnDrag-export'); + } this._rows.push({ domain: row.domainPath, - format: formattedName, - locale: format.locale, - copyAsHTML: format.contentType == 'html', + exportFormat: exportName, + bibliographyFormat: bibName, + locale: site.bibliography && site.bibliography.locale || '', + copyAsHTML: !!(site.bibliography && site.bibliography.contentType === 'html'), + useOnDrag: dragLabel, + siteSetting: site, }); } @@ -459,14 +425,20 @@ Zotero_Preferences.Export = { flex: 2 }, { - dataKey: "format", - label: "zotero.preferences.quickCopy.siteEditor.format", - flex: 4 + dataKey: "exportFormat", + label: "preferences-quickCopy-siteEditor-export-format", + flex: 3 + }, + { + dataKey: "bibliographyFormat", + label: "preferences-quickCopy-siteEditor-bibliography-format", + flex: 3 }, { dataKey: "locale", label: "zotero.preferences.quickCopy.siteEditor.locale", - flex: 1 + fixedWidth: true, + width: 85, }, { dataKey: "copyAsHTML", @@ -474,6 +446,12 @@ Zotero_Preferences.Export = { type: 'checkbox', fixedWidth: true, width: 55, + }, + { + dataKey: "useOnDrag", + label: "preferences-quickCopy-siteEditor-use-on-drag", + fixedWidth: true, + width: 85, } ]; var handleKeyDown = (event) => { diff --git a/chrome/content/zotero/preferences/preferences_export.xhtml b/chrome/content/zotero/preferences/preferences_export.xhtml index e41d975e3e..0a803ede58 100644 --- a/chrome/content/zotero/preferences/preferences_export.xhtml +++ b/chrome/content/zotero/preferences/preferences_export.xhtml @@ -35,36 +35,50 @@ -