@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
@@ -2,6 +2,8 @@
|
|
2
2
|
/** @typedef {import('./BookModel.js').PageModel} PageModel */
|
3
3
|
/** @typedef {import('./ImageCache.js').ImageCache} ImageCache */
|
4
4
|
|
5
|
+
import { sleep } from './utils.js';
|
6
|
+
|
5
7
|
|
6
8
|
export class PageContainer {
|
7
9
|
/**
|
@@ -9,12 +11,10 @@ export class PageContainer {
|
|
9
11
|
* @param {object} opts
|
10
12
|
* @param {boolean} opts.isProtected Whether we're in a protected book
|
11
13
|
* @param {ImageCache} opts.imageCache
|
12
|
-
* @param {string} opts.loadingImage
|
13
14
|
*/
|
14
|
-
constructor(page, {isProtected, imageCache
|
15
|
+
constructor(page, {isProtected, imageCache}) {
|
15
16
|
this.page = page;
|
16
17
|
this.imageCache = imageCache;
|
17
|
-
this.loadingImage = loadingImage;
|
18
18
|
this.$container = $('<div />', {
|
19
19
|
'class': `BRpagecontainer ${page ? `pagediv${page.index}` : 'BRemptypage'}`,
|
20
20
|
css: { position: 'absolute' },
|
@@ -43,33 +43,127 @@ export class PageContainer {
|
|
43
43
|
return;
|
44
44
|
}
|
45
45
|
|
46
|
-
const
|
47
|
-
const
|
46
|
+
const finalReduce = this.imageCache.getFinalReduce(this.page.index, reduce);
|
47
|
+
const newImageURI = this.page.getURI(finalReduce, 0);
|
48
48
|
|
49
|
-
//
|
50
|
-
this
|
51
|
-
|
52
|
-
.image(this.page.index, reduce)
|
53
|
-
.prependTo(this.$container);
|
49
|
+
// Note: These must be computed _before_ we call .image()
|
50
|
+
const alreadyLoaded = this.imageCache.imageLoaded(this.page.index, finalReduce);
|
51
|
+
const nextBestLoadedReduce = this.imageCache.getBestLoadedReduce(this.page.index, reduce);
|
54
52
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
53
|
+
// Avoid removing/re-adding the image if it's already there
|
54
|
+
// This can be called quite a bit, so we need to be fast
|
55
|
+
if (this.$img?.data('src') == newImageURI) {
|
56
|
+
return this;
|
59
57
|
}
|
60
|
-
|
61
|
-
|
58
|
+
|
59
|
+
let $oldImg = this.$img;
|
60
|
+
this.$img = this.imageCache.image(this.page.index, finalReduce);
|
61
|
+
if ($oldImg) {
|
62
|
+
this.$img.insertAfter($oldImg);
|
63
|
+
} else {
|
64
|
+
this.$img.prependTo(this.$container);
|
62
65
|
}
|
63
66
|
|
64
67
|
if (!alreadyLoaded) {
|
65
|
-
this.$
|
66
|
-
.css('background', backgroundLayers.join(','))
|
67
|
-
.one('loadend', async (ev) => {
|
68
|
-
$(ev.target).css({ 'background': '' })
|
69
|
-
$(ev.target).parent().removeClass('BRpageloading');
|
70
|
-
});
|
68
|
+
this.$container.addClass('BRpageloading');
|
71
69
|
}
|
72
70
|
|
71
|
+
if (!alreadyLoaded && nextBestLoadedReduce) {
|
72
|
+
// If we have a slightly lower quality image loaded, use that as the background
|
73
|
+
// while the higher res one loads
|
74
|
+
const nextBestUri = this.page.getURI(nextBestLoadedReduce, 0);
|
75
|
+
if ($oldImg) {
|
76
|
+
if ($oldImg.data('src') == nextBestUri) {
|
77
|
+
// Do nothing! It's already showing the right thing
|
78
|
+
} else {
|
79
|
+
// We have a different src, need to update the src
|
80
|
+
this.imageCache.image(this.page.index, nextBestLoadedReduce, $oldImg[0]);
|
81
|
+
}
|
82
|
+
} else {
|
83
|
+
// We don't have an old <img>, so we need to create a new one
|
84
|
+
$oldImg = this.imageCache.image(this.page.index, nextBestLoadedReduce);
|
85
|
+
$oldImg.prependTo(this.$container);
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
this.$img
|
90
|
+
.one('load', async (ev) => {
|
91
|
+
this.$container.removeClass('BRpageloading');
|
92
|
+
// `load` can fire a little early, so wait a spell before removing the old image
|
93
|
+
// to avoid flicker
|
94
|
+
await sleep(100);
|
95
|
+
$oldImg?.remove();
|
96
|
+
});
|
97
|
+
|
73
98
|
return this;
|
74
99
|
}
|
75
100
|
}
|
101
|
+
|
102
|
+
|
103
|
+
/**
|
104
|
+
* @param {PageModel} page
|
105
|
+
* @param {string} className
|
106
|
+
*/
|
107
|
+
export function createSVGPageLayer(page, className) {
|
108
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
109
|
+
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
110
|
+
svg.setAttribute("viewBox", `0 0 ${page.width} ${page.height}`);
|
111
|
+
svg.setAttribute('class', `BRPageLayer ${className}`);
|
112
|
+
svg.setAttribute('preserveAspectRatio', 'none');
|
113
|
+
return svg;
|
114
|
+
}
|
115
|
+
|
116
|
+
/**
|
117
|
+
* @param {PageModel} page
|
118
|
+
* @param {string} className
|
119
|
+
*/
|
120
|
+
export function createDIVPageLayer(page, className) {
|
121
|
+
const div = document.createElement("div");
|
122
|
+
div.style.width = `${page.width}px`;
|
123
|
+
div.style.height = `${page.height}px`;
|
124
|
+
div.setAttribute('class', `BRPageLayer ${className}`);
|
125
|
+
return div;
|
126
|
+
}
|
127
|
+
|
128
|
+
/**
|
129
|
+
* @param {{ l: number, r: number, b: number, t: number }} box
|
130
|
+
*/
|
131
|
+
export function boxToSVGRect({ l: left, r: right, b: bottom, t: top }) {
|
132
|
+
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
133
|
+
rect.setAttribute("x", left.toString());
|
134
|
+
rect.setAttribute("y", top.toString());
|
135
|
+
rect.setAttribute("width", (right - left).toString());
|
136
|
+
rect.setAttribute("height", (bottom - top).toString());
|
137
|
+
|
138
|
+
// Some style; corner radius 4px. Can't set this in CSS yet
|
139
|
+
rect.setAttribute("rx", "4");
|
140
|
+
rect.setAttribute("ry", "4");
|
141
|
+
return rect;
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* @param {string} layerClass
|
146
|
+
* @param {Array<{ l: number, r: number, b: number, t: number }>} boxes
|
147
|
+
* @param {PageModel} page
|
148
|
+
* @param {HTMLElement} containerEl
|
149
|
+
* @param {string[]} [rectClasses] CSS classes to add to the rects
|
150
|
+
*/
|
151
|
+
export function renderBoxesInPageContainerLayer(layerClass, boxes, page, containerEl, rectClasses = null) {
|
152
|
+
const mountedSvg = containerEl.querySelector(`.${layerClass}`);
|
153
|
+
// Create the layer if it's not there
|
154
|
+
const svg = mountedSvg || createSVGPageLayer(page, layerClass);
|
155
|
+
if (!mountedSvg) {
|
156
|
+
// Insert after the image if the image is already loaded.
|
157
|
+
const imgEl = containerEl.querySelector('.BRpageimage');
|
158
|
+
if (imgEl) $(svg).insertAfter(imgEl);
|
159
|
+
else $(svg).prependTo(containerEl);
|
160
|
+
}
|
161
|
+
|
162
|
+
for (const [i, box] of boxes.entries()) {
|
163
|
+
const rect = boxToSVGRect(box);
|
164
|
+
if (rectClasses) {
|
165
|
+
rect.setAttribute('class', rectClasses[i]);
|
166
|
+
}
|
167
|
+
svg.appendChild(rect);
|
168
|
+
}
|
169
|
+
}
|
@@ -7,7 +7,7 @@
|
|
7
7
|
/** @type {ReduceSet} */
|
8
8
|
export const IntegerReduceSet = {
|
9
9
|
floor: Math.floor,
|
10
|
-
decr(n) { return n - 1; }
|
10
|
+
decr(n) { return n - 1; },
|
11
11
|
};
|
12
12
|
|
13
13
|
/** @type {ReduceSet} */
|
@@ -17,8 +17,8 @@ export const Pow2ReduceSet = {
|
|
17
17
|
},
|
18
18
|
decr(n) {
|
19
19
|
return 2 ** (Math.log2(n) - 1);
|
20
|
-
}
|
21
|
-
}
|
20
|
+
},
|
21
|
+
};
|
22
22
|
|
23
23
|
export const NAMED_REDUCE_SETS = {
|
24
24
|
pow2: Pow2ReduceSet,
|
@@ -53,8 +53,8 @@ export class Toolbar {
|
|
53
53
|
$('<a>')
|
54
54
|
.attr({href: br.bookUrl, title: br.bookUrlTitle})
|
55
55
|
.addClass('BRreturn')
|
56
|
-
.html(br.bookUrlText || br.bookTitle)
|
57
|
-
)
|
56
|
+
.html(br.bookUrlText || br.bookTitle),
|
57
|
+
);
|
58
58
|
} else if (br.bookTitle) {
|
59
59
|
$titleSectionEl.append(br.bookUrlText || br.bookTitle);
|
60
60
|
}
|
@@ -75,8 +75,6 @@ export class Toolbar {
|
|
75
75
|
br.$('.BRnavCntl').addClass('BRup');
|
76
76
|
br.$('.pause').hide();
|
77
77
|
|
78
|
-
this.updateToolbarZoom(br.reduce); // Pretty format
|
79
|
-
|
80
78
|
// We build in mode 2
|
81
79
|
br.refs.$BRtoolbar.append();
|
82
80
|
|
@@ -115,7 +113,7 @@ export class Toolbar {
|
|
115
113
|
onLoad: () => {
|
116
114
|
br.trigger(EVENTS.stop);
|
117
115
|
br.$('.BRpageviewValue').val(window.location.href);
|
118
|
-
}
|
116
|
+
},
|
119
117
|
});
|
120
118
|
br.$('.info').colorbox({
|
121
119
|
inline: true,
|
@@ -123,35 +121,10 @@ export class Toolbar {
|
|
123
121
|
href: br.$('.BRinfo'),
|
124
122
|
onLoad: () => {
|
125
123
|
br.trigger(EVENTS.stop);
|
126
|
-
}
|
124
|
+
},
|
127
125
|
});
|
128
126
|
}
|
129
127
|
|
130
|
-
/**
|
131
|
-
* @deprecated
|
132
|
-
* @todo .BRzoom doesn't exist anywhere, so this is likely dead code
|
133
|
-
* Update the displayed zoom factor based on reduction factor
|
134
|
-
* @param {number} reduce
|
135
|
-
*/
|
136
|
-
updateToolbarZoom(reduce) {
|
137
|
-
const { br } = this;
|
138
|
-
// $$$ TODO preserve zoom/fit for each mode
|
139
|
-
const autofit = br.mode == br.constMode2up ? br.twoPage.autofit : br.onePage.autofit;
|
140
|
-
/** @type {string} */
|
141
|
-
let value;
|
142
|
-
if (autofit) {
|
143
|
-
value = autofit.slice(0,1).toUpperCase() + autofit.slice(1);
|
144
|
-
} else {
|
145
|
-
value = (100 / reduce)
|
146
|
-
.toFixed(2)
|
147
|
-
// Strip trailing zeroes and decimal if all zeroes
|
148
|
-
.replace(/0+$/,'')
|
149
|
-
.replace(/\.$/,'')
|
150
|
-
+ '%';
|
151
|
-
}
|
152
|
-
br.$('.BRzoom').text(value);
|
153
|
-
}
|
154
|
-
|
155
128
|
/**
|
156
129
|
* @param {JQuery} $shareDiv
|
157
130
|
*/
|
@@ -212,11 +185,11 @@ export class Toolbar {
|
|
212
185
|
$form.appendTo($shareDiv);
|
213
186
|
|
214
187
|
$form.find('.fieldset-embed input').on('change', event => {
|
215
|
-
const form = $(event.target).parents('form
|
188
|
+
const form = $(event.target).parents('form').first();
|
216
189
|
const params = {};
|
217
190
|
params.mode = $(form.find('.fieldset-embed input[name=pages]:checked')).val();
|
218
191
|
if (form.find('.fieldset-embed input[name=thispage]').prop('checked')) {
|
219
|
-
params.page = br.getPageNum(br.currentIndex());
|
192
|
+
params.page = br.book.getPageNum(br.currentIndex());
|
220
193
|
}
|
221
194
|
|
222
195
|
if (br.getEmbedCode) {
|
@@ -232,20 +205,20 @@ export class Toolbar {
|
|
232
205
|
// Bind share buttons
|
233
206
|
|
234
207
|
// Use url without hashes
|
235
|
-
$form.find('.facebook-share-button').click(
|
208
|
+
$form.find('.facebook-share-button').on("click", () => {
|
236
209
|
const params = $.param({ u: this._getSocialShareUrl() });
|
237
210
|
const url = 'https://www.facebook.com/sharer.php?' + params;
|
238
211
|
createPopup(url, 600, 400, 'Share');
|
239
212
|
});
|
240
|
-
$form.find('.twitter-share-button').click(
|
213
|
+
$form.find('.twitter-share-button').on("click", () => {
|
241
214
|
const params = $.param({
|
242
215
|
url: this._getSocialShareUrl(),
|
243
|
-
text: br.bookTitle
|
216
|
+
text: br.bookTitle,
|
244
217
|
});
|
245
218
|
const url = 'https://twitter.com/intent/tweet?' + params;
|
246
219
|
createPopup(url, 600, 400, 'Share');
|
247
220
|
});
|
248
|
-
$form.find('.email-share-button').click(
|
221
|
+
$form.find('.email-share-button').on("click", () => {
|
249
222
|
const body = `${br.bookTitle}\n\n${this._getSocialShareUrl()}`;
|
250
223
|
window.location.href = `mailto:?subject=${encodeURI(br.bookTitle)}&body=${encodeURI(body)}`;
|
251
224
|
});
|
@@ -331,11 +304,11 @@ export class Toolbar {
|
|
331
304
|
}
|
332
305
|
}
|
333
306
|
|
334
|
-
|
307
|
+
function blankInfoDiv() {
|
335
308
|
return $(`
|
336
309
|
<div class="BRfloat BRinfo">
|
337
310
|
<div class="BRfloatHead">About this book
|
338
|
-
<button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></button>
|
311
|
+
<button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="br-colorbox-shift">Close</span></button>
|
339
312
|
</div>
|
340
313
|
<div class="BRfloatBody">
|
341
314
|
<div class="BRfloatCover"></div>
|
@@ -351,12 +324,12 @@ export function blankInfoDiv() {
|
|
351
324
|
</div>`);
|
352
325
|
}
|
353
326
|
|
354
|
-
|
327
|
+
function blankShareDiv() {
|
355
328
|
return $(`
|
356
329
|
<div class="BRfloat BRshare">
|
357
330
|
<div class="BRfloatHead">
|
358
331
|
Share
|
359
|
-
<button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></button>
|
332
|
+
<button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="br-colorbox-shift">Close</span></button>
|
360
333
|
</div>
|
361
334
|
</div>`);
|
362
335
|
}
|
package/src/BookReader/events.js
CHANGED
@@ -3,11 +3,11 @@ export const EVENTS = {
|
|
3
3
|
/** Indicates that the fragment (a serialization of the reader
|
4
4
|
* state) has changed. */
|
5
5
|
fragmentChange: 'fragmentChange',
|
6
|
+
pageChanged: 'pageChanged',
|
6
7
|
PostInit: 'PostInit',
|
7
8
|
stop: 'stop',
|
8
9
|
resize: 'resize',
|
9
|
-
//
|
10
|
-
navToggled: 'navToggled',
|
10
|
+
userAction: 'userAction', // event to know if user is actively reading
|
11
11
|
// menu click events
|
12
12
|
fullscreenToggled: 'fullscreenToggled',
|
13
13
|
zoomOut: 'zoomOut',
|
@@ -16,5 +16,4 @@ export const EVENTS = {
|
|
16
16
|
'2PageViewSelected': '2PageViewSelected',
|
17
17
|
/* currently 3 represents thumbnail view */
|
18
18
|
'3PageViewSelected': '3PageViewSelected',
|
19
|
-
mobileNavOpen: 'mobileNavOpen',
|
20
19
|
};
|
@@ -1,3 +1,4 @@
|
|
1
|
+
// @ts-check
|
1
2
|
/** @typedef {import('./BookModel.js').PageNumString} PageNumString */
|
2
3
|
/** @typedef {import('./BookModel.js').LeafNum} LeafNum */
|
3
4
|
|
@@ -25,9 +26,13 @@ export const DEFAULT_OPTIONS = {
|
|
25
26
|
thumbMaxLoading: 4,
|
26
27
|
/** spacing between thumbnails */
|
27
28
|
thumbPadding: 10,
|
29
|
+
/** min zoom in columns */
|
30
|
+
thumbMinZoomColumns: 2,
|
31
|
+
/** max zoom out columns */
|
32
|
+
thumbMaxZoomColumns: 8,
|
28
33
|
|
29
34
|
/** @type {number | 'fast' | 'slow'} speed for flip animation */
|
30
|
-
flipSpeed:
|
35
|
+
flipSpeed: 400,
|
31
36
|
|
32
37
|
showToolbar: true,
|
33
38
|
showNavbar: true,
|
@@ -61,7 +66,7 @@ export const DEFAULT_OPTIONS = {
|
|
61
66
|
{reduce: 2, autofit: null},
|
62
67
|
{reduce: 3, autofit: null},
|
63
68
|
{reduce: 4, autofit: null},
|
64
|
-
{reduce: 6, autofit: null}
|
69
|
+
{reduce: 6, autofit: null},
|
65
70
|
],
|
66
71
|
|
67
72
|
/** Object to hold parameters related to 1up mode */
|
@@ -79,7 +84,7 @@ export const DEFAULT_OPTIONS = {
|
|
79
84
|
/** Width of book spine $$$ consider sizing based on book length */
|
80
85
|
bookSpineDivWidth: 64,
|
81
86
|
/** @type {AutoFitValues} */
|
82
|
-
autofit: 'auto'
|
87
|
+
autofit: 'auto',
|
83
88
|
},
|
84
89
|
|
85
90
|
onePageMinBreakpoint: 800,
|
@@ -136,8 +141,18 @@ export const DEFAULT_OPTIONS = {
|
|
136
141
|
* but going forward we'll keep them here.
|
137
142
|
**/
|
138
143
|
plugins: {
|
139
|
-
/** @type {import('../plugins/plugin.
|
144
|
+
/** @type {import('../plugins/plugin.archive_analytics.js').ArchiveAnalyticsPlugin['options']}*/
|
145
|
+
archiveAnalytics: null,
|
146
|
+
/** @type {import('../plugins/plugin.autoplay.js').AutoplayPlugin['options']}*/
|
147
|
+
autoplay: null,
|
148
|
+
/** @type {import('../plugins/plugin.iiif.js').IiifPlugin['options']} */
|
149
|
+
iiif: null,
|
150
|
+
/** @type {import('../plugins/plugin.resume.js').ResumePlugin['options']} */
|
151
|
+
resume: null,
|
152
|
+
/** @type {import('../plugins/plugin.text_selection.js').TextSelectionPlugin['options']} */
|
140
153
|
textSelection: null,
|
154
|
+
/** @type {import('../plugins/tts/plugin.tts.js').TtsPlugin['options']} */
|
155
|
+
tts: null,
|
141
156
|
},
|
142
157
|
|
143
158
|
/**
|
@@ -176,16 +191,32 @@ export const DEFAULT_OPTIONS = {
|
|
176
191
|
*/
|
177
192
|
data: [],
|
178
193
|
|
179
|
-
/**
|
194
|
+
/** @type {import('../plugins/plugin.chapters.js').TocEntry[]} */
|
195
|
+
table_of_contents: null,
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Advanced methods for page rendering.
|
199
|
+
* All option functions have their `this` object set to the BookReader instance.
|
200
|
+
**/
|
201
|
+
|
180
202
|
/** @type {() => number} */
|
181
203
|
getNumLeafs: null,
|
182
204
|
/** @type {(index: number) => number} */
|
183
205
|
getPageWidth: null,
|
184
206
|
/** @type {(index: number) => number} */
|
185
207
|
getPageHeight: null,
|
186
|
-
/** @type {(index: number, reduce: number, rotate: number) =>
|
208
|
+
/** @type {(index: number, reduce: number, rotate: number) => string} */
|
187
209
|
getPageURI: null,
|
188
210
|
|
211
|
+
/**
|
212
|
+
* @type {(img: HTMLImageElement, uri: string) => Promise<void>}
|
213
|
+
* Render the page URI into the image element. Perform any necessary preloading,
|
214
|
+
* authentication, etc.
|
215
|
+
*/
|
216
|
+
renderPageURI(img, uri) {
|
217
|
+
img.src = uri;
|
218
|
+
},
|
219
|
+
|
189
220
|
/**
|
190
221
|
* @type {(index: number) => 'L' | 'R'}
|
191
222
|
* Return which side, left or right, that a given page should be displayed on
|
@@ -218,31 +249,31 @@ export const DEFAULT_OPTIONS = {
|
|
218
249
|
visible: true,
|
219
250
|
label: 'Flip left',
|
220
251
|
className: 'book_left',
|
221
|
-
iconClassName: 'left-arrow'
|
252
|
+
iconClassName: 'left-arrow',
|
222
253
|
},
|
223
254
|
bookRight: {
|
224
255
|
visible: true,
|
225
256
|
label: 'Flip right',
|
226
257
|
className: 'book_right',
|
227
|
-
iconClassName: 'left-arrow hflip'
|
258
|
+
iconClassName: 'left-arrow hflip',
|
228
259
|
},
|
229
260
|
onePage: {
|
230
261
|
visible: true,
|
231
262
|
label: 'One-page view',
|
232
263
|
className: 'onepg',
|
233
|
-
iconClassName: 'onepg'
|
264
|
+
iconClassName: 'onepg',
|
234
265
|
},
|
235
266
|
twoPage: {
|
236
267
|
visible: true,
|
237
268
|
label: 'Two-page view',
|
238
269
|
className: 'twopg',
|
239
|
-
iconClassName: 'twopg'
|
270
|
+
iconClassName: 'twopg',
|
240
271
|
},
|
241
272
|
thumbnail: {
|
242
273
|
visible: true,
|
243
274
|
label: 'Thumbnail view',
|
244
275
|
className: 'thumb',
|
245
|
-
iconClassName: 'thumb'
|
276
|
+
iconClassName: 'thumb',
|
246
277
|
},
|
247
278
|
viewmode: {
|
248
279
|
visible: true,
|
@@ -253,19 +284,19 @@ export const DEFAULT_OPTIONS = {
|
|
253
284
|
visible: true,
|
254
285
|
label: 'Zoom out',
|
255
286
|
className: 'zoom_out',
|
256
|
-
iconClassName: 'magnify'
|
287
|
+
iconClassName: 'magnify',
|
257
288
|
},
|
258
289
|
zoomIn: {
|
259
290
|
visible: true,
|
260
291
|
label: 'Zoom in',
|
261
292
|
className: 'zoom_in',
|
262
|
-
iconClassName: 'magnify plus'
|
293
|
+
iconClassName: 'magnify plus',
|
263
294
|
},
|
264
295
|
fullScreen: {
|
265
296
|
visible: true,
|
266
297
|
label: 'Toggle fullscreen',
|
267
298
|
className: 'full',
|
268
|
-
iconClassName: 'fullscreen'
|
299
|
+
iconClassName: 'fullscreen',
|
269
300
|
},
|
270
301
|
},
|
271
302
|
|
@@ -275,6 +306,12 @@ export const DEFAULT_OPTIONS = {
|
|
275
306
|
*/
|
276
307
|
startFullscreen: false,
|
277
308
|
|
309
|
+
/**
|
310
|
+
* @type {Boolean}
|
311
|
+
* will show logo at fullscreen mode
|
312
|
+
*/
|
313
|
+
enableFSLogoShortcut: false,
|
314
|
+
|
278
315
|
/**
|
279
316
|
* @type {Boolean}
|
280
317
|
* On init, by default, we want to handle resizing bookreader
|
@@ -288,9 +325,24 @@ export const DEFAULT_OPTIONS = {
|
|
288
325
|
* On init, by default, we want to use srcSet for images
|
289
326
|
*/
|
290
327
|
useSrcSet: false,
|
328
|
+
|
329
|
+
/**
|
330
|
+
* @type {string}
|
331
|
+
* Path to the image to display when a page is unviewable (i.e. when
|
332
|
+
* displaying a preview of a book).
|
333
|
+
*
|
334
|
+
* Relative to the imagesBaseURL if a relative path is specified.
|
335
|
+
*/
|
336
|
+
unviewablePageURI: './unviewable_page.png',
|
291
337
|
};
|
292
338
|
|
293
|
-
/**
|
339
|
+
/**
|
340
|
+
* @typedef {'width' | 'height' | 'auto' | 'none'} AutoFitValues
|
341
|
+
* - width: fill the width of the container
|
342
|
+
* - height: fill the height of the container
|
343
|
+
* - auto: fill the width or height of the container, whichever is smaller
|
344
|
+
* - none: do not autofit
|
345
|
+
**/
|
294
346
|
|
295
347
|
/**
|
296
348
|
* @typedef {object} ReductionFactor
|
@@ -318,3 +370,9 @@ export const DEFAULT_OPTIONS = {
|
|
318
370
|
|
319
371
|
/** @typedef {typeof DEFAULT_OPTIONS} BookReaderOptions */
|
320
372
|
|
373
|
+
/**
|
374
|
+
* Thrown when an error occurs while parsing options.
|
375
|
+
* Potentially recoverable and non-halting.
|
376
|
+
*/
|
377
|
+
export class OptionsParseError extends Error {
|
378
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
// @ts-check
|
2
|
+
import { debounce } from '../utils';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Computing these things repeatedly is expensive (the browser needs to
|
6
|
+
* do a lot of computations/redrawing to make sure these are correct),
|
7
|
+
* so we store them here, and only recompute them when necessary:
|
8
|
+
* - window resize could have cause the container to change size
|
9
|
+
* - zoom could have cause scrollbars to appear/disappear, changing
|
10
|
+
* the client size.
|
11
|
+
*/
|
12
|
+
export class HTMLDimensionsCacher {
|
13
|
+
clientWidth = 100;
|
14
|
+
clientHeight = 100;
|
15
|
+
|
16
|
+
boundingClientRect = { top: 0, left: 0 };
|
17
|
+
|
18
|
+
/**
|
19
|
+
* @param {HTMLElement} element
|
20
|
+
*/
|
21
|
+
constructor(element) {
|
22
|
+
/** @type {HTMLElement} */
|
23
|
+
this.element = element;
|
24
|
+
}
|
25
|
+
|
26
|
+
updateClientSizes = () => {
|
27
|
+
const bc = this.element.getBoundingClientRect();
|
28
|
+
this.clientWidth = this.element.clientWidth;
|
29
|
+
this.clientHeight = this.element.clientHeight;
|
30
|
+
this.boundingClientRect.top = bc.top;
|
31
|
+
this.boundingClientRect.left = bc.left;
|
32
|
+
}
|
33
|
+
debouncedUpdateClientSizes = debounce(this.updateClientSizes, 150, false);
|
34
|
+
|
35
|
+
/** @param {EventTarget} win */
|
36
|
+
attachResizeListener(win = window) {
|
37
|
+
win.addEventListener('resize', this.debouncedUpdateClientSizes);
|
38
|
+
}
|
39
|
+
|
40
|
+
/** @param {EventTarget} win */
|
41
|
+
detachResizeListener(win = window) {
|
42
|
+
win.removeEventListener('resize', this.debouncedUpdateClientSizes);
|
43
|
+
}
|
44
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
/** Adds a class while the given element is experiencing scrolling */
|
2
|
+
export class ScrollClassAdder {
|
3
|
+
/**
|
4
|
+
* @param {HTMLElement} element
|
5
|
+
* @param {string} className
|
6
|
+
*/
|
7
|
+
constructor(element, className) {
|
8
|
+
/** @type {HTMLElement} */
|
9
|
+
this.element = element;
|
10
|
+
/** @type {string} */
|
11
|
+
this.className = className;
|
12
|
+
this.timeout = null;
|
13
|
+
}
|
14
|
+
|
15
|
+
attach() {
|
16
|
+
this.element.addEventListener('scroll', this.onScroll);
|
17
|
+
}
|
18
|
+
|
19
|
+
detach() {
|
20
|
+
this.element.removeEventListener('scroll', this.onScroll);
|
21
|
+
}
|
22
|
+
|
23
|
+
onScroll = () => {
|
24
|
+
this.element.classList.add(this.className);
|
25
|
+
clearTimeout(this.timeout);
|
26
|
+
// TODO: Also remove class on mousemove, touch, click, etc.
|
27
|
+
this.timeout = setTimeout(() => {
|
28
|
+
this.element.classList.remove(this.className);
|
29
|
+
}, 600);
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
// @ts-check
|
2
|
+
export class SelectionObserver {
|
3
|
+
selecting = false;
|
4
|
+
startedInSelector = false;
|
5
|
+
/** @type {HTMLElement} */
|
6
|
+
target = null;
|
7
|
+
|
8
|
+
/**
|
9
|
+
* @param {string} selector
|
10
|
+
* @param {function('started' | 'cleared', HTMLElement): any} handler
|
11
|
+
*/
|
12
|
+
constructor(selector, handler) {
|
13
|
+
this.selector = selector;
|
14
|
+
this.handler = handler;
|
15
|
+
}
|
16
|
+
|
17
|
+
attach() {
|
18
|
+
// We can't just use selectstart, because safari on iOS just
|
19
|
+
// randomly decides when to fire it 😤
|
20
|
+
// document.addEventListener("selectstart", this._onSelectStart);
|
21
|
+
// This has to be on document :/
|
22
|
+
document.addEventListener("selectionchange", this._onSelectionChange);
|
23
|
+
}
|
24
|
+
|
25
|
+
detach() {
|
26
|
+
document.removeEventListener("selectionchange", this._onSelectionChange);
|
27
|
+
}
|
28
|
+
|
29
|
+
_onSelectionChange = () => {
|
30
|
+
const sel = window.getSelection();
|
31
|
+
|
32
|
+
if (!this.selecting && sel.toString()) {
|
33
|
+
const target = $(sel.anchorNode).closest(this.selector)[0];
|
34
|
+
if (!target) return;
|
35
|
+
this.target = target;
|
36
|
+
this.selecting = true;
|
37
|
+
this.handler('started', this.target);
|
38
|
+
}
|
39
|
+
|
40
|
+
if (this.selecting && (sel.isCollapsed || !sel.toString() || !$(sel.anchorNode).closest(this.selector)[0])) {
|
41
|
+
this.selecting = false;
|
42
|
+
this.handler('cleared', this.target);
|
43
|
+
}
|
44
|
+
};
|
45
|
+
}
|