@internetarchive/bookreader 5.0.0-93 → 5.0.0-95
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/.github/workflows/npm-publish.yml +2 -12
- package/BookReaderDemo/IADemoBr.js +1 -24
- package/BookReaderDemo/demo-internetarchive.html +1 -0
- package/CHANGELOG.md +19 -1
- package/README.md +0 -2
- package/package.json +8 -4
- package/scripts/postversion.js +3 -2
- package/scripts/preversion.js +3 -1
- package/scripts/version.js +4 -6
- package/src/BookNavigator/book-navigator.js +38 -12
- package/src/BookNavigator/downloads/downloads-provider.js +2 -2
- package/src/BookNavigator/search/search-provider.js +5 -5
- package/src/BookNavigator/search/search-results.js +1 -1
- package/src/BookNavigator/sharing.js +2 -2
- package/src/BookNavigator/viewable-files.js +2 -2
- package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +3 -3
- package/src/BookNavigator/visual-adjustments/visual-adjustments.js +2 -2
- package/src/BookReader.js +57 -31
- package/src/assets/images/hypothesis.ico +0 -0
- package/src/css/_TextSelection.scss +3 -1
- package/src/plugins/plugin.autoplay.js +3 -3
- package/src/plugins/plugin.chapters.js +2 -2
- package/src/plugins/plugin.experiments.js +294 -0
- package/src/plugins/plugin.iiif.js +1 -1
- package/src/plugins/plugin.text_selection.js +112 -1
- package/src/plugins/search/view.js +5 -5
- package/src/plugins/tts/plugin.tts.js +3 -3
- package/src/plugins/url/plugin.url.js +2 -2
- package/tests/e2e/autoplay.test.js +1 -1
- package/tests/e2e/base.test.js +4 -4
- package/tests/e2e/helpers/base.js +2 -2
- package/tests/e2e/models/BookReader.js +1 -1
- package/tests/e2e/rightToLeft.test.js +4 -4
- package/tests/e2e/viewmode.test.js +2 -2
- package/tests/jest/BookNavigator/book-navigator.test.js +0 -13
- package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +1 -1
- package/tests/jest/BookNavigator/downloads/downloads.test.js +1 -1
- package/tests/jest/BookNavigator/search/search-provider.test.js +5 -5
- package/tests/jest/BookReader.test.js +10 -10
- package/tests/jest/plugins/plugin.autoplay.test.js +6 -6
- package/tests/jest/plugins/plugin.chapters.test.js +2 -2
- package/tests/jest/plugins/plugin.resume.test.js +13 -13
- package/tests/jest/plugins/plugin.text_selection.test.js +155 -24
- package/tests/jest/plugins/search/plugin.search.test.js +7 -7
- package/tests/jest/plugins/search/plugin.search.view.test.js +8 -8
- package/tests/jest/plugins/search/utils.js +1 -1
- package/tests/jest/plugins/tts/PageChunkIterator.test.js +2 -2
- package/tests/jest/plugins/url/UrlPlugin.test.js +1 -1
- package/webpack.config.js +8 -3
- package/BookReader/BookReader.css +0 -2250
- package/BookReader/BookReader.js +0 -3
- package/BookReader/BookReader.js.LICENSE.txt +0 -72
- package/BookReader/BookReader.js.map +0 -1
- package/BookReader/ia-bookreader-bundle.js +0 -1782
- package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +0 -7
- package/BookReader/ia-bookreader-bundle.js.map +0 -1
- package/BookReader/icons/1up.svg +0 -1
- package/BookReader/icons/2up.svg +0 -1
- package/BookReader/icons/advance.svg +0 -3
- package/BookReader/icons/chevron-right.svg +0 -1
- package/BookReader/icons/close-circle-dark.svg +0 -1
- package/BookReader/icons/close-circle.svg +0 -1
- package/BookReader/icons/fullscreen.svg +0 -1
- package/BookReader/icons/fullscreen_exit.svg +0 -1
- package/BookReader/icons/hamburger.svg +0 -1
- package/BookReader/icons/left-arrow.svg +0 -1
- package/BookReader/icons/magnify-minus.svg +0 -1
- package/BookReader/icons/magnify-plus.svg +0 -1
- package/BookReader/icons/magnify.svg +0 -1
- package/BookReader/icons/pause.svg +0 -1
- package/BookReader/icons/play.svg +0 -1
- package/BookReader/icons/playback-speed.svg +0 -1
- package/BookReader/icons/read-aloud.svg +0 -1
- package/BookReader/icons/review.svg +0 -3
- package/BookReader/icons/thumbnails.svg +0 -1
- package/BookReader/icons/voice.svg +0 -1
- package/BookReader/icons/volume-full.svg +0 -1
- package/BookReader/images/BRicons.png +0 -0
- package/BookReader/images/BRicons.svg +0 -5
- package/BookReader/images/BRicons_ia.png +0 -0
- package/BookReader/images/back_pages.png +0 -0
- package/BookReader/images/book_bottom_icon.png +0 -0
- package/BookReader/images/book_down_icon.png +0 -0
- package/BookReader/images/book_left_icon.png +0 -0
- package/BookReader/images/book_leftmost_icon.png +0 -0
- package/BookReader/images/book_right_icon.png +0 -0
- package/BookReader/images/book_rightmost_icon.png +0 -0
- package/BookReader/images/book_top_icon.png +0 -0
- package/BookReader/images/book_up_icon.png +0 -0
- package/BookReader/images/books_graphic.svg +0 -1
- package/BookReader/images/booksplit.png +0 -0
- package/BookReader/images/control_pause_icon.png +0 -0
- package/BookReader/images/control_play_icon.png +0 -0
- package/BookReader/images/embed_icon.png +0 -0
- package/BookReader/images/icon-home-ia.png +0 -0
- package/BookReader/images/icon_OL-logo-xs.png +0 -0
- package/BookReader/images/icon_alert-xs.png +0 -0
- package/BookReader/images/icon_book.svg +0 -1
- package/BookReader/images/icon_bookmark.svg +0 -1
- package/BookReader/images/icon_close-pop.png +0 -0
- package/BookReader/images/icon_download.png +0 -0
- package/BookReader/images/icon_gear.svg +0 -1
- package/BookReader/images/icon_hamburger.svg +0 -1
- package/BookReader/images/icon_home.png +0 -0
- package/BookReader/images/icon_home.svg +0 -1
- package/BookReader/images/icon_home_ia.png +0 -0
- package/BookReader/images/icon_indicator.png +0 -0
- package/BookReader/images/icon_info.svg +0 -1
- package/BookReader/images/icon_one_page.svg +0 -1
- package/BookReader/images/icon_pause.svg +0 -1
- package/BookReader/images/icon_play.svg +0 -1
- package/BookReader/images/icon_playback-rate.svg +0 -1
- package/BookReader/images/icon_return.png +0 -0
- package/BookReader/images/icon_search_button.svg +0 -1
- package/BookReader/images/icon_share.svg +0 -1
- package/BookReader/images/icon_skip-ahead.svg +0 -1
- package/BookReader/images/icon_skip-back.svg +0 -2
- package/BookReader/images/icon_speaker.svg +0 -1
- package/BookReader/images/icon_speaker_open.svg +0 -1
- package/BookReader/images/icon_thumbnails.svg +0 -1
- package/BookReader/images/icon_toc.svg +0 -1
- package/BookReader/images/icon_two_pages.svg +0 -1
- package/BookReader/images/icon_zoomer.png +0 -0
- package/BookReader/images/loading.gif +0 -0
- package/BookReader/images/logo_icon.png +0 -0
- package/BookReader/images/marker_chap-off.png +0 -0
- package/BookReader/images/marker_chap-off.svg +0 -1
- package/BookReader/images/marker_chap-off_ia.png +0 -0
- package/BookReader/images/marker_chap-on.png +0 -0
- package/BookReader/images/marker_chap-on.svg +0 -1
- package/BookReader/images/marker_srch-on.svg +0 -1
- package/BookReader/images/marker_srchchap-off.png +0 -0
- package/BookReader/images/marker_srchchap-on.png +0 -0
- package/BookReader/images/nav_control-dn.png +0 -0
- package/BookReader/images/nav_control-dn_ia.png +0 -0
- package/BookReader/images/nav_control-up.png +0 -0
- package/BookReader/images/nav_control-up_ia.png +0 -0
- package/BookReader/images/nav_control.png +0 -0
- package/BookReader/images/one_page_mode_icon.png +0 -0
- package/BookReader/images/paper-badge.png +0 -0
- package/BookReader/images/print_icon.png +0 -0
- package/BookReader/images/progressbar.gif +0 -0
- package/BookReader/images/right_edges.png +0 -0
- package/BookReader/images/slider.png +0 -0
- package/BookReader/images/slider_ia.png +0 -0
- package/BookReader/images/thumbnail_mode_icon.png +0 -0
- package/BookReader/images/transparent.png +0 -0
- package/BookReader/images/two_page_mode_icon.png +0 -0
- package/BookReader/images/unviewable_page.png +0 -0
- package/BookReader/images/zoom_in_icon.png +0 -0
- package/BookReader/images/zoom_out_icon.png +0 -0
- package/BookReader/jquery-3.js +0 -2
- package/BookReader/jquery-3.js.LICENSE.txt +0 -24
- package/BookReader/plugins/plugin.archive_analytics.js +0 -2
- package/BookReader/plugins/plugin.archive_analytics.js.map +0 -1
- package/BookReader/plugins/plugin.autoplay.js +0 -2
- package/BookReader/plugins/plugin.autoplay.js.map +0 -1
- package/BookReader/plugins/plugin.chapters.js +0 -26
- package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +0 -1
- package/BookReader/plugins/plugin.chapters.js.map +0 -1
- package/BookReader/plugins/plugin.iframe.js +0 -2
- package/BookReader/plugins/plugin.iframe.js.map +0 -1
- package/BookReader/plugins/plugin.iiif.js +0 -2
- package/BookReader/plugins/plugin.iiif.js.map +0 -1
- package/BookReader/plugins/plugin.resume.js +0 -2
- package/BookReader/plugins/plugin.resume.js.map +0 -1
- package/BookReader/plugins/plugin.search.js +0 -3
- package/BookReader/plugins/plugin.search.js.LICENSE.txt +0 -1
- package/BookReader/plugins/plugin.search.js.map +0 -1
- package/BookReader/plugins/plugin.text_selection.js +0 -3
- package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +0 -1
- package/BookReader/plugins/plugin.text_selection.js.map +0 -1
- package/BookReader/plugins/plugin.tts.js +0 -3
- package/BookReader/plugins/plugin.tts.js.LICENSE.txt +0 -29
- package/BookReader/plugins/plugin.tts.js.map +0 -1
- package/BookReader/plugins/plugin.url.js +0 -2
- package/BookReader/plugins/plugin.url.js.map +0 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js +0 -2
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +0 -1
- package/BookReader/webcomponents-bundle.js +0 -3
- package/BookReader/webcomponents-bundle.js.LICENSE.txt +0 -9
- package/BookReader/webcomponents-bundle.js.map +0 -1
- package/src/BookReader/BookModel.js +0 -554
- package/src/BookReader/DragScrollable.js +0 -233
- package/src/BookReader/ImageCache.js +0 -149
- package/src/BookReader/Mode1Up.js +0 -108
- package/src/BookReader/Mode1UpLit.js +0 -388
- package/src/BookReader/Mode2Up.js +0 -105
- package/src/BookReader/Mode2UpLit.js +0 -777
- package/src/BookReader/ModeCoordinateSpace.js +0 -29
- package/src/BookReader/ModeSmoothZoom.js +0 -312
- package/src/BookReader/ModeThumb.js +0 -342
- package/src/BookReader/Navbar/Navbar.js +0 -355
- package/src/BookReader/PageContainer.js +0 -169
- package/src/BookReader/ReduceSet.js +0 -26
- package/src/BookReader/Toolbar/Toolbar.js +0 -362
- package/src/BookReader/events.js +0 -19
- package/src/BookReader/options.js +0 -382
- package/src/BookReader/utils/HTMLDimensionsCacher.js +0 -44
- package/src/BookReader/utils/ScrollClassAdder.js +0 -31
- package/src/BookReader/utils/SelectionObserver.js +0 -45
- package/src/BookReader/utils/classes.js +0 -36
- package/src/BookReader/utils.js +0 -300
- package/tests/jest/BookReader/BookModel.test.js +0 -372
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +0 -263
- package/tests/jest/BookReader/ImageCache.test.js +0 -150
- package/tests/jest/BookReader/Mode1UpLit.test.js +0 -73
- package/tests/jest/BookReader/Mode2Up.test.js +0 -98
- package/tests/jest/BookReader/Mode2UpLit.test.js +0 -190
- package/tests/jest/BookReader/ModeCoordinateSpace.test.js +0 -16
- package/tests/jest/BookReader/ModeSmoothZoom.test.js +0 -218
- package/tests/jest/BookReader/ModeThumb.test.js +0 -71
- package/tests/jest/BookReader/Navbar/Navbar.test.js +0 -182
- package/tests/jest/BookReader/PageContainer.test.js +0 -238
- package/tests/jest/BookReader/ReduceSet.test.js +0 -38
- package/tests/jest/BookReader/Toolbar/Toolbar.test.js +0 -26
- package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +0 -59
- package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +0 -49
- package/tests/jest/BookReader/utils/SelectionObserver.test.js +0 -57
- package/tests/jest/BookReader/utils/classes.test.js +0 -88
- package/tests/jest/BookReader/utils.test.js +0 -250
- /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
- /package/{.testcaferc.js → .testcaferc.cjs} +0 -0
- /package/{babel.config.js → babel.config.cjs} +0 -0
package/src/BookReader/utils.js
DELETED
@@ -1,300 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Bind mouse handlers
|
3
|
-
* Disable mouse click to avoid selected/highlighted page images
|
4
|
-
* @param {JQuery} jObject
|
5
|
-
*/
|
6
|
-
export function disableSelect(jObject) {
|
7
|
-
// $$$ check here for right-click and don't disable. Also use jQuery style
|
8
|
-
// for stopping propagation. See https://bugs.edge.launchpad.net/gnubook/+bug/362626
|
9
|
-
jObject.bind('mousedown', () => false);
|
10
|
-
// Special hack for IE7
|
11
|
-
jObject[0].onselectstart = () => false;
|
12
|
-
}
|
13
|
-
|
14
|
-
/**
|
15
|
-
* @param {number} value
|
16
|
-
* @param {number} min
|
17
|
-
* @param {number} max
|
18
|
-
* @return {number}
|
19
|
-
*/
|
20
|
-
export function clamp(value, min, max) {
|
21
|
-
return Math.min(Math.max(value, min), max);
|
22
|
-
}
|
23
|
-
|
24
|
-
/**
|
25
|
-
* Given value and maximum, calculate a percentage suitable for CSS
|
26
|
-
* @param {number} value
|
27
|
-
* @param {number} max
|
28
|
-
* @return {string}
|
29
|
-
*/
|
30
|
-
export function cssPercentage(value, max) {
|
31
|
-
return ((value / max) * 100) + '%';
|
32
|
-
}
|
33
|
-
|
34
|
-
/**
|
35
|
-
* @param {*} value
|
36
|
-
* @param {Array} array
|
37
|
-
* @return {boolean}
|
38
|
-
*/
|
39
|
-
export function notInArray(value, array) {
|
40
|
-
return !array.includes(value);
|
41
|
-
}
|
42
|
-
|
43
|
-
/**
|
44
|
-
* Determines the active element, going into shadow doms.
|
45
|
-
* @return {Element}
|
46
|
-
*/
|
47
|
-
export function getActiveElement(doc = document, recurseShadowDom = true) {
|
48
|
-
const activeElement = doc.activeElement;
|
49
|
-
if (recurseShadowDom && activeElement?.shadowRoot) {
|
50
|
-
return getActiveElement(activeElement.shadowRoot, true);
|
51
|
-
}
|
52
|
-
return activeElement;
|
53
|
-
}
|
54
|
-
|
55
|
-
/** Check if an input field/textarea is active. Also checks shadow DOMs. */
|
56
|
-
export function isInputActive(doc = document) {
|
57
|
-
const activeEl = getActiveElement(doc);
|
58
|
-
return activeEl?.tagName == "INPUT" || activeEl?.tagName == "TEXTAREA";
|
59
|
-
}
|
60
|
-
|
61
|
-
/**
|
62
|
-
* @param {HTMLIFrameElement} iframe
|
63
|
-
* @return {Document}
|
64
|
-
*/
|
65
|
-
export function getIFrameDocument(iframe) {
|
66
|
-
// Adapted from http://xkr.us/articles/dom/iframe-document/
|
67
|
-
const outer = iframe.contentWindow || iframe.contentDocument;
|
68
|
-
return outer.document || outer;
|
69
|
-
}
|
70
|
-
|
71
|
-
/**
|
72
|
-
* @param {string} str
|
73
|
-
* @return {string}
|
74
|
-
*/
|
75
|
-
export function escapeHTML(str) {
|
76
|
-
return str.replace(/&/g,'&')
|
77
|
-
.replace(/>/g,'>')
|
78
|
-
.replace(/</g,'<')
|
79
|
-
.replace(/"/g,'"');
|
80
|
-
}
|
81
|
-
|
82
|
-
/**
|
83
|
-
* Decodes a URI component and converts '+' to ' '
|
84
|
-
* @param {string} value
|
85
|
-
* @return {string}
|
86
|
-
*/
|
87
|
-
export function decodeURIComponentPlus(value) {
|
88
|
-
return decodeURIComponent(value).replace(/\+/g, ' ');
|
89
|
-
}
|
90
|
-
|
91
|
-
/**
|
92
|
-
* Encodes a URI component and converts ' ' to '+'
|
93
|
-
* @param {string|number|boolean} value
|
94
|
-
* @return {string};
|
95
|
-
*/
|
96
|
-
export function encodeURIComponentPlus(value) {
|
97
|
-
return encodeURIComponent(value).replace(/%20/g, '+');
|
98
|
-
}
|
99
|
-
|
100
|
-
/**
|
101
|
-
* @template {Function} T
|
102
|
-
* Returns a function, that, as long as it continues to be invoked, will not
|
103
|
-
* be triggered. The function will be called after it stops being called for
|
104
|
-
* N milliseconds. If `immediate` is passed, trigger the function on the
|
105
|
-
* leading edge, instead of the trailing.
|
106
|
-
* @see https://davidwalsh.name/javascript-debounce-function
|
107
|
-
*
|
108
|
-
* @param {T} func
|
109
|
-
* @param {number} wait
|
110
|
-
* @param {boolean} immediate
|
111
|
-
* @return {T}
|
112
|
-
*/
|
113
|
-
export function debounce(func, wait, immediate) {
|
114
|
-
let timeout;
|
115
|
-
return function() {
|
116
|
-
const context = this;
|
117
|
-
const args = arguments;
|
118
|
-
const later = () => {
|
119
|
-
timeout = null;
|
120
|
-
if (!immediate) func.apply(context, args);
|
121
|
-
};
|
122
|
-
const callNow = immediate && !timeout;
|
123
|
-
clearTimeout(timeout);
|
124
|
-
timeout = setTimeout(later, wait);
|
125
|
-
if (callNow) func.apply(context, args);
|
126
|
-
};
|
127
|
-
}
|
128
|
-
|
129
|
-
/**
|
130
|
-
* @template T
|
131
|
-
* Throttle function
|
132
|
-
* @see https://remysharp.com/2010/07/21/throttling-function-calls
|
133
|
-
* @param {T} fn
|
134
|
-
* @param {number} threshold
|
135
|
-
* @param {boolean} delay
|
136
|
-
* @return {T}
|
137
|
-
*/
|
138
|
-
export function throttle(fn, threshold, delay) {
|
139
|
-
threshold || (threshold = 250);
|
140
|
-
let last;
|
141
|
-
let deferTimer;
|
142
|
-
if (delay) last = +new Date;
|
143
|
-
return function () {
|
144
|
-
const context = this;
|
145
|
-
const now = +new Date;
|
146
|
-
const args = arguments;
|
147
|
-
if (last && now < last + threshold) {
|
148
|
-
// hold on to it
|
149
|
-
clearTimeout(deferTimer);
|
150
|
-
deferTimer = setTimeout(() => {
|
151
|
-
last = now;
|
152
|
-
fn.apply(context, args);
|
153
|
-
}, threshold);
|
154
|
-
} else {
|
155
|
-
last = now;
|
156
|
-
fn.apply(context, args);
|
157
|
-
}
|
158
|
-
};
|
159
|
-
}
|
160
|
-
|
161
|
-
/**
|
162
|
-
* FIXME we need a better way to do this :/ This is not automatically poly-filled by
|
163
|
-
* core-js https://github.com/zloirock/core-js/issues/354
|
164
|
-
* @param {Window} window
|
165
|
-
*/
|
166
|
-
export function polyfillCustomEvent(window) {
|
167
|
-
if (typeof window.CustomEvent === "function") return false;
|
168
|
-
window.CustomEvent = PolyfilledCustomEvent;
|
169
|
-
}
|
170
|
-
|
171
|
-
/**
|
172
|
-
* https://caniuse.com/customevent has issues on older browsers where it can't be
|
173
|
-
* called as a constructor, so we have to use older methods.
|
174
|
-
* @param {String} eventName
|
175
|
-
* @return {CustomEvent}
|
176
|
-
*/
|
177
|
-
export function PolyfilledCustomEvent(eventName, {bubbles = false, cancelable = false, detail = null} = {}) {
|
178
|
-
const event = document.createEvent('CustomEvent');
|
179
|
-
event.initCustomEvent(eventName, bubbles, cancelable, detail);
|
180
|
-
return event;
|
181
|
-
}
|
182
|
-
|
183
|
-
/*
|
184
|
-
* Returns the number pixels something should be rendered at to be ~1n on the users
|
185
|
-
* screen when measured with a ruler.
|
186
|
-
*/
|
187
|
-
export function calcScreenDPI() {
|
188
|
-
const el = document.createElement('div');
|
189
|
-
el.style.width = '1in';
|
190
|
-
document.body.appendChild(el);
|
191
|
-
const dpi = el.offsetWidth;
|
192
|
-
document.body.removeChild(el);
|
193
|
-
|
194
|
-
// Do you believe in magic... numbers? We tested on some devices, and the displayed
|
195
|
-
// size of `width: 1in` was less than desired. On @pezvi's mac, it was ~75% ; on
|
196
|
-
// @cdrini's laptop it was ~85%. Since we want to avoid things appearing too small,
|
197
|
-
// let's just use a multiplier of 1.25
|
198
|
-
const screenDPI = dpi * 1.25;
|
199
|
-
// This will return 0 in testing; never want it to be 0!
|
200
|
-
return screenDPI == 0 ? 100 : screenDPI;
|
201
|
-
}
|
202
|
-
|
203
|
-
/**
|
204
|
-
* @param {number[]} nums
|
205
|
-
* @returns {number}
|
206
|
-
*/
|
207
|
-
export function sum(nums) {
|
208
|
-
return nums.reduce((cur, acc) => cur + acc, 0);
|
209
|
-
}
|
210
|
-
|
211
|
-
/**
|
212
|
-
* @template T
|
213
|
-
* @param {Generator<T>} gen
|
214
|
-
* @returns {T[]}
|
215
|
-
*/
|
216
|
-
export function genToArray(gen) {
|
217
|
-
const result = [];
|
218
|
-
for (const item of gen) {
|
219
|
-
result.push(item);
|
220
|
-
}
|
221
|
-
return result;
|
222
|
-
}
|
223
|
-
|
224
|
-
/**
|
225
|
-
* Check if arrays contain the same elements. Does reference comparison.
|
226
|
-
* @param {Array} arr1
|
227
|
-
* @param {Array} arr2
|
228
|
-
*/
|
229
|
-
export function arrEquals(arr1, arr2) {
|
230
|
-
return arr1.length == arr2.length && arr1.every((x, i) => x == arr2[i]);
|
231
|
-
}
|
232
|
-
|
233
|
-
/**
|
234
|
-
* Check if array has changed; namely to be used with lit's property.hasChanged
|
235
|
-
* @param {Array} [arr1]
|
236
|
-
* @param {Array} [arr2]
|
237
|
-
*/
|
238
|
-
export function arrChanged(arr1, arr2) {
|
239
|
-
return arr1 && arr2 && !arrEquals(arr1, arr2);
|
240
|
-
}
|
241
|
-
|
242
|
-
/**
|
243
|
-
* Waits the provided number of ms and then resolves with a promise
|
244
|
-
* @param {number} ms
|
245
|
-
**/
|
246
|
-
export async function sleep(ms) {
|
247
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
248
|
-
}
|
249
|
-
|
250
|
-
/**
|
251
|
-
* @template T
|
252
|
-
* @param {function(): T} fn
|
253
|
-
* @param {Object} options
|
254
|
-
* @param {function(T): boolean} [options.until]
|
255
|
-
* @return {T | undefined}
|
256
|
-
*/
|
257
|
-
export async function poll(fn, { step = 50, timeout = 500, until = val => Boolean(val), _sleep = sleep } = {}) {
|
258
|
-
const startTime = Date.now();
|
259
|
-
while (Date.now() - startTime < timeout) {
|
260
|
-
const result = fn();
|
261
|
-
if (until(result)) return result;
|
262
|
-
await _sleep(step);
|
263
|
-
}
|
264
|
-
}
|
265
|
-
|
266
|
-
/**
|
267
|
-
* Convert a EventTarget style event into a promise
|
268
|
-
* @param {EventTarget} target
|
269
|
-
* @param {string} eventType
|
270
|
-
* @return {Promise<Event>}
|
271
|
-
*/
|
272
|
-
export function promisifyEvent(target, eventType) {
|
273
|
-
return new Promise(res => {
|
274
|
-
const resolver = ev => {
|
275
|
-
target.removeEventListener(eventType, resolver);
|
276
|
-
res(ev);
|
277
|
-
};
|
278
|
-
target.addEventListener(eventType, resolver);
|
279
|
-
});
|
280
|
-
}
|
281
|
-
|
282
|
-
/**
|
283
|
-
* Escapes regex special characters in a string. Allows for safe usage in regexes.
|
284
|
-
* Src: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions
|
285
|
-
* @param {string} string
|
286
|
-
* @returns {string}
|
287
|
-
*/
|
288
|
-
export function escapeRegExp(string) {
|
289
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
290
|
-
}
|
291
|
-
|
292
|
-
/**
|
293
|
-
* @param {number | 'fast' | 'slow' | string} speed
|
294
|
-
* Parsing of the jquery animation speed; see https://api.jquery.com/animate/
|
295
|
-
*/
|
296
|
-
export function parseAnimationSpeed(speed) {
|
297
|
-
if (speed === 'slow') return 600;
|
298
|
-
if (speed === 'fast') return 200;
|
299
|
-
return parseInt(speed, 10);
|
300
|
-
}
|
@@ -1,372 +0,0 @@
|
|
1
|
-
import sinon from 'sinon';
|
2
|
-
import { deepCopy } from '../utils.js';
|
3
|
-
import { BookModel } from '@/src/BookReader/BookModel.js';
|
4
|
-
import { NAMED_REDUCE_SETS } from '@/src/BookReader/ReduceSet.js';
|
5
|
-
/** @typedef {import('@/src/BookReader/options.js').BookReaderOptions} BookReaderOptions */
|
6
|
-
|
7
|
-
afterEach(() => {
|
8
|
-
sinon.restore();
|
9
|
-
});
|
10
|
-
|
11
|
-
/** @type {BookReaderOptions['data']} */
|
12
|
-
const SAMPLE_DATA = [
|
13
|
-
[
|
14
|
-
{ width: 123, height: 123, uri: 'https://archive.org/image0.jpg', pageNum: '1' },
|
15
|
-
],
|
16
|
-
[
|
17
|
-
{ width: 123, height: 123, uri: 'https://archive.org/image1.jpg', pageNum: '2' },
|
18
|
-
{ width: 123, height: 123, uri: 'https://archive.org/image2.jpg', pageNum: '3' },
|
19
|
-
],
|
20
|
-
[
|
21
|
-
{ width: 123, height: 123, uri: 'https://archive.org/image3.jpg', pageNum: '4' },
|
22
|
-
],
|
23
|
-
];
|
24
|
-
|
25
|
-
describe('getMedianPageSizeInches', () => {
|
26
|
-
test('handles single page data', () => {
|
27
|
-
const bm = new BookModel({ data: SAMPLE_DATA.slice(0, 1), options: {ppi: 10} });
|
28
|
-
expect(bm.getMedianPageSizeInches()).toEqual({ width: 12.3, height: 12.3 });
|
29
|
-
});
|
30
|
-
|
31
|
-
test('handles odd pages data', () => {
|
32
|
-
const sizes = [
|
33
|
-
{width: 300, height: 2200},
|
34
|
-
{width: 200, height: 2100},
|
35
|
-
{width: 100, height: 2300},
|
36
|
-
];
|
37
|
-
const data = deepCopy(SAMPLE_DATA);
|
38
|
-
delete data[2];
|
39
|
-
Object.assign(data[0][0], sizes[0]);
|
40
|
-
Object.assign(data[1][0], sizes[1]);
|
41
|
-
Object.assign(data[1][1], sizes[2]);
|
42
|
-
const bm = new BookModel({ data, options: {ppi: 10} });
|
43
|
-
expect(bm.getMedianPageSizeInches()).toEqual({ width: 20, height: 220 });
|
44
|
-
});
|
45
|
-
|
46
|
-
|
47
|
-
test('handles even pages data', () => {
|
48
|
-
const sizes = [
|
49
|
-
{width: 300, height: 2200},
|
50
|
-
{width: 200, height: 2100},
|
51
|
-
{width: 100, height: 2300},
|
52
|
-
{width: 400, height: 2400},
|
53
|
-
];
|
54
|
-
const data = deepCopy(SAMPLE_DATA);
|
55
|
-
Object.assign(data[0][0], sizes[0]);
|
56
|
-
Object.assign(data[1][0], sizes[1]);
|
57
|
-
Object.assign(data[1][1], sizes[2]);
|
58
|
-
Object.assign(data[2][0], sizes[3]);
|
59
|
-
const bm = new BookModel({ data, options: {ppi: 10} });
|
60
|
-
expect(bm.getMedianPageSizeInches()).toEqual({ width: 30, height: 230 });
|
61
|
-
});
|
62
|
-
|
63
|
-
test('does not lexicographic sort for median', () => {
|
64
|
-
const sizes = [
|
65
|
-
{width: 100, height: 100},
|
66
|
-
{width: 20, height: 20},
|
67
|
-
{width: 30, height: 30},
|
68
|
-
];
|
69
|
-
const data = deepCopy(SAMPLE_DATA);
|
70
|
-
delete data[2];
|
71
|
-
Object.assign(data[0][0], sizes[0]);
|
72
|
-
Object.assign(data[1][0], sizes[1]);
|
73
|
-
Object.assign(data[1][1], sizes[2]);
|
74
|
-
const bm = new BookModel({ data, options: {ppi: 10} });
|
75
|
-
expect(bm.getMedianPageSizeInches()).toEqual({ width: 3, height: 3 });
|
76
|
-
});
|
77
|
-
|
78
|
-
test('caches result', () => {
|
79
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
80
|
-
const firstResult = bm.getMedianPageSizeInches();
|
81
|
-
expect(bm.getMedianPageSizeInches()).toBe(firstResult);
|
82
|
-
expect(bm.getMedianPageSizeInches()).toBe(firstResult);
|
83
|
-
expect(bm.getMedianPageSizeInches()).toBe(firstResult);
|
84
|
-
});
|
85
|
-
});
|
86
|
-
|
87
|
-
describe('getPageIndices', () => {
|
88
|
-
test('handles unique indices', () => {
|
89
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
90
|
-
expect(bm.getPageIndices('1')).toEqual([0]);
|
91
|
-
});
|
92
|
-
|
93
|
-
test('handles invalid indices', () => {
|
94
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
95
|
-
expect(bm.getPageIndices('foo')).toEqual([]);
|
96
|
-
expect(bm.getPageIndices('123')).toEqual([]);
|
97
|
-
});
|
98
|
-
|
99
|
-
test('handles non-unique indices', () => {
|
100
|
-
const data = deepCopy(SAMPLE_DATA);
|
101
|
-
data[0][0].pageNum = '2';
|
102
|
-
const bm = new BookModel({ data });
|
103
|
-
expect(bm.getPageIndices('2')).toEqual([0, 1]);
|
104
|
-
});
|
105
|
-
|
106
|
-
test('handles n-prefixed pages', () => {
|
107
|
-
const data = deepCopy(SAMPLE_DATA);
|
108
|
-
data[0][0].pageNum = 'n0';
|
109
|
-
const bm = new BookModel({ data });
|
110
|
-
expect(bm.getPageIndices('n0')).toEqual([0]);
|
111
|
-
});
|
112
|
-
});
|
113
|
-
|
114
|
-
test('getPageName', () => {
|
115
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
116
|
-
expect(bm.getPageName(0)).toEqual('Page 1');
|
117
|
-
expect(bm.getPageName(1)).toEqual('Page 2');
|
118
|
-
});
|
119
|
-
|
120
|
-
describe('numLeafs', () => {
|
121
|
-
test('reads from data', () => {
|
122
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
123
|
-
expect(bm.getNumLeafs()).toBe(4);
|
124
|
-
});
|
125
|
-
|
126
|
-
test('numLeafs can be overriden', () => {
|
127
|
-
const bm = new BookModel({ data: SAMPLE_DATA, numLeafs: 12 });
|
128
|
-
expect(bm.getNumLeafs()).toBe(12);
|
129
|
-
});
|
130
|
-
});
|
131
|
-
|
132
|
-
test('getPageNum', () => {
|
133
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
134
|
-
expect(bm.getPageNum(0)).toBe('1');
|
135
|
-
});
|
136
|
-
|
137
|
-
test('getPageProp gets values from correct document', () => {
|
138
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
139
|
-
expect(bm.getPageProp(1, 'uri')).toBe(SAMPLE_DATA[1][0].uri);
|
140
|
-
});
|
141
|
-
|
142
|
-
describe('getSpreadIndices', () => {
|
143
|
-
test('Works for left to right text', () => {
|
144
|
-
const bm = new BookModel({ data: SAMPLE_DATA, pageProgression: 'lr' });
|
145
|
-
expect(bm.getSpreadIndices(1)).toEqual([1, 2]);
|
146
|
-
expect(bm.getSpreadIndices(2)).toEqual([1, 2]);
|
147
|
-
});
|
148
|
-
});
|
149
|
-
|
150
|
-
describe('leafNumToIndex', () => {
|
151
|
-
test('returns input if no leafNum in data', () => {
|
152
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
153
|
-
expect(bm.leafNumToIndex(1)).toBe(1);
|
154
|
-
expect(bm.leafNumToIndex(2)).toBe(2);
|
155
|
-
expect(bm.leafNumToIndex(3)).toBe(3);
|
156
|
-
});
|
157
|
-
|
158
|
-
test('returns true index when leafNum does not point to index', () => {
|
159
|
-
const data = deepCopy(SAMPLE_DATA);
|
160
|
-
for (const spread of data) {
|
161
|
-
for (const page of spread) {
|
162
|
-
page.leafNum = parseFloat(page.pageNum) + 2;
|
163
|
-
}
|
164
|
-
}
|
165
|
-
|
166
|
-
const bm = new BookModel({ data });
|
167
|
-
expect(bm.leafNumToIndex(3)).toBe(0);
|
168
|
-
});
|
169
|
-
});
|
170
|
-
|
171
|
-
describe('parsePageString', () => {
|
172
|
-
test('handles leaf-prefixed PageString', () => {
|
173
|
-
const data = deepCopy(SAMPLE_DATA);
|
174
|
-
for (const spread of data) {
|
175
|
-
for (const page of spread) {
|
176
|
-
page.leafNum = parseFloat(page.pageNum) + 2;
|
177
|
-
}
|
178
|
-
}
|
179
|
-
|
180
|
-
const bm = new BookModel({ data });
|
181
|
-
expect(bm.parsePageString('leaf3')).toBe(0);
|
182
|
-
});
|
183
|
-
});
|
184
|
-
|
185
|
-
describe('pagesIterator', () => {
|
186
|
-
test('Goes through all pages', () => {
|
187
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
188
|
-
let i = 0;
|
189
|
-
for (const page of bm.pagesIterator()) {
|
190
|
-
expect(page.index).toBe(i++);
|
191
|
-
}
|
192
|
-
});
|
193
|
-
|
194
|
-
test('Can combine unviewables', () => {
|
195
|
-
const data = deepCopy(SAMPLE_DATA);
|
196
|
-
// add some more pages
|
197
|
-
data.splice(2, 0, deepCopy(data[0]));
|
198
|
-
data[1].forEach(page => page.viewable = false);
|
199
|
-
const bm = new BookModel({ data });
|
200
|
-
for (const page of bm.pagesIterator({combineConsecutiveUnviewables: true})) {
|
201
|
-
expect(page.isConsecutiveUnviewable).toBe(false);
|
202
|
-
}
|
203
|
-
});
|
204
|
-
});
|
205
|
-
|
206
|
-
describe('_getDataFlattened', () => {
|
207
|
-
test('Assigns correct page sides', () => {
|
208
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
209
|
-
expect(bm._getDataFlattened().map(page => page.pageSide)).toEqual(['R', 'L', 'R', 'L']);
|
210
|
-
});
|
211
|
-
|
212
|
-
test('Memoized based on data length', () => {
|
213
|
-
const data = deepCopy(SAMPLE_DATA);
|
214
|
-
const bm = new BookModel({ data });
|
215
|
-
const firstResult = bm._getDataFlattened();
|
216
|
-
expect(bm._getDataFlattened()).toBe(firstResult);
|
217
|
-
expect(bm._getDataFlattened()).toBe(firstResult);
|
218
|
-
bm.br.data = data.slice(0, 1);
|
219
|
-
expect(bm._getDataFlattened()).not.toBe(firstResult);
|
220
|
-
});
|
221
|
-
|
222
|
-
test('Assigns unviewablesStart', () => {
|
223
|
-
const data = deepCopy(SAMPLE_DATA);
|
224
|
-
data.slice(1, -1)
|
225
|
-
.forEach(spread => spread.forEach(page => {
|
226
|
-
page.viewable = false;
|
227
|
-
}));
|
228
|
-
const bm = new BookModel({ data });
|
229
|
-
const pages = bm._getDataFlattened();
|
230
|
-
expect(pages[0].unviewablesStart).toBeUndefined();
|
231
|
-
expect(pages[1].unviewablesStart).toBe(1);
|
232
|
-
expect(pages[2].unviewablesStart).toBe(1);
|
233
|
-
expect(pages[3].unviewablesStart).toBeUndefined();
|
234
|
-
});
|
235
|
-
});
|
236
|
-
|
237
|
-
describe('getPage', () => {
|
238
|
-
test('loops around by default', () => {
|
239
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
240
|
-
expect(bm.getPage(-1).index).toBe(3);
|
241
|
-
expect(bm.getPage(-2).index).toBe(2);
|
242
|
-
expect(bm.getPage(-3).index).toBe(1);
|
243
|
-
expect(bm.getPage(4).index).toBe(0);
|
244
|
-
expect(bm.getPage(5).index).toBe(1);
|
245
|
-
});
|
246
|
-
|
247
|
-
test('does not loop if loop=false', () => {
|
248
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
249
|
-
expect(bm.getPage(-1, false)).toBeUndefined();
|
250
|
-
expect(bm.getPage(-2, false)).toBeUndefined();
|
251
|
-
expect(bm.getPage(4, false)).toBeUndefined();
|
252
|
-
expect(bm.getPage(5, false)).toBeUndefined();
|
253
|
-
});
|
254
|
-
});
|
255
|
-
|
256
|
-
describe('PageModel', () => {
|
257
|
-
test('constructor copies fields from book model', () => {
|
258
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
259
|
-
const spy = sinon.spy(bm, 'getPageWidth');
|
260
|
-
const page = bm.getPage(0);
|
261
|
-
expect(spy.callCount).toBe(1);
|
262
|
-
expect(page.width).toBe(SAMPLE_DATA[0][0].width);
|
263
|
-
});
|
264
|
-
|
265
|
-
test('prev at start of book returns null', () => {
|
266
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
267
|
-
expect(bm.getPage(0).prev).toBeUndefined();
|
268
|
-
});
|
269
|
-
|
270
|
-
test('prev to return previous', () => {
|
271
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
272
|
-
expect(bm.getPage(1).prev.index).toBe(0);
|
273
|
-
expect(bm.getPage(3).prev.prev.index).toBe(1);
|
274
|
-
});
|
275
|
-
|
276
|
-
test('next at end of book returns null', () => {
|
277
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
278
|
-
expect(bm.getPage(-1).next).toBeUndefined();
|
279
|
-
});
|
280
|
-
|
281
|
-
test('next to return next page', () => {
|
282
|
-
const bm = new BookModel({ data: SAMPLE_DATA });
|
283
|
-
expect(bm.getPage(0).next.index).toBe(1);
|
284
|
-
expect(bm.getPage(1).next.next.index).toBe(3);
|
285
|
-
});
|
286
|
-
|
287
|
-
describe('findNext combineConsecutiveUnviewables=true', () => {
|
288
|
-
const data = deepCopy(SAMPLE_DATA);
|
289
|
-
// add some more pages
|
290
|
-
data.splice(2, 0, deepCopy(data[0]));
|
291
|
-
data[1].forEach(page => page.viewable = false);
|
292
|
-
|
293
|
-
const bm = new BookModel({ data });
|
294
|
-
|
295
|
-
test('does not skip the first unviewable page', () => {
|
296
|
-
expect(bm.getPage(0).findNext({ combineConsecutiveUnviewables: true }).index).toBe(1);
|
297
|
-
});
|
298
|
-
|
299
|
-
test('skips consecutive unviewables', () => {
|
300
|
-
expect(bm.getPage(1).findNext({ combineConsecutiveUnviewables: true }).index).toBe(3);
|
301
|
-
});
|
302
|
-
|
303
|
-
test('at end is undefined', () => {
|
304
|
-
expect(bm.getPage(-1).findNext({ combineConsecutiveUnviewables: true })).toBeUndefined();
|
305
|
-
});
|
306
|
-
});
|
307
|
-
|
308
|
-
describe('findPrev', () => {
|
309
|
-
const data = deepCopy(SAMPLE_DATA);
|
310
|
-
// add some more pages
|
311
|
-
data.splice(2, 0, deepCopy(data[0]));
|
312
|
-
data[1].forEach(page => page.viewable = false);
|
313
|
-
|
314
|
-
const bm = new BookModel({ data });
|
315
|
-
|
316
|
-
test('works if called on first unviewable', () => {
|
317
|
-
expect(bm.getPage(1).findPrev({ combineConsecutiveUnviewables: true }).index).toBe(0);
|
318
|
-
});
|
319
|
-
|
320
|
-
test('works if called within unviewable chunk', () => {
|
321
|
-
expect(bm.getPage(2).findPrev({ combineConsecutiveUnviewables: true }).index).toBe(1);
|
322
|
-
});
|
323
|
-
|
324
|
-
test('at start is undefined', () => {
|
325
|
-
expect(bm.getPage(0).findPrev({ combineConsecutiveUnviewables: true })).toBeUndefined();
|
326
|
-
});
|
327
|
-
});
|
328
|
-
|
329
|
-
describe('findLeft/findRight', () => {
|
330
|
-
const data = deepCopy(SAMPLE_DATA);
|
331
|
-
|
332
|
-
test('Calls findNext/findPrev based on progression', () => {
|
333
|
-
const bm = new BookModel({ data });
|
334
|
-
const page = bm.getPage(0);
|
335
|
-
const findNextStub = sinon.stub(page, 'findNext');
|
336
|
-
const findPrevStub = sinon.stub(page, 'findPrev');
|
337
|
-
bm.pageProgression = 'lr';
|
338
|
-
page.findLeft();
|
339
|
-
expect(findPrevStub.callCount).toBe(1);
|
340
|
-
expect(findNextStub.callCount).toBe(0);
|
341
|
-
page.findRight();
|
342
|
-
expect(findPrevStub.callCount).toBe(1);
|
343
|
-
expect(findNextStub.callCount).toBe(1);
|
344
|
-
bm.pageProgression = 'rl';
|
345
|
-
page.findLeft();
|
346
|
-
expect(findPrevStub.callCount).toBe(1);
|
347
|
-
expect(findNextStub.callCount).toBe(2);
|
348
|
-
page.findRight();
|
349
|
-
expect(findPrevStub.callCount).toBe(2);
|
350
|
-
expect(findNextStub.callCount).toBe(2);
|
351
|
-
});
|
352
|
-
});
|
353
|
-
|
354
|
-
describe('getURISrcSet', () => {
|
355
|
-
const data = deepCopy(SAMPLE_DATA);
|
356
|
-
const bm = new BookModel({ data, reduceSet: NAMED_REDUCE_SETS.pow2 });
|
357
|
-
bm.getPageURI = (index, scale, rotate) => `correctURL.png?scale=${scale}`;
|
358
|
-
const page = bm.getPage(0);
|
359
|
-
|
360
|
-
test('with 0 elements in srcset', () => {
|
361
|
-
expect(page.getURISrcSet(1)).toBe("");
|
362
|
-
});
|
363
|
-
|
364
|
-
test('with 2 elements in srcset', () => {
|
365
|
-
expect(page.getURISrcSet(5)).toBe("correctURL.png?scale=2 2x, correctURL.png?scale=1 4x");
|
366
|
-
});
|
367
|
-
|
368
|
-
test('with the most elements in srcset', () => {
|
369
|
-
expect(page.getURISrcSet(35)).toBe("correctURL.png?scale=16 2x, correctURL.png?scale=8 4x, correctURL.png?scale=4 8x, correctURL.png?scale=2 16x, correctURL.png?scale=1 32x");
|
370
|
-
});
|
371
|
-
});
|
372
|
-
});
|