@internetarchive/bookreader 5.0.0-54 → 5.0.0-56
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/BookReader/BookReader.css +1 -1
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +188 -92
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.resume.js +1 -1
- package/BookReader/plugins/plugin.resume.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +1 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/CHANGELOG.md +8 -0
- package/package.json +7 -7
- package/src/BookReader/options.js +6 -0
- package/src/BookReader.js +32 -37
- package/src/css/_icons.scss +1 -1
- package/src/plugins/url/UrlPlugin.js +12 -3
- package/src/plugins/url/plugin.url.js +17 -10
- package/src/util/docCookies.js +21 -2
- package/tests/jest/BookReader.test.js +7 -30
- package/tests/jest/plugins/url/plugin.url.test.js +20 -0
- package/tests/jest/util/docCookies.test.js +9 -0
package/src/BookReader.js
CHANGED
|
@@ -31,7 +31,7 @@ import PACKAGE_JSON from '../package.json';
|
|
|
31
31
|
import * as utils from './BookReader/utils.js';
|
|
32
32
|
import { exposeOverrideable } from './BookReader/utils/classes.js';
|
|
33
33
|
import { Navbar } from './BookReader/Navbar/Navbar.js';
|
|
34
|
-
import { DEFAULT_OPTIONS } from './BookReader/options.js';
|
|
34
|
+
import { DEFAULT_OPTIONS, OptionsParseError } from './BookReader/options.js';
|
|
35
35
|
/** @typedef {import('./BookReader/options.js').BookReaderOptions} BookReaderOptions */
|
|
36
36
|
/** @typedef {import('./BookReader/options.js').ReductionFactor} ReductionFactor */
|
|
37
37
|
/** @typedef {import('./BookReader/BookModel.js').PageIndex} PageIndex */
|
|
@@ -452,61 +452,56 @@ BookReader.prototype.readQueryString = function() {
|
|
|
452
452
|
* Determines the initial mode for starting if a mode is not already
|
|
453
453
|
* present in the params argument
|
|
454
454
|
* @param {object} params
|
|
455
|
-
* @return {
|
|
455
|
+
* @return {1 | 2 | 3} the initial mode
|
|
456
456
|
*/
|
|
457
457
|
BookReader.prototype.getInitialMode = function(params) {
|
|
458
|
-
let nextMode;
|
|
459
|
-
|
|
460
458
|
// if mobile breakpoint, we always show this.constMode1up mode
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
// In full mode OR device width, we set the default based on width
|
|
470
|
-
nextMode = this.constMode1up;
|
|
459
|
+
const windowWidth = $(window).width();
|
|
460
|
+
const isMobile = windowWidth && windowWidth <= this.onePageMinBreakpoint;
|
|
461
|
+
|
|
462
|
+
let initialMode;
|
|
463
|
+
if (params.mode) {
|
|
464
|
+
initialMode = params.mode;
|
|
465
|
+
} else if (isMobile) {
|
|
466
|
+
initialMode = this.constMode1up;
|
|
471
467
|
} else {
|
|
472
|
-
|
|
468
|
+
initialMode = this.constMode2up;
|
|
473
469
|
}
|
|
474
470
|
|
|
475
|
-
if (!this.canSwitchToMode(
|
|
476
|
-
|
|
471
|
+
if (!this.canSwitchToMode(initialMode)) {
|
|
472
|
+
initialMode = this.constMode1up;
|
|
477
473
|
}
|
|
478
474
|
|
|
479
475
|
// override defaults mode via `options.defaults` metadata
|
|
480
476
|
if (this.options.defaults) {
|
|
481
|
-
|
|
477
|
+
try {
|
|
478
|
+
initialMode = _modeStringToNumber(this.options.defaults);
|
|
479
|
+
} catch (e) {
|
|
480
|
+
// Can ignore this error
|
|
481
|
+
}
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
-
return
|
|
484
|
+
return initialMode;
|
|
485
485
|
};
|
|
486
486
|
|
|
487
487
|
/**
|
|
488
|
-
*
|
|
489
|
-
* @
|
|
488
|
+
* Converts a mode string to a the mode numeric constant
|
|
489
|
+
* @param {'mode/1up'|'mode/2up'|'mode/thumb'} modeString
|
|
490
|
+
* @return {1 | 2 | 3}
|
|
490
491
|
*/
|
|
491
|
-
|
|
492
|
-
|
|
492
|
+
export function _modeStringToNumber(modeString) {
|
|
493
|
+
const MAPPING = {
|
|
494
|
+
'mode/1up': 1,
|
|
495
|
+
'mode/2up': 2,
|
|
496
|
+
'mode/thumb': 3,
|
|
497
|
+
};
|
|
493
498
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
nextMode = this.constMode1up;
|
|
497
|
-
break;
|
|
498
|
-
case 'mode/2up':
|
|
499
|
-
nextMode = this.constMode2up;
|
|
500
|
-
break;
|
|
501
|
-
case 'mode/thumb':
|
|
502
|
-
nextMode = this.constModeThumb;
|
|
503
|
-
break;
|
|
504
|
-
default:
|
|
505
|
-
break;
|
|
499
|
+
if (!(modeString in MAPPING)) {
|
|
500
|
+
throw new OptionsParseError(`Invalid mode string: ${modeString}`);
|
|
506
501
|
}
|
|
507
502
|
|
|
508
|
-
return
|
|
509
|
-
}
|
|
503
|
+
return MAPPING[modeString];
|
|
504
|
+
}
|
|
510
505
|
|
|
511
506
|
/**
|
|
512
507
|
* This is called by the client to initialize BookReader.
|
package/src/css/_icons.scss
CHANGED
|
@@ -139,11 +139,20 @@ export class UrlPlugin {
|
|
|
139
139
|
const urlStrPath = this.urlStateToUrlString(this.urlState);
|
|
140
140
|
const concatenatedPath = urlStrPath !== '/' ? urlStrPath : '';
|
|
141
141
|
if (this.urlMode == 'history') {
|
|
142
|
-
if (window.history
|
|
142
|
+
if (!window.history || !window.history.replaceState) {
|
|
143
|
+
this.options.urlMode = 'hash';
|
|
144
|
+
} else {
|
|
143
145
|
const newUrlPath = `${this.urlHistoryBasePath}${concatenatedPath}`.trim().replace(/(\/+)/g, '/');
|
|
144
|
-
|
|
146
|
+
try {
|
|
147
|
+
window.history.replaceState({}, null, newUrlPath);
|
|
148
|
+
} catch (e) {
|
|
149
|
+
// DOMException on Chrome when in sandboxed iframe
|
|
150
|
+
this.urlMode = 'hash';
|
|
151
|
+
}
|
|
145
152
|
}
|
|
146
|
-
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (this.urlMode == 'hash') {
|
|
147
156
|
window.location.replace('#' + concatenatedPath);
|
|
148
157
|
}
|
|
149
158
|
this.oldLocationHash = urlStrPath;
|
|
@@ -124,7 +124,7 @@ BookReader.prototype.urlStartLocationPolling = function() {
|
|
|
124
124
|
*/
|
|
125
125
|
BookReader.prototype.urlUpdateFragment = function() {
|
|
126
126
|
const allParams = this.paramsFromCurrent();
|
|
127
|
-
const {
|
|
127
|
+
const { urlTrackIndex0, urlTrackedParams } = this.options;
|
|
128
128
|
|
|
129
129
|
if (!urlTrackIndex0
|
|
130
130
|
&& (typeof(allParams.index) !== 'undefined')
|
|
@@ -140,29 +140,36 @@ BookReader.prototype.urlUpdateFragment = function() {
|
|
|
140
140
|
return validParams;
|
|
141
141
|
}, {});
|
|
142
142
|
|
|
143
|
-
const newFragment = this.fragmentFromParams(params, urlMode);
|
|
143
|
+
const newFragment = this.fragmentFromParams(params, this.options.urlMode);
|
|
144
144
|
const currFragment = this.urlReadFragment();
|
|
145
145
|
const currQueryString = this.getLocationSearch();
|
|
146
|
-
const newQueryString = this.queryStringFromParams(params, currQueryString, urlMode);
|
|
146
|
+
const newQueryString = this.queryStringFromParams(params, currQueryString, this.options.urlMode);
|
|
147
147
|
if (currFragment === newFragment && currQueryString === newQueryString) {
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
if (urlMode === 'history') {
|
|
152
|
-
if (window.history
|
|
151
|
+
if (this.options.urlMode === 'history') {
|
|
152
|
+
if (!window.history || !window.history.replaceState) {
|
|
153
|
+
this.options.urlMode = 'hash';
|
|
154
|
+
} else {
|
|
153
155
|
const baseWithoutSlash = this.options.urlHistoryBasePath.replace(/\/+$/, '');
|
|
154
156
|
const newFragmentWithSlash = newFragment === '' ? '' : `/${newFragment}`;
|
|
155
157
|
|
|
156
158
|
const newUrlPath = `${baseWithoutSlash}${newFragmentWithSlash}${newQueryString}`;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
try {
|
|
160
|
+
window.history.replaceState({}, null, newUrlPath);
|
|
161
|
+
this.oldLocationHash = newFragment + newQueryString;
|
|
162
|
+
} catch (e) {
|
|
163
|
+
// DOMException on Chrome when in sandboxed iframe
|
|
164
|
+
this.options.urlMode = 'hash';
|
|
165
|
+
}
|
|
160
166
|
}
|
|
161
|
-
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (this.options.urlMode === 'hash') {
|
|
162
170
|
const newQueryStringSearch = this.urlParamsFiltersOnlySearch(this.readQueryString());
|
|
163
171
|
window.location.replace('#' + newFragment + newQueryStringSearch);
|
|
164
172
|
this.oldLocationHash = newFragment + newQueryStringSearch;
|
|
165
|
-
|
|
166
173
|
}
|
|
167
174
|
};
|
|
168
175
|
|
package/src/util/docCookies.js
CHANGED
|
@@ -9,6 +9,22 @@
|
|
|
9
9
|
* http://www.gnu.org/licenses/gpl-3.0-standalone.html
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Check to see if the browser has cookies enabled.
|
|
14
|
+
* Accessing document.cookies errors if eg iframe with sandbox enabled.
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
export function areCookiesBlocked(doc = document) {
|
|
18
|
+
try {
|
|
19
|
+
doc.cookie;
|
|
20
|
+
return false;
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const COOKIES_BLOCKED = areCookiesBlocked();
|
|
27
|
+
|
|
12
28
|
/**
|
|
13
29
|
* Get specific key's value stored in cookie
|
|
14
30
|
*
|
|
@@ -17,7 +33,7 @@
|
|
|
17
33
|
* @returns {string|null}
|
|
18
34
|
*/
|
|
19
35
|
export function getItem(sKey) {
|
|
20
|
-
if (!sKey) return null;
|
|
36
|
+
if (COOKIES_BLOCKED || !sKey) return null;
|
|
21
37
|
|
|
22
38
|
return decodeURIComponent(
|
|
23
39
|
// eslint-disable-next-line no-useless-escape
|
|
@@ -34,9 +50,11 @@ export function getItem(sKey) {
|
|
|
34
50
|
* @param {string} [sDomain] domain name
|
|
35
51
|
* @param {boolean} [bSecure]
|
|
36
52
|
*
|
|
37
|
-
* @returns {
|
|
53
|
+
* @returns {boolean}
|
|
38
54
|
*/
|
|
39
55
|
export function setItem(sKey, sValue, vEnd, sPath, sDomain, bSecure) {
|
|
56
|
+
if (COOKIES_BLOCKED) return false;
|
|
57
|
+
|
|
40
58
|
document.cookie = encodeURIComponent(sKey) + '=' + encodeURIComponent(sValue)
|
|
41
59
|
+ (vEnd ? `; expires=${vEnd.toUTCString()}` : '')
|
|
42
60
|
+ (sDomain ? `; domain=${sDomain}` : '')
|
|
@@ -56,6 +74,7 @@ export function setItem(sKey, sValue, vEnd, sPath, sDomain, bSecure) {
|
|
|
56
74
|
* @returns {boolean}
|
|
57
75
|
*/
|
|
58
76
|
export function removeItem(sKey, sPath, sDomain) {
|
|
77
|
+
if (COOKIES_BLOCKED) return false;
|
|
59
78
|
// eslint-disable-next-line
|
|
60
79
|
if (!hasItem(sKey)) return false;
|
|
61
80
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import BookReader from '@/src/BookReader.js';
|
|
2
|
+
import BookReader, {_modeStringToNumber} from '@/src/BookReader.js';
|
|
3
3
|
import '@/src/plugins/plugin.resume.js';
|
|
4
4
|
import '@/src/plugins/url/plugin.url.js';
|
|
5
5
|
|
|
@@ -279,35 +279,12 @@ describe('nextReduce', () => {
|
|
|
279
279
|
expect(nextReduce(2, 'auto', SAMPLE_FACTORS).reduce).toBe(0.5);
|
|
280
280
|
});
|
|
281
281
|
});
|
|
282
|
+
});
|
|
282
283
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
expect(nextModeNumber).toBe(1);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
test('replace current mode with options.default is set mode/2up', () => {
|
|
292
|
-
br.options.defaults = 'mode/2up';
|
|
293
|
-
|
|
294
|
-
const nextModeNumber = br.overridesBookMode();
|
|
295
|
-
expect(nextModeNumber).toBe(2);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test('replace current mode with options.default is set mode/thumb', () => {
|
|
299
|
-
br.options.defaults = 'mode/thumb';
|
|
300
|
-
|
|
301
|
-
const nextModeNumber = br.overridesBookMode();
|
|
302
|
-
expect(nextModeNumber).toBe(3);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test('test if options.default is NOT set', () => {
|
|
306
|
-
br.options.defaults = null;
|
|
307
|
-
|
|
308
|
-
// use mode/2up as default when no options.default metadata found
|
|
309
|
-
const nextModeNumber = br.overridesBookMode();
|
|
310
|
-
expect(nextModeNumber).toBe(2);
|
|
311
|
-
});
|
|
284
|
+
describe('_modeStringToNumber', () => {
|
|
285
|
+
test('Returns correct number', () => {
|
|
286
|
+
expect(_modeStringToNumber('mode/1up')).toBe(1);
|
|
287
|
+
expect(_modeStringToNumber('mode/2up')).toBe(2);
|
|
288
|
+
expect(_modeStringToNumber('mode/thumb')).toBe(3);
|
|
312
289
|
});
|
|
313
290
|
});
|
|
@@ -114,6 +114,26 @@ describe('Plugin: URL controller', () => {
|
|
|
114
114
|
expect(window.history.replaceState).toHaveBeenCalled();
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
test('switches to hashMode if replaceState errors', () => {
|
|
118
|
+
window.history.replaceState = jest.fn(() => {
|
|
119
|
+
throw new Error('foo');
|
|
120
|
+
});
|
|
121
|
+
BookReader.prototype.currentIndex = jest.fn(() => 1);
|
|
122
|
+
BookReader.prototype.urlReadFragment = jest.fn(() => '');
|
|
123
|
+
BookReader.prototype.paramsFromCurrent = jest.fn(() => ({
|
|
124
|
+
index: 1,
|
|
125
|
+
mode: 2,
|
|
126
|
+
view: 'theater'
|
|
127
|
+
}));
|
|
128
|
+
BookReader.prototype.search = jest.fn();
|
|
129
|
+
br.options.urlMode = 'history';
|
|
130
|
+
br.init();
|
|
131
|
+
br.urlUpdateFragment();
|
|
132
|
+
|
|
133
|
+
expect(window.history.replaceState).toHaveBeenCalled();
|
|
134
|
+
expect(br.options.urlMode).toEqual('hash');
|
|
135
|
+
});
|
|
136
|
+
|
|
117
137
|
test('does not update URL when search in query string', () => {
|
|
118
138
|
window.history.replaceState = jest.fn();
|
|
119
139
|
BookReader.prototype.currentIndex = jest.fn(() => 1);
|
|
@@ -13,3 +13,12 @@ describe('Helper function: set and get cookie item', () => {
|
|
|
13
13
|
expect(docCookies.getItem('test-cookie')).toEqual('jack-sparow');
|
|
14
14
|
});
|
|
15
15
|
});
|
|
16
|
+
|
|
17
|
+
describe('isCookiesBlocked', () => {
|
|
18
|
+
test('return true if cookies are blocked', () => {
|
|
19
|
+
expect(docCookies.areCookiesBlocked({ get cookie() { throw new Error(); }})).toBeTruthy();
|
|
20
|
+
});
|
|
21
|
+
test('return false if cookies are not blocked', () => {
|
|
22
|
+
expect(docCookies.areCookiesBlocked({ cookie: 'blah' })).toBeFalsy();
|
|
23
|
+
});
|
|
24
|
+
});
|