diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2019-09-28 10:07:52 -0400 | 
|---|---|---|
| committer | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2019-09-28 11:28:55 -0400 | 
| commit | 03c52625a98c1f932a19d8377d82b388fa23c78c (patch) | |
| tree | 07ad00941afb2f2c09b4c02664f5d1009e26cd2a /ext | |
| parent | a5f393fa2c95476c5b2af5714b03a50b92a2e109 (diff) | |
Refactor seekForward and seekBackward
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/fg/js/source.js | 176 | 
1 files changed, 107 insertions, 69 deletions
| diff --git a/ext/fg/js/source.js b/ext/fg/js/source.js index af2119e8..ee4f58e2 100644 --- a/ext/fg/js/source.js +++ b/ext/fg/js/source.js @@ -83,10 +83,6 @@ class TextSourceRange {      }      static shouldEnter(node) { -        if (node.nodeType !== 1) { -            return false; -        } -          switch (node.nodeName.toUpperCase()) {              case 'RT':              case 'SCRIPT': @@ -101,102 +97,128 @@ class TextSourceRange {              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) {          const state = {node, offset, remainder: length, content: ''}; -        if (!TextSourceRange.seekForwardHelper(node, state)) { -            return state; +        const TEXT_NODE = Node.TEXT_NODE; +        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) { -            for (let sibling = current.nextSibling; sibling !== null; sibling = sibling.nextSibling) { -                if (!TextSourceRange.seekForwardHelper(sibling, state)) { -                    return state; +        while (node !== null) { +            let visitChildren = true; +            const nodeType = node.nodeType; + +            if (nodeType === TEXT_NODE) { +                state.node = node; +                if (TextSourceRange.seekForwardTextNode(state, resetOffset)) { +                    break;                  } +                resetOffset = true; +            } else if (nodeType === ELEMENT_NODE) { +                visitChildren = TextSourceRange.shouldEnter(node);              } + +            node = TextSourceRange.getNextNode(node, visitChildren);          }          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; -                } -            } - -            state.node = node; -            state.offset = offset + consumed + stripped; -            state.remainder -= consumed; -        } else if (TextSourceRange.shouldEnter(node)) { -            for (let i = 0; i < node.childNodes.length; ++i) { -                if (!TextSourceRange.seekForwardHelper(node.childNodes[i], 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;                  }              }          } -        return state.remainder > 0; +        state.offset = offset; +        state.content = content; +        state.remainder = remainder; +        return result;      }      static seekBackward(node, offset, length) {          const state = {node, offset, remainder: length, content: ''}; -        if (!TextSourceRange.seekBackwardHelper(node, state)) { -            return state; +        const TEXT_NODE = Node.TEXT_NODE; +        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) { -            for (let sibling = current.previousSibling; sibling !== null; sibling = sibling.previousSibling) { -                if (!TextSourceRange.seekBackwardHelper(sibling, state)) { -                    return state; +        while (node !== null) { +            let visitChildren = true; +            const nodeType = node.nodeType; + +            if (nodeType === TEXT_NODE) { +                state.node = node; +                if (TextSourceRange.seekBackwardTextNode(state, resetOffset)) { +                    break;                  } +                resetOffset = true; +            } else if (nodeType === ELEMENT_NODE) { +                visitChildren = TextSourceRange.shouldEnter(node);              } + +            node = TextSourceRange.getPreviousNode(node, visitChildren);          }          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; -                } -            } - -            state.node = node; -            state.offset = offset - consumed - stripped; -            state.remainder -= consumed; -        } else if (TextSourceRange.shouldEnter(node)) { -            for (let i = node.childNodes.length - 1; i >= 0; --i) { -                if (!TextSourceRange.seekBackwardHelper(node.childNodes[i], 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;                  }              }          } -        return state.remainder > 0; +        state.offset = offset; +        state.content = content; +        state.remainder = remainder; +        return result;      }      static getParentElement(node) { @@ -219,15 +241,15 @@ class TextSourceRange {      static getNodesInRange(range) {          const end = range.endContainer;          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);              if (node === end) { break; }          }          return nodes;      } -    static getNextNode(node) { -        let next = node.firstChild; +    static getNextNode(node, visitChildren) { +        let next = visitChildren ? node.firstChild : null;          if (next === null) {              while (true) {                  next = node.nextSibling; @@ -242,6 +264,22 @@ class TextSourceRange {          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) {          for (const node of nodeList) {              if (TextSourceRange.nodeMatchesSelector(node, selector)) { |