Refactor seekForward and seekBackward
This commit is contained in:
parent
a5f393fa2c
commit
03c52625a9
@ -83,10 +83,6 @@ class TextSourceRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static shouldEnter(node) {
|
static shouldEnter(node) {
|
||||||
if (node.nodeType !== 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (node.nodeName.toUpperCase()) {
|
switch (node.nodeName.toUpperCase()) {
|
||||||
case 'RT':
|
case 'RT':
|
||||||
case 'SCRIPT':
|
case 'SCRIPT':
|
||||||
@ -101,102 +97,128 @@ class TextSourceRange {
|
|||||||
parseFloat(style.fontSize) === 0);
|
parseFloat(style.fontSize) === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getRubyElement(node) {
|
||||||
|
node = TextSourceRange.getParentElement(node);
|
||||||
|
if (node !== null && node.nodeName.toUpperCase() === "RT") {
|
||||||
|
node = node.parentNode;
|
||||||
|
return (node !== null && node.nodeName.toUpperCase() === "RUBY") ? node : null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
static seekForward(node, offset, length) {
|
static seekForward(node, offset, length) {
|
||||||
const state = {node, offset, remainder: length, content: ''};
|
const state = {node, offset, remainder: length, content: ''};
|
||||||
if (!TextSourceRange.seekForwardHelper(node, state)) {
|
const TEXT_NODE = Node.TEXT_NODE;
|
||||||
return state;
|
const ELEMENT_NODE = Node.ELEMENT_NODE;
|
||||||
|
let resetOffset = false;
|
||||||
|
|
||||||
|
const ruby = TextSourceRange.getRubyElement(node);
|
||||||
|
if (ruby !== null) {
|
||||||
|
node = ruby;
|
||||||
|
resetOffset = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let current = node; current !== null; current = current.parentElement) {
|
while (node !== null) {
|
||||||
for (let sibling = current.nextSibling; sibling !== null; sibling = sibling.nextSibling) {
|
let visitChildren = true;
|
||||||
if (!TextSourceRange.seekForwardHelper(sibling, state)) {
|
const nodeType = node.nodeType;
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
static seekForwardHelper(node, state) {
|
|
||||||
if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) {
|
|
||||||
const offset = state.node === node ? state.offset : 0;
|
|
||||||
|
|
||||||
let consumed = 0;
|
|
||||||
let stripped = 0;
|
|
||||||
while (state.remainder - consumed > 0) {
|
|
||||||
const currentChar = node.nodeValue[offset + consumed + stripped];
|
|
||||||
if (!currentChar) {
|
|
||||||
break;
|
|
||||||
} else if (currentChar.match(IGNORE_TEXT_PATTERN)) {
|
|
||||||
stripped++;
|
|
||||||
} else {
|
|
||||||
consumed++;
|
|
||||||
state.content += currentChar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (nodeType === TEXT_NODE) {
|
||||||
state.node = node;
|
state.node = node;
|
||||||
state.offset = offset + consumed + stripped;
|
if (TextSourceRange.seekForwardTextNode(state, resetOffset)) {
|
||||||
state.remainder -= consumed;
|
break;
|
||||||
} else if (TextSourceRange.shouldEnter(node)) {
|
}
|
||||||
for (let i = 0; i < node.childNodes.length; ++i) {
|
resetOffset = true;
|
||||||
if (!TextSourceRange.seekForwardHelper(node.childNodes[i], state)) {
|
} else if (nodeType === ELEMENT_NODE) {
|
||||||
|
visitChildren = TextSourceRange.shouldEnter(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node = TextSourceRange.getNextNode(node, visitChildren);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static seekForwardTextNode(state, resetOffset) {
|
||||||
|
const nodeValue = state.node.nodeValue;
|
||||||
|
const nodeValueLength = nodeValue.length;
|
||||||
|
let content = state.content;
|
||||||
|
let offset = resetOffset ? 0 : state.offset;
|
||||||
|
let remainder = state.remainder;
|
||||||
|
let result = false;
|
||||||
|
|
||||||
|
for (; offset < nodeValueLength; ++offset) {
|
||||||
|
const c = nodeValue[offset];
|
||||||
|
if (!IGNORE_TEXT_PATTERN.test(c)) {
|
||||||
|
content += c;
|
||||||
|
if (--remainder <= 0) {
|
||||||
|
result = true;
|
||||||
|
++offset;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.remainder > 0;
|
state.offset = offset;
|
||||||
|
state.content = content;
|
||||||
|
state.remainder = remainder;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static seekBackward(node, offset, length) {
|
static seekBackward(node, offset, length) {
|
||||||
const state = {node, offset, remainder: length, content: ''};
|
const state = {node, offset, remainder: length, content: ''};
|
||||||
if (!TextSourceRange.seekBackwardHelper(node, state)) {
|
const TEXT_NODE = Node.TEXT_NODE;
|
||||||
return state;
|
const ELEMENT_NODE = Node.ELEMENT_NODE;
|
||||||
|
let resetOffset = false;
|
||||||
|
|
||||||
|
const ruby = TextSourceRange.getRubyElement(node);
|
||||||
|
if (ruby !== null) {
|
||||||
|
node = ruby;
|
||||||
|
resetOffset = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let current = node; current !== null; current = current.parentElement) {
|
while (node !== null) {
|
||||||
for (let sibling = current.previousSibling; sibling !== null; sibling = sibling.previousSibling) {
|
let visitChildren = true;
|
||||||
if (!TextSourceRange.seekBackwardHelper(sibling, state)) {
|
const nodeType = node.nodeType;
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
static seekBackwardHelper(node, state) {
|
|
||||||
if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) {
|
|
||||||
const offset = state.node === node ? state.offset : node.length;
|
|
||||||
|
|
||||||
let consumed = 0;
|
|
||||||
let stripped = 0;
|
|
||||||
while (state.remainder - consumed > 0) {
|
|
||||||
const currentChar = node.nodeValue[offset - 1 - consumed - stripped]; // negative indices are undefined in JS
|
|
||||||
if (!currentChar) {
|
|
||||||
break;
|
|
||||||
} else if (currentChar.match(IGNORE_TEXT_PATTERN)) {
|
|
||||||
stripped++;
|
|
||||||
} else {
|
|
||||||
consumed++;
|
|
||||||
state.content = currentChar + state.content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (nodeType === TEXT_NODE) {
|
||||||
state.node = node;
|
state.node = node;
|
||||||
state.offset = offset - consumed - stripped;
|
if (TextSourceRange.seekBackwardTextNode(state, resetOffset)) {
|
||||||
state.remainder -= consumed;
|
break;
|
||||||
} else if (TextSourceRange.shouldEnter(node)) {
|
}
|
||||||
for (let i = node.childNodes.length - 1; i >= 0; --i) {
|
resetOffset = true;
|
||||||
if (!TextSourceRange.seekBackwardHelper(node.childNodes[i], state)) {
|
} else if (nodeType === ELEMENT_NODE) {
|
||||||
|
visitChildren = TextSourceRange.shouldEnter(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node = TextSourceRange.getPreviousNode(node, visitChildren);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static seekBackwardTextNode(state, resetOffset) {
|
||||||
|
const nodeValue = state.node.nodeValue;
|
||||||
|
let content = state.content;
|
||||||
|
let offset = resetOffset ? nodeValue.length : state.offset;
|
||||||
|
let remainder = state.remainder;
|
||||||
|
let result = false;
|
||||||
|
|
||||||
|
for (; offset > 0; --offset) {
|
||||||
|
const c = nodeValue[offset - 1];
|
||||||
|
if (!IGNORE_TEXT_PATTERN.test(c)) {
|
||||||
|
content = c + content;
|
||||||
|
if (--remainder <= 0) {
|
||||||
|
result = true;
|
||||||
|
--offset;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.remainder > 0;
|
state.offset = offset;
|
||||||
|
state.content = content;
|
||||||
|
state.remainder = remainder;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getParentElement(node) {
|
static getParentElement(node) {
|
||||||
@ -219,15 +241,15 @@ class TextSourceRange {
|
|||||||
static getNodesInRange(range) {
|
static getNodesInRange(range) {
|
||||||
const end = range.endContainer;
|
const end = range.endContainer;
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
for (let node = range.startContainer; node !== null; node = TextSourceRange.getNextNode(node)) {
|
for (let node = range.startContainer; node !== null; node = TextSourceRange.getNextNode(node, true)) {
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
if (node === end) { break; }
|
if (node === end) { break; }
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getNextNode(node) {
|
static getNextNode(node, visitChildren) {
|
||||||
let next = node.firstChild;
|
let next = visitChildren ? node.firstChild : null;
|
||||||
if (next === null) {
|
if (next === null) {
|
||||||
while (true) {
|
while (true) {
|
||||||
next = node.nextSibling;
|
next = node.nextSibling;
|
||||||
@ -242,6 +264,22 @@ class TextSourceRange {
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getPreviousNode(node, visitChildren) {
|
||||||
|
let next = visitChildren ? node.lastChild : null;
|
||||||
|
if (next === null) {
|
||||||
|
while (true) {
|
||||||
|
next = node.previousSibling;
|
||||||
|
if (next !== null) { break; }
|
||||||
|
|
||||||
|
next = node.parentNode;
|
||||||
|
if (next === null) { break; }
|
||||||
|
|
||||||
|
node = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
static anyNodeMatchesSelector(nodeList, selector) {
|
static anyNodeMatchesSelector(nodeList, selector) {
|
||||||
for (const node of nodeList) {
|
for (const node of nodeList) {
|
||||||
if (TextSourceRange.nodeMatchesSelector(node, selector)) {
|
if (TextSourceRange.nodeMatchesSelector(node, selector)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user