[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;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /**
 | 
				
			||||||
 | 
					       * 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Get Node Position
 | 
				
			||||||
 | 
					      let nodePos =
 | 
				
			||||||
 | 
					        currentNode.nodeName.toLowerCase() + "[" + relativeIndex + "]";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Update Reference
 | 
				
			||||||
 | 
					      currentNode = parentElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Update Position
 | 
				
			||||||
 | 
					      allPos = "/" + nodePos + allPos;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let xpath = newPos + childPos;
 | 
					    // Combine XPath
 | 
				
			||||||
 | 
					    let xpath = basePos + allPos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Return derived progress
 | 
					    // 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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user