diff --git a/ReactNativeClient/lib/import-enex-md-gen.js b/ReactNativeClient/lib/import-enex-md-gen.js index e11afa5a62..fa862050b6 100644 --- a/ReactNativeClient/lib/import-enex-md-gen.js +++ b/ReactNativeClient/lib/import-enex-md-gen.js @@ -320,15 +320,6 @@ function enexXmlToMdArray(stream, resources) { type: 'table', lines: [], parent: section, - toString: function() { - let output = []; - output.push(BLOCK_OPEN); - for (let i = 0; i < this.lines.length; i++) { - output = output.concat(this.lines[i].toMdLines()); - } - output.push(BLOCK_CLOSE); - return processMdArrayNewLines(output); - }, }; section.lines.push(newSection); section = newSection; @@ -345,17 +336,6 @@ function enexXmlToMdArray(stream, resources) { lines: [], parent: section, isHeader: false, - // Normally tables are rendered properly as markdown, but for table within table within table... we cannot - // handle this in Markdown so simply render it as one cell per line. - toMdLines: function() { - let output = []; - output.push(BLOCK_OPEN); - for (let i = 0; i < this.lines.length; i++) { - output.push(this.lines[i].toString()); - } - output.push(BLOCK_CLOSE); - return output; - }, } section.lines.push(newSection); @@ -372,9 +352,6 @@ function enexXmlToMdArray(stream, resources) { type: 'td', lines: [], parent: section, - toString: function() { - return processMdArrayNewLines(this.lines); - }, }; section.lines.push(newSection); @@ -577,6 +554,18 @@ function enexXmlToMdArray(stream, resources) { section.lines.pop(); section.lines.push(url); } else { + // Need to remove any new line character between the current ']' and the previous '[' + // otherwise it won't render properly. + for (let i = section.lines.length - 1; i >= 0; i--) { + const c = section.lines[i]; + if (c === '[') { + break; + } else { + if (c === BLOCK_CLOSE || c === BLOCK_OPEN || c === NEWLINE) { + section.lines[i] = SPACE; + } + } + } section.lines.push('](' + url + ')'); } } else if (isListTag(n)) { @@ -607,50 +596,28 @@ function enexXmlToMdArray(stream, resources) { }); } -function setTableCellContent(table) { - if (!table.type == 'table') throw new Error('Only for tables'); - - for (let trIndex = 0; trIndex < table.lines.length; trIndex++) { - const tr = table.lines[trIndex]; - for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) { - let td = tr.lines[tdIndex]; - td.content = processMdArrayNewLines(td.lines); - td.content = td.content.replace(/\n\n\n\n\n/g, ' '); - td.content = td.content.replace(/\n\n\n\n/g, ' '); - td.content = td.content.replace(/\n\n\n/g, ' '); - td.content = td.content.replace(/\n\n/g, ' '); - td.content = td.content.replace(/\n/g, ' '); - } - } - - return table; +function removeTableCellNewLines(cellText) { + return cellText.replace(/\n+/g, " "); } -function cellWidth(cellText) { - const lines = cellText.split("\n"); - let maxWidth = 0; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (line.length > maxWidth) maxWidth = line.length; - } - return maxWidth; -} - -function colWidths(table) { - let output = []; +function tableHasSubTables(table) { for (let trIndex = 0; trIndex < table.lines.length; trIndex++) { const tr = table.lines[trIndex]; for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) { const td = tr.lines[tdIndex]; - const w = Math.min(cellWidth(td.content), 20); // Have to set a max width otherwise it can be extremely long for notes that import entire web pages (eg. Hacker News comment pages) - if (output.length <= tdIndex) output.push(0); - if (w > output[tdIndex]) output[tdIndex] = w; + for (let i = 0; i < td.lines.length; i++) { + if (typeof td.lines[i] === 'object') return true; + } } } - return output; + return false; } -function drawTable(table, colWidths) { +// Markdown tables don't support tables within tables, which is common in notes that are complete web pages, for example when imported +// via Web Clipper. So to handle this, we render all the outer tables as regular text (as if replacing all the , and
+// elements by
) and only the inner ones, those that don't contain any other tables, are rendered as actual tables. This is generally +// the required behaviour since the outer tables are usually for layout and the inner ones are the content. +function drawTable(table) { // | First Header | Second Header | // | ------------- | ------------- | // | Content Cell | Content Cell | @@ -658,8 +625,11 @@ function drawTable(table, colWidths) { // There must be at least 3 dashes separating each header cell. // https://gist.github.com/IanWang/28965e13cdafdef4e11dc91f578d160d#tables + + const flatRender = tableHasSubTables(table); // Render the table has regular text const minColWidth = 3; let lines = []; + lines.push(BLOCK_OPEN); let headerDone = false; for (let trIndex = 0; trIndex < table.lines.length; trIndex++) { const tr = table.lines[trIndex]; @@ -667,37 +637,79 @@ function drawTable(table, colWidths) { let line = []; let headerLine = []; let emptyHeader = null; - for (let tdIndex = 0; tdIndex < colWidths.length; tdIndex++) { - const width = Math.max(minColWidth, colWidths[tdIndex]); - const cell = tr.lines[tdIndex] ? tr.lines[tdIndex].content : ''; - line.push(stringPadding(cell, width, ' ', stringPadding.RIGHT)); + for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) { + const td = tr.lines[tdIndex]; - if (!headerDone) { - if (!isHeader) { - if (!emptyHeader) emptyHeader = []; - let h = stringPadding(' ', width, ' ', stringPadding.RIGHT); - if (!width) h = ''; - emptyHeader.push(h); + if (flatRender) { + line.push(BLOCK_OPEN); + + let currentCells = []; + + const renderCurrentCells = () => { + if (!currentCells.length) return; + const cellText = processMdArrayNewLines(currentCells); + line.push(cellText); + currentCells = []; } - headerLine.push('-'.repeat(width)); + + // In here, recursively render the tables + for (let i = 0; i < td.lines.length; i++) { + const c = td.lines[i]; + if (typeof c === 'object') { // This is a table + renderCurrentCells(); + currentCells = currentCells.concat(drawTable(c)); + } else { // This is plain text + currentCells.push(c); + } + } + + renderCurrentCells(); + + line.push(BLOCK_CLOSE); + } else { // Regular table rendering + + // A cell in a Markdown table cannot have new lines so remove them + const cellText = removeTableCellNewLines(processMdArrayNewLines(td.lines)); + + const width = Math.max(cellText.length, 3); + line.push(stringPadding(cellText, width, ' ', stringPadding.RIGHT)); + + if (!headerDone) { + if (!isHeader) { + if (!emptyHeader) emptyHeader = []; + let h = stringPadding(' ', width, ' ', stringPadding.RIGHT); + emptyHeader.push(h); + } + headerLine.push('-'.repeat(width)); + } + } } - if (emptyHeader) { - lines.push('| ' + emptyHeader.join(' | ') + ' |'); - lines.push('| ' + headerLine.join(' | ') + ' |'); + if (flatRender) { headerDone = true; - } + lines.push(BLOCK_OPEN); + lines = lines.concat(line); + lines.push(BLOCK_CLOSE); + } else { + if (emptyHeader) { + lines.push('| ' + emptyHeader.join(' | ') + ' |'); + lines.push('| ' + headerLine.join(' | ') + ' |'); + headerDone = true; + } - lines.push('| ' + line.join(' | ') + ' |'); + lines.push('| ' + line.join(' | ') + ' |'); - if (!headerDone) { - lines.push('| ' + headerLine.join(' | ') + ' |'); - headerDone = true; + if (!headerDone) { + lines.push('| ' + headerLine.join(' | ') + ' |'); + headerDone = true; + } } } - return lines.join('<<<<:D>>>>' + NEWLINE + '<<<<:D>>>>').split('<<<<:D>>>>'); + lines.push(BLOCK_CLOSE); + + return flatRender ? lines : lines.join('<<<<:D>>>>' + NEWLINE + '<<<<:D>>>>').split('<<<<:D>>>>'); } async function enexXmlToMd(stream, resources) { @@ -708,13 +720,9 @@ async function enexXmlToMd(stream, resources) { for (let i = 0; i < result.content.lines.length; i++) { let line = result.content.lines[i]; if (typeof line === 'object') { // A table - let table = setTableCellContent(line); - //console.log(require('util').inspect(table, false, null)) - const cw = colWidths(table); - const tableLines = drawTable(table, cw); - mdLines.push(BLOCK_OPEN); + const table = line; + const tableLines = drawTable(table); mdLines = mdLines.concat(tableLines); - mdLines.push(BLOCK_CLOSE); } else { // an actual line mdLines.push(line); }