mirror of
https://github.com/zadam/trilium.git
synced 2025-11-02 11:26:15 +01:00
Add 'packages/turndown-plugin-gfm/' from commit '0f43299c17108e9508e7e6237369e86f504ea60a'
git-subtree-dir: packages/turndown-plugin-gfm git-subtree-mainline:ebebdd0b07git-subtree-split:0f43299c17
This commit is contained in:
15
packages/turndown-plugin-gfm/src/gfm.js
Normal file
15
packages/turndown-plugin-gfm/src/gfm.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import highlightedCodeBlock from './highlighted-code-block'
|
||||
import strikethrough from './strikethrough'
|
||||
import tables from './tables'
|
||||
import taskListItems from './task-list-items'
|
||||
|
||||
function gfm (turndownService) {
|
||||
turndownService.use([
|
||||
highlightedCodeBlock,
|
||||
strikethrough,
|
||||
tables,
|
||||
taskListItems
|
||||
])
|
||||
}
|
||||
|
||||
export { gfm, highlightedCodeBlock, strikethrough, tables, taskListItems }
|
||||
25
packages/turndown-plugin-gfm/src/highlighted-code-block.js
Normal file
25
packages/turndown-plugin-gfm/src/highlighted-code-block.js
Normal file
@@ -0,0 +1,25 @@
|
||||
var highlightRegExp = /highlight-(?:text|source)-([a-z0-9]+)/
|
||||
|
||||
export default function highlightedCodeBlock (turndownService) {
|
||||
turndownService.addRule('highlightedCodeBlock', {
|
||||
filter: function (node) {
|
||||
var firstChild = node.firstChild
|
||||
return (
|
||||
node.nodeName === 'DIV' &&
|
||||
highlightRegExp.test(node.className) &&
|
||||
firstChild &&
|
||||
firstChild.nodeName === 'PRE'
|
||||
)
|
||||
},
|
||||
replacement: function (content, node, options) {
|
||||
var className = node.className || ''
|
||||
var language = (className.match(highlightRegExp) || [null, ''])[1]
|
||||
|
||||
return (
|
||||
'\n\n' + options.fence + language + '\n' +
|
||||
node.firstChild.textContent +
|
||||
'\n' + options.fence + '\n\n'
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
8
packages/turndown-plugin-gfm/src/strikethrough.js
Normal file
8
packages/turndown-plugin-gfm/src/strikethrough.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function strikethrough (turndownService) {
|
||||
turndownService.addRule('strikethrough', {
|
||||
filter: ['del', 's', 'strike'],
|
||||
replacement: function (content) {
|
||||
return '~' + content + '~'
|
||||
}
|
||||
})
|
||||
}
|
||||
171
packages/turndown-plugin-gfm/src/tables.js
Normal file
171
packages/turndown-plugin-gfm/src/tables.js
Normal file
@@ -0,0 +1,171 @@
|
||||
var indexOf = Array.prototype.indexOf
|
||||
var every = Array.prototype.every
|
||||
var rules = {}
|
||||
|
||||
rules.tableCell = {
|
||||
filter: ['th', 'td'],
|
||||
replacement: function (content, node) {
|
||||
if (tableShouldBeSkipped(nodeParentTable(node))) return content;
|
||||
return cell(content, node)
|
||||
}
|
||||
}
|
||||
|
||||
rules.tableRow = {
|
||||
filter: 'tr',
|
||||
replacement: function (content, node) {
|
||||
const parentTable = nodeParentTable(node);
|
||||
if (tableShouldBeSkipped(parentTable)) return content;
|
||||
|
||||
var borderCells = ''
|
||||
var alignMap = { left: ':--', right: '--:', center: ':-:' }
|
||||
|
||||
if (isHeadingRow(node)) {
|
||||
const colCount = tableColCount(parentTable);
|
||||
for (var i = 0; i < colCount; i++) {
|
||||
const childNode = colCount >= node.childNodes.length ? null : node.childNodes[i];
|
||||
var border = '---'
|
||||
var align = childNode ? (childNode.getAttribute('align') || '').toLowerCase() : '';
|
||||
|
||||
if (align) border = alignMap[align] || border
|
||||
|
||||
if (childNode) {
|
||||
borderCells += cell(border, node.childNodes[i])
|
||||
} else {
|
||||
borderCells += cell(border, null, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return '\n' + content + (borderCells ? '\n' + borderCells : '')
|
||||
}
|
||||
}
|
||||
|
||||
rules.table = {
|
||||
// Only convert tables with a heading row.
|
||||
// Tables with no heading row are kept using `keep` (see below).
|
||||
filter: function (node) {
|
||||
return node.nodeName === 'TABLE'
|
||||
},
|
||||
|
||||
replacement: function (content, node) {
|
||||
if (tableShouldBeSkipped(node)) return content;
|
||||
|
||||
// Ensure there are no blank lines
|
||||
content = content.replace(/\n+/g, '\n')
|
||||
|
||||
// If table has no heading, add an empty one so as to get a valid Markdown table
|
||||
var secondLine = content.trim().split('\n');
|
||||
if (secondLine.length >= 2) secondLine = secondLine[1]
|
||||
var secondLineIsDivider = secondLine.indexOf('| ---') === 0
|
||||
|
||||
var columnCount = tableColCount(node);
|
||||
var emptyHeader = ''
|
||||
if (columnCount && !secondLineIsDivider) {
|
||||
emptyHeader = '|' + ' |'.repeat(columnCount) + '\n' + '|' + ' --- |'.repeat(columnCount)
|
||||
}
|
||||
|
||||
return '\n\n' + emptyHeader + content + '\n\n'
|
||||
}
|
||||
}
|
||||
|
||||
rules.tableSection = {
|
||||
filter: ['thead', 'tbody', 'tfoot'],
|
||||
replacement: function (content) {
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
// A tr is a heading row if:
|
||||
// - the parent is a THEAD
|
||||
// - or if its the first child of the TABLE or the first TBODY (possibly
|
||||
// following a blank THEAD)
|
||||
// - and every cell is a TH
|
||||
function isHeadingRow (tr) {
|
||||
var parentNode = tr.parentNode
|
||||
return (
|
||||
parentNode.nodeName === 'THEAD' ||
|
||||
(
|
||||
parentNode.firstChild === tr &&
|
||||
(parentNode.nodeName === 'TABLE' || isFirstTbody(parentNode)) &&
|
||||
every.call(tr.childNodes, function (n) { return n.nodeName === 'TH' })
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function isFirstTbody (element) {
|
||||
var previousSibling = element.previousSibling
|
||||
return (
|
||||
element.nodeName === 'TBODY' && (
|
||||
!previousSibling ||
|
||||
(
|
||||
previousSibling.nodeName === 'THEAD' &&
|
||||
/^\s*$/i.test(previousSibling.textContent)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function cell (content, node = null, index = null) {
|
||||
if (index === null) index = indexOf.call(node.parentNode.childNodes, node)
|
||||
var prefix = ' '
|
||||
if (index === 0) prefix = '| '
|
||||
let filteredContent = content.trim().replace(/\n\r/g, '<br>').replace(/\n/g, "<br>");
|
||||
filteredContent = filteredContent.replace(/\|+/g, '\\|')
|
||||
while (filteredContent.length < 3) filteredContent += ' ';
|
||||
if (node) filteredContent = handleColSpan(filteredContent, node, ' ');
|
||||
return prefix + filteredContent + ' |'
|
||||
}
|
||||
|
||||
function nodeContainsTable(node) {
|
||||
if (!node.childNodes) return false;
|
||||
|
||||
for (let i = 0; i < node.childNodes.length; i++) {
|
||||
const child = node.childNodes[i];
|
||||
if (child.nodeName === 'TABLE') return true;
|
||||
if (nodeContainsTable(child)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Various conditions under which a table should be skipped - i.e. each cell
|
||||
// will be rendered one after the other as if they were paragraphs.
|
||||
function tableShouldBeSkipped(tableNode) {
|
||||
if (!tableNode) return true;
|
||||
if (!tableNode.rows) return true;
|
||||
if (tableNode.rows.length === 1 && tableNode.rows[0].childNodes.length <= 1) return true; // Table with only one cell
|
||||
if (nodeContainsTable(tableNode)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function nodeParentTable(node) {
|
||||
let parent = node.parentNode;
|
||||
while (parent.nodeName !== 'TABLE') {
|
||||
parent = parent.parentNode;
|
||||
if (!parent) return null;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
function handleColSpan(content, node, emptyChar) {
|
||||
const colspan = node.getAttribute('colspan') || 1;
|
||||
for (let i = 1; i < colspan; i++) {
|
||||
content += ' | ' + emptyChar.repeat(3);
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
function tableColCount(node) {
|
||||
let maxColCount = 0;
|
||||
for (let i = 0; i < node.rows.length; i++) {
|
||||
const row = node.rows[i]
|
||||
const colCount = row.childNodes.length
|
||||
if (colCount > maxColCount) maxColCount = colCount
|
||||
}
|
||||
return maxColCount
|
||||
}
|
||||
|
||||
export default function tables (turndownService) {
|
||||
turndownService.keep(function (node) {
|
||||
return node.nodeName === 'TABLE'
|
||||
})
|
||||
for (var key in rules) turndownService.addRule(key, rules[key])
|
||||
}
|
||||
10
packages/turndown-plugin-gfm/src/task-list-items.js
Normal file
10
packages/turndown-plugin-gfm/src/task-list-items.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function taskListItems (turndownService) {
|
||||
turndownService.addRule('taskListItems', {
|
||||
filter: function (node) {
|
||||
return node.type === 'checkbox' && node.parentNode.nodeName === 'LI'
|
||||
},
|
||||
replacement: function (content, node) {
|
||||
return (node.checked ? '[x]' : '[ ]') + ' '
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user