@internetarchive/bookreader 5.0.0-4 → 5.0.0-40-a1

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 (228) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +75 -4
  3. package/.github/workflows/npm-publish.yml +2 -16
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +83 -323
  6. package/BookReader/BookReader.js +1 -1
  7. package/BookReader/BookReader.js.LICENSE.txt +24 -0
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1623 -0
  10. package/BookReader/{bookreader-component-bundle.js.LICENSE.txt → ia-bookreader-bundle.js.LICENSE.txt} +14 -10
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/close-circle-dark.svg +1 -0
  13. package/BookReader/icons/magnify-minus.svg +1 -1
  14. package/BookReader/icons/magnify-plus.svg +1 -1
  15. package/BookReader/icons/voice.svg +1 -0
  16. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  17. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  18. package/BookReader/plugins/plugin.autoplay.js +1 -1
  19. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  20. package/BookReader/plugins/plugin.chapters.js +1 -1
  21. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  22. package/BookReader/plugins/plugin.iframe.js +1 -1
  23. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  24. package/BookReader/plugins/plugin.mobile_nav.js +1 -1
  25. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  26. package/BookReader/plugins/plugin.resume.js +1 -1
  27. package/BookReader/plugins/plugin.resume.js.map +1 -1
  28. package/BookReader/plugins/plugin.search.js +1 -1
  29. package/BookReader/plugins/plugin.search.js.map +1 -1
  30. package/BookReader/plugins/plugin.text_selection.js +1 -1
  31. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  32. package/BookReader/plugins/plugin.tts.js +1 -1
  33. package/BookReader/plugins/plugin.tts.js.map +1 -1
  34. package/BookReader/plugins/plugin.url.js +1 -1
  35. package/BookReader/plugins/plugin.url.js.map +1 -1
  36. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  37. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  38. package/BookReader/webcomponents-bundle.js +3 -0
  39. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  40. package/BookReader/webcomponents-bundle.js.map +1 -0
  41. package/BookReaderDemo/BookReaderDemo.css +14 -1
  42. package/BookReaderDemo/IADemoBr.js +120 -0
  43. package/BookReaderDemo/demo-advanced.html +1 -1
  44. package/BookReaderDemo/demo-autoplay.html +1 -0
  45. package/BookReaderDemo/demo-embed-iframe-src.html +1 -0
  46. package/BookReaderDemo/demo-fullscreen-mobile.html +1 -0
  47. package/BookReaderDemo/demo-fullscreen.html +1 -0
  48. package/BookReaderDemo/demo-iiif.html +1 -0
  49. package/BookReaderDemo/demo-internetarchive.html +74 -17
  50. package/BookReaderDemo/demo-multiple.html +1 -0
  51. package/BookReaderDemo/demo-preview-pages.html +1 -0
  52. package/BookReaderDemo/demo-simple.html +1 -0
  53. package/BookReaderDemo/demo-vendor-fullscreen.html +1 -0
  54. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  55. package/BookReaderDemo/immersion-1up.html +1 -0
  56. package/BookReaderDemo/immersion-mode.html +1 -0
  57. package/BookReaderDemo/toggle_controls.html +1 -0
  58. package/BookReaderDemo/view_mode.html +1 -0
  59. package/BookReaderDemo/viewmode-cycle.html +1 -2
  60. package/CHANGELOG.md +166 -0
  61. package/README.md +14 -1
  62. package/babel.config.js +18 -0
  63. package/codecov.yml +6 -0
  64. package/index.html +3 -0
  65. package/jsconfig.json +19 -0
  66. package/package.json +62 -47
  67. package/renovate.json +43 -0
  68. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  69. package/src/BookNavigator/assets/button-base.js +9 -2
  70. package/src/BookNavigator/assets/ia-logo.js +17 -0
  71. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  72. package/src/BookNavigator/assets/icon_close.js +1 -1
  73. package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
  74. package/src/BookNavigator/assets/icon_sort_desc.js +5 -0
  75. package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
  76. package/src/BookNavigator/assets/icon_volumes.js +11 -0
  77. package/src/BookNavigator/book-navigator.js +556 -0
  78. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  79. package/src/BookNavigator/bookmarks/bookmark-edit.js +4 -4
  80. package/src/BookNavigator/bookmarks/bookmarks-list.js +3 -3
  81. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  82. package/src/BookNavigator/bookmarks/bookmarks-provider.js +23 -12
  83. package/src/BookNavigator/bookmarks/ia-bookmarks.js +98 -62
  84. package/src/BookNavigator/delete-modal-actions.js +1 -1
  85. package/src/BookNavigator/downloads/downloads-provider.js +23 -17
  86. package/src/BookNavigator/downloads/downloads.js +17 -25
  87. package/src/BookNavigator/search/a-search-result.js +3 -3
  88. package/src/BookNavigator/search/search-provider.js +57 -24
  89. package/src/BookNavigator/search/search-results.js +8 -20
  90. package/src/BookNavigator/sharing.js +27 -0
  91. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -13
  92. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +4 -3
  93. package/src/BookNavigator/volumes/volumes-provider.js +114 -0
  94. package/src/BookNavigator/volumes/volumes.js +188 -0
  95. package/src/BookReader/DebugConsole.js +3 -3
  96. package/src/BookReader/DragScrollable.js +233 -0
  97. package/src/BookReader/Mode1Up.js +51 -351
  98. package/src/BookReader/Mode1UpLit.js +441 -0
  99. package/src/BookReader/Mode2Up.js +104 -71
  100. package/src/BookReader/ModeSmoothZoom.js +179 -0
  101. package/src/BookReader/ModeThumb.js +16 -8
  102. package/src/BookReader/Navbar/Navbar.js +2 -31
  103. package/src/BookReader/PageContainer.js +57 -6
  104. package/src/BookReader/ReduceSet.js +1 -1
  105. package/src/BookReader/Toolbar/Toolbar.js +7 -7
  106. package/src/BookReader/options.js +10 -0
  107. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  108. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  109. package/src/BookReader/utils.js +68 -13
  110. package/src/BookReader.js +375 -289
  111. package/src/assets/icons/close-circle-dark.svg +1 -0
  112. package/src/assets/icons/magnify-minus.svg +3 -7
  113. package/src/assets/icons/magnify-plus.svg +3 -7
  114. package/src/assets/icons/voice.svg +1 -0
  115. package/src/css/BookReader.scss +0 -12
  116. package/src/css/_BRComponent.scss +1 -1
  117. package/src/css/_BRmain.scss +19 -24
  118. package/src/css/_BRnav.scss +4 -26
  119. package/src/css/_BRpages.scss +35 -0
  120. package/src/css/_BRsearch.scss +11 -215
  121. package/src/css/_TextSelection.scss +14 -17
  122. package/src/css/_colorbox.scss +2 -2
  123. package/src/css/_controls.scss +16 -3
  124. package/src/css/_icons.scss +6 -0
  125. package/src/ia-bookreader/ia-bookreader.js +224 -0
  126. package/src/plugins/plugin.chapters.js +26 -33
  127. package/src/plugins/plugin.mobile_nav.js +11 -10
  128. package/src/plugins/plugin.resume.js +3 -3
  129. package/src/plugins/plugin.text_selection.js +26 -39
  130. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  131. package/src/plugins/search/plugin.search.js +106 -107
  132. package/src/plugins/search/view.js +50 -163
  133. package/src/plugins/tts/AbstractTTSEngine.js +46 -37
  134. package/src/plugins/tts/FestivalTTSEngine.js +12 -13
  135. package/src/plugins/tts/PageChunk.js +15 -21
  136. package/src/plugins/tts/PageChunkIterator.js +8 -12
  137. package/src/plugins/tts/WebTTSEngine.js +64 -68
  138. package/src/plugins/tts/plugin.tts.js +79 -108
  139. package/src/plugins/url/UrlPlugin.js +184 -0
  140. package/src/plugins/{plugin.url.js → url/plugin.url.js} +28 -6
  141. package/tests/e2e/README.md +37 -0
  142. package/tests/e2e/autoplay.test.js +2 -2
  143. package/tests/e2e/base.test.js +7 -7
  144. package/tests/e2e/helpers/base.js +8 -3
  145. package/tests/e2e/helpers/debug.js +1 -1
  146. package/tests/e2e/helpers/desktopSearch.js +14 -13
  147. package/tests/e2e/helpers/mobileSearch.js +3 -3
  148. package/tests/e2e/helpers/params.js +17 -0
  149. package/tests/e2e/models/Navigation.js +12 -3
  150. package/tests/e2e/rightToLeft.test.js +4 -5
  151. package/tests/e2e/viewmode.test.js +38 -33
  152. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +3 -3
  153. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +176 -0
  154. package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
  155. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  156. package/tests/jest/BookReader/Mode1UpLit.test.js +88 -0
  157. package/tests/{BookReader → jest/BookReader}/Mode2Up.test.js +5 -7
  158. package/tests/jest/BookReader/ModeSmoothZoom.test.js +149 -0
  159. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  160. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +7 -7
  161. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +79 -6
  162. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  163. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  164. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  165. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  166. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  167. package/tests/jest/BookReader/utils.test.js +136 -0
  168. package/tests/jest/BookReader.keyboard.test.js +190 -0
  169. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  170. package/tests/{BookReader.test.js → jest/BookReader.test.js} +20 -4
  171. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  172. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +2 -2
  173. package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +8 -8
  174. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  175. package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
  176. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  177. package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +39 -47
  178. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  179. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +24 -25
  180. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +6 -6
  181. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +6 -6
  182. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  183. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  184. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  185. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +1 -1
  186. package/tests/{plugins → jest/plugins}/tts/utils.test.js +3 -3
  187. package/tests/jest/plugins/url/UrlPlugin.test.js +190 -0
  188. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +33 -14
  189. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  190. package/tests/{util → jest/util}/docCookies.test.js +1 -1
  191. package/tests/{util → jest/util}/strings.test.js +1 -1
  192. package/tests/{utils.js → jest/utils.js} +38 -0
  193. package/tests/karma/BookNavigator/book-navigator.test.js +501 -0
  194. package/tests/karma/BookNavigator/bookmarks/bookmark-button.test.js +44 -0
  195. package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +1 -3
  196. package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +3 -4
  197. package/tests/karma/BookNavigator/bookmarks/ia-bookmarks.test.js +57 -0
  198. package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +67 -0
  199. package/tests/karma/BookNavigator/downloads/downloads.test.js +54 -0
  200. package/tests/karma/BookNavigator/search/search-provider.test.js +123 -0
  201. package/tests/karma/BookNavigator/{search-results.test.js → search/search-results.test.js} +1 -4
  202. package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +49 -0
  203. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -2
  204. package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +184 -0
  205. package/tests/karma/BookNavigator/volumes/volumes.test.js +98 -0
  206. package/webpack.config.js +10 -4
  207. package/.babelrc +0 -12
  208. package/.dependabot/config.yml +0 -6
  209. package/.testcaferc.json +0 -5
  210. package/BookReader/bookreader-component-bundle.js +0 -1450
  211. package/BookReader/bookreader-component-bundle.js.map +0 -1
  212. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  213. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  214. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  215. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  216. package/src/BookNavigator/BookModel.js +0 -14
  217. package/src/BookNavigator/BookNavigator.js +0 -435
  218. package/src/BookNavigator/assets/book-loader.js +0 -27
  219. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  220. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  221. package/src/ItemNavigator/ItemNavigator.js +0 -372
  222. package/src/ItemNavigator/providers/sharing.js +0 -29
  223. package/src/dragscrollable-br.js +0 -261
  224. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  225. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  226. package/tests/BookReader/Mode1Up.test.js +0 -164
  227. package/tests/BookReader/utils.test.js +0 -109
  228. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
@@ -1,5 +1,6 @@
1
- import { render, nothing } from 'lit-html';
2
- import { LitElement, html, css } from 'lit-element';
1
+ import { LitElement, html, css, render } from 'lit';
2
+ // eslint-disable-next-line no-unused-vars
3
+ import { ModalConfig, ModalManager } from '@internetarchive/modal-manager';
3
4
  import buttonStyles from '../assets/button-base.js';
4
5
  import './bookmarks-loginCTA.js';
5
6
 
@@ -57,9 +58,11 @@ class IABookmarks extends LitElement {
57
58
  activeBookmarkID: { type: String },
58
59
  bookmarks: { type: Array },
59
60
  bookreader: { type: Object },
60
- options: { type: Object },
61
61
  displayMode: { type: String },
62
62
  editedBookmark: { type: Object },
63
+ deleteModalConfig: { type: Object},
64
+ modal: { attribute: false },
65
+ loginOptions: { type: Object, attribute: false }
63
66
  };
64
67
  }
65
68
 
@@ -92,7 +95,12 @@ class IABookmarks extends LitElement {
92
95
  this.bookmarks = [];
93
96
  this.bookreader = {};
94
97
  this.editedBookmark = {};
95
- this.options = {};
98
+ /** @type {ModalManager} */
99
+ this.modal = undefined;
100
+ this.loginOptions = {
101
+ loginClicked: () => {},
102
+ loginUrl: '',
103
+ };
96
104
  /**
97
105
  * Toggles display to either bookmarks or login cta
98
106
  * @param {('bookmarks'|'login')} displayMode
@@ -113,21 +121,45 @@ class IABookmarks extends LitElement {
113
121
  // eslint-disable-next-line
114
122
  this.defaultColor = this.bookmarkColors[0];
115
123
  this.api = api;
124
+ this.deleteModalConfig = new ModalConfig({
125
+ title: 'Delete Bookmark',
126
+ headline: 'This bookmark contains a note. Deleting it will permanently delete the note. Are you sure?',
127
+ headerColor: '#194880',
128
+ });
116
129
  }
117
130
 
118
- updated() {
131
+ updated(changed) {
132
+ if (changed.has('displayMode')) {
133
+ this.updateDisplay();
134
+ }
135
+
119
136
  this.emitBookmarksChanged();
120
137
  }
121
138
 
122
139
  setup() {
123
140
  this.api.identifier = this.bookreader.bookId;
124
- this.fetchBookmarks()
125
- .then(() => this.initializeBookmarks())
126
- .catch((err) => this.displayMode = 'login');
141
+ if (this.displayMode === 'login') {
142
+ return;
143
+ }
144
+ this.fetchUserBookmarks();
145
+ this.setBREventListeners();
127
146
  }
128
147
 
129
- initializeBookmarks() {
130
- this.displayMode = 'bookmarks';
148
+ updateDisplay() {
149
+ if (this.displayMode === 'bookmarks') {
150
+ this.fetchUserBookmarks();
151
+ }
152
+ }
153
+
154
+ async fetchUserBookmarks() {
155
+ if (!this.api.identifier) {
156
+ return;
157
+ }
158
+ await this.fetchBookmarks();
159
+ this.initializeBookmarks();
160
+ }
161
+
162
+ setBREventListeners() {
131
163
  ['3PageViewSelected'].forEach((event) => {
132
164
  window.addEventListener(`BookReader:${event}`, (e) => {
133
165
  setTimeout(() => {
@@ -147,12 +179,12 @@ class IABookmarks extends LitElement {
147
179
  });
148
180
  ['zoomOut', 'zoomIn', 'resize'].forEach((event) => {
149
181
  window.addEventListener(`BookReader:${event}`, () => {
150
- if (this.bookreader.mode === this.bookreader.constModeThumb) {
151
- this.renderBookmarkButtons();
152
- }
182
+ this.renderBookmarkButtons();
153
183
  });
154
184
  });
185
+ }
155
186
 
187
+ initializeBookmarks() {
156
188
  this.renderBookmarkButtons();
157
189
  this.markActiveBookmark(true);
158
190
  this.emitBookmarksChanged();
@@ -194,27 +226,35 @@ class IABookmarks extends LitElement {
194
226
  return bookmark;
195
227
  }
196
228
 
197
- fetchBookmarks() {
198
- return this.api.getAll().then((res) => res.json()).then(({
229
+ async fetchBookmarks() {
230
+ const resText = await this.api.getAll().then(r=> r.text());
231
+ let parsedResponse;
232
+ try {
233
+ parsedResponse = JSON.parse(resText);
234
+ } catch (e) {
235
+ parsedResponse = {error : e.message};
236
+ }
237
+
238
+ const {
199
239
  success,
200
240
  error = 'Something happened while fetching bookmarks.',
201
241
  value: bkmrks = [],
202
- }) => {
203
- if (!success) {
204
- throw new Error(`Failed to load bookmarks: ${error}`);
205
- }
242
+ } = parsedResponse;
206
243
 
207
- const bookmarks = {};
208
- Object.keys(bkmrks).forEach((leafNum) => {
209
- const bookmark = bkmrks[leafNum];
210
- const formattedLeafNum = parseInt(leafNum, 10);
211
- const formattedBookmark = this.formatBookmark({ ...bookmark, leafNum: formattedLeafNum });
212
- bookmarks[leafNum] = formattedBookmark;
213
- });
244
+ if (!success) {
245
+ console?.warn('Error fetching bookmarks', error);
246
+ }
214
247
 
215
- this.bookmarks = bookmarks;
216
- return bookmarks;
248
+ const bookmarks = {};
249
+ Object.keys(bkmrks).forEach((leafNum) => {
250
+ const bookmark = bkmrks[leafNum];
251
+ const formattedLeafNum = parseInt(leafNum, 10);
252
+ const formattedBookmark = this.formatBookmark({ ...bookmark, leafNum: formattedLeafNum });
253
+ bookmarks[leafNum] = formattedBookmark;
217
254
  });
255
+
256
+ this.bookmarks = bookmarks;
257
+ return bookmarks;
218
258
  }
219
259
 
220
260
  emitBookmarksChanged() {
@@ -250,7 +290,9 @@ class IABookmarks extends LitElement {
250
290
 
251
291
  pages.forEach((pageEl) => {
252
292
  const existingButton = pageEl.querySelector('.bookmark-button');
253
- if (existingButton) { existingButton.remove(); }
293
+ if (existingButton) {
294
+ existingButton.remove();
295
+ }
254
296
  const pageID = +pageEl.classList.value.match(/pagediv\d+/)[0].replace(/\D/g, '');
255
297
  const pageBookmark = this.getBookmark(pageID);
256
298
  const bookmarkState = pageBookmark ? 'filled' : 'hollow';
@@ -390,33 +432,26 @@ class IABookmarks extends LitElement {
390
432
  confirmDeletion(pageID) {
391
433
  const existingBookmark = this.getBookmark(pageID);
392
434
  if (existingBookmark.note) {
393
- this.emitShowModal(pageID);
435
+ this.displayDeletionModal(pageID);
394
436
  return;
395
437
  }
396
438
  this.deleteBookmark({ detail: { id: `${pageID}` } });
397
439
  }
398
440
 
399
- emitShowModal(pageID) {
400
- this.dispatchEvent(new CustomEvent('showItemNavigatorModal', {
401
- bubbles: true,
402
- composed: true,
403
- detail: {
404
- customModalContent: html`
405
- <delete-modal-actions
406
- .deleteAction=${() => this.deleteBookmark({ detail: { id: `${pageID}` } })}
407
- .cancelAction=${() => this.emitCloseModal()}
408
- .pageID=${pageID}
409
- ></delete-modal-actions>
410
- `,
411
- },
412
- }));
413
- }
441
+ displayDeletionModal(pageID) {
442
+ const customModalContent = html`
443
+ <delete-modal-actions
444
+ .deleteAction=${() => this.deleteBookmark({ detail: { id: `${pageID}` } })}
445
+ .cancelAction=${() => this.modal.closeModal()}
446
+ .pageID=${pageID}
447
+ ></delete-modal-actions>
448
+ `;
414
449
 
415
- emitCloseModal() {
416
- this.dispatchEvent(new CustomEvent('closeItemNavigatorModal', {
417
- bubbles: true,
418
- composed: true,
419
- }));
450
+
451
+ this.modal.showModal({
452
+ config: this.deleteModalConfig,
453
+ customModalContent,
454
+ });
420
455
  }
421
456
 
422
457
  deleteBookmark({ detail }) {
@@ -427,18 +462,10 @@ class IABookmarks extends LitElement {
427
462
 
428
463
  this.api.delete(detail.id);
429
464
  this.editedBookmark = {};
430
- this.emitCloseModal();
465
+ this.modal.closeModal();
431
466
  this.renderBookmarkButtons();
432
467
  }
433
468
 
434
- /**
435
- * call `loginClicked` callback
436
- */
437
- loginClick() {
438
- const { loginClicked = () => {} } = this.options;
439
- loginClicked();
440
- }
441
-
442
469
  /**
443
470
  * Tells us if we should allow user to add bookmark via menu panel
444
471
  * returns { Boolean }
@@ -460,6 +487,7 @@ class IABookmarks extends LitElement {
460
487
  return html`
461
488
  <button
462
489
  class="ia-button primary"
490
+ tabindex="-1"
463
491
  ?disabled=${this.shouldEnableAddBookmarkButton}
464
492
  @click=${this.addBookmark}>
465
493
  Add bookmark
@@ -483,15 +511,23 @@ class IABookmarks extends LitElement {
483
511
  `;
484
512
  }
485
513
 
514
+ get bookmarkHelperMessage() {
515
+ return html`<p>Please use 1up or 2up view modes to add bookmark.</p>`;
516
+ }
517
+
486
518
  render() {
487
- const { loginUrl } = this.options;
488
519
  const bookmarks = html`
489
520
  ${this.bookmarksList}
490
- ${this.allowAddingBookmark ? this.addBookmarkButton : nothing}
521
+ ${this.allowAddingBookmark ? this.addBookmarkButton : this.bookmarkHelperMessage}
491
522
  `;
492
523
  return html`
493
524
  <section class="bookmarks">
494
- ${this.displayMode === 'login' ? html`<bookmarks-login @click=${this.loginClick} .url=${loginUrl}></bookmarks-login>` : bookmarks}
525
+ ${ this.displayMode === 'login'
526
+ ? html`<bookmarks-login
527
+ @click=${() => this.loginOptions.loginClicked()}
528
+ .url=${this.loginOptions.loginUrl}></bookmarks-login>`
529
+ : bookmarks
530
+ }
495
531
  </section>
496
532
  `;
497
533
  }
@@ -1,4 +1,4 @@
1
- import { LitElement, html, css } from 'lit-element';
1
+ import { LitElement, html, css } from 'lit';
2
2
 
3
3
  export default class DeleteModalActions extends LitElement {
4
4
  static get styles() {
@@ -1,10 +1,7 @@
1
- import { html } from 'lit-element';
1
+ import { html } from 'lit';
2
+ import '@internetarchive/icon-dl/icon-dl';
3
+ import './downloads';
2
4
 
3
- /* register subpanel */
4
- import { IABookDownloads } from './downloads';
5
- customElements.define('ia-book-downloads', IABookDownloads);
6
-
7
- let downloads = [];
8
5
  const menuBase = {
9
6
  pdf: {
10
7
  type: 'Encrypted Adobe PDF',
@@ -18,25 +15,33 @@ const menuBase = {
18
15
  }
19
16
  };
20
17
 
21
- export default class {
22
- constructor() {
23
- this.icon = html`<ia-icon icon="download" style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon>`;
18
+ const publicMenuBase = {
19
+ pdf: "PDF",
20
+ epub: "ePub"
21
+ };
22
+
23
+ export default class DownloadsProvider {
24
+
25
+ constructor({ bookreader }) {
26
+ this.icon = html`<ia-icon-dl style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon-dl>`;
24
27
  this.label = 'Downloadable files';
25
28
  this.menuDetails = '';
29
+ this.downloads = [];
26
30
  this.id = 'downloads';
27
31
  this.component = '';
32
+ this.isBookProtected = bookreader?.options?.isProtected || false;
28
33
 
29
34
  this.computeAvailableTypes = this.computeAvailableTypes.bind(this);
30
35
  this.update = this.update.bind(this);
31
36
  }
32
37
 
33
-
34
38
  update(downloadTypes) {
35
39
  this.computeAvailableTypes(downloadTypes);
36
40
  this.component = this.menu;
41
+ this.component.isBookProtected = this.isBookProtected;
37
42
 
38
- const ending = downloads.length === 1 ? '' : 's';
39
- this.menuDetails = `(${downloads.length} format${ending})`;
43
+ const ending = this.downloads.length === 1 ? '' : 's';
44
+ this.menuDetails = `(${this.downloads.length} format${ending})`;
40
45
  }
41
46
 
42
47
  /**
@@ -45,22 +50,23 @@ export default class {
45
50
  * @param availableTypes
46
51
  */
47
52
  computeAvailableTypes(availableTypes = []) {
48
- const menuData = availableTypes.reduce((found, incoming = [], index) => {
53
+ const menuData = availableTypes.reduce((found, incoming = []) => {
49
54
  const [ type = '', link = '' ] = incoming;
50
55
  const formattedType = type.toLowerCase();
51
56
  const downloadOption = menuBase[formattedType] || null;
57
+
52
58
  if (downloadOption) {
53
- const menuInfo = Object.assign({}, downloadOption, { url: link });
59
+ const menuButtonText = this.isBookProtected ? menuBase[formattedType].type : publicMenuBase[formattedType];
60
+ const menuInfo = Object.assign({}, downloadOption, { url: link, type: menuButtonText});
54
61
  found.push(menuInfo);
55
62
  }
56
63
  return found;
57
64
  }, []);
58
65
 
59
- downloads = menuData;
66
+ this.downloads = menuData;
60
67
  }
61
68
 
62
-
63
69
  get menu () {
64
- return html`<ia-book-downloads .downloads=${downloads}></ia-book-downloads>`;
70
+ return html`<ia-book-downloads .downloads=${this.downloads}></ia-book-downloads>`;
65
71
  }
66
72
  }
@@ -1,11 +1,12 @@
1
- import { css, html, LitElement } from 'lit-element';
2
- import { nothing } from 'lit-html';
1
+ import { css, html, LitElement, nothing } from 'lit';
2
+ import buttonStyles from '../assets/button-base.js';
3
3
  export class IABookDownloads extends LitElement {
4
4
  static get properties() {
5
5
  return {
6
6
  downloads: { type: Array },
7
7
  expiration: { type: Number },
8
8
  renderHeader: { type: Boolean },
9
+ isBookProtected: { type: Boolean },
9
10
  };
10
11
  }
11
12
 
@@ -14,6 +15,7 @@ export class IABookDownloads extends LitElement {
14
15
  this.downloads = [];
15
16
  this.expiration = 0;
16
17
  this.renderHeader = false;
18
+ this.isBookProtected = false;
17
19
  }
18
20
 
19
21
  get formatsCount() {
@@ -31,7 +33,7 @@ export class IABookDownloads extends LitElement {
31
33
  return this.downloads.map(option => (
32
34
  html`
33
35
  <li>
34
- <a class="button" href="${option.url}">Get ${option.type}</a>
36
+ <a class="ia-button link primary" href="${option.url}">Get ${option.type}</a>
35
37
  ${option.note ? html`<p>${option.note}</p>` : html``}
36
38
  </li>
37
39
  `
@@ -50,18 +52,24 @@ export class IABookDownloads extends LitElement {
50
52
  `;
51
53
  }
52
54
 
55
+ get accessProtectedBook() {
56
+ return html`
57
+ <p>To access downloaded books, you need Adobe-compliant software on your device. The Internet Archive will administer this loan, but Adobe may also collect some information.</p>
58
+ <a class="ia-button external primary" href="https://www.adobe.com/solutions/ebook/digital-editions/download.html" rel="noopener noreferrer" target="_blank">Install Adobe Digital Editions</a>
59
+ `;
60
+ }
61
+
53
62
  render() {
54
63
  return html`
55
64
  ${this.header}
56
65
  ${this.loanExpiryMessage}
57
66
  <ul>${this.renderDownloadOptions()}</ul>
58
- <p>To access downloaded books, you need Adobe-compliant software on your device. The Internet Archive will administer this loan, but Adobe may also collect some information.</p>
59
- <a class="button external" href="https://www.adobe.com/solutions/ebook/digital-editions/download.html" rel="noopener noreferrer" target="_blank">Install Adobe Digital Editions</a>
67
+ ${this.isBookProtected ? this.accessProtectedBook : nothing}
60
68
  `;
61
69
  }
62
70
 
63
71
  static get styles() {
64
- return css`
72
+ const mainCss = css`
65
73
  :host {
66
74
  display: block;
67
75
  height: 100%;
@@ -79,24 +87,6 @@ export class IABookDownloads extends LitElement {
79
87
  justify-self: end;
80
88
  }
81
89
 
82
- .button {
83
- color: var(--primaryTextColor);
84
- background: var(--primaryCTAFill);
85
- border: 1px solid var(--primaryCTABorder);
86
- display: inline-block;
87
- padding: .6rem 1rem;
88
- font-size: 1.4rem;
89
- text-decoration: none;
90
- text-shadow: 1px 1px #484848;
91
- border-radius: 4px;
92
- }
93
-
94
- .button.external {
95
- background: var(--secondaryCTAFill, transparent);
96
- border: 1px solid var(--secondaryCTABorder, #999);
97
- text-shadow: none;
98
- }
99
-
100
90
  header {
101
91
  display: flex;
102
92
  align-items: center;
@@ -109,7 +99,6 @@ export class IABookDownloads extends LitElement {
109
99
  font-weight: bold;
110
100
  font-style: italic;
111
101
  }
112
-
113
102
  header div {
114
103
  display: flex;
115
104
  align-items: baseline;
@@ -142,5 +131,8 @@ export class IABookDownloads extends LitElement {
142
131
  line-height: 140%;
143
132
  }
144
133
  `;
134
+
135
+ return [buttonStyles, mainCss];
145
136
  }
146
137
  }
138
+ customElements.define('ia-book-downloads', IABookDownloads);
@@ -1,6 +1,5 @@
1
- import { nothing } from 'lit-html';
2
- import { html, LitElement } from 'lit-element';
3
- import { unsafeHTML } from 'lit-html/directives/unsafe-html';
1
+ import { html, LitElement, nothing } from 'lit';
2
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
4
3
 
5
4
  export class BookSearchResult extends LitElement {
6
5
  static get properties() {
@@ -52,3 +51,4 @@ export class BookSearchResult extends LitElement {
52
51
  `;
53
52
  }
54
53
  }
54
+ customElements.define('book-search-result', BookSearchResult);
@@ -1,9 +1,6 @@
1
- import { html } from 'lit-element';
2
- import { nothing } from 'lit-html';
3
-
4
- /* instantiate web component */
5
- import { IABookSearchResults } from './search-results';
6
- customElements.define('ia-book-search-results', IABookSearchResults);
1
+ import { html, nothing } from 'lit';
2
+ import '@internetarchive/icon-search/icon-search';
3
+ import './search-results';
7
4
 
8
5
  let searchState = {
9
6
  query: '',
@@ -12,8 +9,11 @@ let searchState = {
12
9
  queryInProgress: false,
13
10
  errorMessage: '',
14
11
  };
15
- export default class {
16
- constructor(onSearchChange = () => {}, brInstance) {
12
+ export default class SearchProvider {
13
+ constructor({
14
+ onProviderChange,
15
+ bookreader
16
+ }) {
17
17
  /* search menu events */
18
18
  this.onBookSearchInitiated = this.onBookSearchInitiated.bind(this);
19
19
  /* bookreader search events */
@@ -22,6 +22,8 @@ export default class {
22
22
  this.onSearchResultsClicked = this.onSearchResultsClicked.bind(this);
23
23
  this.onSearchResultsChange = this.onSearchResultsChange.bind(this);
24
24
  this.onSearchResultsCleared = this.onSearchResultsCleared.bind(this);
25
+ this.searchCanceledInMenu = this.searchCanceledInMenu.bind(this);
26
+
25
27
  /* class methods */
26
28
  this.bindEventListeners = this.bindEventListeners.bind(this);
27
29
  this.getMenuDetails = this.getMenuDetails.bind(this);
@@ -29,9 +31,9 @@ export default class {
29
31
  this.advanceToPage = this.advanceToPage.bind(this);
30
32
  this.updateMenu = this.updateMenu.bind(this);
31
33
 
32
- this.onSearchChange = onSearchChange;
33
- this.bookreader = brInstance;
34
- this.icon = html`<ia-icon icon="search" style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon>`;
34
+ this.onProviderChange = onProviderChange;
35
+ this.bookreader = bookreader;
36
+ this.icon = html`<ia-icon-search style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon-search>`;
35
37
  this.label = 'Search inside';
36
38
  this.menuDetails = this.getMenuDetails();
37
39
  this.id = 'search';
@@ -41,7 +43,7 @@ export default class {
41
43
 
42
44
  getMenuDetails() {
43
45
  const { resultsCount, query, queryInProgress } = searchState;
44
- if (queryInProgress || !query) { return nothing }
46
+ if (queryInProgress || !query) { return nothing; }
45
47
  const unit = resultsCount === 1 ? 'result' : 'results';
46
48
  return html`(${resultsCount} ${unit})`;
47
49
  }
@@ -49,14 +51,36 @@ export default class {
49
51
  bindEventListeners() {
50
52
  window.addEventListener('BookReader:SearchStarted', this.onSearchStarted);
51
53
  window.addEventListener('BookReader:SearchCallback', this.onSearchResultsChange);
52
- window.addEventListener('BookReader:SearchCallbackEmpty', (event) => { this.onSearchRequestError(event, 'noResults') });
53
- window.addEventListener('BookReader:SearchCallbackNotIndexed', (event) => { this.onSearchRequestError(event, 'notIndexed') });
54
- window.addEventListener('BookReader:SearchCallbackError', (event) => { this.onSearchRequestError(event) });
55
- window.addEventListener('BookReader:SearchResultsCleared', () => { this.onSearchResultsCleared() });
54
+ window.addEventListener('BookReader:SearchCallbackEmpty', (event) => { this.onSearchRequestError(event, 'noResults'); });
55
+ window.addEventListener('BookReader:SearchCallbackNotIndexed', (event) => { this.onSearchRequestError(event, 'notIndexed'); });
56
+ window.addEventListener('BookReader:SearchCallbackError', (event) => { this.onSearchRequestError(event); });
57
+ window.addEventListener('BookReader:SearchResultsCleared', () => { this.onSearchResultsCleared(); });
58
+ window.addEventListener('BookReader:SearchCanceled', (e) => { this.onSearchCanceled(e); });
59
+ }
60
+
61
+ /**
62
+ * Cancel search handler
63
+ * resets `searchState`
64
+ */
65
+ onSearchCanceled() {
66
+ searchState = {
67
+ query: '',
68
+ results: [],
69
+ resultsCount: 0,
70
+ queryInProgress: false,
71
+ errorMessage: '',
72
+ };
73
+ const updateMenuFor = {
74
+ searchCanceled: true
75
+ };
76
+ this.updateMenu(updateMenuFor);
56
77
  }
57
78
 
58
79
  onSearchStarted(e) {
59
- const { term = '' } = e.detail.props;
80
+ const { term = '', instance } = e.detail.props;
81
+ if (instance) {
82
+ this.bookreader = instance;
83
+ }
60
84
  searchState.query = term;
61
85
  searchState.results = [];
62
86
  searchState.resultsCount = 0;
@@ -81,10 +105,11 @@ export default class {
81
105
  noResults: '0 results',
82
106
  notIndexed: `This book hasn't been indexed for searching yet. We've just started indexing it,
83
107
  so search should be available soon. Please try again later. Thanks!`,
84
- default: 'Sorry, there was an error with your search. The text may still be processing.',
108
+ default: 'Sorry, there was an error with your search. Please try again.',
85
109
  };
86
110
 
87
111
  const messageToShow = errorMessages[errorType] ?? errorMessages.default;
112
+ searchState.query = instance?.searchResults?.q || '';
88
113
  searchState.results = [];
89
114
  searchState.resultsCount = 0;
90
115
  searchState.queryInProgress = false;
@@ -106,6 +131,10 @@ export default class {
106
131
  this.updateMenu();
107
132
  }
108
133
 
134
+ searchCanceledInMenu() {
135
+ this.bookreader?.cancelSearchRequest();
136
+ }
137
+
109
138
  onSearchResultsCleared() {
110
139
  searchState = {
111
140
  query: '',
@@ -113,15 +142,19 @@ export default class {
113
142
  resultsCount: 0,
114
143
  queryInProgress: false,
115
144
  errorMessage: '',
116
- }
117
- this.updateMenu();
118
- this.bookreader?.searchView?.clearSearchFieldAndResults();
145
+ };
146
+ this.updateMenu({ openMenu: false });
147
+ this.bookreader?.searchView?.clearSearchFieldAndResults(false);
119
148
  }
120
149
 
121
- updateMenu() {
150
+ /**
151
+ * Relays how to update side menu given the context of a search update
152
+ @param {{searchCanceled: boolean}} searchUpdates
153
+ */
154
+ updateMenu(searchUpdates = {}) {
122
155
  this.menuDetails = this.getMenuDetails();
123
156
  this.component = this.getComponent();
124
- this.onSearchChange(this.bookreader);
157
+ this.onProviderChange(this.bookreader, searchUpdates);
125
158
  }
126
159
 
127
160
  getComponent() {
@@ -136,6 +169,7 @@ export default class {
136
169
  @resultSelected=${this.onSearchResultsClicked}
137
170
  @bookSearchInitiated=${this.onBookSearchInitiated}
138
171
  @bookSearchResultsCleared=${this.onSearchResultsCleared}
172
+ @bookSearchCanceled=${this.searchCanceledInMenu}
139
173
  ></ia-book-search-results>
140
174
  `;
141
175
  }
@@ -148,6 +182,5 @@ export default class {
148
182
  advanceToPage(leaf) {
149
183
  const page = this.bookreader.leafNumToIndex(leaf);
150
184
  this.bookreader._searchPluginGoToResult(page);
151
- this.bookreader.updateSearchHilites();
152
185
  }
153
186
  }