@internetarchive/bookreader 5.0.0-26 → 5.0.0-29
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/.husky/_/husky.sh +30 -0
- package/BookReader/BookReader.css +1 -1
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/bookreader-component-bundle.js +570 -542
- package/BookReader/bookreader-component-bundle.js.LICENSE.txt +23 -0
- package/BookReader/bookreader-component-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.search.js +1 -1
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +1 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/BookReaderDemo/BookReaderDemo.css +14 -1
- package/BookReaderDemo/IADemoBr.js +104 -0
- package/BookReaderDemo/demo-internetarchive.html +65 -98
- package/CHANGELOG.md +10 -0
- package/package.json +9 -6
- package/src/BookNavigator/assets/ia-logo.js +17 -0
- package/src/BookNavigator/book-navigator.js +521 -0
- package/src/BookNavigator/bookmarks/bookmark-button.js +2 -1
- package/src/BookNavigator/bookmarks/bookmarks-provider.js +20 -8
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +84 -51
- package/src/BookNavigator/downloads/downloads-provider.js +5 -9
- package/src/BookNavigator/downloads/downloads.js +1 -0
- package/src/BookNavigator/search/search-provider.js +15 -8
- package/src/BookNavigator/sharing.js +27 -0
- package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +9 -8
- package/src/BookNavigator/volumes/volumes-provider.js +44 -13
- package/src/BookNavigator/volumes/volumes.js +14 -3
- package/src/BookReader/options.js +6 -0
- package/src/BookReader.js +20 -8
- package/src/BookReaderComponent/BookReaderComponent.js +53 -32
- package/src/css/_BRComponent.scss +1 -1
- package/src/plugins/search/plugin.search.js +10 -9
- package/src/plugins/tts/FestivalTTSEngine.js +1 -1
- package/src/plugins/url/UrlPlugin.js +184 -0
- package/src/plugins/url/plugin.url.js +220 -0
- package/{src → stat}/BookNavigator/BookModel.js +0 -0
- package/{src → stat}/BookNavigator/BookNavigator.js +109 -102
- package/stat/BookNavigator/assets/bookmark-colors.js +15 -0
- package/stat/BookNavigator/assets/button-base.js +61 -0
- package/stat/BookNavigator/assets/ia-logo.js +17 -0
- package/stat/BookNavigator/assets/icon_checkmark.js +6 -0
- package/stat/BookNavigator/assets/icon_close.js +3 -0
- package/stat/BookNavigator/assets/icon_sort_asc.js +5 -0
- package/stat/BookNavigator/assets/icon_sort_desc.js +5 -0
- package/stat/BookNavigator/assets/icon_sort_neutral.js +5 -0
- package/stat/BookNavigator/assets/icon_volumes.js +11 -0
- package/stat/BookNavigator/bookmarks/bookmark-button.js +64 -0
- package/stat/BookNavigator/bookmarks/bookmark-edit.js +215 -0
- package/stat/BookNavigator/bookmarks/bookmarks-list.js +285 -0
- package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
- package/stat/BookNavigator/bookmarks/bookmarks-provider.js +56 -0
- package/stat/BookNavigator/bookmarks/ia-bookmarks.js +523 -0
- package/{src → stat}/BookNavigator/br-fullscreen-mgr.js +1 -2
- package/stat/BookNavigator/delete-modal-actions.js +49 -0
- package/stat/BookNavigator/downloads/downloads-provider.js +72 -0
- package/stat/BookNavigator/downloads/downloads.js +139 -0
- package/stat/BookNavigator/provider-config.js +0 -0
- package/stat/BookNavigator/search/a-search-result.js +55 -0
- package/stat/BookNavigator/search/search-provider.js +180 -0
- package/stat/BookNavigator/search/search-results.js +360 -0
- package/{src/ItemNavigator/providers → stat/BookNavigator}/sharing.js +3 -5
- package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +94 -0
- package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
- package/stat/BookNavigator/volumes/volumes-provider.js +83 -0
- package/stat/BookNavigator/volumes/volumes.js +178 -0
- package/stat/BookReader/BookModel.js +518 -0
- package/stat/BookReader/DebugConsole.js +54 -0
- package/stat/BookReader/DragScrollable.js +233 -0
- package/stat/BookReader/ImageCache.js +116 -0
- package/stat/BookReader/Mode1Up.js +102 -0
- package/stat/BookReader/Mode1UpLit.js +434 -0
- package/stat/BookReader/Mode2Up.js +1372 -0
- package/stat/BookReader/ModeSmoothZoom.js +177 -0
- package/stat/BookReader/ModeThumb.js +344 -0
- package/stat/BookReader/Navbar/Navbar.js +310 -0
- package/stat/BookReader/PageContainer.js +120 -0
- package/stat/BookReader/ReduceSet.js +26 -0
- package/stat/BookReader/Toolbar/Toolbar.js +384 -0
- package/stat/BookReader/events.js +20 -0
- package/stat/BookReader/options.js +324 -0
- package/stat/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/stat/BookReader/utils/classes.js +36 -0
- package/stat/BookReader/utils.js +240 -0
- package/stat/BookReader.js +2550 -0
- package/stat/BookReaderComponent/BookReaderComponent.js +117 -0
- package/stat/assets/icons/1up.svg +12 -0
- package/stat/assets/icons/2up.svg +15 -0
- package/stat/assets/icons/advance.svg +26 -0
- package/stat/assets/icons/chevron-right.svg +1 -0
- package/stat/assets/icons/close-circle-dark.svg +1 -0
- package/stat/assets/icons/close-circle.svg +1 -0
- package/stat/assets/icons/fullscreen.svg +17 -0
- package/stat/assets/icons/fullscreen_exit.svg +17 -0
- package/stat/assets/icons/hamburger.svg +15 -0
- package/stat/assets/icons/left-arrow.svg +12 -0
- package/stat/assets/icons/magnify-minus.svg +16 -0
- package/stat/assets/icons/magnify-plus.svg +17 -0
- package/stat/assets/icons/magnify.svg +15 -0
- package/stat/assets/icons/pause.svg +23 -0
- package/stat/assets/icons/play.svg +22 -0
- package/stat/assets/icons/playback-speed.svg +34 -0
- package/stat/assets/icons/read-aloud.svg +22 -0
- package/stat/assets/icons/review.svg +22 -0
- package/stat/assets/icons/thumbnails.svg +17 -0
- package/stat/assets/icons/voice.svg +1 -0
- package/stat/assets/icons/volume-full.svg +22 -0
- package/stat/assets/images/BRicons.png +0 -0
- package/stat/assets/images/BRicons.svg +94 -0
- package/stat/assets/images/BRicons_ia.png +0 -0
- package/stat/assets/images/back_pages.png +0 -0
- package/stat/assets/images/book_bottom_icon.png +0 -0
- package/stat/assets/images/book_down_icon.png +0 -0
- package/stat/assets/images/book_left_icon.png +0 -0
- package/stat/assets/images/book_leftmost_icon.png +0 -0
- package/stat/assets/images/book_right_icon.png +0 -0
- package/stat/assets/images/book_rightmost_icon.png +0 -0
- package/stat/assets/images/book_top_icon.png +0 -0
- package/stat/assets/images/book_up_icon.png +0 -0
- package/stat/assets/images/books_graphic.svg +177 -0
- package/stat/assets/images/booksplit.png +0 -0
- package/stat/assets/images/control_pause_icon.png +0 -0
- package/stat/assets/images/control_play_icon.png +0 -0
- package/stat/assets/images/embed_icon.png +0 -0
- package/stat/assets/images/icon-home-ia.png +0 -0
- package/stat/assets/images/icon_OL-logo-xs.png +0 -0
- package/stat/assets/images/icon_alert-xs.png +0 -0
- package/stat/assets/images/icon_book.svg +12 -0
- package/stat/assets/images/icon_bookmark.svg +12 -0
- package/stat/assets/images/icon_close-pop.png +0 -0
- package/stat/assets/images/icon_download.png +0 -0
- package/stat/assets/images/icon_gear.svg +14 -0
- package/stat/assets/images/icon_hamburger.svg +20 -0
- package/stat/assets/images/icon_home.png +0 -0
- package/stat/assets/images/icon_home.svg +21 -0
- package/stat/assets/images/icon_home_ia.png +0 -0
- package/stat/assets/images/icon_indicator.png +0 -0
- package/stat/assets/images/icon_info.svg +11 -0
- package/stat/assets/images/icon_one_page.svg +8 -0
- package/stat/assets/images/icon_pause.svg +1 -0
- package/stat/assets/images/icon_play.svg +1 -0
- package/stat/assets/images/icon_playback-rate.svg +15 -0
- package/stat/assets/images/icon_return.png +0 -0
- package/stat/assets/images/icon_search_button.svg +8 -0
- package/stat/assets/images/icon_share.svg +9 -0
- package/stat/assets/images/icon_skip-ahead.svg +6 -0
- package/stat/assets/images/icon_skip-back.svg +13 -0
- package/stat/assets/images/icon_speaker.svg +18 -0
- package/stat/assets/images/icon_speaker_open.svg +10 -0
- package/stat/assets/images/icon_thumbnails.svg +12 -0
- package/stat/assets/images/icon_toc.svg +5 -0
- package/stat/assets/images/icon_two_pages.svg +9 -0
- package/stat/assets/images/icon_zoomer.png +0 -0
- package/stat/assets/images/loading.gif +0 -0
- package/stat/assets/images/logo_icon.png +0 -0
- package/stat/assets/images/marker_chap-off.png +0 -0
- package/stat/assets/images/marker_chap-off.svg +11 -0
- package/stat/assets/images/marker_chap-off_ia.png +0 -0
- package/stat/assets/images/marker_chap-on.png +0 -0
- package/stat/assets/images/marker_chap-on.svg +11 -0
- package/stat/assets/images/marker_srch-on.svg +11 -0
- package/stat/assets/images/marker_srchchap-off.png +0 -0
- package/stat/assets/images/marker_srchchap-on.png +0 -0
- package/stat/assets/images/nav_control-dn.png +0 -0
- package/stat/assets/images/nav_control-dn_ia.png +0 -0
- package/stat/assets/images/nav_control-up.png +0 -0
- package/stat/assets/images/nav_control-up_ia.png +0 -0
- package/stat/assets/images/nav_control.png +0 -0
- package/stat/assets/images/one_page_mode_icon.png +0 -0
- package/stat/assets/images/paper-badge.png +0 -0
- package/stat/assets/images/print_icon.png +0 -0
- package/stat/assets/images/progressbar.gif +0 -0
- package/stat/assets/images/right_edges.png +0 -0
- package/stat/assets/images/slider.png +0 -0
- package/stat/assets/images/slider_ia.png +0 -0
- package/stat/assets/images/thumbnail_mode_icon.png +0 -0
- package/stat/assets/images/transparent.png +0 -0
- package/stat/assets/images/two_page_mode_icon.png +0 -0
- package/stat/assets/images/zoom_in_icon.png +0 -0
- package/stat/assets/images/zoom_out_icon.png +0 -0
- package/stat/css/BookReader.scss +89 -0
- package/stat/css/_BRBookmarks.scss +29 -0
- package/stat/css/_BRComponent.scss +13 -0
- package/stat/css/_BRfloat.scss +197 -0
- package/stat/css/_BRicon.scss +48 -0
- package/stat/css/_BRmain.scss +251 -0
- package/stat/css/_BRnav.scss +359 -0
- package/stat/css/_BRpages.scss +139 -0
- package/stat/css/_BRsearch.scss +226 -0
- package/stat/css/_BRtoolbar.scss +84 -0
- package/stat/css/_BRvendor.scss +5 -0
- package/stat/css/_MobileNav.scss +194 -0
- package/stat/css/_TextSelection.scss +32 -0
- package/stat/css/_colorbox.scss +52 -0
- package/stat/css/_controls.scss +253 -0
- package/stat/css/_icons.scss +121 -0
- package/stat/jquery-wrapper.js +4 -0
- package/stat/plugins/plugin.archive_analytics.js +86 -0
- package/stat/plugins/plugin.autoplay.js +129 -0
- package/stat/plugins/plugin.chapters.js +248 -0
- package/stat/plugins/plugin.iframe.js +48 -0
- package/stat/plugins/plugin.mobile_nav.js +288 -0
- package/stat/plugins/plugin.resume.js +68 -0
- package/stat/plugins/plugin.text_selection.js +291 -0
- package/{src → stat}/plugins/plugin.url.js +0 -0
- package/stat/plugins/plugin.vendor-fullscreen.js +247 -0
- package/stat/plugins/search/plugin.search.js +439 -0
- package/stat/plugins/search/view.js +439 -0
- package/stat/plugins/tts/AbstractTTSEngine.js +249 -0
- package/stat/plugins/tts/FestivalTTSEngine.js +169 -0
- package/stat/plugins/tts/PageChunk.js +107 -0
- package/stat/plugins/tts/PageChunkIterator.js +163 -0
- package/stat/plugins/tts/WebTTSEngine.js +357 -0
- package/stat/plugins/tts/plugin.tts.js +357 -0
- package/stat/plugins/tts/tooltip_dict.js +15 -0
- package/stat/plugins/tts/utils.js +91 -0
- package/stat/util/browserSniffing.js +30 -0
- package/stat/util/debouncer.js +26 -0
- package/stat/util/docCookies.js +67 -0
- package/stat/util/strings.js +34 -0
- package/tests/e2e/viewmode.test.js +30 -30
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +64 -52
- package/tests/jest/BookReader.test.js +1 -1
- package/tests/jest/plugins/url/UrlPlugin.test.js +175 -0
- package/tests/jest/plugins/{plugin.url.test.js → url/plugin.url.test.js} +3 -2
- package/tests/karma/BookNavigator/book-navigator.test.js +413 -108
- package/tests/karma/BookNavigator/bookmarks/bookmark-button.test.js +44 -0
- package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +6 -3
- package/tests/karma/BookNavigator/search/search-provider.test.js +106 -6
- package/tests/karma/BookNavigator/search/search-results.test.js +0 -2
- package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +29 -20
- package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +46 -22
- package/webpack.config.js +1 -1
- package/src/BookNavigator/assets/book-loader.js +0 -27
- package/src/ItemNavigator/ItemNavigator.js +0 -377
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
import { createSVGPageLayer } from '../BookReader/PageContainer.js';
|
|
3
|
+
import { isFirefox, isSafari } from '../util/browserSniffing.js';
|
|
4
|
+
import { applyVariables } from '../util/strings.js';
|
|
5
|
+
/** @typedef {import('../util/strings.js').StringWithVars} StringWithVars */
|
|
6
|
+
/** @typedef {import('../BookReader/PageContainer.js').PageContainer} PageContainer */
|
|
7
|
+
|
|
8
|
+
const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader);
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_OPTIONS = {
|
|
11
|
+
enabled: true,
|
|
12
|
+
/** @type {StringWithVars} The URL to fetch the entire DJVU xml. Supports options.vars */
|
|
13
|
+
fullDjvuXmlUrl: null,
|
|
14
|
+
/** @type {StringWithVars} The URL to fetch a single page of the DJVU xml. Supports options.vars. Also has {{pageIndex}} */
|
|
15
|
+
singlePageDjvuXmlUrl: null,
|
|
16
|
+
};
|
|
17
|
+
/** @typedef {typeof DEFAULT_OPTIONS} TextSelectionPluginOptions */
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @template T
|
|
21
|
+
*/
|
|
22
|
+
export class Cache {
|
|
23
|
+
constructor(maxSize = 10) {
|
|
24
|
+
this.maxSize = maxSize;
|
|
25
|
+
/** @type {T[]} */
|
|
26
|
+
this.entries = [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {T} entry
|
|
31
|
+
*/
|
|
32
|
+
add(entry) {
|
|
33
|
+
if (this.entries.length >= this.maxSize) {
|
|
34
|
+
this.entries.shift();
|
|
35
|
+
}
|
|
36
|
+
this.entries.push(entry);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class TextSelectionPlugin {
|
|
41
|
+
|
|
42
|
+
constructor(options = DEFAULT_OPTIONS, optionVariables, avoidTspans = isFirefox(), pointerEventsOnParagraph = isSafari()) {
|
|
43
|
+
this.options = options;
|
|
44
|
+
this.optionVariables = optionVariables;
|
|
45
|
+
/**@type {PromiseLike<JQuery<HTMLElement>|undefined>} */
|
|
46
|
+
this.djvuPagesPromise = null;
|
|
47
|
+
// Using text elements instead of tspans for words because Firefox does not allow svg tspan stretch.
|
|
48
|
+
// Tspans are necessary on Chrome because they prevent newline character after every word when copying
|
|
49
|
+
this.svgParagraphElement = "text";
|
|
50
|
+
this.svgWordElement = "tspan";
|
|
51
|
+
this.insertNewlines = avoidTspans;
|
|
52
|
+
// Safari has a bug where `pointer-events` doesn't work on `<tspans>`. So
|
|
53
|
+
// there we will set `pointer-events: all` on the paragraph element. We don't
|
|
54
|
+
// do this everywhere, because it's a worse experience. Thanks Safari :/
|
|
55
|
+
this.pointerEventsOnParagraph = pointerEventsOnParagraph;
|
|
56
|
+
if (avoidTspans) {
|
|
57
|
+
this.svgParagraphElement = "g";
|
|
58
|
+
this.svgWordElement = "text";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** @type {Cache<{index: number, response: any}>} */
|
|
62
|
+
this.pageTextCache = new Cache();
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Sometimes there are too many words on a page, and the browser becomes near
|
|
66
|
+
* unusable. For now don't render text layer for pages with too many words.
|
|
67
|
+
*/
|
|
68
|
+
this.maxWordRendered = 2500;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
init() {
|
|
72
|
+
// Only fetch the full djvu xml if the single page url isn't there
|
|
73
|
+
if (this.options.singlePageDjvuXmlUrl) return;
|
|
74
|
+
this.djvuPagesPromise = $.ajax({
|
|
75
|
+
type: "GET",
|
|
76
|
+
url: applyVariables(this.options.fullDjvuXmlUrl, this.optionVariables),
|
|
77
|
+
dataType: "html",
|
|
78
|
+
error: (e) => undefined
|
|
79
|
+
}).then((res) => {
|
|
80
|
+
try {
|
|
81
|
+
const xmlMap = $.parseXML(res);
|
|
82
|
+
return xmlMap && $(xmlMap).find("OBJECT");
|
|
83
|
+
} catch (e) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {number} index
|
|
91
|
+
* @returns {Promise<HTMLElement|undefined>}
|
|
92
|
+
*/
|
|
93
|
+
async getPageText(index) {
|
|
94
|
+
if (this.options.singlePageDjvuXmlUrl) {
|
|
95
|
+
const cachedEntry = this.pageTextCache.entries.find(x => x.index == index);
|
|
96
|
+
if (cachedEntry) {
|
|
97
|
+
return cachedEntry.response;
|
|
98
|
+
}
|
|
99
|
+
return $.ajax({
|
|
100
|
+
type: "GET",
|
|
101
|
+
url: applyVariables(this.options.singlePageDjvuXmlUrl, this.optionVariables, { pageIndex: index }),
|
|
102
|
+
dataType: "html",
|
|
103
|
+
error: (e) => undefined,
|
|
104
|
+
}).then((res) => {
|
|
105
|
+
try {
|
|
106
|
+
const xmlDoc = $.parseXML(res);
|
|
107
|
+
const result = xmlDoc && $(xmlDoc).find("OBJECT")[0];
|
|
108
|
+
this.pageTextCache.add({ index, response: result });
|
|
109
|
+
return result;
|
|
110
|
+
} catch (e) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
const XMLpagesArr = await this.djvuPagesPromise;
|
|
116
|
+
if (XMLpagesArr) return XMLpagesArr[index];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Intercept copied text to remove any styling applied to it
|
|
122
|
+
* @param {JQuery} $container
|
|
123
|
+
*/
|
|
124
|
+
interceptCopy($container) {
|
|
125
|
+
$container[0].addEventListener('copy', (event) => {
|
|
126
|
+
const selection = document.getSelection();
|
|
127
|
+
event.clipboardData.setData('text/plain', selection.toString());
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Applies mouse events when in default mode
|
|
134
|
+
* @param {SVGElement} svg
|
|
135
|
+
*/
|
|
136
|
+
defaultMode(svg) {
|
|
137
|
+
svg.classList.remove("selectingSVG");
|
|
138
|
+
$(svg).on("mousedown.textSelectPluginHandler", (event) => {
|
|
139
|
+
if (!$(event.target).is(".BRwordElement")) return;
|
|
140
|
+
event.stopPropagation();
|
|
141
|
+
svg.classList.add("selectingSVG");
|
|
142
|
+
$(svg).one("mouseup.textSelectPluginHandler", (event) => {
|
|
143
|
+
if (window.getSelection().toString() != "") {
|
|
144
|
+
event.stopPropagation();
|
|
145
|
+
$(svg).off(".textSelectPluginHandler");
|
|
146
|
+
this.textSelectingMode(svg);
|
|
147
|
+
}
|
|
148
|
+
else svg.classList.remove("selectingSVG");
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Applies mouse events when in textSelecting mode
|
|
155
|
+
* @param {SVGElement} svg
|
|
156
|
+
*/
|
|
157
|
+
textSelectingMode(svg) {
|
|
158
|
+
$(svg).on('mousedown.textSelectPluginHandler', (event) => {
|
|
159
|
+
if (!$(event.target).is(".BRwordElement")) {
|
|
160
|
+
if (window.getSelection().toString() != "") window.getSelection().removeAllRanges();
|
|
161
|
+
}
|
|
162
|
+
event.stopPropagation();
|
|
163
|
+
});
|
|
164
|
+
$(svg).on('mouseup.textSelectPluginHandler', (event) => {
|
|
165
|
+
event.stopPropagation();
|
|
166
|
+
if (window.getSelection().toString() == "") {
|
|
167
|
+
$(svg).off(".textSelectPluginHandler");
|
|
168
|
+
this.defaultMode(svg); }
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Initializes text selection modes if there is an svg on the page
|
|
174
|
+
* @param {JQuery} $container
|
|
175
|
+
*/
|
|
176
|
+
stopPageFlip($container) {
|
|
177
|
+
/** @type {JQuery<SVGElement>} */
|
|
178
|
+
const $svg = $container.find('svg.textSelectionSVG');
|
|
179
|
+
if (!$svg.length) return;
|
|
180
|
+
$svg.each((i, s) => this.defaultMode(s));
|
|
181
|
+
this.interceptCopy($container);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @param {PageContainer} pageContainer
|
|
186
|
+
*/
|
|
187
|
+
async createTextLayer(pageContainer) {
|
|
188
|
+
const pageIndex = pageContainer.page.index;
|
|
189
|
+
const $container = pageContainer.$container;
|
|
190
|
+
const $svgLayers = $container.find('.textSelectionSVG');
|
|
191
|
+
if ($svgLayers.length) return;
|
|
192
|
+
const XMLpage = await this.getPageText(pageIndex);
|
|
193
|
+
if (!XMLpage) return;
|
|
194
|
+
|
|
195
|
+
const totalWords = $(XMLpage).find("WORD").length;
|
|
196
|
+
if (totalWords > this.maxWordRendered) {
|
|
197
|
+
console.log(`Page ${pageIndex} has too many words (${totalWords} > ${this.maxWordRendered}). Not rendering text layer.`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const svg = createSVGPageLayer(pageContainer.page, 'textSelectionSVG');
|
|
202
|
+
$container.append(svg);
|
|
203
|
+
|
|
204
|
+
$(XMLpage).find("PARAGRAPH").each((i, paragraph) => {
|
|
205
|
+
// Adding text element for each paragraph in the page
|
|
206
|
+
const words = $(paragraph).find("WORD");
|
|
207
|
+
if (!words.length) return;
|
|
208
|
+
const paragSvg = document.createElementNS("http://www.w3.org/2000/svg", this.svgParagraphElement);
|
|
209
|
+
paragSvg.setAttribute("class", "BRparagElement");
|
|
210
|
+
if (this.pointerEventsOnParagraph) {
|
|
211
|
+
paragSvg.style.pointerEvents = "all";
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const wordHeightArr = [];
|
|
215
|
+
|
|
216
|
+
for (let i = 0; i < words.length; i++) {
|
|
217
|
+
// Adding tspan for each word in paragraph
|
|
218
|
+
const currWord = words[i];
|
|
219
|
+
// eslint-disable-next-line no-unused-vars
|
|
220
|
+
const [left, bottom, right, top] = $(currWord).attr("coords").split(',').map(parseFloat);
|
|
221
|
+
const wordHeight = bottom - top;
|
|
222
|
+
wordHeightArr.push(wordHeight);
|
|
223
|
+
|
|
224
|
+
const wordTspan = document.createElementNS("http://www.w3.org/2000/svg", this.svgWordElement);
|
|
225
|
+
wordTspan.setAttribute("class", "BRwordElement");
|
|
226
|
+
wordTspan.setAttribute("x", left.toString());
|
|
227
|
+
wordTspan.setAttribute("y", bottom.toString());
|
|
228
|
+
wordTspan.setAttribute("textLength", (right - left).toString());
|
|
229
|
+
wordTspan.setAttribute("lengthAdjust", "spacingAndGlyphs");
|
|
230
|
+
wordTspan.textContent = currWord.textContent;
|
|
231
|
+
paragSvg.appendChild(wordTspan);
|
|
232
|
+
|
|
233
|
+
// Adding spaces after words except at the end of the paragraph
|
|
234
|
+
// TODO: assumes left-to-right text
|
|
235
|
+
if (i < words.length - 1) {
|
|
236
|
+
const nextWord = words[i + 1];
|
|
237
|
+
// eslint-disable-next-line no-unused-vars
|
|
238
|
+
const [leftNext, bottomNext, rightNext, topNext] = $(nextWord).attr("coords").split(',').map(parseFloat);
|
|
239
|
+
const spaceTspan = document.createElementNS("http://www.w3.org/2000/svg", this.svgWordElement);
|
|
240
|
+
spaceTspan.setAttribute("class", "BRwordElement");
|
|
241
|
+
spaceTspan.setAttribute("x", right.toString());
|
|
242
|
+
spaceTspan.setAttribute("y", bottom.toString());
|
|
243
|
+
if ((leftNext - right) > 0) spaceTspan.setAttribute("textLength", (leftNext - right).toString());
|
|
244
|
+
spaceTspan.setAttribute("lengthAdjust", "spacingAndGlyphs");
|
|
245
|
+
spaceTspan.textContent = " ";
|
|
246
|
+
paragSvg.appendChild(spaceTspan);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Adds newline at the end of paragraph on Firefox
|
|
250
|
+
if ((i == words.length - 1 && (this.insertNewlines))) {
|
|
251
|
+
paragSvg.appendChild(document.createTextNode("\n"));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
wordHeightArr.sort();
|
|
256
|
+
const paragWordHeight = wordHeightArr[Math.floor(wordHeightArr.length * 0.85)];
|
|
257
|
+
paragSvg.setAttribute("font-size", paragWordHeight.toString());
|
|
258
|
+
svg.appendChild(paragSvg);
|
|
259
|
+
});
|
|
260
|
+
this.stopPageFlip($container);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export class BookreaderWithTextSelection extends BookReader {
|
|
265
|
+
init() {
|
|
266
|
+
const options = Object.assign({}, DEFAULT_OPTIONS, this.options.plugins.textSelection);
|
|
267
|
+
if (options.enabled) {
|
|
268
|
+
this.textSelectionPlugin = new TextSelectionPlugin(options, this.options.vars);
|
|
269
|
+
// Write this back; this way the plugin is the source of truth, and BR just
|
|
270
|
+
// contains a reference to it.
|
|
271
|
+
this.options.plugins.textSelection = options;
|
|
272
|
+
this.textSelectionPlugin.init();
|
|
273
|
+
}
|
|
274
|
+
super.init();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @param {number} index
|
|
279
|
+
*/
|
|
280
|
+
_createPageContainer(index) {
|
|
281
|
+
const pageContainer = super._createPageContainer(index);
|
|
282
|
+
// Disable if thumb mode; it's too janky
|
|
283
|
+
// .page can be null for "pre-cover" region
|
|
284
|
+
if (this.mode !== this.constModeThumb && pageContainer.page) {
|
|
285
|
+
this.textSelectionPlugin?.createTextLayer(pageContainer);
|
|
286
|
+
}
|
|
287
|
+
return pageContainer;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
window.BookReader = BookreaderWithTextSelection;
|
|
291
|
+
export default BookreaderWithTextSelection;
|
|
File without changes
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/* global BookReader */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Toggles browser's native fullscreen mode if available device is not mobile
|
|
5
|
+
*/
|
|
6
|
+
if (!isMobile()) {
|
|
7
|
+
const EVENT_NAMESPACE = '.bookreader_vendor-fullscreen';
|
|
8
|
+
|
|
9
|
+
jQuery.extend(BookReader.defaultOptions, {
|
|
10
|
+
/** @type {boolean} */
|
|
11
|
+
enableVendorFullscreenPlugin: true
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/** @override */
|
|
15
|
+
BookReader.prototype.setup = (function(super_) {
|
|
16
|
+
return function(options) {
|
|
17
|
+
super_.call(this, options);
|
|
18
|
+
|
|
19
|
+
this.isVendorFullscreenActive = false;
|
|
20
|
+
};
|
|
21
|
+
})(BookReader.prototype.setup);
|
|
22
|
+
|
|
23
|
+
/** @override */
|
|
24
|
+
BookReader.prototype.getInitialMode = (function(super_) {
|
|
25
|
+
return function(params) {
|
|
26
|
+
let nextMode = super_.call(this, params);
|
|
27
|
+
if (this.isVendorFullscreenActive) {
|
|
28
|
+
nextMode = this.constMode1up;
|
|
29
|
+
}
|
|
30
|
+
return nextMode;
|
|
31
|
+
};
|
|
32
|
+
})(BookReader.prototype.getInitialMode);
|
|
33
|
+
|
|
34
|
+
/** @override */
|
|
35
|
+
BookReader.prototype.init = (function(super_) {
|
|
36
|
+
return function() {
|
|
37
|
+
super_.call(this);
|
|
38
|
+
|
|
39
|
+
if (!fullscreenAllowed()) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// In fullscreen mode the colorbox and overlay need to be inside the fullscreen element to display properly.
|
|
43
|
+
bindFullscreenChangeListener(this, (e) => {
|
|
44
|
+
e.data.resize();
|
|
45
|
+
e.data.updateBrClasses();
|
|
46
|
+
const cboxOverlay = $('#cboxOverlay');
|
|
47
|
+
const cbox = $('#colorbox');
|
|
48
|
+
if (isFullscreenActive()) {
|
|
49
|
+
// In full screen mode, the colorbox and overlay need
|
|
50
|
+
// to be children of the fullscreen element to display properly.
|
|
51
|
+
const $fullscreen = $(getFullscreenElement());
|
|
52
|
+
$fullscreen.append(cboxOverlay).append(cbox);
|
|
53
|
+
} else {
|
|
54
|
+
// In non-fullscreen mode, the colorbox and overlay need
|
|
55
|
+
// to be children of the main document body.
|
|
56
|
+
$(document.body).append(cboxOverlay).append(cbox);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
})(BookReader.prototype.init);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Start fullscreen mode
|
|
64
|
+
*/
|
|
65
|
+
BookReader.prototype.enterFullWindow = function() {
|
|
66
|
+
this.refs.$brContainer.css('opacity', 0);
|
|
67
|
+
|
|
68
|
+
const windowWidth = $(window).width();
|
|
69
|
+
if (windowWidth <= this.onePageMinBreakpoint) {
|
|
70
|
+
this.switchMode(this.constMode1up);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.isVendorFullscreenActive = true;
|
|
74
|
+
this.updateBrClasses();
|
|
75
|
+
|
|
76
|
+
this.resize();
|
|
77
|
+
this.jumpToIndex(this.currentIndex());
|
|
78
|
+
|
|
79
|
+
this.refs.$brContainer.animate({ opacity: 1 }, 400, 'linear');
|
|
80
|
+
|
|
81
|
+
$(document).on(`keyup.${EVENT_NAMESPACE}`, e => {
|
|
82
|
+
if (e.keyCode === 27) this.exitFullScreen();
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Exit from fullscreen mode
|
|
88
|
+
*/
|
|
89
|
+
BookReader.prototype.exitFullWindow = function() {
|
|
90
|
+
this.refs.$brContainer.css('opacity', 0);
|
|
91
|
+
|
|
92
|
+
$(document).off('keyup' + EVENT_NAMESPACE);
|
|
93
|
+
|
|
94
|
+
this.isFullscreenActive = false;
|
|
95
|
+
this.updateBrClasses();
|
|
96
|
+
|
|
97
|
+
this.resize();
|
|
98
|
+
this.refs.$brContainer.animate({ opacity: 1 }, 400, 'linear');
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Returns true if fullscreen mode is enabled
|
|
103
|
+
*
|
|
104
|
+
* @returns {boolean}
|
|
105
|
+
*/
|
|
106
|
+
BookReader.prototype.isFullscreen = function() {
|
|
107
|
+
return isFullscreenActive() || this.isVendorFullscreenActive;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Toggle screen
|
|
112
|
+
*/
|
|
113
|
+
BookReader.prototype.toggleFullscreen = function() {
|
|
114
|
+
if (this.isFullscreen()) {
|
|
115
|
+
if (fullscreenAllowed()) {
|
|
116
|
+
exitFullscreen();
|
|
117
|
+
} else {
|
|
118
|
+
this.exitFullWindow();
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
if (fullscreenAllowed()) {
|
|
122
|
+
requestFullscreen(this.refs.$br[0]);
|
|
123
|
+
} else {
|
|
124
|
+
this.enterFullWindow();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/** @deprecated */
|
|
130
|
+
BookReader.util.isMobile = isMobile;
|
|
131
|
+
|
|
132
|
+
/** @deprecated */
|
|
133
|
+
BookReader.util.getFullscreenElement = getFullscreenElement;
|
|
134
|
+
|
|
135
|
+
/** @deprecated */
|
|
136
|
+
BookReader.util.bindFullscreenChangeListener = bindFullscreenChangeListener;
|
|
137
|
+
|
|
138
|
+
/** @deprecated */
|
|
139
|
+
BookReader.util.fullscreenAllowed = fullscreenAllowed;
|
|
140
|
+
|
|
141
|
+
/** @deprecated */
|
|
142
|
+
BookReader.util.requestFullscreen = requestFullscreen;
|
|
143
|
+
|
|
144
|
+
/** @deprecated */
|
|
145
|
+
BookReader.util.exitFullscreen = exitFullscreen;
|
|
146
|
+
|
|
147
|
+
/** @deprecated */
|
|
148
|
+
BookReader.util.isFullscreenActive = isFullscreenActive;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Returns the DOM element being used for fullscreen.
|
|
154
|
+
*
|
|
155
|
+
* @returns {HTMLElement}
|
|
156
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/fullscreenElement
|
|
157
|
+
*/
|
|
158
|
+
export function getFullscreenElement() {
|
|
159
|
+
return document.fullscreenElement ||
|
|
160
|
+
document.webkitFullscreenElement ||
|
|
161
|
+
document.mozFullScreenElement ||
|
|
162
|
+
document.msFullscreenElement;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Returns true if the document is in fullscreen mode.
|
|
167
|
+
*
|
|
168
|
+
* @returns {boolean}
|
|
169
|
+
*/
|
|
170
|
+
export function isFullscreenActive() {
|
|
171
|
+
const fullscreenElement = getFullscreenElement();
|
|
172
|
+
return fullscreenElement !== null && fullscreenElement !== undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Exits fullscreen mode.
|
|
177
|
+
*
|
|
178
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/exitFullscreen
|
|
179
|
+
*/
|
|
180
|
+
export function exitFullscreen() {
|
|
181
|
+
if (document.exitFullscreen) {
|
|
182
|
+
document.exitFullscreen();
|
|
183
|
+
} else if (document.webkitExitFullscreen) {
|
|
184
|
+
document.webkitExitFullscreen();
|
|
185
|
+
} else if (document.mozCancelFullScreen) {
|
|
186
|
+
document.mozCancelFullScreen();
|
|
187
|
+
} else if (document.msExitFullscreen) {
|
|
188
|
+
document.msExitFullscreen();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Requests fullscreen mode for the given element
|
|
194
|
+
*
|
|
195
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen
|
|
196
|
+
*/
|
|
197
|
+
export function requestFullscreen(element) {
|
|
198
|
+
if (element.requestFullscreen) {
|
|
199
|
+
element.requestFullscreen();
|
|
200
|
+
} else if (element.webkitRequestFullscreen) {
|
|
201
|
+
element.webkitRequestFullscreen();
|
|
202
|
+
} else if (element.mozRequestFullScreen) {
|
|
203
|
+
element.mozRequestFullScreen();
|
|
204
|
+
} else if (element.msRequestFullscreen) {
|
|
205
|
+
element.msRequestFullscreen();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Returns true if fullscreen mode is allowed on this device and document.
|
|
211
|
+
*
|
|
212
|
+
* @returns {boolean}
|
|
213
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/fullscreenEnabled
|
|
214
|
+
*/
|
|
215
|
+
export function fullscreenAllowed() {
|
|
216
|
+
return (document.fullscreenEnabled ||
|
|
217
|
+
document.webkitFullscreenEnabled ||
|
|
218
|
+
document.mozFullScreenEnabled ||
|
|
219
|
+
document.msFullScreenEnabled);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* jQuery-style binding to a fullscreenchange event.
|
|
224
|
+
*
|
|
225
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/fullscreenchange_event
|
|
226
|
+
*/
|
|
227
|
+
export function bindFullscreenChangeListener(
|
|
228
|
+
data, fullscreenchangeListener
|
|
229
|
+
) {
|
|
230
|
+
const event = 'fullscreenchange ';
|
|
231
|
+
const vendor_prefixes = [
|
|
232
|
+
'webkit',
|
|
233
|
+
'moz',
|
|
234
|
+
'ms'
|
|
235
|
+
];
|
|
236
|
+
const all_events = (event + vendor_prefixes.join(event) + event).trim();
|
|
237
|
+
$(document).on(all_events, data, fullscreenchangeListener);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Returns true if current device is mobile
|
|
242
|
+
*
|
|
243
|
+
* @returns {boolean}
|
|
244
|
+
*/
|
|
245
|
+
export function isMobile() {
|
|
246
|
+
return (typeof window.orientation !== 'undefined') || (navigator.userAgent.indexOf('IEMobile') !== -1);
|
|
247
|
+
}
|