Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FYI, my quick and dirty implementation (non-critical usage, so largely untested at this stage) #1

Open
danielweck opened this issue Mar 19, 2019 · 4 comments

Comments

@danielweck
Copy link

danielweck commented Mar 19, 2019

Like Tiny-CFI, this also only supports DOM elements, but with ID assertions (yeah!) :)

(internally I use a custom DOM-Range JSON serialization format, so I have not relied on CFI much, other than for informational purposes ... but CFI can indeed be a useful interoperable "standard" in some cases, so I would love to see a complete mini-footprint robust implementation that includes support for character ranges ;)

export function generateCfiSteps(target) {
    if (!target || target.nodeType !== this.window.Node.ELEMENT_NODE) {
        return undefined;
    }

    let cfi = "";

    let currentElement = target;
    while (currentElement.parentNode && currentElement.parentNode.nodeType === Node.ELEMENT_NODE) {
        const children = currentElement.parentNode.children;

        for (let i = 0; i < children.length; i++) {
            if (currentElement === children[i]) {
                cfi = ((i + 1) * 2) +
                    (currentElement.id ? ("[" + currentElement.id + "]") : "") +
                    (cfi.length ? ("/" + cfi) : "");

                break;
            }
        }

        currentElement = currentElement.parentNode;
    }

    return "/" + cfi;
}

tiny-cfi:

tiny-cfi/tinycfi.js

Lines 1 to 41 in e9fc323

function isWhitespaceNode(node) {
return !/[^\t\n\r ]/.test(node.textContent);
}
export function generateCfiSteps(target, root = this.window.document.documentElement) {
const window = this.window;
const treeWalker = window.document.createTreeWalker(
root,
window.NodeFilter.SHOW_ELEMENT + window.NodeFilter.SHOW_TEXT,
{
acceptNode: function (node) {
if (node.nodeType === window.Node.TEXT_NODE && isWhitespaceNode(node)) {
return window.NodeFilter.FILTER_REJECT;
}
return window.NodeFilter.FILTER_ACCEPT;
}
},
false
);
let currentNode;
if (target.nodeType === window.Node.TEXT_NODE) {
currentNode = target.parentNode;
} else {
currentNode = target;
}
treeWalker.currentNode = currentNode;
const path = [];
do {
let index = 1;
while (treeWalker.previousSibling()) {
index = index + 1;
}
path.push(index * 2);
currentNode = treeWalker.parentNode();
} while (currentNode && currentNode !== root);
return `/${path.reverse().join('/')}`;
}

@jccr
Copy link
Owner

jccr commented Mar 20, 2019

@danielweck Nice one Daniel.

FYI I'm using treewalker to attempt to harness its ability to give me a custom view of the tree.
Hopefully I can use that to counteract the problems of text nodes broken up with injected span elements.

I'm gaining understanding from the spec: https://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#TreeWalker

@jccr
Copy link
Owner

jccr commented Mar 20, 2019

My next plan is to port a streamlined CFI test suite and bring it here.

@danielweck
Copy link
Author

Tree Walker / Visitor Pattern is a nice abstraction, which I use in another project to navigate a complex hierarchy. It certainly makes sense to use this for a DOM tree :)

PS: the code snippet I pasted here originally contains a "blacklist" check for DOM elements (identifiable by ID or CSS class) that are injected by the reading system / not part of the publication document. I removed the if statement to illustrate the basic DOM parent/ancestor iteration.

@danielweck
Copy link
Author

Perhaps I should point out that I also use code that converts DOM range to CFI equivalent. This code however is non-critical (untested) as the CFI is provided only for informational purposes. My ad-hoc DOM range JSON serialization format actually relies on CSS Selectors to reference parent elements, and indices for text nodes. It is pretty simple, and much more performant than encoding via CFI.
https://github.com/readium/r2-navigator-js/blob/develop/src/electron/renderer/webview/selection.ts#L220

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants