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