@internetarchive/bookreader 5.0.0-5 → 5.0.0-50-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 (260) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +77 -6
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +131 -339
  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 +1493 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +17 -0
  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/pause.svg +1 -1
  16. package/BookReader/icons/playback-speed.svg +1 -1
  17. package/BookReader/icons/read-aloud.svg +1 -1
  18. package/BookReader/icons/voice.svg +1 -0
  19. package/BookReader/images/BRicons.svg +2 -2
  20. package/BookReader/images/books_graphic.svg +1 -1
  21. package/BookReader/images/icon_book.svg +1 -1
  22. package/BookReader/images/icon_gear.svg +1 -1
  23. package/BookReader/images/icon_info.svg +1 -1
  24. package/BookReader/images/icon_playback-rate.svg +1 -1
  25. package/BookReader/images/icon_search_button.svg +1 -1
  26. package/BookReader/images/icon_share.svg +1 -1
  27. package/BookReader/images/icon_speaker.svg +1 -1
  28. package/BookReader/images/icon_speaker_open.svg +1 -1
  29. package/BookReader/images/marker_chap-off.svg +1 -1
  30. package/BookReader/images/marker_chap-on.svg +1 -1
  31. package/BookReader/images/marker_srch-on.svg +1 -1
  32. package/BookReader/jquery-3.js +2 -0
  33. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  34. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  35. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  36. package/BookReader/plugins/plugin.autoplay.js +1 -1
  37. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  38. package/BookReader/plugins/plugin.chapters.js +1 -1
  39. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  40. package/BookReader/plugins/plugin.iframe.js +1 -1
  41. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  42. package/BookReader/plugins/plugin.mobile_nav.js +1 -1
  43. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  44. package/BookReader/plugins/plugin.resume.js +1 -1
  45. package/BookReader/plugins/plugin.resume.js.map +1 -1
  46. package/BookReader/plugins/plugin.search.js +1 -1
  47. package/BookReader/plugins/plugin.search.js.map +1 -1
  48. package/BookReader/plugins/plugin.text_selection.js +1 -1
  49. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  50. package/BookReader/plugins/plugin.tts.js +1 -1
  51. package/BookReader/plugins/plugin.tts.js.map +1 -1
  52. package/BookReader/plugins/plugin.url.js +1 -1
  53. package/BookReader/plugins/plugin.url.js.map +1 -1
  54. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  55. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  56. package/BookReader/webcomponents-bundle.js +3 -0
  57. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  58. package/BookReader/webcomponents-bundle.js.map +1 -0
  59. package/BookReaderDemo/BookReaderDemo.css +14 -1
  60. package/BookReaderDemo/IADemoBr.js +148 -0
  61. package/BookReaderDemo/demo-advanced.html +2 -2
  62. package/BookReaderDemo/demo-autoplay.html +2 -1
  63. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  64. package/BookReaderDemo/demo-fullscreen-mobile.html +2 -1
  65. package/BookReaderDemo/demo-fullscreen.html +2 -1
  66. package/BookReaderDemo/demo-iiif.html +2 -1
  67. package/BookReaderDemo/demo-internetarchive.html +84 -17
  68. package/BookReaderDemo/demo-multiple.html +2 -1
  69. package/BookReaderDemo/demo-preview-pages.html +2 -1
  70. package/BookReaderDemo/demo-simple.html +2 -1
  71. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -1
  72. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  73. package/BookReaderDemo/immersion-1up.html +2 -1
  74. package/BookReaderDemo/immersion-mode.html +2 -1
  75. package/BookReaderDemo/toggle_controls.html +2 -1
  76. package/BookReaderDemo/view_mode.html +2 -1
  77. package/BookReaderDemo/viewmode-cycle.html +2 -3
  78. package/CHANGELOG.md +202 -0
  79. package/README.md +14 -1
  80. package/babel.config.js +18 -0
  81. package/codecov.yml +6 -0
  82. package/index.html +3 -0
  83. package/jsconfig.json +19 -0
  84. package/package.json +66 -56
  85. package/renovate.json +52 -0
  86. package/scripts/preversion.js +4 -1
  87. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  88. package/src/BookNavigator/assets/button-base.js +9 -2
  89. package/src/BookNavigator/assets/ia-logo.js +17 -0
  90. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  91. package/src/BookNavigator/assets/icon_close.js +1 -1
  92. package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
  93. package/src/BookNavigator/assets/icon_sort_desc.js +5 -0
  94. package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
  95. package/src/BookNavigator/assets/icon_volumes.js +11 -0
  96. package/src/BookNavigator/book-navigator.js +583 -0
  97. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  98. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  99. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  100. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  101. package/src/BookNavigator/bookmarks/bookmarks-provider.js +21 -8
  102. package/src/BookNavigator/bookmarks/ia-bookmarks.js +102 -66
  103. package/src/BookNavigator/delete-modal-actions.js +1 -1
  104. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  105. package/src/BookNavigator/downloads/downloads.js +41 -25
  106. package/src/BookNavigator/search/a-search-result.js +18 -13
  107. package/src/BookNavigator/search/search-provider.js +80 -28
  108. package/src/BookNavigator/search/search-results.js +10 -18
  109. package/src/BookNavigator/sharing.js +27 -0
  110. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  111. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  112. package/src/BookNavigator/volumes/volumes-provider.js +114 -0
  113. package/src/BookNavigator/volumes/volumes.js +188 -0
  114. package/src/BookReader/BookModel.js +0 -29
  115. package/src/BookReader/DebugConsole.js +3 -3
  116. package/src/BookReader/DragScrollable.js +233 -0
  117. package/src/BookReader/Mode1Up.js +51 -351
  118. package/src/BookReader/Mode1UpLit.js +441 -0
  119. package/src/BookReader/Mode2Up.js +120 -105
  120. package/src/BookReader/ModeSmoothZoom.js +179 -0
  121. package/src/BookReader/ModeThumb.js +17 -11
  122. package/src/BookReader/Navbar/Navbar.js +10 -36
  123. package/src/BookReader/PageContainer.js +69 -6
  124. package/src/BookReader/ReduceSet.js +1 -1
  125. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  126. package/src/BookReader/options.js +10 -0
  127. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  128. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  129. package/src/BookReader/utils.js +92 -13
  130. package/src/BookReader.js +431 -620
  131. package/src/assets/icons/close-circle-dark.svg +1 -0
  132. package/src/assets/icons/magnify-minus.svg +3 -7
  133. package/src/assets/icons/magnify-plus.svg +3 -7
  134. package/src/assets/icons/voice.svg +1 -0
  135. package/src/css/BookReader.scss +0 -12
  136. package/src/css/_BRComponent.scss +1 -1
  137. package/src/css/_BRmain.scss +19 -24
  138. package/src/css/_BRnav.scss +4 -26
  139. package/src/css/_BRpages.scss +35 -0
  140. package/src/css/_BRsearch.scss +25 -216
  141. package/src/css/_TextSelection.scss +14 -17
  142. package/src/css/_colorbox.scss +2 -2
  143. package/src/css/_controls.scss +17 -5
  144. package/src/css/_icons.scss +6 -0
  145. package/src/ia-bookreader/ia-bookreader.js +224 -0
  146. package/src/plugins/plugin.autoplay.js +4 -4
  147. package/src/plugins/plugin.chapters.js +28 -35
  148. package/src/plugins/plugin.mobile_nav.js +11 -10
  149. package/src/plugins/plugin.resume.js +3 -3
  150. package/src/plugins/plugin.text_selection.js +26 -39
  151. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  152. package/src/plugins/search/plugin.search.js +174 -116
  153. package/src/plugins/search/view.js +63 -179
  154. package/src/plugins/tts/AbstractTTSEngine.js +46 -37
  155. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  156. package/src/plugins/tts/PageChunk.js +15 -21
  157. package/src/plugins/tts/PageChunkIterator.js +8 -12
  158. package/src/plugins/tts/WebTTSEngine.js +66 -69
  159. package/src/plugins/tts/plugin.tts.js +92 -109
  160. package/src/plugins/tts/utils.js +0 -9
  161. package/src/plugins/url/UrlPlugin.js +184 -0
  162. package/src/plugins/{plugin.url.js → url/plugin.url.js} +28 -6
  163. package/src/util/manifestGenerator.js +0 -0
  164. package/tests/e2e/README.md +37 -0
  165. package/tests/e2e/autoplay.test.js +2 -2
  166. package/tests/e2e/base.test.js +7 -7
  167. package/tests/e2e/helpers/base.js +9 -3
  168. package/tests/e2e/helpers/debug.js +1 -1
  169. package/tests/e2e/helpers/desktopSearch.js +14 -13
  170. package/tests/e2e/helpers/mobileSearch.js +3 -3
  171. package/tests/e2e/helpers/params.js +17 -0
  172. package/tests/e2e/models/Navigation.js +13 -4
  173. package/tests/e2e/rightToLeft.test.js +4 -5
  174. package/tests/e2e/viewmode.test.js +38 -33
  175. package/tests/jest/BookNavigator/book-navigator.test.js +634 -0
  176. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  177. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  178. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  179. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  180. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  181. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  182. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  183. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +102 -58
  184. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  185. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  186. package/tests/jest/BookNavigator/volumes/volumes-provider.test.js +184 -0
  187. package/tests/jest/BookNavigator/volumes/volumes.test.js +97 -0
  188. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +34 -14
  189. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +176 -0
  190. package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
  191. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  192. package/tests/jest/BookReader/Mode1UpLit.test.js +92 -0
  193. package/tests/{BookReader → jest/BookReader}/Mode2Up.test.js +36 -15
  194. package/tests/jest/BookReader/ModeSmoothZoom.test.js +149 -0
  195. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  196. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +7 -7
  197. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  198. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  199. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  200. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  201. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  202. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  203. package/tests/jest/BookReader/utils.test.js +186 -0
  204. package/tests/jest/BookReader.keyboard.test.js +190 -0
  205. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  206. package/tests/{BookReader.test.js → jest/BookReader.test.js} +18 -37
  207. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  208. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  209. package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +10 -11
  210. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  211. package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
  212. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  213. package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +39 -47
  214. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  215. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +63 -47
  216. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +35 -6
  217. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +9 -9
  218. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  219. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  220. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  221. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +1 -1
  222. package/tests/{plugins → jest/plugins}/tts/utils.test.js +3 -28
  223. package/tests/jest/plugins/url/UrlPlugin.test.js +190 -0
  224. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +33 -14
  225. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  226. package/tests/{util → jest/util}/docCookies.test.js +1 -1
  227. package/tests/{util → jest/util}/strings.test.js +1 -1
  228. package/tests/{utils.js → jest/utils.js} +38 -0
  229. package/webpack.config.js +11 -5
  230. package/.babelrc +0 -12
  231. package/.dependabot/config.yml +0 -6
  232. package/.testcaferc.json +0 -5
  233. package/BookReader/bookreader-component-bundle.js +0 -1450
  234. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  235. package/BookReader/bookreader-component-bundle.js.map +0 -1
  236. package/BookReader/jquery-1.10.1.js +0 -2
  237. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  238. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  239. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  240. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  241. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  242. package/karma.conf.js +0 -23
  243. package/src/BookNavigator/BookModel.js +0 -14
  244. package/src/BookNavigator/BookNavigator.js +0 -438
  245. package/src/BookNavigator/assets/book-loader.js +0 -27
  246. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  247. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  248. package/src/ItemNavigator/ItemNavigator.js +0 -372
  249. package/src/ItemNavigator/providers/sharing.js +0 -29
  250. package/src/Layers/sharing/sharing-provider.js +0 -22
  251. package/src/dragscrollable-br.js +0 -261
  252. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  253. package/src/plugins/plugin.bookmarks.js +0 -50
  254. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  255. package/tests/BookReader/Mode1Up.test.js +0 -164
  256. package/tests/BookReader/utils.test.js +0 -109
  257. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  258. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  259. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  260. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
@@ -1,4 +1,4 @@
1
- import { html } from 'lit-element';
1
+ import { html } from 'lit';
2
2
  import '../delete-modal-actions.js';
3
3
  import './bookmark-button.js';
4
4
  import './ia-bookmarks.js';
@@ -10,17 +10,32 @@ import { IAIconBookmark } from '@internetarchive/icon-bookmark';
10
10
  customElements.define('icon-bookmark', IAIconBookmark);
11
11
 
12
12
  export default class BookmarksProvider {
13
- constructor(options, bookreader) {
14
- const boundOptions = Object.assign(this, options, {loginClicked: this.bookmarksLoginClicked});
13
+ constructor(options) {
14
+ const {
15
+ baseHost,
16
+ signedIn,
17
+ bookreader,
18
+ modal,
19
+ onProviderChange,
20
+ } = options;
21
+
22
+ const referrerStr = `referer=${encodeURIComponent(location.href)}`;
23
+ const loginUrl = `https://${baseHost}/account/login?${referrerStr}`;
24
+
15
25
  this.component = document.createElement('ia-bookmarks');
16
26
  this.component.bookreader = bookreader;
17
- this.component.options = boundOptions;
18
-
27
+ this.component.displayMode = signedIn ? 'bookmarks' : 'login';
28
+ this.component.modal = modal;
29
+ this.component.loginOptions = {
30
+ loginClicked: this.bookmarksLoginClicked,
31
+ loginUrl
32
+ };
19
33
  this.bindEvents();
20
34
 
21
35
  this.icon = html`<icon-bookmark state="hollow" style="--iconWidth: 16px; --iconHeight: 24px;"></icon-bookmark>`;
22
36
  this.label = 'Bookmarks';
23
37
  this.id = 'bookmarks';
38
+ this.onProviderChange = onProviderChange;
24
39
  this.component.setup();
25
40
  this.updateMenu(this.component.bookmarks.length);
26
41
  }
@@ -31,14 +46,12 @@ export default class BookmarksProvider {
31
46
 
32
47
  bindEvents() {
33
48
  this.component.addEventListener('bookmarksChanged', this.bookmarksChanged.bind(this));
34
- this.component.addEventListener('showItemNavigatorModal', this.showItemNavigatorModal);
35
- this.component.addEventListener('closeItemNavigatorModal', this.closeItemNavigatorModal);
36
49
  }
37
50
 
38
51
  bookmarksChanged({ detail }) {
39
52
  const bookmarksLength = Object.keys(detail.bookmarks).length;
40
53
  this.updateMenu(bookmarksLength);
41
- this.onBookmarksChanged(detail.bookmarks);
54
+ this.onProviderChange(detail.bookmarks, detail.showSidePanel);
42
55
  }
43
56
 
44
57
  bookmarksLoginClicked() {
@@ -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();
@@ -181,8 +213,8 @@ class IABookmarks extends LitElement {
181
213
  color: this.getBookmarkColor(color) ? color : this.defaultColor.id,
182
214
  };
183
215
 
184
- const page = IABookmarks.formatPage(this.bookreader.getPageNum(leafNum));
185
- const thumbnail = this.bookreader.getPageURI(`${leafNum}`.replace(/\D/g, ''), 32); // Request thumbnail 1/32 the size of original image
216
+ const page = IABookmarks.formatPage(this.bookreader.book.getPageNum(leafNum));
217
+ const thumbnail = this.bookreader.book.getPageURI(`${leafNum}`.replace(/\D/g, ''), 32); // Request thumbnail 1/32 the size of original image
186
218
  const bookmark = {
187
219
  ...nomalizedParams,
188
220
  id: leafNum,
@@ -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,12 +290,14 @@ 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';
257
299
  // eslint-disable-next-line
258
- const pageData = this.bookreader._models.book.getPage(pageID);
300
+ const pageData = this.bookreader.book.getPage(pageID);
259
301
  const { isViewable } = pageData;
260
302
 
261
303
  if (!isViewable) { return; }
@@ -375,7 +417,7 @@ class IABookmarks extends LitElement {
375
417
 
376
418
  bookmarkSelected({ detail }) {
377
419
  const { leafNum } = detail.bookmark;
378
- this.bookreader.jumpToPage(`${this.bookreader.getPageNum(`${leafNum}`.replace(/\D/g, ''))}`);
420
+ this.bookreader.jumpToPage(`${this.bookreader.book.getPageNum(`${leafNum}`.replace(/\D/g, ''))}`);
379
421
  this.activeBookmarkID = leafNum;
380
422
  }
381
423
 
@@ -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,42 +1,56 @@
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',
11
8
  url: '#',
12
9
  note: 'PDF files contain high quality images of pages.',
13
10
  },
11
+ lcppdf: {
12
+ type: 'Get LCP PDF',
13
+ url: '#',
14
+ note: 'PDF files contain high quality images of pages.',
15
+ },
16
+ lcpepub: {
17
+ type: 'Get LCP ePub',
18
+ url: '#',
19
+ note: 'ePub files are smaller in size, but may contain errors.',
20
+ },
14
21
  epub: {
15
22
  type: 'Encrypted Adobe ePub',
16
23
  url: '#',
17
24
  note: 'ePub files are smaller in size, but may contain errors.',
18
- }
25
+ },
26
+ };
27
+
28
+ const publicMenuBase = {
29
+ pdf: "PDF",
30
+ epub: "ePub",
31
+ lcppdf: "LCP PDF",
32
+ lcpepub: "LCP ePub",
19
33
  };
20
34
 
21
- export default class {
22
- constructor() {
23
- this.icon = html`<ia-icon icon="download" style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon>`;
35
+ export default class DownloadsProvider {
36
+
37
+ constructor({ bookreader }) {
38
+ this.icon = html`<ia-icon-dl style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon-dl>`;
24
39
  this.label = 'Downloadable files';
25
40
  this.menuDetails = '';
41
+ this.downloads = [];
26
42
  this.id = 'downloads';
27
43
  this.component = '';
28
-
29
- this.computeAvailableTypes = this.computeAvailableTypes.bind(this);
30
- this.update = this.update.bind(this);
44
+ this.isBookProtected = bookreader?.options?.isProtected || false;
31
45
  }
32
46
 
33
-
34
47
  update(downloadTypes) {
35
48
  this.computeAvailableTypes(downloadTypes);
36
49
  this.component = this.menu;
50
+ this.component.isBookProtected = this.isBookProtected;
37
51
 
38
- const ending = downloads.length === 1 ? '' : 's';
39
- this.menuDetails = `(${downloads.length} format${ending})`;
52
+ const ending = this.downloads.length === 1 ? '' : 's';
53
+ this.menuDetails = `(${this.downloads.length} format${ending})`;
40
54
  }
41
55
 
42
56
  /**
@@ -45,22 +59,23 @@ export default class {
45
59
  * @param availableTypes
46
60
  */
47
61
  computeAvailableTypes(availableTypes = []) {
48
- const menuData = availableTypes.reduce((found, incoming = [], index) => {
62
+ const menuData = availableTypes.reduce((found, incoming = []) => {
49
63
  const [ type = '', link = '' ] = incoming;
50
64
  const formattedType = type.toLowerCase();
51
65
  const downloadOption = menuBase[formattedType] || null;
66
+
52
67
  if (downloadOption) {
53
- const menuInfo = Object.assign({}, downloadOption, { url: link });
68
+ const menuButtonText = this.isBookProtected ? menuBase[formattedType].type : publicMenuBase[formattedType];
69
+ const menuInfo = Object.assign({}, downloadOption, { url: link, type: menuButtonText});
54
70
  found.push(menuInfo);
55
71
  }
56
72
  return found;
57
73
  }, []);
58
74
 
59
- downloads = menuData;
75
+ this.downloads = menuData;
60
76
  }
61
77
 
62
-
63
78
  get menu () {
64
- return html`<ia-book-downloads .downloads=${downloads}></ia-book-downloads>`;
79
+ return html`<ia-book-downloads .downloads=${this.downloads}></ia-book-downloads>`;
65
80
  }
66
81
  }
@@ -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,13 +33,23 @@ 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
  `
38
40
  ));
39
41
  }
40
42
 
43
+ /**
44
+ * checks if downloads list contains an LCP option
45
+ * @return {boolean}
46
+ */
47
+ get hasLCPOption() {
48
+ const regex = /^(LCP)/g;
49
+ const lcpAvailable = this.downloads.some(option => option.type?.match(regex));
50
+ return lcpAvailable;
51
+ }
52
+
41
53
  get header() {
42
54
  if (!this.renderHeader) {
43
55
  return nothing;
@@ -50,18 +62,38 @@ export class IABookDownloads extends LitElement {
50
62
  `;
51
63
  }
52
64
 
65
+ get accessProtectedBook() {
66
+ return html`
67
+ <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>
68
+ <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>
69
+ `;
70
+ }
71
+
72
+ get installSimplyEAldikoThoriumMsg() {
73
+ return html`
74
+ <p>For LCP downloads, make sure you have SimplyE or Aldiko Next installed on mobile or Thorium on desktop.</p>
75
+ <ul>
76
+ <li><a href="https://librarysimplified.org/simplye/" rel="noopener noreferrer nofollow" target="_blank">Install SimplyE</a></li>
77
+ <li><a href="https://www.demarque.com/en-aldiko" rel="noopener noreferrer nofollow" target="_blank">Install Aldiko</a></li>
78
+ <li><a href="https://www.edrlab.org/software/thorium-reader/" rel="noopener noreferrer nofollow" target="_blank">Install Thorium</a></li>
79
+ </ul>
80
+ `;
81
+ }
82
+
53
83
  render() {
54
84
  return html`
55
85
  ${this.header}
56
86
  ${this.loanExpiryMessage}
57
87
  <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>
88
+ ${this.hasLCPOption
89
+ ? this.installSimplyEAldikoThoriumMsg
90
+ : (this.isBookProtected ? this.accessProtectedBook : nothing)
91
+ }
60
92
  `;
61
93
  }
62
94
 
63
95
  static get styles() {
64
- return css`
96
+ const mainCss = css`
65
97
  :host {
66
98
  display: block;
67
99
  height: 100%;
@@ -79,24 +111,6 @@ export class IABookDownloads extends LitElement {
79
111
  justify-self: end;
80
112
  }
81
113
 
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
114
  header {
101
115
  display: flex;
102
116
  align-items: center;
@@ -109,7 +123,6 @@ export class IABookDownloads extends LitElement {
109
123
  font-weight: bold;
110
124
  font-style: italic;
111
125
  }
112
-
113
126
  header div {
114
127
  display: flex;
115
128
  align-items: baseline;
@@ -142,5 +155,8 @@ export class IABookDownloads extends LitElement {
142
155
  line-height: 140%;
143
156
  }
144
157
  `;
158
+
159
+ return [buttonStyles, mainCss];
145
160
  }
146
161
  }
162
+ customElements.define('ia-book-downloads', IABookDownloads);
@@ -1,6 +1,7 @@
1
- import { nothing } from 'lit-html';
2
- import { html, LitElement } from 'lit-element';
3
- import { unsafeHTML } from 'lit-html/directives/unsafe-html';
1
+ import { escapeHTML } from '../../BookReader/utils.js';
2
+ import { html, LitElement, nothing } from 'lit';
3
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
4
+ /** @typedef {import('@/src/plugins/search/plugin.search.js').SearchInsideMatch} SearchInsideMatch */
4
5
 
5
6
  export class BookSearchResult extends LitElement {
6
7
  static get properties() {
@@ -12,17 +13,24 @@ export class BookSearchResult extends LitElement {
12
13
  constructor() {
13
14
  super();
14
15
 
15
- this.matchRegex = new RegExp('{{{(.+?)}}}', 'g');
16
+ this.matchRegex = new RegExp('{{{(.+?)}}}', 'gs');
16
17
  }
17
18
 
18
19
  createRenderRoot() {
19
20
  return this;
20
21
  }
21
22
 
23
+ /**
24
+ * Converts the search hit to a `<p>` template containing the full search result with all of
25
+ * its `{{{triple-brace-delimited}}}` matches replaced by `<mark>HTML mark tags</mark>`.
26
+ *
27
+ * This approach safely avoids the use of `unsafeHTML` and leaves any existing HTML tags
28
+ * in the snippets intact (as inert text), rather than stripping them away with DOMPurify.
29
+ */
22
30
  highlightedHit(hit) {
23
- return html`
24
- <p>${unsafeHTML(hit.replace(this.matchRegex, '<mark>$1</mark>'))}</p>
25
- `;
31
+ return html`<p>
32
+ ${unsafeHTML(escapeHTML(hit).replace(this.matchRegex, '<mark>$1</mark>'))}
33
+ </p>`;
26
34
  }
27
35
 
28
36
  resultSelected() {
@@ -36,17 +44,14 @@ export class BookSearchResult extends LitElement {
36
44
  }
37
45
 
38
46
  render() {
39
- const { match } = this;
40
- const { par = [] } = match;
41
- const [resultDetails = {}] = par;
42
- const pageNumber = Number.isInteger(resultDetails.page)
43
- ? html`<p class="page-num">Page -${resultDetails.page}-</p>` : nothing;
47
+ /** @type {SearchInsideMatch} */
48
+ const match = this.match;
44
49
  const coverImage = html`<img src="${match.cover}" />`;
45
50
  return html`
46
51
  <li @click=${this.resultSelected}>
47
52
  ${match.cover ? coverImage : nothing}
48
53
  <h4>${match.title || nothing}</h4>
49
- ${pageNumber}
54
+ <p class="page-num">Page ${match.displayPageNumber}</p>
50
55
  ${this.highlightedHit(match.text)}
51
56
  </li>
52
57
  `;