diff --git a/src/js/views/TableEditorView.js b/src/js/views/TableEditorView.js index 71f6dbde2..478c96e1d 100644 --- a/src/js/views/TableEditorView.js +++ b/src/js/views/TableEditorView.js @@ -5,22 +5,16 @@ define([ "markdownTableFromJson", "markdownTableToJson", "text!templates/tableEditor.html", -], function ( - _, - $, - Backbone, - markdownTableFromJson, - markdownTableToJson, - Template, -) { +], (_, $, Backbone, markdownTableFromJson, markdownTableToJson, Template) => { /** * @class TableEditorView - * @classdesc A view of an HTML textarea with markdown editor UI and preview tab + * @classdesc A view of an HTML textarea with markdown editor UI and preview + * tab * @classcategory Views - * @extends Backbone.View - * @constructor + * @augments Backbone.View + * @class */ - var TableEditorView = Backbone.View.extend( + const TableEditorView = Backbone.View.extend( /** @lends TableEditorView.prototype */ { /** @@ -48,14 +42,14 @@ define([ * header row * @type {number} */ - rowCount: 0, // No of rows + rowCount: 0, /** - * The current number of columns displayed in the spreadsheet, including the - * row number column + * The current number of columns displayed in the spreadsheet, including + * the row number column * @type {number} */ - colCount: 0, // No of cols + colCount: 0, /** * The same data shown in the table as a stringified JSON object. @@ -70,8 +64,9 @@ define([ sortingHistory: new Map(), /** - * The events this view will listen to and the associated function to call. - * @type {Object} + * The events this view will listen to and the associated function to + * call. + * @type {object} */ events: { "click #reset": "resetData", @@ -93,269 +88,204 @@ define([ /** * Initialize is executed when a new tableEditor is created. * @constructs TableEditorView - * @param {Object} options - A literal object with options to pass to the view - */ - initialize: function (options) { - try { - options = _.extend(this.defaults, options); - - // Get all the options and apply them to this view - if (options) { - var optionKeys = Object.keys(options); - _.each( - optionKeys, - function (key, i) { - this[key] = options[key]; - }, - this, - ); - } - } catch (e) { - console.log( - "Failed to initialize the table editor view, error message: " + e, - ); - } + * @param {object} options - A literal object with options to pass to the + * view + * @param {string} options.markdown - A markdown table to edit. + * @param {string} options.tableData - The table data as a stringified + * JSON in the form of an array of arrays. Only used if markdown is not + * provided. + */ + initialize(options = {}) { + const mergedOptions = { ...this.defaults, ...options }; + + Object.keys(mergedOptions).forEach((key) => { + this[key] = mergedOptions[key]; + }); }, /** - * render - Renders the tableEditor - add UI for creating and editing tables - */ - render: function () { - try { - // Insert the template into the view - this.$el - .html( - this.template({ - cid: this.cid, - }), - ) - .data("view", this); - - // If initalized with markdown, convert to JSON and use as table data - // Parse the table string into a javascript object so that we can pass it - // into the table editor view to be edited by the user. - if (this.markdown && this.markdown.length > 0) { - var tableArray = this.getJSONfromMarkdown(this.markdown); - if (tableArray && Array.isArray(tableArray) && tableArray.length) { - this.saveData(tableArray); - this.createSpreadsheet(); - // Add the column that we use for row numbers in the editor - this.addColumn(0, "left"); - } - } else { + * Renders the tableEditor - add UI for creating and editing tables + */ + render() { + // Insert the template into the view + this.$el + .html( + this.template({ + cid: this.cid, + }), + ) + .data("view", this); + + // If initalized with markdown, convert to JSON and use as table data + // Parse the table string into a javascript object so that we can pass + // it into the table editor view to be edited by the user. + if (this.markdown && this.markdown.length > 0) { + const tableArray = this.getJSONfromMarkdown(this.markdown); + if (tableArray && Array.isArray(tableArray) && tableArray.length) { + this.saveData(tableArray); this.createSpreadsheet(); + // Add the column that we use for row numbers in the editor + this.addColumn(0, "left"); } - } catch (e) { - console.log( - "Failed to render the table editor view, error message: " + e, - ); + } else { + this.createSpreadsheet(); } }, /** - * createSpreadsheet - Creates or re-creates the table & headers with data, - * if there is any. + * Creates or re-creates the table & headers with data, if there is any. */ - createSpreadsheet: function () { - try { - const spreadsheetData = this.getData(); + createSpreadsheet() { + const spreadsheetData = this.getData(); - this.rowCount = spreadsheetData.length - 1 || this.initialRowCount; - this.colCount = spreadsheetData[0].length - 1 || this.initialColCount; + this.rowCount = spreadsheetData.length - 1 || this.initialRowCount; + this.colCount = spreadsheetData[0].length - 1 || this.initialColCount; - const tableHeaderElement = this.$el.find(".table-headers")[0]; - const tableBodyElement = this.$el.find(".table-body")[0]; + const tableHeaderElement = this.$el.find(".table-headers")[0]; + const tableBodyElement = this.$el.find(".table-body")[0]; - const tableBody = tableBodyElement.cloneNode(true); - tableBodyElement.parentNode.replaceChild(tableBody, tableBodyElement); - const tableHeaders = tableHeaderElement.cloneNode(true); - tableHeaderElement.parentNode.replaceChild( - tableHeaders, - tableHeaderElement, - ); + const tableBody = tableBodyElement.cloneNode(true); + tableBodyElement.parentNode.replaceChild(tableBody, tableBodyElement); + const tableHeaders = tableHeaderElement.cloneNode(true); + tableHeaderElement.parentNode.replaceChild( + tableHeaders, + tableHeaderElement, + ); - tableHeaders.innerHTML = ""; - tableBody.innerHTML = ""; + tableHeaders.innerHTML = ""; + tableBody.innerHTML = ""; - tableHeaders.appendChild(this.createHeaderRow(this.colCount)); - this.createTableBody(tableBody, this.rowCount, this.colCount); + tableHeaders.appendChild(this.createHeaderRow(this.colCount)); + this.createTableBody(tableBody, this.rowCount, this.colCount); - this.populateTable(); - } catch (e) { - console.log( - "Failed to create a spreadsheet in the table editor view, error message: " + - e, - ); - } + this.populateTable(); }, /** - * populateTable - Fill data in created table from saved data + * Fill data in created table from saved data */ - populateTable: function () { - try { - const data = this.getData(); - if (data === undefined || data === null) return; - - for (let i = 0; i < data.length; i++) { - for (let j = 1; j < data[i].length; j++) { - const cell = this.$el.find(`#r-${i}-${j}`)[0]; - let value = data[i][j]; - if (i > 0) { - cell.innerHTML = data[i][j]; - } else { - // table headers - if (!value) { - value = "Col " + j; - } - $(cell).find(".column-header-span")[0].innerHTML = value; + populateTable() { + const data = this.getData(); + if (data === undefined || data === null) return; + + for (let i = 0; i < data.length; i += 1) { + for (let j = 1; j < data[i].length; j += 1) { + const cell = this.$el.find(`#r-${i}-${j}`)[0]; + let value = data[i][j]; + if (i > 0) { + cell.innerHTML = data[i][j]; + } else { + // table headers + if (!value) { + value = `Col ${j}`; } + // TODO: test this + $(cell).find(".column-header-span").text(value); } } - } catch (e) { - console.log( - "Failed to populate the table in the table editor view, error message: " + - e, - ); } }, /** - * getData - Get the saved data and parse it. If there's no saved data, - * create it. + * Get the saved data and parse it. If there's no saved data, create it. + * @returns {Array} The table data as an array of arrays */ - getData: function () { - try { - let data = this.tableData; - if (data === undefined || data === null || data.length == 0) { - return this.initializeData(); - } - return JSON.parse(data); - } catch (e) { - console.log( - "Failed to get and parse data in the Table Editor View, error message: " + - e, - ); + getData() { + const data = this.tableData; + if (!data) { + return this.initializeData(); } + return JSON.parse(data); }, /** - * initializeData - Create some empty arrays to hold data + * Create some empty arrays to hold data + * @returns {Array} An array of arrays, each of which is an empty array */ - initializeData: function () { - try { - const data = []; - for (let i = 0; i <= this.rowCount; i++) { - const child = []; - for (let j = 0; j <= this.colCount; j++) { - child.push(""); - } - data.push(child); + initializeData() { + const data = []; + for (let i = 0; i <= this.rowCount; i += 1) { + const child = []; + for (let j = 0; j <= this.colCount; j += 1) { + child.push(""); } - return data; - } catch (e) { - console.log( - "Failed to create new data in the Table Editor View, error message: " + - e, - ); + data.push(child); } + return data; }, /** - * updateData - When the user focuses out, presume they've changed the data, - * and updated the saved data. - * + * When the user focuses out, presume they've changed the data, and + * updated the saved data. * @param {event} e The focus out event that triggered this function */ - updateData: function (e) { - try { - if (e.target) { - let item; - let newValue; - if (e.target.nodeName === "TD") { - item = e.target; - newValue = item.textContent; - } else if (e.target.classList.contains("column-header-span")) { - item = e.target.parentNode; - newValue = e.target.textContent; - } - if (item) { - const indices = item.id.split("-"); - let spreadsheetData = this.getData(); - spreadsheetData[indices[1]][indices[2]] = newValue; - this.saveData(spreadsheetData); - } + updateData(e) { + if (e.target) { + let item; + let newValue; + if (e.target.nodeName === "TD") { + item = e.target; + newValue = item.textContent; + } else if (e.target.classList.contains("column-header-span")) { + item = e.target.parentNode; + newValue = e.target.textContent; + } + if (item) { + const indices = item.id.split("-"); + const spreadsheetData = this.getData(); + spreadsheetData[indices[1]][indices[2]] = newValue; + this.saveData(spreadsheetData); } - } catch (e) { - console.log( - "Failed to update data in the Table Editor View, error message: " + - e, - ); } }, /** - * saveData - Save the data as a string. - * - * @param {type} data description - * @return {type} description + * Save the data as a string on the tableData property of the view + * @param {Array} data The table data as an array of arrays */ - saveData: function (data) { - try { - this.tableData = JSON.stringify(data); - } catch (e) { - console.log( - "Failed to save data in the Table Editor View, error message: " + e, - ); - } + saveData(data) { + this.tableData = JSON.stringify(data); }, /** - * resetData - Clear the saved data and reset the table to the default - * number of rows & columns - * - * @param {event} e - the event that triggered this function + * Clear the saved data and reset the table to the default number of rows + * & columns + * @param {event} _e - the event that triggered this function */ - resetData: function (e) { - try { - confirmation = confirm( - "This will erase all data and reset the table. Are you sure?", - ); - if (confirmation == true) { - this.tableData = ""; - this.rowCount = this.initialRowCount; - this.colCount = this.initialColCount; - this.createSpreadsheet(); - } else { - return; - } - } catch (e) { - console.log( - "Failed to reset data in the Table Editor View, error message: " + - e, - ); + resetData(_e) { + // eslint-disable-next-line no-restricted-globals, no-alert + const confirmation = confirm( + "This will erase all data and reset the table. Are you sure?", + ); + if (confirmation === true) { + this.tableData = ""; + this.rowCount = this.initialRowCount; + this.colCount = this.initialColCount; + this.createSpreadsheet(); + } else { + // TODO? } }, /** - * createHeaderRow - Create a header row for the table - */ - createHeaderRow: function () { - try { - const tr = document.createElement("tr"); - tr.setAttribute("id", "r-0"); - for (let i = 0; i <= this.colCount; i++) { - const th = document.createElement("th"); - th.setAttribute("id", `r-0-${i}`); - th.setAttribute("class", `${i === 0 ? "" : "column-header"}`); - if (i !== 0) { - const span = document.createElement("span"); - span.innerHTML = `Col ${i}`; - span.setAttribute("class", "column-header-span"); - span.setAttribute("contentEditable", "true"); - const dropDownDiv = document.createElement("div"); - dropDownDiv.setAttribute("class", "dropdown"); - dropDownDiv.innerHTML = ` + * Create a header row for the table + * @returns {HTMLElement} The header row element + */ + createHeaderRow() { + const tr = document.createElement("tr"); + tr.setAttribute("id", "r-0"); + for (let i = 0; i <= this.colCount; i += 1) { + const th = document.createElement("th"); + th.setAttribute("id", `r-0-${i}`); + th.setAttribute("class", `${i === 0 ? "" : "column-header"}`); + if (i !== 0) { + const span = document.createElement("span"); + span.innerHTML = `Col ${i}`; + span.setAttribute("class", "column-header-span"); + span.setAttribute("contentEditable", "true"); + const dropDownDiv = document.createElement("div"); + dropDownDiv.setAttribute("class", "dropdown"); + dropDownDiv.innerHTML = ` @@ -366,39 +296,33 @@ define([ `; - th.appendChild(span); - th.appendChild(dropDownDiv); - } - tr.appendChild(th); + th.appendChild(span); + th.appendChild(dropDownDiv); } - return tr; - } catch (e) { - console.log( - "Failed to create header row in the Table Editor View, error message: " + - e, - ); + tr.appendChild(th); } + return tr; }, /** - * createTableBodyRow - Create a row for the table - * - * @param {number} rowNum The table row number to add to the table, where 0 is the header row - */ - createTableBodyRow: function (rowNum) { - try { - const tr = document.createElement("tr"); - tr.setAttribute("id", `r-${rowNum}`); - for (let i = 0; i <= this.colCount; i++) { - const cell = document.createElement(`${i === 0 ? "th" : "td"}`); - // header - if (i === 0) { - cell.contentEditable = false; - const span = document.createElement("span"); - const dropDownDiv = document.createElement("div"); - span.innerHTML = rowNum; - dropDownDiv.setAttribute("class", "dropdown"); - dropDownDiv.innerHTML = ` + * Create a row for the table + * @param {number} rowNum The table row number to add to the table, where + * 0 is the header row + * @returns {HTMLElement} The row element + */ + createTableBodyRow(rowNum) { + const tr = document.createElement("tr"); + tr.setAttribute("id", `r-${rowNum}`); + for (let i = 0; i <= this.colCount; i += 1) { + const cell = document.createElement(`${i === 0 ? "th" : "td"}`); + // header + if (i === 0) { + cell.contentEditable = false; + const span = document.createElement("span"); + const dropDownDiv = document.createElement("div"); + span.innerHTML = rowNum; + dropDownDiv.setAttribute("class", "dropdown"); + dropDownDiv.innerHTML = ` @@ -408,441 +332,332 @@ define([ `; - cell.appendChild(span); - cell.appendChild(dropDownDiv); - cell.setAttribute("class", "row-header"); - } else { - cell.contentEditable = true; - } - cell.setAttribute("id", `r-${rowNum}-${i}`); - tr.appendChild(cell); + cell.appendChild(span); + cell.appendChild(dropDownDiv); + cell.setAttribute("class", "row-header"); + } else { + cell.contentEditable = true; } - return tr; - } catch (e) { - console.log( - "Failed to create table row in the Table Editor View, error message: " + - e, - ); + cell.setAttribute("id", `r-${rowNum}-${i}`); + tr.appendChild(cell); } + return tr; }, /** - * createTableBody - Given a table element, add table rows - * + * Given a table element, add table rows * @param {HTMLElement} tableBody A table HTML Element */ - createTableBody: function (tableBody) { - try { - for (let rowNum = 1; rowNum <= this.rowCount; rowNum++) { - tableBody.appendChild(this.createTableBodyRow(rowNum)); - } - } catch (e) { - console.log( - "Failed to create table body in the Table Editor View, error message: " + - e, - ); + createTableBody(tableBody) { + for (let rowNum = 1; rowNum <= this.rowCount; rowNum += 1) { + tableBody.appendChild(this.createTableBodyRow(rowNum)); } }, /** - * addRow - Utility function to add row - * + * Utility function to add row * @param {number} currentRow The row number at which to add a new row - * @param {string} direction Can be "top" or "bottom", indicating whether to new row should be above or below the current row - */ - addRow: function (currentRow, direction) { - try { - let data = this.getData(); - const colCount = data[0].length; - const newRow = new Array(colCount).fill(""); - if (direction === "top") { - data.splice(currentRow, 0, newRow); - } else if (direction === "bottom") { - data.splice(currentRow + 1, 0, newRow); - } - this.rowCount++; - this.saveData(data); - this.createSpreadsheet(); - } catch (e) { - console.log( - "Failed to add row in the Table Editor View, error message: " + e, - ); + * @param {string} direction Can be "top" or "bottom", indicating + * whether to new row should be above or below the current row + */ + addRow(currentRow, direction) { + const data = this.getData(); + const colCount = data[0].length; + const newRow = new Array(colCount).fill(""); + if (direction === "top") { + data.splice(currentRow, 0, newRow); + } else if (direction === "bottom") { + data.splice(currentRow + 1, 0, newRow); } + this.rowCount += 1; + this.saveData(data); + this.createSpreadsheet(); }, /** - * deleteRow - Utility function to delete row - * + * Utility function to delete row * @param {number} currentRow The row number to delete */ - deleteRow: function (currentRow) { - try { - let data = this.getData(); - // Don't allow deletion of the last row - if (data.length <= 2) { - this.resetData(); - return; - } - data.splice(currentRow, 1); - this.rowCount--; - this.saveData(data); - this.createSpreadsheet(); - } catch (e) { - console.log( - "Failed to delete row in the Table Editor View, error message: " + - e, - ); + deleteRow(currentRow) { + const data = this.getData(); + // Don't allow deletion of the last row + if (data.length <= 2) { + this.resetData(); + return; } + data.splice(currentRow, 1); + this.rowCount -= 1; + this.saveData(data); + this.createSpreadsheet(); }, /** - * addColumn - Utility function to add columns - * - * @param {number} currentCol The column number at which to add a new column - * @param {string} direction Can be "left" or "right", indicating whether to new column should be to the left or right of the current column - */ - addColumn: function (currentCol, direction) { - try { - let data = this.getData(); - for (let i = 0; i <= this.rowCount; i++) { - if (direction === "left") { - data[i].splice(currentCol, 0, ""); - } else if (direction === "right") { - data[i].splice(currentCol + 1, 0, ""); - } + * Utility function to add columns + * @param {number} currentCol The column number at which to add a new + * column + * @param {string} direction Can be "left" or "right", indicating + * whether to new column should be to the left or right of the current + * column + */ + addColumn(currentCol, direction) { + const data = this.getData(); + for (let i = 0; i <= this.rowCount; i += 1) { + if (direction === "left") { + data[i].splice(currentCol, 0, ""); + } else if (direction === "right") { + data[i].splice(currentCol + 1, 0, ""); } - this.colCount++; - this.saveData(data); - this.createSpreadsheet(); - } catch (e) { - console.log( - "Failed to add column in the Table Editor View, error message: " + - e, - ); } + this.colCount += 1; + this.saveData(data); + this.createSpreadsheet(); }, /** - * deleteColumn - Utility function to delete column - * + * Utility function to delete column * @param {number} currentCol The number of the column to delete */ - deleteColumn: function (currentCol) { - try { - let data = this.getData(); - // Don't allow deletion of the last column - if (data[0].length <= 2) { - this.resetData(); - return; - } - for (let i = 0; i <= this.rowCount; i++) { - data[i].splice(currentCol, 1); - } - this.colCount--; - this.saveData(data); - this.createSpreadsheet(); - } catch (e) { - console.log( - "Failed to delete column in the Table Editor View, error message: " + - e, - ); + deleteColumn(currentCol) { + const data = this.getData(); + // Don't allow deletion of the last column + if (data[0].length <= 2) { + this.resetData(); + return; + } + for (let i = 0; i <= this.rowCount; i += 1) { + data[i].splice(currentCol, 1); } + this.colCount -= 1; + this.saveData(data); + this.createSpreadsheet(); }, /** - * sortColumn - Utility function to sort columns - * + * Utility function to sort columns * @param {number} currentCol The column number of the column to delete */ - sortColumn: function (currentCol) { - try { - let spreadSheetData = this.getData(); - let data = spreadSheetData.slice(1); - let headers = spreadSheetData.slice(0, 1)[0]; - if (!data.some((a) => a[currentCol] !== "")) return; - if (this.sortingHistory.has(currentCol)) { - const sortOrder = this.sortingHistory.get(currentCol); - switch (sortOrder) { - case "desc": - data.sort(this.ascSort.bind(this, currentCol)); - this.sortingHistory.set(currentCol, "asc"); - break; - case "asc": - data.sort(this.dscSort.bind(this, currentCol)); - this.sortingHistory.set(currentCol, "desc"); - break; - } - } else { + sortColumn(currentCol) { + const spreadSheetData = this.getData(); + const data = spreadSheetData.slice(1); + const headers = spreadSheetData.slice(0, 1)[0]; + if (!data.some((a) => a[currentCol] !== "")) return; + if (this.sortingHistory.has(currentCol)) { + const sortOrder = this.sortingHistory.get(currentCol); + if (sortOrder === "desc") { data.sort(this.ascSort.bind(this, currentCol)); this.sortingHistory.set(currentCol, "asc"); + } else { + data.sort(this.dscSort.bind(this, currentCol)); + this.sortingHistory.set(currentCol, "desc"); } - data.splice(0, 0, headers); - this.saveData(data); - this.createSpreadsheet(); - } catch (e) { - console.log( - "Failed to sort column in the Table Editor View, error message: " + - e, - ); + } else { + data.sort(this.ascSort.bind(this, currentCol)); + this.sortingHistory.set(currentCol, "asc"); } + data.splice(0, 0, headers); + this.saveData(data); + this.createSpreadsheet(); }, /** - * ascSort - Compare Functions for sorting - ascending - * + * Compare Functions for sorting - ascending * @param {number} currentCol The number of the column to sort - * @param {*} a One of two items to compare - * @param {*} b The second of two items to compare - * @return {number} A number indicating the order to place a vs b in the list. It it returns less than zero, then a will be placed before b in the list. + * @param {*} a One of two items to compare + * @param {*} b The second of two items to compare + * @returns {number} A number indicating the order to place a vs b in the + * list. It it returns less than zero, then a will be placed before b in + * the list. */ - ascSort: function (currentCol, a, b) { + ascSort(currentCol, a, b) { try { - let _a = a[currentCol]; - let _b = b[currentCol]; - if (_a === "") return 1; - if (_b === "") return -1; + let valA = a[currentCol]; + let valB = b[currentCol]; + if (valA === "") return 1; + if (valB === "") return -1; // Check for strings and numbers - if (isNaN(_a) || isNaN(_b)) { - _a = _a.toUpperCase(); - _b = _b.toUpperCase(); - if (_a < _b) return -1; - if (_a > _b) return 1; + if (Number.isNaN(valA) || Number.isNaN(valB)) { + valA = valA.toUpperCase(); + valB = valB.toUpperCase(); + if (valA < valB) return -1; + if (valA > valB) return 1; return 0; } - return _a - _b; + return valA - valB; } catch (e) { - console.log( - "The ascending compare function in Table Editor View failed, error message: " + - e, - ); return 0; } }, /** - * dscSort - Descending compare function - * + * Descending compare function * @param {number} currentCol The number of the column to sort * @param {*} a One of two items to compare * @param {*} b The second of two items to compare - * @return {number} A number indicating the order to place a vs b in the list. It it returns less than zero, then a will be placed before b in the list. + * @returns {number} A number indicating the order to place a vs + * b in the list. It it returns less than zero, then a will be placed + * before b in the list. */ - dscSort: function (currentCol, a, b) { + dscSort(currentCol, a, b) { try { - let _a = a[currentCol]; - let _b = b[currentCol]; - if (_a === "") return 1; - if (_b === "") return -1; + let valA = a[currentCol]; + let valB = b[currentCol]; + if (valA === "") return 1; + if (valB === "") return -1; // Check for strings and numbers - if (isNaN(_a) || isNaN(_b)) { - _a = _a.toUpperCase(); - _b = _b.toUpperCase(); - if (_a < _b) return 1; - if (_a > _b) return -1; + if (Number.isNaN(valA) || Number.isNaN(valB)) { + valA = valA.toUpperCase(); + valB = valB.toUpperCase(); + if (valA < valB) return 1; + if (valA > valB) return -1; return 0; } - return _b - _a; + return valB - valA; } catch (e) { - console.log( - "The descending compare function in Table Editor View failed, error message: " + - e, - ); return 0; } }, /** - * convertToMarkdown - Returns the table data as markdown - * - * @return {string} The markdownified table as string + * Returns the table data as markdown + * @returns {string} The markdownified table as string */ - getMarkdown: function () { - try { - // Ensure there are at least two dashes below the table header, - // i.e. use | -- | not | - | - // Showdown requries this to avoid ambiguous markdown. - const minStringLength = function (s) { - l = s.length <= 1 ? 2 : s.length; - return l; - }; - // Get the current table data - var tableData = this.getData(); - // Remove the empty column that we use for row numbers first - if (this.hasEmptyCol1(tableData)) { - for (let i = 0; i <= tableData.length - 1; i++) { - tableData[i].splice(0, 1); - } + getMarkdown() { + // Ensure there are at least two dashes below the table header, i.e. use + // | -- | not | - | Showdown requries this to avoid ambiguous markdown. + const minStringLength = (s) => (s.length <= 1 ? 2 : s.length); + // Get the current table data + const tableData = this.getData(); + // Remove the empty column that we use for row numbers first + if (this.hasEmptyCol1(tableData)) { + for (let i = 0; i <= tableData.length - 1; i += 1) { + tableData[i].splice(0, 1); } - // Convert json data to markdown, for options see https://github.com/wooorm/markdown-table - // TODO: Add alignment information that we will store in view as an array - // Include in markdownTableFromJson() options like this - align: ['l', 'c', 'r'] - var markdown = markdownTableFromJson(tableData, { - stringLength: minStringLength, - }); - // Add a new line to the end - return markdown + "\n"; - } catch (e) { - console.log( - "Failed to convert json to markdown in the Table Editor View, error message: " + - e, - ); - return ""; } + // Convert json data to markdown, for options see + // https://github.com/wooorm/markdown-table TODO: Add alignment + // information that we will store in view as an array Include in + // markdownTableFromJson() options like this - align: ['l', 'c', 'r'] + const markdown = markdownTableFromJson(tableData, { + stringLength: minStringLength, + }); + // Add a new line to the end + return `${markdown}\n`; }, /** - * getJSONfromMarkdown - Converts a given markdown table string to JSON. - * + * Converts a given markdown table string to JSON. * @param {string} markdown description - * @return {Array} The markdown table as an array of arrays, where the header is the first array and each row is an array that follows. - */ - getJSONfromMarkdown: function (markdown) { - try { - parsedMarkdown = markdownTableToJson(markdown); - if (!parsedMarkdown) return; - // TODO: Add alignment information to the view, returned as parsedMarkdown.align - return parsedMarkdown.table; - } catch (e) { - console.log( - "Failed to parse markdown in the Table Editor View, error message: " + - e, - ); - return []; - } + * @returns {Array} The markdown table as an array of arrays, + * where the header is the first array and each row is an array that + * follows. + */ + getJSONfromMarkdown(markdown) { + const parsedMarkdown = markdownTableToJson(markdown); + if (!parsedMarkdown) return null; + // TODO: Add alignment information to the view, returned as + // parsedMarkdown.align + return parsedMarkdown.table; }, /** - * hasEmptyCol1 - Checks whether the first column is empty. - * - * @param {Object} data The table data in the form of an array of arrays - * @return {boolean} returns true if the first column is empty, false if at least one cell in the first column contains a value + * Checks whether the first column is empty. + * @param {object} data The table data in the form of an array of arrays + * @returns {boolean} returns true if the first column is empty, false + * if at least one cell in the first column contains a value */ - hasEmptyCol1: function (data) { - try { - var firstColEmpty = true; - // Check if the first item in each row is blank - for (let i = 0; i <= data.length - 1; i++) { - if (data[i][0] != "") { - firstColEmpty = false; - break; - } + hasEmptyCol1(data) { + let firstColEmpty = true; + // Check if the first item in each row is blank + for (let i = 0; i <= data.length - 1; i += 1) { + if (data[i][0] !== "" && data[i][0] !== undefined) { + firstColEmpty = false; + break; } - return firstColEmpty; - } catch (e) { - console.log( - "Failed to detect if there's an empty first column in the Table Editor View. Assuming the first column has data, but this could cause some issues. Error message: " + - e, - ); - return false; } + return firstColEmpty; }, /** - * closeDropdown - Close the dropdown menu if the user clicks outside of it - * + * Close the dropdown menu if the user clicks outside of it * @param {type} e The event that triggered this function */ - closeDropdown: function (e) { - try { - if (!e.target.matches(".dropbtn") || !e) { - var dropdowns = document.getElementsByClassName("dropdown-content"); - var i; - for (i = 0; i < dropdowns.length; i++) { - var openDropdown = dropdowns[i]; - if (openDropdown.classList.contains("show")) { - openDropdown.classList.remove("show"); - } + closeDropdown(e) { + if (!e.target.matches(".dropbtn") || !e) { + const dropdowns = document.getElementsByClassName("dropdown-content"); + let i; + for (i = 0; i < dropdowns.length; i += 1) { + const openDropdown = dropdowns[i]; + if (openDropdown.classList.contains("show")) { + openDropdown.classList.remove("show"); } } - } catch (e) { - console.log( - "Failed to close a dropdown menu in the Table Editor View, error message: " + - e, - ); } }, /** - * handleHeadersClick - Called when the table header is clicked. Depending - * on what is clicked, shows or hides the dropdown menus in the header, - * or calls one of the functions listed in the menu (e.g. delete column). - * + * Called when the table header is clicked. Depending on what is clicked, + * shows or hides the dropdown menus in the header, or calls one of the + * functions listed in the menu (e.g. delete column). * @param {event} e The event that triggered this function */ - handleHeadersClick: function (e) { - try { - var view = this; - if (e.target) { - var classes = e.target.classList; - - if (classes.contains("column-header-span")) { - // If the header element is clicked... - } else if (classes.contains("dropbtn")) { - const idArr = e.target.id.split("-"); - document - .getElementById(`col-dropdown-${idArr[2]}`) - .classList.toggle("show"); - } else if (classes.contains("col-dropdown-option")) { - const index = e.target.parentNode.id.split("-")[2]; - - if (classes.contains("col-insert-left")) { - view.addColumn(index, "left"); - } else if (classes.contains("col-insert-right")) { - view.addColumn(index, "right"); - } else if (classes.contains("col-sort")) { - view.sortColumn(index); - } else if (classes.contains("col-delete")) { - view.deleteColumn(index); - } + handleHeadersClick(e) { + const view = this; + if (e.target) { + const classes = e.target.classList; + + if (classes.contains("column-header-span")) { + // If the header element is clicked... + } else if (classes.contains("dropbtn")) { + const idArr = e.target.id.split("-"); + document + .getElementById(`col-dropdown-${idArr[2]}`) + .classList.toggle("show"); + } else if (classes.contains("col-dropdown-option")) { + const index = e.target.parentNode.id.split("-")[2]; + + if (classes.contains("col-insert-left")) { + view.addColumn(index, "left"); + } else if (classes.contains("col-insert-right")) { + view.addColumn(index, "right"); + } else if (classes.contains("col-sort")) { + view.sortColumn(index); + } else if (classes.contains("col-delete")) { + view.deleteColumn(index); } } - } catch (e) { - console.log( - "Failed to handle a click in the table header in the Table Editor View, error message: " + - e, - ); } }, /** - * handleHeadersClick - Called when the table body is clicked. Depending - * on what is clicked, shows or hides the dropdown menus in the body, - * or calls one of the functions listed in the menu (e.g. delete row). - * - * @param {type} e description - * @return {type} description + * Called when the table body is clicked. Depending on what is clicked, + * shows or hides the dropdown menus in the body, or calls one of the + * functions listed in the menu (e.g. delete row). + * @param {type} e The event that triggered this function */ - handleBodyClick: function (e) { - try { - var view = this; - if (e.target) { - var classes = e.target.classList; - - if (classes.contains("dropbtn")) { - const idArr = e.target.id.split("-"); - view.$el - .find(`#row-dropdown-${idArr[2]}`)[0] - .classList.toggle("show"); - } else if (classes.contains("row-dropdown-option")) { - const index = parseInt(e.target.parentNode.id.split("-"))[2]; - if (classes.contains("row-insert-top")) { - view.addRow(index, "top"); - } - if (classes.contains("row-insert-bottom")) { - view.addRow(index, "bottom"); - } - if (classes.contains("row-delete")) { - view.deleteRow(index); - } + handleBodyClick(e) { + const view = this; + if (e.target) { + const classes = e.target.classList; + + if (classes.contains("dropbtn")) { + const idArr = e.target.id.split("-"); + view.$el + .find(`#row-dropdown-${idArr[2]}`)[0] + .classList.toggle("show"); + } else if (classes.contains("row-dropdown-option")) { + const index = parseInt(e.target.parentNode.id.split("-"), 10)[2]; + if (classes.contains("row-insert-top")) { + view.addRow(index, "top"); + } + if (classes.contains("row-insert-bottom")) { + view.addRow(index, "bottom"); + } + if (classes.contains("row-delete")) { + view.deleteRow(index); } } - } catch (e) { - console.log( - "Failed to handle a click in the table body in the Table Editor View, error message: " + - e, - ); } }, },