2023-07-17 13:19:34 +02:00
|
|
|
import { isBlock, isVoid, hasVoid, isCodeBlock, isMeaningfulWhenBlank, hasMeaningfulWhenBlank } from './utilities'
|
2020-12-02 17:43:44 +02:00
|
|
|
|
2023-07-17 13:19:34 +02:00
|
|
|
export default function Node (node, options) {
|
2020-12-02 17:43:44 +02:00
|
|
|
node.isBlock = isBlock(node)
|
2023-07-17 13:19:34 +02:00
|
|
|
node.isCode = node.nodeName === 'CODE' || node.parentNode.isCode || isCodeBlock(node);
|
2020-12-02 17:43:44 +02:00
|
|
|
node.isBlank = isBlank(node)
|
2023-07-17 13:19:34 +02:00
|
|
|
node.flankingWhitespace = flankingWhitespace(node, options)
|
2020-12-02 17:43:44 +02:00
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
|
|
|
function isBlank (node) {
|
|
|
|
return (
|
|
|
|
!isVoid(node) &&
|
2023-07-17 13:19:34 +02:00
|
|
|
!isMeaningfulWhenBlank(node) &&
|
|
|
|
/^\s*$/i.test(node.textContent) &&
|
|
|
|
!hasVoid(node) &&
|
|
|
|
!hasMeaningfulWhenBlank(node)
|
2020-12-02 17:43:44 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-07-17 13:19:34 +02:00
|
|
|
function flankingWhitespace (node, options) {
|
|
|
|
if (node.isBlock || (options.preformattedCode && node.isCode)) {
|
|
|
|
return { leading: '', trailing: '' }
|
|
|
|
}
|
2020-12-02 17:43:44 +02:00
|
|
|
|
2023-07-17 13:19:34 +02:00
|
|
|
var edges = edgeWhitespace(node.textContent)
|
2020-12-02 17:43:44 +02:00
|
|
|
|
2023-07-17 13:19:34 +02:00
|
|
|
// abandon leading ASCII WS if left-flanked by ASCII WS
|
|
|
|
if (edges.leadingAscii && isFlankedByWhitespace('left', node, options)) {
|
|
|
|
edges.leading = edges.leadingNonAscii
|
2020-12-02 17:43:44 +02:00
|
|
|
}
|
|
|
|
|
2023-07-17 13:19:34 +02:00
|
|
|
// abandon trailing ASCII WS if right-flanked by ASCII WS
|
|
|
|
if (edges.trailingAscii && isFlankedByWhitespace('right', node, options)) {
|
|
|
|
edges.trailing = edges.trailingNonAscii
|
|
|
|
}
|
|
|
|
|
|
|
|
return { leading: edges.leading, trailing: edges.trailing }
|
|
|
|
}
|
|
|
|
|
|
|
|
function edgeWhitespace (string) {
|
|
|
|
var m = string.match(/^(([ \t\r\n]*)(\s*))(?:(?=\S)[\s\S]*\S)?((\s*?)([ \t\r\n]*))$/)
|
|
|
|
return {
|
|
|
|
leading: m[1], // whole string for whitespace-only strings
|
|
|
|
leadingAscii: m[2],
|
|
|
|
leadingNonAscii: m[3],
|
|
|
|
trailing: m[4], // empty for whitespace-only strings
|
|
|
|
trailingNonAscii: m[5],
|
|
|
|
trailingAscii: m[6]
|
|
|
|
}
|
2020-12-02 17:43:44 +02:00
|
|
|
}
|
|
|
|
|
2023-07-17 13:19:34 +02:00
|
|
|
function isFlankedByWhitespace (side, node, options) {
|
2020-12-02 17:43:44 +02:00
|
|
|
var sibling
|
|
|
|
var regExp
|
|
|
|
var isFlanked
|
|
|
|
|
|
|
|
if (side === 'left') {
|
|
|
|
sibling = node.previousSibling
|
|
|
|
regExp = / $/
|
|
|
|
} else {
|
|
|
|
sibling = node.nextSibling
|
|
|
|
regExp = /^ /
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sibling) {
|
|
|
|
if (sibling.nodeType === 3) {
|
|
|
|
isFlanked = regExp.test(sibling.nodeValue)
|
2023-07-17 13:19:34 +02:00
|
|
|
} else if (options.preformattedCode && sibling.nodeName === 'CODE') {
|
|
|
|
isFlanked = false
|
2020-12-02 17:43:44 +02:00
|
|
|
} else if (sibling.nodeType === 1 && !isBlock(sibling)) {
|
|
|
|
isFlanked = regExp.test(sibling.textContent)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return isFlanked
|
|
|
|
}
|