@internetarchive/bookreader 5.0.0-58 → 5.0.0-59

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 (50) hide show
  1. package/BookReader/BookReader.css +110 -39
  2. package/BookReader/BookReader.js +1 -1
  3. package/BookReader/BookReader.js.LICENSE.txt +0 -20
  4. package/BookReader/BookReader.js.map +1 -1
  5. package/BookReader/ia-bookreader-bundle.js +1 -1
  6. package/BookReader/ia-bookreader-bundle.js.map +1 -1
  7. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  8. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  9. package/BookReader/plugins/plugin.autoplay.js +1 -1
  10. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  11. package/BookReader/plugins/plugin.resume.js +1 -1
  12. package/BookReader/plugins/plugin.resume.js.map +1 -1
  13. package/BookReader/plugins/plugin.tts.js +1 -1
  14. package/BookReader/plugins/plugin.tts.js.map +1 -1
  15. package/BookReader/plugins/plugin.url.js +1 -1
  16. package/BookReader/plugins/plugin.url.js.map +1 -1
  17. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  18. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  19. package/BookReaderDemo/IADemoBr.js +1 -2
  20. package/CHANGELOG.md +4 -0
  21. package/package.json +1 -1
  22. package/src/BookReader/BookModel.js +59 -1
  23. package/src/BookReader/Mode1UpLit.js +13 -70
  24. package/src/BookReader/Mode2Up.js +72 -1332
  25. package/src/BookReader/Mode2UpLit.js +774 -0
  26. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  27. package/src/BookReader/ModeSmoothZoom.js +32 -0
  28. package/src/BookReader/options.js +8 -2
  29. package/src/BookReader/utils.js +16 -0
  30. package/src/BookReader.js +24 -217
  31. package/src/css/_BRBookmarks.scss +1 -1
  32. package/src/css/_BRmain.scss +14 -0
  33. package/src/css/_BRpages.scss +113 -41
  34. package/src/plugins/plugin.autoplay.js +1 -6
  35. package/src/plugins/tts/WebTTSEngine.js +2 -2
  36. package/src/plugins/tts/plugin.tts.js +3 -17
  37. package/src/plugins/tts/utils.js +0 -16
  38. package/tests/e2e/helpers/base.js +20 -20
  39. package/tests/e2e/helpers/rightToLeft.js +4 -10
  40. package/tests/e2e/viewmode.test.js +10 -8
  41. package/tests/jest/BookReader/BookModel.test.js +25 -0
  42. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +28 -11
  43. package/tests/jest/BookReader/Mode1UpLit.test.js +0 -19
  44. package/tests/jest/BookReader/Mode2Up.test.js +55 -225
  45. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  46. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  47. package/tests/jest/BookReader/ModeSmoothZoom.test.js +26 -0
  48. package/tests/jest/BookReader/Navbar/Navbar.test.js +3 -3
  49. package/tests/jest/BookReader/utils.test.js +32 -1
  50. package/tests/jest/plugins/tts/utils.test.js +0 -34
@@ -0,0 +1,29 @@
1
+ import { calcScreenDPI } from './utils';
2
+
3
+ /**
4
+ * There are a few different "coordinate spaces" at play in BR:
5
+ * (1) World units: i.e. inches. Unless otherwise stated, all computations
6
+ * are done in world units.
7
+ * (2) Rendered Pixels: i.e. img.width = '300'. Note this does _not_ take
8
+ * into account zoom scaling.
9
+ * (3) Visible Pixels: Just rendered pixels, but taking into account scaling.
10
+ */
11
+ export class ModeCoordinateSpace {
12
+ screenDPI = calcScreenDPI();
13
+
14
+ /**
15
+ * @param {{ scale: number }} mode
16
+ */
17
+ constructor(mode) {
18
+ this.mode = mode;
19
+ }
20
+
21
+ worldUnitsToRenderedPixels = (/** @type {number} */inches) => inches * this.screenDPI;
22
+ renderedPixelsToWorldUnits = (/** @type {number} */px) => px / this.screenDPI;
23
+
24
+ renderedPixelsToVisiblePixels = (/** @type {number} */px) => px * this.mode.scale;
25
+ visiblePixelsToRenderedPixels = (/** @type {number} */px) => px / this.mode.scale;
26
+
27
+ worldUnitsToVisiblePixels = (/** @type {number} */px) => this.renderedPixelsToVisiblePixels(this.worldUnitsToRenderedPixels(px));
28
+ visiblePixelsToWorldUnits = (/** @type {number} */px) => this.renderedPixelsToWorldUnits(this.visiblePixelsToRenderedPixels(px));
29
+ }
@@ -6,6 +6,7 @@ import Hammer from "hammerjs";
6
6
  * @typedef {object} SmoothZoomable
7
7
  * @property {HTMLElement} $container
8
8
  * @property {HTMLElement} $visibleWorld
9
+ * @property {import("./options.js").AutoFitValues} autoFit
9
10
  * @property {number} scale
10
11
  * @property {{ x: number, y: number }} scaleCenter
11
12
  * @property {HTMLDimensionsCacher} htmlDimensionsCacher
@@ -91,6 +92,7 @@ export class ModeSmoothZoom {
91
92
  this.oldScale = 1;
92
93
  this.mode.$visibleWorld.classList.add("BRsmooth-zooming");
93
94
  this.mode.$visibleWorld.style.willChange = "transform";
95
+ this.mode.autoFit = "none";
94
96
  this.detachCtrlZoom();
95
97
  this.mode.detachScrollListeners?.();
96
98
  }
@@ -161,6 +163,7 @@ export class ModeSmoothZoom {
161
163
 
162
164
  // Zoom around the cursor
163
165
  this.updateScaleCenter(ev);
166
+ this.mode.autoFit = "none";
164
167
  this.mode.scale *= 1 - Math.sign(ev.deltaY) * zoomMultiplier;
165
168
  }
166
169
 
@@ -176,4 +179,33 @@ export class ModeSmoothZoom {
176
179
  y: (clientY - bc.top) / this.mode.htmlDimensionsCacher.clientHeight,
177
180
  };
178
181
  }
182
+
183
+ /**
184
+ * @param {number} newScale
185
+ * @param {number} oldScale
186
+ */
187
+ updateViewportOnZoom(newScale, oldScale) {
188
+ const container = this.mode.$container;
189
+ const { scrollTop: T, scrollLeft: L } = container;
190
+ const W = this.mode.htmlDimensionsCacher.clientWidth;
191
+ const H = this.mode.htmlDimensionsCacher.clientHeight;
192
+
193
+ // Scale factor change
194
+ const F = newScale / oldScale;
195
+
196
+ // Where in the viewport the zoom is centered on
197
+ const XPOS = this.mode.scaleCenter.x;
198
+ const YPOS = this.mode.scaleCenter.y;
199
+ const oldCenter = {
200
+ x: L + XPOS * W,
201
+ y: T + YPOS * H,
202
+ };
203
+ const newCenter = {
204
+ x: F * oldCenter.x,
205
+ y: F * oldCenter.y,
206
+ };
207
+
208
+ container.scrollTop = newCenter.y - YPOS * H;
209
+ container.scrollLeft = newCenter.x - XPOS * W;
210
+ }
179
211
  }
@@ -31,7 +31,7 @@ export const DEFAULT_OPTIONS = {
31
31
  thumbMaxZoomColumns: 8,
32
32
 
33
33
  /** @type {number | 'fast' | 'slow'} speed for flip animation */
34
- flipSpeed: 'fast',
34
+ flipSpeed: 400,
35
35
 
36
36
  showToolbar: true,
37
37
  showNavbar: true,
@@ -300,7 +300,13 @@ export const DEFAULT_OPTIONS = {
300
300
  useSrcSet: false,
301
301
  };
302
302
 
303
- /** @typedef {'width' | 'height' | 'auto' | 'none'} AutoFitValues */
303
+ /**
304
+ * @typedef {'width' | 'height' | 'auto' | 'none'} AutoFitValues
305
+ * - width: fill the width of the container
306
+ * - height: fill the height of the container
307
+ * - auto: fill the width or height of the container, whichever is smaller
308
+ * - none: do not autofit
309
+ **/
304
310
 
305
311
  /**
306
312
  * @typedef {object} ReductionFactor
@@ -262,3 +262,19 @@ export async function poll(fn, { step = 50, timeout = 500, until = val => Boolea
262
262
  await _sleep(step);
263
263
  }
264
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
+ }
package/src/BookReader.js CHANGED
@@ -19,9 +19,6 @@ This file is part of BookReader.
19
19
  The BookReader source is hosted at http://github.com/internetarchive/bookreader/
20
20
 
21
21
  */
22
- // effect.js gives acces to extra easing function (e.g. easeInOutExpo)
23
- import 'jquery-ui/ui/effect.js';
24
-
25
22
  // Needed by touch-punch
26
23
  import 'jquery-ui/ui/widget.js';
27
24
  import 'jquery-ui/ui/widgets/mouse.js';
@@ -155,10 +152,11 @@ BookReader.prototype.setup = function(options) {
155
152
  this.displayedIndices = [];
156
153
 
157
154
  this.animating = false;
158
- this.flipSpeed = options.flipSpeed;
155
+ this.flipSpeed = typeof options.flipSpeed === 'number' ? options.flipSpeed : {
156
+ 'fast': 200,
157
+ 'slow': 600,
158
+ }[options.flipSpeed] || 400;
159
159
  this.flipDelay = options.flipDelay;
160
- this.twoPagePopUp = null;
161
- this.leafEdgeTmp = null;
162
160
 
163
161
  /**
164
162
  * Represents the first displayed index
@@ -167,7 +165,6 @@ BookReader.prototype.setup = function(options) {
167
165
  * @property {number|null} firstIndex
168
166
  */
169
167
  this.firstIndex = null;
170
- this.lastDisplayableIndex2up = null;
171
168
  this.isFullscreenActive = options.startFullscreen || false;
172
169
  this.lastScroll = null;
173
170
 
@@ -266,7 +263,7 @@ BookReader.prototype.setup = function(options) {
266
263
  * Includes cached elements which might be rendered again.
267
264
  */
268
265
  BookReader.prototype.getActivePageContainerElements = function() {
269
- let containerEls = Object.values(this._modes.mode2Up.pageContainers).map(pc => pc.$container[0])
266
+ let containerEls = Object.values(this._modes.mode2Up.mode2UpLit.pageContainerCache).map(pc => pc.$container[0])
270
267
  .concat(Object.values(this._modes.mode1Up.mode1UpLit.pageContainerCache).map(pc => pc.$container[0]));
271
268
  if (this.mode == this.constModeThumb) {
272
269
  containerEls = containerEls.concat(this.$('.BRpagecontainer').toArray());
@@ -281,7 +278,7 @@ BookReader.prototype.getActivePageContainerElements = function() {
281
278
  */
282
279
  BookReader.prototype.getActivePageContainerElementsForIndex = function(pageIndex) {
283
280
  return [
284
- this._modes.mode2Up.pageContainers[pageIndex]?.$container?.[0],
281
+ this._modes.mode2Up.mode2UpLit.pageContainerCache[pageIndex]?.$container?.[0],
285
282
  this._modes.mode1Up.mode1UpLit.pageContainerCache[pageIndex]?.$container?.[0],
286
283
  ...(this.mode == this.constModeThumb ? this.$(`.pagediv${pageIndex}`).toArray() : [])
287
284
  ].filter(x => x);
@@ -648,27 +645,7 @@ BookReader.prototype.resize = function() {
648
645
  } else if (this.constModeThumb == this.mode) {
649
646
  this._modes.modeThumb.prepare();
650
647
  } else {
651
- // We only need to prepare again in autofit (size of spread changes)
652
- if (this.twoPage.autofit) {
653
- // most common path, esp. for archive.org books
654
- this._modes.mode2Up.prepare();
655
- } else {
656
- // used when zoomed in
657
- // Re-center if the scrollbars have disappeared
658
- const center = this.twoPageGetViewCenter();
659
- let doRecenter = false;
660
- if (this.twoPage.totalWidth < this.refs.$brContainer.prop('clientWidth')) {
661
- center.percentageX = 0.5;
662
- doRecenter = true;
663
- }
664
- if (this.twoPage.totalHeight < this.refs.$brContainer.prop('clientHeight')) {
665
- center.percentageY = 0.5;
666
- doRecenter = true;
667
- }
668
- if (doRecenter) {
669
- this._modes.mode2Up.centerView(center.percentageX, center.percentageY);
670
- }
671
- }
648
+ this._modes.mode2Up.resizePageView();
672
649
  }
673
650
  this.trigger(BookReader.eventNames.resize);
674
651
  };
@@ -1012,13 +989,7 @@ BookReader.prototype.jumpToIndex = function(index, pageX, pageY, noAnimate) {
1012
989
 
1013
990
  this.trigger(BookReader.eventNames.stop);
1014
991
 
1015
- if (this.constMode2up == this.mode) {
1016
- this._modes.mode2Up.jumpToIndex(index);
1017
- } else if (this.constModeThumb == this.mode) {
1018
- this._modes.modeThumb.jumpToIndex(index);
1019
- } else { // 1up
1020
- this._modes.mode1Up.jumpToIndex(index, pageX, pageY, noAnimate);
1021
- }
992
+ this.activeMode.jumpToIndex(index, pageX, pageY, noAnimate);
1022
993
  };
1023
994
 
1024
995
  /**
@@ -1084,16 +1055,7 @@ BookReader.prototype.switchMode = function(
1084
1055
  this.reduce = this.quantizeReduce(this.reduce, this.reductionFactors);
1085
1056
  this._modes.modeThumb.prepare();
1086
1057
  } else {
1087
- // $$$ why don't we save autofit?
1088
- // this.twoPage.autofit = null; // Take zoom level from other mode
1089
- // spread indices not set, so let's set them
1090
- if (init || !pageFound) {
1091
- this._modes.mode2Up.setSpreadIndices();
1092
- }
1093
-
1094
- this._modes.mode2Up.calculateReductionFactors(); // this sets this.twoPage && this.reduce
1095
1058
  this._modes.mode2Up.prepare();
1096
- this._modes.mode2Up.centerView(0.5, 0.5); // $$$ TODO preserve center
1097
1059
  }
1098
1060
 
1099
1061
  if (!(this.suppressFragmentChange || suppressFragmentChange)) {
@@ -1108,11 +1070,11 @@ BookReader.prototype.switchMode = function(
1108
1070
  BookReader.prototype.updateBrClasses = function() {
1109
1071
  const modeToClass = {};
1110
1072
  modeToClass[this.constMode1up] = 'BRmode1up';
1111
- modeToClass[this.constMode2up] = 'BRmode2Up';
1073
+ modeToClass[this.constMode2up] = 'BRmode2up';
1112
1074
  modeToClass[this.constModeThumb] = 'BRmodeThumb';
1113
1075
 
1114
1076
  this.refs.$br
1115
- .removeClass('BRmode1up BRmode2Up BRmodeThumb')
1077
+ .removeClass('BRmode1up BRmode2up BRmodeThumb')
1116
1078
  .addClass(modeToClass[this.mode]);
1117
1079
 
1118
1080
  if (this.isFullscreen()) {
@@ -1143,14 +1105,13 @@ BookReader.prototype.toggleFullscreen = async function(bindKeyboardControls = tr
1143
1105
  /**
1144
1106
  * Enters fullscreen
1145
1107
  * including:
1146
- * - animation
1147
1108
  * - binds keyboard controls
1148
1109
  * - fires custom event
1149
1110
  * @param { boolean } bindKeyboardControls
1150
1111
  */
1151
1112
  BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = true) {
1113
+ this.refs.$br.addClass('BRfullscreenAnimation');
1152
1114
  const currentIndex = this.currentIndex();
1153
- this.refs.$brContainer.css('opacity', 0);
1154
1115
 
1155
1116
  if (bindKeyboardControls) {
1156
1117
  this._fullscreenCloseHandler = (e) => {
@@ -1167,8 +1128,6 @@ BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = tru
1167
1128
  this.isFullscreenActive = true;
1168
1129
  // prioritize class updates so CSS can propagate
1169
1130
  this.updateBrClasses();
1170
- this.animating = true;
1171
- await new Promise(res => this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', res));
1172
1131
  if (this.activeMode instanceof Mode1Up) {
1173
1132
  this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this.book.getPage(currentIndex));
1174
1133
  // Need the new scale to be applied before calling jumpToIndex
@@ -1176,7 +1135,6 @@ BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = tru
1176
1135
  await this.activeMode.mode1UpLit.updateComplete;
1177
1136
  }
1178
1137
  this.jumpToIndex(currentIndex);
1179
- this.animating = false;
1180
1138
 
1181
1139
  this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1182
1140
  // Add "?view=theater"
@@ -1185,10 +1143,11 @@ BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = tru
1185
1143
  // class updates happen before book-nav relays to web components
1186
1144
  this.trigger(BookReader.eventNames.fullscreenToggled);
1187
1145
 
1188
- setTimeout(() => {
1189
- // resize book after all events & css updates
1190
- this.resize();
1191
- }, 0);
1146
+ // resize book after all events & css updates
1147
+ await new Promise(resolve => setTimeout(resolve, 0));
1148
+
1149
+ this.resize();
1150
+ this.refs.$br.removeClass('BRfullscreenAnimation');
1192
1151
  };
1193
1152
 
1194
1153
  /**
@@ -1199,12 +1158,10 @@ BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = tru
1199
1158
  * @param { boolean } bindKeyboardControls
1200
1159
  */
1201
1160
  BookReader.prototype.exitFullScreen = async function () {
1202
- this.refs.$brContainer.css('opacity', 0);
1203
-
1161
+ this.refs.$br.addClass('BRfullscreenAnimation');
1204
1162
  $(document).off('keyup', this._fullscreenCloseHandler);
1205
1163
 
1206
1164
  const windowWidth = $(window).width();
1207
-
1208
1165
  const canShow2up = this.options.controls.twoPage.visible;
1209
1166
  if (canShow2up && (windowWidth <= this.onePageMinBreakpoint)) {
1210
1167
  this.switchMode(this.constMode2up);
@@ -1216,8 +1173,7 @@ BookReader.prototype.exitFullScreen = async function () {
1216
1173
  this.trigger(BookReader.eventNames.fullscreenToggled);
1217
1174
 
1218
1175
  this.updateBrClasses();
1219
- this.animating = true;
1220
- await new Promise((res => this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', res)));
1176
+ await new Promise(resolve => setTimeout(resolve, 0));
1221
1177
  this.resize();
1222
1178
 
1223
1179
  if (this.activeMode instanceof Mode1Up) {
@@ -1226,11 +1182,10 @@ BookReader.prototype.exitFullScreen = async function () {
1226
1182
  await this.activeMode.mode1UpLit.updateComplete;
1227
1183
  }
1228
1184
 
1229
- this.animating = false;
1230
-
1231
1185
  this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1232
1186
  // Remove "?view=theater"
1233
1187
  this.trigger(BookReader.eventNames.fragmentChange);
1188
+ this.refs.$br.removeClass('BRfullscreenAnimation');
1234
1189
  };
1235
1190
 
1236
1191
  /**
@@ -1329,9 +1284,9 @@ BookReader.prototype.leftmost = function() {
1329
1284
  BookReader.prototype.next = function({triggerStop = true} = {}) {
1330
1285
  if (this.constMode2up == this.mode) {
1331
1286
  if (triggerStop) this.trigger(BookReader.eventNames.stop);
1332
- this._modes.mode2Up.flipFwdToIndex(null);
1287
+ this._modes.mode2Up.mode2UpLit.flipAnimation('next');
1333
1288
  } else {
1334
- if (this.firstIndex < this.lastDisplayableIndex()) {
1289
+ if (this.firstIndex < this.book.getNumLeafs() - 1) {
1335
1290
  this.jumpToIndex(this.firstIndex + 1);
1336
1291
  }
1337
1292
  }
@@ -1343,7 +1298,7 @@ BookReader.prototype.prev = function({triggerStop = true} = {}) {
1343
1298
 
1344
1299
  if (this.constMode2up == this.mode) {
1345
1300
  if (triggerStop) this.trigger(BookReader.eventNames.stop);
1346
- this._modes.mode2Up.flipBackToIndex(null);
1301
+ this._modes.mode2Up.mode2UpLit.flipAnimation('prev');
1347
1302
  } else {
1348
1303
  if (this.firstIndex >= 1) {
1349
1304
  this.jumpToIndex(this.firstIndex - 1);
@@ -1352,87 +1307,13 @@ BookReader.prototype.prev = function({triggerStop = true} = {}) {
1352
1307
  };
1353
1308
 
1354
1309
  BookReader.prototype.first = function() {
1355
- this.jumpToIndex(this.firstDisplayableIndex());
1310
+ this.jumpToIndex(0);
1356
1311
  };
1357
1312
 
1358
1313
  BookReader.prototype.last = function() {
1359
- this.jumpToIndex(this.lastDisplayableIndex());
1360
- };
1361
-
1362
- /**
1363
- * Scrolls down one screen view
1364
- */
1365
- BookReader.prototype.scrollDown = function() {
1366
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1367
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1368
- // Whole pages are visible, scroll whole page only
1369
- return this.next();
1370
- }
1371
-
1372
- this.refs.$brContainer.stop(true).animate(
1373
- { scrollTop: '+=' + this._scrollAmount() + 'px'},
1374
- 400, 'easeInOutExpo'
1375
- );
1376
- return true;
1377
- } else {
1378
- return false;
1379
- }
1380
- };
1381
-
1382
- /**
1383
- * Scrolls up one screen view
1384
- */
1385
- BookReader.prototype.scrollUp = function() {
1386
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1387
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1388
- // Whole pages are visible, scroll whole page only
1389
- return this.prev();
1390
- }
1391
-
1392
- this.refs.$brContainer.stop(true).animate(
1393
- { scrollTop: '-=' + this._scrollAmount() + 'px'},
1394
- 400, 'easeInOutExpo'
1395
- );
1396
- return true;
1397
- } else {
1398
- return false;
1399
- }
1400
- };
1401
-
1402
- /**
1403
- * The amount to scroll vertically in integer pixels
1404
- */
1405
- BookReader.prototype._scrollAmount = function() {
1406
- if (this.constMode1up == this.mode) {
1407
- // Overlap by % of page size
1408
- return parseInt(this.refs.$brContainer.prop('clientHeight') - this.book.getPageHeight(this.currentIndex()) / this.reduce * 0.03);
1409
- }
1410
-
1411
- return parseInt(0.9 * this.refs.$brContainer.prop('clientHeight'));
1314
+ this.jumpToIndex(this.book.getNumLeafs() - 1);
1412
1315
  };
1413
1316
 
1414
- /**
1415
- * Immediately stop flip animations. Callbacks are triggered.
1416
- */
1417
- BookReader.prototype.stopFlipAnimations = function() {
1418
- this.trigger(BookReader.eventNames.stop);
1419
-
1420
- // Stop animation, clear queue, trigger callbacks
1421
- if (this.leafEdgeTmp) {
1422
- $(this.leafEdgeTmp).stop(false, true);
1423
- }
1424
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1425
- $(this.$container).stop(false, true);
1426
- });
1427
-
1428
- // And again since animations also queued in callbacks
1429
- if (this.leafEdgeTmp) {
1430
- $(this.leafEdgeTmp).stop(false, true);
1431
- }
1432
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1433
- $(this.$container).stop(false, true);
1434
- });
1435
- };
1436
1317
 
1437
1318
  /**
1438
1319
  * @template TClass extends { br: BookReader }
@@ -1492,20 +1373,6 @@ BookReader.prototype.bindNavigationHandlers = function() {
1492
1373
  this.trigger(BookReader.eventNames.stop);
1493
1374
  this.right();
1494
1375
  },
1495
- book_up: () => {
1496
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1497
- this.scrollUp();
1498
- } else {
1499
- this.prev();
1500
- }
1501
- },
1502
- book_down: () => {
1503
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1504
- this.scrollDown();
1505
- } else {
1506
- this.next();
1507
- }
1508
- },
1509
1376
  book_top: this.first.bind(this),
1510
1377
  book_bottom: this.last.bind(this),
1511
1378
  book_leftmost: this.leftmost.bind(this),
@@ -1881,64 +1748,6 @@ BookReader.prototype.showNavigation = function() {
1881
1748
  }
1882
1749
  };
1883
1750
 
1884
- /**
1885
- * Returns the index of the first visible page, dependent on the mode.
1886
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
1887
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
1888
- * @return {number}
1889
- */
1890
- BookReader.prototype.firstDisplayableIndex = function() {
1891
- if (this.mode != this.constMode2up) {
1892
- return 0;
1893
- }
1894
-
1895
- if ('rl' != this.pageProgression) {
1896
- // LTR
1897
- if (this.book.getPageSide(0) == 'L') {
1898
- return 0;
1899
- } else {
1900
- return -1;
1901
- }
1902
- } else {
1903
- // RTL
1904
- if (this.book.getPageSide(0) == 'R') {
1905
- return 0;
1906
- } else {
1907
- return -1;
1908
- }
1909
- }
1910
- };
1911
-
1912
- /**
1913
- * Returns the index of the last visible page, dependent on the mode.
1914
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
1915
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
1916
- * @return {number}
1917
- */
1918
- BookReader.prototype.lastDisplayableIndex = function() {
1919
-
1920
- const lastIndex = this.book.getNumLeafs() - 1;
1921
-
1922
- if (this.mode != this.constMode2up) {
1923
- return lastIndex;
1924
- }
1925
-
1926
- if ('rl' != this.pageProgression) {
1927
- // LTR
1928
- if (this.book.getPageSide(lastIndex) == 'R') {
1929
- return lastIndex;
1930
- } else {
1931
- return lastIndex + 1;
1932
- }
1933
- } else {
1934
- // RTL
1935
- if (this.book.getPageSide(lastIndex) == 'L') {
1936
- return lastIndex;
1937
- } else {
1938
- return lastIndex + 1;
1939
- }
1940
- }
1941
- };
1942
1751
 
1943
1752
 
1944
1753
  /**************************/
@@ -2112,8 +1921,6 @@ BookReader.prototype.initUIStrings = function() {
2112
1921
  '.full': 'Toggle fullscreen',
2113
1922
  '.book_left': 'Flip left',
2114
1923
  '.book_right': 'Flip right',
2115
- '.book_up': 'Page up',
2116
- '.book_down': 'Page down',
2117
1924
  '.play': 'Play',
2118
1925
  '.pause': 'Pause',
2119
1926
  '.BRdn': 'Show/hide nav bar', // Would have to keep updating on state change to have just "Hide nav bar"
@@ -6,7 +6,7 @@
6
6
  z-index: 2;
7
7
  opacity: 0;
8
8
  }
9
- .BRtwopageview .BRpagecontainer[data-side="L"] .bookmark-button {
9
+ .BRmode2up .BRpagecontainer[data-side="L"] .bookmark-button {
10
10
  right: auto;
11
11
  left: 0;
12
12
  }
@@ -249,3 +249,17 @@ body.BRfullscreenActive {
249
249
  .BRpagediv1up { background-color: white; }
250
250
  .BRpagedivthumb { background-color: white; }
251
251
  }
252
+
253
+ .BRfullscreenAnimation .br-mode-2up__book {
254
+ transition: transform .2s ease-in-out;
255
+ }
256
+
257
+ .fullscreenActive.BRmodeThumb .BRcontainer,
258
+ .fullscreenActive.BRmode1up .BRcontainer {
259
+ animation: flash 0.3s ease-in-out;
260
+ }
261
+
262
+ @keyframes flash {
263
+ 0% { opacity: 0; }
264
+ 100% { opacity: 1; }
265
+ }