@internetarchive/bookreader 5.0.0-101 → 5.0.0-103

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 (114) hide show
  1. package/BookReader/474.js +1 -1
  2. package/BookReader/474.js.map +1 -1
  3. package/BookReader/BookReader.css +71 -8
  4. package/BookReader/BookReader.js +1 -1
  5. package/BookReader/BookReader.js.LICENSE.txt +1 -1
  6. package/BookReader/BookReader.js.map +1 -1
  7. package/BookReader/ia-bookreader-bundle.js +148 -109
  8. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +13 -1
  9. package/BookReader/ia-bookreader-bundle.js.map +1 -1
  10. package/BookReader/icons/1up.svg +1 -1
  11. package/BookReader/icons/2up.svg +1 -1
  12. package/BookReader/icons/advance.svg +1 -1
  13. package/BookReader/icons/chevron-right.svg +1 -1
  14. package/BookReader/icons/close-circle-dark.svg +1 -1
  15. package/BookReader/icons/close-circle.svg +1 -1
  16. package/BookReader/icons/fullscreen.svg +1 -1
  17. package/BookReader/icons/fullscreen_exit.svg +1 -1
  18. package/BookReader/icons/hamburger.svg +1 -1
  19. package/BookReader/icons/left-arrow.svg +1 -1
  20. package/BookReader/icons/magnify-minus.svg +1 -1
  21. package/BookReader/icons/magnify-plus.svg +1 -1
  22. package/BookReader/icons/magnify.svg +1 -1
  23. package/BookReader/icons/pause.svg +1 -1
  24. package/BookReader/icons/play.svg +1 -1
  25. package/BookReader/icons/playback-speed.svg +1 -1
  26. package/BookReader/icons/read-aloud.svg +1 -1
  27. package/BookReader/icons/review.svg +1 -1
  28. package/BookReader/icons/slider-toggle.svg +1 -0
  29. package/BookReader/icons/thumbnails.svg +1 -1
  30. package/BookReader/icons/voice.svg +1 -1
  31. package/BookReader/icons/volume-full.svg +1 -1
  32. package/BookReader/images/BRicons.svg +2 -2
  33. package/BookReader/images/books_graphic.svg +1 -1
  34. package/BookReader/images/icon_book.svg +1 -1
  35. package/BookReader/images/icon_bookmark.svg +1 -1
  36. package/BookReader/images/icon_gear.svg +1 -1
  37. package/BookReader/images/icon_hamburger.svg +1 -1
  38. package/BookReader/images/icon_home.svg +1 -1
  39. package/BookReader/images/icon_info.svg +1 -1
  40. package/BookReader/images/icon_one_page.svg +1 -1
  41. package/BookReader/images/icon_pause.svg +1 -1
  42. package/BookReader/images/icon_play.svg +1 -1
  43. package/BookReader/images/icon_playback-rate.svg +1 -1
  44. package/BookReader/images/icon_search_button.svg +1 -1
  45. package/BookReader/images/icon_share.svg +1 -1
  46. package/BookReader/images/icon_skip-back.svg +1 -1
  47. package/BookReader/images/icon_speaker.svg +1 -1
  48. package/BookReader/images/icon_speaker_open.svg +1 -1
  49. package/BookReader/images/icon_thumbnails.svg +1 -1
  50. package/BookReader/images/icon_toc.svg +1 -1
  51. package/BookReader/images/icon_two_pages.svg +1 -1
  52. package/BookReader/images/marker_chap-off.svg +1 -1
  53. package/BookReader/images/marker_chap-on.svg +1 -1
  54. package/BookReader/images/marker_srch-on.svg +1 -1
  55. package/BookReader/images/translate.svg +1 -1
  56. package/BookReader/jquery-3.js +1 -1
  57. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  58. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  59. package/BookReader/plugins/plugin.autoplay.js +1 -1
  60. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  61. package/BookReader/plugins/plugin.chapters.js +2 -2
  62. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -1
  63. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  64. package/BookReader/plugins/plugin.experiments.js +1 -1
  65. package/BookReader/plugins/plugin.experiments.js.LICENSE.txt +1 -1
  66. package/BookReader/plugins/plugin.experiments.js.map +1 -1
  67. package/BookReader/plugins/plugin.iframe.js +1 -1
  68. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  69. package/BookReader/plugins/plugin.iiif.js +1 -1
  70. package/BookReader/plugins/plugin.iiif.js.map +1 -1
  71. package/BookReader/plugins/plugin.resume.js +1 -1
  72. package/BookReader/plugins/plugin.resume.js.map +1 -1
  73. package/BookReader/plugins/plugin.search.js +1 -1
  74. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -1
  75. package/BookReader/plugins/plugin.search.js.map +1 -1
  76. package/BookReader/plugins/plugin.text_selection.js +1 -1
  77. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -1
  78. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  79. package/BookReader/plugins/plugin.translate.js +4 -4
  80. package/BookReader/plugins/plugin.translate.js.LICENSE.txt +1 -1
  81. package/BookReader/plugins/plugin.translate.js.map +1 -1
  82. package/BookReader/plugins/plugin.tts.js +1 -1
  83. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +1 -1
  84. package/BookReader/plugins/plugin.tts.js.map +1 -1
  85. package/BookReader/plugins/plugin.url.js +1 -1
  86. package/BookReader/plugins/plugin.url.js.map +1 -1
  87. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  88. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  89. package/BookReader/plugins/translator-worker.js +1 -1
  90. package/BookReader/plugins/translator-worker.js.map +1 -1
  91. package/BookReader/webcomponents-bundle.js +1 -1
  92. package/BookReader/webcomponents-bundle.js.map +1 -1
  93. package/package.json +21 -21
  94. package/src/BookNavigator/assets/button-base.js +0 -1
  95. package/src/BookNavigator/bookmarks/bookmarks-provider.js +1 -1
  96. package/src/BookNavigator/downloads/downloads-provider.js +1 -1
  97. package/src/BookNavigator/search/search-provider.js +5 -1
  98. package/src/BookNavigator/search/search-results.js +84 -63
  99. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +5 -5
  100. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +77 -59
  101. package/src/BookReader/Navbar/Navbar.js +61 -73
  102. package/src/BookReader/options.js +6 -0
  103. package/src/BookReader.js +12 -0
  104. package/src/assets/icons/slider-toggle.svg +1 -0
  105. package/src/css/_BRnav.scss +57 -4
  106. package/src/css/_icons.scss +7 -0
  107. package/src/plugins/plugin.chapters.js +1 -1
  108. package/src/plugins/plugin.experiments.js +1 -1
  109. package/src/plugins/plugin.text_selection.js +9 -0
  110. package/src/plugins/search/plugin.search.js +4 -0
  111. package/src/plugins/search/utils.js +8 -1
  112. package/src/plugins/search/view.js +1 -1
  113. package/src/plugins/translate/TranslationManager.js +6 -4
  114. package/src/plugins/tts/utils.js +43 -17
@@ -23,18 +23,23 @@ export class Navbar {
23
23
 
24
24
  /** @type {Object} controls will be switch over "this.maximumControls" */
25
25
  this.minimumControls = [
26
- 'viewmode',
26
+ 'toggle_slider', 'viewmode',
27
27
  ];
28
28
  /** @type {Object} controls will be switch over "this.minimumControls" */
29
29
  this.maximumControls = [
30
- 'book_left', 'book_right', 'zoom_in', 'zoom_out', 'onepg', 'twopg', 'thumb',
30
+ 'BRnavpos', 'book_left', 'book_right', 'zoom_in', 'zoom_out', 'onepg', 'twopg', 'thumb',
31
31
  ];
32
32
 
33
33
  this.updateNavIndexThrottled = throttle(this.updateNavIndex.bind(this), 250, false);
34
34
  }
35
35
 
36
- controlFor(controlName) {
37
- const option = this.br.options.controls[controlName];
36
+ /**
37
+ * @param {string} controlName
38
+ * @param {Object} optionOverrides
39
+ */
40
+ controlFor(controlName, optionOverrides = null) {
41
+ const brOption = this.br.options.controls[controlName];
42
+ const option = Object.assign({},brOption, optionOverrides);
38
43
  if (!option.visible) { return ''; }
39
44
  if (option.template) {
40
45
  return `<li>${option.template(this.br)}</li>`;
@@ -50,6 +55,7 @@ export class Navbar {
50
55
  /** @private */
51
56
  _renderControls() {
52
57
  return [
58
+ 'toggleSlider',
53
59
  'bookLeft',
54
60
  'bookRight',
55
61
  'onePage',
@@ -126,6 +132,9 @@ export class Navbar {
126
132
 
127
133
  // Map of jIcon class -> click handler
128
134
  const navigationControls = {
135
+ toggle_slider: () => {
136
+ this.br.toggleSlider();
137
+ },
129
138
  book_left: () => {
130
139
  this.br.trigger(EVENTS.stop);
131
140
  this.br.left();
@@ -199,71 +208,48 @@ export class Navbar {
199
208
  * Switch navbar controls on mobile and desktop
200
209
  */
201
210
  switchNavbarControls() {
202
- if (this.br.refs.$brContainer.prop('clientWidth') < 640) {
203
- this.showMinimumNavPageNum();
204
- // we don't want navbar controls switching with liner-notes
205
- if (this.br.options.bookType !== 'linerNotes') {
206
- this.showMinimumNavbarControls();
207
- }
208
- } else {
209
- this.showMaximumNavPageNum();
210
- // we don't want navbar controls switching with liner-notes
211
- if (this.br.options.bookType !== 'linerNotes') {
212
- this.showMaximumNavbarControls();
211
+ // we don't want navbar controls switching with liner-notes
212
+ if (this.br.options.bookType !== 'linerNotes') {
213
+ if (this.br.refs.$brContainer.prop('clientWidth') < 640) {
214
+ this.showMobileControls();
215
+ } else {
216
+ this.showDesktopControls();
213
217
  }
214
218
  }
215
219
  }
216
220
 
217
221
  /**
218
- * Switch Book Nav page number display to minimum/mobile
219
- */
220
- showMinimumNavPageNum() {
221
- const minElement = document.querySelector('.BRcurrentpage.BRmin');
222
- const maxElement = document.querySelector('.BRcurrentpage.BRmax');
223
-
224
- if (minElement) minElement.classList.remove('hide');
225
- if (maxElement) maxElement.classList.add('hide');
226
- }
227
-
228
- /**
229
- * Switch Book Nav page number display to maximum/desktop
230
- */
231
- showMaximumNavPageNum() {
232
- const minElement = document.querySelector('.BRcurrentpage.BRmin');
233
- const maxElement = document.querySelector('.BRcurrentpage.BRmax');
234
-
235
- if (minElement) minElement.classList.add('hide');
236
- if (maxElement) maxElement.classList.remove('hide');
237
- }
238
-
239
- /**
240
- * Switch Book Navbar controls to minimised
241
- * NOTE: only `this.minimumControls` and `this.maximumControls` switch on resize
222
+ * Switch Book controls to mobile mode
223
+ * NOTE: `this.minimumControls`, `this.maximumControls`, and .BRnavMobile switch on resize
242
224
  */
243
- showMinimumNavbarControls() {
225
+ showMobileControls() {
244
226
  this.minimumControls.forEach((control) => {
245
- const element = document.querySelector(`.controls .${control}`);
227
+ const element = document.querySelector(`.BRnavMain .controls .${control}`);
246
228
  if (element) element.classList.remove('hide');
247
229
  });
248
230
  this.maximumControls.forEach((control) => {
249
- const element = document.querySelector(`.controls .${control}`);
231
+ const element = document.querySelector(`.BRnavMain .controls .${control}`);
250
232
  if (element) element.classList.add('hide');
251
233
  });
234
+ const mobileNav = document.querySelector(`.BRnavMobile`);
235
+ if (mobileNav) mobileNav.classList.remove('hide');
252
236
  }
253
237
 
254
238
  /**
255
- * Switch Book Navbar controls to maximized
256
- * NOTE: only `this.minimumControls` and `this.maximumControls` switch on resize
239
+ * Switch Book controls to desktop mode
240
+ * NOTE: `this.minimumControls`, `this.maximumControls`, and .BRnavMobile switch on resize
257
241
  */
258
- showMaximumNavbarControls() {
242
+ showDesktopControls() {
259
243
  this.maximumControls.forEach((control) => {
260
- const element = document.querySelector(`.controls .${control}`);
244
+ const element = document.querySelector(`.BRnavMain .controls .${control}`);
261
245
  if (element) element.classList.remove('hide');
262
246
  });
263
247
  this.minimumControls.forEach((control) => {
264
- const element = document.querySelector(`.controls .${control}`);
248
+ const element = document.querySelector(`.BRnavMain .controls .${control}`);
265
249
  if (element) element.classList.add('hide');
266
250
  });
251
+ const mobileNav = document.querySelector(`.BRnavMobile`);
252
+ if (mobileNav) mobileNav.classList.add('hide');
267
253
  }
268
254
 
269
255
  /**
@@ -282,7 +268,7 @@ export class Navbar {
282
268
 
283
269
  br.refs.$BRfooter = this.$root = $(`<div class="BRfooter"></div>`);
284
270
  br.refs.$BRnav = this.$nav = $(
285
- `<div class="BRnav BRnavDesktop">
271
+ `<div class="BRnav BRnavMobile docked">
286
272
  ${title ? `<div class="BRnavTitle">${title}</div>` : ''}
287
273
  <nav class="BRcontrols">
288
274
  <ul class="controls">
@@ -291,10 +277,24 @@ export class Navbar {
291
277
  <div class="BRpager"></div>
292
278
  <div class="BRnavline"></div>
293
279
  </div>
280
+ </li>
281
+ ${this.controlFor('bookLeft', {visible: true})}
282
+ ${this.controlFor('bookRight', {visible: true})}
283
+ </ul>
284
+ </nav>
285
+ </div>
286
+ <div class="BRnav BRnavMain">
287
+ ${title ? `<div class="BRnavTitle">${title}</div>` : ''}
288
+ <nav class="BRcontrols">
289
+ <ul class="controls">
290
+ <li class="scrubber">
294
291
  <p>
295
- <span class="BRcurrentpage BRmax"></span>
296
- <span class="BRcurrentpage BRmin"></span>
292
+ <span class="BRcurrentpage"></span>
297
293
  </p>
294
+ <div class="BRnavpos hide">
295
+ <div class="BRpager"></div>
296
+ <div class="BRnavline"></div>
297
+ </div>
298
298
  </li>
299
299
  ${this._renderControls()}
300
300
  </ul>
@@ -304,7 +304,8 @@ export class Navbar {
304
304
  this.$root.append(this.$nav);
305
305
  br.refs.$br.append(this.$root);
306
306
 
307
- const $slider = this.$root.find('.BRpager').slider({
307
+ /** @type {JQuery} sliders */
308
+ const $sliders = this.$root.find('.BRpager').slider({
308
309
  animate: true,
309
310
  min: 0,
310
311
  max: br.book.getNumLeafs() - 1,
@@ -312,16 +313,16 @@ export class Navbar {
312
313
  range: "min",
313
314
  });
314
315
 
315
- $slider.on('slide', (event, ui) => {
316
+ $sliders.on('slide', (event, ui) => {
316
317
  this.updateNavPageNum(ui.value);
317
318
  return true;
318
319
  });
319
320
 
320
- $slider.on('slidechange', (event, ui) => {
321
+ $sliders.on('slidechange', (event, ui) => {
321
322
  this.updateNavPageNum(ui.value);
322
323
  // recursion prevention for jumpToIndex
323
- if ($slider.data('swallowchange')) {
324
- $slider.data('swallowchange', false);
324
+ if ($(event.currentTarget).data('swallowchange')) {
325
+ $(event.currentTarget).data('swallowchange', false);
325
326
  } else {
326
327
  br.jumpToIndex(ui.value);
327
328
  }
@@ -337,10 +338,9 @@ export class Navbar {
337
338
  /**
338
339
  * Returns the textual representation of the current page for the navbar
339
340
  * @param {number} index
340
- * @param {boolean} [useMaxFormat = false]
341
341
  * @return {string}
342
342
  */
343
- getNavPageNumString(index, useMaxFormat = false) {
343
+ getNavPageNumString(index) {
344
344
  const { br } = this;
345
345
  // Accessible index starts at 0 (alas) so we add 1 to make human
346
346
  const pageNum = br.book.getPageNum(index);
@@ -360,7 +360,7 @@ export class Navbar {
360
360
  this.maxPageNum = maxPageNum;
361
361
  }
362
362
 
363
- return getNavPageNumHtml(index, numLeafs, pageNum, pageType, this.maxPageNum, useMaxFormat);
363
+ return getNavPageNumHtml(index, numLeafs, pageNum, pageType, this.maxPageNum);
364
364
 
365
365
  }
366
366
 
@@ -369,8 +369,7 @@ export class Navbar {
369
369
  * @param {number} index
370
370
  */
371
371
  updateNavPageNum(index) {
372
- this.$root.find('.BRcurrentpage.BRmax').html(this.getNavPageNumString(index, true));
373
- this.$root.find('.BRcurrentpage.BRmin').html(this.getNavPageNumString(index));
372
+ this.$root.find('.BRcurrentpage').html(this.getNavPageNumString(index, true));
374
373
  }
375
374
 
376
375
  /**
@@ -392,25 +391,14 @@ export class Navbar {
392
391
  * @param {number|string} pageNum
393
392
  * @param {*} pageType - Deprecated
394
393
  * @param {number} maxPageNum
395
- * @param {boolean} [useMaxFormat = false]
396
394
  * @return {string}
397
395
  */
398
- export function getNavPageNumHtml(index, numLeafs, pageNum, pageType, maxPageNum, useMaxFormat = false) {
396
+ export function getNavPageNumHtml(index, numLeafs, pageNum, pageType, maxPageNum) {
399
397
  const pageIsAsserted = pageNum[0] != 'n';
400
398
  const pageIndex = index + 1;
401
399
 
402
400
  if (!pageIsAsserted) {
403
401
  pageNum = '—';
404
402
  }
405
-
406
- if (useMaxFormat === true) {
407
- return `Page ${pageNum} (${pageIndex}/${numLeafs})`;
408
- }
409
-
410
- if (!pageIsAsserted) {
411
- return `(${pageIndex} of ${numLeafs})`;
412
- }
413
-
414
- const bookLengthLabel = (maxPageNum && parseFloat(pageNum)) ? ` of ${maxPageNum}` : '';
415
- return `${pageNum}${bookLengthLabel}`;
403
+ return `Page ${pageNum} (${pageIndex}/${numLeafs})`;
416
404
  }
@@ -258,6 +258,12 @@ export const DEFAULT_OPTIONS = {
258
258
  getEmbedCode: null,
259
259
 
260
260
  controls: {
261
+ toggleSlider: {
262
+ visible: true,
263
+ label: 'Toggle page controls',
264
+ className: 'toggle_slider',
265
+ iconClassName: 'toggle-slider',
266
+ },
261
267
  bookLeft: {
262
268
  visible: true,
263
269
  label: 'Flip left',
package/src/BookReader.js CHANGED
@@ -1380,6 +1380,17 @@ BookReader.prototype.exitFullScreen = async function () {
1380
1380
  this.refs.$br.removeClass('BRfullscreenAnimation');
1381
1381
  };
1382
1382
 
1383
+ /**
1384
+ * Toggles the mobile slider and page controls
1385
+ */
1386
+ BookReader.prototype.toggleSlider = function () {
1387
+ const toggleButton = this.refs.$BRnav.find('.toggle_slider');
1388
+ const mobileControls = this.refs.$br.find('.BRnavMobile');
1389
+
1390
+ toggleButton.toggleClass('active');
1391
+ mobileControls.toggleClass('docked');
1392
+ };
1393
+
1383
1394
  /**
1384
1395
  * Returns the currently active index
1385
1396
  * @return {number}
@@ -1734,6 +1745,7 @@ BookReader.prototype.initUIStrings = function() {
1734
1745
  '.share': 'Share this book',
1735
1746
  '.info': 'About this book',
1736
1747
  '.full': 'Toggle fullscreen',
1748
+ '.toggle_slider': 'Toggle page controls',
1737
1749
  '.book_left': 'Flip left',
1738
1750
  '.book_right': 'Flip right',
1739
1751
  '.play': 'Play',
@@ -0,0 +1 @@
1
+ <svg height="100" viewBox="0 0 100 100" width="100" xmlns="http://www.w3.org/2000/svg"><g fill="#fff" fill-rule="evenodd" transform="translate(0 23)"><path d="m35 0v54l-35-27.8266318zm30 0 35 26.1733682-35 27.8266318z"/><circle cx="50" cy="27.236059" r="10"/></g></svg>
@@ -118,7 +118,6 @@
118
118
  height: 100%;
119
119
  }
120
120
 
121
-
122
121
  .BRToolbarButton {
123
122
  .icon {
124
123
  width: $toolbarHeight;
@@ -226,6 +225,12 @@
226
225
  border: none;
227
226
  }
228
227
 
228
+ &.ui-slider-docked {
229
+ height: 1px;
230
+ width: 100%;
231
+ margin: 0px;
232
+ }
233
+
229
234
  .ui-slider-horizontal .ui-slider-handle,
230
235
  .ui-slider-handle {
231
236
  position: absolute;
@@ -262,10 +267,10 @@
262
267
  }
263
268
  .BRcurrentpage {
264
269
  display: inline-block;
265
- text-align: center;
270
+ text-align: left;
266
271
  padding: 0 0 0 0;
267
272
  font-size: $brFontSizeSmaller;
268
- margin-left: 5px;
273
+ margin-left: 15px;
269
274
  margin-right: 10px;
270
275
  line-height: 1;
271
276
  }
@@ -310,7 +315,6 @@
310
315
  .BRnavline .BRchapter { display: none; }
311
316
 
312
317
  .BRpager.ui-slider {
313
- height: 10px;
314
318
  top: math.div($brNavHeightMobile, 2) - 5px;
315
319
  }
316
320
  }
@@ -325,3 +329,52 @@
325
329
  .DrawerSettingsWrapper .zoom_in {
326
330
  background-position: -1384px center;
327
331
  }
332
+
333
+ .BRnavMobile {
334
+ box-sizing: content-box;
335
+ }
336
+
337
+ .BRnavMobile.docked {
338
+ height: 0px;
339
+ border: none;
340
+ .BRcontrols .scrubber .BRpager.ui-slider {
341
+ top: -1px;
342
+ }
343
+
344
+ .ui-slider,
345
+ .ui-slider-range {
346
+ height: 2px;
347
+ border-radius: 0px;
348
+ }
349
+
350
+ .BRnavpos {
351
+ margin: 0px;
352
+ }
353
+
354
+ .scrubber {
355
+ margin: 0px;
356
+ padding: 0px;
357
+ height: 0px;
358
+ }
359
+
360
+ .controls {
361
+ padding: 0px;
362
+ background-color: transparent;
363
+ }
364
+
365
+ .BRnavline {
366
+ display: none;
367
+ }
368
+
369
+ .ui-slider-handle {
370
+ display: none;
371
+ }
372
+
373
+ button {
374
+ display: none;
375
+ }
376
+ }
377
+
378
+ .BRnavMobile.hide {
379
+ display: none;
380
+ }
@@ -119,3 +119,10 @@
119
119
  height: 12px;
120
120
  background-image: url("icons/close-circle-dark.svg");
121
121
  }
122
+
123
+ .icon-toggle-slider {
124
+ background-size: 25px;
125
+ width: 25px;
126
+ height: 25px;
127
+ background-image: url("icons/slider-toggle.svg");
128
+ }
@@ -110,7 +110,7 @@ export class ChaptersPlugin extends BookReaderPlugin {
110
110
  _render() {
111
111
  this.br.shell.menuProviders['chapters'] = {
112
112
  id: 'chapters',
113
- icon: html`<ia-icon-toc style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon-toc>`,
113
+ icon: html`<ia-icon-toc aria-hidden="true" role="presentation" style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon-toc>`,
114
114
  label: 'Table of Contents',
115
115
  component: html`<br-chapters-panel
116
116
  .contents="${this._tocEntries}"
@@ -174,7 +174,7 @@ export class ExperimentsPlugin extends BookReaderPlugin {
174
174
  this.br.shell.menuProviders['experiments'] = {
175
175
  id: 'experiments',
176
176
  icon: html`
177
- <img src="${this.br.options.imagesBaseURL}/icon_experiment.svg" width="26"/>
177
+ <img src="${this.br.options.imagesBaseURL}/icon_experiment.svg" alt="" width="26"/>
178
178
  `,
179
179
  label: 'Experiments',
180
180
  component: html`<br-experiments-panel
@@ -137,6 +137,15 @@ export class TextSelectionPlugin extends BookReaderPlugin {
137
137
  if ($textLayers.length) return;
138
138
  const XMLpage = await this.getPageText(pageIndex);
139
139
  if (!XMLpage) return;
140
+ // Seeing some 0 left and 0 top coordinates in OCR, remove it entirely to prevent odd rendering
141
+ // eg https://archive.org/details/illustratedbooko00robe/page/n11/mode/2up
142
+ $(XMLpage).find("WORD").filter((_, ele) => {
143
+ const [left, , , top] = ele.getAttribute('coords').split(",").map(parseFloat);
144
+ if (left == 0 && top == 0) {
145
+ console.error("Found invalid ocr word coordinates");
146
+ return true;
147
+ }
148
+ }).remove();
140
149
  recursivelyAddCoords(XMLpage);
141
150
 
142
151
  const totalWords = $(XMLpage).find("WORD").length;
@@ -26,6 +26,7 @@ import SearchView from './view.js';
26
26
  import { marshallSearchResults } from './utils.js';
27
27
  import { BookReaderPlugin } from '../../BookReaderPlugin.js';
28
28
  import { applyVariables } from '../../util/strings.js';
29
+ import { toISO6391 } from '../tts/utils.js';
29
30
  /** @typedef {import('../../BookReader/PageContainer').PageContainer} PageContainer */
30
31
  /** @typedef {import('../../BookReader/BookModel').PageIndex} PageIndex */
31
32
  /** @typedef {import('../../BookReader/BookModel').LeafNum} LeafNum */
@@ -239,11 +240,13 @@ export class SearchPlugin extends BookReaderPlugin {
239
240
  * @param {boolean} options.goToFirstResult
240
241
  */
241
242
  BRSearchCallback(results, options) {
243
+ const bookLangCode = toISO6391(this.br.options.bookLanguage);
242
244
  marshallSearchResults(
243
245
  results,
244
246
  pageNum => this.br.book.getPageNum(this.br.book.leafNumToIndex(pageNum)),
245
247
  this.options.preTag,
246
248
  this.options.postTag,
249
+ bookLangCode,
247
250
  );
248
251
  this.searchResults = results || null;
249
252
 
@@ -435,6 +438,7 @@ BookReader?.registerPlugin('search', SearchPlugin);
435
438
  * @property {string} displayPageNumber (fake field) The page number as it should be displayed in the UI.
436
439
  * @property {string} html (computed field) The html-escaped raw html to display in the UI.
437
440
  * @property {string} text
441
+ * @property {string} lang (fake field) The ISO 639-1 language code of the book
438
442
  * @property {Array<{ page: number, boxes: SearchInsideMatchBox[] }>} par
439
443
  */
440
444
 
@@ -1,4 +1,8 @@
1
+ // @ts-check
1
2
  import { escapeHTML, escapeRegExp } from '../../BookReader/utils.js';
3
+ /** @typedef {import('@/src/plugins/search/plugin.search.js').SearchInsideResults} SearchInsideResults */
4
+ /** @typedef {import('@/src/plugins/search/plugin.search.js').LeafNum} LeafNum */
5
+ /** @typedef {import('@/src/plugins/search/plugin.search.js').PageNumString} PageNumString */
2
6
 
3
7
  /**
4
8
  * @param {string} match
@@ -26,14 +30,17 @@ export function renderMatch(match, preTag, postTag) {
26
30
  * @param {(pageNum: LeafNum) => PageNumString} displayPageNumberFn
27
31
  * @param {string} preTag
28
32
  * @param {string} postTag
33
+ * @param {string | null} bookLanguage The ISO 639-1 language code of the book
29
34
  */
30
- export function marshallSearchResults(results, displayPageNumberFn, preTag, postTag) {
35
+ export function marshallSearchResults(results, displayPageNumberFn, preTag, postTag, bookLanguage) {
31
36
  // Attach matchIndex to a few things to make it easier to identify
32
37
  // an active/selected match
38
+
33
39
  for (const [index, match] of results.matches.entries()) {
34
40
  match.matchIndex = index;
35
41
  match.displayPageNumber = displayPageNumberFn(match.par[0].page);
36
42
  match.html = renderMatch(match.text, preTag, postTag);
43
+ match.lang = bookLanguage;
37
44
  for (const par of match.par) {
38
45
  for (const box of par.boxes) {
39
46
  box.matchIndex = index;
@@ -60,7 +60,7 @@ class SearchView {
60
60
 
61
61
  renderSearchNavigation() {
62
62
  const selector = 'BRsearch-navigation';
63
- $('.BRnav').before(`
63
+ $('.BRnav.BRnavMain').before(`
64
64
  <div class="${selector}">
65
65
  <button class="toggle-sidebar">
66
66
  <h4>
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
  import { Cache } from '../../util/cache.js';
3
3
  import { BatchTranslator } from '@internetarchive/bergamot-translator/translator.js';
4
- import { toISO6391 } from '../tts/utils.js';
4
+ import { toISO6391, toNativeName } from '../tts/utils.js';
5
5
 
6
6
  export class TranslationManager {
7
7
  /** @type {Cache<{index: string, response: string}>} */
@@ -36,7 +36,7 @@ export class TranslationManager {
36
36
 
37
37
  constructor() {
38
38
  //TODO Should default to the book language as the first element
39
- const enModel = {code: "en", name: "English", type: "prod"};
39
+ const enModel = {code: "en", name: "English (en)", type: "prod"};
40
40
  this.fromLanguages.push(enModel);
41
41
  this.toLanguages.push(enModel);
42
42
  }
@@ -77,10 +77,12 @@ export class TranslationManager {
77
77
  // List of dev models found here https://github.com/mozilla/firefox-translations-models/tree/main/models/base
78
78
  // There are also differences between the model types in the repo above here: https://github.com/mozilla/firefox-translations-models?tab=readme-ov-file#firefox-translations-models
79
79
  if (firstLang !== "en") {
80
- this.fromLanguages.push({code: firstLang, name: toISO6391(firstLang, true), type: "prod"});
80
+ const name = `${toNativeName(firstLang)} (${toISO6391(firstLang)})`;
81
+ this.fromLanguages.push({code: firstLang, name: name, type: "prod"});
81
82
  }
82
83
  if (secondLang !== "en") {
83
- this.toLanguages.push({code: secondLang, name: toISO6391(secondLang, true), type: "prod"});
84
+ const name = `${toNativeName(secondLang)} (${toISO6391(secondLang)})`;
85
+ this.toLanguages.push({code: secondLang, name: name, type: "prod"});
84
86
  }
85
87
  }
86
88
  this._initResolve([this.modelRegistry]);
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  import langs from 'iso-language-codes';
2
3
 
3
4
  /**
@@ -24,43 +25,68 @@ export function isAndroid(userAgent = navigator.userAgent) {
24
25
  const specialLangs = {
25
26
  "zh-hans": "中文 (Zhōngwén)",
26
27
  };
28
+
27
29
  /**
28
30
  * @typedef {string} ISO6391
29
31
  * Language code in ISO 639-1 format. e.g. en, fr, zh
30
- * Can also retrieve language native name + ISO 639-1 code
31
32
  **/
32
33
 
33
34
  /**
34
35
  * @param {string} language in some format
35
- * @param {boolean?} getName gets Native Name + language code if true
36
36
  * @return {ISO6391?}
37
37
  */
38
- export function toISO6391(language, getName = false) {
38
+ export function toISO6391(language) {
39
39
  if (!language) return null;
40
- language = language.toLowerCase();
40
+ language = language.toLocaleLowerCase();
41
+ if (specialLangs[language]) {
42
+ return language;
43
+ }
44
+ const codeObj = (
45
+ findLanguage(language, ['iso639_1']) ||
46
+ findLanguage(language, ['iso639_2T']) ||
47
+ findLanguage(language, ['iso639_2B', 'name', 'nativeName'])
48
+ );
49
+ if (codeObj) return codeObj.iso639_1;
50
+ return null;
51
+ }
41
52
 
42
- return searchForISO6391(language, ['iso639_1'], getName) ||
43
- searchForISO6391(language, ['iso639_2T'], getName) ||
44
- searchForISO6391(language, ['iso639_2B', 'nativeName', 'name'], getName);
53
+ /**
54
+ *
55
+ * @param {string} language
56
+ * @returns {string}
57
+ */
58
+ export function toNativeName(language) {
59
+ if (!language) return null;
60
+ language = language.toLocaleLowerCase();
61
+ if (specialLangs[language]) {
62
+ return specialLangs[language];
63
+ }
64
+ const codeObj = (
65
+ findLanguage(language, ['iso639_1']) ||
66
+ findLanguage(language, ['iso639_2T']) ||
67
+ findLanguage(language, ['iso639_2B', 'name', 'nativeName'])
68
+ );
69
+ if (codeObj?.nativeName) return codeObj.nativeName.split(", ")[0];
70
+ return null;
45
71
  }
46
72
 
73
+ /** @typedef {import('iso-language-codes').Code} Code */
74
+
47
75
  /**
48
- * Searches for the given long in the given columns.
49
76
  * @param {string} language
50
- * @param {Array<keyof import('iso-language-codes').Code>} columnsToSearch
51
- * @param {boolean?} getName
52
- * @return {ISO6391?}
77
+ * @param {Array<'iso639_1' | 'iso639_2T' | 'iso639_2B' | 'nativeName' | 'name'>} fields
78
+ * @returns {Code | null}
53
79
  */
54
- function searchForISO6391(language, columnsToSearch, getName) {
80
+ function findLanguage(language, fields) {
81
+ if (!language) return null;
82
+ language = language.toLowerCase();
55
83
  for (const lang of langs) {
56
- for (const colName of columnsToSearch) {
57
- if (lang[colName].split(', ').map(x => x.toLowerCase()).indexOf(language) != -1) {
58
- if (getName) return `${lang.nativeName.split(", ")[0]} (${lang.iso639_1})`;
59
- return lang.iso639_1;
84
+ for (const codeType of fields) {
85
+ if (lang[codeType]?.toLowerCase().split(", ").includes(language)) {
86
+ return lang;
60
87
  }
61
88
  }
62
89
  }
63
- if (specialLangs[language]) return `${specialLangs[language]} (${language})`;
64
90
  return null;
65
91
  }
66
92