import { isBlock, isVoid, hasVoid, isMeaningfulWhenBlank, hasMeaningfulWhenBlank } from './utilities'
// 给节点添加一些额外属性之后返回
export default function Node (node, options) {
// 是否是块级元素
node.isBlock = isBlock(node)
// 是否是代码元素或其子元素
node.isCode = node.nodeName === 'CODE' || node.parentNode.isCode
// 是否是空白元素
node.isBlank = isBlank(node)
// 两侧的空白
node.flankingWhitespace = flankingWhitespace(node, options)
return node
}
function isBlank (node) {
// 空白元素:不是空元素,且不是有意义的空白元素,
// 也不包含上述元素,并且内容为空或者全是空白字符
return (
!isVoid(node) &&
!isMeaningfulWhenBlank(node) &&
/^\s*$/i.test(node.textContent) &&
!hasVoid(node) &&
!hasMeaningfulWhenBlank(node)
)
}
function flankingWhitespace (node, options) {
// 如果是块级元素,不需要填充空白,都返回空串
if (node.isBlock || (options.preformattedCode && node.isCode)) {
return { leading: '', trailing: '' }
}
var edges = edgeWhitespace(node.textContent)
// 如果左侧有空白,那么去掉当前节点的前导 ASCII 空白
if (edges.leadingAscii && isFlankedByWhitespace('left', node, options)) {
edges.leading = edges.leadingNonAscii
}
// 如果右侧有空白,那么去掉当前节点的尾随 ASCII 空白
if (edges.trailingAscii && isFlankedByWhitespace('right', node, options)) {
edges.trailing = edges.trailingNonAscii
}
return { leading: edges.leading, trailing: edges.trailing }
}
function edgeWhitespace (string) {
// 通过单个正则获取前导和尾随空白,又进一步分为ASCII 和 非 ASCII 空白
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]
}
}
// 判断上一个或者下一个元素和当前元素之间是否有空白
function isFlankedByWhitespace (side, node, options) {
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)
} else if (options.preformattedCode && sibling.nodeName === 'CODE') {
// 如果它是代码,需要保持原样,返回否
isFlanked = false
} else if (sibling.nodeType === 1 && !isBlock(sibling)) {
// 除此之外,检查元素的内容
isFlanked = regExp.test(sibling.textContent)
}
}
return isFlanked
}