Created
October 20, 2017 08:29
-
-
Save ukyo/02768bf3793faf8d73c1845543457891 to your computer and use it in GitHub Desktop.
brで改行している要素から擬似的なパラグラフ列を生成する
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function isBlock(node) { | |
if (node.nodeType !== Node.ELEMENT_NODE) return false; | |
return /^(ADDRESS|ARTICLE|ASIDE|BLOCKQUOTE|CANVAS|DD|DIV|DL|DT|FIELDSET|FIGCAPTION|FIGURE|FOOTER|FORM|H1|H2|H3|H4|H5|H6|HEADER|HGROUP|HR|LI|MAIN|NAV|NOSCRIPT|OL|OUTPUT|P|PRE|SECTION|TABLE|TFOOT|UL|VIDEO)$/.test(node.nodeName); | |
} | |
function createPseudoParagraph(node) { | |
return { nodeName: "PSEUDO_PARAGRAPH", childNodes: [node] }; | |
} | |
function createBrBlock(nodes) { | |
return { nodeName: "BR_BLOCK", childNodes: nodes }; | |
} | |
function getBoundingRect(node) { | |
const rect = node.getBoundingClientRect(); | |
return { | |
left: rect.left + window.pageXOffset, | |
top: rect.top + window.pageYOffset, | |
width: rect.width, | |
height: rect.height, | |
}; | |
} | |
function brbrTextToParagraphs(node) { | |
// 空白、コメントを消す | |
const nodes = [...node.childNodes || []].filter(n => { | |
if (n.nodeType === Node.ELEMENT_NODE) return true; | |
return n.nodeType === Node.TEXT_NODE && !!(n.nodeValue || "").trim(); | |
}); | |
// とりあえず普通のブロック要素と、PSEUDO_PARAGRAPH(text + インライン要素), BR_BLOCK(brが2個以上続いているやつ)に分割 | |
const paragraphs = []; | |
let idx = 0; | |
for (let i = 0; i < nodes.length; ++i) { | |
const _node = nodes[i]; | |
if (_node.nodeName === "BR") { | |
const ii = nodes.slice(i + 1).findIndex(n => n.nodeName !== "BR"); | |
if (ii === -1) { | |
paragraphs.push(createBrBlock(nodes.slice(i))); | |
break; | |
} else if (ii > 0) { | |
paragraphs.push(createBrBlock(nodes.slice(i, i + ii + 1))); | |
idx += 2; | |
i += ii; | |
} else { | |
if (!paragraphs[idx]) { | |
paragraphs[idx] = createPseudoParagraph(_node); | |
} else { | |
paragraphs[idx].childNodes.push(_node); | |
} | |
} | |
} else if (isBlock(_node)) { | |
paragraphs.push(_node); | |
idx += 2; | |
} else { | |
if (!paragraphs[idx]) { | |
paragraphs[idx] = createPseudoParagraph(_node); | |
} else { | |
paragraphs[idx].childNodes.push(_node); | |
} | |
} | |
} | |
// 位置計算 | |
let rect = getBoundingRect(node); | |
// まずは普通のブロック要素と、BR_BLOCKの箱を決定する | |
for (let i = 0; i < paragraphs.length; ++i) { | |
const _node = paragraphs[i]; | |
if (isBlock(_node)) { | |
_node._rect = getBoundingRect(_node); | |
} else if (_node.nodeName === "BR_BLOCK") { | |
const first = getBoundingRect(_node.childNodes[0]); | |
const last = getBoundingRect(_node.childNodes[_node.childNodes.length - 1]); | |
const top = first.top + last.height; | |
_node._rect = { | |
left: first.left, | |
top, | |
width: 0, | |
height: last.top + last.height - top, | |
}; | |
} | |
} | |
// PSEUDO_PARAGRAPHの箱を前後関係から決定する | |
for (let i = 0; i < paragraphs.length; ++i) { | |
const _node = paragraphs[i]; | |
if (_node.nodeName !== "PSEUDO_PARAGRAPH" || _node.childNodes.every(x => x.nodeName === "BR")) continue; | |
// top | |
const first = _node.childNodes[0]; | |
let top = 0; | |
if (first.nodeName === "BR") { | |
_node.childNodes.shift(); | |
const r = getBoundingRect(first); | |
top = r.top + r.height; | |
} else if (first.nodeType !== Node.TEXT_NODE) { | |
const r = getBoundingRect(first); | |
top = r.top; | |
} else { | |
const prev = paragraphs[i - 1]; | |
top = prev ? prev._rect.top + prev._rect.height : rect.top; | |
} | |
// height | |
const last = _node.childNodes[_node.childNodes.length - 1]; | |
let height = 0; | |
if (last.nodeName === "BR") { | |
_node.childNodes.pop(); | |
const r = getBoundingRect(last); | |
height = r.top + r.height - top; | |
} else if (last.nodeType !== Node.TEXT_NODE) { | |
const r = getBoundingRect(last); | |
height = r.top + r.height - top; | |
} else { | |
const next = paragraphs[i + 1]; | |
height = (next ? next._rect.top : rect.top + rect.height) - top; | |
} | |
_node._rect = { | |
left: rect.left, | |
width: rect.width, | |
top, | |
height, | |
}; | |
} | |
return paragraphs.filter(node => node.nodeType === Node.ELEMENT_NODE || !node.childNodes.every(x => x.nodeName === "BR")); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment