Skip to content

Instantly share code, notes, and snippets.

@ukyo
Created October 20, 2017 08:29
Show Gist options
  • Save ukyo/02768bf3793faf8d73c1845543457891 to your computer and use it in GitHub Desktop.
Save ukyo/02768bf3793faf8d73c1845543457891 to your computer and use it in GitHub Desktop.
brで改行している要素から擬似的なパラグラフ列を生成する
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