Skip to content

Commit 4f082ee

Browse files
authored
Support for keyboard events in the navigators (#267)
1 parent de0a198 commit 4f082ee

File tree

16 files changed

+994
-115
lines changed

16 files changed

+994
-115
lines changed

.github/workflows/checks.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ env:
1313
jobs:
1414
build:
1515
name: Build
16-
runs-on: macos-latest
16+
runs-on: macos-11
1717
env:
1818
scheme: ${{ 'Readium-Package' }}
1919
DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer
@@ -37,7 +37,7 @@ jobs:
3737
3838
lint:
3939
name: Lint
40-
runs-on: macos-latest
40+
runs-on: macos-11
4141
env:
4242
scripts: ${{ 'Sources/Navigator/EPUB/Scripts' }}
4343

@@ -57,7 +57,7 @@ jobs:
5757
5858
int-dev:
5959
name: Integration (Local)
60-
runs-on: macos-latest
60+
runs-on: macos-11
6161
defaults:
6262
run:
6363
working-directory: TestApp
@@ -75,7 +75,7 @@ jobs:
7575
7676
int-spm:
7777
name: Integration (Swift Package Manager)
78-
runs-on: macos-latest
78+
runs-on: macos-11
7979
defaults:
8080
run:
8181
working-directory: TestApp
@@ -99,7 +99,7 @@ jobs:
9999
100100
int-carthage:
101101
name: Integration (Carthage)
102-
runs-on: macos-latest
102+
runs-on: macos-11
103103
defaults:
104104
run:
105105
working-directory: TestApp
@@ -126,7 +126,7 @@ jobs:
126126
int-cocoapods:
127127
name: Integration (CocoaPods)
128128
if: github.event_name == 'push'
129-
runs-on: macos-latest
129+
runs-on: macos-11
130130
defaults:
131131
run:
132132
working-directory: TestApp

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ All notable changes to this project will be documented in this file. Take a look
1212

1313
* Positions computation, TTS and search is now enabled for Readium Web Publications conforming to the [EPUB profile](https://readium.org/webpub-manifest/profiles/epub.html).
1414

15+
#### Navigator
16+
17+
* New `VisualNavigatorDelegate` APIs to handle keyboard events (contributed by [@lukeslu](https://github.com/readium/swift-toolkit/pull/267)).
18+
* This can be used to turn pages with the arrow keys, for example.
19+
1520
### Changed
1621

1722
#### Navigator

Sources/Navigator/EPUB/Assets/Static/scripts/readium-fixed.js

Lines changed: 109 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,6 +1621,7 @@ window.addEventListener("load", function () {
16211621
"use strict";
16221622
__webpack_require__.r(__webpack_exports__);
16231623
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
1624+
/* harmony export */ "findNearestInteractiveElement": () => (/* binding */ findNearestInteractiveElement),
16241625
/* harmony export */ "findFirstVisibleLocator": () => (/* binding */ findFirstVisibleLocator)
16251626
/* harmony export */ });
16261627
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ "./src/utils.js");
@@ -1632,6 +1633,35 @@ __webpack_require__.r(__webpack_exports__);
16321633
// available in the top-level LICENSE file of the project.
16331634
//
16341635

1636+
// Returns `element` or its first parent that is considered "user interactive".
1637+
// For example a link, a video clip or a text field.
1638+
//
1639+
// See. https://github.com/JayPanoz/architecture/tree/touch-handling/misc/touch-handling
1640+
1641+
function findNearestInteractiveElement(element) {
1642+
if (element == null) {
1643+
return null;
1644+
}
1645+
1646+
var interactiveTags = ["a", "audio", "button", "canvas", "details", "input", "label", "option", "select", "submit", "textarea", "video"];
1647+
1648+
if (interactiveTags.indexOf(element.nodeName.toLowerCase()) !== -1) {
1649+
return element.outerHTML;
1650+
} // Checks whether the element is editable by the user.
1651+
1652+
1653+
if (element.hasAttribute("contenteditable") && element.getAttribute("contenteditable").toLowerCase() != "false") {
1654+
return element.outerHTML;
1655+
} // Checks parents recursively because the touch might be for example on an <em> inside a <a>.
1656+
1657+
1658+
if (element.parentElement) {
1659+
return findNearestInteractiveElement(element.parentElement);
1660+
}
1661+
1662+
return null;
1663+
} /// Returns the `Locator` object to the first block element that is visible on
1664+
/// the screen.
16351665

16361666
function findFirstVisibleLocator() {
16371667
var element = findElement(document.body);
@@ -1748,13 +1778,15 @@ function shouldIgnoreElement(element) {
17481778
__webpack_require__.r(__webpack_exports__);
17491779
/* harmony import */ var _decorator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./decorator */ "./src/decorator.js");
17501780
/* harmony import */ var _rect__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rect */ "./src/rect.js");
1781+
/* harmony import */ var _dom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dom */ "./src/dom.js");
17511782
//
17521783
// Copyright 2021 Readium Foundation. All rights reserved.
17531784
// Use of this source code is governed by the BSD-style license
17541785
// available in the top-level LICENSE file of the project.
17551786
//
17561787

17571788

1789+
17581790
window.addEventListener("DOMContentLoaded", function () {
17591791
// If we don't set the CSS cursor property to pointer, then the click events are not triggered pre-iOS 13.
17601792
document.body.style.cursor = "pointer";
@@ -1776,7 +1808,7 @@ function onClick(event) {
17761808
x: point.x,
17771809
y: point.y,
17781810
targetElement: event.target.outerHTML,
1779-
interactiveElement: nearestInteractiveElement(event.target)
1811+
interactiveElement: (0,_dom__WEBPACK_IMPORTED_MODULE_2__.findNearestInteractiveElement)(event.target)
17801812
};
17811813

17821814
if ((0,_decorator__WEBPACK_IMPORTED_MODULE_0__.handleDecorationClickEvent)(event, clickEvent)) {
@@ -1789,27 +1821,6 @@ function onClick(event) {
17891821
webkit.messageHandlers.tap.postMessage(clickEvent); // We don't want to disable the default WebView behavior as it breaks some features without bringing any value.
17901822
// event.stopPropagation();
17911823
// event.preventDefault();
1792-
} // See. https://github.com/JayPanoz/architecture/tree/touch-handling/misc/touch-handling
1793-
1794-
1795-
function nearestInteractiveElement(element) {
1796-
var interactiveTags = ["a", "audio", "button", "canvas", "details", "input", "label", "option", "select", "submit", "textarea", "video"];
1797-
1798-
if (interactiveTags.indexOf(element.nodeName.toLowerCase()) !== -1) {
1799-
return element.outerHTML;
1800-
} // Checks whether the element is editable by the user.
1801-
1802-
1803-
if (element.hasAttribute("contenteditable") && element.getAttribute("contenteditable").toLowerCase() != "false") {
1804-
return element.outerHTML;
1805-
} // Checks parents recursively because the touch might be for example on an <em> inside a <a>.
1806-
1807-
1808-
if (element.parentElement) {
1809-
return nearestInteractiveElement(element.parentElement);
1810-
}
1811-
1812-
return null;
18131824
}
18141825

18151826
/***/ }),
@@ -1823,9 +1834,10 @@ function nearestInteractiveElement(element) {
18231834
"use strict";
18241835
__webpack_require__.r(__webpack_exports__);
18251836
/* harmony import */ var _gestures__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./gestures */ "./src/gestures.js");
1826-
/* harmony import */ var _dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./dom */ "./src/dom.js");
1827-
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ "./src/utils.js");
1828-
/* harmony import */ var _decorator__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./decorator */ "./src/decorator.js");
1837+
/* harmony import */ var _keyboard__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./keyboard */ "./src/keyboard.js");
1838+
/* harmony import */ var _dom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dom */ "./src/dom.js");
1839+
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils */ "./src/utils.js");
1840+
/* harmony import */ var _decorator__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./decorator */ "./src/decorator.js");
18291841
//
18301842
// Copyright 2021 Readium Foundation. All rights reserved.
18311843
// Use of this source code is governed by the BSD-style license
@@ -1835,26 +1847,88 @@ __webpack_require__.r(__webpack_exports__);
18351847

18361848

18371849

1850+
18381851
// Public API used by the navigator.
18391852

18401853
__webpack_require__.g.readium = {
18411854
// utils
1842-
scrollToId: _utils__WEBPACK_IMPORTED_MODULE_2__.scrollToId,
1843-
scrollToPosition: _utils__WEBPACK_IMPORTED_MODULE_2__.scrollToPosition,
1844-
scrollToText: _utils__WEBPACK_IMPORTED_MODULE_2__.scrollToText,
1845-
scrollLeft: _utils__WEBPACK_IMPORTED_MODULE_2__.scrollLeft,
1846-
scrollRight: _utils__WEBPACK_IMPORTED_MODULE_2__.scrollRight,
1847-
setProperty: _utils__WEBPACK_IMPORTED_MODULE_2__.setProperty,
1848-
removeProperty: _utils__WEBPACK_IMPORTED_MODULE_2__.removeProperty,
1855+
scrollToId: _utils__WEBPACK_IMPORTED_MODULE_3__.scrollToId,
1856+
scrollToPosition: _utils__WEBPACK_IMPORTED_MODULE_3__.scrollToPosition,
1857+
scrollToText: _utils__WEBPACK_IMPORTED_MODULE_3__.scrollToText,
1858+
scrollLeft: _utils__WEBPACK_IMPORTED_MODULE_3__.scrollLeft,
1859+
scrollRight: _utils__WEBPACK_IMPORTED_MODULE_3__.scrollRight,
1860+
setProperty: _utils__WEBPACK_IMPORTED_MODULE_3__.setProperty,
1861+
removeProperty: _utils__WEBPACK_IMPORTED_MODULE_3__.removeProperty,
18491862
// decoration
1850-
registerDecorationTemplates: _decorator__WEBPACK_IMPORTED_MODULE_3__.registerTemplates,
1851-
getDecorations: _decorator__WEBPACK_IMPORTED_MODULE_3__.getDecorations,
1863+
registerDecorationTemplates: _decorator__WEBPACK_IMPORTED_MODULE_4__.registerTemplates,
1864+
getDecorations: _decorator__WEBPACK_IMPORTED_MODULE_4__.getDecorations,
18521865
// DOM
1853-
findFirstVisibleLocator: _dom__WEBPACK_IMPORTED_MODULE_1__.findFirstVisibleLocator
1866+
findFirstVisibleLocator: _dom__WEBPACK_IMPORTED_MODULE_2__.findFirstVisibleLocator
18541867
};
18551868

18561869
/***/ }),
18571870

1871+
/***/ "./src/keyboard.js":
1872+
/*!*************************!*\
1873+
!*** ./src/keyboard.js ***!
1874+
\*************************/
1875+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1876+
1877+
"use strict";
1878+
__webpack_require__.r(__webpack_exports__);
1879+
/* harmony import */ var _dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dom */ "./src/dom.js");
1880+
//
1881+
// Copyright 2023 Readium Foundation. All rights reserved.
1882+
// Use of this source code is governed by the BSD-style license
1883+
// available in the top-level LICENSE file of the project.
1884+
//
1885+
1886+
window.addEventListener("keydown", event => {
1887+
if (shouldIgnoreEvent(event)) {
1888+
return;
1889+
}
1890+
1891+
preventDefault(event);
1892+
sendPressKeyMessage(event, "keydown");
1893+
});
1894+
window.addEventListener("keyup", event => {
1895+
if (shouldIgnoreEvent(event)) {
1896+
return;
1897+
}
1898+
1899+
preventDefault(event);
1900+
sendPressKeyMessage(event, "keyup");
1901+
});
1902+
1903+
function shouldIgnoreEvent(event) {
1904+
return event.defaultPrevented || (0,_dom__WEBPACK_IMPORTED_MODULE_0__.findNearestInteractiveElement)(document.activeElement) != null;
1905+
} // We prevent the default behavior for keyboard events, otherwise the web view
1906+
// might scroll.
1907+
1908+
1909+
function preventDefault(event) {
1910+
event.stopPropagation();
1911+
event.preventDefault();
1912+
}
1913+
1914+
function sendPressKeyMessage(event, keyType) {
1915+
if (event.repeat) return;
1916+
webkit.messageHandlers.pressKey.postMessage({
1917+
type: keyType,
1918+
code: event.code,
1919+
// We use a deprecated `keyCode` property, because the value of `event.key`
1920+
// changes depending on which modifier is pressed, while `event.code` shows
1921+
// the key code of the physical keyboard key, ignoring the virtual layout.
1922+
key: String.fromCharCode(event.keyCode),
1923+
option: event.altKey,
1924+
control: event.ctrlKey,
1925+
shift: event.shiftKey,
1926+
command: event.metaKey
1927+
});
1928+
}
1929+
1930+
/***/ }),
1931+
18581932
/***/ "./src/rect.js":
18591933
/*!*********************!*\
18601934
!*** ./src/rect.js ***!

0 commit comments

Comments
 (0)