[fix] xpath & cfi resolution
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
5cc1e2d71c
commit
856bc7e2e6
@ -910,7 +910,7 @@ class EBookReader {
|
|||||||
* Get XPath from current location
|
* Get XPath from current location
|
||||||
**/
|
**/
|
||||||
async getXPathFromCFI(cfi) {
|
async getXPathFromCFI(cfi) {
|
||||||
// Get DocFragment (current book spline index)
|
// Get DocFragment (Spine Index)
|
||||||
let startCFI = cfi.replace("epubcfi(", "");
|
let startCFI = cfi.replace("epubcfi(", "");
|
||||||
let docFragmentIndex =
|
let docFragmentIndex =
|
||||||
this.book.spine.spineItems.find((item) =>
|
this.book.spine.spineItems.find((item) =>
|
||||||
@ -918,58 +918,62 @@ class EBookReader {
|
|||||||
).index + 1;
|
).index + 1;
|
||||||
|
|
||||||
// Base Progress
|
// Base Progress
|
||||||
let newPos = "/body/DocFragment[" + docFragmentIndex + "]/body";
|
let basePos = "/body/DocFragment[" + docFragmentIndex + "]/body";
|
||||||
|
|
||||||
// Get first visible node
|
// Get First Node & Element Reference
|
||||||
let contents = this.rendition.getContents()[0];
|
let contents = this.rendition.getContents()[0];
|
||||||
let node = contents.range(cfi).startContainer;
|
let currentNode = contents.range(cfi).startContainer;
|
||||||
let element = null;
|
let element =
|
||||||
|
currentNode.nodeType == Node.ELEMENT_NODE
|
||||||
|
? currentNode
|
||||||
|
: currentNode.parentElement;
|
||||||
|
|
||||||
// Walk upwards and build progress until body
|
// XPath Reference
|
||||||
let childPos = "";
|
let allPos = "";
|
||||||
while (node.nodeName != "BODY") {
|
|
||||||
let ownValue;
|
|
||||||
|
|
||||||
switch (node.nodeType) {
|
// Walk Upwards
|
||||||
case Node.ELEMENT_NODE:
|
while (currentNode.nodeName != "BODY") {
|
||||||
// Store First Element Node
|
// Get Parent
|
||||||
if (!element) element = node;
|
let parentElement = currentNode.parentElement;
|
||||||
let relativeIndex =
|
|
||||||
Array.from(node.parentNode.children)
|
|
||||||
.filter((item) => item.nodeName == node.nodeName)
|
|
||||||
.indexOf(node) + 1;
|
|
||||||
|
|
||||||
ownValue = node.nodeName.toLowerCase() + "[" + relativeIndex + "]";
|
// Unknown Node -> Update Reference
|
||||||
break;
|
if (currentNode.nodeType != Node.ELEMENT_NODE) {
|
||||||
case Node.ATTRIBUTE_NODE:
|
console.log("[getXPathFromCFI] Unknown Node Type:", currentNode);
|
||||||
ownValue = "@" + node.nodeName;
|
currentNode = parentElement;
|
||||||
break;
|
continue;
|
||||||
case Node.TEXT_NODE:
|
|
||||||
case Node.CDATA_SECTION_NODE:
|
|
||||||
ownValue = "text()";
|
|
||||||
break;
|
|
||||||
case Node.PROCESSING_INSTRUCTION_NODE:
|
|
||||||
ownValue = "processing-instruction()";
|
|
||||||
break;
|
|
||||||
case Node.COMMENT_NODE:
|
|
||||||
ownValue = "comment()";
|
|
||||||
break;
|
|
||||||
case Node.DOCUMENT_NODE:
|
|
||||||
ownValue = "";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ownValue = "";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepend childPos & Update node reference
|
/**
|
||||||
childPos = "/" + ownValue + childPos;
|
* Exclude A tags. This could potentially be all inline elements:
|
||||||
node = node.parentNode;
|
* https://github.com/koreader/crengine/blob/master/cr3gui/data/epub.css#L149
|
||||||
|
**/
|
||||||
|
while (parentElement.nodeName == "A") {
|
||||||
|
parentElement = parentElement.parentElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
let xpath = newPos + childPos;
|
/**
|
||||||
|
* Note: This is depth / document order first, which means that this
|
||||||
|
* _could_ return incorrect results when dealing with nested "A" tags
|
||||||
|
* (dependent on how KOReader deals with nested "A" tags)
|
||||||
|
**/
|
||||||
|
let allDescendents = parentElement.querySelectorAll(currentNode.nodeName);
|
||||||
|
let relativeIndex = Array.from(allDescendents).indexOf(currentNode) + 1;
|
||||||
|
|
||||||
// Return derived progress
|
// Get Node Position
|
||||||
|
let nodePos =
|
||||||
|
currentNode.nodeName.toLowerCase() + "[" + relativeIndex + "]";
|
||||||
|
|
||||||
|
// Update Reference
|
||||||
|
currentNode = parentElement;
|
||||||
|
|
||||||
|
// Update Position
|
||||||
|
allPos = "/" + nodePos + allPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine XPath
|
||||||
|
let xpath = basePos + allPos;
|
||||||
|
|
||||||
|
// Return Derived Progress
|
||||||
return { xpath, element };
|
return { xpath, element };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -977,19 +981,13 @@ class EBookReader {
|
|||||||
* Get CFI from current location
|
* Get CFI from current location
|
||||||
**/
|
**/
|
||||||
async getCFIFromXPath(xpath) {
|
async getCFIFromXPath(xpath) {
|
||||||
// XPath Reference - Example: /body/DocFragment[15]/body/div[10]/text().184
|
|
||||||
//
|
|
||||||
// - /body/DocFragment[15] = 15th item in book spline
|
|
||||||
// - [...]/body/div[10] = 10th child div under body (direct descendents only)
|
|
||||||
// - [...]/text().184 = text node of parent, character offset @ 184 chars?
|
|
||||||
|
|
||||||
// No XPath
|
// No XPath
|
||||||
if (!xpath || xpath == "") return {};
|
if (!xpath || xpath == "") return {};
|
||||||
|
|
||||||
// Match Document Fragment Index
|
// Match Document Fragment Index
|
||||||
let fragMatch = xpath.match(/^\/body\/DocFragment\[(\d+)\]/);
|
let fragMatch = xpath.match(/^\/body\/DocFragment\[(\d+)\]/);
|
||||||
if (!fragMatch) {
|
if (!fragMatch) {
|
||||||
console.warn("No XPath Match");
|
console.warn("[getCFIFromXPath] No XPath Match");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1031,10 +1029,25 @@ class EBookReader {
|
|||||||
// Remove potential trailing `text()`
|
// Remove potential trailing `text()`
|
||||||
.replace(/\/text\(\)(\[\d+\])?$/, "");
|
.replace(/\/text\(\)(\[\d+\])?$/, "");
|
||||||
|
|
||||||
// XPath to CSS Selector
|
// XPath to Element
|
||||||
let derivedSelector = remainingXPath
|
let derivedSelectorElement = remainingXPath
|
||||||
.replace(/^\/html\/body/, "body")
|
.replace(/^\/html\/body/, "body")
|
||||||
.replace(/\/(\w+)\[(\d+)\]/g, " $1:nth-of-type($2)");
|
.split("/")
|
||||||
|
.reduce((el, item) => {
|
||||||
|
// No Match
|
||||||
|
if (!el) return null;
|
||||||
|
|
||||||
|
// Non Index
|
||||||
|
let indexMatch = item.match(/(\w+)\[(\d+)\]$/);
|
||||||
|
if (!indexMatch) return el.querySelector(item);
|
||||||
|
|
||||||
|
// Get @ Index
|
||||||
|
let tag = indexMatch[1];
|
||||||
|
let index = parseInt(indexMatch[2]) - 1;
|
||||||
|
return el.querySelectorAll(tag)[index];
|
||||||
|
}, docItem);
|
||||||
|
|
||||||
|
console.log("[getCFIFromXPath] Selector Element:", derivedSelectorElement);
|
||||||
|
|
||||||
// Validate Namespace
|
// Validate Namespace
|
||||||
if (namespaceURI) remainingXPath = remainingXPath.replaceAll("/", "/ns:");
|
if (namespaceURI) remainingXPath = remainingXPath.replaceAll("/", "/ns:");
|
||||||
@ -1070,9 +1083,7 @@ class EBookReader {
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
// Get Element & CFI (XPath -> CSS Selector Fallback)
|
// Get Element & CFI (XPath -> CSS Selector Fallback)
|
||||||
let element =
|
let element = docSearch.iterateNext() || derivedSelectorElement;
|
||||||
docSearch.iterateNext() || docItem.querySelector(derivedSelector);
|
|
||||||
|
|
||||||
let cfi = sectionItem.cfiFromElement(element);
|
let cfi = sectionItem.cfiFromElement(element);
|
||||||
|
|
||||||
return { cfi, element };
|
return { cfi, element };
|
||||||
|
@ -101,9 +101,6 @@ func getSVGBezierOpposedLine(pointA SVGGraphPoint, pointB SVGGraphPoint) SVGBezi
|
|||||||
Length: int(math.Sqrt(math.Pow(lengthX, 2) + math.Pow(lengthY, 2))),
|
Length: int(math.Sqrt(math.Pow(lengthX, 2) + math.Pow(lengthY, 2))),
|
||||||
Angle: int(math.Atan2(lengthY, lengthX)),
|
Angle: int(math.Atan2(lengthY, lengthX)),
|
||||||
}
|
}
|
||||||
|
|
||||||
// length = Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)),
|
|
||||||
// angle = Math.atan2(lengthY, lengthX)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSVGBezierControlPoint(currentPoint *SVGGraphPoint, prevPoint *SVGGraphPoint, nextPoint *SVGGraphPoint, isReverse bool) SVGGraphPoint {
|
func getSVGBezierControlPoint(currentPoint *SVGGraphPoint, prevPoint *SVGGraphPoint, nextPoint *SVGGraphPoint, isReverse bool) SVGGraphPoint {
|
||||||
|
Loading…
Reference in New Issue
Block a user