@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.
Files changed (236) hide show
  1. package/.husky/_/husky.sh +30 -0
  2. package/BookReader/BookReader.css +1 -1
  3. package/BookReader/BookReader.js +1 -1
  4. package/BookReader/BookReader.js.map +1 -1
  5. package/BookReader/bookreader-component-bundle.js +570 -542
  6. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +23 -0
  7. package/BookReader/bookreader-component-bundle.js.map +1 -1
  8. package/BookReader/plugins/plugin.search.js +1 -1
  9. package/BookReader/plugins/plugin.search.js.map +1 -1
  10. package/BookReader/plugins/plugin.tts.js.map +1 -1
  11. package/BookReader/plugins/plugin.url.js +1 -1
  12. package/BookReader/plugins/plugin.url.js.map +1 -1
  13. package/BookReaderDemo/BookReaderDemo.css +14 -1
  14. package/BookReaderDemo/IADemoBr.js +104 -0
  15. package/BookReaderDemo/demo-internetarchive.html +65 -98
  16. package/CHANGELOG.md +10 -0
  17. package/package.json +9 -6
  18. package/src/BookNavigator/assets/ia-logo.js +17 -0
  19. package/src/BookNavigator/book-navigator.js +521 -0
  20. package/src/BookNavigator/bookmarks/bookmark-button.js +2 -1
  21. package/src/BookNavigator/bookmarks/bookmarks-provider.js +20 -8
  22. package/src/BookNavigator/bookmarks/ia-bookmarks.js +84 -51
  23. package/src/BookNavigator/downloads/downloads-provider.js +5 -9
  24. package/src/BookNavigator/downloads/downloads.js +1 -0
  25. package/src/BookNavigator/search/search-provider.js +15 -8
  26. package/src/BookNavigator/sharing.js +27 -0
  27. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +9 -8
  28. package/src/BookNavigator/volumes/volumes-provider.js +44 -13
  29. package/src/BookNavigator/volumes/volumes.js +14 -3
  30. package/src/BookReader/options.js +6 -0
  31. package/src/BookReader.js +20 -8
  32. package/src/BookReaderComponent/BookReaderComponent.js +53 -32
  33. package/src/css/_BRComponent.scss +1 -1
  34. package/src/plugins/search/plugin.search.js +10 -9
  35. package/src/plugins/tts/FestivalTTSEngine.js +1 -1
  36. package/src/plugins/url/UrlPlugin.js +184 -0
  37. package/src/plugins/url/plugin.url.js +220 -0
  38. package/{src → stat}/BookNavigator/BookModel.js +0 -0
  39. package/{src → stat}/BookNavigator/BookNavigator.js +109 -102
  40. package/stat/BookNavigator/assets/bookmark-colors.js +15 -0
  41. package/stat/BookNavigator/assets/button-base.js +61 -0
  42. package/stat/BookNavigator/assets/ia-logo.js +17 -0
  43. package/stat/BookNavigator/assets/icon_checkmark.js +6 -0
  44. package/stat/BookNavigator/assets/icon_close.js +3 -0
  45. package/stat/BookNavigator/assets/icon_sort_asc.js +5 -0
  46. package/stat/BookNavigator/assets/icon_sort_desc.js +5 -0
  47. package/stat/BookNavigator/assets/icon_sort_neutral.js +5 -0
  48. package/stat/BookNavigator/assets/icon_volumes.js +11 -0
  49. package/stat/BookNavigator/bookmarks/bookmark-button.js +64 -0
  50. package/stat/BookNavigator/bookmarks/bookmark-edit.js +215 -0
  51. package/stat/BookNavigator/bookmarks/bookmarks-list.js +285 -0
  52. package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
  53. package/stat/BookNavigator/bookmarks/bookmarks-provider.js +56 -0
  54. package/stat/BookNavigator/bookmarks/ia-bookmarks.js +523 -0
  55. package/{src → stat}/BookNavigator/br-fullscreen-mgr.js +1 -2
  56. package/stat/BookNavigator/delete-modal-actions.js +49 -0
  57. package/stat/BookNavigator/downloads/downloads-provider.js +72 -0
  58. package/stat/BookNavigator/downloads/downloads.js +139 -0
  59. package/stat/BookNavigator/provider-config.js +0 -0
  60. package/stat/BookNavigator/search/a-search-result.js +55 -0
  61. package/stat/BookNavigator/search/search-provider.js +180 -0
  62. package/stat/BookNavigator/search/search-results.js +360 -0
  63. package/{src/ItemNavigator/providers → stat/BookNavigator}/sharing.js +3 -5
  64. package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +94 -0
  65. package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
  66. package/stat/BookNavigator/volumes/volumes-provider.js +83 -0
  67. package/stat/BookNavigator/volumes/volumes.js +178 -0
  68. package/stat/BookReader/BookModel.js +518 -0
  69. package/stat/BookReader/DebugConsole.js +54 -0
  70. package/stat/BookReader/DragScrollable.js +233 -0
  71. package/stat/BookReader/ImageCache.js +116 -0
  72. package/stat/BookReader/Mode1Up.js +102 -0
  73. package/stat/BookReader/Mode1UpLit.js +434 -0
  74. package/stat/BookReader/Mode2Up.js +1372 -0
  75. package/stat/BookReader/ModeSmoothZoom.js +177 -0
  76. package/stat/BookReader/ModeThumb.js +344 -0
  77. package/stat/BookReader/Navbar/Navbar.js +310 -0
  78. package/stat/BookReader/PageContainer.js +120 -0
  79. package/stat/BookReader/ReduceSet.js +26 -0
  80. package/stat/BookReader/Toolbar/Toolbar.js +384 -0
  81. package/stat/BookReader/events.js +20 -0
  82. package/stat/BookReader/options.js +324 -0
  83. package/stat/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  84. package/stat/BookReader/utils/classes.js +36 -0
  85. package/stat/BookReader/utils.js +240 -0
  86. package/stat/BookReader.js +2550 -0
  87. package/stat/BookReaderComponent/BookReaderComponent.js +117 -0
  88. package/stat/assets/icons/1up.svg +12 -0
  89. package/stat/assets/icons/2up.svg +15 -0
  90. package/stat/assets/icons/advance.svg +26 -0
  91. package/stat/assets/icons/chevron-right.svg +1 -0
  92. package/stat/assets/icons/close-circle-dark.svg +1 -0
  93. package/stat/assets/icons/close-circle.svg +1 -0
  94. package/stat/assets/icons/fullscreen.svg +17 -0
  95. package/stat/assets/icons/fullscreen_exit.svg +17 -0
  96. package/stat/assets/icons/hamburger.svg +15 -0
  97. package/stat/assets/icons/left-arrow.svg +12 -0
  98. package/stat/assets/icons/magnify-minus.svg +16 -0
  99. package/stat/assets/icons/magnify-plus.svg +17 -0
  100. package/stat/assets/icons/magnify.svg +15 -0
  101. package/stat/assets/icons/pause.svg +23 -0
  102. package/stat/assets/icons/play.svg +22 -0
  103. package/stat/assets/icons/playback-speed.svg +34 -0
  104. package/stat/assets/icons/read-aloud.svg +22 -0
  105. package/stat/assets/icons/review.svg +22 -0
  106. package/stat/assets/icons/thumbnails.svg +17 -0
  107. package/stat/assets/icons/voice.svg +1 -0
  108. package/stat/assets/icons/volume-full.svg +22 -0
  109. package/stat/assets/images/BRicons.png +0 -0
  110. package/stat/assets/images/BRicons.svg +94 -0
  111. package/stat/assets/images/BRicons_ia.png +0 -0
  112. package/stat/assets/images/back_pages.png +0 -0
  113. package/stat/assets/images/book_bottom_icon.png +0 -0
  114. package/stat/assets/images/book_down_icon.png +0 -0
  115. package/stat/assets/images/book_left_icon.png +0 -0
  116. package/stat/assets/images/book_leftmost_icon.png +0 -0
  117. package/stat/assets/images/book_right_icon.png +0 -0
  118. package/stat/assets/images/book_rightmost_icon.png +0 -0
  119. package/stat/assets/images/book_top_icon.png +0 -0
  120. package/stat/assets/images/book_up_icon.png +0 -0
  121. package/stat/assets/images/books_graphic.svg +177 -0
  122. package/stat/assets/images/booksplit.png +0 -0
  123. package/stat/assets/images/control_pause_icon.png +0 -0
  124. package/stat/assets/images/control_play_icon.png +0 -0
  125. package/stat/assets/images/embed_icon.png +0 -0
  126. package/stat/assets/images/icon-home-ia.png +0 -0
  127. package/stat/assets/images/icon_OL-logo-xs.png +0 -0
  128. package/stat/assets/images/icon_alert-xs.png +0 -0
  129. package/stat/assets/images/icon_book.svg +12 -0
  130. package/stat/assets/images/icon_bookmark.svg +12 -0
  131. package/stat/assets/images/icon_close-pop.png +0 -0
  132. package/stat/assets/images/icon_download.png +0 -0
  133. package/stat/assets/images/icon_gear.svg +14 -0
  134. package/stat/assets/images/icon_hamburger.svg +20 -0
  135. package/stat/assets/images/icon_home.png +0 -0
  136. package/stat/assets/images/icon_home.svg +21 -0
  137. package/stat/assets/images/icon_home_ia.png +0 -0
  138. package/stat/assets/images/icon_indicator.png +0 -0
  139. package/stat/assets/images/icon_info.svg +11 -0
  140. package/stat/assets/images/icon_one_page.svg +8 -0
  141. package/stat/assets/images/icon_pause.svg +1 -0
  142. package/stat/assets/images/icon_play.svg +1 -0
  143. package/stat/assets/images/icon_playback-rate.svg +15 -0
  144. package/stat/assets/images/icon_return.png +0 -0
  145. package/stat/assets/images/icon_search_button.svg +8 -0
  146. package/stat/assets/images/icon_share.svg +9 -0
  147. package/stat/assets/images/icon_skip-ahead.svg +6 -0
  148. package/stat/assets/images/icon_skip-back.svg +13 -0
  149. package/stat/assets/images/icon_speaker.svg +18 -0
  150. package/stat/assets/images/icon_speaker_open.svg +10 -0
  151. package/stat/assets/images/icon_thumbnails.svg +12 -0
  152. package/stat/assets/images/icon_toc.svg +5 -0
  153. package/stat/assets/images/icon_two_pages.svg +9 -0
  154. package/stat/assets/images/icon_zoomer.png +0 -0
  155. package/stat/assets/images/loading.gif +0 -0
  156. package/stat/assets/images/logo_icon.png +0 -0
  157. package/stat/assets/images/marker_chap-off.png +0 -0
  158. package/stat/assets/images/marker_chap-off.svg +11 -0
  159. package/stat/assets/images/marker_chap-off_ia.png +0 -0
  160. package/stat/assets/images/marker_chap-on.png +0 -0
  161. package/stat/assets/images/marker_chap-on.svg +11 -0
  162. package/stat/assets/images/marker_srch-on.svg +11 -0
  163. package/stat/assets/images/marker_srchchap-off.png +0 -0
  164. package/stat/assets/images/marker_srchchap-on.png +0 -0
  165. package/stat/assets/images/nav_control-dn.png +0 -0
  166. package/stat/assets/images/nav_control-dn_ia.png +0 -0
  167. package/stat/assets/images/nav_control-up.png +0 -0
  168. package/stat/assets/images/nav_control-up_ia.png +0 -0
  169. package/stat/assets/images/nav_control.png +0 -0
  170. package/stat/assets/images/one_page_mode_icon.png +0 -0
  171. package/stat/assets/images/paper-badge.png +0 -0
  172. package/stat/assets/images/print_icon.png +0 -0
  173. package/stat/assets/images/progressbar.gif +0 -0
  174. package/stat/assets/images/right_edges.png +0 -0
  175. package/stat/assets/images/slider.png +0 -0
  176. package/stat/assets/images/slider_ia.png +0 -0
  177. package/stat/assets/images/thumbnail_mode_icon.png +0 -0
  178. package/stat/assets/images/transparent.png +0 -0
  179. package/stat/assets/images/two_page_mode_icon.png +0 -0
  180. package/stat/assets/images/zoom_in_icon.png +0 -0
  181. package/stat/assets/images/zoom_out_icon.png +0 -0
  182. package/stat/css/BookReader.scss +89 -0
  183. package/stat/css/_BRBookmarks.scss +29 -0
  184. package/stat/css/_BRComponent.scss +13 -0
  185. package/stat/css/_BRfloat.scss +197 -0
  186. package/stat/css/_BRicon.scss +48 -0
  187. package/stat/css/_BRmain.scss +251 -0
  188. package/stat/css/_BRnav.scss +359 -0
  189. package/stat/css/_BRpages.scss +139 -0
  190. package/stat/css/_BRsearch.scss +226 -0
  191. package/stat/css/_BRtoolbar.scss +84 -0
  192. package/stat/css/_BRvendor.scss +5 -0
  193. package/stat/css/_MobileNav.scss +194 -0
  194. package/stat/css/_TextSelection.scss +32 -0
  195. package/stat/css/_colorbox.scss +52 -0
  196. package/stat/css/_controls.scss +253 -0
  197. package/stat/css/_icons.scss +121 -0
  198. package/stat/jquery-wrapper.js +4 -0
  199. package/stat/plugins/plugin.archive_analytics.js +86 -0
  200. package/stat/plugins/plugin.autoplay.js +129 -0
  201. package/stat/plugins/plugin.chapters.js +248 -0
  202. package/stat/plugins/plugin.iframe.js +48 -0
  203. package/stat/plugins/plugin.mobile_nav.js +288 -0
  204. package/stat/plugins/plugin.resume.js +68 -0
  205. package/stat/plugins/plugin.text_selection.js +291 -0
  206. package/{src → stat}/plugins/plugin.url.js +0 -0
  207. package/stat/plugins/plugin.vendor-fullscreen.js +247 -0
  208. package/stat/plugins/search/plugin.search.js +439 -0
  209. package/stat/plugins/search/view.js +439 -0
  210. package/stat/plugins/tts/AbstractTTSEngine.js +249 -0
  211. package/stat/plugins/tts/FestivalTTSEngine.js +169 -0
  212. package/stat/plugins/tts/PageChunk.js +107 -0
  213. package/stat/plugins/tts/PageChunkIterator.js +163 -0
  214. package/stat/plugins/tts/WebTTSEngine.js +357 -0
  215. package/stat/plugins/tts/plugin.tts.js +357 -0
  216. package/stat/plugins/tts/tooltip_dict.js +15 -0
  217. package/stat/plugins/tts/utils.js +91 -0
  218. package/stat/util/browserSniffing.js +30 -0
  219. package/stat/util/debouncer.js +26 -0
  220. package/stat/util/docCookies.js +67 -0
  221. package/stat/util/strings.js +34 -0
  222. package/tests/e2e/viewmode.test.js +30 -30
  223. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +64 -52
  224. package/tests/jest/BookReader.test.js +1 -1
  225. package/tests/jest/plugins/url/UrlPlugin.test.js +175 -0
  226. package/tests/jest/plugins/{plugin.url.test.js → url/plugin.url.test.js} +3 -2
  227. package/tests/karma/BookNavigator/book-navigator.test.js +413 -108
  228. package/tests/karma/BookNavigator/bookmarks/bookmark-button.test.js +44 -0
  229. package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +6 -3
  230. package/tests/karma/BookNavigator/search/search-provider.test.js +106 -6
  231. package/tests/karma/BookNavigator/search/search-results.test.js +0 -2
  232. package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +29 -20
  233. package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +46 -22
  234. package/webpack.config.js +1 -1
  235. package/src/BookNavigator/assets/book-loader.js +0 -27
  236. 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
+ }