@internetarchive/bookreader 5.0.0-9-multiple-files → 5.0.0-90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +21 -19
- package/.github/workflows/node.js.yml +81 -7
- package/.github/workflows/npm-publish.yml +6 -20
- package/.testcaferc.js +10 -0
- package/BookReader/BookReader.css +505 -1442
- package/BookReader/BookReader.js +2 -21564
- package/BookReader/BookReader.js.LICENSE.txt +20 -20
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +1782 -0
- package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +7 -0
- package/BookReader/ia-bookreader-bundle.js.map +1 -0
- package/BookReader/icons/1up.svg +1 -12
- package/BookReader/icons/2up.svg +1 -15
- package/BookReader/icons/advance.svg +3 -26
- package/BookReader/icons/chevron-right.svg +1 -1
- package/BookReader/icons/close-circle-dark.svg +1 -0
- package/BookReader/icons/close-circle.svg +1 -1
- package/BookReader/icons/fullscreen.svg +1 -17
- package/BookReader/icons/fullscreen_exit.svg +1 -17
- package/BookReader/icons/hamburger.svg +1 -15
- package/BookReader/icons/left-arrow.svg +1 -12
- package/BookReader/icons/magnify-minus.svg +1 -16
- package/BookReader/icons/magnify-plus.svg +1 -17
- package/BookReader/icons/magnify.svg +1 -15
- package/BookReader/icons/pause.svg +1 -23
- package/BookReader/icons/play.svg +1 -22
- package/BookReader/icons/playback-speed.svg +1 -34
- package/BookReader/icons/read-aloud.svg +1 -22
- package/BookReader/icons/review.svg +3 -22
- package/BookReader/icons/thumbnails.svg +1 -17
- package/BookReader/icons/voice.svg +1 -0
- package/BookReader/icons/volume-full.svg +1 -22
- package/BookReader/images/BRicons.svg +5 -94
- package/BookReader/images/books_graphic.svg +1 -177
- package/BookReader/images/icon_book.svg +1 -12
- package/BookReader/images/icon_bookmark.svg +1 -12
- package/BookReader/images/icon_gear.svg +1 -14
- package/BookReader/images/icon_hamburger.svg +1 -20
- package/BookReader/images/icon_home.svg +1 -21
- package/BookReader/images/icon_info.svg +1 -11
- package/BookReader/images/icon_one_page.svg +1 -8
- package/BookReader/images/icon_pause.svg +1 -1
- package/BookReader/images/icon_play.svg +1 -1
- package/BookReader/images/icon_playback-rate.svg +1 -15
- package/BookReader/images/icon_search_button.svg +1 -8
- package/BookReader/images/icon_share.svg +1 -9
- package/BookReader/images/icon_skip-ahead.svg +1 -6
- package/BookReader/images/icon_skip-back.svg +2 -13
- package/BookReader/images/icon_speaker.svg +1 -18
- package/BookReader/images/icon_speaker_open.svg +1 -10
- package/BookReader/images/icon_thumbnails.svg +1 -12
- package/BookReader/images/icon_toc.svg +1 -5
- package/BookReader/images/icon_two_pages.svg +1 -9
- package/BookReader/images/marker_chap-off.svg +1 -11
- package/BookReader/images/marker_chap-on.svg +1 -11
- package/BookReader/images/marker_srch-on.svg +1 -11
- package/BookReader/images/unviewable_page.png +0 -0
- package/BookReader/jquery-3.js +2 -0
- package/BookReader/jquery-3.js.LICENSE.txt +24 -0
- package/BookReader/plugins/plugin.archive_analytics.js +1 -172
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
- package/BookReader/plugins/plugin.autoplay.js +1 -165
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js +22 -301
- package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.iframe.js +1 -74
- package/BookReader/plugins/plugin.iframe.js.map +1 -1
- package/BookReader/plugins/plugin.iiif.js +2 -0
- package/BookReader/plugins/plugin.iiif.js.map +1 -0
- package/BookReader/plugins/plugin.resume.js +1 -368
- package/BookReader/plugins/plugin.resume.js.map +1 -1
- package/BookReader/plugins/plugin.search.js +2 -1420
- package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.text_selection.js +2 -1080
- package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +2 -9193
- package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +1 -269
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
- package/BookReader/webcomponents-bundle.js +3 -0
- package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
- package/BookReader/webcomponents-bundle.js.map +1 -0
- package/BookReaderDemo/BookReaderDemo.css +18 -19
- package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
- package/BookReaderDemo/BookReaderJSSimple.js +1 -0
- package/BookReaderDemo/IADemoBr.js +144 -0
- package/BookReaderDemo/demo-advanced.html +2 -2
- package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
- package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
- package/BookReaderDemo/demo-fullscreen.html +2 -4
- package/BookReaderDemo/demo-iiif.html +99 -12
- package/BookReaderDemo/demo-internetarchive.html +214 -18
- package/BookReaderDemo/demo-multiple.html +2 -1
- package/BookReaderDemo/demo-preview-pages.html +526 -525
- package/BookReaderDemo/demo-simple.html +2 -1
- package/BookReaderDemo/demo-vendor-fullscreen.html +2 -4
- package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
- package/BookReaderDemo/immersion-1up.html +2 -2
- package/BookReaderDemo/immersion-mode.html +2 -4
- package/BookReaderDemo/toggle_controls.html +3 -2
- package/BookReaderDemo/view_mode.html +2 -1
- package/BookReaderDemo/viewmode-cycle.html +2 -3
- package/CHANGELOG.md +595 -33
- package/README.md +14 -1
- package/babel.config.js +20 -0
- package/codecov.yml +6 -0
- package/index.html +5 -2
- package/jsconfig.json +19 -0
- package/netlify.toml +9 -0
- package/package.json +70 -62
- package/renovate.json +52 -0
- package/scripts/preversion.js +0 -1
- package/src/BookNavigator/assets/bookmark-colors.js +1 -1
- package/src/BookNavigator/assets/button-base.js +10 -2
- package/src/BookNavigator/assets/ia-logo.js +17 -0
- package/src/BookNavigator/assets/icon_checkmark.js +1 -1
- package/src/BookNavigator/assets/icon_close.js +1 -1
- package/src/BookNavigator/book-navigator.js +590 -0
- package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
- package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
- package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
- package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +4 -9
- package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
- package/src/BookNavigator/delete-modal-actions.js +1 -1
- package/src/BookNavigator/downloads/downloads-provider.js +36 -21
- package/src/BookNavigator/downloads/downloads.js +29 -25
- package/src/BookNavigator/search/search-provider.js +80 -28
- package/src/BookNavigator/search/search-results.js +29 -26
- package/src/BookNavigator/sharing.js +27 -0
- package/src/BookNavigator/viewable-files.js +95 -0
- package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +13 -12
- package/src/BookNavigator/visual-adjustments/visual-adjustments.js +7 -7
- package/src/BookReader/BookModel.js +76 -41
- package/src/BookReader/DragScrollable.js +233 -0
- package/src/BookReader/ImageCache.js +48 -15
- package/src/BookReader/Mode1Up.js +56 -351
- package/src/BookReader/Mode1UpLit.js +388 -0
- package/src/BookReader/Mode2Up.js +73 -1318
- package/src/BookReader/Mode2UpLit.js +777 -0
- package/src/BookReader/ModeCoordinateSpace.js +29 -0
- package/src/BookReader/ModeSmoothZoom.js +312 -0
- package/src/BookReader/ModeThumb.js +19 -13
- package/src/BookReader/Navbar/Navbar.js +70 -54
- package/src/BookReader/PageContainer.js +116 -22
- package/src/BookReader/ReduceSet.js +3 -3
- package/src/BookReader/Toolbar/Toolbar.js +14 -41
- package/src/BookReader/events.js +2 -3
- package/src/BookReader/options.js +73 -15
- package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/src/BookReader/utils/ScrollClassAdder.js +31 -0
- package/src/BookReader/utils/SelectionObserver.js +45 -0
- package/src/BookReader/utils/classes.js +1 -1
- package/src/BookReader/utils.js +128 -13
- package/src/BookReader.js +562 -1078
- package/src/BookReaderPlugin.js +44 -0
- package/src/assets/icons/close-circle-dark.svg +1 -0
- package/src/assets/icons/magnify-minus.svg +3 -7
- package/src/assets/icons/magnify-plus.svg +3 -7
- package/src/assets/icons/voice.svg +1 -0
- package/src/assets/images/unviewable_page.png +0 -0
- package/src/css/BookReader.scss +1 -17
- package/src/css/_BRBookmarks.scss +1 -1
- package/src/css/_BRComponent.scss +1 -1
- package/src/css/_BRicon.scss +8 -2
- package/src/css/_BRmain.scss +33 -27
- package/src/css/_BRnav.scss +12 -42
- package/src/css/_BRpages.scss +170 -42
- package/src/css/_BRsearch.scss +68 -230
- package/src/css/_BRtoolbar.scss +5 -5
- package/src/css/_TextSelection.scss +87 -27
- package/src/css/_colorbox.scss +2 -2
- package/src/css/_controls.scss +24 -7
- package/src/css/_icons.scss +7 -1
- package/src/ia-bookreader/ia-bookreader.js +224 -0
- package/src/plugins/plugin.archive_analytics.js +84 -78
- package/src/plugins/plugin.autoplay.js +99 -104
- package/src/plugins/plugin.chapters.js +237 -191
- package/src/plugins/plugin.iframe.js +1 -1
- package/src/plugins/plugin.iiif.js +141 -0
- package/src/plugins/plugin.resume.js +53 -50
- package/src/plugins/plugin.text_selection.js +503 -175
- package/src/plugins/plugin.vendor-fullscreen.js +7 -7
- package/src/plugins/search/plugin.search.js +183 -121
- package/src/plugins/search/utils.js +43 -0
- package/src/plugins/search/view.js +67 -202
- package/src/plugins/tts/AbstractTTSEngine.js +75 -45
- package/src/plugins/tts/FestivalTTSEngine.js +21 -31
- package/src/plugins/tts/PageChunk.js +16 -23
- package/src/plugins/tts/PageChunkIterator.js +11 -17
- package/src/plugins/tts/WebTTSEngine.js +88 -72
- package/src/plugins/tts/plugin.tts.js +310 -350
- package/src/plugins/tts/utils.js +16 -26
- package/src/plugins/url/UrlPlugin.js +191 -0
- package/src/plugins/{plugin.url.js → url/plugin.url.js} +47 -18
- package/src/util/browserSniffing.js +22 -0
- package/src/util/docCookies.js +21 -2
- package/src/util/strings.js +1 -0
- package/tests/e2e/README.md +37 -0
- package/tests/e2e/autoplay.test.js +9 -6
- package/tests/e2e/base.test.js +8 -16
- package/tests/e2e/helpers/base.js +55 -50
- package/tests/e2e/helpers/debug.js +1 -1
- package/tests/e2e/helpers/mockSearch.js +19 -22
- package/tests/e2e/helpers/params.js +17 -0
- package/tests/e2e/helpers/rightToLeft.js +8 -14
- package/tests/e2e/helpers/search.js +73 -0
- package/tests/e2e/models/Navigation.js +20 -37
- package/tests/e2e/rightToLeft.test.js +4 -5
- package/tests/e2e/viewmode.test.js +40 -33
- package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
- package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
- package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
- package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
- package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
- package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
- package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
- package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
- package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +109 -60
- package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
- package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
- package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
- package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
- package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
- package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
- package/tests/jest/BookReader/Mode2Up.test.js +98 -0
- package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
- package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
- package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
- package/tests/jest/BookReader/ModeThumb.test.js +71 -0
- package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +42 -29
- package/tests/jest/BookReader/PageContainer.test.js +238 -0
- package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
- package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +3 -3
- package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
- package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
- package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
- package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
- package/tests/jest/BookReader/utils.test.js +250 -0
- package/tests/jest/BookReader.keyboard.test.js +190 -0
- package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +10 -2
- package/tests/{BookReader.test.js → jest/BookReader.test.js} +43 -53
- package/tests/jest/plugins/plugin.archive_analytics.test.js +20 -0
- package/tests/jest/plugins/plugin.autoplay.test.js +35 -0
- package/tests/jest/plugins/plugin.chapters.test.js +195 -0
- package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +4 -4
- package/tests/{plugins → jest/plugins}/plugin.resume.test.js +22 -35
- package/tests/jest/plugins/plugin.text_selection.test.js +316 -0
- package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
- package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +26 -47
- package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +42 -9
- package/tests/jest/plugins/search/utils.js +25 -0
- package/tests/jest/plugins/search/utils.test.js +29 -0
- package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +30 -10
- package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
- package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
- package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
- package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
- package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
- package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
- package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +57 -18
- package/tests/jest/setup.js +3 -0
- package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
- package/tests/jest/util/docCookies.test.js +24 -0
- package/tests/{util → jest/util}/strings.test.js +1 -1
- package/tests/{utils.js → jest/utils.js} +38 -0
- package/webpack.config.js +16 -10
- package/.babelrc +0 -12
- package/.dependabot/config.yml +0 -6
- package/.testcaferc.json +0 -5
- package/BookReader/bookreader-component-bundle.js +0 -14330
- package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
- package/BookReader/bookreader-component-bundle.js.map +0 -1
- package/BookReader/icons/sort-ascending.svg +0 -1
- package/BookReader/icons/sort-descending.svg +0 -1
- package/BookReader/jquery-1.10.1.js +0 -108
- package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
- package/BookReader/plugins/plugin.menu_toggle.js +0 -369
- package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
- package/BookReader/plugins/plugin.mobile_nav.js +0 -335
- package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
- package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
- package/BookReaderDemo/IIIFBookReader.js +0 -207
- package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
- package/BookReaderDemo/demo-autoplay.html +0 -38
- package/BookReaderDemo/demo-iiif.js +0 -26
- package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
- package/karma.conf.js +0 -23
- package/src/BookNavigator/BookModel.js +0 -14
- package/src/BookNavigator/BookNavigator.js +0 -452
- package/src/BookNavigator/assets/book-loader.js +0 -27
- package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
- package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
- package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
- package/src/BookNavigator/search/a-search-result.js +0 -55
- package/src/BookNavigator/volumes/volumes-provider.js +0 -108
- package/src/BookNavigator/volumes/volumes.js +0 -162
- package/src/BookReader/DebugConsole.js +0 -54
- package/src/BookReaderComponent/BookReaderComponent.js +0 -112
- package/src/ItemNavigator/ItemNavigator.js +0 -372
- package/src/ItemNavigator/providers/sharing.js +0 -29
- package/src/assets/icons/sort-ascending.svg +0 -1
- package/src/assets/icons/sort-descending.svg +0 -1
- package/src/css/_MobileNav.scss +0 -194
- package/src/dragscrollable-br.js +0 -261
- package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
- package/src/plugins/plugin.mobile_nav.js +0 -287
- package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
- package/tests/BookReader/DebugConsole.test.js +0 -25
- package/tests/BookReader/Mode1Up.test.js +0 -164
- package/tests/BookReader/Mode2Up.test.js +0 -247
- package/tests/BookReader/PageContainer.test.js +0 -115
- package/tests/BookReader/utils.test.js +0 -109
- package/tests/e2e/helpers/desktopSearch.js +0 -72
- package/tests/e2e/helpers/mobileSearch.js +0 -85
- package/tests/e2e/ia-production/ia-prod-base.js +0 -17
- package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
- package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
- package/tests/karma/BookNavigator/volumes.test.js +0 -133
- package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
- package/tests/plugins/plugin.archive_analytics.test.js +0 -23
- package/tests/plugins/plugin.autoplay.test.js +0 -52
- package/tests/plugins/plugin.chapters.test.js +0 -130
- package/tests/plugins/plugin.mobile_nav.test.js +0 -66
- package/tests/plugins/plugin.text_selection.test.js +0 -203
- package/tests/util/docCookies.test.js +0 -15
@@ -1,19 +1,13 @@
|
|
1
1
|
//@ts-check
|
2
|
-
import {
|
2
|
+
import { createDIVPageLayer } from '../BookReader/PageContainer.js';
|
3
|
+
import { SelectionObserver } from '../BookReader/utils/SelectionObserver.js';
|
4
|
+
import { BookReaderPlugin } from '../BookReaderPlugin.js';
|
3
5
|
import { applyVariables } from '../util/strings.js';
|
4
6
|
/** @typedef {import('../util/strings.js').StringWithVars} StringWithVars */
|
7
|
+
/** @typedef {import('../BookReader/PageContainer.js').PageContainer} PageContainer */
|
5
8
|
|
6
9
|
const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader);
|
7
10
|
|
8
|
-
export const DEFAULT_OPTIONS = {
|
9
|
-
enabled: true,
|
10
|
-
/** @type {StringWithVars} The URL to fetch the entire DJVU xml. Supports options.vars */
|
11
|
-
fullDjvuXmlUrl: null,
|
12
|
-
/** @type {StringWithVars} The URL to fetch a single page of the DJVU xml. Supports options.vars. Also has {{pageIndex}} */
|
13
|
-
singlePageDjvuXmlUrl: null,
|
14
|
-
};
|
15
|
-
/** @typedef {typeof DEFAULT_OPTIONS} TextSelectionPluginOptions */
|
16
|
-
|
17
11
|
/**
|
18
12
|
* @template T
|
19
13
|
*/
|
@@ -35,45 +29,101 @@ export class Cache {
|
|
35
29
|
}
|
36
30
|
}
|
37
31
|
|
38
|
-
export class TextSelectionPlugin {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
this.svgWordElement = "tspan";
|
49
|
-
this.insertNewlines = avoidTspans
|
50
|
-
// Safari has a bug where `pointer-events` doesn't work on `<tspans>`. So
|
51
|
-
// there we will set `pointer-events: all` on the paragraph element. We don't
|
52
|
-
// do this everywhere, because it's a worse experience. Thanks Safari :/
|
53
|
-
this.pointerEventsOnParagraph = pointerEventsOnParagraph;
|
54
|
-
if (avoidTspans) {
|
55
|
-
this.svgParagraphElement = "g";
|
56
|
-
this.svgWordElement = "text";
|
57
|
-
}
|
32
|
+
export class TextSelectionPlugin extends BookReaderPlugin {
|
33
|
+
options = {
|
34
|
+
enabled: true,
|
35
|
+
/** @type {StringWithVars} The URL to fetch the entire DJVU xml. Supports options.vars */
|
36
|
+
fullDjvuXmlUrl: null,
|
37
|
+
/** @type {StringWithVars} The URL to fetch a single page of the DJVU xml. Supports options.vars. Also has {{pageIndex}} */
|
38
|
+
singlePageDjvuXmlUrl: null,
|
39
|
+
/** Whether to fetch the XML as a jsonp */
|
40
|
+
jsonp: false,
|
41
|
+
}
|
58
42
|
|
59
|
-
|
60
|
-
|
43
|
+
/**@type {PromiseLike<JQuery<HTMLElement>|undefined>} */
|
44
|
+
djvuPagesPromise = null;
|
61
45
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
46
|
+
/** @type {Cache<{index: number, response: any}>} */
|
47
|
+
pageTextCache = new Cache();
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Sometimes there are too many words on a page, and the browser becomes near
|
51
|
+
* unusable. For now don't render text layer for pages with too many words.
|
52
|
+
*/
|
53
|
+
maxWordRendered = 2500;
|
54
|
+
|
55
|
+
/**
|
56
|
+
* @param {import('../BookReader.js').default} br
|
57
|
+
*/
|
58
|
+
constructor(br) {
|
59
|
+
super(br);
|
60
|
+
// In the future this should be in the ocr file
|
61
|
+
// since a book being right to left doesn't mean the ocr is right to left. But for
|
62
|
+
// now we do make that assumption.
|
63
|
+
/** Whether the book is right-to-left */
|
64
|
+
this.rtl = this.br.pageProgression === 'rl';
|
65
|
+
this.selectionObserver = new SelectionObserver('.BRtextLayer', this._onSelectionChange);
|
67
66
|
}
|
68
67
|
|
68
|
+
/** @override */
|
69
69
|
init() {
|
70
|
+
if (!this.options.enabled) return;
|
71
|
+
|
72
|
+
this.loadData();
|
73
|
+
|
74
|
+
this.selectionObserver.attach();
|
75
|
+
new SelectionObserver('.BRtextLayer', (selectEvent) => {
|
76
|
+
// Track how often selection is used
|
77
|
+
if (selectEvent == 'started') {
|
78
|
+
this.br._plugins.archiveAnalytics?.sendEvent('BookReader', 'SelectStart');
|
79
|
+
|
80
|
+
// Set a class on the page to avoid hiding it when zooming/etc
|
81
|
+
this.br.refs.$br.find('.BRpagecontainer--hasSelection').removeClass('BRpagecontainer--hasSelection');
|
82
|
+
$(window.getSelection().anchorNode).closest('.BRpagecontainer').addClass('BRpagecontainer--hasSelection');
|
83
|
+
}
|
84
|
+
}).attach();
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* @override
|
89
|
+
* @param {PageContainer} pageContainer
|
90
|
+
* @returns {PageContainer}
|
91
|
+
*/
|
92
|
+
_configurePageContainer(pageContainer) {
|
93
|
+
// Disable if thumb mode; it's too janky
|
94
|
+
// .page can be null for "pre-cover" region
|
95
|
+
if (this.br.mode !== this.br.constModeThumb && pageContainer.page) {
|
96
|
+
this.createTextLayer(pageContainer);
|
97
|
+
}
|
98
|
+
return pageContainer;
|
99
|
+
}
|
100
|
+
|
101
|
+
/**
|
102
|
+
* @param {'started' | 'cleared'} type
|
103
|
+
* @param {HTMLElement} target
|
104
|
+
*/
|
105
|
+
_onSelectionChange = (type, target) => {
|
106
|
+
if (type === 'started') {
|
107
|
+
this.textSelectingMode(target);
|
108
|
+
} else if (type === 'cleared') {
|
109
|
+
this.defaultMode(target);
|
110
|
+
} else {
|
111
|
+
throw new Error(`Unknown type ${type}`);
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
loadData() {
|
70
116
|
// Only fetch the full djvu xml if the single page url isn't there
|
71
117
|
if (this.options.singlePageDjvuXmlUrl) return;
|
72
118
|
this.djvuPagesPromise = $.ajax({
|
73
119
|
type: "GET",
|
74
|
-
url: applyVariables(this.options.fullDjvuXmlUrl, this.
|
75
|
-
dataType: "html",
|
76
|
-
|
120
|
+
url: applyVariables(this.options.fullDjvuXmlUrl, this.br.options.vars),
|
121
|
+
dataType: this.options.jsonp ? "jsonp" : "html",
|
122
|
+
cache: true,
|
123
|
+
xhrFields: {
|
124
|
+
withCredentials: this.br.protected,
|
125
|
+
},
|
126
|
+
error: (e) => undefined,
|
77
127
|
}).then((res) => {
|
78
128
|
try {
|
79
129
|
const xmlMap = $.parseXML(res);
|
@@ -94,21 +144,24 @@ export class TextSelectionPlugin {
|
|
94
144
|
if (cachedEntry) {
|
95
145
|
return cachedEntry.response;
|
96
146
|
}
|
97
|
-
|
147
|
+
const res = await $.ajax({
|
98
148
|
type: "GET",
|
99
|
-
url: applyVariables(this.options.singlePageDjvuXmlUrl, this.
|
100
|
-
dataType: "html",
|
149
|
+
url: applyVariables(this.options.singlePageDjvuXmlUrl, this.br.options.vars, { pageIndex: index }),
|
150
|
+
dataType: this.options.jsonp ? "jsonp" : "html",
|
151
|
+
cache: true,
|
152
|
+
xhrFields: {
|
153
|
+
withCredentials: this.br.protected,
|
154
|
+
},
|
101
155
|
error: (e) => undefined,
|
102
|
-
}).then((res) => {
|
103
|
-
try {
|
104
|
-
const xmlDoc = $.parseXML(res);
|
105
|
-
const result = xmlDoc && $(xmlDoc).find("OBJECT")[0];
|
106
|
-
this.pageTextCache.add({ index, response: result });
|
107
|
-
return result;
|
108
|
-
} catch (e) {
|
109
|
-
return undefined;
|
110
|
-
}
|
111
156
|
});
|
157
|
+
try {
|
158
|
+
const xmlDoc = $.parseXML(res);
|
159
|
+
const result = xmlDoc && $(xmlDoc).find("OBJECT")[0];
|
160
|
+
this.pageTextCache.add({ index, response: result });
|
161
|
+
return result;
|
162
|
+
} catch (e) {
|
163
|
+
return undefined;
|
164
|
+
}
|
112
165
|
} else {
|
113
166
|
const XMLpagesArr = await this.djvuPagesPromise;
|
114
167
|
if (XMLpagesArr) return XMLpagesArr[index];
|
@@ -129,67 +182,87 @@ export class TextSelectionPlugin {
|
|
129
182
|
|
130
183
|
/**
|
131
184
|
* Applies mouse events when in default mode
|
132
|
-
* @param {
|
185
|
+
* @param {HTMLElement} textLayer
|
133
186
|
*/
|
134
|
-
defaultMode(
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
187
|
+
defaultMode(textLayer) {
|
188
|
+
const $pageContainer = $(textLayer).closest('.BRpagecontainer');
|
189
|
+
textLayer.style.pointerEvents = "none";
|
190
|
+
$pageContainer.find("img").css("pointer-events", "auto");
|
191
|
+
|
192
|
+
$(textLayer).off(".textSelectPluginHandler");
|
193
|
+
const startedMouseDown = this.mouseIsDown;
|
194
|
+
let skipNextMouseup = this.mouseIsDown;
|
195
|
+
if (startedMouseDown) {
|
196
|
+
textLayer.style.pointerEvents = "auto";
|
197
|
+
}
|
198
|
+
|
199
|
+
// Need to stop propagation to prevent DragScrollable from
|
200
|
+
// blocking selection
|
201
|
+
$(textLayer).on("mousedown.textSelectPluginHandler", (event) => {
|
202
|
+
this.mouseIsDown = true;
|
203
|
+
if ($(event.target).is(".BRwordElement, .BRspace")) {
|
204
|
+
event.stopPropagation();
|
205
|
+
}
|
206
|
+
});
|
207
|
+
|
208
|
+
$(textLayer).on("mouseup.textSelectPluginHandler", (event) => {
|
209
|
+
this.mouseIsDown = false;
|
210
|
+
textLayer.style.pointerEvents = "none";
|
211
|
+
if (skipNextMouseup) {
|
212
|
+
skipNextMouseup = false;
|
213
|
+
event.stopPropagation();
|
214
|
+
}
|
215
|
+
});
|
149
216
|
}
|
150
217
|
|
151
218
|
/**
|
152
|
-
*
|
153
|
-
* @param {
|
219
|
+
* This mode is active while there is a selection on the given textLayer
|
220
|
+
* @param {HTMLElement} textLayer
|
154
221
|
*/
|
155
|
-
textSelectingMode(
|
156
|
-
$(
|
157
|
-
|
158
|
-
|
159
|
-
|
222
|
+
textSelectingMode(textLayer) {
|
223
|
+
const $pageContainer = $(textLayer).closest('.BRpagecontainer');
|
224
|
+
// Make text layer consume all events
|
225
|
+
textLayer.style.pointerEvents = "all";
|
226
|
+
// Block img from getting long-press to save while selecting
|
227
|
+
$pageContainer.find("img").css("pointer-events", "none");
|
228
|
+
|
229
|
+
$(textLayer).off(".textSelectPluginHandler");
|
230
|
+
|
231
|
+
$(textLayer).on('mousedown.textSelectPluginHandler', (event) => {
|
232
|
+
this.mouseIsDown = true;
|
160
233
|
event.stopPropagation();
|
161
|
-
})
|
162
|
-
|
234
|
+
});
|
235
|
+
|
236
|
+
// Prevent page flip on click
|
237
|
+
$(textLayer).on('mouseup.textSelectPluginHandler', (event) => {
|
238
|
+
this.mouseIsDown = false;
|
163
239
|
event.stopPropagation();
|
164
|
-
|
165
|
-
$(svg).off(".textSelectPluginHandler");
|
166
|
-
this.defaultMode(svg); }
|
167
|
-
})
|
240
|
+
});
|
168
241
|
}
|
169
242
|
|
170
243
|
/**
|
171
|
-
* Initializes text selection modes if there is
|
244
|
+
* Initializes text selection modes if there is a text layer on the page
|
172
245
|
* @param {JQuery} $container
|
173
246
|
*/
|
174
247
|
stopPageFlip($container) {
|
175
|
-
/** @type {JQuery<
|
176
|
-
const $
|
177
|
-
if (!$
|
178
|
-
$
|
248
|
+
/** @type {JQuery<HTMLElement>} */
|
249
|
+
const $textLayer = $container.find('.BRtextLayer');
|
250
|
+
if (!$textLayer.length) return;
|
251
|
+
$textLayer.each((i, s) => this.defaultMode(s));
|
179
252
|
this.interceptCopy($container);
|
180
253
|
}
|
181
254
|
|
182
255
|
/**
|
183
|
-
* @param {
|
184
|
-
* @param {JQuery} $container
|
256
|
+
* @param {PageContainer} pageContainer
|
185
257
|
*/
|
186
|
-
async createTextLayer(
|
187
|
-
const
|
188
|
-
|
258
|
+
async createTextLayer(pageContainer) {
|
259
|
+
const pageIndex = pageContainer.page.index;
|
260
|
+
const $container = pageContainer.$container;
|
261
|
+
const $textLayers = $container.find('.BRtextLayer');
|
262
|
+
if ($textLayers.length) return;
|
189
263
|
const XMLpage = await this.getPageText(pageIndex);
|
190
264
|
if (!XMLpage) return;
|
191
|
-
|
192
|
-
const XMLheight = $(XMLpage).attr("height");
|
265
|
+
recursivelyAddCoords(XMLpage);
|
193
266
|
|
194
267
|
const totalWords = $(XMLpage).find("WORD").length;
|
195
268
|
if (totalWords > this.maxWordRendered) {
|
@@ -197,107 +270,362 @@ export class TextSelectionPlugin {
|
|
197
270
|
return;
|
198
271
|
}
|
199
272
|
|
200
|
-
const
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
$(
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
"left": "0",
|
273
|
+
const textLayer = createDIVPageLayer(pageContainer.page, 'BRtextLayer');
|
274
|
+
const ratioW = parseFloat(pageContainer.$container[0].style.width) / pageContainer.page.width;
|
275
|
+
const ratioH = parseFloat(pageContainer.$container[0].style.height) / pageContainer.page.height;
|
276
|
+
textLayer.style.transform = `scale(${ratioW}, ${ratioH})`;
|
277
|
+
textLayer.setAttribute("dir", this.rtl ? "rtl" : "ltr");
|
278
|
+
|
279
|
+
const ocrParagraphs = $(XMLpage).find("PARAGRAPH[coords]").toArray();
|
280
|
+
const paragEls = ocrParagraphs.map(p => {
|
281
|
+
const el = this.renderParagraph(p);
|
282
|
+
textLayer.appendChild(el);
|
283
|
+
return el;
|
212
284
|
});
|
213
285
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
const
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
286
|
+
// Fix up paragraph positions
|
287
|
+
const paragraphRects = determineRealRects(textLayer, '.BRparagraphElement');
|
288
|
+
let yAdded = 0;
|
289
|
+
for (const [ocrParagraph, paragEl] of zip(ocrParagraphs, paragEls)) {
|
290
|
+
const ocrParagBounds = $(ocrParagraph).attr("coords").split(",").map(parseFloat);
|
291
|
+
const realRect = paragraphRects.get(paragEl);
|
292
|
+
const [ocrLeft, , ocrRight, ocrTop] = ocrParagBounds;
|
293
|
+
const newStartMargin = this.rtl ? (realRect.right - ocrRight) : (ocrLeft - realRect.left);
|
294
|
+
const newTop = ocrTop - (realRect.top + yAdded);
|
223
295
|
|
224
|
-
|
296
|
+
paragEl.style[this.rtl ? 'marginRight' : 'marginLeft'] = `${newStartMargin}px`;
|
297
|
+
paragEl.style.marginTop = `${newTop}px`;
|
298
|
+
yAdded += newTop;
|
299
|
+
textLayer.appendChild(paragEl);
|
300
|
+
}
|
301
|
+
$container.append(textLayer);
|
302
|
+
this.stopPageFlip($container);
|
303
|
+
}
|
304
|
+
|
305
|
+
/**
|
306
|
+
* @param {HTMLElement} ocrParagraph
|
307
|
+
* @returns {HTMLParagraphElement}
|
308
|
+
*/
|
309
|
+
renderParagraph(ocrParagraph) {
|
310
|
+
const paragEl = document.createElement('p');
|
311
|
+
paragEl.classList.add('BRparagraphElement');
|
312
|
+
const [paragLeft, paragBottom, paragRight, paragTop] = $(ocrParagraph).attr("coords").split(",").map(parseFloat);
|
313
|
+
const wordHeightArr = [];
|
314
|
+
const lines = $(ocrParagraph).find("LINE[coords]").toArray();
|
315
|
+
if (!lines.length) return paragEl;
|
225
316
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
317
|
+
|
318
|
+
for (const [prevLine, line, nextLine] of lookAroundWindow(genMap(lines, augmentLine))) {
|
319
|
+
const isLastLineOfParagraph = line.ocrElement == lines[lines.length - 1];
|
320
|
+
const lineEl = document.createElement('span');
|
321
|
+
lineEl.classList.add('BRlineElement');
|
322
|
+
|
323
|
+
for (const [wordIndex, currWord] of line.words.entries()) {
|
324
|
+
const [, bottom, right, top] = $(currWord).attr("coords").split(',').map(parseFloat);
|
231
325
|
const wordHeight = bottom - top;
|
232
326
|
wordHeightArr.push(wordHeight);
|
233
327
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
paragSvg.appendChild(wordTspan);
|
242
|
-
|
243
|
-
// Adding spaces after words except at the end of the paragraph
|
244
|
-
// TODO: assumes left-to-right text
|
245
|
-
if (i < words.length - 1) {
|
246
|
-
const nextWord = words[i + 1];
|
247
|
-
// eslint-disable-next-line no-unused-vars
|
248
|
-
const [leftNext, bottomNext, rightNext, topNext] = $(nextWord).attr("coords").split(',').map(parseFloat);
|
249
|
-
const spaceTspan = document.createElementNS("http://www.w3.org/2000/svg", this.svgWordElement);
|
250
|
-
spaceTspan.setAttribute("class", "BRwordElement");
|
251
|
-
spaceTspan.setAttribute("x", right.toString());
|
252
|
-
spaceTspan.setAttribute("y", bottom.toString());
|
253
|
-
if ((leftNext - right) > 0) spaceTspan.setAttribute("textLength", (leftNext - right).toString());
|
254
|
-
spaceTspan.setAttribute("lengthAdjust", "spacingAndGlyphs");
|
255
|
-
spaceTspan.textContent = " ";
|
256
|
-
paragSvg.appendChild(spaceTspan);
|
328
|
+
if (wordIndex == 0 && prevLine?.lastWord.textContent.trim().endsWith('-')) {
|
329
|
+
// ideally prefer the next line to determine the left position,
|
330
|
+
// since the previous line could be the first line of the paragraph
|
331
|
+
// and hence have an incorrectly indented first word.
|
332
|
+
// E.g. https://archive.org/details/driitaleofdaring00bachuoft/page/360/mode/2up
|
333
|
+
const [newLeft, , , ] = $((nextLine || prevLine).firstWord).attr("coords").split(',').map(parseFloat);
|
334
|
+
$(currWord).attr("coords", `${newLeft},${bottom},${right},${top}`);
|
257
335
|
}
|
258
336
|
|
259
|
-
|
260
|
-
|
261
|
-
|
337
|
+
const wordEl = document.createElement('span');
|
338
|
+
wordEl.setAttribute("class", "BRwordElement");
|
339
|
+
wordEl.textContent = currWord.textContent.trim();
|
340
|
+
|
341
|
+
if (wordIndex > 0) {
|
342
|
+
const space = document.createElement('span');
|
343
|
+
space.classList.add('BRspace');
|
344
|
+
space.textContent = ' ';
|
345
|
+
lineEl.append(space);
|
346
|
+
|
347
|
+
// Edge ignores empty elements (like BRspace), so add another
|
348
|
+
// space to ensure Edge's ReadAloud works correctly.
|
349
|
+
lineEl.appendChild(document.createTextNode(' '));
|
262
350
|
}
|
351
|
+
|
352
|
+
lineEl.appendChild(wordEl);
|
263
353
|
}
|
264
354
|
|
265
|
-
|
266
|
-
const
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
355
|
+
const hasHyphen = line.lastWord.textContent.trim().endsWith('-');
|
356
|
+
const lastWordEl = lineEl.children[lineEl.children.length - 1];
|
357
|
+
if (hasHyphen && !isLastLineOfParagraph) {
|
358
|
+
lastWordEl.textContent = lastWordEl.textContent.trim().slice(0, -1);
|
359
|
+
lastWordEl.classList.add('BRwordElement--hyphen');
|
360
|
+
}
|
361
|
+
|
362
|
+
paragEl.appendChild(lineEl);
|
363
|
+
if (!isLastLineOfParagraph && !hasHyphen) {
|
364
|
+
// Edge does not correctly have spaces between the lines.
|
365
|
+
paragEl.appendChild(document.createTextNode(' '));
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
wordHeightArr.sort((a, b) => a - b);
|
370
|
+
const paragWordHeight = wordHeightArr[Math.floor(wordHeightArr.length * 0.85)] + 4;
|
371
|
+
paragEl.style.left = `${paragLeft}px`;
|
372
|
+
paragEl.style.top = `${paragTop}px`;
|
373
|
+
paragEl.style.width = `${paragRight - paragLeft}px`;
|
374
|
+
paragEl.style.height = `${paragBottom - paragTop}px`;
|
375
|
+
paragEl.style.fontSize = `${paragWordHeight}px`;
|
376
|
+
|
377
|
+
// Fix up sizes - stretch/crush words as necessary using letter spacing
|
378
|
+
let wordRects = determineRealRects(paragEl, '.BRwordElement');
|
379
|
+
const ocrWords = $(ocrParagraph).find("WORD").toArray();
|
380
|
+
const wordEls = paragEl.querySelectorAll('.BRwordElement');
|
381
|
+
for (const [ocrWord, wordEl] of zip(ocrWords, wordEls)) {
|
382
|
+
const realRect = wordRects.get(wordEl);
|
383
|
+
const [left, , right ] = $(ocrWord).attr("coords").split(',').map(parseFloat);
|
384
|
+
let ocrWidth = right - left;
|
385
|
+
// Some books (eg theworksofplato01platiala) have a space _inside_ the <WORD>
|
386
|
+
// element. That makes it impossible to determine the correct positining
|
387
|
+
// of everything, but to avoid the BRspace's being width 0, which makes selection
|
388
|
+
// janky on Chrome Android, assume the space is the same width as one of the
|
389
|
+
// letters.
|
390
|
+
if (ocrWord.textContent.endsWith(' ')) {
|
391
|
+
ocrWidth = ocrWidth * (ocrWord.textContent.length - 1) / ocrWord.textContent.length;
|
392
|
+
}
|
393
|
+
const diff = ocrWidth - realRect.width;
|
394
|
+
wordEl.style.letterSpacing = `${diff / (ocrWord.textContent.length - 1)}px`;
|
395
|
+
}
|
396
|
+
|
397
|
+
// Stretch/crush lines as necessary using line spacing
|
398
|
+
// Recompute rects after letter spacing
|
399
|
+
wordRects = determineRealRects(paragEl, '.BRwordElement');
|
400
|
+
const spaceRects = determineRealRects(paragEl, '.BRspace');
|
401
|
+
|
402
|
+
const ocrLines = $(ocrParagraph).find("LINE[coords]").toArray();
|
403
|
+
const lineEls = Array.from(paragEl.querySelectorAll('.BRlineElement'));
|
404
|
+
|
405
|
+
let ySoFar = paragTop;
|
406
|
+
for (const [ocrLine, lineEl] of zip(ocrLines, lineEls)) {
|
407
|
+
// shift words using marginLeft to align with the correct x position
|
408
|
+
const words = $(ocrLine).find("WORD").toArray();
|
409
|
+
// const ocrLineLeft = Math.min(...words.map(w => parseFloat($(w).attr("coords").split(',')[0])));
|
410
|
+
let xSoFar = this.rtl ? paragRight : paragLeft;
|
411
|
+
for (const [ocrWord, wordEl] of zip(words, lineEl.querySelectorAll('.BRwordElement'))) {
|
412
|
+
// start of line, need to compute the offset relative to the OCR words
|
413
|
+
const wordRect = wordRects.get(wordEl);
|
414
|
+
const [ocrLeft, , ocrRight ] = $(ocrWord).attr("coords").split(',').map(parseFloat);
|
415
|
+
const diff = (this.rtl ? -(ocrRight - xSoFar) : ocrLeft - xSoFar);
|
416
|
+
|
417
|
+
if (wordEl.previousElementSibling) {
|
418
|
+
const space = wordEl.previousElementSibling;
|
419
|
+
space.style.letterSpacing = `${diff - spaceRects.get(space).width}px`;
|
420
|
+
} else {
|
421
|
+
wordEl.style[this.rtl ? 'paddingRight' : 'paddingLeft'] = `${diff}px`;
|
422
|
+
}
|
423
|
+
if (this.rtl) xSoFar -= diff + wordRect.width;
|
424
|
+
else xSoFar += diff + wordRect.width;
|
425
|
+
}
|
426
|
+
// And also fix y position
|
427
|
+
const ocrLineTop = Math.min(...words.map(w => parseFloat($(w).attr("coords").split(',')[3])));
|
428
|
+
const diff = ocrLineTop - ySoFar;
|
429
|
+
if (lineEl.previousElementSibling) {
|
430
|
+
lineEl.previousElementSibling.style.lineHeight = `${diff}px`;
|
431
|
+
ySoFar += diff;
|
432
|
+
}
|
433
|
+
}
|
434
|
+
|
435
|
+
// The last line will have a line height subtracting from the paragraph height
|
436
|
+
lineEls[lineEls.length - 1].style.lineHeight = `${paragBottom - ySoFar}px`;
|
437
|
+
|
438
|
+
// Edge does not include a newline for some reason when copying/pasting the <p> els
|
439
|
+
paragEl.appendChild(document.createElement('br'));
|
440
|
+
return paragEl;
|
271
441
|
}
|
272
442
|
}
|
273
443
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
444
|
+
BookReader?.registerPlugin('textSelection', TextSelectionPlugin);
|
445
|
+
|
446
|
+
|
447
|
+
/**
|
448
|
+
* @param {HTMLElement} parentEl
|
449
|
+
* @param {string} selector
|
450
|
+
* @returns {Map<Element, Rect>}
|
451
|
+
*/
|
452
|
+
function determineRealRects(parentEl, selector) {
|
453
|
+
const initals = {
|
454
|
+
position: parentEl.style.position,
|
455
|
+
visibility: parentEl.style.visibility,
|
456
|
+
top: parentEl.style.top,
|
457
|
+
left: parentEl.style.left,
|
458
|
+
transform: parentEl.style.transform,
|
459
|
+
};
|
460
|
+
parentEl.style.position = 'absolute';
|
461
|
+
parentEl.style.visibility = 'hidden';
|
462
|
+
parentEl.style.top = '0';
|
463
|
+
parentEl.style.left = '0';
|
464
|
+
parentEl.style.transform = 'none';
|
465
|
+
document.body.appendChild(parentEl);
|
466
|
+
const rects = new Map(
|
467
|
+
Array.from(parentEl.querySelectorAll(selector))
|
468
|
+
.map(wordEl => {
|
469
|
+
const origRect = wordEl.getBoundingClientRect();
|
470
|
+
return [wordEl, new Rect(
|
471
|
+
origRect.left + window.scrollX,
|
472
|
+
origRect.top + window.scrollY,
|
473
|
+
origRect.width,
|
474
|
+
origRect.height,
|
475
|
+
)];
|
476
|
+
}),
|
477
|
+
);
|
478
|
+
document.body.removeChild(parentEl);
|
479
|
+
Object.assign(parentEl.style, initals);
|
480
|
+
return rects;
|
481
|
+
}
|
482
|
+
|
483
|
+
/**
|
484
|
+
* @param {HTMLElement} line
|
485
|
+
*/
|
486
|
+
function augmentLine(line) {
|
487
|
+
const words = $(line).find("WORD").toArray();
|
488
|
+
return {
|
489
|
+
ocrElement: line,
|
490
|
+
words,
|
491
|
+
firstWord: words[0],
|
492
|
+
lastWord: words[words.length - 1],
|
493
|
+
};
|
494
|
+
}
|
495
|
+
|
496
|
+
/**
|
497
|
+
* @template TFrom, TTo
|
498
|
+
* Generator version of map
|
499
|
+
* @param {Iterable<TFrom>} gen
|
500
|
+
* @param {function(TFrom): TTo} fn
|
501
|
+
* @returns {Iterable<TTo>}
|
502
|
+
*/
|
503
|
+
export function* genMap(gen, fn) {
|
504
|
+
for (const x of gen) yield fn(x);
|
505
|
+
}
|
506
|
+
|
507
|
+
/**
|
508
|
+
* @template T
|
509
|
+
* Generator that provides a sliding window of 3 elements,
|
510
|
+
* prev, current, and next.
|
511
|
+
* @param {Iterable<T>} gen
|
512
|
+
* @returns {Iterable<[T | undefined, T, T | undefined]>}
|
513
|
+
*/
|
514
|
+
export function* lookAroundWindow(gen) {
|
515
|
+
let prev = undefined;
|
516
|
+
let cur = undefined;
|
517
|
+
let next = undefined;
|
518
|
+
for (const x of gen) {
|
519
|
+
if (typeof cur !== 'undefined') {
|
520
|
+
next = x;
|
521
|
+
yield [prev, cur, next];
|
283
522
|
}
|
284
|
-
|
523
|
+
prev = cur;
|
524
|
+
cur = x;
|
525
|
+
next = undefined;
|
526
|
+
}
|
527
|
+
|
528
|
+
if (typeof cur !== 'undefined') {
|
529
|
+
yield [prev, cur, next];
|
285
530
|
}
|
531
|
+
}
|
286
532
|
|
533
|
+
/**
|
534
|
+
* @template T1, T2
|
535
|
+
* Lazy zip implementation to avoid importing lodash
|
536
|
+
* Expects iterators to be of the same length
|
537
|
+
* @param {Iterable<T1>} gen1
|
538
|
+
* @param {Iterable<T2>} gen2
|
539
|
+
* @returns {Iterable<[T1, T2]>}
|
540
|
+
*/
|
541
|
+
export function* zip(gen1, gen2) {
|
542
|
+
const it1 = gen1[Symbol.iterator]();
|
543
|
+
const it2 = gen2[Symbol.iterator]();
|
544
|
+
while (true) {
|
545
|
+
const r1 = it1.next();
|
546
|
+
const r2 = it2.next();
|
547
|
+
if (r1.done && r2.done) {
|
548
|
+
return;
|
549
|
+
}
|
550
|
+
if (r1.done || r2.done) {
|
551
|
+
throw new Error('zip: one of the iterators is done');
|
552
|
+
}
|
553
|
+
yield [r1.value, r2.value];
|
554
|
+
}
|
555
|
+
}
|
556
|
+
|
557
|
+
/**
|
558
|
+
* [left, bottom, right, top]
|
559
|
+
* @param {Array<[number, number, number, number]>} bounds
|
560
|
+
* @returns {[number, number, number, number]}
|
561
|
+
*/
|
562
|
+
function determineBounds(bounds) {
|
563
|
+
let leftMost = Infinity;
|
564
|
+
let bottomMost = -Infinity;
|
565
|
+
let rightMost = -Infinity;
|
566
|
+
let topMost = Infinity;
|
567
|
+
|
568
|
+
for (const [left, bottom, right, top] of bounds) {
|
569
|
+
leftMost = Math.min(leftMost, left);
|
570
|
+
bottomMost = Math.max(bottomMost, bottom);
|
571
|
+
rightMost = Math.max(rightMost, right);
|
572
|
+
topMost = Math.min(topMost, top);
|
573
|
+
}
|
574
|
+
|
575
|
+
return [leftMost, bottomMost, rightMost, topMost];
|
576
|
+
}
|
577
|
+
|
578
|
+
/**
|
579
|
+
* Recursively traverses the XML tree and adds coords
|
580
|
+
* which are the bounding box of all child coords
|
581
|
+
* @param {Element} xmlEl
|
582
|
+
*/
|
583
|
+
function recursivelyAddCoords(xmlEl) {
|
584
|
+
if ($(xmlEl).attr('coords') || !xmlEl.children) {
|
585
|
+
return;
|
586
|
+
}
|
587
|
+
|
588
|
+
const children = $(xmlEl).children().toArray();
|
589
|
+
if (children.length === 0) {
|
590
|
+
return;
|
591
|
+
}
|
592
|
+
|
593
|
+
for (const child of children) {
|
594
|
+
recursivelyAddCoords(child);
|
595
|
+
}
|
596
|
+
|
597
|
+
const childCoords = [];
|
598
|
+
|
599
|
+
for (const child of children) {
|
600
|
+
if (!$(child).attr('coords')) continue;
|
601
|
+
childCoords.push($(child).attr('coords').split(',').map(parseFloat));
|
602
|
+
}
|
603
|
+
|
604
|
+
const boundingCoords = determineBounds(childCoords);
|
605
|
+
if (Math.abs(boundingCoords[0]) != Infinity) {
|
606
|
+
$(xmlEl).attr('coords', boundingCoords.join(','));
|
607
|
+
}
|
608
|
+
}
|
609
|
+
|
610
|
+
/**
|
611
|
+
* Basically a polyfill for the native DOMRect class
|
612
|
+
*/
|
613
|
+
class Rect {
|
287
614
|
/**
|
288
|
-
* @param {number}
|
615
|
+
* @param {number} x
|
616
|
+
* @param {number} y
|
617
|
+
* @param {number} width
|
618
|
+
* @param {number} height
|
289
619
|
*/
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
const lastPageIndex = this.getNumLeafs() - 1;
|
296
|
-
if (this.mode !== this.constModeThumb && (index >= 0 && index <= lastPageIndex)) {
|
297
|
-
this.textSelectionPlugin?.createTextLayer(index, pageContainer.$container);
|
298
|
-
}
|
299
|
-
return pageContainer;
|
620
|
+
constructor(x, y, width, height) {
|
621
|
+
this.x = x;
|
622
|
+
this.y = y;
|
623
|
+
this.width = width;
|
624
|
+
this.height = height;
|
300
625
|
}
|
626
|
+
|
627
|
+
get right() { return this.x + this.width; }
|
628
|
+
get bottom() { return this.y + this.height; }
|
629
|
+
get top() { return this.y; }
|
630
|
+
get left() { return this.x; }
|
301
631
|
}
|
302
|
-
window.BookReader = BookreaderWithTextSelection;
|
303
|
-
export default BookreaderWithTextSelection;
|