@internetarchive/bookreader 5.0.0-24-sortingstate-11 → 5.0.0-26

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. package/BookReader/BookReader.css +4 -0
  2. package/BookReader/BookReader.js +2 -32145
  3. package/BookReader/BookReader.js.map +1 -1
  4. package/BookReader/bookreader-component-bundle.js +1286 -11256
  5. package/BookReader/bookreader-component-bundle.js.map +1 -1
  6. package/BookReader/icons/1up.svg +1 -12
  7. package/BookReader/icons/2up.svg +1 -15
  8. package/BookReader/icons/advance.svg +3 -26
  9. package/BookReader/icons/chevron-right.svg +1 -1
  10. package/BookReader/icons/close-circle-dark.svg +1 -1
  11. package/BookReader/icons/close-circle.svg +1 -1
  12. package/BookReader/icons/fullscreen.svg +1 -17
  13. package/BookReader/icons/fullscreen_exit.svg +1 -17
  14. package/BookReader/icons/hamburger.svg +1 -15
  15. package/BookReader/icons/left-arrow.svg +1 -12
  16. package/BookReader/icons/magnify-minus.svg +1 -16
  17. package/BookReader/icons/magnify-plus.svg +1 -17
  18. package/BookReader/icons/magnify.svg +1 -15
  19. package/BookReader/icons/pause.svg +1 -23
  20. package/BookReader/icons/play.svg +1 -22
  21. package/BookReader/icons/playback-speed.svg +1 -34
  22. package/BookReader/icons/read-aloud.svg +1 -22
  23. package/BookReader/icons/review.svg +3 -22
  24. package/BookReader/icons/thumbnails.svg +1 -17
  25. package/BookReader/icons/voice.svg +1 -1
  26. package/BookReader/icons/volume-full.svg +1 -22
  27. package/BookReader/images/BRicons.svg +5 -94
  28. package/BookReader/images/books_graphic.svg +1 -177
  29. package/BookReader/images/icon_book.svg +1 -12
  30. package/BookReader/images/icon_bookmark.svg +1 -12
  31. package/BookReader/images/icon_gear.svg +1 -14
  32. package/BookReader/images/icon_hamburger.svg +1 -20
  33. package/BookReader/images/icon_home.svg +1 -21
  34. package/BookReader/images/icon_info.svg +1 -11
  35. package/BookReader/images/icon_one_page.svg +1 -8
  36. package/BookReader/images/icon_pause.svg +1 -1
  37. package/BookReader/images/icon_play.svg +1 -1
  38. package/BookReader/images/icon_playback-rate.svg +1 -15
  39. package/BookReader/images/icon_search_button.svg +1 -8
  40. package/BookReader/images/icon_share.svg +1 -9
  41. package/BookReader/images/icon_skip-ahead.svg +1 -6
  42. package/BookReader/images/icon_skip-back.svg +2 -13
  43. package/BookReader/images/icon_speaker.svg +1 -18
  44. package/BookReader/images/icon_speaker_open.svg +1 -10
  45. package/BookReader/images/icon_thumbnails.svg +1 -12
  46. package/BookReader/images/icon_toc.svg +1 -5
  47. package/BookReader/images/icon_two_pages.svg +1 -9
  48. package/BookReader/images/marker_chap-off.svg +1 -11
  49. package/BookReader/images/marker_chap-on.svg +1 -11
  50. package/BookReader/images/marker_srch-on.svg +1 -11
  51. package/BookReader/jquery-1.10.1.js +2 -108
  52. package/BookReader/plugins/plugin.archive_analytics.js +1 -170
  53. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  54. package/BookReader/plugins/plugin.autoplay.js +1 -163
  55. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  56. package/BookReader/plugins/plugin.chapters.js +1 -333
  57. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  58. package/BookReader/plugins/plugin.iframe.js +1 -72
  59. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  60. package/BookReader/plugins/plugin.mobile_nav.js +1 -332
  61. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  62. package/BookReader/plugins/plugin.resume.js +1 -241
  63. package/BookReader/plugins/plugin.resume.js.map +1 -1
  64. package/BookReader/plugins/plugin.search.js +1 -1263
  65. package/BookReader/plugins/plugin.search.js.map +1 -1
  66. package/BookReader/plugins/plugin.text_selection.js +1 -839
  67. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  68. package/BookReader/plugins/plugin.tts.js +2 -9114
  69. package/BookReader/plugins/plugin.tts.js.map +1 -1
  70. package/BookReader/plugins/plugin.url.js +1 -768
  71. package/BookReader/plugins/plugin.url.js.map +1 -1
  72. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -326
  73. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  74. package/BookReader/webcomponents-bundle.js +2 -411
  75. package/BookReader/webcomponents-bundle.js.map +1 -1
  76. package/BookReaderDemo/demo-internetarchive.html +86 -4
  77. package/CHANGELOG.md +5 -0
  78. package/package.json +1 -1
  79. package/src/BookNavigator/bookmarks/ia-bookmarks.js +1 -0
  80. package/src/BookNavigator/volumes/volumes-provider.js +9 -40
  81. package/src/BookReader.js +102 -64
  82. package/src/css/_controls.scss +4 -0
  83. package/src/plugins/plugin.url.js +2 -218
  84. package/tests/jest/BookReader.keyboard.test.js +190 -0
  85. package/tests/jest/plugins/plugin.url.test.js +1 -151
  86. package/.nvmrc +0 -1
@@ -29,6 +29,10 @@
29
29
  <script type="module" src="../BookReader/bookreader-component-bundle.js"></script>
30
30
 
31
31
  <link rel="stylesheet" href="BookReaderDemo.css"/>
32
+
33
+ <!-- IA scripts -->
34
+ <script src="https://archive.org/bookreader/BookReaderJSIA.js"></script>
35
+
32
36
  </head>
33
37
 
34
38
  <body>
@@ -59,16 +63,94 @@
59
63
  <script id="pageUrl" type="text/javascript"></script>
60
64
 
61
65
  <script>
62
- var ocaid = location.href.match(/ocaid=([^&#]+)/i)[1];
66
+ // gather params here
67
+ const urlParams = new URLSearchParams(window.location.search);
68
+
69
+ const ocaid = urlParams.get('ocaid');
70
+ const openFullImmersionTheater = urlParams.get('view') === 'theater';
71
+ const ui = urlParams.get('ui');
72
+ const autoflip = urlParams.get('autoflip');
73
+ const searchTerm = urlParams.get('q');
63
74
 
64
75
  // Override options coming from IA
65
76
  BookReader.optionOverrides.imagesBaseURL = '/BookReader/images/';
66
77
 
78
+ const initializeBookReader = (brManifest) => {
79
+ console.log('initializeBookReader', brManifest);
80
+ const br = new BookReader();
81
+ const iaBookManifestUrl = '';
82
+
83
+ const customAutoflipParams = {
84
+ autoflip: !!autoflip,
85
+ flipSpeed: urlParams.flipSpeed || 2000,
86
+ flipDelay: urlParams.flipDelay || 5000
87
+ };
88
+
89
+ const options = {
90
+ el: '#BookReader',
91
+ /* Url plugin - IA uses History mode for URL */
92
+ // commenting these out as demo uses hash mode
93
+ // keeping them here for reference
94
+ // urlHistoryBasePath: `/details/{$ocaid}/`,
95
+ // resumeCookiePath: `/details/{$ocaid}/`,
96
+ // urlMode: 'history',
97
+ // Only reflect these params onto the URL
98
+ // urlTrackedParams: ['page', 'search', 'mode'],
99
+ /* End url plugin */
100
+ enableBookTitleLink: false,
101
+ bookUrlText: null,
102
+ startFullscreen: urlParams.view === 'theater',
103
+ initialSearchTerm: searchTerm ? searchTerm : '',
104
+ // leaving this option commented out bc we change given user agent on archive.org
105
+ // onePage: { autofit: <?=json_encode($this->ios ? 'width' : 'auto')?> },
106
+ showToolbar: false,
107
+ /* Multiple volumes */
108
+ // To show multiple volumes:
109
+ enableMultipleBooks: false, // turn this on
110
+ multipleBooksList: [], // populate this // TODO: get sample blob and tie into demo
111
+ /* End multiple volumes */
112
+ };
113
+
114
+ // we want to show item as embedded when ?ui=embed is in URI
115
+ if (ui === 'embed') {
116
+ options.mode = 1;
117
+ options.ui = 'embed';
118
+ }
119
+
120
+ // we expect this at the global level
121
+ BookReaderJSIAinit(brManifest.data, { ...options });
122
+
123
+ if (customAutoflipParams.autoflip) {
124
+ br.autoToggle(customAutoflipParams);
125
+ }
126
+ }
127
+
128
+ const fetchBookManifestAndInitializeBookreader = async (iaMetadata) => {
129
+ const {
130
+ metadata: {
131
+ identifier
132
+ },
133
+ } = iaMetadata;
134
+
135
+ const locator =`https://archive.org/bookreader/BookReaderJSLocate.php?format=json&subPrefix=&id=${identifier}`;
136
+ // Todo: move from `locator` to create `iaManifestUrl` url from `iaMetadata`
137
+ // so we can support multiple volumes
138
+ // const iaManifestUrl = `https://${server}/BookReader/BookReaderJSIA.php?format=jsonp&itemPath=${dir}&id=${identifier}`;
139
+
140
+ const manifest = await fetch(locator)
141
+ .then(response => response.json())
142
+
143
+ initializeBookReader(manifest);
144
+ }
145
+
67
146
  // Temp; Circumvent bug in BookReaderJSIA code
68
147
  window.Sentry = null;
69
-
70
- var script_url = 'https://archive.org/bookreader/BookReaderJSLocate.php?subPrefix=&id=' + ocaid;
71
- document.getElementById('pageUrl').src = script_url;
148
+ window.logError = function(e) {
149
+ console.error(e);
150
+ };
151
+ fetch(`https://archive.org/metadata/${ocaid}`)
152
+ .then(response => response.json())
153
+ .then(iaMetadata => fetchBookManifestAndInitializeBookreader(iaMetadata));
72
154
  </script>
73
155
 
74
156
  </body>
package/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 5.0.0-26
2
+ Fix: read aloud play/pause button @nsharma123
3
+ Dev: strict keyboard shortcuts @mc2
4
+ Dev: update IA demo page @iisa
5
+
1
6
  # 5.0.0-24
2
7
  Fix: book-nav side panel zoom out @mc2
3
8
  Dev: refactor zoom code @mc2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/bookreader",
3
- "version": "5.0.0-24-sortingstate-11",
3
+ "version": "5.0.0-26",
4
4
  "description": "The Internet Archive BookReader.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -463,6 +463,7 @@ class IABookmarks extends LitElement {
463
463
  return html`
464
464
  <button
465
465
  class="ia-button primary"
466
+ tabindex="-1"
466
467
  ?disabled=${this.shouldEnableAddBookmarkButton}
467
468
  @click=${this.addBookmark}>
468
469
  Add bookmark
@@ -7,16 +7,8 @@ import volumesIcon from '../assets/icon_volumes.js';
7
7
 
8
8
  import './volumes.js';
9
9
 
10
- const sortType = {
11
- title_asc: 'title_asc',
12
- title_desc: 'title_desc',
13
- default: 'default'
14
- };
15
10
  export default class VolumesProvider {
16
11
 
17
- /**
18
- * @param {import('../../BookReader').default} bookreader
19
- */
20
12
  constructor(baseHost, bookreader, optionChange) {
21
13
  this.optionChange = optionChange;
22
14
  this.component = document.createElement("viewable-files");
@@ -25,9 +17,6 @@ export default class VolumesProvider {
25
17
  this.viewableFiles = Object.keys(files).map(item => files[item]);
26
18
  this.volumeCount = Object.keys(files).length;
27
19
 
28
- /** @type {import('../../BookReader').default} */
29
- this.bookreader = bookreader;
30
-
31
20
  this.component.subPrefix = bookreader.options.subPrefix || "";
32
21
  this.component.hostUrl = baseHost;
33
22
  this.component.viewableFiles = this.viewableFiles;
@@ -36,28 +25,20 @@ export default class VolumesProvider {
36
25
  this.label = `Viewable files (${this.volumeCount})`;
37
26
  this.icon = html`${volumesIcon}`;
38
27
 
39
- // get sort state from query param
40
- this.bookreader.urlPlugin.pullFromAddressBar();
41
- const urlSortValue = this.bookreader.urlPlugin.getUrlParam('sort');
42
- console.log('urlSortValue: ', urlSortValue);
43
- if (urlSortValue === sortType.title_asc || urlSortValue === sortType.title_desc) {
44
- this.sortOrderBy = urlSortValue;
45
- } else {
46
- this.sortOrderBy = sortType.default;
47
- }
48
- this.sortVolumes(this.sortOrderBy);
28
+ this.sortOrderBy = "orig_sort";
29
+ this.sortVolumes("orig_sort");
49
30
  }
50
31
 
51
32
  get sortButton() {
52
33
  const sortIcons = {
53
- default: html`
34
+ orig_sort: html`
54
35
  <button class="sort-by neutral-icon" aria-label="Sort volumes in initial order" @click=${() => this.sortVolumes("title_asc")}>${sortNeutralIcon}</button>
55
36
  `,
56
37
  title_asc: html`
57
38
  <button class="sort-by asc-icon" aria-label="Sort volumes in ascending order" @click=${() => this.sortVolumes("title_desc")}>${sortAscIcon}</button>
58
39
  `,
59
40
  title_desc: html`
60
- <button class="sort-by desc-icon" aria-label="Sort volumes in descending order" @click=${() => this.sortVolumes("default")}>${sortDescIcon}</button>
41
+ <button class="sort-by desc-icon" aria-label="Sort volumes in descending order" @click=${() => this.sortVolumes("orig_sort")}>${sortDescIcon}</button>
61
42
  `,
62
43
  };
63
44
 
@@ -65,40 +46,28 @@ export default class VolumesProvider {
65
46
  }
66
47
 
67
48
  /**
68
- * @param {'default' | 'title_asc' | 'title_desc'} sortByType
49
+ * @param {'orig_sort' | 'title_asc' | 'title_desc'} sortByType
69
50
  */
70
51
  sortVolumes(sortByType) {
71
52
  let sortedFiles = [];
72
53
 
73
54
  const files = this.viewableFiles;
74
55
  sortedFiles = files.sort((a, b) => {
75
- if (sortByType === sortType.title_asc) return a.title.localeCompare(b.title);
76
- else if (sortByType === sortType.title_desc) return b.title.localeCompare(a.title);
77
- else return a.orig_sort - b.orig_sort;
56
+ if (sortByType === 'orig_sort') return a.orig_sort - b.orig_sort;
57
+ else if (sortByType === 'title_asc') return a.title.localeCompare(b.title);
58
+ else return b.title.localeCompare(a.title);
78
59
  });
79
60
 
80
61
  this.sortOrderBy = sortByType;
81
62
  this.component.viewableFiles = [...sortedFiles];
82
63
  this.actionButton = this.sortButton;
83
-
84
- if (this.sortOrderBy !== sortType.default) {
85
- this.bookreader.urlPlugin.setUrlParam('sort', sortByType);
86
- } else {
87
- this.bookreader.urlPlugin.removeUrlParam('sort');
88
- }
89
-
90
- const urlSchema = this.bookreader.urlPlugin.urlSchema;
91
- const urlState = this.bookreader.urlPlugin.urlState;
92
- this.bookreader.urlPlugin.urlStateToUrlString(urlSchema, urlState);
93
- this.bookreader.urlPlugin.pushToAddressBar();
94
-
95
64
  this.optionChange(this.bookreader);
96
65
 
97
66
  this.multipleFilesClicked(sortByType);
98
67
  }
99
68
 
100
69
  /**
101
- * @param {'default' | 'title_asc' | 'title_desc'} orderBy
70
+ * @param {'orig_sort' | 'title_asc' | 'title_desc'} orderBy
102
71
  */
103
72
  multipleFilesClicked(orderBy) {
104
73
  if (!window.archive_analytics) {
package/src/BookReader.js CHANGED
@@ -264,6 +264,15 @@ BookReader.prototype.setup = function(options) {
264
264
  useSrcSet: this.options.useSrcSet,
265
265
  reduceSet: this.reduceSet,
266
266
  });
267
+
268
+ /**
269
+ * Flag if BookReader has "focus" for keyboard shortcuts
270
+ * Initially true, set to false when:
271
+ * - BookReader scrolled out of view
272
+ * Set to true when:
273
+ * - BookReader scrolled into view
274
+ */
275
+ this.hasKeyFocus = true;
267
276
  };
268
277
 
269
278
  /**
@@ -662,87 +671,116 @@ BookReader.prototype.resize = function() {
662
671
  };
663
672
 
664
673
  /**
665
- * Binds keyboard event listeners
674
+ * Binds keyboard and keyboard focus event listeners
666
675
  */
667
- BookReader.prototype.setupKeyListeners = function() {
668
- var self = this;
676
+ BookReader.prototype.setupKeyListeners = function () {
669
677
 
670
- var KEY_PGUP = 33;
671
- var KEY_PGDOWN = 34;
672
- var KEY_END = 35;
673
- var KEY_HOME = 36;
674
-
675
- var KEY_LEFT = 37;
676
- var KEY_UP = 38;
677
- var KEY_RIGHT = 39;
678
- var KEY_DOWN = 40;
679
- // The minus(-) and equal(=) keys have different mappings for different browsers
680
- var KEY_MINUS = 189; // Chrome
681
- var KEY_MINUS_F = 173; // Firefox
682
- var KEY_NUMPAD_SUBTRACT = 109;
683
- var KEY_EQUAL = 187; // Chrome
684
- var KEY_EQUAL_F = 61; // Firefox
685
- var KEY_NUMPAD_ADD = 107;
686
-
687
- // We use document here instead of window to avoid a bug in jQuery on IE7
688
- $(document).on("keydown", function(e) {
689
-
690
- // Keyboard navigation
691
- switch (e.keyCode) {
692
- case KEY_PGUP:
693
- case KEY_UP:
694
- // In 1up mode page scrolling is handled by browser
695
- if (!utils.isInputActive() && self.constMode2up == self.mode) {
696
- e.preventDefault();
697
- self.prev();
698
- }
678
+ // Keyboard focus by BookReader in viewport
679
+ //
680
+ // Intersection observer and callback sets BookReader keyboard
681
+ // "focus" flag off when the BookReader is not in the viewport.
682
+ if (window.IntersectionObserver) {
683
+ const observer = new IntersectionObserver((entries) => {
684
+ entries.forEach((entry) => {
685
+ if (entry.intersectionRatio === 0) {
686
+ this.hasKeyFocus = false;
687
+ } else {
688
+ this.hasKeyFocus = true;
689
+ }
690
+ });
691
+ }, {
692
+ root: null,
693
+ rootMargin: '0px',
694
+ threshold: [0, 0.05, 1],
695
+ });
696
+ observer.observe(this.refs.$br[0]);
697
+ }
698
+
699
+ // Keyboard listeners
700
+ document.addEventListener('keydown', (e) => {
701
+
702
+ // Ignore if BookReader "focus" flag not set
703
+ if (!this.hasKeyFocus) {
704
+ return;
705
+ }
706
+
707
+ // Ignore if modifiers are active.
708
+ if (e.getModifierState('Control') ||
709
+ e.getModifierState('Alt') ||
710
+ e.getModifierState('Meta') ||
711
+ e.getModifierState('Win') /* hack for IE */) {
712
+ return;
713
+ }
714
+
715
+ // Ignore in input elements
716
+ if (utils.isInputActive()) {
717
+ return;
718
+ }
719
+
720
+ // KeyboardEvent code values:
721
+ // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values
722
+ switch (e.key) {
723
+
724
+ // Page navigation
725
+ case "Home":
726
+ e.preventDefault();
727
+ this.first();
699
728
  break;
700
- case KEY_DOWN:
701
- case KEY_PGDOWN:
702
- if (!utils.isInputActive() && self.constMode2up == self.mode) {
703
- e.preventDefault();
704
- self.next();
705
- }
729
+ case "End":
730
+ e.preventDefault();
731
+ this.last();
706
732
  break;
707
- case KEY_END:
708
- if (!utils.isInputActive()) {
733
+ case "ArrowDown":
734
+ case "PageDown":
735
+ case "Down": // hack for IE and old Gecko
736
+ // In 1up and thumb mode page scrolling handled by browser
737
+ if (this.constMode2up === this.mode) {
709
738
  e.preventDefault();
710
- self.last();
739
+ this.next();
711
740
  }
712
741
  break;
713
- case KEY_HOME:
714
- if (!utils.isInputActive()) {
742
+ case "ArrowUp":
743
+ case "PageUp":
744
+ case "Up": // hack for IE and old Gecko
745
+ // In 1up and thumb mode page scrolling handled by browser
746
+ if (this.constMode2up === this.mode) {
715
747
  e.preventDefault();
716
- self.first();
748
+ this.prev();
717
749
  }
718
750
  break;
719
- case KEY_LEFT:
720
- if (!utils.isInputActive() && self.constModeThumb != self.mode) {
751
+ case "ArrowLeft":
752
+ case "Left": // hack for IE and old Gecko
753
+ // No y-scrolling in thumb mode
754
+ if (this.constModeThumb != this.mode) {
721
755
  e.preventDefault();
722
- self.left();
756
+ this.left();
723
757
  }
724
758
  break;
725
- case KEY_RIGHT:
726
- if (!utils.isInputActive() && self.constModeThumb != self.mode) {
759
+ case "ArrowRight":
760
+ case "Right": // hack for IE and old Gecko
761
+ // No y-scrolling in thumb mode
762
+ if (this.constModeThumb != this.mode) {
727
763
  e.preventDefault();
728
- self.right();
764
+ this.right();
729
765
  }
730
766
  break;
731
- case KEY_MINUS:
732
- case KEY_MINUS_F:
733
- case KEY_NUMPAD_SUBTRACT:
734
- if (!utils.isInputActive()) {
735
- e.preventDefault();
736
- self.zoom(-1);
737
- }
767
+ // Zoom
768
+ case '-':
769
+ case 'Subtract':
770
+ e.preventDefault();
771
+ this.zoom(-1);
738
772
  break;
739
- case KEY_EQUAL:
740
- case KEY_EQUAL_F:
741
- case KEY_NUMPAD_ADD:
742
- if (!utils.isInputActive()) {
743
- e.preventDefault();
744
- self.zoom(+1);
745
- }
773
+ case '+':
774
+ case '=':
775
+ case 'Add':
776
+ e.preventDefault();
777
+ this.zoom(1);
778
+ break;
779
+ // Fullscreen
780
+ case 'F':
781
+ case 'f':
782
+ e.preventDefault();
783
+ this.toggleFullscreen();
746
784
  break;
747
785
  }
748
786
  });
@@ -109,6 +109,10 @@
109
109
  &.visible {
110
110
  display: flex;
111
111
  animation: slideUp 0.2s;
112
+ button {
113
+ width: unset;
114
+ height: unset;
115
+ }
112
116
  }
113
117
 
114
118
  li {