/* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ var { exportAttributes } = ChromeUtils.importESModule( "resource:///modules/AddrBookUtils.sys.mjs" ); /** * A component to config the mapping between CSV fields and address book fields. * For each CSV field, there is a elements. */ _initSelectOptions() { let fields; let fieldIndexes = Services.prefs.getCharPref("mail.import.csv.fields", ""); if (fieldIndexes) { // If the user has done CSV importing before, show the same field mapping. fieldIndexes = fieldIndexes.split(","); fields = fieldIndexes.map(i => (i == "" ? i : this._supportedFields[+i])); } else { // Show the same field orders as in an exported CSV file. fields = this._supportedFields; fieldIndexes = this._allFieldIndexes; } let i = 0; for (const select of this.querySelectorAll("select")) { if (fields[i]) { const option = document.createElement("option"); option.value = fieldIndexes[i]; option.textContent = fields[i]; select.add(option); } else { select.disabled = true; select.closest("tr").querySelector("input[type=checkbox]").checked = false; } i++; } this._updateSelectOptions(); } /** * When a element. */ _enableSelect(select) { const selects = [...this._elTbody.querySelectorAll("select")]; const selectedFieldIndexes = selects.map(fieldSelect => fieldSelect.value); const availableFieldIndexes = this._allFieldIndexes.filter( index => !selectedFieldIndexes.includes(index) ); for (let i = 0; i < availableFieldIndexes.length; i++) { const option = document.createElement("option"); option.value = availableFieldIndexes[i]; option.textContent = this._supportedFields[option.value]; select.add(option); } } /** * Update the options of all , this option should no longer be shown as an option * for other element. */ _updateSelectOptions(changedSelect) { const selects = [...this._elTbody.querySelectorAll("select")]; const selectedFieldIndexes = selects.map(select => select.value); const availableFieldIndexes = this._allFieldIndexes.filter( index => !selectedFieldIndexes.includes(index) ); for (const select of selects) { if (select.disabled || select == changedSelect) { continue; } for (let i = select.options.length - 1; i >= 0; i--) { // Remove unselected options first. if (i != select.selectedIndex) { select.remove(i); } } for (let i = 0; i < availableFieldIndexes.length; i++) { // Add all available options. const option = document.createElement("option"); option.value = availableFieldIndexes[i]; option.textContent = this._supportedFields[option.value]; select.add(option); } } } /** * Handle the change event of . */ _bindEvents() { this._elTbody.addEventListener("change", e => { const el = e.target; if (el.tagName == "select") { this._updateSelectOptions(el); } else if (el.tagName == "input" && el.type == "checkbox") { const select = el.closest("tr").querySelector("select"); select.disabled = !el.checked; if (select.disabled) { // Because it's disabled, remove all the options. for (let i = select.options.length - 1; i >= 0; i--) { select.remove(i); } } else { this._enableSelect(select); } this._updateSelectOptions(); } }); } /** * Render the table structure. */ async _renderLayout() { this.innerHTML = ""; const [ firstRowContainsHeaders, sourceField, sourceFirstRecord, sourceSecondRecord, targetField, ] = await document.l10n.formatValues([ "csv-first-row-contains-headers", "csv-source-field", "csv-source-first-record", "csv-source-second-record", "csv-target-field", ]); const label = document.createElement("label"); label.className = "toggle-container-with-text"; const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.checked = Services.prefs.getBoolPref( "mail.import.csv.skipfirstrow", true ); const labelText = document.createElement("span"); labelText.textContent = firstRowContainsHeaders; label.appendChild(checkbox); label.appendChild(labelText); this.appendChild(label); const table = document.createElement("table"); const thead = document.createElement("thead"); const tr = document.createElement("tr"); const headers = []; for (const colName of [sourceField, sourceFirstRecord, targetField, ""]) { const th = document.createElement("th"); th.textContent = colName; tr.appendChild(th); headers.push(th); } thead.appendChild(tr); table.appendChild(thead); this._elTbody = document.createElement("tbody"); table.appendChild(this._elTbody); this.appendChild(table); this._bindEvents(); checkbox.addEventListener("change", () => { if (checkbox.checked) { headers[0].textContent = sourceField; headers[1].textContent = sourceFirstRecord; } else { headers[0].textContent = sourceFirstRecord; headers[1].textContent = sourceSecondRecord; } Services.prefs.setBoolPref( "mail.import.csv.skipfirstrow", checkbox.checked ); }); } /** * Render the table content. Each row contains four columns: * Source field | Source Data | Address book field | */ _renderTable() { const colCount = this._rows[0].length; for (let i = 0; i < colCount; i++) { const tr = document.createElement("tr"); // Render the source field name and source data. for (let j = 0; j < this.DATA_ROWS_LIMIT; j++) { const td = document.createElement("td"); td.textContent = this._rows[j]?.[i] || ""; tr.appendChild(td); } // Render a