From 6bf37fd07c53282f6ac9e48404711e92d1404eea Mon Sep 17 00:00:00 2001 From: Erik Olsson Date: Wed, 15 Feb 2023 12:02:08 +0100 Subject: [PATCH] add `findElementLocator` --- .../Assets/Static/scripts/readium-fixed.js | 24 +++++++++++++++++-- .../Static/scripts/readium-reflowable.js | 24 +++++++++++++++++-- .../EPUB/EPUBNavigatorViewController.swift | 8 +++++++ Sources/Navigator/EPUB/EPUBSpreadView.swift | 18 ++++++++++++++ Sources/Navigator/EPUB/Scripts/src/dom.js | 18 ++++++++++++++ Sources/Navigator/EPUB/Scripts/src/index.js | 7 +++++- Sources/Navigator/VisualNavigator.swift | 9 +++++++ 7 files changed, 103 insertions(+), 5 deletions(-) diff --git a/Sources/Navigator/EPUB/Assets/Static/scripts/readium-fixed.js b/Sources/Navigator/EPUB/Assets/Static/scripts/readium-fixed.js index a416ffc29..9cbe77db2 100644 --- a/Sources/Navigator/EPUB/Assets/Static/scripts/readium-fixed.js +++ b/Sources/Navigator/EPUB/Assets/Static/scripts/readium-fixed.js @@ -1622,7 +1622,8 @@ window.addEventListener("load", function () { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "findNearestInteractiveElement": () => (/* binding */ findNearestInteractiveElement), -/* harmony export */ "findFirstVisibleLocator": () => (/* binding */ findFirstVisibleLocator) +/* harmony export */ "findFirstVisibleLocator": () => (/* binding */ findFirstVisibleLocator), +/* harmony export */ "findLocatorAtPoint": () => (/* binding */ findLocatorAtPoint) /* harmony export */ }); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ "./src/utils.js"); /* harmony import */ var css_selector_generator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! css-selector-generator */ "./node_modules/css-selector-generator/build/index.js"); @@ -1681,6 +1682,24 @@ function findFirstVisibleLocator() { } }; } +function findLocatorAtPoint(x, y) { + var element = document.elementFromPoint(x, y); + + if (!element) { + return undefined; + } + + return { + href: "#", + type: "application/xhtml+xml", + locations: { + cssSelector: (0,css_selector_generator__WEBPACK_IMPORTED_MODULE_1__.getCssSelector)(element) + }, + text: { + highlight: element.textContent + } + }; +} function findElement(rootElement) { var foundElement = undefined; @@ -1863,7 +1882,8 @@ __webpack_require__.g.readium = { registerDecorationTemplates: _decorator__WEBPACK_IMPORTED_MODULE_4__.registerTemplates, getDecorations: _decorator__WEBPACK_IMPORTED_MODULE_4__.getDecorations, // DOM - findFirstVisibleLocator: _dom__WEBPACK_IMPORTED_MODULE_2__.findFirstVisibleLocator + findFirstVisibleLocator: _dom__WEBPACK_IMPORTED_MODULE_2__.findFirstVisibleLocator, + findLocatorAtPoint: _dom__WEBPACK_IMPORTED_MODULE_2__.findLocatorAtPoint }; /***/ }), diff --git a/Sources/Navigator/EPUB/Assets/Static/scripts/readium-reflowable.js b/Sources/Navigator/EPUB/Assets/Static/scripts/readium-reflowable.js index 4a6a432d4..352b86191 100644 --- a/Sources/Navigator/EPUB/Assets/Static/scripts/readium-reflowable.js +++ b/Sources/Navigator/EPUB/Assets/Static/scripts/readium-reflowable.js @@ -1622,7 +1622,8 @@ window.addEventListener("load", function () { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "findNearestInteractiveElement": () => (/* binding */ findNearestInteractiveElement), -/* harmony export */ "findFirstVisibleLocator": () => (/* binding */ findFirstVisibleLocator) +/* harmony export */ "findFirstVisibleLocator": () => (/* binding */ findFirstVisibleLocator), +/* harmony export */ "findLocatorAtPoint": () => (/* binding */ findLocatorAtPoint) /* harmony export */ }); /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ "./src/utils.js"); /* harmony import */ var css_selector_generator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! css-selector-generator */ "./node_modules/css-selector-generator/build/index.js"); @@ -1681,6 +1682,24 @@ function findFirstVisibleLocator() { } }; } +function findLocatorAtPoint(x, y) { + var element = document.elementFromPoint(x, y); + + if (!element) { + return undefined; + } + + return { + href: "#", + type: "application/xhtml+xml", + locations: { + cssSelector: (0,css_selector_generator__WEBPACK_IMPORTED_MODULE_1__.getCssSelector)(element) + }, + text: { + highlight: element.textContent + } + }; +} function findElement(rootElement) { var foundElement = undefined; @@ -1863,7 +1882,8 @@ __webpack_require__.g.readium = { registerDecorationTemplates: _decorator__WEBPACK_IMPORTED_MODULE_4__.registerTemplates, getDecorations: _decorator__WEBPACK_IMPORTED_MODULE_4__.getDecorations, // DOM - findFirstVisibleLocator: _dom__WEBPACK_IMPORTED_MODULE_2__.findFirstVisibleLocator + findFirstVisibleLocator: _dom__WEBPACK_IMPORTED_MODULE_2__.findFirstVisibleLocator, + findLocatorAtPoint: _dom__WEBPACK_IMPORTED_MODULE_2__.findLocatorAtPoint }; /***/ }), diff --git a/Sources/Navigator/EPUB/EPUBNavigatorViewController.swift b/Sources/Navigator/EPUB/EPUBNavigatorViewController.swift index 89032c30d..d05f14cf1 100644 --- a/Sources/Navigator/EPUB/EPUBNavigatorViewController.swift +++ b/Sources/Navigator/EPUB/EPUBNavigatorViewController.swift @@ -588,6 +588,14 @@ open class EPUBNavigatorViewController: UIViewController, VisualNavigator, Selec } spreadView.findFirstVisibleElementLocator(completion: completion) } + + public func elementLocator(at point: CGPoint, completion: @escaping (Locator?) -> Void) { + guard let spreadView = paginationView.currentView as? EPUBSpreadView else { + DispatchQueue.main.async { completion(nil) } + return + } + spreadView.findElementLocator(at: point, completion: completion) + } /// Last current location notified to the delegate. /// Used to avoid sending twice the same location. diff --git a/Sources/Navigator/EPUB/EPUBSpreadView.swift b/Sources/Navigator/EPUB/EPUBSpreadView.swift index 2db11d4a3..60b567f71 100644 --- a/Sources/Navigator/EPUB/EPUBSpreadView.swift +++ b/Sources/Navigator/EPUB/EPUBSpreadView.swift @@ -337,6 +337,24 @@ class EPUBSpreadView: UIView, Loggable, PageView { } } + + func findElementLocator(at point: CGPoint, completion: @escaping (Locator?) -> Void) { + let x = Int(point.x - webView.frame.minX) + let y = Int(point.y - webView.frame.minY) + evaluateScript("readium.findLocatorAtPoint(\(x), \(y))") { result in + DispatchQueue.main.async { + do { + let resource = self.spread.leading + let locator = try Locator(json: result.get())? + .copy(href: resource.href, type: resource.type ?? MediaType.xhtml.string) + completion(locator) + } catch { + self.log(.error, error) + completion(nil) + } + } + } + } // MARK: - JS Messages diff --git a/Sources/Navigator/EPUB/Scripts/src/dom.js b/Sources/Navigator/EPUB/Scripts/src/dom.js index a555ce7b5..abe15006e 100644 --- a/Sources/Navigator/EPUB/Scripts/src/dom.js +++ b/Sources/Navigator/EPUB/Scripts/src/dom.js @@ -70,6 +70,24 @@ export function findFirstVisibleLocator() { }; } +export function findLocatorAtPoint(x, y) { + const element = document.elementFromPoint(x, y); + if (!element) { + return undefined; + } + + return { + href: "#", + type: "application/xhtml+xml", + locations: { + cssSelector: getCssSelector(element), + }, + text: { + highlight: element.textContent, + }, + }; +} + function findElement(rootElement) { var foundElement = undefined; for (var i = rootElement.children.length - 1; i >= 0; i--) { diff --git a/Sources/Navigator/EPUB/Scripts/src/index.js b/Sources/Navigator/EPUB/Scripts/src/index.js index f21a485bf..b5970b272 100644 --- a/Sources/Navigator/EPUB/Scripts/src/index.js +++ b/Sources/Navigator/EPUB/Scripts/src/index.js @@ -8,7 +8,11 @@ import "./gestures"; import "./keyboard"; -import { findFirstVisibleLocator } from "./dom"; +import { + findFirstVisibleLocator, + findLocatorAtPoint, +} from "./dom"; + import { removeProperty, scrollLeft, @@ -37,4 +41,5 @@ global.readium = { // DOM findFirstVisibleLocator: findFirstVisibleLocator, + findLocatorAtPoint: findLocatorAtPoint, }; diff --git a/Sources/Navigator/VisualNavigator.swift b/Sources/Navigator/VisualNavigator.swift index d2a10024f..8e3348046 100644 --- a/Sources/Navigator/VisualNavigator.swift +++ b/Sources/Navigator/VisualNavigator.swift @@ -31,6 +31,9 @@ public protocol VisualNavigator: Navigator { /// Returns the `Locator` to the first content element that begins on the current screen. func firstVisibleElementLocator(completion: @escaping (Locator?) -> Void) + + /// Returns the `Locator` to the first content element located at the given point + func elementLocator(at point: CGPoint, completion: @escaping (Locator?) -> Void) } public extension VisualNavigator { @@ -40,6 +43,12 @@ public extension VisualNavigator { completion(currentLocation) } } + + func elementLocator(at point: CGPoint, completion: @escaping (Locator?) -> Void) { + DispatchQueue.main.async { + completion(currentLocation) + } + } @discardableResult func goLeft(animated: Bool = false, completion: @escaping () -> Void = {}) -> Bool {