@internetarchive/bookreader 5.0.0-26 → 5.0.0-29

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 (236) hide show
  1. package/.husky/_/husky.sh +30 -0
  2. package/BookReader/BookReader.css +1 -1
  3. package/BookReader/BookReader.js +1 -1
  4. package/BookReader/BookReader.js.map +1 -1
  5. package/BookReader/bookreader-component-bundle.js +570 -542
  6. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +23 -0
  7. package/BookReader/bookreader-component-bundle.js.map +1 -1
  8. package/BookReader/plugins/plugin.search.js +1 -1
  9. package/BookReader/plugins/plugin.search.js.map +1 -1
  10. package/BookReader/plugins/plugin.tts.js.map +1 -1
  11. package/BookReader/plugins/plugin.url.js +1 -1
  12. package/BookReader/plugins/plugin.url.js.map +1 -1
  13. package/BookReaderDemo/BookReaderDemo.css +14 -1
  14. package/BookReaderDemo/IADemoBr.js +104 -0
  15. package/BookReaderDemo/demo-internetarchive.html +65 -98
  16. package/CHANGELOG.md +10 -0
  17. package/package.json +9 -6
  18. package/src/BookNavigator/assets/ia-logo.js +17 -0
  19. package/src/BookNavigator/book-navigator.js +521 -0
  20. package/src/BookNavigator/bookmarks/bookmark-button.js +2 -1
  21. package/src/BookNavigator/bookmarks/bookmarks-provider.js +20 -8
  22. package/src/BookNavigator/bookmarks/ia-bookmarks.js +84 -51
  23. package/src/BookNavigator/downloads/downloads-provider.js +5 -9
  24. package/src/BookNavigator/downloads/downloads.js +1 -0
  25. package/src/BookNavigator/search/search-provider.js +15 -8
  26. package/src/BookNavigator/sharing.js +27 -0
  27. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +9 -8
  28. package/src/BookNavigator/volumes/volumes-provider.js +44 -13
  29. package/src/BookNavigator/volumes/volumes.js +14 -3
  30. package/src/BookReader/options.js +6 -0
  31. package/src/BookReader.js +20 -8
  32. package/src/BookReaderComponent/BookReaderComponent.js +53 -32
  33. package/src/css/_BRComponent.scss +1 -1
  34. package/src/plugins/search/plugin.search.js +10 -9
  35. package/src/plugins/tts/FestivalTTSEngine.js +1 -1
  36. package/src/plugins/url/UrlPlugin.js +184 -0
  37. package/src/plugins/url/plugin.url.js +220 -0
  38. package/{src → stat}/BookNavigator/BookModel.js +0 -0
  39. package/{src → stat}/BookNavigator/BookNavigator.js +109 -102
  40. package/stat/BookNavigator/assets/bookmark-colors.js +15 -0
  41. package/stat/BookNavigator/assets/button-base.js +61 -0
  42. package/stat/BookNavigator/assets/ia-logo.js +17 -0
  43. package/stat/BookNavigator/assets/icon_checkmark.js +6 -0
  44. package/stat/BookNavigator/assets/icon_close.js +3 -0
  45. package/stat/BookNavigator/assets/icon_sort_asc.js +5 -0
  46. package/stat/BookNavigator/assets/icon_sort_desc.js +5 -0
  47. package/stat/BookNavigator/assets/icon_sort_neutral.js +5 -0
  48. package/stat/BookNavigator/assets/icon_volumes.js +11 -0
  49. package/stat/BookNavigator/bookmarks/bookmark-button.js +64 -0
  50. package/stat/BookNavigator/bookmarks/bookmark-edit.js +215 -0
  51. package/stat/BookNavigator/bookmarks/bookmarks-list.js +285 -0
  52. package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
  53. package/stat/BookNavigator/bookmarks/bookmarks-provider.js +56 -0
  54. package/stat/BookNavigator/bookmarks/ia-bookmarks.js +523 -0
  55. package/{src → stat}/BookNavigator/br-fullscreen-mgr.js +1 -2
  56. package/stat/BookNavigator/delete-modal-actions.js +49 -0
  57. package/stat/BookNavigator/downloads/downloads-provider.js +72 -0
  58. package/stat/BookNavigator/downloads/downloads.js +139 -0
  59. package/stat/BookNavigator/provider-config.js +0 -0
  60. package/stat/BookNavigator/search/a-search-result.js +55 -0
  61. package/stat/BookNavigator/search/search-provider.js +180 -0
  62. package/stat/BookNavigator/search/search-results.js +360 -0
  63. package/{src/ItemNavigator/providers → stat/BookNavigator}/sharing.js +3 -5
  64. package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +94 -0
  65. package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
  66. package/stat/BookNavigator/volumes/volumes-provider.js +83 -0
  67. package/stat/BookNavigator/volumes/volumes.js +178 -0
  68. package/stat/BookReader/BookModel.js +518 -0
  69. package/stat/BookReader/DebugConsole.js +54 -0
  70. package/stat/BookReader/DragScrollable.js +233 -0
  71. package/stat/BookReader/ImageCache.js +116 -0
  72. package/stat/BookReader/Mode1Up.js +102 -0
  73. package/stat/BookReader/Mode1UpLit.js +434 -0
  74. package/stat/BookReader/Mode2Up.js +1372 -0
  75. package/stat/BookReader/ModeSmoothZoom.js +177 -0
  76. package/stat/BookReader/ModeThumb.js +344 -0
  77. package/stat/BookReader/Navbar/Navbar.js +310 -0
  78. package/stat/BookReader/PageContainer.js +120 -0
  79. package/stat/BookReader/ReduceSet.js +26 -0
  80. package/stat/BookReader/Toolbar/Toolbar.js +384 -0
  81. package/stat/BookReader/events.js +20 -0
  82. package/stat/BookReader/options.js +324 -0
  83. package/stat/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  84. package/stat/BookReader/utils/classes.js +36 -0
  85. package/stat/BookReader/utils.js +240 -0
  86. package/stat/BookReader.js +2550 -0
  87. package/stat/BookReaderComponent/BookReaderComponent.js +117 -0
  88. package/stat/assets/icons/1up.svg +12 -0
  89. package/stat/assets/icons/2up.svg +15 -0
  90. package/stat/assets/icons/advance.svg +26 -0
  91. package/stat/assets/icons/chevron-right.svg +1 -0
  92. package/stat/assets/icons/close-circle-dark.svg +1 -0
  93. package/stat/assets/icons/close-circle.svg +1 -0
  94. package/stat/assets/icons/fullscreen.svg +17 -0
  95. package/stat/assets/icons/fullscreen_exit.svg +17 -0
  96. package/stat/assets/icons/hamburger.svg +15 -0
  97. package/stat/assets/icons/left-arrow.svg +12 -0
  98. package/stat/assets/icons/magnify-minus.svg +16 -0
  99. package/stat/assets/icons/magnify-plus.svg +17 -0
  100. package/stat/assets/icons/magnify.svg +15 -0
  101. package/stat/assets/icons/pause.svg +23 -0
  102. package/stat/assets/icons/play.svg +22 -0
  103. package/stat/assets/icons/playback-speed.svg +34 -0
  104. package/stat/assets/icons/read-aloud.svg +22 -0
  105. package/stat/assets/icons/review.svg +22 -0
  106. package/stat/assets/icons/thumbnails.svg +17 -0
  107. package/stat/assets/icons/voice.svg +1 -0
  108. package/stat/assets/icons/volume-full.svg +22 -0
  109. package/stat/assets/images/BRicons.png +0 -0
  110. package/stat/assets/images/BRicons.svg +94 -0
  111. package/stat/assets/images/BRicons_ia.png +0 -0
  112. package/stat/assets/images/back_pages.png +0 -0
  113. package/stat/assets/images/book_bottom_icon.png +0 -0
  114. package/stat/assets/images/book_down_icon.png +0 -0
  115. package/stat/assets/images/book_left_icon.png +0 -0
  116. package/stat/assets/images/book_leftmost_icon.png +0 -0
  117. package/stat/assets/images/book_right_icon.png +0 -0
  118. package/stat/assets/images/book_rightmost_icon.png +0 -0
  119. package/stat/assets/images/book_top_icon.png +0 -0
  120. package/stat/assets/images/book_up_icon.png +0 -0
  121. package/stat/assets/images/books_graphic.svg +177 -0
  122. package/stat/assets/images/booksplit.png +0 -0
  123. package/stat/assets/images/control_pause_icon.png +0 -0
  124. package/stat/assets/images/control_play_icon.png +0 -0
  125. package/stat/assets/images/embed_icon.png +0 -0
  126. package/stat/assets/images/icon-home-ia.png +0 -0
  127. package/stat/assets/images/icon_OL-logo-xs.png +0 -0
  128. package/stat/assets/images/icon_alert-xs.png +0 -0
  129. package/stat/assets/images/icon_book.svg +12 -0
  130. package/stat/assets/images/icon_bookmark.svg +12 -0
  131. package/stat/assets/images/icon_close-pop.png +0 -0
  132. package/stat/assets/images/icon_download.png +0 -0
  133. package/stat/assets/images/icon_gear.svg +14 -0
  134. package/stat/assets/images/icon_hamburger.svg +20 -0
  135. package/stat/assets/images/icon_home.png +0 -0
  136. package/stat/assets/images/icon_home.svg +21 -0
  137. package/stat/assets/images/icon_home_ia.png +0 -0
  138. package/stat/assets/images/icon_indicator.png +0 -0
  139. package/stat/assets/images/icon_info.svg +11 -0
  140. package/stat/assets/images/icon_one_page.svg +8 -0
  141. package/stat/assets/images/icon_pause.svg +1 -0
  142. package/stat/assets/images/icon_play.svg +1 -0
  143. package/stat/assets/images/icon_playback-rate.svg +15 -0
  144. package/stat/assets/images/icon_return.png +0 -0
  145. package/stat/assets/images/icon_search_button.svg +8 -0
  146. package/stat/assets/images/icon_share.svg +9 -0
  147. package/stat/assets/images/icon_skip-ahead.svg +6 -0
  148. package/stat/assets/images/icon_skip-back.svg +13 -0
  149. package/stat/assets/images/icon_speaker.svg +18 -0
  150. package/stat/assets/images/icon_speaker_open.svg +10 -0
  151. package/stat/assets/images/icon_thumbnails.svg +12 -0
  152. package/stat/assets/images/icon_toc.svg +5 -0
  153. package/stat/assets/images/icon_two_pages.svg +9 -0
  154. package/stat/assets/images/icon_zoomer.png +0 -0
  155. package/stat/assets/images/loading.gif +0 -0
  156. package/stat/assets/images/logo_icon.png +0 -0
  157. package/stat/assets/images/marker_chap-off.png +0 -0
  158. package/stat/assets/images/marker_chap-off.svg +11 -0
  159. package/stat/assets/images/marker_chap-off_ia.png +0 -0
  160. package/stat/assets/images/marker_chap-on.png +0 -0
  161. package/stat/assets/images/marker_chap-on.svg +11 -0
  162. package/stat/assets/images/marker_srch-on.svg +11 -0
  163. package/stat/assets/images/marker_srchchap-off.png +0 -0
  164. package/stat/assets/images/marker_srchchap-on.png +0 -0
  165. package/stat/assets/images/nav_control-dn.png +0 -0
  166. package/stat/assets/images/nav_control-dn_ia.png +0 -0
  167. package/stat/assets/images/nav_control-up.png +0 -0
  168. package/stat/assets/images/nav_control-up_ia.png +0 -0
  169. package/stat/assets/images/nav_control.png +0 -0
  170. package/stat/assets/images/one_page_mode_icon.png +0 -0
  171. package/stat/assets/images/paper-badge.png +0 -0
  172. package/stat/assets/images/print_icon.png +0 -0
  173. package/stat/assets/images/progressbar.gif +0 -0
  174. package/stat/assets/images/right_edges.png +0 -0
  175. package/stat/assets/images/slider.png +0 -0
  176. package/stat/assets/images/slider_ia.png +0 -0
  177. package/stat/assets/images/thumbnail_mode_icon.png +0 -0
  178. package/stat/assets/images/transparent.png +0 -0
  179. package/stat/assets/images/two_page_mode_icon.png +0 -0
  180. package/stat/assets/images/zoom_in_icon.png +0 -0
  181. package/stat/assets/images/zoom_out_icon.png +0 -0
  182. package/stat/css/BookReader.scss +89 -0
  183. package/stat/css/_BRBookmarks.scss +29 -0
  184. package/stat/css/_BRComponent.scss +13 -0
  185. package/stat/css/_BRfloat.scss +197 -0
  186. package/stat/css/_BRicon.scss +48 -0
  187. package/stat/css/_BRmain.scss +251 -0
  188. package/stat/css/_BRnav.scss +359 -0
  189. package/stat/css/_BRpages.scss +139 -0
  190. package/stat/css/_BRsearch.scss +226 -0
  191. package/stat/css/_BRtoolbar.scss +84 -0
  192. package/stat/css/_BRvendor.scss +5 -0
  193. package/stat/css/_MobileNav.scss +194 -0
  194. package/stat/css/_TextSelection.scss +32 -0
  195. package/stat/css/_colorbox.scss +52 -0
  196. package/stat/css/_controls.scss +253 -0
  197. package/stat/css/_icons.scss +121 -0
  198. package/stat/jquery-wrapper.js +4 -0
  199. package/stat/plugins/plugin.archive_analytics.js +86 -0
  200. package/stat/plugins/plugin.autoplay.js +129 -0
  201. package/stat/plugins/plugin.chapters.js +248 -0
  202. package/stat/plugins/plugin.iframe.js +48 -0
  203. package/stat/plugins/plugin.mobile_nav.js +288 -0
  204. package/stat/plugins/plugin.resume.js +68 -0
  205. package/stat/plugins/plugin.text_selection.js +291 -0
  206. package/{src → stat}/plugins/plugin.url.js +0 -0
  207. package/stat/plugins/plugin.vendor-fullscreen.js +247 -0
  208. package/stat/plugins/search/plugin.search.js +439 -0
  209. package/stat/plugins/search/view.js +439 -0
  210. package/stat/plugins/tts/AbstractTTSEngine.js +249 -0
  211. package/stat/plugins/tts/FestivalTTSEngine.js +169 -0
  212. package/stat/plugins/tts/PageChunk.js +107 -0
  213. package/stat/plugins/tts/PageChunkIterator.js +163 -0
  214. package/stat/plugins/tts/WebTTSEngine.js +357 -0
  215. package/stat/plugins/tts/plugin.tts.js +357 -0
  216. package/stat/plugins/tts/tooltip_dict.js +15 -0
  217. package/stat/plugins/tts/utils.js +91 -0
  218. package/stat/util/browserSniffing.js +30 -0
  219. package/stat/util/debouncer.js +26 -0
  220. package/stat/util/docCookies.js +67 -0
  221. package/stat/util/strings.js +34 -0
  222. package/tests/e2e/viewmode.test.js +30 -30
  223. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +64 -52
  224. package/tests/jest/BookReader.test.js +1 -1
  225. package/tests/jest/plugins/url/UrlPlugin.test.js +175 -0
  226. package/tests/jest/plugins/{plugin.url.test.js → url/plugin.url.test.js} +3 -2
  227. package/tests/karma/BookNavigator/book-navigator.test.js +413 -108
  228. package/tests/karma/BookNavigator/bookmarks/bookmark-button.test.js +44 -0
  229. package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +6 -3
  230. package/tests/karma/BookNavigator/search/search-provider.test.js +106 -6
  231. package/tests/karma/BookNavigator/search/search-results.test.js +0 -2
  232. package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +29 -20
  233. package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +46 -22
  234. package/webpack.config.js +1 -1
  235. package/src/BookNavigator/assets/book-loader.js +0 -27
  236. package/src/ItemNavigator/ItemNavigator.js +0 -377
@@ -0,0 +1,523 @@
1
+ import { render, nothing } from 'lit-html';
2
+ import { LitElement, html, css } from 'lit-element';
3
+ import { ModalConfig } from '@internetarchive/modal-manager';
4
+ import buttonStyles from '../assets/button-base.js';
5
+ import './bookmarks-loginCTA.js';
6
+
7
+ const api = {
8
+ endpoint: '/services/bookmarks.php',
9
+ headers: {
10
+ 'Content-Type': 'application/json',
11
+ },
12
+ delete(page) {
13
+ return fetch(`${this.endpoint}?identifier=${this.identifier}&page_num=${page}`, {
14
+ credentials: 'same-origin',
15
+ method: 'DELETE',
16
+ headers: this.headers,
17
+ });
18
+ },
19
+ get(page) {
20
+ return fetch(`${this.endpoint}?identifier=${this.identifier}&page_num=${page}`, {
21
+ credentials: 'same-origin',
22
+ method: 'GET',
23
+ headers: this.headers,
24
+ });
25
+ },
26
+ getAll() {
27
+ return fetch(`${this.endpoint}?identifier=${this.identifier}`, {
28
+ credentials: 'same-origin',
29
+ method: 'GET',
30
+ headers: this.headers,
31
+ });
32
+ },
33
+ post(bookmark) {
34
+ return this.sendBookmarkData(bookmark, 'POST');
35
+ },
36
+ put(bookmark) {
37
+ return this.sendBookmarkData(bookmark, 'POST');
38
+ },
39
+ sendBookmarkData(bookmark, method) {
40
+ const notes = {
41
+ note: bookmark.note,
42
+ color: bookmark.color,
43
+ };
44
+ return fetch(`${this.endpoint}?identifier=${this.identifier}&page_num=${bookmark.id}`, {
45
+ credentials: 'same-origin',
46
+ method,
47
+ headers: this.headers,
48
+ body: JSON.stringify({
49
+ notes,
50
+ }),
51
+ });
52
+ },
53
+ };
54
+
55
+ class IABookmarks extends LitElement {
56
+ static get properties() {
57
+ return {
58
+ activeBookmarkID: { type: String },
59
+ bookmarks: { type: Array },
60
+ bookreader: { type: Object },
61
+ options: { type: Object },
62
+ displayMode: { type: String },
63
+ editedBookmark: { type: Object },
64
+ deleteModalConfig: { type: Object},
65
+ modal: { attribute: false }
66
+ };
67
+ }
68
+
69
+ static get styles() {
70
+ const mainCss = css`
71
+ .bookmarks {
72
+ height: 100%;
73
+ overflow: hidden;
74
+ padding-bottom: 20px;
75
+ }
76
+
77
+ .list ia-bookmark-edit {
78
+ display: none;
79
+ }
80
+
81
+ .edit ia-bookmarks-list {
82
+ display: none;
83
+ }
84
+ `;
85
+
86
+ return [buttonStyles, mainCss];
87
+ }
88
+
89
+ static formatPage(page) {
90
+ return isNaN(+page) ? `(${page.replace(/\D/g, '')})` : page;
91
+ }
92
+
93
+ constructor() {
94
+ super();
95
+ this.bookmarks = [];
96
+ this.bookreader = {};
97
+ this.editedBookmark = {};
98
+ this.options = {};
99
+ this.modal = undefined;
100
+ /**
101
+ * Toggles display to either bookmarks or login cta
102
+ * @param {('bookmarks'|'login')} displayMode
103
+ */
104
+ this.displayMode = 'bookmarks';
105
+
106
+ this.bookmarkColors = [{
107
+ id: 0,
108
+ className: 'red',
109
+ }, {
110
+ id: 1,
111
+ className: 'blue',
112
+ }, {
113
+ id: 2,
114
+ className: 'green',
115
+ }];
116
+
117
+ // eslint-disable-next-line
118
+ this.defaultColor = this.bookmarkColors[0];
119
+ this.api = api;
120
+ this.deleteModalConfig = new ModalConfig({
121
+ title: 'Delete Bookmark',
122
+ headline: 'This bookmark contains a note. Deleting it will permanently delete the note. Are you sure?',
123
+ headerColor: '#194880',
124
+ });
125
+ }
126
+
127
+ updated(changed) {
128
+ if (changed.has('options')) {
129
+ this.updateDisplay();
130
+ }
131
+
132
+ this.emitBookmarksChanged();
133
+ }
134
+
135
+ setup() {
136
+ this.api.identifier = this.bookreader.bookId;
137
+ if (this.displayMode === 'login') {
138
+ return;
139
+ }
140
+ this.setBREventListeners();
141
+ this.initializeBookmarks();
142
+ }
143
+
144
+ updateDisplay() {
145
+ if (this.displayMode === 'bookmarks') {
146
+ this.fetchUserBookmarks();
147
+ }
148
+ }
149
+
150
+ fetchUserBookmarks() {
151
+ this.fetchBookmarks()
152
+ .then(() => {
153
+ this.initializeBookmarks();
154
+ });
155
+ }
156
+
157
+ setBREventListeners() {
158
+ ['3PageViewSelected'].forEach((event) => {
159
+ window.addEventListener(`BookReader:${event}`, (e) => {
160
+ setTimeout(() => {
161
+ // wait a lil bit so bookreader can draw its DOM to attach onto
162
+ this.renderBookmarkButtons();
163
+ }, 100);
164
+ });
165
+ });
166
+ ['pageChanged', '1PageViewSelected', '2PageViewSelected'].forEach((event) => {
167
+ window.addEventListener(`BookReader:${event}`, (e) => {
168
+ setTimeout(() => {
169
+ // wait a lil bit so bookreader can draw its DOM to attach onto
170
+ this.renderBookmarkButtons();
171
+ this.markActiveBookmark();
172
+ }, 100);
173
+ });
174
+ });
175
+ ['zoomOut', 'zoomIn', 'resize'].forEach((event) => {
176
+ window.addEventListener(`BookReader:${event}`, () => {
177
+ this.renderBookmarkButtons();
178
+ });
179
+ });
180
+ }
181
+
182
+ initializeBookmarks() {
183
+ this.renderBookmarkButtons();
184
+ this.markActiveBookmark(true);
185
+ this.emitBookmarksChanged();
186
+ }
187
+
188
+ /**
189
+ * @typedef {object} Bookmark
190
+ * @property {number} id - bookreader page index, becomes key store
191
+ * @property {number} color - color number
192
+ * @property {string} page - bookmark's page label to display
193
+ * @property {string} note - optional, note that one can add
194
+ * @property {string} thumbnail - optional, image url
195
+ */
196
+ /**
197
+ * Formats bookmark view model
198
+ * @param {Object} bookmarkAttrs
199
+ * @param {number} bookmarkAttrs.leafNum
200
+ * @param {string} bookmarkAttrs.notes
201
+ *
202
+ * @returns Bookmark
203
+ */
204
+ formatBookmark({ leafNum = '', notes = {} }) {
205
+ const { note = '', color } = notes;
206
+ const nomalizedParams = {
207
+ note,
208
+ color: this.getBookmarkColor(color) ? color : this.defaultColor.id,
209
+ };
210
+
211
+ const page = IABookmarks.formatPage(this.bookreader.getPageNum(leafNum));
212
+ const thumbnail = this.bookreader.getPageURI(`${leafNum}`.replace(/\D/g, ''), 32); // Request thumbnail 1/32 the size of original image
213
+ const bookmark = {
214
+ ...nomalizedParams,
215
+ id: leafNum,
216
+ leafNum,
217
+ page,
218
+ thumbnail,
219
+ };
220
+
221
+ return bookmark;
222
+ }
223
+
224
+ fetchBookmarks() {
225
+ return this.api.getAll().then((res) => res.json()).then(({
226
+ success,
227
+ error = 'Something happened while fetching bookmarks.',
228
+ value: bkmrks = [],
229
+ }) => {
230
+ if (!success) {
231
+ throw new Error(`Failed to load bookmarks: ${error}`);
232
+ }
233
+
234
+ const bookmarks = {};
235
+ Object.keys(bkmrks).forEach((leafNum) => {
236
+ const bookmark = bkmrks[leafNum];
237
+ const formattedLeafNum = parseInt(leafNum, 10);
238
+ const formattedBookmark = this.formatBookmark({ ...bookmark, leafNum: formattedLeafNum });
239
+ bookmarks[leafNum] = formattedBookmark;
240
+ });
241
+
242
+ this.bookmarks = bookmarks;
243
+ return bookmarks;
244
+ });
245
+ }
246
+
247
+ emitBookmarksChanged(showSidePanel = false) {
248
+ this.dispatchEvent(new CustomEvent('bookmarksChanged', {
249
+ bubbles: true,
250
+ composed: true,
251
+ detail: {
252
+ bookmarks: this.bookmarks,
253
+ showSidePanel
254
+ },
255
+ }));
256
+ }
257
+
258
+ emitBookmarkButtonClicked() {
259
+ this.dispatchEvent(new CustomEvent('bookmarkButtonClicked', {
260
+ bubbles: true,
261
+ composed: true,
262
+ detail: {
263
+ editedBookmark: this.editedBookmark,
264
+ },
265
+ }));
266
+ }
267
+
268
+ bookmarkButtonClicked(pageID) {
269
+ if (this.getBookmark(pageID)) {
270
+ this.confirmDeletion(pageID);
271
+ } else {
272
+ this.createBookmark(pageID);
273
+ }
274
+ }
275
+
276
+ renderBookmarkButtons() {
277
+ const pages = this.bookreader.$('.BRpagecontainer').not('.BRemptypage').get();
278
+
279
+ pages.forEach((pageEl) => {
280
+ const existingButton = pageEl.querySelector('.bookmark-button');
281
+ if (existingButton) {
282
+ existingButton.remove();
283
+ }
284
+ const pageID = +pageEl.classList.value.match(/pagediv\d+/)[0].replace(/\D/g, '');
285
+ const pageBookmark = this.getBookmark(pageID);
286
+ const bookmarkState = pageBookmark ? 'filled' : 'hollow';
287
+ // eslint-disable-next-line
288
+ const pageData = this.bookreader._models.book.getPage(pageID);
289
+ const { isViewable } = pageData;
290
+
291
+ if (!isViewable) { return; }
292
+
293
+ const bookmarkButton = document.createElement('div');
294
+ ['mousedown', 'mouseup'].forEach((event) => {
295
+ bookmarkButton.addEventListener(event, (e) => e.stopPropagation());
296
+ });
297
+ bookmarkButton.classList.add('bookmark-button', bookmarkState);
298
+ if (pageBookmark) {
299
+ bookmarkButton.classList.add(this.getBookmarkColor(pageBookmark.color));
300
+ }
301
+ const pageSide = (pageEl.getAttribute('data-side') === 'L' && this.bookreader.mode === this.bookreader.constMode2up)
302
+ ? 'left' : 'right';
303
+
304
+ render(html`
305
+ <bookmark-button
306
+ @bookmarkButtonClicked=${() => this.bookmarkButtonClicked(pageID)}
307
+ state=${bookmarkState}
308
+ side=${pageSide}
309
+ ></bookmark-button>`, bookmarkButton);
310
+ pageEl.appendChild(bookmarkButton);
311
+ });
312
+ }
313
+
314
+ /**
315
+ * Notes which bookmark is active
316
+ *
317
+ * @param {boolean} atSetup - denotes the first time this is fired
318
+ */
319
+ markActiveBookmark(atSetup = false) {
320
+ const { mode, constMode2up, constModeThumb } = this.bookreader;
321
+ const currentIndex = this.bookreader.currentIndex();
322
+
323
+ if (mode === constModeThumb) {
324
+ // keep active bookmark the same
325
+ // no syncing until we can verify when a bookmark is "in view"
326
+ const requestedPageHasBookmark = this.bookmarks[currentIndex];
327
+ if (atSetup && requestedPageHasBookmark) {
328
+ this.activeBookmarkID = currentIndex;
329
+ }
330
+ return;
331
+ }
332
+ // In 2up, we prefer the right side of the page to bookmark
333
+ // so let's make sure we light that one up.
334
+ if (mode === constMode2up) {
335
+ const pagesInView = this.bookreader.displayedIndices;
336
+ const pagesHaveActiveBookmark = pagesInView.includes(+this.activeBookmarkID);
337
+ if (pagesHaveActiveBookmark) {
338
+ return;
339
+ }
340
+ }
341
+
342
+ // If a bookmark exists with the current index, set it as active
343
+ if (this.bookmarks[currentIndex]) {
344
+ this.activeBookmarkID = currentIndex;
345
+ return;
346
+ }
347
+
348
+ // No bookmark for this page
349
+ this.activeBookmarkID = '';
350
+ }
351
+
352
+ bookmarkEdited({ detail }) {
353
+ const closeEdit = detail.bookmark.id === this.editedBookmark.id;
354
+ this.editedBookmark = closeEdit ? {} : detail.bookmark;
355
+ }
356
+
357
+ /**
358
+ * Gets bookmark by pageindex
359
+ * @param {number} id
360
+ */
361
+ getBookmark(id) {
362
+ return this.bookmarks[id];
363
+ }
364
+
365
+ getBookmarkColor(id) {
366
+ return this.bookmarkColors.find((m) => m.id === id)?.className;
367
+ }
368
+
369
+ /**
370
+ * Adds bookmark for current page
371
+ */
372
+ addBookmark() {
373
+ let pageID = this.bookreader.currentIndex();
374
+ if (this.bookreader.mode === this.bookreader.constMode2up) {
375
+ const pagesInView = this.bookreader.displayedIndices;
376
+
377
+ // add bookmark to right hand page
378
+ pageID = pagesInView[pagesInView.length - 1];
379
+ }
380
+ this.createBookmark(pageID);
381
+ }
382
+
383
+ /**
384
+ * Creates bookmark for a given page
385
+ * @param {number} pageID
386
+ */
387
+ createBookmark(pageID) {
388
+ const existingBookmark = this.getBookmark(pageID);
389
+ if (existingBookmark) {
390
+ this.bookmarkEdited({ detail: { bookmark: existingBookmark } });
391
+ this.emitBookmarkButtonClicked();
392
+ return;
393
+ }
394
+
395
+ this.editedBookmark = this.formatBookmark({ leafNum: pageID });
396
+
397
+ this.api.post(this.editedBookmark);
398
+
399
+ this.bookmarks[pageID] = this.editedBookmark;
400
+ this.activeBookmarkID = pageID;
401
+ this.disableAddBookmarkButton = true;
402
+ this.renderBookmarkButtons();
403
+ this.emitBookmarkButtonClicked();
404
+ }
405
+
406
+ bookmarkSelected({ detail }) {
407
+ const { leafNum } = detail.bookmark;
408
+ this.bookreader.jumpToPage(`${this.bookreader.getPageNum(`${leafNum}`.replace(/\D/g, ''))}`);
409
+ this.activeBookmarkID = leafNum;
410
+ }
411
+
412
+ saveBookmark({ detail }) {
413
+ const existingBookmark = this.bookmarks[detail.bookmark.id];
414
+ Object.assign(existingBookmark, detail.bookmark);
415
+ this.api.put(existingBookmark);
416
+ this.editedBookmark = {};
417
+ this.renderBookmarkButtons();
418
+ }
419
+
420
+ confirmDeletion(pageID) {
421
+ const existingBookmark = this.getBookmark(pageID);
422
+ if (existingBookmark.note) {
423
+ this.displayDeletionModal(pageID);
424
+ return;
425
+ }
426
+ this.deleteBookmark({ detail: { id: `${pageID}` } });
427
+ }
428
+
429
+ displayDeletionModal(pageID) {
430
+ const customModalContent = html`
431
+ <delete-modal-actions
432
+ .deleteAction=${() => this.deleteBookmark({ detail: { id: `${pageID}` } })}
433
+ .cancelAction=${() => this.modal.closeModal()}
434
+ .pageID=${pageID}
435
+ ></delete-modal-actions>
436
+ `;
437
+
438
+
439
+ this.modal.showModal({
440
+ config: this.deleteModalConfig,
441
+ customModalContent,
442
+ });
443
+ }
444
+
445
+ deleteBookmark({ detail }) {
446
+ const { id } = detail;
447
+ const currBookmarks = this.bookmarks;
448
+ delete currBookmarks[id];
449
+ this.bookmarks = { ...currBookmarks };
450
+
451
+ this.api.delete(detail.id);
452
+ this.editedBookmark = {};
453
+ this.modal.closeModal();
454
+ this.renderBookmarkButtons();
455
+ }
456
+
457
+ /**
458
+ * call `loginClicked` callback
459
+ */
460
+ loginClick() {
461
+ const { loginClicked = () => {} } = this.options;
462
+ loginClicked();
463
+ }
464
+
465
+ /**
466
+ * Tells us if we should allow user to add bookmark via menu panel
467
+ * returns { Boolean }
468
+ */
469
+ get shouldEnableAddBookmarkButton() {
470
+ const pageToCheck = this.bookreader.mode === this.bookreader.constMode2up
471
+ ? this.bookreader.displayedIndices[this.bookreader.displayedIndices.length - 1]
472
+ : this.bookreader.currentIndex();
473
+ const pageHasBookmark = this.getBookmark(pageToCheck);
474
+ return !!pageHasBookmark;
475
+ }
476
+
477
+
478
+ get allowAddingBookmark() {
479
+ return this.bookreader.mode !== this.bookreader.constModeThumb;
480
+ }
481
+
482
+ get addBookmarkButton() {
483
+ return html`
484
+ <button
485
+ class="ia-button primary"
486
+ ?disabled=${this.shouldEnableAddBookmarkButton}
487
+ @click=${this.addBookmark}>
488
+ Add bookmark
489
+ </button>
490
+ `;
491
+ }
492
+
493
+ get bookmarksList() {
494
+ return html`
495
+ <ia-bookmarks-list
496
+ @bookmarkEdited=${this.bookmarkEdited}
497
+ @bookmarkSelected=${this.bookmarkSelected}
498
+ @saveBookmark=${this.saveBookmark}
499
+ @deleteBookmark=${this.deleteBookmark}
500
+ .editedBookmark=${this.editedBookmark}
501
+ .bookmarks=${{ ...this.bookmarks }}
502
+ .activeBookmarkID=${this.activeBookmarkID}
503
+ .bookmarkColors=${this.bookmarkColors}
504
+ .defaultBookmarkColor=${this.defaultColor}>
505
+ </ia-bookmarks-list>
506
+ `;
507
+ }
508
+
509
+ render() {
510
+ const { loginUrl } = this.options;
511
+ const bookmarks = html`
512
+ ${this.bookmarksList}
513
+ ${this.allowAddingBookmark ? this.addBookmarkButton : nothing}
514
+ `;
515
+ return html`
516
+ <section class="bookmarks">
517
+ ${this.displayMode === 'login' ? html`<bookmarks-login @click=${this.loginClick} .url=${loginUrl}></bookmarks-login>` : bookmarks}
518
+ </section>
519
+ `;
520
+ }
521
+ }
522
+
523
+ customElements.define('ia-bookmarks', IABookmarks);
@@ -16,9 +16,7 @@ export default class BRFullscreenMgr {
16
16
  this.setup = this.setup.bind(this);
17
17
  this.teardown = this.teardown.bind(this);
18
18
  this.resizeBookReaderContainer = this.resizeBookReaderContainer.bind(this);
19
-
20
19
  this.handleResizeEvent = this.handleResizeEvent.bind(this);
21
-
22
20
  this.handleBookReaderHeight = new Debouncer(
23
21
  this.resizeBookReaderContainer, this.debounceTime, this,
24
22
  );
@@ -79,5 +77,6 @@ export default class BRFullscreenMgr {
79
77
  this.brDom.style.height = newHeight;
80
78
  this.brDom.style.top = loanbarHeight;
81
79
  window.scrollTo(0, 0);
80
+ console.log('resizeBookReaderContainer');
82
81
  }
83
82
  }
@@ -0,0 +1,49 @@
1
+ import { LitElement, html, css } from 'lit-element';
2
+
3
+ export default class DeleteModalActions extends LitElement {
4
+ static get styles() {
5
+ return css`
6
+ div {
7
+ display: flex;
8
+ justify-content: center;
9
+ padding-top: 2rem;
10
+ }
11
+
12
+ button {
13
+ appearance: none;
14
+ padding: 0.5rem 1rem;
15
+ margin: 0 .5rem;
16
+ box-sizing: border-box;
17
+ font: 1.3rem "Helvetica Neue", Helvetica, Arial, sans-serif;
18
+ color: var(--primaryTextColor);
19
+ border: none;
20
+ border-radius: 4px;
21
+ cursor: pointer;
22
+ background: var(--primaryCTAFill);
23
+ }
24
+
25
+ .delete {
26
+ background: var(--primaryErrorCTAFill);
27
+ }
28
+ `;
29
+ }
30
+
31
+ static get properties() {
32
+ return {
33
+ cancelAction: { type: Function },
34
+ deleteAction: { type: Function },
35
+ pageID: { type: String },
36
+ };
37
+ }
38
+
39
+ render() {
40
+ return html`
41
+ <div>
42
+ <button class="delete" @click=${() => this.deleteAction({ detail: { id: `${this.pageID}` } })}>Delete</button>
43
+ <button @click=${() => this.cancelAction()}>Cancel</button>
44
+ </div>
45
+ `;
46
+ }
47
+ }
48
+
49
+ customElements.define('delete-modal-actions', DeleteModalActions);
@@ -0,0 +1,72 @@
1
+ import { html } from 'lit-element';
2
+ import '@internetarchive/icon-dl/icon-dl';
3
+ import './downloads';
4
+
5
+ const menuBase = {
6
+ pdf: {
7
+ type: 'Encrypted Adobe PDF',
8
+ url: '#',
9
+ note: 'PDF files contain high quality images of pages.',
10
+ },
11
+ epub: {
12
+ type: 'Encrypted Adobe ePub',
13
+ url: '#',
14
+ note: 'ePub files are smaller in size, but may contain errors.',
15
+ }
16
+ };
17
+
18
+ const publicMenuBase = {
19
+ pdf: "PDF",
20
+ epub: "ePub"
21
+ };
22
+
23
+ export default class DownloadsProvider {
24
+
25
+ constructor(isBookProtected) {
26
+ this.icon = html`<ia-icon-dl style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon-dl>`;
27
+ this.label = 'Downloadable files';
28
+ this.menuDetails = '';
29
+ this.downloads = [];
30
+ this.id = 'downloads';
31
+ this.component = '';
32
+ this.isBookProtected = isBookProtected;
33
+
34
+ this.computeAvailableTypes = this.computeAvailableTypes.bind(this);
35
+ this.update = this.update.bind(this);
36
+ }
37
+
38
+ update(downloadTypes) {
39
+ this.computeAvailableTypes(downloadTypes);
40
+ this.component = this.menu;
41
+ this.component.isBookProtected = this.isBookProtected;
42
+
43
+ const ending = this.downloads.length === 1 ? '' : 's';
44
+ this.menuDetails = `(${this.downloads.length} format${ending})`;
45
+ }
46
+
47
+ /**
48
+ * Generates Download Menu Info for available types
49
+ * sets global `downloads`
50
+ * @param availableTypes
51
+ */
52
+ computeAvailableTypes(availableTypes = []) {
53
+ const menuData = availableTypes.reduce((found, incoming = []) => {
54
+ const [ type = '', link = '' ] = incoming;
55
+ const formattedType = type.toLowerCase();
56
+ const downloadOption = menuBase[formattedType] || null;
57
+
58
+ if (downloadOption) {
59
+ const menuButtonText = this.isBookProtected ? menuBase[formattedType].type : publicMenuBase[formattedType];
60
+ const menuInfo = Object.assign({}, downloadOption, { url: link, type: menuButtonText});
61
+ found.push(menuInfo);
62
+ }
63
+ return found;
64
+ }, []);
65
+
66
+ this.downloads = menuData;
67
+ }
68
+
69
+ get menu () {
70
+ return html`<ia-book-downloads .downloads=${this.downloads}></ia-book-downloads>`;
71
+ }
72
+ }